arc-vite
Version:
Declaratively bundle and execute code specific to your users ARC and Vite.
1,241 lines (1,222 loc) • 39.2 kB
JavaScript
// src/plugins/build-ssr.ts
import path4 from "path";
// src/utils/file-types.ts
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)(\?|$)/;
var globalCSSFileReg = /(?<!\.module)\.(css|less|sass|scss|styl|stylus|pcss|postcss|sss)(?:$|\?)/;
function isAssetFile(id) {
return assetFileReg.test(id);
}
function isGlobalCSSFile(id) {
return globalCSSFileReg.test(id);
}
// src/utils/index-to-id.ts
var ID_START_CHARS = "hjkmoquxzABCDEFGHIJKLNPQRTUVWXYZ$_";
var ID_START_CHARS_LEN = ID_START_CHARS.length;
var ID_CHARS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789$_";
var ID_CHARS_LEN = ID_CHARS.length;
function indexToId(i) {
let mod = i % ID_START_CHARS_LEN;
let id = ID_START_CHARS[mod];
i = (i - mod) / ID_START_CHARS_LEN;
while (i > 0) {
mod = i % ID_CHARS_LEN;
id += ID_CHARS[mod];
i = (i - mod) / ID_CHARS_LEN;
}
return id;
}
// src/utils/matches.ts
import fs from "fs";
import path from "path";
import Resolver from "arc-resolver";
// src/utils/flags.ts
function createFlagSets(flags) {
const flagSets = [[]];
for (const group of flags) {
const { length } = flagSets;
for (const flag of Array.isArray(group) ? group : [group]) {
for (let i = length; i--; ) {
flagSets.push(flagSets[i].concat(flag));
}
}
}
return normalizeFlagSets(flagSets);
}
function hasFlagSet(flagSets, flagSet) {
let max = flagSets.length;
let pos = 0;
while (pos < max) {
const mid = pos + max >>> 1;
const compared = compareFlagSets(flagSets[mid], flagSet);
if (compared === 0)
return true;
if (compared > 0)
max = mid;
else
pos = mid + 1;
}
return false;
}
function hasFlag(flagSet, flag) {
let max = flagSet.length;
let pos = 0;
while (pos < max) {
const mid = pos + max >>> 1;
const compared = compareFlags(flagSet[mid], flag);
if (compared === 0)
return true;
if (compared > 0)
max = mid;
else
pos = mid + 1;
}
return false;
}
function hasFlags(flagSet, flags) {
for (const flag of flags) {
if (!hasFlag(flagSet, flag)) {
return false;
}
}
return true;
}
function compareFlagSets(a, b) {
const aLen = a.length;
const bLen = b.length;
if (aLen === bLen) {
for (let i = aLen; i--; ) {
const compared = compareFlags(a[i], b[i]);
if (compared !== 0)
return compared;
}
}
return bLen - aLen;
}
function normalizeFlagSets(rawFlagSets) {
if (!rawFlagSets.length)
return [[]];
const sortedFlagSets = rawFlagSets.map(normalizeFlagSet).sort(compareFlagSets);
let prev = sortedFlagSets[0];
const uniqueFlagSets = [prev];
for (let i = 1; i < sortedFlagSets.length; i++) {
if (compareFlagSets(prev, sortedFlagSets[i]) !== 0) {
uniqueFlagSets.push(prev = sortedFlagSets[i]);
}
}
if (prev.length !== 0) {
uniqueFlagSets.push([]);
}
return uniqueFlagSets;
}
function normalizeFlagSet(flags) {
return [...new Set(flags)].sort();
}
function compareFlaggedObject(a, b) {
return compareFlagSets(a.flags, b.flags);
}
function compareFlags(a, b) {
return a > b ? 1 : a < b ? -1 : 0;
}
// src/utils/matches.ts
var resolver = new Resolver(fs);
var hasQuery = /\?.*$/;
function getMatches(id, flagSets) {
if (hasQuery.test(id) || !path.isAbsolute(id))
return;
const raw = tryGetRawMatches(id);
if (!raw)
return;
let i = raw.length - 1;
if (i) {
const defaultMatch = raw[i].value;
let alternates;
for (; i--; ) {
const match = normalizeMatch(raw[i]);
if (hasFlagSet(flagSets, match.flags)) {
if (alternates) {
alternates.push(match);
} else {
alternates = [match];
}
}
}
if (alternates) {
return {
default: defaultMatch,
alternates: alternates.sort(compareFlaggedObject)
};
}
}
}
function clearCache() {
resolver.clearCache();
}
function tryGetRawMatches(id) {
try {
return resolver.getMatchesSync(id).raw;
} catch {
}
}
function normalizeMatch(match) {
return {
flags: normalizeFlagSet(match.flags),
value: match.value
};
}
// src/utils/read-once-persisted-store.ts
import { promises as fs2 } from "fs";
import os from "os";
import path2 from "path";
var noop = () => {
};
var tmpFile = path2.join(os.tmpdir(), "vite-arc-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 ||= fs2.readFile(tmpFile, "utf-8").then(syncDataFromDisk).catch(finishLoadFromDisk));
return this.read();
}
};
function syncDataFromDisk(data) {
finishLoadFromDisk();
fs2.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) {
fs2.writeFile(tmpFile, JSON.stringify([...values])).catch(noop);
}
});
// src/utils/options.ts
function getInternalPluginOptions(options) {
const runtimeId = `arc${options.runtimeId ? `_${options.runtimeId}` : ""}`;
const { FLAGS } = process.env;
return {
runtimeId,
store: new ReadOncePersistedStore(`vite-${runtimeId}`),
forceFlagSet: FLAGS === void 0 ? void 0 : FLAGS === "" ? [] : normalizeFlagSet(FLAGS.split(".")),
flagSets: "flags" in options ? createFlagSets(options.flags) : normalizeFlagSets(options.flagSets)
};
}
// src/utils/to-posix.ts
import path3 from "path";
var POSIX_SEP = "/";
var WINDOWS_SEP = "\\";
var toPosix = path3.sep === WINDOWS_SEP ? (id) => id.replace(/\\/g, POSIX_SEP) : (id) => id;
// src/plugins/build-ssr.ts
var virtualArcServerModuleId = "\0arc-server-virtual";
var arcPrefix = "\0arc-";
var arcJsSuffix = ".mjs";
var arcProxyPrefix = `${arcPrefix}proxy:`;
function pluginBuildSSR({
store,
flagSets,
forceFlagSet
}) {
let root;
let proxyModuleId = 0;
let metaForProxy = /* @__PURE__ */ new Map();
return {
name: "arc-vite:build-ssr",
enforce: "pre",
api: {
getMarkoAssetCodeForEntry(id) {
return `__ARC_ASSETS__(${JSON.stringify(
toPosix(path4.relative(root, id))
)})`;
}
},
apply(config, { command }) {
return command === "build" && !!config.build?.ssr;
},
configResolved(config) {
root = config.root;
},
async resolveId(source, importer, options) {
if (importer) {
switch (source) {
case "arc-server":
return importer === virtualArcServerModuleId ? this.resolve(source, void 0, options) : { id: virtualArcServerModuleId };
case "arc-server/proxy":
return null;
}
if (isArcId(source)) {
return source;
}
if (isArcId(importer)) {
return source;
}
const resolved = await this.resolve(source, importer, {
...options,
skipSelf: true
});
if (resolved && !resolved.external) {
const { id } = resolved;
const matches = getMatches(id, flagSets);
if (matches) {
const proxyId = nextProxyId();
metaForProxy.set(proxyId, { resolved, matches });
return proxyId;
}
}
return resolved;
}
return null;
},
async load(id) {
if (id === virtualArcServerModuleId) {
return {
code: `import * as arc from "arc-server";
export * from "arc-server";
globalThis.__ARC_FLAGS__ = arc.getFlags;
${forceFlagSet ? `const forcedFlags = {${forceFlagSet.map(
(name) => `${JSON.stringify(name)}:true`
)}};
export function setFlags() { return arc.setFlags(forcedFlags); }
export function withFlags(_, fn) { return arc.withFlags(forcedFlags, fn); }` : ""}
export function getAssets(entry, { base = import.meta.env.BASE_URL, injectAttrs = "" } = {}) {
const manifest = __ARC_ASSETS__(entry);
return {
"head-prepend": partsToString(manifest["head-prepend"], base, injectAttrs),
head: partsToString(manifest["head"], base, injectAttrs),
"body-prepend": partsToString(manifest["body-prepend"], base, injectAttrs),
body: partsToString(manifest["body"], base, injectAttrs)
};
};
function partsToString(parts, base, injectAttrs) {
if (!parts) return;
let html = "";
for (const part of parts) {
html += part === 0 ? injectAttrs : part === 1 ? base : part;
}
return html;
}
`,
moduleSideEffects: true
};
}
if (isArcProxyId(id)) {
const { resolved, matches } = metaForProxy.get(id);
let code = "";
if (isGlobalCSSFile(resolved.id)) {
for (const { value } of matches.alternates) {
code += `import ${JSON.stringify(value)};
`;
}
code += `import ${JSON.stringify(matches.default)};
`;
return {
code,
moduleSideEffects: "no-treeshake"
};
}
let matchCode = "";
let matchCodeSep = "";
let i = 0;
for (const { flags, value } of matches.alternates) {
const adaptedImportId = `_${indexToId(i++)}`;
code += `import * as ${adaptedImportId} from ${JSON.stringify(
value
)};
`;
matchCode += matchCodeSep + flags.map((flag) => `f.${flag}`).join("&&") + "?" + adaptedImportId;
matchCodeSep = ":";
}
const defaultId = `_${indexToId(i)}`;
code += `import * as ${defaultId} from ${JSON.stringify(
matches.default
)};
`;
matchCode += `:${defaultId}`;
let syntheticNamedExports = false;
let hasNamedExports = false;
let hasDefaultExport = false;
if (isAssetFile(resolved.id)) {
hasDefaultExport = true;
} else {
let info = this.getModuleInfo(resolved.id);
if (!info?.ast) {
info = await this.load(resolved);
}
if (info.exports) {
hasDefaultExport = info.hasDefaultExport === true;
hasNamedExports = info.exports.length >= (hasDefaultExport ? 2 : 1);
}
}
if (hasNamedExports || hasDefaultExport) {
const proxyCode = `/*@__PURE__*/createAdaptiveProxy({default:${defaultId},match(f){return ${matchCode}}})`;
code += `import createAdaptiveProxy from "arc-server/proxy";
`;
if (hasNamedExports) {
syntheticNamedExports = "_";
code += `export const _ = ${proxyCode};
`;
if (hasDefaultExport) {
code += "export default _.default;\n";
}
} else {
code += `export default ${proxyCode}.default;
`;
}
} else {
code += "export {};\n";
}
return {
code,
syntheticNamedExports,
moduleSideEffects: "no-treeshake"
};
}
return null;
},
closeBundle() {
clearCache();
proxyModuleId = 0;
metaForProxy = /* @__PURE__ */ new Map();
},
generateBundle(outputOptions, bundle, isWrite) {
if (!isWrite) {
this.error("arc-vite-build-ssr requires write=true");
}
const serverEntryFiles = [];
const cwd2 = process.cwd();
const dir = outputOptions.dir ? path4.resolve(outputOptions.dir) : path4.resolve(outputOptions.file, "..");
for (const fileName in bundle) {
const chunk = bundle[fileName];
if (chunk.type === "chunk" && chunk.isEntry && (chunk.imports.includes("arc-server") || chunk.moduleIds.includes(virtualArcServerModuleId))) {
serverEntryFiles.push(
path4.relative(cwd2, path4.resolve(dir, fileName))
);
}
}
if (!serverEntryFiles.length) {
this.error("arc-vite: the server code did not import 'arc-server'");
}
store.write({ serverEntryFiles });
}
};
function nextProxyId() {
return arcProxyPrefix + (proxyModuleId++).toString(36) + arcJsSuffix;
}
}
function isArcId(id) {
return id.startsWith(arcPrefix);
}
function isArcProxyId(id) {
return id.startsWith(arcProxyPrefix);
}
// src/plugins/build-web.ts
import { promises as fs3 } from "fs";
import path5 from "path";
// src/utils/filename-encoding.ts
import { Buffer } from "buffer";
var cwd = process.cwd();
var encodeCwdReg = new RegExp(`~|${escapeReg(cwd)}`, "g");
var decodeCwdReg = /_?~/g;
function encodeFileName(str) {
return Buffer.from(
str.replace(encodeCwdReg, encodeReplace),
"utf-8"
).toString("base64url");
}
function decodeFileName(str) {
return Buffer.from(str, "base64").toString("utf-8").replace(decodeCwdReg, decodeReplace);
}
function encodeReplace(match) {
if (match === "~") {
return "_~";
}
return "~";
}
function decodeReplace(match) {
if (match === "~") {
return cwd;
}
return "~";
}
function escapeReg(value) {
return value.replace(/[|\\{}()[\]^$+*?.]/g, "\\$&").replace(/-/g, "\\x2d");
}
// src/utils/manifest.ts
import { parseDocument, ElementType, DomUtils } from "htmlparser2";
var { isComment, isTag } = DomUtils;
var markerComment = "ARC_VITE";
var voidElements = /* @__PURE__ */ new Set([
"area",
"base",
"br",
"col",
"embed",
"hr",
"img",
"input",
"link",
"meta",
"param",
"source",
"track",
"wbr"
]);
function generatManifest(basePath, html) {
const dom = parseDocument(html);
const headPrepend = [];
const head = [];
const bodyPrepend = [];
const body = [];
for (const node of dom.childNodes) {
if (isTag(node) && node.tagName === "html") {
for (const child of node.childNodes) {
if (isTag(child)) {
switch (child.tagName) {
case "head":
splitNodesByMarker(child.childNodes, headPrepend, head);
break;
case "body":
splitNodesByMarker(child.childNodes, bodyPrepend, body);
break;
}
}
}
}
}
return {
"head-prepend": serializeOrUndefined(basePath, headPrepend),
head: serializeOrUndefined(basePath, head),
"body-prepend": serializeOrUndefined(basePath, bodyPrepend),
body: serializeOrUndefined(basePath, body)
};
}
function generateHTML(code) {
return `<!DOCTYPE html><html><head><!--${markerComment}--></head><body><!--${markerComment}--><script async type="module">${code}</script></body></html>`;
}
function serialize(basePath, nodes, 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;
curString += `<${name}`;
switch (tag.tagName) {
case "script":
parts.push(curString, 0 /* AssetAttrs */);
urlAttr = "src";
curString = "";
break;
case "style":
parts.push(curString, 0 /* AssetAttrs */);
curString = "";
break;
case "link":
urlAttr = "href";
if (tag.attribs.rel === "stylesheet" || tag.attribs.rel === "modulepreload" || tag.attribs.as === "style" || tag.attribs.as === "script") {
parts.push(curString, 0 /* AssetAttrs */);
curString = "";
}
break;
}
for (const attr of tag.attributes) {
if (attr.value === "") {
curString += ` ${attr.name}`;
} else if (attr.name === urlAttr) {
curString += ` ${attr.name}="`;
parts.push(
curString,
1 /* PublicPath */,
stripBasePath(basePath, attr.value).replace(/^\.\//, "").replace(/"/g, "'") + '"'
);
curString = "";
} else {
curString += ` ${attr.name}="${attr.value.replace(/"/g, "'")}"`;
}
}
curString += ">";
if (tag.children.length) {
parts.push(curString);
serialize(basePath, tag.children, parts);
curString = parts.pop();
}
if (!voidElements.has(name)) {
curString += `</${name}>`;
}
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 serializeOrUndefined(basePath, nodes) {
const result = serialize(basePath, nodes);
if (result.length) {
return result;
}
}
function splitNodesByMarker(nodes, before, after) {
for (let i = 0; i < nodes.length; i++) {
let node = nodes[i];
if (isComment(node) && node.data === markerComment) {
i++;
for (; i < nodes.length; i++) {
node = nodes[i];
after.push(node);
}
break;
}
before.push(node);
}
}
function stripBasePath(basePath, path7) {
if (path7.startsWith(basePath))
return path7.slice(basePath.length);
return path7;
}
// src/utils/prepare-arc-entry-html.ts
import toHTML from "dom-serializer";
import { Element, Text } from "domhandler";
import { parseDocument as parseDocument2, DomUtils as DomUtils2, ElementType as ElementType2 } from "htmlparser2";
var { isTag: isTag2, filter, appendChild, prepend } = DomUtils2;
var parserOptions = { decodeEntities: false, encodeEntities: false };
var emptyScriptReg = /^(?:[\s;]+|\/\/[^\n]*|\/\*[\s\S]*?\*\/)*$/;
function prepareArcEntryHTML(runtimeId, html, renderAssetURL, originalChunk, adaptedChunk) {
const originalChunkURL = renderAssetURL(originalChunk.fileName);
if (emptyScriptReg.test(adaptedChunk.code)) {
return [
{
tag: "script",
attrs: {
type: "module",
async: true,
crossorigin: true,
src: originalChunkURL
}
}
];
}
const dom = parseDocument2(html, parserOptions);
const originalChunkIsEmpty = emptyScriptReg.test(originalChunk.code);
const adaptedChunkURL = renderAssetURL(adaptedChunk.fileName);
for (const script of filter(isModule, dom)) {
if (script.attribs.src === adaptedChunkURL) {
if (originalChunkIsEmpty) {
prepend(
script,
new Element(
"script",
{},
[new Text(`${runtimeId}={}`)],
ElementType2.Script
)
);
} else {
delete script.attribs.src;
prepend(
script,
new Element(
"script",
{},
[new Text(`${runtimeId}={}`)],
ElementType2.Script
)
);
appendChild(
script,
new Text(
`import ${JSON.stringify(adaptedChunkURL)}
import ${JSON.stringify(
originalChunkURL
)}`
)
);
}
}
}
return toHTML(dom, parserOptions);
}
function isModule(node) {
return isTag2(node) && node.tagName === "script" && node.attribs.type === "module" && !!node.attribs.src;
}
// src/utils/strip-entry-script.ts
import toHTML2 from "dom-serializer";
import { parseDocument as parseDocument3, DomUtils as DomUtils3 } from "htmlparser2";
var { isTag: isTag3, removeElement, filter: filter2 } = DomUtils3;
function stripEntryScript(entryScriptURL, html) {
const dom = parseDocument3(html);
for (const script of filter2(isModule2, dom)) {
if (script.attribs.src === entryScriptURL) {
removeElement(script);
}
}
return toHTML2(dom);
}
function isModule2(node) {
return isTag3(node) && node.tagName === "script" && node.attribs.type === "module" && !!node.attribs.src;
}
// src/plugins/build-web.ts
var arcPrefix2 = "\0arc-";
var arcJsSuffix2 = ".mjs";
var arcInitPrefix = `${arcPrefix2}init:`;
var arcProxyPrefix2 = `${arcPrefix2}proxy:`;
var arcChunkFileNameReg = /(.+)\.arc(?:\.(.+))?\.html$/;
function pluginBuildWeb({
runtimeId,
flagSets,
store
}) {
const apply = (config, { command }) => command === "build" && !config.build?.ssr;
let globalIds = /* @__PURE__ */ new Map();
let metaForProxy = /* @__PURE__ */ new Map();
let adaptiveImporters = /* @__PURE__ */ new Map();
let bindingsByAdaptiveId = /* @__PURE__ */ new Map();
let metaForAdaptiveChunk = /* @__PURE__ */ new Map();
let proxyModuleId = 0;
let initModuleId = 0;
let basePath = "/";
let resolveAssetURL = (fileName) => basePath + fileName;
return [
{
name: "arc-vite:build-web",
enforce: "pre",
apply,
configResolved(config) {
basePath = config.base;
const { renderBuiltUrl: originalRenderBuiltURL } = config.experimental;
if (originalRenderBuiltURL) {
resolveAssetURL = (fileName, from) => {
const url = originalRenderBuiltURL(fileName, {
ssr: false,
type: "asset",
hostId: from,
hostType: "html"
});
if (typeof url !== "string") {
throw new Error(
`renderBuiltURL must return a string for html assets`
);
}
return url;
};
}
},
closeBundle() {
proxyModuleId = initModuleId = 0;
globalIds = /* @__PURE__ */ new Map();
adaptiveImporters = /* @__PURE__ */ new Map();
bindingsByAdaptiveId = /* @__PURE__ */ new Map();
metaForAdaptiveChunk = /* @__PURE__ */ new Map();
metaForProxy = /* @__PURE__ */ new Map();
},
async resolveId(source, importer, options) {
if (importer) {
if (isArcId2(source)) {
return source;
}
if (isArcId2(importer)) {
return source;
}
const resolved = await this.resolve(source, importer, {
...options,
skipSelf: true
});
if (resolved && !resolved.external) {
const { id } = resolved;
const matches = getMatches(id, flagSets);
if (matches) {
const adaptiveImportsForImporter = adaptiveImporters.get(importer);
if (adaptiveImportsForImporter) {
adaptiveImportsForImporter.set(source, id);
} else {
adaptiveImporters.set(importer, /* @__PURE__ */ new Map([[source, id]]));
}
const proxyId = nextProxyId();
metaForProxy.set(proxyId, { resolved, matches });
return proxyId;
}
}
return resolved;
} else if (metaForAdaptiveChunk.has(source)) {
return source;
} else if (options.isEntry && !isArcId2(source)) {
const resolved = await this.resolve(source, importer, {
...options,
skipSelf: true
});
if (resolved) {
const importsForEntry = /* @__PURE__ */ new Set();
const adaptiveImportsForEntry = [];
const scanImports = async (childId, seenImports, adaptiveImports) => {
if (seenImports.has(childId))
return;
seenImports.add(childId);
if (isArcProxyId2(childId)) {
adaptiveImports.push(childId);
} else {
const info = await this.load({
id: childId,
resolveDependencies: true
});
if (info) {
await Promise.all(
info.importedIdResolutions.map(
(resolvedChild) => !resolvedChild.external && scanImports(
resolvedChild.id,
seenImports,
adaptiveImports
)
)
);
}
}
};
await scanImports(
resolved.id,
importsForEntry,
adaptiveImportsForEntry
);
const flagSetChunkIds = await Promise.all(
flagSets.map(async (flagSet) => {
const importsForFlagSet = new Set(importsForEntry);
const resolvedAdaptiveImports = /* @__PURE__ */ new Map();
let pendingAdaptiveImports = adaptiveImportsForEntry;
while (pendingAdaptiveImports.length) {
const pending = pendingAdaptiveImports;
pendingAdaptiveImports = [];
await Promise.all(
pending.map(async (adaptiveImport) => {
const { matches } = metaForProxy.get(adaptiveImport);
let adaptedImport;
for (const { flags, value } of matches.alternates) {
if (hasFlags(flagSet, flags)) {
adaptedImport = value;
break;
}
}
adaptedImport ||= matches.default;
resolvedAdaptiveImports.set(
adaptiveImport,
adaptedImport
);
await scanImports(
adaptedImport,
importsForFlagSet,
pendingAdaptiveImports
);
})
);
}
const id = `${resolved.id.replace(/\.[^.]+$/, "")}.arc${flagSet.length ? `.${flagSet.join(".")}` : ""}.html`;
metaForAdaptiveChunk.set(id, {
entryId: resolved.id,
adaptiveImports: resolvedAdaptiveImports
});
return id;
})
);
for (const [
importer2,
resolvedAdaptiveImports
] of adaptiveImporters) {
const ast = this.getModuleInfo(importer2)?.ast || (await this.load({ id: importer2 })).ast;
for (const child of ast.body) {
if (child.type === "ImportDeclaration") {
const id = resolvedAdaptiveImports.get(
child.source.value
);
if (!id)
continue;
let bindings = bindingsByAdaptiveId.get(id);
if (bindings === true)
continue;
for (const specifier of child.specifiers) {
switch (specifier.type) {
case "ImportNamespaceSpecifier":
bindingsByAdaptiveId.set(id, true);
continue;
case "ImportDefaultSpecifier":
if (bindings) {
bindings.add("default");
} else {
bindings = /* @__PURE__ */ new Set(["default"]);
bindingsByAdaptiveId.set(id, bindings);
}
break;
case "ImportSpecifier":
if (bindings) {
bindings.add(specifier.imported.name);
} else {
bindings = /* @__PURE__ */ new Set([specifier.imported.name]);
bindingsByAdaptiveId.set(id, bindings);
}
break;
}
}
}
}
}
for (const id of flagSetChunkIds) {
this.emitFile({
type: "chunk",
id
});
}
}
return resolved;
}
return null;
},
async load(id) {
const adaptiveChunkMeta = metaForAdaptiveChunk.get(id);
if (adaptiveChunkMeta) {
let code = "";
const adaptiveImports = [...adaptiveChunkMeta.adaptiveImports];
for (let i = adaptiveImports.length; i--; ) {
const [adaptiveImport, adaptedImport] = adaptiveImports[i];
code += `import ${JSON.stringify(
encodeArcInitId(adaptiveImport, adaptedImport)
)};
`;
}
code = generateHTML(code);
return {
code,
moduleSideEffects: "no-treeshake"
};
} else if (isArcProxyId2(id)) {
const { resolved } = metaForProxy.get(id);
let code = "";
if (isGlobalCSSFile(resolved.id)) {
return { code: "" };
}
let hasNamedExports = false;
let hasDefaultExport = false;
let syntheticNamedExports = false;
if (isAssetFile(resolved.id)) {
hasDefaultExport = true;
} else {
let info = this.getModuleInfo(resolved.id);
if (!info?.ast) {
info = await this.load(resolved);
}
if (info.exports) {
hasDefaultExport = info.hasDefaultExport === true;
hasNamedExports = info.exports.length >= (hasDefaultExport ? 2 : 1);
}
}
if (hasNamedExports || hasDefaultExport) {
const arcId = getArcId(id);
if (hasNamedExports) {
code += `export const {${arcId}} = ${runtimeId};
`;
syntheticNamedExports = arcId;
if (hasDefaultExport) {
code += `export default ${arcId}.default;
`;
}
} else {
code += `export default ${runtimeId}.${arcId}.default;
`;
}
} else {
code = "export {};\n";
}
return {
code,
syntheticNamedExports,
moduleSideEffects: false
};
} else if (isArcInitId(id)) {
const [adaptiveImport, adaptedImport] = decodeArcInitId(id);
const bindings = bindingsByAdaptiveId.get(adaptiveImport);
if (!bindings || isGlobalCSSFile(adaptiveImport)) {
return {
code: `import ${JSON.stringify(adaptedImport)};
`,
moduleSideEffects: "no-treeshake"
};
}
const arcId = getArcId(adaptiveImport);
return {
code: `import * as _${arcId} from ${JSON.stringify(
adaptedImport
)}
${runtimeId}.${arcId}=${bindings === true ? `_${arcId}` : `{${Array.from(
bindings,
(binding) => `${JSON.stringify(binding)}:_${arcId}[${JSON.stringify(
binding
)}]`
).join(",")}}`};
`,
moduleSideEffects: "no-treeshake"
};
}
return null;
},
transformIndexHtml(html, { chunk, bundle }) {
if (!bundle)
return;
const moduleId = chunk?.facadeModuleId;
if (!moduleId)
return;
const adaptiveChunkMeta = metaForAdaptiveChunk.get(moduleId);
if (adaptiveChunkMeta) {
const { entryId } = adaptiveChunkMeta;
for (const curFile in bundle) {
const curChunk = bundle[curFile];
if (curChunk.type === "chunk" && curChunk.isEntry && curChunk.facadeModuleId === entryId) {
return prepareArcEntryHTML(
runtimeId,
html,
(fileName) => resolveAssetURL(fileName, entryId),
curChunk,
chunk
);
}
}
return;
}
return stripEntryScript(
resolveAssetURL(chunk.fileName, moduleId),
html
);
}
},
{
name: "arc-vite:build-web:post",
enforce: "post",
apply,
async generateBundle(_, bundle, isWrite) {
if (!isWrite) {
this.error(
"arc-vite: generateBundle cannot be called with isWrite=false"
);
}
const { serverEntryFiles } = await store.read().catch(() => {
this.error(
"arc-vite: failed to read manifest code. This likely means that the server bundle was not created before browser bundle."
);
});
const flaggedManifestByEntry = /* @__PURE__ */ new Map();
for (const fileName in bundle) {
const chunk = bundle[fileName];
if (chunk.type === "asset") {
const arcHTMLChunkMatch = arcChunkFileNameReg.exec(chunk.fileName);
if (!arcHTMLChunkMatch)
continue;
delete bundle[fileName];
const [, entryName, flagSetStr] = arcHTMLChunkMatch;
const flaggedManifest = {
flags: flagSetStr ? flagSetStr.split(".") : [],
manifest: generatManifest(basePath, chunk.source.toString())
};
const flaggedAssetsForEntry = flaggedManifestByEntry.get(entryName);
if (flaggedAssetsForEntry) {
flaggedAssetsForEntry.push(flaggedManifest);
flaggedManifestByEntry.set(entryName, flaggedAssetsForEntry);
} else {
flaggedManifestByEntry.set(entryName, [flaggedManifest]);
}
}
}
let manifestCode = `;function __ARC_ASSETS__(entry) {const f = __ARC_FLAGS__() || {};switch(entry) {`;
for (const [
entryName,
flaggedAssetsForEntry
] of flaggedManifestByEntry) {
const defaultFlaggedAssets = flaggedAssetsForEntry.sort(compareFlaggedObject).pop();
let matchCodeSep = "";
manifestCode += `case ${JSON.stringify(entryName)}:return `;
for (const { flags, manifest } of flaggedAssetsForEntry) {
manifestCode += matchCodeSep + flags.map((flag) => `f.${flag}`).join("&&") + "?" + JSON.stringify(manifest);
matchCodeSep = ":";
}
manifestCode += `:${JSON.stringify(defaultFlaggedAssets.manifest)};`;
}
manifestCode += `}return {"head": ["<script>console.error('Unable to load adaptive arc files, unknown entry was provided when asking for assets.')</script>"]}};
`;
await Promise.all(
serverEntryFiles.map((file) => fs3.appendFile(file, manifestCode))
);
}
}
];
function nextProxyId() {
return arcProxyPrefix2 + (proxyModuleId++).toString(36) + arcJsSuffix2;
}
function encodeArcInitId(adaptiveImport, adaptedImport) {
return `${arcInitPrefix + (initModuleId++).toString(36)}:${(adaptiveImport === adaptedImport ? encodeFileName(adaptiveImport) : `${encodeFileName(adaptiveImport)},${encodeFileName(
adaptedImport[0] === "\0" ? adaptedImport : path5.relative(path5.dirname(adaptiveImport), adaptedImport)
)}`) + arcJsSuffix2}`;
}
function getArcId(source) {
let id = globalIds.get(source);
if (id === void 0) {
id = indexToId(globalIds.size);
globalIds.set(source, id);
}
return id;
}
}
function isArcId2(id) {
return id.startsWith(arcPrefix2);
}
function isArcProxyId2(id) {
return id.startsWith(arcProxyPrefix2);
}
function isArcInitId(id) {
return id.startsWith(arcInitPrefix);
}
function decodeArcInitId(id) {
const prefixEnd = id.indexOf(":", arcInitPrefix.length + 1) + 1;
const sepStart = id.indexOf(",", prefixEnd);
if (sepStart === -1) {
const adaptedImport2 = decodeFileName(
id.slice(prefixEnd, -arcJsSuffix2.length)
);
return [adaptedImport2, adaptedImport2];
}
const adaptiveImport = decodeFileName(id.slice(prefixEnd, sepStart));
const relativeAdaptedImport = decodeFileName(
id.slice(sepStart + 1, -arcJsSuffix2.length)
);
const adaptedImport = relativeAdaptedImport[0] === "\0" ? relativeAdaptedImport : path5.join(adaptiveImport, "..", relativeAdaptedImport);
return [adaptiveImport, adaptedImport];
}
// src/plugins/serve.ts
import { createRequire } from "module";
import path6 from "path";
function pluginServe({
flagSets,
forceFlagSet
}) {
const flagSet = forceFlagSet?.length ? forceFlagSet : void 0;
return {
name: "arc-vite:serve",
enforce: "pre",
apply(_, { command }) {
return command === "serve";
},
async config(config) {
Object.assign(createRequire(import.meta.url)("arc-server"), {
getAssets() {
return {};
}
});
if (!flagSet)
return;
config.cacheDir = path6.resolve(
`node_modules/.vite/arc/${flagSet.join(".")}`
);
config.optimizeDeps ??= {};
config.optimizeDeps.esbuildOptions ??= {};
config.optimizeDeps.esbuildOptions.plugins ??= [];
config.optimizeDeps.esbuildOptions.plugins.unshift({
name: "arc-vite:serve:esbuild",
setup(build) {
const arcProxyPrefix3 = "arc-proxy:";
build.onResolve(
{ filter: new RegExp(`^${arcProxyPrefix3}`) },
(args) => {
return {
path: args.path.slice(arcProxyPrefix3.length)
};
}
);
build.onLoad({ filter: /./ }, (args) => {
const adaptedImport = getAdaptedMatch(args.path, flagSets, flagSet);
if (adaptedImport) {
const proxiedImportCode = JSON.stringify(
arcProxyPrefix3 + adaptedImport
);
return {
loader: "js",
contents: `module.exports = require(${proxiedImportCode});
`
};
}
});
}
});
},
async resolveId(source, importer, options) {
if (importer && flagSet) {
const resolved = await this.resolve(source, importer, {
...options,
skipSelf: true
});
if (resolved) {
return getAdaptedMatch(resolved.id, flagSets, flagSet) || resolved;
}
}
return null;
}
};
}
function getAdaptedMatch(id, flagSets, flagSet) {
if (path6.isAbsolute(id)) {
const matches = getMatches(id, flagSets);
if (matches) {
let adaptedImport;
for (const { flags, value } of matches.alternates) {
if (hasFlags(flagSet, flags)) {
adaptedImport = value;
break;
}
}
adaptedImport ||= matches.default;
if (adaptedImport !== id) {
return adaptedImport;
}
}
}
}
// src/index.ts
function arcVite(options) {
const pluginOptions = getInternalPluginOptions(options);
return [].concat(
pluginServe(pluginOptions),
pluginBuildWeb(pluginOptions),
pluginBuildSSR(pluginOptions)
);
}
export {
createFlagSets,
arcVite as default,
hasFlags
};