← Files DataARCHIVED FILE
scripts/authored-module-graph.mjs
43.3 KB · Sep 30, 2026 · 23:19 UTC
import { lstat, readFile, readdir, realpath } from "node:fs/promises";
import { basename, dirname, extname, isAbsolute, join, relative, resolve, sep } from "node:path";
import { parseStrictCss } from "./css-syntax.mjs";
import { encodeDataAssetFragment } from "./data-url.mjs";
import { RUNTIME_MODULE_SPECIFIERS, validateRuntimeModuleExports } from "./prebuilt/manifest.mjs";
// These are the namespaces exported by the release-built browser runtime. This
// resolver is a build-dependency boundary, not a sandbox for authored browser JS.
export const AUTHORED_RUNTIME_MODULES = RUNTIME_MODULE_SPECIFIERS;
const RUNTIME_MODULES = new Set(AUTHORED_RUNTIME_MODULES);
const VIRTUAL_PREFIX = "\0data-app-authored:";
const RUNTIME_PREFIX = "\0data-app-runtime:";
const ENTRY_ID = `${VIRTUAL_PREFIX}entry`;
const SNAPSHOT_ID = "@openai/data-app-reviewed-snapshot";
// Match Vite's default extension order so an existing extensionless import does
// not silently select a different sibling when multiple suffixes are present.
const JS_EXTENSIONS = [".mjs", ".js", ".mts", ".ts", ".jsx", ".tsx"];
const RESOLVE_EXTENSIONS = [...JS_EXTENSIONS, ".json"];
const CSS_EXTENSIONS = new Set([".css", ".pcss", ".postcss"]);
const SOURCE_ONLY_CODE = /\.(?:cjs|cts)$/iu;
const SOURCE_ONLY_STYLE = /(?:\.module\.(?:css|pcss|postcss)|\.(?:scss|sass|less|styl|stylus))$/iu;
const NODE_BINDINGS = new Set(["require", "module", "exports", "__dirname", "__filename"]);
const MIME_TYPES = Object.freeze({
".avif": "image/avif",
".bmp": "image/bmp",
".csv": "text/csv",
".css": "text/css",
".eot": "application/vnd.ms-fontobject",
".gif": "image/gif",
".ico": "image/x-icon",
".jpeg": "image/jpeg",
".jpg": "image/jpeg",
".json": "application/json",
".m4a": "audio/mp4",
".mp3": "audio/mpeg",
".mp4": "video/mp4",
".oga": "audio/ogg",
".ogg": "audio/ogg",
".ogv": "video/ogg",
".otf": "font/otf",
".pdf": "application/pdf",
".pcss": "text/css",
".png": "image/png",
".postcss": "text/css",
".svg": "image/svg+xml",
".ttf": "font/ttf",
".txt": "text/plain",
".wav": "audio/wav",
".webm": "video/webm",
".webp": "image/webp",
".woff": "font/woff",
".woff2": "font/woff2",
});
const slash = (value) => value.split(sep).join("/");
const inside = (root, filename) => {
const name = relative(root, filename);
return name === "" || (name !== ".." && !name.startsWith(`..${sep}`) && !isAbsolute(name));
};
const stringLiteral = (value) =>
JSON.stringify(value).replaceAll("<", "\\u003c").replaceAll("\u2028", "\\u2028").replaceAll("\u2029", "\\u2029");
const jsonExpression = (value) => `JSON.parse(${stringLiteral(JSON.stringify(value))})`;
function unsupported(message, filename) {
return new Error(
`Data's prebuilt build cannot ${message}${filename ? ` (${filename})` : ""}. ` +
"Keep authored files beneath src/content and use the bundled public modules, " +
"or use the explicit --source build with an already-installed compatible toolchain.",
);
}
function decodeText(bytes, filename) {
try {
return new TextDecoder("utf-8", { fatal: true }).decode(bytes);
} catch {
throw unsupported("read a non-UTF-8 text file", filename);
}
}
function applyEdits(source, edits) {
const sorted = [...edits].sort((a, b) => b.start - a.start || b.end - a.end);
let end = source.length;
let result = source;
for (const edit of sorted) {
if (edit.start < 0 || edit.end < edit.start || edit.end > end) {
throw new Error("Overlapping authored-source edits.");
}
result = result.slice(0, edit.start) + edit.text + result.slice(edit.end);
end = edit.start;
}
return result;
}
function childrenOf(node) {
return node?.children ? [...node.children] : [];
}
function moduleName(node) {
return node?.type === "Identifier" ? node.name : node?.value;
}
function patternNames(node) {
if (!node) return [];
if (node.type === "Identifier") return [node.name];
if (node.type === "RestElement") return patternNames(node.argument);
if (node.type === "AssignmentPattern") return patternNames(node.left);
if (node.type === "ArrayPattern") return node.elements.flatMap(patternNames);
if (node.type === "ObjectPattern") {
return node.properties.flatMap((property) =>
patternNames(property.type === "RestElement" ? property.argument : property.value),
);
}
return [];
}
function exportedNames(node) {
if (node.type === "ExportAllDeclaration") return node.exported ? [moduleName(node.exported)] : [];
const names = (node.specifiers ?? []).map((specifier) => moduleName(specifier.exported));
if (node.declaration?.type === "VariableDeclaration") {
names.push(...node.declaration.declarations.flatMap((declaration) => patternNames(declaration.id)));
} else if (node.declaration?.id) names.push(...patternNames(node.declaration.id));
return names;
}
function importValue(node) {
if (node?.type === "Literal" && typeof node.value === "string") return node.value;
if (node?.type === "TemplateLiteral" && node.expressions.length === 0) return node.quasis[0].value.cooked;
return undefined;
}
function nodeChildren(node, visit) {
for (const [key, value] of Object.entries(node)) {
if (Array.isArray(value)) {
for (const child of value) if (child && typeof child.type === "string") visit(child, key);
} else if (value && typeof value.type === "string") visit(value, key);
}
}
function identifierIsReference(parent, key) {
if (!parent) return false;
if (
((parent.type === "MemberExpression" || parent.type === "OptionalMemberExpression") &&
key === "property" &&
!parent.computed) ||
((parent.type === "Property" || parent.type === "MethodDefinition" || parent.type === "PropertyDefinition") &&
key === "key" &&
!parent.computed) ||
((parent.type === "LabeledStatement" || parent.type === "BreakStatement" || parent.type === "ContinueStatement") &&
key === "label") ||
parent.type === "MetaProperty" ||
parent.type.startsWith("Import") ||
parent.type === "ExportSpecifier"
)
return false;
return true;
}
// Scope analysis is used only to distinguish real CommonJS/Node references from
// ordinary authored parameters/properties named "require" or "exports". Module
// semantics, live bindings, cycles, and renaming belong to the bundled Rollup.
function inspectJavaScript(ast, filename, { generated = false } = {}) {
const imports = [];
const references = [];
const bindings = new WeakSet();
const identifiers = new Set();
const makeScope = (parent, variable = false) => ({ parent, variable, names: new Set() });
const root = makeScope(null, true);
const variableScope = (scope) => (scope.variable ? scope : variableScope(scope.parent));
const bound = (scope, name) => scope && (scope.names.has(name) || bound(scope.parent, name));
function bindPattern(node, scope) {
if (!node) return;
if (node.type === "Identifier") {
scope.names.add(node.name);
bindings.add(node);
} else if (node.type === "RestElement") bindPattern(node.argument, scope);
else if (node.type === "AssignmentPattern") bindPattern(node.left, scope);
else if (node.type === "ArrayPattern") node.elements.forEach((item) => bindPattern(item, scope));
else if (node.type === "ObjectPattern") {
for (const property of node.properties) {
bindPattern(property.type === "RestElement" ? property.argument : property.value, scope);
}
}
}
function addImport(node, source, dynamic = false) {
const value = importValue(source);
if (typeof value !== "string") throw unsupported("bundle a computed dynamic import", filename);
if (node.phase && node.phase !== "evaluation") throw unsupported("bundle a nonstandard import phase", filename);
const names = [];
if (node.type === "ImportDeclaration") {
for (const specifier of node.specifiers) {
if (specifier.type === "ImportDefaultSpecifier") names.push("default");
if (specifier.type === "ImportSpecifier") names.push(moduleName(specifier.imported));
}
} else if (node.type === "ExportNamedDeclaration") {
for (const specifier of node.specifiers) names.push(moduleName(specifier.local));
}
imports.push({ specifier: value, start: source.start, end: source.end, dynamic, names });
}
function visit(node, scope, parent, key, functionDepth = 0) {
if (!node) return;
if (node.type === "Identifier") {
identifiers.add(node.name);
if (identifierIsReference(parent, key)) references.push({ node, scope, parent, key });
return;
}
if (node.type === "ImportDeclaration") {
addImport(node, node.source);
for (const specifier of node.specifiers) bindPattern(specifier.local, scope);
return;
}
if (node.type === "ExportNamedDeclaration" || node.type === "ExportAllDeclaration") {
if (!generated && exportedNames(node).includes("__proto__")) {
throw unsupported(
'export the ESM name "__proto__"; use another export name or access that key through a default object',
filename,
);
}
if (node.source) addImport(node, node.source);
if (node.declaration) visit(node.declaration, scope, node, "declaration", functionDepth);
return;
}
if (node.type === "ImportExpression") {
if (!generated) {
throw unsupported("preserve lazy dynamic-import evaluation; use a static import", filename);
}
addImport(node, node.source, true);
if (node.options) throw unsupported("bundle dynamic-import options", filename);
return;
}
if (node.type === "MetaProperty" && node.meta.name === "import") {
throw unsupported("use import.meta in the prebuilt authoring surface", filename);
}
if ((node.type === "AwaitExpression" || (node.type === "ForOfStatement" && node.await)) && functionDepth === 0) {
throw unsupported("bundle top-level await", filename);
}
if (node.type === "VariableDeclaration") {
const target = node.kind === "var" ? variableScope(scope) : scope;
for (const declaration of node.declarations) bindPattern(declaration.id, target);
}
if (["FunctionDeclaration", "FunctionExpression", "ArrowFunctionExpression"].includes(node.type)) {
if (node.type === "FunctionDeclaration") bindPattern(node.id, scope);
const parameters = makeScope(scope, true);
if (node.id) bindPattern(node.id, parameters);
for (const parameter of node.params) bindPattern(parameter, parameters);
if (node.id) identifiers.add(node.id.name);
for (const parameter of node.params) visit(parameter, parameters, node, "params", functionDepth + 1);
// Body var/function declarations are not visible to default-parameter
// initializers. Keeping these scopes separate also handles arrow functions.
visit(node.body, makeScope(parameters, true), node, "body", functionDepth + 1);
return;
}
if (node.type === "ClassDeclaration" || node.type === "ClassExpression") {
if (node.type === "ClassDeclaration") bindPattern(node.id, scope);
const inner = makeScope(scope);
if (node.id) {
bindPattern(node.id, inner);
identifiers.add(node.id.name);
}
visit(node.superClass, inner, node, "superClass", functionDepth);
visit(node.body, inner, node, "body", functionDepth);
return;
}
if (node.type === "CatchClause") {
const inner = makeScope(scope);
bindPattern(node.param, inner);
visit(node.param, inner, node, "param", functionDepth);
visit(node.body, inner, node, "body", functionDepth);
return;
}
if (node.type === "SwitchStatement") {
visit(node.discriminant, scope, node, "discriminant", functionDepth);
const inner = makeScope(scope);
for (const branch of node.cases) visit(branch, inner, node, "cases", functionDepth);
return;
}
if (
node.type === "BlockStatement" ||
node.type === "StaticBlock" ||
node.type === "ForStatement" ||
node.type === "ForInStatement" ||
node.type === "ForOfStatement"
)
scope = makeScope(scope, node.type === "StaticBlock");
nodeChildren(node, (child, childKey) => visit(child, scope, node, childKey, functionDepth));
}
visit(ast, root);
const freeNodeReferences = references.filter(
({ node, scope }) => NODE_BINDINGS.has(node.name) && !bindings.has(node) && !bound(scope, node.name),
);
if (!generated && freeNodeReferences.length) {
throw unsupported(`use the Node/CommonJS binding ${JSON.stringify(freeNodeReferences[0].node.name)}`, filename);
}
return { imports, identifiers, freeNodeReferences };
}
function parseSpecifier(specifier, filename) {
if (typeof specifier !== "string" || !specifier || /[\\\u0000-\u001f\u007f]/u.test(specifier)) {
throw unsupported(`resolve the invalid import ${JSON.stringify(specifier)}`, filename);
}
const fragmentAt = specifier.indexOf("#");
const fragment = fragmentAt < 0 ? "" : specifier.slice(fragmentAt + 1);
const withoutFragment = fragmentAt < 0 ? specifier : specifier.slice(0, fragmentAt);
const queryAt = withoutFragment.indexOf("?");
const query = queryAt < 0 ? "" : withoutFragment.slice(queryAt + 1);
let pathname = queryAt < 0 ? withoutFragment : withoutFragment.slice(0, queryAt);
if (!["", "raw", "url", "inline", "worker&inline"].includes(query)) {
throw unsupported(`resolve the import query ${JSON.stringify(query)}`, filename);
}
if (/%(?:2f|5c|00)/iu.test(pathname)) throw unsupported("resolve an encoded path separator", filename);
try {
pathname = decodeURIComponent(pathname);
} catch {
throw unsupported("resolve an invalid percent-encoded path", filename);
}
if (/[:?#\\\u0000-\u001f\u007f]/u.test(pathname)) throw unsupported("resolve an invalid local path", filename);
return { pathname, query, fragment };
}
function cssName(compiler, value) {
return compiler.decodeCssIdentifier(value).toLowerCase();
}
function cssRange(node) {
return { start: node.loc.start.offset, end: node.loc.end.offset };
}
function enclosingRange(node, ranges) {
return ranges.some((range) => node.loc.start.offset >= range.start && node.loc.end.offset <= range.end);
}
function classifyCssUrl(value, filename) {
const url = value.replace(/^[\u0000-\u0020]+|[\u0000-\u0020]+$/gu, "");
if (!url || /[\\\u0000-\u001f\u007f]/u.test(url)) throw unsupported("embed an invalid CSS URL", filename);
if (url.startsWith("#")) return { embedded: true, value: url };
if (/^data:/iu.test(url)) return { embedded: true, value: url };
if (/^(?:[a-z][a-z0-9+.-]*:|\/\/|\/)/iu.test(url)) {
throw unsupported(`load the external CSS resource ${JSON.stringify(url)}`, filename);
}
return { embedded: false, value: url };
}
function cssResources(ast, compiler, filename) {
const imports = [];
const resources = [];
const discarded = [];
const topLevelImports = new Set();
let importsAllowed = true;
for (const child of childrenOf(ast)) {
const name = child.type === "Atrule" ? cssName(compiler, child.name) : "";
if (name === "import") {
if (!importsAllowed) throw unsupported("honor a CSS @import placed after a style rule", filename);
topLevelImports.add(child);
} else if (name !== "charset" && !(name === "layer" && !child.block)) importsAllowed = false;
}
compiler.walkCss(ast, (node) => {
if (node.type !== "Atrule") return;
const name = cssName(compiler, node.name);
if (name === "import") {
if (!topLevelImports.has(node)) throw unsupported("flatten a nested CSS @import", filename);
imports.push(node);
}
if (name === "charset") discarded.push(cssRange(node));
if (name === "namespace") throw unsupported("bundle CSS @namespace rules", filename);
});
const skip = [...imports.map(cssRange), ...discarded];
const functionUrls = [];
compiler.walkCss(ast, (node) => {
if (enclosingRange(node, skip)) return;
if (node.type === "Url") resources.push({ node, value: node.value, kind: "url" });
if (node.type !== "Function") return;
const name = cssName(compiler, node.name);
if (name === "url") {
const children = childrenOf(node);
if (children.length !== 1 || children[0].type !== "String") {
throw unsupported("statically resolve this CSS url() function", filename);
}
functionUrls.push(cssRange(node));
resources.push({ node, value: children[0].value, kind: "url" });
} else if (["image", "image-set", "-webkit-image-set", "src"].includes(name)) {
for (const child of childrenOf(node)) {
if (child.type === "String") resources.push({ node: child, value: child.value, kind: "string" });
}
}
});
return {
imports,
discarded,
resources: resources.filter(
({ node }) =>
!functionUrls.some((range) => node.loc.start.offset > range.start && node.loc.end.offset < range.end),
),
};
}
function importCssParts(node, compiler, filename) {
const children = childrenOf(node.prelude);
const source = children.shift();
let value;
if (source?.type === "String" || source?.type === "Url") value = source.value;
else if (source?.type === "Function" && cssName(compiler, source.name) === "url") {
const args = childrenOf(source);
if (args.length === 1 && args[0].type === "String") value = args[0].value;
}
if (typeof value !== "string" || node.block) throw unsupported("resolve this CSS @import", filename);
let layer = null;
let supports = null;
let media = null;
for (const child of children) {
if (layer === null && child.type === "Identifier" && cssName(compiler, child.name) === "layer") layer = "";
else if (layer === null && child.type === "Function" && cssName(compiler, child.name) === "layer") {
layer = childrenOf(child)
.map((part) => compiler.generateCss(part))
.join("");
} else if (supports === null && child.type === "Function" && cssName(compiler, child.name) === "supports") {
supports = childrenOf(child)
.map((part) => compiler.generateCss(part))
.join("");
} else if (media === null && child.type === "MediaQueryList") media = compiler.generateCss(child);
else throw unsupported("preserve these CSS @import conditions", filename);
}
return {
value,
wrap(css) {
if (media) css = `@media ${media}{${css}}`;
if (supports) css = `@supports (${supports}){${css}}`;
if (layer !== null) css = `@layer${layer ? ` ${layer}` : ""}{${css}}`;
return css;
},
};
}
class AuthoredGraph {
constructor(projectRoot, compiler, runtimeModuleExports, worker = false) {
this.root = projectRoot;
this.compiler = compiler;
this.runtimeModuleExports = runtimeModuleExports;
this.worker = worker;
this.workerModuleCount = 0;
this.content = join(projectRoot, "src/content");
this.publicFile = join(projectRoot, "src/data-app-public.jsx");
this.snapshotFile = join(projectRoot, "src/data.json");
this.themeFile = join(projectRoot, "src/theme.css");
this.modules = new Map();
this.sourceFiles = new Set();
this.cssFiles = new Set();
this.assets = new Set();
this.cssCache = new Map();
this.cssLoading = [];
this.styleFiles = new Set();
this.externalImports = new Map();
}
name(filename) {
return slash(relative(this.root, filename));
}
allowed(filename) {
return inside(this.content, filename) || [this.publicFile, this.snapshotFile, this.themeFile].includes(filename);
}
async exactFile(filename) {
if (!this.allowed(filename)) throw unsupported("read outside the authored source boundary", this.name(filename));
let current = this.root;
const parts = relative(this.root, filename).split(sep);
for (let index = 0; index < parts.length; index++) {
let entries;
try {
entries = await readdir(current);
} catch (error) {
if (error.code === "ENOENT" || error.code === "ENOTDIR") return false;
throw error;
}
if (!entries.includes(parts[index])) {
const differentlyCased = entries.find((name) => name.toLowerCase() === parts[index].toLowerCase());
if (differentlyCased) {
throw unsupported(
`resolve a case-mismatched path; use ${JSON.stringify(differentlyCased)}`,
this.name(filename),
);
}
return false;
}
current = join(current, parts[index]);
const stat = await lstat(current);
if (stat.isSymbolicLink()) throw unsupported("follow an authored-source symlink", this.name(current));
if (index < parts.length - 1 ? !stat.isDirectory() : !stat.isFile()) return false;
}
if ((await realpath(filename)) !== filename)
throw unsupported("follow a redirected authored-source path", this.name(filename));
return true;
}
async read(filename) {
if (!(await this.exactFile(filename))) throw unsupported("find the referenced file", this.name(filename));
this.sourceFiles.add(this.name(filename));
return readFile(filename);
}
async localFile(pathname, importer, { exact = false } = {}) {
if (!pathname.startsWith("./") && !pathname.startsWith("../")) {
throw unsupported(`resolve the non-relative local import ${JSON.stringify(pathname)}`, this.name(importer));
}
const candidate = resolve(dirname(importer), pathname);
const candidates = [candidate];
if (!exact && !extname(candidate)) {
candidates.push(...RESOLVE_EXTENSIONS.map((extension) => candidate + extension));
candidates.push(...RESOLVE_EXTENSIONS.map((extension) => join(candidate, `index${extension}`)));
}
const permitted = candidates.filter((filename) => this.allowed(filename));
if (!permitted.length) {
throw unsupported(`resolve ${JSON.stringify(pathname)} outside src/content`, this.name(importer));
}
for (const filename of permitted) if (await this.exactFile(filename)) return filename;
throw unsupported(`find the local import ${JSON.stringify(pathname)}`, this.name(importer));
}
external(id, names = []) {
if (!this.externalImports.has(id)) this.externalImports.set(id, new Set());
for (const name of names) this.externalImports.get(id).add(name);
return id;
}
runtimeModule(specifier, names, importer) {
const available = this.runtimeModuleExports[specifier];
for (const name of names) {
if (!available.includes(name)) {
throw unsupported(
`import missing export ${JSON.stringify(name)} from bundled module ${JSON.stringify(specifier)}`,
this.name(importer),
);
}
}
// Rollup 3's namespace object emitter cannot safely represent this exotic
// named export. Default objects retain their own __proto__ data unchanged.
if (available.includes("__proto__")) {
throw unsupported(`represent the bundled namespace ${JSON.stringify(specifier)} with a named __proto__ export`);
}
const id = `${RUNTIME_PREFIX}${specifier}`;
if (!this.modules.has(id)) {
// External ESM exports are otherwise unknown to Rollup. Enumerating the
// verified release surface lets its normal linker catch misspellings and
// ambiguous names through arbitrary local reexport/star-export chains.
const exports = available.map((name) => `${stringLiteral(name)} as ${stringLiteral(name)}`).join(", ");
this.modules.set(id, { id, source: `export { ${exports} } from ${stringLiteral(specifier)};` });
}
// Check the same complete namespace again in the browser as a defense
// against accidentally pairing generated content with a different runtime.
this.external(specifier, available);
return id;
}
async dataUrl(filename, fragment = "") {
const bytes = await this.read(filename);
this.assets.add(this.name(filename));
const mime = MIME_TYPES[extname(filename).toLowerCase()] ?? "application/octet-stream";
let suffix = "";
if (fragment) {
try {
suffix = `#${encodeDataAssetFragment(decodeURIComponent(fragment))}`;
} catch {
throw unsupported("encode an invalid asset fragment", this.name(filename));
}
}
return `data:${mime};base64,${bytes.toString("base64")}${suffix}`;
}
async cssUrl(value, importer, { final = false } = {}) {
const classified = classifyCssUrl(value, this.name(importer));
if (classified.embedded) return classified.value;
if (final) throw unsupported("emit a CSS URL that was not embedded during normalization", this.name(importer));
const parsed = parseSpecifier(classified.value, this.name(importer));
if (parsed.query && parsed.query !== "url") throw unsupported("preserve a CSS resource query", this.name(importer));
const pathname = /^(?:\.\/|\.\.\/)/u.test(parsed.pathname) ? parsed.pathname : `./${parsed.pathname}`;
const filename = await this.localFile(pathname, importer, { exact: true });
if (!inside(this.content, filename))
throw unsupported("embed a protected file as a CSS asset", this.name(filename));
return this.dataUrl(filename, parsed.fragment);
}
parseCss(source, filename) {
return parseStrictCss(source, this.compiler, {
filename: this.name(filename),
onInvalid: (error) => {
throw unsupported(`parse CSS: ${error.message}`, this.name(filename));
},
});
}
async css(filename) {
if (SOURCE_ONLY_STYLE.test(filename)) throw unsupported("compile Sass, Less, or CSS modules", this.name(filename));
if (!CSS_EXTENSIONS.has(extname(filename).toLowerCase()))
throw unsupported("import a non-CSS stylesheet", this.name(filename));
if (this.cssCache.has(filename)) return this.cssCache.get(filename);
if (this.cssLoading.includes(filename)) {
throw unsupported(
`flatten a circular CSS @import: ${[...this.cssLoading, filename].map((item) => this.name(item)).join(" -> ")}`,
);
}
this.cssLoading.push(filename);
try {
const source = decodeText(await this.read(filename), this.name(filename));
this.cssFiles.add(this.name(filename));
const ast = this.parseCss(source, filename);
const { imports, resources, discarded } = cssResources(ast, this.compiler, this.name(filename));
const edits = discarded.map((range) => ({ ...range, text: "" }));
for (const node of imports) {
const parts = importCssParts(node, this.compiler, this.name(filename));
const classified = classifyCssUrl(parts.value, this.name(filename));
if (classified.embedded) throw unsupported("flatten a data-URL or fragment CSS @import", this.name(filename));
const parsed = parseSpecifier(classified.value, this.name(filename));
if (parsed.query || parsed.fragment)
throw unsupported("preserve a CSS @import query or fragment", this.name(filename));
const pathname = /^(?:\.\/|\.\.\/)/u.test(parsed.pathname) ? parsed.pathname : `./${parsed.pathname}`;
const imported = await this.localFile(pathname, filename, { exact: true });
edits.push({ ...cssRange(node), text: parts.wrap(await this.css(imported)) });
}
for (const resource of resources) {
const value = await this.cssUrl(resource.value, filename);
const type = resource.kind === "url" ? "Url" : "String";
edits.push({ ...cssRange(resource.node), text: this.compiler.generateCss({ type, value }) });
}
// css-tree may canonicalize escaped identifiers across removed comments.
// Reparse and check the exact generated bytes, not only the input AST.
const rewritten = this.parseCss(applyEdits(source, edits), filename);
const generated = this.compiler.generateCss(rewritten);
const finalAst = this.parseCss(generated, filename);
const finalResources = cssResources(finalAst, this.compiler, this.name(filename));
if (finalResources.imports.length) throw unsupported("emit an unresolved CSS @import", this.name(filename));
for (const resource of finalResources.resources) await this.cssUrl(resource.value, filename, { final: true });
this.cssCache.set(filename, generated);
return generated;
} finally {
this.cssLoading.pop();
}
}
async resolveImport(specifier, importer, names = []) {
if (RUNTIME_MODULES.has(specifier)) {
if (this.worker) throw unsupported("import the UI runtime inside a calculation Worker", this.name(importer));
return this.runtimeModule(specifier, names, importer);
}
const parsed = parseSpecifier(specifier, this.name(importer));
if (!parsed.pathname.startsWith("./") && !parsed.pathname.startsWith("../")) {
throw unsupported(`resolve the package or URL import ${JSON.stringify(specifier)}`, this.name(importer));
}
const filename = await this.localFile(parsed.pathname, importer);
if (filename === this.publicFile || filename === this.snapshotFile) {
if (this.worker) throw unsupported("import protected UI or snapshot modules inside a Worker; pass reviewed data with postMessage", this.name(importer));
if (parsed.query || parsed.fragment)
throw unsupported("apply a query to a protected public/data module", this.name(importer));
if (filename === this.snapshotFile && names.includes("__proto__")) {
throw unsupported(
'import JSON key "__proto__" as a named export; read it from the default JSON object',
this.name(importer),
);
}
this.sourceFiles.add(this.name(filename));
return filename === this.publicFile
? this.runtimeModule("@openai/data-app", names, importer)
: this.external(SNAPSHOT_ID, names);
}
if (SOURCE_ONLY_CODE.test(filename) && !["raw", "url"].includes(parsed.query))
throw unsupported(
"compile CommonJS .cjs or .cts modules; use an ESM .js, .mjs, .ts, or .mts file",
this.name(filename),
);
if (SOURCE_ONLY_STYLE.test(filename)) throw unsupported("compile Sass, Less, or CSS modules", this.name(filename));
if (!parsed.query && extname(filename).toLowerCase() === ".json" && names.includes("__proto__")) {
throw unsupported(
'import JSON key "__proto__" as a named export; read it from the default JSON object',
this.name(importer),
);
}
if (parsed.fragment && parsed.query !== "url" && JS_EXTENSIONS.includes(extname(filename).toLowerCase())) {
throw unsupported("use a fragment on a JavaScript module", this.name(filename));
}
const id = `${VIRTUAL_PREFIX}${this.name(filename)}${parsed.query ? `?${parsed.query}` : ""}${
parsed.fragment ? `#${parsed.fragment}` : ""
}`;
if (!this.modules.has(id)) {
// Install the record before descending so normal ESM cycles reach Rollup.
const record = { id, filename, source: null, loading: true };
this.modules.set(id, record);
record.source = await this.loadModule(filename, parsed);
record.loading = false;
}
return id;
}
async loadModule(filename, { query, fragment }) {
const extension = extname(filename).toLowerCase();
if (query === "worker&inline") {
if (this.worker || fragment || !JS_EXTENSIONS.includes(extension))
throw unsupported("bundle a nested, fragmented, or non-JavaScript calculation Worker", this.name(filename));
const graph = new AuthoredGraph(this.root, this.compiler, this.runtimeModuleExports, true);
const entry = await graph.resolveImport(`./${basename(filename)}`, filename);
graph.modules.set(ENTRY_ID, { id: ENTRY_ID, source: `import ${stringLiteral(entry)};` });
const { code } = await emitBundle(graph);
for (const source of graph.sourceFiles) this.sourceFiles.add(source);
this.workerModuleCount += [...graph.modules.values()].filter(record => record.filename).length;
// The constructor matches the source-build import. Keep the URL alive until
// startup has completed, or release it on construction failure/termination.
return `export default function InlineCalculationWorker(options) {
const url = URL.createObjectURL(new Blob([${stringLiteral(code)}], {type:"text/javascript"}));
let worker, released = false;
const release = () => { if (!released) { released = true; URL.revokeObjectURL(url); } };
try { worker = new Worker(url, options); } catch (error) { release(); throw error; }
worker.addEventListener("message", release, {once:true});
worker.addEventListener("error", release, {once:true});
const terminate = worker.terminate.bind(worker);
worker.terminate = () => { release(); terminate(); };
return worker;
}`;
}
if (this.worker && (query || (!JS_EXTENSIONS.includes(extension) && extension !== ".json")))
throw unsupported("import anything other than local JavaScript/TypeScript and JSON in a calculation Worker", this.name(filename));
if (query === "raw") {
if (fragment) throw unsupported("use a fragment on a raw-text import", this.name(filename));
const value = decodeText(await this.read(filename), this.name(filename));
if (!JS_EXTENSIONS.includes(extension) && extension !== ".json" && !CSS_EXTENSIONS.has(extension))
this.assets.add(this.name(filename));
return `export default ${jsonExpression(value)};`;
}
if (query === "url") {
if (!inside(this.content, filename)) throw unsupported("embed a protected file as an asset", this.name(filename));
if (CSS_EXTENSIONS.has(extension)) {
if (fragment) throw unsupported("use a fragment on a stylesheet URL", this.name(filename));
const css = await this.css(filename);
this.assets.add(this.name(filename));
return `export default ${jsonExpression(`data:text/css;base64,${Buffer.from(css).toString("base64")}`)};`;
}
return `export default ${jsonExpression(await this.dataUrl(filename, fragment))};`;
}
if (CSS_EXTENSIONS.has(extension)) {
const css = await this.css(filename);
if (fragment) throw unsupported("use a fragment on a stylesheet", this.name(filename));
if (query === "inline") return `export default ${jsonExpression(css)};`;
if (filename !== this.themeFile) this.styleFiles.add(filename);
return "export {};";
}
if (query === "inline") throw unsupported("use ?inline on a non-CSS import", this.name(filename));
if (extension === ".json") {
if (fragment) throw unsupported("use a fragment on a JSON module", this.name(filename));
let value;
try {
value = JSON.parse(decodeText(await this.read(filename), this.name(filename)));
} catch (error) {
throw unsupported(`parse JSON: ${error.message}`, this.name(filename));
}
const lines = [`const value = ${jsonExpression(value)};`, "export default value;"];
if (value && typeof value === "object" && !Array.isArray(value)) {
let index = 0;
for (const key of Object.keys(value)) {
if (key === "default" || key === "__esModule" || key === "__proto__" || !/^[a-z_$][\w$]*$/iu.test(key))
continue;
const binding = `jsonValue${index++}`;
lines.push(`const ${binding} = value[${stringLiteral(key)}]; export { ${binding} as ${key} };`);
}
}
return lines.join("\n");
}
if (!JS_EXTENSIONS.includes(extension)) {
if (!inside(this.content, filename)) throw unsupported("embed a protected file as an asset", this.name(filename));
return `export default ${jsonExpression(await this.dataUrl(filename, fragment))};`;
}
const original = decodeText(await this.read(filename), this.name(filename));
let source;
let ast;
try {
source = this.compiler.transform(original, {
filePath: this.name(filename),
commonjs: false,
typescript: [".ts", ".tsx", ".mts"].includes(extension),
});
ast = this.compiler.parseJavaScript(source, { sourceType: "module", locations: true });
} catch (error) {
throw unsupported(`parse authored JavaScript: ${error.message}`, this.name(filename));
}
const inspected = inspectJavaScript(ast, this.name(filename));
const edits = [];
for (const imported of inspected.imports) {
const target = await this.resolveImport(imported.specifier, filename, imported.names);
edits.push({ start: imported.start, end: imported.end, text: stringLiteral(target) });
}
return applyEdits(source, edits);
}
async collect() {
const themeCss = await this.css(this.themeFile);
const conventionalFiles = ["dashboard/dashboard.css", "report/report.css"].map((name) => join(this.content, name));
for (const filename of conventionalFiles) {
await this.css(filename);
this.styleFiles.add(filename);
}
const dashboard = await this.resolveImport(
"./src/content/dashboard/DashboardContent.jsx",
join(this.root, "entry.js"),
);
const report = await this.resolveImport("./src/content/report/ReportContent.jsx", join(this.root, "entry.js"));
this.modules.set(ENTRY_ID, {
id: ENTRY_ID,
source: `export { DashboardContent } from ${stringLiteral(
dashboard,
)};\nexport { ReportContent } from ${stringLiteral(report)};`,
});
return {
themeCss,
// The starter loads these before print.css; component-imported styles
// follow print.css. Keep the combined field for existing graph consumers.
conventionalCss: conventionalFiles.map((filename) => this.cssCache.get(filename)).join("\n"),
importedCss: [...this.styleFiles]
.filter((filename) => !conventionalFiles.includes(filename))
.map((filename) => this.cssCache.get(filename))
.join("\n"),
styles: [...this.styleFiles].map((filename) => this.cssCache.get(filename)).join("\n"),
};
}
}
function validateBundledCode(code, compiler, allowed) {
const ast = compiler.parseJavaScript(code, { sourceType: "script", locations: true });
const inspected = inspectJavaScript(ast, "generated authored bundle", { generated: true });
if (inspected.imports.length) throw new Error("Authored bundle retained an external ESM import.");
for (const reference of inspected.freeNodeReferences) {
if (reference.node.name !== "require") continue;
const call = reference.parent;
if (
call?.type !== "CallExpression" ||
reference.key !== "callee" ||
call.arguments.length !== 1 ||
!allowed.has(importValue(call.arguments[0]))
)
throw new Error("Authored bundle retained an unknown CommonJS dependency.");
}
return inspected.identifiers;
}
async function emitBundle(graph) {
if (typeof graph.compiler.rollup !== "function") {
throw new Error(
"The bundled Data compiler is missing its reviewed module bundler; reinstall a complete Data plugin.",
);
}
const externals = new Set(graph.worker ? [] : [...RUNTIME_MODULES, SNAPSHOT_ID]);
const warnings = [];
const plugin = {
name: "closed-data-authored-graph",
resolveId(id) {
if (graph.modules.has(id)) return id;
if (externals.has(id)) return { id, external: true };
throw unsupported(`resolve an undeclared bundled module ${JSON.stringify(id)}`);
},
load(id) {
const record = graph.modules.get(id);
if (!record || typeof record.source !== "string") throw new Error(`Missing in-memory authored module: ${id}`);
return record.source;
},
};
let bundle;
let code;
try {
bundle = await graph.compiler.rollup({
input: ENTRY_ID,
plugins: [plugin],
external: (id) => externals.has(id),
preserveEntrySignatures: "strict",
// The large product runtime was already tree-shaken at release time.
// Keep the small authored graph intact so Rollup also links otherwise
// unused reexports instead of silently discarding a missing export.
treeshake: false,
onwarn(warning) {
// Rollup may downgrade an unused missing import to a warning. ESM
// still rejects that import at link time, so publishing must fail too.
if (warning.code === "MISSING_EXPORT") throw new Error(warning.message);
warnings.push({ code: warning.code, message: warning.message });
},
});
const result = await bundle.generate({
format: graph.worker ? "iife" : "cjs",
exports: "named",
inlineDynamicImports: true,
dynamicImportInCjs: false,
interop: "esModule",
externalLiveBindings: true,
sourcemap: false,
strict: true,
});
if (result.output.length !== 1 || result.output[0].type !== "chunk") {
throw new Error("The authored bundle must contain exactly one JavaScript chunk.");
}
code = result.output[0].code;
} catch (error) {
throw unsupported(`bundle authored modules: ${error.message}`);
} finally {
await bundle?.close();
}
const identifiers = validateBundledCode(code, graph.compiler, externals);
return { code, warnings, identifiers };
}
async function emitFactory(graph) {
const { code, warnings, identifiers } = await emitBundle(graph);
let prefix = "__dataAuthored";
while ([...identifiers].some((name) => name.startsWith(prefix))) prefix += "_";
const runtime = `${prefix}Runtime`;
const snapshot = `${prefix}Snapshot`;
const data = `${prefix}Data`;
const allowed = `${prefix}Allowed`;
const load = `${prefix}Require`;
const module = `${prefix}Module`;
const key = `${prefix}Key`;
const wanted = `${prefix}Wanted`;
const namespace = `${prefix}Namespace`;
const requirements = [...graph.externalImports].map(([name, names]) => [name, [...names].sort()]);
const factorySource =
`(function(${runtime}, ${snapshot}) {\n"use strict";\n` +
`const ${data} = Object.create(null);\n` +
`Object.defineProperty(${data}, "__esModule", {value:true});\n` +
`Object.defineProperty(${data}, "default", {enumerable:true, value:${snapshot}});\n` +
`if (${snapshot} && typeof ${snapshot} === "object") for (const ${key} of Object.keys(${snapshot})) {\n` +
`if (${key} !== "default" && ${key} !== "__esModule" && ${key} !== "__proto__") Object.defineProperty(${data}, ${key}, {enumerable:true, get:()=>${snapshot}[${key}]});\n}\n` +
`Object.freeze(${data});\nconst ${allowed} = new Set(${jsonExpression(AUTHORED_RUNTIME_MODULES)});\n` +
`function ${load}(id) {\nif (id === ${stringLiteral(SNAPSHOT_ID)}) return ${data};\n` +
`if (${allowed}.has(id) && ${runtime}?.modules && Object.prototype.hasOwnProperty.call(${runtime}.modules,id)) return ${runtime}.modules[id];\n` +
`throw new Error("Missing bundled Data runtime module: " + id);\n}\n` +
`for (const [${key}, ${wanted}] of ${jsonExpression(requirements)}) {\nconst ${namespace} = ${load}(${key});\n` +
`for (const name of ${wanted}) if (!Object.prototype.hasOwnProperty.call(${namespace},name)) throw new Error("Missing bundled Data export " + name + " from " + ${key});\n}\n` +
`const ${module} = {exports:Object.create(null)};\n` +
`(function(module,exports,require){\n${code}\n})(${module},${module}.exports,${load});\n` +
`return {DashboardContent:${module}.exports.DashboardContent,ReportContent:${module}.exports.ReportContent};\n})`;
graph.compiler.parseJavaScript(factorySource, { sourceType: "script" });
return { factorySource, warnings };
}
/** Compile only the supported, contained authored graph; no author code runs in Node. */
export async function compileAuthoredModules({ projectRoot, compiler, runtimeModuleExports }) {
if (compiler?.apiVersion !== 1) throw new Error("Unsupported bundled Data compiler API version.");
const exports = validateRuntimeModuleExports(runtimeModuleExports);
const graph = new AuthoredGraph(await realpath(resolve(projectRoot)), compiler, exports);
const styles = await graph.collect();
const emitted = await emitFactory(graph);
return {
...emitted,
...styles,
moduleCount: [...graph.modules.values()].filter((record) => record.filename).length + graph.workerModuleCount,
assetCount: graph.assets.size,
cssFiles: [...graph.cssFiles].sort(),
sourceFiles: [...graph.sourceFiles].sort(),
};
}
SHA-256: 8122793525bee198992047179ac180e27a531d6a08547b34c03b5d7db3912ae4