@marko/vite
Version:
A Marko plugin for Vite
1,507 lines (1,484 loc) • 50.5 kB
JavaScript
// src/index.ts
import * as compiler2 from "@marko/compiler";
import anyMatch from "anymatch";
import crypto from "crypto";
import glob2 from "fast-glob";
import fs4 from "fs";
import { createRequire } from "module";
import path6 from "path";
// src/babel-plugin-cjs-interop.ts
import * as t from "@babel/types";
// src/resolve.ts
import fs from "fs";
import path from "path";
import Resolve from "resolve";
var moduleNameReg = /^(?:@[^/\\]+[/\\])?[^/\\]+/;
var modulePathReg = /^.*[/\\]node_modules[/\\]((?:@[^/\\]+[/\\])?[^/\\]+[/\\])/;
var cjsModuleLookup = /* @__PURE__ */ new Map();
function isCJSModule(id, fromFile) {
if (/\.cjs$/.test(id)) return true;
if (/\.mjs$/.test(id)) return false;
if (id[0] === ".") return isCJSModule(fromFile, fromFile);
const isAbsolute = path.isAbsolute(id);
const moduleId = moduleNameReg.exec(
isAbsolute ? id.replace(modulePathReg, "$1").replace(/\\/g, "/") : id
)?.[0];
if (!moduleId) return false;
let isCJS = cjsModuleLookup.get(moduleId);
if (isCJS === void 0) {
try {
if (isAbsolute) {
const pkgPath = modulePathReg.exec(id)[0] + "/package.json";
const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf8"));
isCJS = pkg.type !== "module" && !pkg.exports;
} else {
Resolve.sync(moduleId + "/package.json", {
basedir: path.dirname(fromFile),
filename: fromFile,
pathFilter(pkg, _pkgFile, relativePath) {
isCJS = pkg.type !== "module" && !pkg.exports;
return relativePath;
}
});
isCJS ??= false;
}
} catch {
isCJS = false;
}
cjsModuleLookup.set(moduleId, isCJS);
}
return isCJS;
}
// src/babel-plugin-cjs-interop.ts
function plugin(options) {
return {
name: "marko-import-interop",
visitor: {
ImportDeclaration(path7) {
if (!path7.node.specifiers.length || /\.(?:mjs|marko)$|\?/.test(path7.node.source.value) || options.filter?.(path7.node.source.value) === false || !isCJSModule(
path7.node.source.value,
path7.hub.file.opts.filename
)) {
return;
}
let namespaceId;
let defaultImportId;
let imports;
for (const s of path7.node.specifiers) {
if (t.isImportSpecifier(s)) {
(imports ||= []).push({
name: t.isStringLiteral(s.imported) ? s.imported.value : s.imported.name,
alias: s.local.name
});
} else if (t.isImportDefaultSpecifier(s)) {
defaultImportId = s.local;
} else if (t.isImportNamespaceSpecifier(s)) {
namespaceId = s.local;
}
}
const rawImport = path7.scope.generateUidIdentifier(
namespaceId?.name || defaultImportId?.name || path7.node.source.value
);
path7.node.specifiers = [t.importNamespaceSpecifier(rawImport)];
if (defaultImportId) {
path7.insertAfter(
t.variableDeclaration("const", [
t.variableDeclarator(
t.objectPattern([
t.objectProperty(t.identifier("default"), defaultImportId)
]),
t.conditionalExpression(
t.optionalMemberExpression(
t.memberExpression(rawImport, t.identifier("default")),
t.identifier("__esModule"),
false,
true
),
t.memberExpression(rawImport, t.identifier("default")),
rawImport
)
)
])
);
}
if (namespaceId) {
path7.insertAfter(
t.variableDeclaration("const", [
t.variableDeclarator(
namespaceId,
t.conditionalExpression(
t.optionalMemberExpression(
rawImport,
t.identifier("__esModule"),
false,
true
),
rawImport,
t.memberExpression(rawImport, t.identifier("default"))
)
)
])
);
}
if (imports) {
path7.insertAfter(
t.variableDeclaration("const", [
t.variableDeclarator(
t.objectPattern(
imports.map(
({ name, alias }) => t.objectProperty(
t.identifier(name),
t.identifier(alias),
false,
name === alias
)
)
),
t.conditionalExpression(
t.optionalMemberExpression(
rawImport,
t.identifier("__esModule"),
false,
true
),
rawImport,
t.memberExpression(rawImport, t.identifier("default"))
)
)
])
);
}
}
}
};
}
// src/esbuild-plugin.ts
import * as compiler from "@marko/compiler";
import fs2 from "fs";
import path2 from "path";
var markoErrorRegExp = /^(.+?)(?:\((\d+)(?:\s*,\s*(\d+))?\))?: (.*)$/gm;
function esbuildPlugin(config) {
return {
name: "marko",
async setup(build) {
const { platform = "browser" } = build.initialOptions;
const isScan = build.initialOptions.plugins?.some(
(v) => v.name === "vite:dep-scan"
);
const virtualFiles2 = /* @__PURE__ */ new Map();
const finalConfig = {
...config,
output: platform === "browser" ? "dom" : "html",
resolveVirtualDependency(from, dep) {
virtualFiles2.set(path2.join(from, "..", dep.virtualPath), dep);
return dep.virtualPath;
}
};
const scanConfig = {
...finalConfig,
output: "hydrate"
};
build.onResolve({ filter: /\.marko\./ }, (args) => {
return {
namespace: "marko:virtual",
path: path2.resolve(args.resolveDir, args.path),
external: isScan
};
});
build.onLoad(
{ filter: /\.marko\./, namespace: "marko:virtual" },
(args) => ({
contents: virtualFiles2.get(args.path).code,
loader: path2.extname(args.path).slice(1)
})
);
build.onLoad({ filter: /\.marko$/ }, async (args) => {
try {
const { code, meta } = await compiler.compileFile(
args.path,
isScan && args.namespace === "" ? scanConfig : finalConfig
);
return {
loader: "js",
contents: code,
watchFiles: meta.watchFiles,
resolveDir: path2.dirname(args.path)
};
} catch (e) {
const text = e.message;
const errors = [];
let match;
let lines;
while (match = markoErrorRegExp.exec(text)) {
const [, file, rawLine, rawCol, text2] = match;
const line = parseInt(rawLine, 10) || 1;
const column = parseInt(rawCol, 10) || 1;
lines ||= (await fs2.promises.readFile(args.path, "utf-8")).split(
/\n/g
);
errors.push({
text: text2,
location: {
file,
line,
column,
lineText: ` ${lines[line - 1]}`
}
});
}
if (!errors.length) {
errors.push({ text });
}
return {
errors
};
}
});
}
};
}
// src/glob-import-transform.ts
import { types as t2 } from "@marko/compiler";
import glob from "fast-glob";
import path3 from "path";
var programGlobImports = /* @__PURE__ */ new WeakMap();
var glob_import_transform_default = {
MetaProperty(tag) {
const memberExpression2 = tag.parentPath;
if (memberExpression2.node.type === "MemberExpression" && memberExpression2.node.property.type === "Identifier" && memberExpression2.node.property.name === "glob") {
const callExpression = memberExpression2.parentPath;
if (callExpression?.node.type === "CallExpression") {
const args = callExpression.get("arguments").map((arg) => arg.evaluate().value);
if (args[1]?.eager) {
const program = tag.hub.file.path;
const existing = programGlobImports.get(program);
if (!existing) {
programGlobImports.set(program, [args]);
} else {
existing.push(args);
}
}
}
}
},
Program: {
exit(tag) {
const globImports = programGlobImports.get(tag);
if (!globImports) {
return;
}
const { cwd, filename } = tag.hub.file.opts;
const dir = path3.dirname(filename);
const seen = /* @__PURE__ */ new Set();
for (const [patterns, options] of globImports) {
const resolvedPatterns = Array.isArray(patterns) ? patterns.map((p) => path3.resolve(dir, p)) : path3.resolve(dir, patterns);
const results = glob.globSync(resolvedPatterns, {
cwd,
absolute: true,
dot: !!options.exhaustive,
ignore: options.exhaustive ? [] : [path3.join(cwd, "**/node_modules/**")]
});
for (const file of results) {
if (file.endsWith(".marko") && file !== filename && !seen.has(file)) {
seen.add(file);
tag.node.body.push(t2.importDeclaration([], t2.stringLiteral(file)));
}
}
}
}
}
};
// src/manifest-generator.ts
import { ElementType as ElementType2 } from "domelementtype";
import { DomHandler } from "domhandler";
import { Parser } from "htmlparser2";
// src/serializer.ts
import { ElementType } from "domelementtype";
var voidElements = /* @__PURE__ */ new Set([
"area",
"base",
"br",
"col",
"embed",
"hr",
"img",
"input",
"link",
"meta",
"param",
"source",
"track",
"wbr"
]);
function serialize(basePath, nodes, preload, parts) {
let curString = parts ? parts.pop() : "";
parts ??= [];
for (const node of nodes) {
switch (node.type) {
case ElementType.Tag:
case ElementType.Style:
case ElementType.Script: {
const tag = node;
const { name } = tag;
let urlAttr;
let isDedupe = 0;
switch (tag.tagName) {
case "script":
if (tag.attribs.src) {
if (curString) {
parts.push(curString);
curString = "";
}
isDedupe = parts.push(2 /* Dedupe */, tag.attribs.src, 0) - 1;
}
parts.push(`${curString}<${name}`, 0 /* AssetAttrs */);
curString = "";
urlAttr = "src";
break;
case "style":
parts.push(`${curString}<${name}`, 0 /* AssetAttrs */);
curString = "";
break;
case "link":
if (tag.attribs.href) {
if (curString) {
parts.push(curString);
curString = "";
}
isDedupe = parts.push(
2 /* Dedupe */,
[tag.attribs.rel || "", tag.attribs.href, tag.attribs.as].filter((it) => it != null).join("#"),
0
) - 1;
}
urlAttr = "href";
if (tag.attribs.rel === "stylesheet" || tag.attribs.rel === "modulepreload" || tag.attribs.as === "style" || tag.attribs.as === "script") {
parts.push(`${curString}<${name}`, 0 /* AssetAttrs */);
curString = "";
} else {
curString += `<${name}`;
}
break;
default:
curString += `<${name}`;
break;
}
for (const attr of tag.attributes) {
if (attr.value === "") {
curString += ` ${attr.name}`;
} else if (attr.name === urlAttr) {
const id = stripBasePath(basePath, attr.value).replace(/^\.\//, "");
if (tag.name === "script") {
preload.push(id);
}
curString += ` ${attr.name}="`;
parts.push(
curString,
1 /* PublicPath */,
id.replace(/"/g, "'") + '"'
);
curString = "";
} else {
curString += ` ${attr.name}="${attr.value.replace(/"/g, "'")}"`;
}
}
curString += ">";
if (tag.children.length) {
parts.push(curString);
serialize(basePath, tag.children, preload, parts);
curString = parts.pop();
}
if (!voidElements.has(name)) {
curString += `</${name}>`;
}
if (isDedupe) {
if (curString) {
parts.push(curString);
curString = "";
}
parts[isDedupe] = parts.length - isDedupe - 1;
}
break;
}
case ElementType.Text: {
const text = node.data;
if (!/^\s*$/.test(text)) {
curString += text;
}
break;
}
case ElementType.Comment:
curString += `<!--${node.data}-->`;
break;
}
}
if (curString) {
parts.push(curString);
}
return parts;
}
function stripBasePath(basePath, path7) {
if (path7.startsWith(basePath)) return path7.slice(basePath.length);
return path7;
}
// src/manifest-generator.ts
var MARKER_COMMENT = "MARKO_VITE";
function generateDocManifest(basePath, rawHtml) {
return new Promise((resolve, reject) => {
const parser = new Parser(
new DomHandler(function(err, dom) {
if (err) {
return reject(err);
}
const htmlChildren = dom.find(isElement).childNodes;
const preload = [];
const headPrepend = [];
const head = [];
const bodyPrepend = [];
const body = [];
splitNodesByMarker(
htmlChildren.find(
(node) => isElement(node) && node.tagName === "head"
).childNodes,
headPrepend,
head
);
splitNodesByMarker(
htmlChildren.find(
(node) => isElement(node) && node.tagName === "body"
).childNodes,
bodyPrepend,
body
);
resolve({
preload,
"head-prepend": serializeOrNull(basePath, headPrepend, preload),
head: serializeOrNull(basePath, head, preload),
"body-prepend": serializeOrNull(basePath, bodyPrepend, preload),
body: serializeOrNull(basePath, body, preload)
});
})
);
parser.write(rawHtml);
parser.end();
});
}
function generateInputDoc(entry) {
return `<!DOCTYPE html><html><head><!--${MARKER_COMMENT}--></head><body><!--${MARKER_COMMENT}--><script async type="module" src=${JSON.stringify(
entry
)}></script></body></html>`;
}
function serializeOrNull(basePath, nodes, preload) {
const result = serialize(basePath, nodes, preload);
if (result.length) {
return result;
}
return null;
}
function splitNodesByMarker(nodes, before, after) {
for (let i = 0; i < nodes.length; i++) {
let node = nodes[i];
if (node.data === MARKER_COMMENT) {
i++;
for (; i < nodes.length; i++) {
node = nodes[i];
after.push(node);
}
break;
}
before.push(node);
}
}
function isElement(node) {
return node.type === ElementType2.Tag;
}
// src/read-once-persisted-store.ts
import { promises as fs3 } from "fs";
import os from "os";
import path4 from "path";
var noop = () => {
};
var tmpFile = path4.join(os.tmpdir(), "marko-vite-storage.json");
var values = /* @__PURE__ */ new Map();
var loadedFromDisk;
var ReadOncePersistedStore = class {
constructor(uid) {
this.uid = uid;
}
write(value) {
values.set(this.uid, value);
}
async read() {
const { uid } = this;
if (values.has(uid)) {
const value = values.get(uid);
values.delete(uid);
return value;
}
if (loadedFromDisk === true) {
throw new Error(`Value for ${uid} could not be loaded.`);
}
await (loadedFromDisk ||= fs3.readFile(tmpFile, "utf-8").then(syncDataFromDisk).catch(finishLoadFromDisk));
return this.read();
}
};
function syncDataFromDisk(data) {
finishLoadFromDisk();
fs3.unlink(tmpFile).catch(noop);
for (const [k, v] of JSON.parse(data)) {
values.set(k, v);
}
}
function finishLoadFromDisk() {
loadedFromDisk = true;
}
process.once("beforeExit", (code) => {
if (code === 0 && values.size) {
fs3.writeFile(tmpFile, JSON.stringify([...values])).catch(noop);
}
});
// src/relative-assets-transform.ts
var attrSrc = /* @__PURE__ */ new Set(["src"]);
var attrHref = /* @__PURE__ */ new Set(["href"]);
var assetAttrsByTag = /* @__PURE__ */ new Map([
["audio", attrSrc],
["embed", attrSrc],
["iframe", attrSrc],
["img", /* @__PURE__ */ new Set(["src", "srcset"])],
["input", attrSrc],
["source", attrSrc],
["track", attrSrc],
["video", /* @__PURE__ */ new Set(["src", "poster"])],
["a", attrHref],
["area", attrHref],
["link", attrHref],
["object", /* @__PURE__ */ new Set(["data"])],
["body", /* @__PURE__ */ new Set(["background"])],
["script", /* @__PURE__ */ new Set(["src"])]
]);
var assetFileReg = /(?:^\..*\.(?:a?png|jpe?g|jfif|pipeg|pjp|gif|svg|ico|web[pm]|avif|mp4|ogg|mp3|wav|flac|aac|opus|woff2?|eot|[ot]tf|webmanifest|pdf|txt)(\?|$)|\?url\b)/;
function transform(tag, t3) {
const { name, attributes } = tag.node;
if (name.type !== "StringLiteral") {
return;
}
const assetAttrs = assetAttrsByTag.get(name.value);
if (!assetAttrs) {
return;
}
for (const attr of attributes) {
if (attr.type === "MarkoAttribute" && attr.value.type === "StringLiteral" && assetAttrs.has(attr.name)) {
const { value } = attr.value;
if (assetFileReg.test(value)) {
const importedId = tag.scope.generateUid(value);
attr.value = t3.identifier(importedId);
tag.hub.file.path.unshiftContainer(
"body",
t3.importDeclaration(
[t3.importDefaultSpecifier(t3.identifier(importedId))],
t3.stringLiteral(value)
)
);
}
}
}
}
// src/render-assets-runtime.ts
var renderAssetsRuntimeId = "\0marko-render-assets.mjs";
function getRenderAssetsRuntime(opts) {
return `${opts.basePathVar && opts.isBuild ? `const base = globalThis.${opts.basePathVar};
if (typeof base !== "string") throw new Error("${opts.basePathVar} must be defined when using basePathVar.");
if (!base.endsWith("/")) throw new Error("${opts.basePathVar} must end with a '/' when using basePathVar.");` : "const base = import.meta.env.BASE_URL;"}
export function getPrepend(g) {
return (
g.___viteRenderAssets("head-prepend") +
g.___viteRenderAssets("head") +
g.___viteRenderAssets("body-prepend")
);
}
export function getAppend(g) {
return (
g.___viteRenderAssets("body-prepend")
);
}
export function addAssets(g, newEntries) {
const entries = g.___viteEntries;
if (entries) {
g.___viteEntries = entries.concat(newEntries);
return true;
}
g.___viteEntries = newEntries;
g.___viteRenderAssets = renderAssets;
g.___viteInjectAttrs = g.cspNonce
? \` nonce="\${g.cspNonce.replace(/"/g, "'")}"\`
: "";
g.___viteSeenIds = new Set();
${opts.runtimeId ? `g.runtimeId = ${JSON.stringify(opts.runtimeId)};` : ""}
}
function renderAssets(slot) {
const entries = this.___viteEntries;
let html = "";
if (entries) {
const seenIds = this.___viteSeenIds;
const slotWrittenEntriesKey = \`___viteWrittenEntries-\${slot}\`;
const lastWrittenEntry = this[slotWrittenEntriesKey] || 0;
const writtenEntries = (this[slotWrittenEntriesKey] = entries.length);
${opts.basePathVar ? `if (!this.___flushedMBP && slot !== "head-prepend") {
this.___flushedMBP = true;
html += \`<script\${this.___viteInjectAttrs}>${opts.runtimeId ? `$mbp_${opts.runtimeId}` : "$mbp"}=\${JSON.stringify(base)}</script>\`
}` : ""}
for (let i = lastWrittenEntry; i < writtenEntries; i++) {
let entry = entries[i];
if (typeof entry === "string") {
entry = __MARKO_MANIFEST__[entry] || {};
}${opts.isBuild ? "" : ` else if (slot === "head") {
// In dev mode we have is a list entries of the top level modules that need to be imported.
// To avoid FOUC we will hide the page until all of these modules are loaded.
const { preload } = entry;
if (preload) {
html += \`<script class=marko-vite-preload async blocking=render type=module\${this.___viteInjectAttrs}>\`;
for (const id of preload) {
html += \`import \${JSON.stringify(base + id)};\`;
}
html += "document.querySelectorAll('.marko-vite-preload').forEach((el) => el.remove());";
html += "document.documentElement.style.visibility='';";
html +=
"if(document.documentElement.getAttribute('style')==='')document.documentElement.removeAttribute('style');";
html += \`</script><script class=marko-vite-preload\${this.___viteInjectAttrs}>document.documentElement.style.visibility='hidden'</script>\`;
}
}`}
const parts = entry[slot];
if (parts) {
for (let i = 0; i < parts.length; i++) {
const part = parts[i];
switch (part) {
case 0: /** InjectType.AssetAttrs */
html += this.___viteInjectAttrs;
break;
case 1: /** InjectType.PublicPath */
html += base;
break;
case 2: /** InjectType.Dedupe */ {
const id = parts[++i];
const skipParts = parts[++i];
if (seenIds.has(id)) {
i += skipParts;
} else {
seenIds.add(id);
}
break;
}
default:
html += part;
break;
}
}
}
}
}
return html;
}
`;
}
// src/render-assets-transform.ts
var render_assets_transform_default = (tag, t3) => {
if (tag.hub.file.markoOpts.markoViteLinked) {
const body = tag.get("body");
const tagName = tag.get("name").node.value;
body.unshiftContainer("body", renderAssetsCall(t3, `${tagName}-prepend`));
body.pushContainer("body", renderAssetsCall(t3, tagName));
}
};
function renderAssetsCall(t3, slot) {
return t3.markoPlaceholder(
t3.callExpression(
t3.memberExpression(
t3.identifier("$global"),
t3.identifier("___viteRenderAssets")
),
[t3.stringLiteral(slot)]
),
false
);
}
// src/server-entry-template.ts
import path5 from "path";
var server_entry_template_default = async (opts) => {
const fileNameStr = JSON.stringify(`./${path5.basename(opts.fileName)}`);
if (opts.tagsAPI) {
return `import Template from ${fileNameStr};
export * from ${fileNameStr};
import { addAssets, getPrepend, getAppend } from "${renderAssetsRuntimeId}";
static function flush($global, html) {
return getPrepend($global) + html + getAppend($global);
}
static function setFlush($global) {
$global.__flush__ = flush;
}
static const assets = [${opts.entryData.join(",")}];
<const/writeSync=addAssets($global, assets) || setFlush($global)/>
-- $!{writeSync && getPrepend($global)}
<Template ...input/>
-- $!{writeSync && getAppend($global)}
`;
}
return `import template from ${fileNameStr};
export * from ${fileNameStr};
import { addAssets, getPrepend, getAppend } from "${renderAssetsRuntimeId}";
static const assets = [${opts.entryData.join(",")}];
<if(addAssets($global, assets))>
$!{getPrepend($global)}
<\${template} ...input/>
$!{getAppend($global)}
</>
<else>
<__flush_here_and_after__>$!{getPrepend($global)}</>
<\${template} ...input/>
<init-components/>
<await-reorderer/>
<__flush_here_and_after__>$!{getAppend($global)}</>
</>
`;
};
// src/index.ts
var POSIX_SEP = "/";
var WINDOWS_SEP = "\\";
var TEMPLATE_ID_HASH_OPTS = { outputLength: 3 };
var normalizePath = path6.sep === WINDOWS_SEP ? (id) => id.replace(/\\/g, POSIX_SEP) : (id) => id;
var virtualFiles = /* @__PURE__ */ new Map();
var extReg = /\.[^.]+$/;
var queryReg = /\?marko-[^?]+$/;
var browserEntryQuery = "?marko-browser-entry";
var serverEntryQuery = "?marko-server-entry";
var virtualFileQuery = "?marko-virtual";
var browserQuery = "?marko-browser";
var markoExt = ".marko";
var htmlExt = ".html";
var resolveOpts = { skipSelf: true };
var configsByFileSystem = /* @__PURE__ */ new Map();
var cache = /* @__PURE__ */ new Map();
var babelCaller = {
name: "@marko/vite",
supportsStaticESM: true,
supportsDynamicImport: true,
supportsTopLevelAwait: true,
supportsExportNamespaceFrom: true
};
var optimizeKnownTemplatesForRoot = /* @__PURE__ */ new Map();
var registeredTagLib = false;
var cjsToEsm;
function noop2() {
}
function markoPlugin(opts = {}) {
let { linked = true } = opts;
let runtimeId;
let basePathVar;
let baseConfig;
let ssrConfig;
let ssrCjsConfig;
let domConfig;
let hydrateConfig;
const resolveVirtualDependency = (from, dep) => {
const normalizedFrom = normalizePath(from);
const query = `${virtualFileQuery}&id=${encodeURIComponent(dep.virtualPath)}`;
const id = normalizedFrom + query;
if (devServer) {
const prev = virtualFiles.get(id);
if (isDeferredPromise(prev)) {
prev.resolve(dep);
}
}
virtualFiles.set(id, dep);
return `./${path6.posix.basename(normalizedFrom) + query}`;
};
let root;
let rootResolveFile;
let devEntryFile;
let devEntryFilePosix;
let renderAssetsRuntimeCode;
let isTest = false;
let isBuild = false;
let isSSRBuild = false;
let devServer;
let serverManifest;
let basePath = "/";
let getMarkoAssetFns;
const entryIds = /* @__PURE__ */ new Set();
const cachedSources = /* @__PURE__ */ new Map();
const transformWatchFiles = /* @__PURE__ */ new Map();
const transformOptionalFiles = /* @__PURE__ */ new Map();
const store = new ReadOncePersistedStore(
`vite-marko${runtimeId ? `-${runtimeId}` : ""}`
);
const isTagsApi = /* @__PURE__ */ (() => {
let tagsAPI;
return () => {
if (tagsAPI === void 0) {
const translatorPackage = opts.translator || compiler2.globalConfig?.translator || "marko/translator";
if (/^@marko\/translator-(?:default|interop-class-tags)$/.test(
translatorPackage
)) {
tagsAPI = false;
} else {
try {
const require2 = createRequire(import.meta.url);
tagsAPI = require2(translatorPackage).preferAPI !== "class";
} catch {
tagsAPI = true;
}
}
}
return tagsAPI;
};
})();
return [
{
name: "marko-vite:pre",
enforce: "pre",
// Must be pre to allow us to resolve assets before vite.
async config(config, env) {
let optimize = env.mode === "production";
isTest = env.mode === "test";
isBuild = env.command === "build";
if (isTest) {
linked = false;
}
if ("MARKO_DEBUG" in process.env) {
optimize = process.env.MARKO_DEBUG === "false" || process.env.MARKO_DEBUG === "0";
} else {
process.env.MARKO_DEBUG = optimize ? "false" : "true";
}
runtimeId = opts.runtimeId;
basePathVar = opts.basePathVar;
if ("BASE_URL" in process.env && config.base == null) {
config.base = process.env.BASE_URL;
}
root = normalizePath(config.root || process.cwd());
rootResolveFile = path6.join(root, "_.js");
baseConfig = {
cache,
optimize,
runtimeId,
sourceMaps: true,
writeVersionComment: false,
optimizeKnownTemplates: optimize && linked ? getKnownTemplates(root) : void 0,
babelConfig: opts.babelConfig ? {
...opts.babelConfig,
caller: opts.babelConfig.caller ? {
name: "@marko/vite",
supportsStaticESM: true,
supportsDynamicImport: true,
supportsTopLevelAwait: true,
supportsExportNamespaceFrom: true,
...opts.babelConfig.caller
} : babelCaller
} : {
babelrc: false,
configFile: false,
browserslistConfigFile: false,
caller: babelCaller
}
};
if (linked) {
baseConfig.markoViteLinked = linked;
}
ssrConfig = {
...baseConfig,
resolveVirtualDependency,
output: "html"
};
domConfig = {
...baseConfig,
resolveVirtualDependency,
output: "dom"
};
hydrateConfig = {
...baseConfig,
resolveVirtualDependency,
output: "hydrate"
};
compiler2.configure(baseConfig);
devEntryFile = path6.join(root, "index.html");
devEntryFilePosix = normalizePath(devEntryFile);
isSSRBuild = isBuild && linked && Boolean(config.build.ssr);
renderAssetsRuntimeCode = getRenderAssetsRuntime({
isBuild,
basePathVar,
runtimeId
});
if (isTest) {
const { test } = config;
if (test.environment?.includes("dom")) {
config.resolve ??= {};
config.resolve.conditions ??= [];
config.resolve.conditions.push("browser");
test.deps ??= {};
test.deps.optimizer ??= {};
test.deps.optimizer.web ??= {};
test.deps.optimizer.web.enabled ??= true;
}
}
if (!registeredTagLib) {
registeredTagLib = true;
compiler2.taglib.register("@marko/vite", {
transform: glob_import_transform_default,
"<head>": { transformer: render_assets_transform_default },
"<body>": { transformer: render_assets_transform_default },
"<*>": { transformer: transform }
});
}
const optimizeDeps = config.optimizeDeps ??= {};
if (!isTest) {
optimizeDeps.entries ??= [
"**/*.marko",
"!**/__snapshots__/**",
`!**/__tests__/**`,
`!**/coverage/**`
];
}
const domDeps = compiler2.getRuntimeEntryFiles("dom", opts.translator);
optimizeDeps.include = optimizeDeps.include ? [...optimizeDeps.include, ...domDeps] : domDeps;
const optimizeExtensions = optimizeDeps.extensions ??= [];
optimizeExtensions.push(".marko");
const esbuildOptions = optimizeDeps.esbuildOptions ??= {};
const esbuildPlugins = esbuildOptions.plugins ??= [];
esbuildPlugins.push(esbuildPlugin(baseConfig));
const ssr = config.ssr ??= {};
const { noExternal } = ssr;
if (noExternal !== true) {
const noExternalReg = /\.marko$/;
if (noExternal) {
if (Array.isArray(noExternal)) {
ssr.noExternal = [...noExternal, noExternalReg];
} else {
ssr.noExternal = [noExternal, noExternalReg];
}
} else {
ssr.noExternal = noExternalReg;
}
}
if (isSSRBuild && !config.build?.rollupOptions?.output) {
config.build ??= {};
config.build.rollupOptions ??= {};
config.build.rollupOptions.output = {
chunkFileNames: `[name]-[hash].js`
};
}
if (isSSRBuild && !config.build?.commonjsOptions?.esmExternals) {
config.build ??= {};
config.build.commonjsOptions ??= {};
config.build.commonjsOptions.esmExternals = (id) => !isCJSModule(id, rootResolveFile);
}
if (basePathVar) {
config.experimental ??= {};
if (config.experimental.renderBuiltUrl) {
throw new Error(
"Cannot use @marko/vite `basePathVar` with Vite's `renderBuiltUrl` option."
);
}
const assetsDir = config.build?.assetsDir?.replace(/[/\\]$/, "") ?? "assets";
const assetsDirLen = assetsDir.length;
const assetsDirEnd = assetsDirLen + 1;
const trimAssertsDir = (fileName) => {
if (fileName.startsWith(assetsDir)) {
switch (fileName[assetsDirLen]) {
case POSIX_SEP:
case WINDOWS_SEP:
return fileName.slice(assetsDirEnd);
}
}
return fileName;
};
config.experimental.renderBuiltUrl = (fileName, { hostType, ssr: ssr2 }) => {
switch (hostType) {
case "html":
return trimAssertsDir(fileName);
case "js":
return {
runtime: `${ssr2 ? basePathVar : `$mbp${runtimeId ? `_${runtimeId}` : ""}`}+${JSON.stringify(trimAssertsDir(fileName))}`
};
default:
return { relative: true };
}
};
}
return {
resolve: {
alias: [
{
find: /^~(?!\/)/,
replacement: ""
}
]
}
};
},
configResolved(config) {
basePath = config.base;
ssrCjsConfig = {
...ssrConfig,
babelConfig: {
...ssrConfig.babelConfig,
plugins: (ssrConfig.babelConfig.plugins || []).concat(
plugin({
filter: isBuild ? void 0 : (path7) => !/^\./.test(path7)
})
)
}
};
getMarkoAssetFns = void 0;
for (const plugin2 of config.plugins) {
const fn = plugin2.api?.getMarkoAssetCodeForEntry;
if (fn) {
if (getMarkoAssetFns) {
getMarkoAssetFns.push(fn);
} else {
getMarkoAssetFns = [fn];
}
}
}
},
configureServer(_server) {
ssrConfig.hot = domConfig.hot = true;
devServer = _server;
devServer.watcher.on("all", (type, originalFileName) => {
const fileName = normalizePath(originalFileName);
cachedSources.delete(fileName);
if (type === "unlink") {
entryIds.delete(fileName);
transformWatchFiles.delete(fileName);
transformOptionalFiles.delete(fileName);
}
for (const [id, files] of transformWatchFiles) {
if (anyMatch(files, fileName)) {
devServer.watcher.emit("change", id);
}
}
if (type === "add" || type === "unlink") {
for (const [id, files] of transformOptionalFiles) {
if (anyMatch(files, fileName)) {
devServer.watcher.emit("change", id);
}
}
}
});
},
handleHotUpdate(ctx) {
compiler2.taglib.clearCaches();
baseConfig.cache.clear();
optimizeKnownTemplatesForRoot.clear();
for (const [, cache2] of configsByFileSystem) {
cache2.clear();
}
for (const mod of ctx.modules) {
if (mod.id && virtualFiles.has(mod.id)) {
virtualFiles.set(mod.id, createDeferredPromise());
}
}
},
async options(inputOptions) {
if (isBuild && linked && !isSSRBuild) {
try {
serverManifest = await store.read();
inputOptions.input = toHTMLEntries(root, serverManifest.entries);
for (const entry in serverManifest.entrySources) {
const id = normalizePath(path6.resolve(root, entry));
entryIds.add(id);
cachedSources.set(id, serverManifest.entrySources[entry]);
}
} catch (err) {
this.error(
`You must run the "ssr" build before the "browser" build.`
);
}
if (isEmpty(inputOptions.input)) {
this.error("No Marko files were found when compiling the server.");
}
}
},
async buildStart() {
if (isBuild && linked && !isSSRBuild) {
for (const assetId of serverManifest.ssrAssetIds) {
this.load({
id: normalizePath(path6.resolve(root, assetId)),
resolveDependencies: false
}).catch(noop2);
}
}
},
async resolveId(importee, importer, importOpts, ssr = importOpts.ssr) {
if (virtualFiles.has(importee)) {
return importee;
}
if (importee === renderAssetsRuntimeId) {
return { id: renderAssetsRuntimeId };
}
let importeeQuery = getMarkoQuery(importee);
if (importeeQuery) {
importee = importee.slice(0, -importeeQuery.length);
} else if (!importOpts.scan) {
if (ssr && linked && importer && importer[0] !== "\0" && (importer !== devEntryFile || normalizePath(importer) !== devEntryFilePosix) && // Vite tries to resolve against an `index.html` in some cases, we ignore it here.
isMarkoFile(importee) && !isMarkoFile(importer.replace(queryReg, ""))) {
importeeQuery = serverEntryQuery;
} else if (!ssr && isBuild && importer && isMarkoFile(importee) && this.getModuleInfo(importer)?.isEntry) {
importeeQuery = browserEntryQuery;
} else if (!isBuild && linked && !ssr && !importeeQuery && isMarkoFile(importee)) {
importeeQuery = browserQuery;
}
}
if (importeeQuery) {
const resolved = importee[0] === "." ? {
id: normalizePath(
importer ? path6.resolve(importer, "..", importee) : path6.resolve(root, importee)
)
} : await this.resolve(importee, importer, resolveOpts);
if (resolved) {
resolved.id += importeeQuery;
}
return resolved;
}
if (importer) {
const importerQuery = getMarkoQuery(importer);
if (importerQuery) {
importer = importer.slice(0, -importerQuery.length);
if (importee[0] === ".") {
const resolved = normalizePath(
path6.resolve(importer, "..", importee)
);
if (resolved === normalizePath(importer)) return resolved;
}
return this.resolve(importee, importer, resolveOpts);
}
}
return null;
},
async load(rawId) {
const id = stripVersionAndTimeStamp(rawId);
if (id === renderAssetsRuntimeId) {
return renderAssetsRuntimeCode;
}
const query = getMarkoQuery(id);
switch (query) {
case serverEntryQuery: {
entryIds.add(id.slice(0, -query.length));
return null;
}
case browserEntryQuery:
case browserQuery: {
return cachedSources.get(id.slice(0, -query.length)) || null;
}
}
return virtualFiles.get(id) || null;
},
async transform(source, rawId, ssr) {
let id = stripVersionAndTimeStamp(rawId);
const info = isBuild ? this.getModuleInfo(id) : void 0;
const arcSourceId = info?.meta.arcSourceId;
if (arcSourceId) {
const arcFlagSet = info.meta.arcFlagSet;
id = arcFlagSet ? arcSourceId.replace(extReg, `[${arcFlagSet.join("+")}]$&`) : arcSourceId;
}
const isSSR = typeof ssr === "object" ? ssr.ssr : ssr;
const query = getMarkoQuery(id);
if (query && !query.startsWith(virtualFileQuery)) {
id = id.slice(0, -query.length);
if (query === serverEntryQuery) {
const fileName = id;
let mainEntryData;
id = `${id.slice(0, -markoExt.length)}.entry.marko`;
cachedSources.set(fileName, source);
if (isBuild) {
const relativeFileName = normalizePath(
path6.relative(root, fileName)
);
const entryId = toEntryId(relativeFileName);
serverManifest ??= {
entries: {},
entrySources: {},
chunksNeedingAssets: [],
ssrAssetIds: []
};
serverManifest.entries[entryId] = relativeFileName;
serverManifest.entrySources[relativeFileName] = source;
mainEntryData = JSON.stringify(entryId);
} else {
mainEntryData = JSON.stringify(
await generateDocManifest(
basePath,
await devServer.transformIndexHtml(
"/",
generateInputDoc(
fileNameToURL(fileName, root) + browserEntryQuery
)
)
)
);
}
const entryData = [mainEntryData];
if (getMarkoAssetFns) {
for (const getMarkoAsset of getMarkoAssetFns) {
const asset = getMarkoAsset(fileName);
if (asset) {
entryData.push(asset);
}
}
}
source = await server_entry_template_default({
fileName,
entryData,
runtimeId,
basePathVar: isBuild ? basePathVar : void 0,
tagsAPI: isTagsApi()
});
}
}
if (!isMarkoFile(id)) {
if (!isBuild) {
const ext = path6.extname(id);
if (ext === ".cjs" || ext === ".js" && isCJSModule(id, rootResolveFile)) {
if (cjsToEsm === void 0) {
try {
cjsToEsm = (await import("@chialab/cjs-to-esm")).transform;
} catch {
cjsToEsm = null;
return null;
}
}
if (cjsToEsm) {
try {
return await cjsToEsm(source);
} catch {
return null;
}
}
}
}
return null;
}
if (isSSR) {
if (linked) {
cachedSources.set(id, source);
}
if (!query && isCJSModule(id, rootResolveFile)) {
if (isBuild) {
const { code: code2, map: map2, meta: meta2 } = await compiler2.compile(
source,
id,
getConfigForFileSystem(info, ssrCjsConfig)
);
return {
code: code2,
map: map2,
meta: { arcSourceCode: source, arcScanIds: meta2.analyzedTags }
};
}
}
}
const compiled = await compiler2.compile(
source,
id,
getConfigForFileSystem(
info,
isSSR ? isCJSModule(id, rootResolveFile) ? ssrCjsConfig : ssrConfig : query === browserEntryQuery ? hydrateConfig : domConfig
)
);
const { map, meta } = compiled;
let { code } = compiled;
if (query !== browserEntryQuery && devServer && !isTagsApi()) {
code += `
if (import.meta.hot) import.meta.hot.accept(() => {});`;
}
if (devServer) {
const templateName = getPosixBasenameWithoutExt(id);
const optionalFilePrefix = path6.dirname(id) + path6.sep + (templateName === "index" ? "" : `${templateName}.`);
for (const file of meta.watchFiles) {
this.addWatchFile(file);
}
transformOptionalFiles.set(id, [
`${optionalFilePrefix}style.*`,
`${optionalFilePrefix}component.*`,
`${optionalFilePrefix}component-browser.*`,
`${optionalFilePrefix}marko-tag.json`
]);
transformWatchFiles.set(id, meta.watchFiles);
}
return {
code,
map,
meta: isBuild ? { arcSourceCode: source, arcScanIds: meta.analyzedTags } : void 0
};
}
},
{
name: "marko-vite:post",
apply: "build",
enforce: "post",
// We use a "post" plugin to allow us to read the final generated `.html` from vite.
async generateBundle(outputOptions, bundle, isWrite) {
if (!linked) {
return;
}
if (!isWrite) {
this.error(
`Linked builds are currently only supported when in "write" mode.`
);
}
if (!serverManifest) {
this.error(
"No Marko files were found when bundling the server in linked mode."
);
}
if (isSSRBuild) {
const dir = outputOptions.dir ? path6.resolve(outputOptions.dir) : path6.resolve(outputOptions.file, "..");
for (const fileName in bundle) {
const chunk = bundle[fileName];
if (chunk.type === "chunk") {
if (chunk.moduleIds.includes(renderAssetsRuntimeId)) {
serverManifest.chunksNeedingAssets.push(
path6.resolve(dir, fileName)
);
}
}
}
serverManifest.ssrAssetIds = [];
for (const moduleId of this.getModuleIds()) {
if (moduleId.startsWith(root)) {
const module = this.getModuleInfo(moduleId);
if (module?.meta["vite:asset"]) {
serverManifest.ssrAssetIds.push(
"." + moduleId.slice(root.length)
);
}
}
}
store.write(serverManifest);
} else {
const browserManifest = {};
for (const entryId in serverManifest.entries) {
const fileName = serverManifest.entries[entryId];
const chunkId = fileName + htmlExt;
const chunk = bundle[chunkId];
if (chunk?.type === "asset") {
browserManifest[entryId] = {
...await generateDocManifest(
basePath,
chunk.source.toString()
),
preload: void 0
// clear out preload for prod builds.
};
delete bundle[chunkId];
} else {
this.error(
`Marko template had unexpected output from vite, ${fileName}`
);
}
}
const manifestStr = `;var __MARKO_MANIFEST__=${JSON.stringify(
browserManifest
)};
`;
for (const fileName of serverManifest.chunksNeedingAssets) {
await fs4.promises.appendFile(fileName, manifestStr);
}
}
}
}
];
}
function getMarkoQuery(id) {
return queryReg.exec(id)?.[0] || "";
}
function isMarkoFile(id) {
return id.endsWith(markoExt);
}
function toHTMLEntries(root, serverEntries) {
const result = [];
for (const id in serverEntries) {
const markoFile = normalizePath(path6.join(root, serverEntries[id]));
const htmlFile = markoFile + htmlExt;
virtualFiles.set(htmlFile, {
code: generateInputDoc(markoFile + browserEntryQuery)
});
result.push(htmlFile);
}
return result;
}
function toEntryId(id) {
const lastSepIndex = id.lastIndexOf(POSIX_SEP);
let name = id.slice(lastSepIndex + 1, id.indexOf(".", lastSepIndex));
if (name === "index" || name === "template") {
name = id.slice(
id.lastIndexOf(POSIX_SEP, lastSepIndex - 1) + 1,
lastSepIndex
);
}
return `${name}_${crypto.createHash("shake256", TEMPLATE_ID_HASH_OPTS).update(id).digest("base64url")}`;
}
function fileNameToURL(fileName, root) {
const relativeURL = normalizePath(path6.relative(root, fileName));
if (relativeURL[0] === ".") {
throw new Error(
"@marko/vite: Entry templates must exist under the current root directory."
);
}
return `/${relativeURL}`;
}
function getPosixBasenameWithoutExt(file) {
const baseStart = file.lastIndexOf(POSIX_SEP) + 1;
const extStart = file.indexOf(".", baseStart + 1);
return file.slice(baseStart, extStart);
}
function createDeferredPromise() {
let resolve;
let reject;
const promise = new Promise((res, rej) => {
resolve = res;
reject = rej;
});
promise.resolve = resolve;
promise.reject = reject;
return promise;
}
function isDeferredPromise(obj) {
return typeof obj?.then === "function";
}
function isEmpty(obj) {
for (const _ in obj) {
return false;
}
return true;
}
function stripVersionAndTimeStamp(id) {
const queryStart = id.indexOf("?");
if (queryStart === -1) return id;
const url = id.slice(0, queryStart);
const query = id.slice(queryStart + 1).replace(/(?:^|[&])[vt]=[^&]+/g, "");
if (query) return `${url}?${query}`;
return url;
}
function getConfigForFileSystem(info, config) {
const fileSystem = info?.meta.arcFS;
if (!fileSystem) return config;
let configsForFileSystem = configsByFileSystem.get(fileSystem);
if (!configsForFileSystem) {
configsForFileSystem = /* @__PURE__ */ new Map();
configsByFileSystem.set(fileSystem, configsForFileSystem);
}
let configForFileSystem = configsForFileSystem.get(config);
if (!configForFileSystem) {
configForFileSystem = {
...config,
fileSystem,
cache: configsForFileSystem
};
configsForFileSystem.set(config, configForFileSystem);
}
return configForFileSystem;
}
function getKnownTemplates(cwd) {
let knownTemplates = optimizeKnownTemplatesForRoot.get(cwd);
if (!knownTemplates) {
optimizeKnownTemplatesForRoot.set(
cwd,
knownTemplates = glob2.globSync(