Simplify workspace authoring and isolate managed component styles
Use imperative scaffolds without a registry/refresh lifecycle; preserve authored runtime wiring and generate checked descriptors. Consolidate binding options, embed checked TSX/CSS source, queue mutable convergence while allowing parallel immutable checks, and preload real CLI observations. Add Shadow DOM boundaries with scoped overlays and composed-event handling. Update template/toolchain pins and document VM-free authoring, browser, and stateful acceptance; no deployment or default-template selection.
This commit is contained in:
@@ -0,0 +1,41 @@
|
||||
import {parse} from "@babel/parser";
|
||||
import fs from "node:fs/promises";
|
||||
import path from "node:path";
|
||||
|
||||
/** Enforce the authored bundled-code contract, not a security sandbox. */
|
||||
export function bundlePolicyErrors(source: string, filename: string): string[] {
|
||||
const ast = parse(source, {sourceType: "module", plugins: ["typescript", "jsx"]});
|
||||
const errors: string[] = [];
|
||||
const visit = (value: unknown) => {
|
||||
if (Array.isArray(value)) {value.forEach(visit); return;}
|
||||
if (!value || typeof value !== "object") return;
|
||||
const n = value as Record<string, any>;
|
||||
if (typeof n.type !== "string") return;
|
||||
const fail = (message: string) => errors.push(`${filename}:${n.loc?.start.line ?? 1}: ${message}`);
|
||||
if (n.type === "MetaProperty" && n.meta.name === "import") fail("Bundled package code cannot use import.meta; import packaged assets statically");
|
||||
if (n.type === "Identifier" && ["__dirname", "__filename"].includes(n.name)) fail("Bundled package code cannot depend on module filesystem locations");
|
||||
if (["CallExpression", "NewExpression"].includes(n.type)) {
|
||||
if (n.callee?.type === "Identifier" && ["eval", "Function"].includes(n.callee.name)) fail("Dynamic code generation is unsupported in bundled package code");
|
||||
if ((n.callee?.type === "Import" || (n.callee?.type === "Identifier" && n.callee.name === "require")) &&
|
||||
(n.arguments.length !== 1 || n.arguments[0].type !== "StringLiteral")) fail("Module imports must have a static string specifier");
|
||||
}
|
||||
if (n.type === "ImportExpression" && n.source.type !== "StringLiteral") fail("Module imports must have a static string specifier");
|
||||
Object.values(n).forEach(visit);
|
||||
};
|
||||
visit(ast);
|
||||
return errors;
|
||||
}
|
||||
export async function checkBundleSources(directory: string): Promise<void> {
|
||||
const errors: string[] = [];
|
||||
async function walk(current: string) {
|
||||
for (const entry of await fs.readdir(current, {withFileTypes: true})) {
|
||||
if (["gen", "node_modules"].includes(entry.name)) continue;
|
||||
const file = path.join(current, entry.name);
|
||||
if (entry.isSymbolicLink()) throw new Error(`Authored source symlinks are unsupported: ${file}`);
|
||||
if (entry.isDirectory()) await walk(file);
|
||||
else if (/\.[cm]?[jt]sx?$/.test(entry.name) && !entry.name.endsWith(".d.ts")) errors.push(...bundlePolicyErrors(await fs.readFile(file, "utf8"), file));
|
||||
}
|
||||
}
|
||||
await walk(directory);
|
||||
if (errors.length) throw new Error(errors.join("\n"));
|
||||
}
|
||||
Reference in New Issue
Block a user