UNPKG

@marko/vite

Version:
1,507 lines (1,484 loc) 50.5 kB
// 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, "&#39;") + '"' ); curString = ""; } else { curString += ` ${attr.name}="${attr.value.replace(/"/g, "&#39;")}"`; } } 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, "&#39;")}"\` : ""; 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(