@monimesl/dot-compiler 0.1.0-alpha.8 → 0.1.0-alpha.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +10 -16
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +83 -105
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
package/dist/index.d.ts
CHANGED
|
@@ -54,10 +54,10 @@ export declare class CompileWarning extends SourceError {
|
|
|
54
54
|
//#region src/build/build.d.ts
|
|
55
55
|
interface BuildOptions {
|
|
56
56
|
root: string;
|
|
57
|
-
outDir?: string;
|
|
58
|
-
mode?: 'production' | 'development';
|
|
59
|
-
components?: readonly ComponentManifest[];
|
|
60
|
-
conditions?: readonly string[];
|
|
57
|
+
outDir?: string | undefined;
|
|
58
|
+
mode?: 'production' | 'development' | undefined;
|
|
59
|
+
components?: readonly ComponentManifest[] | undefined;
|
|
60
|
+
conditions?: readonly string[] | undefined;
|
|
61
61
|
}
|
|
62
62
|
interface BuildResult {
|
|
63
63
|
readonly outDir: string;
|
|
@@ -90,11 +90,12 @@ export declare function installedRuntime(root: string): InstalledRuntime;
|
|
|
90
90
|
//#region src/view/compiler.d.ts
|
|
91
91
|
interface ViewContext {
|
|
92
92
|
file: string;
|
|
93
|
-
components?: ComponentRegistry;
|
|
94
|
-
handlers?: readonly string[];
|
|
95
|
-
stateKeys?: readonly string[];
|
|
96
|
-
statics?: readonly string[];
|
|
97
|
-
onWarning?: (warning: CompileWarning) => void;
|
|
93
|
+
components?: ComponentRegistry | undefined;
|
|
94
|
+
handlers?: readonly string[] | undefined;
|
|
95
|
+
stateKeys?: readonly string[] | undefined;
|
|
96
|
+
statics?: readonly string[] | undefined;
|
|
97
|
+
onWarning?: ((warning: CompileWarning) => void) | undefined;
|
|
98
|
+
onComponent?: ((name: string) => void) | undefined;
|
|
98
99
|
}
|
|
99
100
|
export declare const ROOT_TAG = "View";
|
|
100
101
|
export declare const DATA_PREFIX = "data-";
|
|
@@ -102,13 +103,6 @@ export declare function dataKey(attribute: string): string;
|
|
|
102
103
|
export declare function canHaveChildren(parent: ComponentManifest, child: ComponentManifest): boolean;
|
|
103
104
|
//#endregion
|
|
104
105
|
//#region src/view/compile.d.ts
|
|
105
|
-
/**
|
|
106
|
-
* Compiles `.dml` markup into a renderer-agnostic ES module:
|
|
107
|
-
* `export default function render(d, { h, on, text, each }) { return h("View", ...) }`.
|
|
108
|
-
*
|
|
109
|
-
* The 'h' will be a function from the renderer to create the virtual element for a component. The
|
|
110
|
-
* function returns the page's root element, the <View>.
|
|
111
|
-
*/
|
|
112
106
|
export declare function compileView(source: string, context: ViewContext): string;
|
|
113
107
|
//#endregion
|
|
114
108
|
//#region src/view/control-flow.d.ts
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","names":[],"sources":["../src/project/categories.ts","../src/project/dot-json.ts","../src/project/project.ts","../src/errors.ts","../src/build/build.ts","../src/build/packages.ts","../src/view/compiler.ts","../src/view/compile.ts","../src/view/control-flow.ts","../src/project/statics.ts"],"mappings":";;;;cACa;KAwCD,sBAAsB;;;UCJjB;WACN;WACA;WACA;WACA;WAEA,sBAAsB;WAEtB;WAEA,cAAc,SAAS,QAAQ,OAAO;WACtC,UAAU;WACV,OAAO;;
|
|
1
|
+
{"version":3,"file":"index.d.ts","names":[],"sources":["../src/project/categories.ts","../src/project/dot-json.ts","../src/project/project.ts","../src/errors.ts","../src/build/build.ts","../src/build/packages.ts","../src/view/compiler.ts","../src/view/compile.ts","../src/view/control-flow.ts","../src/project/statics.ts"],"mappings":";;;;cACa;KAwCD,sBAAsB;;;UCJjB;WACN;WACA;WACA;WACA;WAEA,sBAAsB;WAEtB;WAEA,cAAc,SAAS,QAAQ,OAAO;WACtC,UAAU;WACV,OAAO;;UAIR;WAEC;;UAID;WAEC;;;;qBCjDE;UAOI;WAEN;WAEA;WACA;WACA;WACA;;UAGM;WACN;WAEA,SAAS;WAET;WAEA,gBAAgB;WAEhB;;wBAGW,YAAY,eAAe,QAAQ;;;qBCvC5C,qBAAqB;WACd;;;qBAIP,uBAAuB;WAChB;;;;UC0BH;EACf;EAEA;EAEA;EACA,sBAAsB;EACtB;;UAGe;WACN;WACA;WACA,mBAAmB;;;wBAIR,MAAM,SAAS,eAAe,QAAQ;;UAqJ3C,oBAAoB,KAAK;WAE/B;WAEA;WACA;WACA;WAEA;;;;UCtMM;WACN;WACA,OAAO;WAEP;;UAIM;WACN;WAEA;;wBAQK,iBAAiB,eAAe;;;UCb/B;EACf;EACA,aAAa;EAEb;EAEA;EAEA;EACA,cAAc,SAAS;EAEvB,gBAAgB;;qBAKL;qBAMA;wBAmWG,QAAQ;wBASR,gBAAgB,QAAQ,mBAAmB,OAAO;;;wBC7YlD,YAAY,gBAAgB,SAAS;;;qBCCxC,mBAAmB,SAC9B;WAA0B;WAAwC;;;;qBCCvD;wBAoBS,YAAY,eAAe"}
|
package/dist/index.js
CHANGED
|
@@ -90,19 +90,19 @@ async function readDotJson(file) {
|
|
|
90
90
|
try {
|
|
91
91
|
dotJson = JSON.parse(await readFile(file, "utf8"));
|
|
92
92
|
} catch (error) {
|
|
93
|
-
throw new CompileError(`
|
|
93
|
+
throw new CompileError(`This isn't valid JSON: ${error.message}`, file);
|
|
94
94
|
}
|
|
95
|
-
if (!isObject(dotJson)) throw new CompileError("
|
|
96
|
-
if ("pages" in dotJson) throw new CompileError("
|
|
95
|
+
if (!isObject(dotJson)) throw new CompileError("dot.json must be an object, such as { \"id\": \"com.example.shop\", \"name\": \"Shop\", \"version\": \"1.0.0\" }", file);
|
|
96
|
+
if ("pages" in dotJson) throw new CompileError("List pages in dot.ts, not here: defineDot({ pages: [...] })", file);
|
|
97
97
|
rejectUnknownFields(dotJson, DOT_JSON_FIELDS, file);
|
|
98
98
|
for (const field of REQUIRED_DOT_JSON_FIELDS) if (dotJson[field] === void 0) throw new CompileError(`"${field}" is required`, file);
|
|
99
99
|
const { $schema, id, name, version, description, categories, icon, permissions, network, dash } = dotJson;
|
|
100
100
|
if ($schema !== void 0 && typeof $schema !== "string") throw new CompileError("\"$schema\" must be a string", file);
|
|
101
|
-
if (typeof id !== "string" || !DOT_ID_PATTERN.test(id)) throw new CompileError("\"id\" must be a reverse domain name, such as \"com.example.counter\"
|
|
101
|
+
if (typeof id !== "string" || !DOT_ID_PATTERN.test(id)) throw new CompileError("\"id\" must be a reverse domain name, such as \"com.example.counter\". Use lowercase letters, digits and hyphens, with dots between words. A word can't start or end with a hyphen, or be longer than 63 characters.", file);
|
|
102
102
|
if (id.length > 128) throw new CompileError(`"id" must be at most 128 characters`, file);
|
|
103
|
-
if (!isNonEmptyString(name)) throw new CompileError("\"name\" must be
|
|
103
|
+
if (!isNonEmptyString(name)) throw new CompileError("\"name\" must be text, and not empty", file);
|
|
104
104
|
if (!isSemanticVersion(version)) throw new CompileError("\"version\" must be a semantic version, such as \"1.0.0\"", file);
|
|
105
|
-
if (description !== void 0 && !isNonEmptyString(description)) throw new CompileError("\"description\" must be
|
|
105
|
+
if (description !== void 0 && !isNonEmptyString(description)) throw new CompileError("\"description\" must be text, and not empty", file);
|
|
106
106
|
if (icon !== void 0 && !(isNonEmptyString(icon) && icon.endsWith(".png"))) throw new CompileError("\"icon\" must be the path to a PNG file, such as \"icon.png\"", file);
|
|
107
107
|
return {
|
|
108
108
|
id,
|
|
@@ -144,7 +144,7 @@ function readNetworkAccess(network, file) {
|
|
|
144
144
|
if (!Array.isArray(hosts) || hosts.length === 0) throw new CompileError("\"network.hosts\" must list the hosts the dot sends requests to, such as [\"api.example.com\"]", file);
|
|
145
145
|
const listed = [];
|
|
146
146
|
for (const host of hosts) {
|
|
147
|
-
if (typeof host !== "string" || !isNetworkHost(host)) throw new CompileError(`"${String(host)}" is not a host
|
|
147
|
+
if (typeof host !== "string" || !isNetworkHost(host)) throw new CompileError(`"${String(host)}" is not a host. Use a lowercase host name, such as "api.example.com", or "*.example.com" for all its subdomains. Leave out https://, the port and the path.`, file);
|
|
148
148
|
if (listed.includes(host)) throw new CompileError(`"network.hosts" lists "${host}" more than once`, file);
|
|
149
149
|
listed.push(host);
|
|
150
150
|
}
|
|
@@ -213,10 +213,10 @@ async function listStatics(root) {
|
|
|
213
213
|
}
|
|
214
214
|
async function checkImage(file, extension) {
|
|
215
215
|
const isFormat = IMAGE_FORMATS[extension];
|
|
216
|
-
if (!isFormat) throw new CompileError(
|
|
216
|
+
if (!isFormat) throw new CompileError(`${STATICS_DIR}/ can only hold images: ${STATIC_ASSET_EXTENSIONS.join(", ")}`, file);
|
|
217
217
|
const { size } = await stat(file);
|
|
218
|
-
if (size > 1048576) throw new CompileError(`The image is ${(size / MAX_STATIC_ASSET_BYTES).toFixed(1)} MB
|
|
219
|
-
if (!isFormat(await readFile(file))) throw new CompileError(`The file
|
|
218
|
+
if (size > 1048576) throw new CompileError(`The image is ${(size / MAX_STATIC_ASSET_BYTES).toFixed(1)} MB. Images must be 1 MB or smaller`, file);
|
|
219
|
+
if (!isFormat(await readFile(file))) throw new CompileError(`The file isn't really a ${extension.slice(1)} image`, file);
|
|
220
220
|
}
|
|
221
221
|
function isJpeg(header) {
|
|
222
222
|
return header[0] === 255 && header[1] === 216 && header[2] === 255;
|
|
@@ -233,11 +233,11 @@ const PAGE_FILES = {
|
|
|
233
233
|
async function loadProject(root) {
|
|
234
234
|
root = resolve(root);
|
|
235
235
|
const dotJsonFile = join(root, DOT_JSON_FILE);
|
|
236
|
-
if (!existsSync(dotJsonFile)) throw new CompileError(`
|
|
236
|
+
if (!existsSync(dotJsonFile)) throw new CompileError(`There is no ${DOT_JSON_FILE} in this folder`, root);
|
|
237
237
|
const dotJson = await readDotJson(dotJsonFile);
|
|
238
238
|
if (dotJson.icon !== void 0 && !existsSync(join(root, dotJson.icon))) throw new CompileError(`The icon "${dotJson.icon}" is missing`, dotJsonFile);
|
|
239
239
|
const dotFile = join(root, DOT_FILE);
|
|
240
|
-
if (!existsSync(dotFile)) throw new CompileError(`
|
|
240
|
+
if (!existsSync(dotFile)) throw new CompileError(`There is no ${DOT_FILE}. Add one: export default defineDot({ pages: [...] })`, root);
|
|
241
241
|
const { pages } = parseDotModule(await readFile(dotFile, "utf8"), dotFile);
|
|
242
242
|
const sources = await Promise.all(pages.map((path) => loadPage(root, path, dotFile)));
|
|
243
243
|
const seen = /* @__PURE__ */ new Map();
|
|
@@ -295,10 +295,11 @@ function literal(value) {
|
|
|
295
295
|
function pageTable(project, binding) {
|
|
296
296
|
return `{${project.pages.map((page, index) => `\n ${literal(page.id)}: ${binding}${index},`).join("")}\n }`;
|
|
297
297
|
}
|
|
298
|
-
function uiEntry(project) {
|
|
298
|
+
function uiEntry(project, components) {
|
|
299
|
+
const renderer = components ? [`import { createReactRenderer, ${components.join(", ")} } from ${literal(RUNTIME.renderer)};`, `const createRenderer = () => createReactRenderer({ components: { ${components.join(", ")} } });`] : [`import createRenderer from ${literal(RUNTIME.renderer)};`];
|
|
299
300
|
return [
|
|
300
301
|
`import { runWebview } from ${literal(RUNTIME.ui)};`,
|
|
301
|
-
|
|
302
|
+
...renderer,
|
|
302
303
|
...project.pages.map((page, index) => `import view${index} from ${literal(viewModuleId(page.id))};`),
|
|
303
304
|
"",
|
|
304
305
|
"runWebview({",
|
|
@@ -346,7 +347,7 @@ function dotGlobals(root) {
|
|
|
346
347
|
transform(code, id) {
|
|
347
348
|
if (!isDotModule(id)) return null;
|
|
348
349
|
const used = new Set(unboundReferences(code, Object.values(DOT_GLOBALS).flat(), id));
|
|
349
|
-
if (isPageModule(id) && used.has("dash")) this.error(`${relative(realRoot, id)} uses the global \`dash\`. In a page,
|
|
350
|
+
if (isPageModule(id) && used.has("dash")) this.error(`${relative(realRoot, id)} uses the global \`dash\`. In a page, use \`this.dash\` in actions and hooks, so its calls stop when the page closes.`);
|
|
350
351
|
const imports = [];
|
|
351
352
|
for (const [source, names] of Object.entries(DOT_GLOBALS)) {
|
|
352
353
|
const imported = names.filter((name) => used.has(name));
|
|
@@ -368,15 +369,14 @@ async function bundle(options) {
|
|
|
368
369
|
recursive: true,
|
|
369
370
|
force: true
|
|
370
371
|
});
|
|
372
|
+
const plugins = [];
|
|
373
|
+
if (options.external.length > 0) plugins.push(esmExternalRequirePlugin({ external: [...options.external] }));
|
|
374
|
+
plugins.push(generatedModules(options.root, options.modules), dotGlobals(options.root));
|
|
371
375
|
const bundler = await rolldown({
|
|
372
376
|
input: options.entry,
|
|
373
377
|
cwd: options.root,
|
|
374
378
|
platform: "browser",
|
|
375
|
-
plugins
|
|
376
|
-
...options.external.length > 0 ? [esmExternalRequirePlugin({ external: [...options.external] })] : [],
|
|
377
|
-
generatedModules(options.root, options.modules),
|
|
378
|
-
dotGlobals(options.root)
|
|
379
|
-
],
|
|
379
|
+
plugins,
|
|
380
380
|
resolve: { conditionNames: [
|
|
381
381
|
...options.conditions,
|
|
382
382
|
"browser",
|
|
@@ -412,11 +412,12 @@ function generatedModules(root, modules) {
|
|
|
412
412
|
if (modules.has(source)) return `\0${source}`;
|
|
413
413
|
if (!from?.startsWith("\0dot:")) return null;
|
|
414
414
|
const resolved = await this.resolve(source, importer, { skipSelf: true });
|
|
415
|
-
if (!resolved) this.error(`
|
|
415
|
+
if (!resolved) this.error(`Can't find "${source}" from ${root}. Is it installed?`);
|
|
416
416
|
return resolved;
|
|
417
417
|
},
|
|
418
418
|
load(id) {
|
|
419
|
-
|
|
419
|
+
if (!id.startsWith("\0")) return null;
|
|
420
|
+
return modules.get(id.slice(1)) ?? null;
|
|
420
421
|
}
|
|
421
422
|
};
|
|
422
423
|
}
|
|
@@ -457,10 +458,6 @@ const EMPTY_SCOPE = /* @__PURE__ */ new Map();
|
|
|
457
458
|
function isIdentifier(name) {
|
|
458
459
|
return IDENTIFIER.test(name);
|
|
459
460
|
}
|
|
460
|
-
/**
|
|
461
|
-
* Prints a binding as JS. Loop variables read their local; everything else reads page data from
|
|
462
|
-
* `d`. Member access never throws on nullish values.
|
|
463
|
-
*/
|
|
464
461
|
function printExpr(expr, scope) {
|
|
465
462
|
const print = (node) => printExpr(node, scope);
|
|
466
463
|
switch (expr.type) {
|
|
@@ -512,11 +509,6 @@ const CONTROL_FLOW_TAGS = {
|
|
|
512
509
|
required: ["each"]
|
|
513
510
|
}
|
|
514
511
|
};
|
|
515
|
-
const CONTROL_FLOW = new Set(Object.keys(CONTROL_FLOW_TAGS));
|
|
516
|
-
/**
|
|
517
|
-
* `<If condition>`, then any `<ElseIf condition>`, then an optional `<Else>`, as consecutive
|
|
518
|
-
* siblings compile to a spread conditional: `...(a ? [...]: b ? [...]: [...])`.
|
|
519
|
-
*/
|
|
520
512
|
function compileConditional(compiler, branches, parent, scope) {
|
|
521
513
|
let code = "[]";
|
|
522
514
|
for (const branch of [...branches].reverse()) {
|
|
@@ -527,12 +519,6 @@ function compileConditional(compiler, branches, parent, scope) {
|
|
|
527
519
|
}
|
|
528
520
|
return `...(${code})`;
|
|
529
521
|
}
|
|
530
|
-
/**
|
|
531
|
-
* `<For each="{{ list }}" item="item" index="index" key="id">`. `item` and `index` default to those
|
|
532
|
-
* names. `key` is a property of each item, `*this` for the item itself, or `*index` to match items
|
|
533
|
-
* by position; it is optional, but a warning is logged when missing. Compiles to
|
|
534
|
-
* `each(list, (item, index) => [...], key?)`.
|
|
535
|
-
*/
|
|
536
522
|
function compileLoop(compiler, node, parent, scope) {
|
|
537
523
|
compiler.checkControlFlowElementAttributes(node);
|
|
538
524
|
const list = compiler.compileSingleBinding(node, "each", scope);
|
|
@@ -542,12 +528,12 @@ function compileLoop(compiler, node, parent, scope) {
|
|
|
542
528
|
};
|
|
543
529
|
checkVariableName("item", item);
|
|
544
530
|
checkVariableName("index", index);
|
|
545
|
-
if (item === index) compiler.fail("<For> `item` and `index`
|
|
531
|
+
if (item === index) compiler.fail("<For> `item` and `index` need different names", node.loc);
|
|
546
532
|
const itemVar = "$" + item;
|
|
547
533
|
const indexVar = "$" + index;
|
|
548
534
|
const inner = new Map(scope).set(item, itemVar).set(index, indexVar);
|
|
549
535
|
const args = [list, `(${itemVar}, ${indexVar}) => [${compiler.compile(node.children, parent, inner).join(", ")}]`];
|
|
550
|
-
if (key === void 0) compiler.warn("<For> has no `key`, so items are matched by position. Add key=\"<property>\" (or \"*this\")
|
|
536
|
+
if (key === void 0) compiler.warn("<For> has no `key`, so items are matched by position. Add key=\"<property>\" (or \"*this\") so rows stay right when items move, or key=\"*index\" to keep matching by position.", node.loc);
|
|
551
537
|
else if (key === "*index") {} else if (key === "*this") args.push(`(${itemVar}) => ${itemVar}`);
|
|
552
538
|
else if (isIdentifier(key)) args.push(`(${itemVar}) => ${itemVar}?.${key}`);
|
|
553
539
|
else compiler.fail(`<For> \`key\` must be a property name, *this or *index, got "${key}"`, node.loc);
|
|
@@ -570,14 +556,14 @@ var ViewCompiler = class {
|
|
|
570
556
|
constructor(context) {
|
|
571
557
|
this.#context = context;
|
|
572
558
|
this.#components = context.components ?? builtinRegistry;
|
|
573
|
-
for (const name of
|
|
559
|
+
for (const name of Object.keys(CONTROL_FLOW_TAGS)) if (name in this.#components) this.fail(`<${name}> is reserved for control flow`);
|
|
574
560
|
}
|
|
575
561
|
compile(nodes, parent, scope) {
|
|
576
562
|
const out = [];
|
|
577
563
|
for (let i = 0; i < nodes.length; i++) {
|
|
578
564
|
const node = nodes[i];
|
|
579
565
|
if (node.type === "Text") {
|
|
580
|
-
out.push(...this
|
|
566
|
+
out.push(...this.#compileText(node.content, parent, scope, node.loc));
|
|
581
567
|
continue;
|
|
582
568
|
}
|
|
583
569
|
switch (node.tag) {
|
|
@@ -588,48 +574,43 @@ var ViewCompiler = class {
|
|
|
588
574
|
break;
|
|
589
575
|
}
|
|
590
576
|
case "ElseIf":
|
|
591
|
-
case "Else": return this.fail(`<${node.tag}> must
|
|
577
|
+
case "Else": return this.fail(`<${node.tag}> must come right after <If> or <ElseIf>`, node.loc);
|
|
592
578
|
case "For":
|
|
593
579
|
out.push(compileLoop(this, node, parent, scope));
|
|
594
580
|
break;
|
|
595
|
-
default: out.push(this
|
|
581
|
+
default: out.push(`h(${this.#compileElementArgs(node, parent, scope).join(", ")})`);
|
|
596
582
|
}
|
|
597
583
|
}
|
|
598
584
|
return out;
|
|
599
585
|
}
|
|
600
|
-
compileText(content, parent, scope, loc) {
|
|
586
|
+
#compileText(content, parent, scope, loc) {
|
|
601
587
|
if (parent?.children !== "text") {
|
|
602
|
-
const wrappers = Object.values(this.#components).filter((
|
|
603
|
-
this.fail(`'${content}' must be
|
|
588
|
+
const wrappers = Object.values(this.#components).filter((component) => component.inline && component.children === "text").sort((a, b) => Number(b.name === "Text") - Number(a.name === "Text")).map((component) => `<${component.name}>`).join(" or ");
|
|
589
|
+
this.fail(`'${content}' must be inside ${wrappers || "an inline component"}`, loc);
|
|
604
590
|
}
|
|
605
|
-
return this
|
|
606
|
-
}
|
|
607
|
-
compileElement(node, parent, scope) {
|
|
608
|
-
return `h(${this.#compileElementArgs(node, parent, scope).join(", ")})`;
|
|
591
|
+
return this.#compileAsSegments(content, scope, loc).map(printSegment);
|
|
609
592
|
}
|
|
610
|
-
/**
|
|
611
|
-
* Compiles a page's markup, which must be a single <View> with everything else inside it, to the
|
|
612
|
-
* arguments of that root's `h` call.
|
|
613
|
-
*/
|
|
614
593
|
compileRoot(nodes) {
|
|
615
594
|
if (!this.#components["View"]) this.fail(`The components must include <${ROOT_TAG}>, which is the root of every page`);
|
|
616
595
|
const [root, outside] = nodes;
|
|
617
|
-
if (root?.type !== "Element" || root.tag !== "View") return this.fail(`A page
|
|
596
|
+
if (root?.type !== "Element" || root.tag !== "View") return this.fail(`A page must have one <${ROOT_TAG}> around everything else`, root?.loc);
|
|
618
597
|
if (outside) {
|
|
619
598
|
const name = outside.type === "Text" ? `'${outside.content}'` : `<${outside.tag}>`;
|
|
620
|
-
this.fail(`${name} must be inside the
|
|
599
|
+
this.fail(`${name} must be inside the page's <${ROOT_TAG}>`, outside.loc);
|
|
621
600
|
}
|
|
622
601
|
return this.#compileElementArgs(root, null, EMPTY_SCOPE, ROOT_DEFAULTS);
|
|
623
602
|
}
|
|
624
603
|
#compileElementArgs(node, parent, scope, defaults = {}) {
|
|
625
604
|
const component = this.#components[node.tag];
|
|
626
605
|
if (!component) return this.fail(`Unknown component <${node.tag}>.`, node.loc);
|
|
627
|
-
|
|
606
|
+
this.#context.onComponent?.(component.name);
|
|
607
|
+
if (parent && !canHaveChildren(parent, component)) this.fail(`<${node.tag}> can't go inside <${parent.name}>`, node.loc);
|
|
628
608
|
const attributes = Object.entries(node.attributes);
|
|
609
|
+
for (const { prop, ignores, because } of component.overrides ?? []) if (prop in node.attributes && ignores in node.attributes) this.warn(`<${component.name}> ignores \`${ignores}\` with \`${prop}\`: ${because}`, node.loc);
|
|
629
610
|
const data = this.#compileData(attributes.filter(([name]) => name.startsWith(DATA_PREFIX)), scope, node.loc);
|
|
630
611
|
const props = [
|
|
631
|
-
...attributes.filter(([name]) => !name.startsWith(DATA_PREFIX)).map(([name, value]) => this
|
|
632
|
-
...Object.entries(node.events).map(([name, handler]) => this
|
|
612
|
+
...attributes.filter(([name]) => !name.startsWith(DATA_PREFIX)).map(([name, value]) => this.#compileProp(component, name, value, scope, node.loc)),
|
|
613
|
+
...Object.entries(node.events).map(([name, handler]) => this.#compileEvent(component, name, handler, data, node.loc)),
|
|
633
614
|
...Object.entries(defaults).filter(([name]) => !(name in node.attributes)).map(([name, value]) => `${name}: ${JSON.stringify(value)}`)
|
|
634
615
|
];
|
|
635
616
|
return [
|
|
@@ -638,31 +619,31 @@ var ViewCompiler = class {
|
|
|
638
619
|
...this.compile(node.children, component, scope)
|
|
639
620
|
];
|
|
640
621
|
}
|
|
641
|
-
compileProp(component, name, value, scope, loc) {
|
|
622
|
+
#compileProp(component, name, value, scope, loc) {
|
|
642
623
|
const type = component.props[name];
|
|
643
|
-
if (!type) return this.fail(`<${component.name}>
|
|
644
|
-
const segments = this
|
|
624
|
+
if (!type) return this.fail(`<${component.name}> has no \`${name}\` attribute`, loc);
|
|
625
|
+
const segments = this.#compileAsSegments(value, scope, loc);
|
|
645
626
|
const bindingCode = singleBindingCode(segments);
|
|
646
627
|
if (bindingCode !== null) return `${name}: ${bindingCode}`;
|
|
647
628
|
if (segments.every((segment) => segment.kind === "static")) {
|
|
648
629
|
this.#checkStaticsReference(value, loc);
|
|
649
|
-
return `${name}: ${this
|
|
630
|
+
return `${name}: ${this.#compileLiteral(type, value, loc)}`;
|
|
650
631
|
}
|
|
651
632
|
if (type !== "string" && type !== "any") {
|
|
652
633
|
const kind = typeof type === "string" ? `a ${type}` : "an enum";
|
|
653
|
-
this.fail(`\`${name}\` is ${kind}
|
|
634
|
+
this.fail(`\`${name}\` is ${kind}, so set it with one {{ }} binding`, loc);
|
|
654
635
|
}
|
|
655
636
|
return `${name}: ${segments.map(printSegment).join(" + ")}`;
|
|
656
637
|
}
|
|
657
638
|
#compileData(attributes, scope, loc) {
|
|
658
639
|
if (attributes.length === 0) return null;
|
|
659
640
|
return `{ ${attributes.map(([name, value]) => {
|
|
660
|
-
if (!DATA_ATTRIBUTE.test(name)) this.fail(`\`${name}\` must
|
|
641
|
+
if (!DATA_ATTRIBUTE.test(name)) this.fail(`\`${name}\` must start with data- and use lowercase words joined by -, such as data-account-id`, loc);
|
|
661
642
|
return `${dataKey(name)}: ${this.#compileDataValue(value, scope, loc)}`;
|
|
662
643
|
}).join(", ")} }`;
|
|
663
644
|
}
|
|
664
645
|
#compileDataValue(value, scope, loc) {
|
|
665
|
-
const segments = this
|
|
646
|
+
const segments = this.#compileAsSegments(value, scope, loc);
|
|
666
647
|
const bindingCode = singleBindingCode(segments);
|
|
667
648
|
if (bindingCode !== null) return bindingCode;
|
|
668
649
|
if (segments.length === 0) return "\"\"";
|
|
@@ -672,9 +653,9 @@ var ViewCompiler = class {
|
|
|
672
653
|
const { statics } = this.#context;
|
|
673
654
|
if (statics && value.startsWith(`statics/`) && !statics.includes(value)) this.fail(`"${value}" is not in ${STATICS_DIR}/`, loc);
|
|
674
655
|
}
|
|
675
|
-
compileEvent(component, attribute, handler, data, loc) {
|
|
656
|
+
#compileEvent(component, attribute, handler, data, loc) {
|
|
676
657
|
const event = eventName(attribute);
|
|
677
|
-
if (!component.events.includes(event)) this.fail(`<${component.name}> has no
|
|
658
|
+
if (!component.events.includes(event)) this.fail(`<${component.name}> has no \`${attribute}\` event`, loc);
|
|
678
659
|
if (!isIdentifier(handler)) this.fail(`\`${attribute}\` must name a mutation or action in controller.ts, got "${handler}"`, loc);
|
|
679
660
|
const { handlers } = this.#context;
|
|
680
661
|
if (handlers && !handlers.includes(handler)) this.fail(`\`${handler}\` is not a mutation or action in controller.ts`, loc);
|
|
@@ -683,16 +664,16 @@ var ViewCompiler = class {
|
|
|
683
664
|
checkControlFlowElementAttributes(node) {
|
|
684
665
|
const { attributes: allowed, required } = CONTROL_FLOW_TAGS[node.tag];
|
|
685
666
|
const [event] = Object.keys(node.events);
|
|
686
|
-
if (event) this.fail(`<${node.tag}>
|
|
687
|
-
for (const name of Object.keys(node.attributes)) if (!allowed.includes(name)) this.fail(`<${node.tag}>
|
|
667
|
+
if (event) this.fail(`<${node.tag}> can't handle events such as \`${event}\`. Put the handler on a component inside it.`, node.loc);
|
|
668
|
+
for (const name of Object.keys(node.attributes)) if (!allowed.includes(name)) this.fail(`<${node.tag}> has no \`${name}\` attribute`, node.loc);
|
|
688
669
|
for (const name of required) if (!(name in node.attributes)) this.fail(`<${node.tag}> requires \`${name}\``, node.loc);
|
|
689
670
|
}
|
|
690
671
|
compileSingleBinding(node, attribute, scope) {
|
|
691
|
-
const bindingCode = singleBindingCode(this
|
|
692
|
-
if (bindingCode === null) return this.fail(`<${node.tag}> \`${attribute}\` must be
|
|
672
|
+
const bindingCode = singleBindingCode(this.#compileAsSegments(node.attributes[attribute] ?? "", scope, node.loc));
|
|
673
|
+
if (bindingCode === null) return this.fail(`<${node.tag}> \`${attribute}\` must be one {{ }} binding`, node.loc);
|
|
693
674
|
return bindingCode;
|
|
694
675
|
}
|
|
695
|
-
compileLiteral(type, value, loc) {
|
|
676
|
+
#compileLiteral(type, value, loc) {
|
|
696
677
|
if (typeof type !== "string") {
|
|
697
678
|
if (type.includes(value)) return JSON.stringify(value);
|
|
698
679
|
return this.fail(`Expected one of ${type.map((option) => `"${option}"`).join(", ")}, got "${value}"`, loc);
|
|
@@ -701,7 +682,7 @@ var ViewCompiler = class {
|
|
|
701
682
|
case "boolean":
|
|
702
683
|
if (value === "" || value === "true") return "true";
|
|
703
684
|
if (value === "false") return "false";
|
|
704
|
-
return this.fail(`Expected
|
|
685
|
+
return this.fail(`Expected true or false, got "${value}"`, loc);
|
|
705
686
|
case "number":
|
|
706
687
|
if (value.trim() !== "" && Number.isFinite(Number(value))) return String(Number(value));
|
|
707
688
|
return this.fail(`Expected a number, got "${value}"`, loc);
|
|
@@ -709,7 +690,7 @@ var ViewCompiler = class {
|
|
|
709
690
|
case "any": return JSON.stringify(value);
|
|
710
691
|
}
|
|
711
692
|
}
|
|
712
|
-
compileAsSegments(input, scope, loc) {
|
|
693
|
+
#compileAsSegments(input, scope, loc) {
|
|
713
694
|
const segments = [];
|
|
714
695
|
let last = 0;
|
|
715
696
|
for (const match of input.matchAll(SEGMENT_BINDING_REGEXP)) {
|
|
@@ -717,7 +698,7 @@ var ViewCompiler = class {
|
|
|
717
698
|
kind: "static",
|
|
718
699
|
value: input.slice(last, match.index)
|
|
719
700
|
});
|
|
720
|
-
const expr = this
|
|
701
|
+
const expr = this.#parseBindingExpression(match[1], scope, loc);
|
|
721
702
|
segments.push({
|
|
722
703
|
kind: "binding",
|
|
723
704
|
code: printExpr(expr, scope)
|
|
@@ -725,14 +706,14 @@ var ViewCompiler = class {
|
|
|
725
706
|
last = match.index + match[0].length;
|
|
726
707
|
}
|
|
727
708
|
const rest = input.slice(last);
|
|
728
|
-
if (rest.includes("{{")) this.fail("
|
|
709
|
+
if (rest.includes("{{")) this.fail("A binding is missing its closing `}}`", loc);
|
|
729
710
|
if (rest) segments.push({
|
|
730
711
|
kind: "static",
|
|
731
712
|
value: rest
|
|
732
713
|
});
|
|
733
714
|
return segments;
|
|
734
715
|
}
|
|
735
|
-
parseBindingExpression(source, scope, loc) {
|
|
716
|
+
#parseBindingExpression(source, scope, loc) {
|
|
736
717
|
let expr;
|
|
737
718
|
try {
|
|
738
719
|
expr = parseExpression(source);
|
|
@@ -745,7 +726,7 @@ var ViewCompiler = class {
|
|
|
745
726
|
const undeclared = stateReads(expr, scope).find((name) => !stateKeys.includes(name));
|
|
746
727
|
if (undeclared) {
|
|
747
728
|
const declared = stateKeys.join(", ") || "none";
|
|
748
|
-
this.fail(`\`${undeclared}\`
|
|
729
|
+
this.fail(`\`${undeclared}\` isn't in state.ts. It has: ${declared}`, loc);
|
|
749
730
|
}
|
|
750
731
|
}
|
|
751
732
|
return expr;
|
|
@@ -789,13 +770,6 @@ function canHaveChildren(parent, child) {
|
|
|
789
770
|
}
|
|
790
771
|
//#endregion
|
|
791
772
|
//#region src/view/compile.ts
|
|
792
|
-
/**
|
|
793
|
-
* Compiles `.dml` markup into a renderer-agnostic ES module:
|
|
794
|
-
* `export default function render(d, { h, on, text, each }) { return h("View", ...) }`.
|
|
795
|
-
*
|
|
796
|
-
* The 'h' will be a function from the renderer to create the virtual element for a component. The
|
|
797
|
-
* function returns the page's root element, the <View>.
|
|
798
|
-
*/
|
|
799
773
|
function compileView(source, context) {
|
|
800
774
|
const nodes = parseDmlToIR(source, {
|
|
801
775
|
locations: true,
|
|
@@ -879,20 +853,18 @@ async function build(options) {
|
|
|
879
853
|
const modules = await compileModules(project, env, componentRegistry([...Object.values(builtinComponents), ...options.components ?? []]), mode, warnings);
|
|
880
854
|
const outDir = resolve(project.root, options.outDir ?? "dist");
|
|
881
855
|
const uiDir = join(outDir, "ui");
|
|
882
|
-
const bundleLayer = (entry, layerDir, format, external) => {
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
});
|
|
895
|
-
};
|
|
856
|
+
const bundleLayer = (entry, layerDir, format, external) => bundle({
|
|
857
|
+
root: project.root,
|
|
858
|
+
modules,
|
|
859
|
+
entry,
|
|
860
|
+
outDir: layerDir,
|
|
861
|
+
fileName: "dot.js",
|
|
862
|
+
format,
|
|
863
|
+
mode,
|
|
864
|
+
minify: mode === "production",
|
|
865
|
+
conditions: options.conditions ?? [],
|
|
866
|
+
external
|
|
867
|
+
});
|
|
896
868
|
const files = [
|
|
897
869
|
...await bundleLayer(ENTRY.ui, uiDir, "esm", Object.keys(REACT_HOST_MODULES)),
|
|
898
870
|
...await bundleLayer(ENTRY.logic, join(outDir, "logic"), "iife", []),
|
|
@@ -930,11 +902,13 @@ async function writeArchive(project, outDir, files) {
|
|
|
930
902
|
}
|
|
931
903
|
async function compileModules(project, env, components, mode, warnings) {
|
|
932
904
|
const modules = /* @__PURE__ */ new Map();
|
|
905
|
+
const used = /* @__PURE__ */ new Set();
|
|
933
906
|
for (const page of project.pages) {
|
|
934
|
-
const view = await compilePage(page, components, project.statics, warnings);
|
|
907
|
+
const view = await compilePage(page, components, project.statics, warnings, used);
|
|
935
908
|
modules.set(viewModuleId(page.id), view);
|
|
936
909
|
}
|
|
937
|
-
|
|
910
|
+
const builtins = [...used].filter((name) => name in builtinComponents).sort();
|
|
911
|
+
modules.set(ENTRY.ui, uiEntry(project, mode === "production" ? builtins : void 0));
|
|
938
912
|
modules.set(ENTRY.logic, logicEntry(project, { strict: mode === "development" }));
|
|
939
913
|
modules.set(ENV_MODULE, envModule(env));
|
|
940
914
|
return modules;
|
|
@@ -986,7 +960,7 @@ function dotManifest(project, runtime) {
|
|
|
986
960
|
pages
|
|
987
961
|
};
|
|
988
962
|
}
|
|
989
|
-
async function compilePage(page, components, statics, warnings) {
|
|
963
|
+
async function compilePage(page, components, statics, warnings, used) {
|
|
990
964
|
const [view, state, controller] = await Promise.all([
|
|
991
965
|
readFile(page.view, "utf8"),
|
|
992
966
|
readFile(page.state, "utf8"),
|
|
@@ -996,7 +970,8 @@ async function compilePage(page, components, statics, warnings) {
|
|
|
996
970
|
file: page.view,
|
|
997
971
|
components,
|
|
998
972
|
statics,
|
|
999
|
-
onWarning: (warning) => warnings.push(warning)
|
|
973
|
+
onWarning: (warning) => warnings.push(warning),
|
|
974
|
+
onComponent: (name) => used.add(name)
|
|
1000
975
|
};
|
|
1001
976
|
const { keys } = parseStateModule(state, page.state);
|
|
1002
977
|
if (keys) context.stateKeys = keys;
|
|
@@ -1012,7 +987,10 @@ function indexHtml(title, reactVersion) {
|
|
|
1012
987
|
<html lang="en">
|
|
1013
988
|
<head>
|
|
1014
989
|
<meta charset="UTF-8" />
|
|
1015
|
-
<meta
|
|
990
|
+
<meta
|
|
991
|
+
name="viewport"
|
|
992
|
+
content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"
|
|
993
|
+
/>
|
|
1016
994
|
<title>${escaped}</title>
|
|
1017
995
|
<link rel="stylesheet" href="./${STYLES_FILE}" />
|
|
1018
996
|
<style>
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../src/errors.ts","../src/project/categories.ts","../src/project/dot-json.ts","../src/project/statics.ts","../src/project/project.ts","../src/entries.ts","../src/globals.ts","../src/build/bundle.ts","../src/build/packages.ts","../src/view/expression.ts","../src/view/control-flow.ts","../src/view/compiler.ts","../src/view/compile.ts","../src/types.ts","../src/project/env.ts","../src/build/build.ts"],"sourcesContent":["import { SourceError } from '@monimesl/dot-core';\n\nexport class CompileError extends SourceError {\n override readonly name = 'CompileError';\n}\n\n/** A problem that does not stop compilation. */\nexport class CompileWarning extends SourceError {\n override readonly name = 'CompileWarning';\n}\n","// Where Dash lists a dot, in snake case. The runtime's dot.schema.json lists the same ones.\nexport const CATEGORIES = [\n 'agriculture',\n 'asset_finance',\n 'donations',\n 'education',\n 'electricity',\n 'entertainment',\n 'events',\n 'finance',\n 'food_and_drink',\n 'gambling',\n 'games',\n 'government',\n 'health',\n 'insurance',\n 'internet_service_provider',\n 'investments',\n 'jobs',\n 'lifestyle',\n 'loans',\n 'micro_credit',\n 'microfinance',\n 'news',\n 'other',\n 'productivity',\n 'real_estate',\n 'religion',\n 'remittances',\n 'savings',\n 'shopping',\n 'social',\n 'sports',\n 'telecom',\n 'television',\n 'transport',\n 'travel',\n 'utilities',\n 'water',\n] as const;\n\nexport type DotCategory = (typeof CATEGORIES)[number];\n\nexport function isCategory(value: unknown): value is DotCategory {\n return (CATEGORIES as readonly unknown[]).includes(value);\n}\n","import { readFile } from 'node:fs/promises';\n\nimport type { DotPermission } from '@monimesl/dot-core';\nimport { PERMISSIONS, isNetworkHost, isPermission, isSemanticVersion } from '@monimesl/dot-core';\n\nimport { CompileError } from '../errors';\nimport type { DotCategory } from './categories';\nimport { CATEGORIES, isCategory } from './categories';\n\nexport const DOT_JSON_FILE = 'dot.json';\n\n// The fields dot.json allows. The runtime's `dot.schema.json` describes the same ones for editors,\n// and a test keeps the two in step.\nexport const DOT_JSON_FIELDS: readonly string[] = [\n '$schema',\n 'id',\n 'name',\n 'version',\n 'description',\n 'categories',\n 'icon',\n 'permissions',\n 'network',\n 'dash',\n];\nexport const REQUIRED_DOT_JSON_FIELDS: readonly string[] = ['id', 'name', 'version'];\nexport const DASH_FIELDS: readonly string[] = ['minVersion'];\nexport const NETWORK_FIELDS: readonly string[] = ['hosts'];\n\n// A reverse domain name, which stays the same for the dot's whole life: `com.example.counter`.\n// Hosts give each dot its own web origin named after it, so it must also be a valid host name.\n// Each word is lowercase letters and digits with hyphens inside, up to 63 characters; the first\n// starts with a letter.\nconst ID_WORD = '[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?';\nexport const DOT_ID_PATTERN = new RegExp(`^(?=[a-z])${ID_WORD}(?:\\\\.${ID_WORD})+$`);\nexport const DOT_ID_MAX_LENGTH = 128;\n\nexport interface DotJson {\n readonly id: string;\n readonly name: string;\n readonly version: string;\n readonly description?: string | undefined;\n // Where Dash lists the dot: one or more, with none repeated.\n readonly categories?: readonly DotCategory[] | undefined;\n // A PNG file, relative to the project root.\n readonly icon?: string | undefined;\n // Each permission the dot uses, with the reason Dash shows the person.\n readonly permissions?: Readonly<Partial<Record<DotPermission, string>>> | undefined;\n readonly network?: NetworkAccess | undefined;\n readonly dash?: DashRequirements | undefined;\n}\n\n// Where the dot may send requests with dash.network. Without it, it may send none.\nexport interface NetworkAccess {\n // Host names, such as \"api.example.com\", or \"*.example.com\" for any subdomain.\n readonly hosts: readonly string[];\n}\n\n// What a dot needs from Dash, the app it runs in.\nexport interface DashRequirements {\n // The oldest Dash version the dot runs in.\n readonly minVersion?: string | undefined;\n}\n\nexport async function readDotJson(file: string): Promise<DotJson> {\n let dotJson: unknown;\n try {\n dotJson = JSON.parse(await readFile(file, 'utf8'));\n } catch (error) {\n throw new CompileError(`Invalid JSON: ${(error as Error).message}`, file);\n }\n if (!isObject(dotJson)) {\n throw new CompileError(\n 'Expected an object, such as { \"id\": \"com.example.shop\", \"name\": \"Shop\", \"version\": \"1.0.0\" }',\n file,\n );\n }\n if ('pages' in dotJson) {\n throw new CompileError('Pages are listed in dot.ts: defineDot({ pages: [...] })', file);\n }\n rejectUnknownFields(dotJson, DOT_JSON_FIELDS, file);\n for (const field of REQUIRED_DOT_JSON_FIELDS) {\n if (dotJson[field] === undefined) {\n throw new CompileError(`\"${field}\" is required`, file);\n }\n }\n\n const { $schema, id, name, version, description, categories, icon, permissions, network, dash } =\n dotJson;\n if ($schema !== undefined && typeof $schema !== 'string') {\n throw new CompileError('\"$schema\" must be a string', file);\n }\n if (typeof id !== 'string' || !DOT_ID_PATTERN.test(id)) {\n throw new CompileError(\n '\"id\" must be a reverse domain name, such as \"com.example.counter\": lowercase words of ' +\n \"letters, digits and hyphens, joined by dots. A word can't start or end with a hyphen, \" +\n 'or be longer than 63 characters.',\n file,\n );\n }\n if (id.length > DOT_ID_MAX_LENGTH) {\n throw new CompileError(`\"id\" must be at most ${DOT_ID_MAX_LENGTH} characters`, file);\n }\n if (!isNonEmptyString(name)) {\n throw new CompileError('\"name\" must be a non-empty string', file);\n }\n if (!isSemanticVersion(version)) {\n throw new CompileError('\"version\" must be a semantic version, such as \"1.0.0\"', file);\n }\n if (description !== undefined && !isNonEmptyString(description)) {\n throw new CompileError('\"description\" must be a non-empty string', file);\n }\n if (icon !== undefined && !(isNonEmptyString(icon) && icon.endsWith('.png'))) {\n throw new CompileError('\"icon\" must be the path to a PNG file, such as \"icon.png\"', file);\n }\n return {\n id,\n name,\n version,\n description,\n categories: readCategories(categories, file),\n icon,\n permissions: readPermissions(permissions, file),\n network: readNetworkAccess(network, file),\n dash: readDashRequirements(dash, file),\n };\n}\n\nfunction readCategories(categories: unknown, file: string): DotCategory[] | undefined {\n if (categories === undefined) {\n return undefined;\n }\n if (!Array.isArray(categories) || categories.length === 0) {\n throw new CompileError(\n '\"categories\" must list one or more categories, such as [\"finance\", \"savings\"]',\n file,\n );\n }\n const listed: DotCategory[] = [];\n for (const category of categories as unknown[]) {\n if (!isCategory(category)) {\n throw new CompileError(\n `\"${String(category)}\" is not a category. The categories are ${quotedList(CATEGORIES)}`,\n file,\n );\n }\n if (listed.includes(category)) {\n throw new CompileError(`\"categories\" lists \"${category}\" more than once`, file);\n }\n listed.push(category);\n }\n return listed;\n}\n\nfunction readPermissions(\n permissions: unknown,\n file: string,\n): Partial<Record<DotPermission, string>> | undefined {\n if (permissions === undefined) {\n return undefined;\n }\n if (!isObject(permissions)) {\n throw new CompileError(\n '\"permissions\" must be an object, such as { \"account\": \"Show your name on receipts\" }',\n file,\n );\n }\n for (const [permission, reason] of Object.entries(permissions)) {\n if (!isPermission(permission)) {\n throw new CompileError(\n `Unknown permission \"${permission}\". The permissions are ${quotedList(PERMISSIONS)}`,\n file,\n );\n }\n if (!isNonEmptyString(reason)) {\n throw new CompileError(\n `\"permissions.${permission}\" must say why the dot needs it, such as \"Show your name on receipts\"`,\n file,\n );\n }\n }\n return permissions;\n}\n\nfunction readNetworkAccess(network: unknown, file: string): NetworkAccess | undefined {\n if (network === undefined) {\n return undefined;\n }\n if (!isObject(network)) {\n throw new CompileError(\n '\"network\" must be an object, such as { \"hosts\": [\"api.example.com\"] }',\n file,\n );\n }\n rejectUnknownFields(network, NETWORK_FIELDS, file, 'network');\n const { hosts } = network;\n if (!Array.isArray(hosts) || hosts.length === 0) {\n throw new CompileError(\n '\"network.hosts\" must list the hosts the dot sends requests to, such as [\"api.example.com\"]',\n file,\n );\n }\n const listed: string[] = [];\n for (const host of hosts as unknown[]) {\n if (typeof host !== 'string' || !isNetworkHost(host)) {\n throw new CompileError(\n `\"${String(host)}\" is not a host: use a lowercase host name, such as \"api.example.com\", ` +\n 'or \"*.example.com\" for its subdomains, without a scheme, port or path',\n file,\n );\n }\n if (listed.includes(host)) {\n throw new CompileError(`\"network.hosts\" lists \"${host}\" more than once`, file);\n }\n listed.push(host);\n }\n return { hosts: listed };\n}\n\nfunction readDashRequirements(dash: unknown, file: string): DashRequirements | undefined {\n if (dash === undefined) {\n return undefined;\n }\n if (!isObject(dash)) {\n throw new CompileError('\"dash\" must be an object, such as { \"minVersion\": \"2.4.0\" }', file);\n }\n rejectUnknownFields(dash, DASH_FIELDS, file, 'dash');\n const { minVersion } = dash;\n if (minVersion !== undefined && !isSemanticVersion(minVersion)) {\n throw new CompileError('\"dash.minVersion\" must be a semantic version, such as \"2.4.0\"', file);\n }\n return { minVersion };\n}\n\nfunction rejectUnknownFields(\n object: Record<string, unknown>,\n allowed: readonly string[],\n file: string,\n parent?: string,\n): void {\n for (const field of Object.keys(object)) {\n if (allowed.includes(field)) {\n continue;\n }\n if (parent === undefined) {\n throw new CompileError(\n `Unknown field \"${field}\". The fields allowed are ${quotedList(allowed)}`,\n file,\n );\n }\n throw new CompileError(\n `Unknown field \"${parent}.${field}\". The fields allowed in \"${parent}\" are ${quotedList(allowed)}`,\n file,\n );\n }\n}\n\nfunction quotedList(values: readonly string[]): string {\n return values.map((value) => `\"${value}\"`).join(', ');\n}\n\nfunction isObject(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\nfunction isNonEmptyString(value: unknown): value is string {\n return typeof value === 'string' && value !== '';\n}\n","import { existsSync } from 'node:fs';\nimport { readdir, readFile, stat } from 'node:fs/promises';\nimport { extname, join, relative, sep } from 'node:path';\n\nimport { CompileError } from '../errors';\n\n// Images a dot shows. The build copies them next to the webview as they are, and markup and state\n// refer to them as `statics/<path>`, for example `statics/logo.png`.\nexport const STATICS_DIR = 'statics';\n\nexport const MAX_STATIC_ASSET_BYTES = 1024 * 1024;\n\nconst PNG_SIGNATURE = Buffer.from([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a]);\n\n// Raster images only, each recognised by its first bytes, so a file is the image its extension\n// says. SVG is left out: it is a document that can carry scripts.\nconst IMAGE_FORMATS: Readonly<Record<string, (header: Buffer) => boolean>> = {\n '.png': (header) => header.subarray(0, 8).equals(PNG_SIGNATURE),\n '.jpg': isJpeg,\n '.jpeg': isJpeg,\n '.gif': (header) => ['GIF87a', 'GIF89a'].includes(header.toString('latin1', 0, 6)),\n '.webp': (header) =>\n header.toString('latin1', 0, 4) === 'RIFF' && header.toString('latin1', 8, 12) === 'WEBP',\n};\n\nexport const STATIC_ASSET_EXTENSIONS: readonly string[] = Object.keys(IMAGE_FORMATS);\n\n// The images under statics/, as `statics/<path>`. Anything else fails the build.\nexport async function listStatics(root: string): Promise<string[]> {\n const staticsDir = join(root, STATICS_DIR);\n if (!existsSync(staticsDir)) {\n return [];\n }\n const statics: string[] = [];\n for (const entry of await readdir(staticsDir, { recursive: true, withFileTypes: true })) {\n // Hidden files, such as the .DS_Store macOS leaves in folders, are not part of the dot.\n if (!entry.isFile() || entry.name.startsWith('.')) {\n continue;\n }\n const file = join(entry.parentPath, entry.name);\n await checkImage(file, extname(entry.name).toLowerCase());\n statics.push(`${STATICS_DIR}/${relative(staticsDir, file).split(sep).join('/')}`);\n }\n return statics.sort();\n}\n\nasync function checkImage(file: string, extension: string): Promise<void> {\n const isFormat = IMAGE_FORMATS[extension];\n if (!isFormat) {\n throw new CompileError(\n `Only images belong in ${STATICS_DIR}/: ${STATIC_ASSET_EXTENSIONS.join(', ')}`,\n file,\n );\n }\n const { size } = await stat(file);\n if (size > MAX_STATIC_ASSET_BYTES) {\n const megabytes = (size / MAX_STATIC_ASSET_BYTES).toFixed(1);\n throw new CompileError(`The image is ${megabytes} MB; each one must be 1 MB or smaller`, file);\n }\n if (!isFormat(await readFile(file))) {\n throw new CompileError(`The file is not the ${extension.slice(1)} image its name says`, file);\n }\n}\n\nfunction isJpeg(header: Buffer): boolean {\n return header[0] === 0xff && header[1] === 0xd8 && header[2] === 0xff;\n}\n","import { existsSync } from 'node:fs';\nimport { join, resolve } from 'node:path';\nimport { readFile } from 'node:fs/promises';\n\nimport { parseDotModule, parseLogicModule } from '@monimesl/dot-script-parser';\n\nimport { CompileError } from '../errors';\nimport type { DotJson } from './dot-json';\nimport { DOT_JSON_FILE, readDotJson } from './dot-json';\nimport { listStatics } from './statics';\n\nconst DOT_FILE = 'dot.ts';\nexport const PAGES_DIR = 'pages';\nconst PAGE_FILES = {\n view: 'view.dml',\n state: 'state.ts',\n controller: 'controller.ts',\n} as const;\n\nexport interface PageSource {\n // The page's folder under `pages/`, `edsa-meter/confirmation`.\n readonly path: string;\n // The id pages navigate to: `definePage({ id })`, or else the path.\n readonly id: string;\n readonly view: string;\n readonly state: string;\n readonly controller: string;\n}\n\nexport interface Project {\n readonly root: string;\n // Checked, and its icon found.\n readonly dotJson: DotJson;\n // `dot.ts`, which has `export default defineDot({ pages, theme, plugins, hooks })`.\n readonly dot: string;\n // In the order of `defineDot({ pages })`; the first is the entry page.\n readonly pages: readonly PageSource[];\n // The assets under statics/, as `statics/logo.png`.\n readonly statics: readonly string[];\n}\n\nexport async function loadProject(root: string): Promise<Project> {\n root = resolve(root);\n const dotJsonFile = join(root, DOT_JSON_FILE);\n if (!existsSync(dotJsonFile)) {\n throw new CompileError(`Missing ${DOT_JSON_FILE} file in project`, root);\n }\n const dotJson = await readDotJson(dotJsonFile);\n if (dotJson.icon !== undefined && !existsSync(join(root, dotJson.icon))) {\n throw new CompileError(`The icon \"${dotJson.icon}\" is missing`, dotJsonFile);\n }\n const dotFile = join(root, DOT_FILE);\n if (!existsSync(dotFile)) {\n throw new CompileError(`Missing ${DOT_FILE}: export default defineDot({ pages: [...] })`, root);\n }\n const { pages } = parseDotModule(await readFile(dotFile, 'utf8'), dotFile);\n\n const sources = await Promise.all(pages.map((path) => loadPage(root, path, dotFile)));\n const seen = new Map<string, PageSource>();\n for (const page of sources) {\n const other = seen.get(page.id.toLowerCase());\n if (other) {\n // Point at where the clashing id is written: a custom id in controller.ts, or dot.ts\n // when both ids are folder names.\n let file = dotFile;\n if (page.id !== page.path) {\n file = page.controller;\n } else if (other.id !== other.path) {\n file = other.controller;\n }\n throw new CompileError(\n `Page id \"${page.id}\" (${page.path}) is already used by ${other.path}`,\n file,\n );\n }\n seen.set(page.id.toLowerCase(), page);\n }\n return { root, dotJson, dot: dotFile, pages: sources, statics: await listStatics(root) };\n}\n\nasync function loadPage(root: string, path: string, dotFile: string): Promise<PageSource> {\n const dir = join(root, PAGES_DIR, path);\n const files = {\n view: join(dir, PAGE_FILES.view),\n state: join(dir, PAGE_FILES.state),\n controller: join(dir, PAGE_FILES.controller),\n };\n const missing = Object.values(files).filter((file) => !existsSync(file));\n if (missing.length > 0) {\n throw new CompileError(`Page \"${path}\" is missing ${missing.join(', ')}`, dotFile);\n }\n const { id } = parseLogicModule(await readFile(files.controller, 'utf8'), files.controller);\n return { path, id: id ?? path, ...files };\n}\n","import type { PublicEnv } from './project/env';\nimport type { Project } from './project/project';\n\nexport const RUNTIME_PACKAGE = '@monimesl/dot-runtime';\n\nconst RUNTIME = {\n ui: `${RUNTIME_PACKAGE}/ui`,\n logic: `${RUNTIME_PACKAGE}/logic`,\n renderer: `${RUNTIME_PACKAGE}/react`,\n} as const;\n\nexport const ENTRY = { ui: 'dot:ui', logic: 'dot:logic' } as const;\n\n// The dot's public configuration, which the global `env` imports.\nexport const ENV_MODULE = 'dot:env';\n\nexport const viewModuleId = (id: string) => `dot:view/${id}`;\n\nfunction literal(value: string): string {\n return JSON.stringify(value);\n}\n\nfunction pageTable(project: Project, binding: string): string {\n const rows = project.pages.map((page, index) => `\\n ${literal(page.id)}: ${binding}${index},`);\n return `{${rows.join('')}\\n }`;\n}\n\nexport function uiEntry(project: Project): string {\n return [\n `import { runWebview } from ${literal(RUNTIME.ui)};`,\n `import createRenderer from ${literal(RUNTIME.renderer)};`,\n ...project.pages.map(\n (page, index) => `import view${index} from ${literal(viewModuleId(page.id))};`,\n ),\n '',\n // Runs again for each hot reload while developing, which the running webview takes in place.\n 'runWebview({',\n ' renderer: createRenderer,',\n ` views: ${pageTable(project, 'view')},`,\n '});',\n '',\n ].join('\\n');\n}\n\nexport function logicEntry(project: Project, { strict }: { strict: boolean }): string {\n const options = [' dot,', ` pages: ${pageTable(project, 'page')},`];\n if (strict) {\n options.push(' strict: true,');\n }\n return [\n `import { runService } from ${literal(RUNTIME.logic)};`,\n `import dot from ${literal(project.dot)};`,\n ...project.pages.map((page, index) => `import page${index} from ${literal(page.controller)};`),\n '',\n // Runs again for each hot reload while developing, which the running service takes in place.\n 'runService({',\n ...options,\n '});',\n '',\n ].join('\\n');\n}\n\nexport function envModule(env: PublicEnv): string {\n return `export const env = Object.freeze(${JSON.stringify(env, null, 2)});\\n`;\n}\n","import { realpathSync } from 'node:fs';\nimport { join, relative, sep } from 'node:path';\n\nimport type { Plugin } from 'rolldown';\nimport { unboundReferences } from '@monimesl/dot-script-parser';\n\nimport { ENV_MODULE } from './entries';\nimport { PAGES_DIR } from './project/project';\n\nexport const DOT_GLOBALS = {\n '@monimesl/dot-runtime': ['definePage', 'defineDot', 'createEvent', 'dash'],\n [ENV_MODULE]: ['env'],\n} as const satisfies Record<string, readonly string[]>;\n\nconst SCRIPT = /\\.[cm]?[jt]sx?$/;\n\n// Imports the globals each module of a dot uses. Pages call Dash as `this.dash`, which stops their\n// calls when they close, so the global `dash` is refused in them.\nexport function dotGlobals(root: string): Plugin {\n // The bundler names modules by their real path, such as /private/var/… for /var/… on macOS.\n const realRoot = realpathSync(root);\n const isDotModule = (id: string) =>\n id.startsWith(realRoot + sep) && !id.includes(`${sep}node_modules${sep}`) && SCRIPT.test(id);\n const isPageModule = (id: string) => id.startsWith(join(realRoot, PAGES_DIR) + sep);\n return {\n name: 'dot:globals',\n transform(code, id) {\n if (!isDotModule(id)) {\n return null;\n }\n const used = new Set(unboundReferences(code, Object.values(DOT_GLOBALS).flat(), id));\n if (isPageModule(id) && used.has('dash')) {\n this.error(\n `${relative(realRoot, id)} uses the global \\`dash\\`. In a page, call \\`this.dash\\` in actions ` +\n 'and hooks: it stops the calls a page started when the page closes.',\n );\n }\n const imports: string[] = [];\n for (const [source, names] of Object.entries(DOT_GLOBALS)) {\n const imported = names.filter((name) => used.has(name));\n if (imported.length > 0) {\n imports.push(`import { ${imported.join(', ')} } from ${JSON.stringify(source)};`);\n }\n }\n if (imports.length === 0) {\n return null;\n }\n return { code: `${code}\\n${imports.join('\\n')}\\n`, map: null };\n },\n };\n}\n","import { join } from 'node:path';\nimport { rm } from 'node:fs/promises';\n\nimport type { Plugin } from 'rolldown';\nimport { rolldown } from 'rolldown';\nimport { esmExternalRequirePlugin } from 'rolldown/plugins';\n\nimport { dotGlobals } from '../globals';\nimport { DOT_JSON_FILE } from '../project/dot-json';\n\nexport interface BundleOptions {\n root: string;\n modules: ReadonlyMap<string, string>;\n entry: string;\n outDir: string;\n fileName: string;\n format: 'esm' | 'iife';\n mode: 'production' | 'development';\n minify: boolean;\n conditions: readonly string[];\n // Imports left for the page to resolve, such as React, which hosts serve.\n external: readonly string[];\n}\n\n/** Bundles one entry with rolldown, replacing `outDir`. Returns the written files. */\nexport async function bundle(options: BundleOptions): Promise<string[]> {\n await rm(options.outDir, { recursive: true, force: true });\n const bundler = await rolldown({\n input: options.entry,\n cwd: options.root,\n platform: 'browser',\n plugins: [\n ...(options.external.length > 0\n ? [esmExternalRequirePlugin({ external: [...options.external] })]\n : []),\n generatedModules(options.root, options.modules),\n dotGlobals(options.root),\n ],\n resolve: {\n conditionNames: [...options.conditions, 'browser', 'import', 'require', 'default'],\n },\n transform: { define: { 'process.env.NODE_ENV': JSON.stringify(options.mode) } },\n onLog(level, log, defaultHandler) {\n if (log.code !== 'MODULE_LEVEL_DIRECTIVE') {\n defaultHandler(level, log);\n }\n },\n });\n try {\n const { output } = await bundler.write({\n dir: options.outDir,\n format: options.format,\n entryFileNames: options.fileName,\n sourcemap: options.mode === 'development',\n sourcemapExcludeSources: options.mode === 'production',\n minify: options.minify,\n });\n return output.map((chunk) => join(options.outDir, chunk.fileName));\n } finally {\n await bundler.close();\n }\n}\n\n/** Serves generated modules and resolves their imports as if they lived in the project root. */\nfunction generatedModules(root: string, modules: ReadonlyMap<string, string>): Plugin {\n const importer = join(root, DOT_JSON_FILE);\n return {\n name: 'dot:generated',\n // The `\\0` prefix is rolldown's mark for virtual modules: other plugins leave them alone.\n async resolveId(source, from) {\n if (modules.has(source)) {\n return `\\0${source}`;\n }\n if (!from?.startsWith('\\0dot:')) {\n return null;\n }\n const resolved = await this.resolve(source, importer, { skipSelf: true });\n if (!resolved) {\n this.error(`Cannot resolve \"${source}\" from ${root}. Is it installed in the project?`);\n }\n return resolved;\n },\n load(id) {\n return id.startsWith('\\0') ? (modules.get(id.slice(1)) ?? null) : null;\n },\n };\n}\n","import { readFileSync } from 'node:fs';\nimport { createRequire } from 'node:module';\nimport { dirname, join } from 'node:path';\n\nimport { CompileError } from '../errors';\nimport { RUNTIME_PACKAGE } from '../entries';\n\nconst RENDERER_PACKAGE = '@monimesl/dot-renderer-react';\n\n// The @monimesl/dot-runtime a project builds with.\nexport interface InstalledRuntime {\n readonly version: string;\n readonly react: ReactHost;\n // Generated Garra/Panda CSS owned by the installed React renderer.\n readonly stylesheet: string;\n}\n\n// The React the runtime's renderer uses, which hosts serve rather than each dot bundling it.\nexport interface ReactHost {\n readonly version: string;\n // The built files, in a folder per version: `<dir>/<version>/react.js`.\n readonly dir: string;\n}\n\ninterface InstalledPackage {\n readonly dir: string;\n readonly version: string;\n}\n\nexport function installedRuntime(root: string): InstalledRuntime {\n const runtime = requirePackage(root, RUNTIME_PACKAGE, 'the project');\n const renderer = requirePackage(runtime.dir, RENDERER_PACKAGE, RUNTIME_PACKAGE);\n const react = requirePackage(renderer.dir, 'react', RENDERER_PACKAGE);\n return {\n version: runtime.version,\n react: { version: react.version, dir: join(renderer.dir, 'dist', 'react-host') },\n stylesheet: join(renderer.dir, 'dist', 'styles.css'),\n };\n}\n\nfunction requirePackage(fromDir: string, name: string, installedIn: string): InstalledPackage {\n const require = createRequire(join(fromDir, 'package.json'));\n let packageFile: string;\n try {\n packageFile = require.resolve(`${name}/package.json`);\n } catch {\n throw new CompileError(`${name} is not installed in ${installedIn}`, fromDir);\n }\n const { version } = JSON.parse(readFileSync(packageFile, 'utf8')) as { version: string };\n return { dir: dirname(packageFile), version };\n}\n","import type { ExprNode } from '@monimesl/dot-script-parser';\n\nexport type Scope = ReadonlyMap<string, string>;\n\nconst IDENTIFIER = /^[A-Za-z_$][\\w$]*$/;\n\nexport const EMPTY_SCOPE: Scope = new Map();\n\n// Whether `name` can be written as a variable or property name in generated code.\nexport function isIdentifier(name: string): boolean {\n return IDENTIFIER.test(name);\n}\n\n/**\n * Prints a binding as JS. Loop variables read their local; everything else reads page data from\n * `d`. Member access never throws on nullish values.\n */\nexport function printExpr(expr: ExprNode, scope: Scope): string {\n const print = (node: ExprNode) => printExpr(node, scope);\n switch (expr.type) {\n case 'Literal':\n return JSON.stringify(expr.value);\n case 'Identifier':\n return scope.get(expr.name) ?? `d.${expr.name}`;\n case 'Member':\n return expr.property.type === 'Literal' && typeof expr.property.value === 'string'\n ? `${print(expr.object)}?.${expr.property.value}`\n : `${print(expr.object)}?.[${print(expr.property)}]`;\n case 'Unary':\n return `(${expr.operator}${expr.operator === 'typeof' ? ' ' : ''}${print(expr.argument)})`;\n case 'Binary':\n return `(${print(expr.left)} ${expr.operator} ${print(expr.right)})`;\n case 'Conditional':\n return `(${print(expr.test)} ? ${print(expr.consequent)} : ${print(expr.alternate)})`;\n }\n}\n\nexport function stateReads(expr: ExprNode, scope: Scope): string[] {\n const reads = (node: ExprNode) => stateReads(node, scope);\n switch (expr.type) {\n case 'Literal':\n return [];\n case 'Identifier':\n return scope.has(expr.name) ? [] : [expr.name];\n case 'Member':\n return [...reads(expr.object), ...reads(expr.property)];\n case 'Unary':\n return reads(expr.argument);\n case 'Binary':\n return [...reads(expr.left), ...reads(expr.right)];\n case 'Conditional':\n return [...reads(expr.test), ...reads(expr.consequent), ...reads(expr.alternate)];\n }\n}\n","import type { Scope } from './expression';\nimport { isIdentifier } from './expression';\nimport type { ViewCompiler } from './compiler';\nimport type { ComponentManifest } from '@monimesl/dot-core';\nimport type { DmlElementNode } from '@monimesl/dot-dml-parser';\n\n// The control flow tags, with the attributes each takes and those it requires.\nexport const CONTROL_FLOW_TAGS: Readonly<\n Record<string, { readonly attributes: readonly string[]; readonly required: readonly string[] }>\n> = {\n If: { attributes: ['condition'], required: ['condition'] },\n ElseIf: { attributes: ['condition'], required: ['condition'] },\n Else: { attributes: [], required: [] },\n For: { attributes: ['each', 'item', 'index', 'key'], required: ['each'] },\n};\n\nexport const CONTROL_FLOW = new Set(Object.keys(CONTROL_FLOW_TAGS));\n\n/**\n * `<If condition>`, then any `<ElseIf condition>`, then an optional `<Else>`, as consecutive\n * siblings compile to a spread conditional: `...(a ? [...]: b ? [...]: [...])`.\n */\nexport function compileConditional(\n compiler: ViewCompiler,\n branches: readonly DmlElementNode[],\n parent: ComponentManifest | null,\n scope: Scope,\n): string {\n let code = '[]';\n for (const branch of [...branches].reverse()) {\n const body = `[${compiler.compile(branch.children, parent, scope).join(', ')}]`;\n compiler.checkControlFlowElementAttributes(branch);\n if (branch.tag === 'Else') {\n code = body;\n } else {\n const condition = compiler.compileSingleBinding(branch, 'condition', scope);\n code = `${condition} ? ${body} : ${code}`;\n }\n }\n return `...(${code})`;\n}\n\n/**\n * `<For each=\"{{ list }}\" item=\"item\" index=\"index\" key=\"id\">`. `item` and `index` default to those\n * names. `key` is a property of each item, `*this` for the item itself, or `*index` to match items\n * by position; it is optional, but a warning is logged when missing. Compiles to\n * `each(list, (item, index) => [...], key?)`.\n */\nexport function compileLoop(\n compiler: ViewCompiler,\n node: DmlElementNode,\n parent: ComponentManifest | null,\n scope: Scope,\n): string {\n compiler.checkControlFlowElementAttributes(node);\n const list = compiler.compileSingleBinding(node, 'each', scope);\n const { item = 'item', index = 'index', key } = node.attributes;\n\n const checkVariableName = (attribute: string, name: string) => {\n if (!isIdentifier(name)) {\n compiler.fail(`<For> \\`${attribute}\\` must be a variable name, got \"${name}\"`, node.loc);\n }\n };\n checkVariableName('item', item);\n checkVariableName('index', index);\n if (item === index) {\n compiler.fail('<For> `item` and `index` must differ', node.loc);\n }\n // Loop variables get a `$` prefix in the generated code, so they never clash with its own names.\n const itemVar = '$' + item;\n const indexVar = '$' + index;\n const inner = new Map(scope).set(item, itemVar).set(index, indexVar);\n const body = compiler.compile(node.children, parent, inner).join(', ');\n const args = [list, `(${itemVar}, ${indexVar}) => [${body}]`];\n if (key === undefined) {\n compiler.warn(\n '<For> has no `key`, so items are matched by position. Add key=\"<property>\" (or \"*this\") ' +\n 'to keep rows stable when items move, or key=\"*index\" to keep matching by position.',\n node.loc,\n );\n } else if (key === '*index') {\n // Matching by position is what `each` does without a key.\n } else if (key === '*this') {\n args.push(`(${itemVar}) => ${itemVar}`);\n } else if (isIdentifier(key)) {\n args.push(`(${itemVar}) => ${itemVar}?.${key}`);\n } else {\n compiler.fail(`<For> \\`key\\` must be a property name, *this or *index, got \"${key}\"`, node.loc);\n }\n return `each(${args.join(', ')})`;\n}\n","import { eventName } from '@monimesl/dot-core';\nimport type { SourceLocation } from '@monimesl/dot-core';\nimport {\n builtinRegistry,\n type ComponentManifest,\n type ComponentRegistry,\n type PropType,\n} from '@monimesl/dot-core';\nimport { STATICS_DIR } from '../project/statics';\nimport { CompileError, CompileWarning } from '../errors';\nimport type { DmlElementNode, DmlNode } from '@monimesl/dot-dml-parser';\nimport { compileConditional, compileLoop, CONTROL_FLOW, CONTROL_FLOW_TAGS } from './control-flow';\nimport { EMPTY_SCOPE, isIdentifier, printExpr, type Scope, stateReads } from './expression';\nimport { type ExprNode, parseExpression, ScriptParseError } from '@monimesl/dot-script-parser';\n\nexport interface ViewContext {\n file: string;\n components?: ComponentRegistry;\n // mutations and actions declared in controller.ts; event attributes must name one of them\n handlers?: readonly string[];\n // keys declared in state.ts; omitted skips the check\n stateKeys?: readonly string[];\n // the assets under statics/, as `statics/logo.png`; a written reference to any other is an error\n statics?: readonly string[];\n // receives problems that do not stop compilation\n onWarning?: (warning: CompileWarning) => void;\n}\n\n// Every page's markup is one root <View>. Unless the markup says otherwise, it fills the page and\n// puts space at the page's edges and between its children.\nexport const ROOT_TAG = 'View';\nconst ROOT_DEFAULTS = { padding: 'large', gap: 'large', grow: true };\n\ntype Segment = { kind: 'static'; value: string } | { kind: 'binding'; code: string };\nconst SEGMENT_BINDING_REGEXP = /{{([\\s\\S]*?)}}/g;\n\nexport const DATA_PREFIX = 'data-';\nconst DATA_ATTRIBUTE = /^data-[a-z][a-z0-9]*(-[a-z0-9]+)*$/;\n\nexport class ViewCompiler {\n readonly #context: ViewContext;\n readonly #components: ComponentRegistry;\n\n constructor(context: ViewContext) {\n this.#context = context;\n this.#components = context.components ?? builtinRegistry;\n for (const name of CONTROL_FLOW) {\n if (name in this.#components) {\n this.fail(`<${name}> is reserved for control flow`);\n }\n }\n }\n\n compile(nodes: readonly DmlNode[], parent: ComponentManifest | null, scope: Scope): string[] {\n const out: string[] = [];\n for (let i = 0; i < nodes.length; i++) {\n const node = nodes[i]!;\n if (node.type === 'Text') {\n out.push(...this.compileText(node.content, parent, scope, node.loc));\n continue;\n }\n switch (node.tag) {\n case 'If': {\n const branches = ifChain(node, nodes.slice(i + 1));\n out.push(compileConditional(this, branches, parent, scope));\n i += branches.length - 1;\n break;\n }\n case 'ElseIf':\n case 'Else':\n return this.fail(`<${node.tag}> must directly follow <If> or <ElseIf>`, node.loc);\n case 'For':\n out.push(compileLoop(this, node, parent, scope));\n break;\n default:\n out.push(this.compileElement(node, parent, scope));\n }\n }\n return out;\n }\n\n private compileText(\n content: string,\n parent: ComponentManifest | null,\n scope: Scope,\n loc?: SourceLocation,\n ): string[] {\n if (parent?.children !== 'text') {\n const inline = Object.values(this.#components).filter(\n (componentManifest) => componentManifest.inline && componentManifest.children === 'text',\n );\n const wrappers = inline.map((component) => `<${component.name}>`).join(' or ');\n this.fail(`'${content}' must be wrapped in ${wrappers || 'an inline component'}`, loc);\n }\n return this.compileAsSegments(content, scope, loc).map(printSegment);\n }\n\n compileElement(node: DmlElementNode, parent: ComponentManifest | null, scope: Scope): string {\n return `h(${this.#compileElementArgs(node, parent, scope).join(', ')})`;\n }\n\n /**\n * Compiles a page's markup, which must be a single <View> with everything else inside it, to the\n * arguments of that root's `h` call.\n */\n compileRoot(nodes: readonly DmlNode[]): string[] {\n if (!this.#components[ROOT_TAG]) {\n this.fail(`The components must include <${ROOT_TAG}>, which is the root of every page`);\n }\n const [root, outside] = nodes;\n if (root?.type !== 'Element' || root.tag !== ROOT_TAG) {\n return this.fail(\n `A page's markup must be one root <${ROOT_TAG}>, with everything else inside it`,\n root?.loc,\n );\n }\n if (outside) {\n const name = outside.type === 'Text' ? `'${outside.content}'` : `<${outside.tag}>`;\n this.fail(`${name} must be inside the root <${ROOT_TAG}>`, outside.loc);\n }\n return this.#compileElementArgs(root, null, EMPTY_SCOPE, ROOT_DEFAULTS);\n }\n\n #compileElementArgs(\n node: DmlElementNode,\n parent: ComponentManifest | null,\n scope: Scope,\n defaults: Readonly<Record<string, unknown>> = {},\n ): string[] {\n const component = this.#components[node.tag];\n if (!component) {\n return this.fail(`Unknown component <${node.tag}>.`, node.loc);\n }\n if (parent && !canHaveChildren(parent, component)) {\n this.fail(`<${node.tag}> cannot be placed inside <${parent.name}>`, node.loc);\n }\n const attributes = Object.entries(node.attributes);\n const data = this.#compileData(\n attributes.filter(([name]) => name.startsWith(DATA_PREFIX)),\n scope,\n node.loc,\n );\n const props = [\n ...attributes\n .filter(([name]) => !name.startsWith(DATA_PREFIX))\n .map(([name, value]) => this.compileProp(component, name, value, scope, node.loc)),\n ...Object.entries(node.events).map(([name, handler]) =>\n this.compileEvent(component, name, handler, data, node.loc),\n ),\n ...Object.entries(defaults)\n .filter(([name]) => !(name in node.attributes))\n .map(([name, value]) => `${name}: ${JSON.stringify(value)}`),\n ];\n return [\n JSON.stringify(component.name),\n props.length ? `{ ${props.join(', ')} }` : 'null',\n ...this.compile(node.children, component, scope),\n ];\n }\n\n compileProp(\n component: ComponentManifest,\n name: string,\n value: string,\n scope: Scope,\n loc?: SourceLocation,\n ): string {\n const type = component.props[name];\n if (!type) {\n return this.fail(`<${component.name}> does not support the \\`${name}\\` attribute`, loc);\n }\n const segments = this.compileAsSegments(value, scope, loc);\n const bindingCode = singleBindingCode(segments);\n if (bindingCode !== null) {\n return `${name}: ${bindingCode}`;\n }\n if (segments.every((segment) => segment.kind === 'static')) {\n this.#checkStaticsReference(value, loc);\n return `${name}: ${this.compileLiteral(type, value, loc)}`;\n }\n if (type !== 'string' && type !== 'any') {\n const kind = typeof type === 'string' ? `a ${type}` : 'an enum';\n this.fail(`\\`${name}\\` is ${kind}; bind it with a single {{ }} expression`, loc);\n }\n return `${name}: ${segments.map(printSegment).join(' + ')}`;\n }\n\n // `data-account-id=\"{{ account.id }}\"` becomes `{ accountId: d.account.id }`, which the element's\n // events carry to their handlers.\n #compileData(\n attributes: readonly [string, string][],\n scope: Scope,\n loc?: SourceLocation,\n ): string | null {\n if (attributes.length === 0) {\n return null;\n }\n const entries = attributes.map(([name, value]) => {\n if (!DATA_ATTRIBUTE.test(name)) {\n this.fail(\n `\\`${name}\\` must be data- and lowercase words joined by -, such as data-account-id`,\n loc,\n );\n }\n return `${dataKey(name)}: ${this.#compileDataValue(value, scope, loc)}`;\n });\n return `{ ${entries.join(', ')} }`;\n }\n\n // A single binding keeps its value's type, such as a number; anything else is a string.\n #compileDataValue(value: string, scope: Scope, loc?: SourceLocation): string {\n const segments = this.compileAsSegments(value, scope, loc);\n const bindingCode = singleBindingCode(segments);\n if (bindingCode !== null) {\n return bindingCode;\n }\n if (segments.length === 0) {\n return '\"\"';\n }\n return segments.map(printSegment).join(' + ');\n }\n\n #checkStaticsReference(value: string, loc: SourceLocation | undefined): void {\n const { statics } = this.#context;\n if (statics && value.startsWith(`${STATICS_DIR}/`) && !statics.includes(value)) {\n this.fail(`\"${value}\" is not in ${STATICS_DIR}/`, loc);\n }\n }\n\n compileEvent(\n component: ComponentManifest,\n attribute: string,\n handler: string,\n data: string | null,\n loc?: SourceLocation,\n ): string {\n const event = eventName(attribute);\n if (!component.events.includes(event)) {\n this.fail(`<${component.name}> has no defined \\`${attribute}\\` event handler`, loc);\n }\n if (!isIdentifier(handler)) {\n this.fail(\n `\\`${attribute}\\` must name a mutation or action in controller.ts, got \"${handler}\"`,\n loc,\n );\n }\n const { handlers } = this.#context;\n if (handlers && !handlers.includes(handler)) {\n this.fail(`\\`${handler}\\` is not a mutation or action in controller.ts`, loc);\n }\n const args = data ? `${JSON.stringify(handler)}, ${data}` : JSON.stringify(handler);\n return `${attribute}: on(${args})`;\n }\n\n checkControlFlowElementAttributes(node: DmlElementNode): void {\n const { attributes: allowed, required } = CONTROL_FLOW_TAGS[node.tag]!;\n const [event] = Object.keys(node.events);\n if (event) {\n this.fail(\n `<${node.tag}> is control flow component and cannot handle events such as \\`${event}\\`. ` +\n 'Put the handler on a component inside it.',\n node.loc,\n );\n }\n for (const name of Object.keys(node.attributes)) {\n if (!allowed.includes(name)) {\n this.fail(`<${node.tag}> does not support the \\`${name}\\` attribute`, node.loc);\n }\n }\n for (const name of required) {\n if (!(name in node.attributes)) {\n this.fail(`<${node.tag}> requires \\`${name}\\``, node.loc);\n }\n }\n }\n\n compileSingleBinding(node: DmlElementNode, attribute: string, scope: Scope): string {\n const segments = this.compileAsSegments(node.attributes[attribute] ?? '', scope, node.loc);\n const bindingCode = singleBindingCode(segments);\n if (bindingCode === null) {\n return this.fail(`<${node.tag}> \\`${attribute}\\` must be a single {{ }} binding`, node.loc);\n }\n return bindingCode;\n }\n\n compileLiteral(type: PropType, value: string, loc?: SourceLocation): string {\n if (typeof type !== 'string') {\n if (type.includes(value)) {\n return JSON.stringify(value);\n }\n return this.fail(\n `Expected one of ${type.map((option) => `\"${option}\"`).join(', ')}, got \"${value}\"`,\n loc,\n );\n }\n switch (type) {\n case 'boolean':\n if (value === '' || value === 'true') {\n return 'true';\n }\n if (value === 'false') {\n return 'false';\n }\n return this.fail(`Expected a boolean, got \"${value}\"`, loc);\n case 'number':\n if (value.trim() !== '' && Number.isFinite(Number(value))) {\n return String(Number(value));\n }\n return this.fail(`Expected a number, got \"${value}\"`, loc);\n case 'string':\n case 'any':\n return JSON.stringify(value);\n }\n }\n\n private compileAsSegments(input: string, scope: Scope, loc?: SourceLocation): Segment[] {\n const segments: Segment[] = [];\n let last = 0;\n for (const match of input.matchAll(SEGMENT_BINDING_REGEXP)) {\n if (match.index > last) {\n segments.push({ kind: 'static', value: input.slice(last, match.index) });\n }\n const expr = this.parseBindingExpression(match[1]!, scope, loc);\n segments.push({ kind: 'binding', code: printExpr(expr, scope) });\n last = match.index + match[0].length;\n }\n const rest = input.slice(last);\n if (rest.includes('{{')) {\n this.fail('Unclosed binding: missing `}}`', loc);\n }\n if (rest) {\n segments.push({ kind: 'static', value: rest });\n }\n return segments;\n }\n\n parseBindingExpression(source: string, scope: Scope, loc?: SourceLocation): ExprNode {\n let expr: ExprNode;\n try {\n expr = parseExpression(source);\n } catch (error) {\n if (!(error instanceof ScriptParseError)) {\n throw error;\n }\n return this.fail(`Invalid binding \\`{{${source}}}\\`: ${error.reason}`, loc);\n }\n const { stateKeys } = this.#context;\n if (stateKeys) {\n const undeclared = stateReads(expr, scope).find((name) => !stateKeys.includes(name));\n if (undeclared) {\n const declared = stateKeys.join(', ') || 'none';\n this.fail(`\\`${undeclared}\\` is not declared in state.ts (declared: ${declared})`, loc);\n }\n }\n return expr;\n }\n\n warn(message: string, loc?: SourceLocation): void {\n this.#context.onWarning?.(new CompileWarning(message, this.#context.file, loc));\n }\n\n fail(message: string, loc?: SourceLocation): never {\n throw new CompileError(message, this.#context.file, loc);\n }\n}\n\n// The <If>, then the <ElseIf> and <Else> siblings that continue it.\nfunction ifChain(ifNode: DmlElementNode, nextSiblings: readonly DmlNode[]): DmlElementNode[] {\n const branches = [ifNode];\n for (const sibling of nextSiblings) {\n if (branches.at(-1)!.tag === 'Else' || !continuesIf(sibling)) {\n break;\n }\n branches.push(sibling);\n }\n return branches;\n}\n\nfunction continuesIf(node: DmlNode): node is DmlElementNode {\n return node.type === 'Element' && (node.tag === 'ElseIf' || node.tag === 'Else');\n}\n\n// The code of the binding when `segments` is exactly one binding, else null.\nfunction singleBindingCode(segments: readonly Segment[]): string | null {\n const [first] = segments;\n if (segments.length === 1 && first?.kind === 'binding') {\n return first.code;\n }\n return null;\n}\n\n// `data-account-id` -> `accountId`\nexport function dataKey(attribute: string): string {\n const [first = '', ...rest] = attribute.slice(DATA_PREFIX.length).split('-');\n return first + rest.map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join('');\n}\n\nfunction printSegment(segment: Segment): string {\n return segment.kind === 'static' ? JSON.stringify(segment.value) : `text(${segment.code})`;\n}\n\nexport function canHaveChildren(parent: ComponentManifest, child: ComponentManifest): boolean {\n switch (parent.children) {\n case 'none':\n return false;\n case 'text':\n return child.inline === true;\n case 'elements':\n return true;\n }\n}\n","import { parseDmlToIR } from '@monimesl/dot-dml-parser';\nimport { ViewCompiler, type ViewContext } from './compiler';\n\n/**\n * Compiles `.dml` markup into a renderer-agnostic ES module:\n * `export default function render(d, { h, on, text, each }) { return h(\"View\", ...) }`.\n *\n * The 'h' will be a function from the renderer to create the virtual element for a component. The\n * function returns the page's root element, the <View>.\n */\nexport function compileView(source: string, context: ViewContext): string {\n const nodes = parseDmlToIR(source, {\n locations: true,\n file: context.file,\n whitespace: 'collapse',\n });\n const compiler = new ViewCompiler(context);\n const rootArgs = compiler.compileRoot(nodes);\n return [\n 'export default function render(d, { h, on, text, each }) {',\n ` return h(${rootArgs.map((arg) => `\\n ${arg},`).join('')}\\n );`,\n '}',\n '',\n ].join('\\n');\n}\n","import { mkdir, readFile, writeFile } from 'node:fs/promises';\nimport { dirname, join } from 'node:path';\n\nimport type { PublicEnv } from './project/env';\nimport type { Project } from './project/project';\n\nconst PAGE_TYPES_FILE = '.dot/pages.d.ts';\nconst ENV_TYPES_FILE = '.dot/env.d.ts';\n\n// Page ids, so navigation targets are typed.\nexport async function writePageTypes(project: Project): Promise<string> {\n const members = project.pages.map((page) => `${JSON.stringify(page.id)}: true;`);\n return writeIfChanged(\n join(project.root, PAGE_TYPES_FILE),\n globalInterface('dot.ts', 'DotPageIds', members),\n );\n}\n\n// The names in the global `env`, so a misspelled one is a type error.\nexport async function writeEnvTypes(project: Project, env: PublicEnv): Promise<string> {\n const members = Object.keys(env).map((name) => `readonly ${name}: string;`);\n return writeIfChanged(\n join(project.root, ENV_TYPES_FILE),\n globalInterface('the variables in .env files', 'DotPublicEnv', members),\n );\n}\n\n// A declaration file that fills in one of the runtime's global interfaces.\nfunction globalInterface(source: string, name: string, members: readonly string[]): string {\n return [\n `// Generated by dotctl from ${source}. Do not edit.`,\n 'declare global {',\n ` interface ${name} {`,\n ...members.map((member) => ` ${member}`),\n ' }',\n '}',\n '',\n 'export {};',\n '',\n ].join('\\n');\n}\n\n// Leaves an unchanged file alone, so editors and watchers don't see a change.\nasync function writeIfChanged(file: string, content: string): Promise<string> {\n const current = await readFile(file, 'utf8').catch(() => null);\n if (current !== content) {\n await mkdir(dirname(file), { recursive: true });\n await writeFile(file, content);\n }\n return file;\n}\n","import { existsSync } from 'node:fs';\nimport { readFile } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport { parseEnv } from 'node:util';\n\nimport { CompileError } from '../errors';\n\n// Every variable in the .env files reaches the dot, where anyone who has it can read them: a\n// secret never belongs in them.\nconst PUBLIC_ENV_NAME = /^[A-Z][A-Z0-9_]*$/;\n\nexport type PublicEnv = Readonly<Record<string, string>>;\n\n// The dot's public configuration, from `.env`, then `.env.<mode>`; each later one wins. The shell's\n// environment then overrides the variables the files declare, so CI can set a value, but it never\n// adds one: the rest of the shell, such as PATH or a CI secret, stays out of the dot.\nexport async function loadPublicEnv(\n root: string,\n mode: string,\n shellEnv: Readonly<Record<string, string | undefined>> = process.env,\n): Promise<PublicEnv> {\n const env: Record<string, string> = {};\n for (const fileName of ['.env', `.env.${mode}`]) {\n const file = join(root, fileName);\n if (existsSync(file)) {\n addVariables(env, parseEnv(await readFile(file, 'utf8')), file);\n }\n }\n // Sorted, so the bundle and the generated types don't change with the order of the files.\n const sorted: Record<string, string> = {};\n for (const name of Object.keys(env).sort()) {\n sorted[name] = shellEnv[name] ?? env[name]!;\n }\n return sorted;\n}\n\nfunction addVariables(\n env: Record<string, string>,\n variables: Readonly<Record<string, string | undefined>>,\n file: string,\n): void {\n for (const [name, value] of Object.entries(variables)) {\n if (value === undefined) {\n continue;\n }\n if (!PUBLIC_ENV_NAME.test(name)) {\n throw new CompileError(\n `\"${name}\" must be capital letters, digits and underscores, starting with a letter`,\n file,\n );\n }\n env[name] = value;\n }\n}\n","import { copyFile, mkdir, readdir, readFile, rm, writeFile } from 'node:fs/promises';\nimport { dirname, join, relative, resolve, sep } from 'node:path';\n\nimport { zipSync } from 'fflate';\n\nimport type { ComponentManifest, ComponentRegistry } from '@monimesl/dot-core';\nimport {\n REACT_HOST_MODULES,\n builtinComponents,\n componentRegistry,\n reactHostUrl,\n} from '@monimesl/dot-core';\nimport { parseLogicModule, parseStateModule } from '@monimesl/dot-script-parser';\n\nimport { bundle } from './bundle';\nimport type { InstalledRuntime } from './packages';\nimport { installedRuntime } from './packages';\nimport { compileView } from '../view';\nimport type { ViewContext } from '../view';\nimport { writeEnvTypes, writePageTypes } from '../types';\nimport type { PublicEnv } from '../project/env';\nimport { loadPublicEnv } from '../project/env';\nimport type { CompileWarning } from '../errors';\nimport type { DotJson } from '../project/dot-json';\nimport type { PageSource, Project } from '../project/project';\nimport { loadProject } from '../project/project';\nimport { ENTRY, ENV_MODULE, envModule, logicEntry, uiEntry, viewModuleId } from '../entries';\n\nconst MANIFEST_FILE = 'manifest.json';\nconst ICON_FILE = 'icon.png';\nconst STYLES_FILE = 'dot.css';\n// The earliest date a ZIP file can hold.\nconst ARCHIVE_DATE = new Date(1980, 0, 1);\n\nexport interface BuildOptions {\n root: string;\n // defaults to `<root>/dist`\n outDir?: string;\n // `production` (the default) is always minified; `development` never is, so it reads in a debugger.\n mode?: 'production' | 'development';\n components?: readonly ComponentManifest[];\n conditions?: readonly string[];\n}\n\nexport interface BuildResult {\n readonly outDir: string;\n readonly files: readonly string[];\n readonly warnings: readonly CompileWarning[];\n}\n\n/** Compiles and bundles the dot into `outDir` for hosts, and refreshes the types in `.dot/`. */\nexport async function build(options: BuildOptions): Promise<BuildResult> {\n const project = await loadProject(options.root);\n const mode = options.mode ?? 'production';\n const env = await loadPublicEnv(project.root, mode);\n const runtime = installedRuntime(project.root);\n await writePageTypes(project);\n await writeEnvTypes(project, env);\n\n const warnings: CompileWarning[] = [];\n const components = componentRegistry([\n ...Object.values(builtinComponents),\n ...(options.components ?? []),\n ]);\n const modules = await compileModules(project, env, components, mode, warnings);\n\n const outDir = resolve(project.root, options.outDir ?? 'dist');\n const uiDir = join(outDir, 'ui');\n const bundleLayer = (\n entry: string,\n layerDir: string,\n format: 'esm' | 'iife',\n external: readonly string[],\n ) => {\n return bundle({\n root: project.root,\n modules,\n entry,\n outDir: layerDir,\n fileName: 'dot.js',\n format,\n mode,\n minify: mode === 'production',\n conditions: options.conditions ?? [],\n external,\n });\n };\n // Each bundle empties its folder first, so the files that go next to it are written after.\n const files = [\n // React stays out of the webview bundle: the host serves it, and the import map points at it.\n ...(await bundleLayer(ENTRY.ui, uiDir, 'esm', Object.keys(REACT_HOST_MODULES))),\n ...(await bundleLayer(ENTRY.logic, join(outDir, 'logic'), 'iife', [])),\n await copyRendererStyles(runtime, uiDir),\n await writeIndexHtml(project, uiDir, runtime.react.version),\n ...(await copyStatics(project, uiDir)),\n await writeManifest(project, outDir, runtime),\n ];\n const icon = await copyIcon(project, outDir);\n if (icon) {\n files.push(icon);\n }\n if (mode === 'production') {\n files.push(await writeArchive(project, outDir, files));\n }\n return { outDir, files, warnings };\n}\n\nasync function copyRendererStyles(runtime: InstalledRuntime, uiDir: string): Promise<string> {\n const file = join(uiDir, STYLES_FILE);\n await mkdir(uiDir, { recursive: true });\n await copyFile(runtime.stylesheet, file);\n return file;\n}\n\n// The build as the one file Dash downloads, `<id>-<version>.zip`, without source maps. Its files\n// all get the same date, so the same build always makes the same archive.\nasync function writeArchive(project: Project, outDir: string, files: string[]): Promise<string> {\n const entries: Record<string, [Uint8Array, { mtime: Date }]> = {};\n for (const file of files) {\n if (!file.endsWith('.map')) {\n const name = relative(outDir, file).split(sep).join('/');\n entries[name] = [await readFile(file), { mtime: ARCHIVE_DATE }];\n }\n }\n const { id, version } = project.dotJson;\n // An earlier version's archive would be left beside it.\n for (const name of await readdir(outDir)) {\n if (name.startsWith(`${id}-`) && name.endsWith('.zip')) {\n await rm(join(outDir, name));\n }\n }\n const file = join(outDir, `${id}-${version}.zip`);\n await writeFile(file, zipSync(entries, { level: 9 }));\n return file;\n}\n\n// Everything the bundles are made from: each page's compiled view, the two entries, and the public\n// configuration.\nasync function compileModules(\n project: Project,\n env: PublicEnv,\n components: ComponentRegistry,\n mode: 'production' | 'development',\n warnings: CompileWarning[],\n): Promise<Map<string, string>> {\n const modules = new Map<string, string>();\n for (const page of project.pages) {\n const view = await compilePage(page, components, project.statics, warnings);\n modules.set(viewModuleId(page.id), view);\n }\n modules.set(ENTRY.ui, uiEntry(project));\n modules.set(ENTRY.logic, logicEntry(project, { strict: mode === 'development' }));\n modules.set(ENV_MODULE, envModule(env));\n return modules;\n}\n\nasync function writeIndexHtml(\n project: Project,\n uiDir: string,\n reactVersion: string,\n): Promise<string> {\n const file = join(uiDir, 'index.html');\n await mkdir(uiDir, { recursive: true });\n await writeFile(file, indexHtml(project.dotJson.name, reactVersion));\n return file;\n}\n\nasync function copyStatics(project: Project, uiDir: string): Promise<string[]> {\n const copies: string[] = [];\n for (const file of project.statics) {\n const copy = join(uiDir, file);\n await mkdir(dirname(copy), { recursive: true });\n await copyFile(join(project.root, file), copy);\n copies.push(copy);\n }\n return copies;\n}\n\nasync function writeManifest(\n project: Project,\n outDir: string,\n runtime: InstalledRuntime,\n): Promise<string> {\n const file = join(outDir, MANIFEST_FILE);\n const manifest = dotManifest(project, runtime);\n await writeFile(file, `${JSON.stringify(manifest, null, 2)}\\n`);\n return file;\n}\n\n// The icon goes next to the manifest under one name, so hosts always find it in the same place.\nasync function copyIcon(project: Project, outDir: string): Promise<string | null> {\n if (project.dotJson.icon === undefined) {\n return null;\n }\n const file = join(outDir, ICON_FILE);\n await copyFile(join(project.root, project.dotJson.icon), file);\n return file;\n}\n\n/** What a host needs to run the build: dot.json without `$schema`, where it starts and its pages. */\nexport interface DotManifest extends Omit<DotJson, 'icon'> {\n // The version of @monimesl/dot-runtime in the bundles, so a host can tell whether it runs them.\n readonly runtime: string;\n // The React version the webview imports from the host.\n readonly react: string;\n readonly entry: string;\n readonly pages: readonly string[];\n // `icon.png` next to the manifest, when dot.json names an icon.\n readonly icon: string | undefined;\n}\n\nfunction dotManifest(project: Project, runtime: InstalledRuntime): DotManifest {\n const { id, name, version, description, categories, permissions, network, dash } =\n project.dotJson;\n const pages = project.pages.map((page) => page.id);\n return {\n id,\n name,\n version,\n runtime: runtime.version,\n react: runtime.react.version,\n description,\n categories,\n icon: project.dotJson.icon === undefined ? undefined : ICON_FILE,\n permissions,\n network,\n dash,\n entry: pages[0]!,\n pages,\n };\n}\n\nasync function compilePage(\n page: PageSource,\n components: ComponentRegistry,\n statics: readonly string[],\n warnings: CompileWarning[],\n): Promise<string> {\n const [view, state, controller] = await Promise.all([\n readFile(page.view, 'utf8'),\n readFile(page.state, 'utf8'),\n readFile(page.controller, 'utf8'),\n ]);\n const context: ViewContext = {\n file: page.view,\n components,\n statics,\n onWarning: (warning) => warnings.push(warning),\n };\n // State keys are checked only when state.ts is a plain object literal.\n const { keys } = parseStateModule(state, page.state);\n if (keys) {\n context.stateKeys = keys;\n }\n // Handler names are checked only when both lists could be read without running the code.\n const { mutations, actions } = parseLogicModule(controller, page.controller);\n if (mutations && actions) {\n context.handlers = [...mutations, ...actions];\n }\n return compileView(view, context);\n}\n\nfunction indexHtml(title: string, reactVersion: string): string {\n const escaped = title.replace(/[&<>\"]/g, (char) => `&#${char.charCodeAt(0)};`);\n const imports: Record<string, string> = {};\n for (const [name, file] of Object.entries(REACT_HOST_MODULES)) {\n imports[name] = reactHostUrl(reactVersion, file);\n }\n return `<!doctype html>\n<html lang=\"en\">\n <head>\n <meta charset=\"UTF-8\" />\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n <title>${escaped}</title>\n <link rel=\"stylesheet\" href=\"./${STYLES_FILE}\" />\n <style>\n body {\n margin: 0;\n }\n </style>\n </head>\n <body>\n <div id=\"root\"></div>\n <script type=\"importmap\">${JSON.stringify({ imports })}</script>\n <script type=\"module\" src=\"./dot.js\"></script>\n </body>\n</html>\n`;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../src/errors.ts","../src/project/categories.ts","../src/project/dot-json.ts","../src/project/statics.ts","../src/project/project.ts","../src/entries.ts","../src/globals.ts","../src/build/bundle.ts","../src/build/packages.ts","../src/view/expression.ts","../src/view/control-flow.ts","../src/view/compiler.ts","../src/view/compile.ts","../src/types.ts","../src/project/env.ts","../src/build/build.ts"],"sourcesContent":["import { SourceError } from '@monimesl/dot-core';\n\nexport class CompileError extends SourceError {\n override readonly name = 'CompileError';\n}\n\n/** A problem that does not stop compilation. */\nexport class CompileWarning extends SourceError {\n override readonly name = 'CompileWarning';\n}\n","// Where Dash lists a dot, in snake case. The runtime's dot.schema.json lists the same ones.\nexport const CATEGORIES = [\n 'agriculture',\n 'asset_finance',\n 'donations',\n 'education',\n 'electricity',\n 'entertainment',\n 'events',\n 'finance',\n 'food_and_drink',\n 'gambling',\n 'games',\n 'government',\n 'health',\n 'insurance',\n 'internet_service_provider',\n 'investments',\n 'jobs',\n 'lifestyle',\n 'loans',\n 'micro_credit',\n 'microfinance',\n 'news',\n 'other',\n 'productivity',\n 'real_estate',\n 'religion',\n 'remittances',\n 'savings',\n 'shopping',\n 'social',\n 'sports',\n 'telecom',\n 'television',\n 'transport',\n 'travel',\n 'utilities',\n 'water',\n] as const;\n\nexport type DotCategory = (typeof CATEGORIES)[number];\n\nexport function isCategory(value: unknown): value is DotCategory {\n return (CATEGORIES as readonly unknown[]).includes(value);\n}\n","import { readFile } from 'node:fs/promises';\n\nimport type { DotPermission } from '@monimesl/dot-core';\nimport { PERMISSIONS, isNetworkHost, isPermission, isSemanticVersion } from '@monimesl/dot-core';\n\nimport { CompileError } from '../errors';\nimport type { DotCategory } from './categories';\nimport { CATEGORIES, isCategory } from './categories';\n\nexport const DOT_JSON_FILE = 'dot.json';\n\n// The fields dot.json allows. The runtime's `dot.schema.json` describes the same ones for editors,\n// and a test keeps the two in step.\nexport const DOT_JSON_FIELDS: readonly string[] = [\n '$schema',\n 'id',\n 'name',\n 'version',\n 'description',\n 'categories',\n 'icon',\n 'permissions',\n 'network',\n 'dash',\n];\nexport const REQUIRED_DOT_JSON_FIELDS: readonly string[] = ['id', 'name', 'version'];\nexport const DASH_FIELDS: readonly string[] = ['minVersion'];\nexport const NETWORK_FIELDS: readonly string[] = ['hosts'];\n\n// A reverse domain name, which stays the same for the dot's whole life: `com.example.counter`.\n// Hosts give each dot its own web origin named after it, so it must also be a valid host name.\n// Each word is lowercase letters and digits with hyphens inside, up to 63 characters; the first\n// starts with a letter.\nconst ID_WORD = '[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?';\nexport const DOT_ID_PATTERN = new RegExp(`^(?=[a-z])${ID_WORD}(?:\\\\.${ID_WORD})+$`);\nexport const DOT_ID_MAX_LENGTH = 128;\n\nexport interface DotJson {\n readonly id: string;\n readonly name: string;\n readonly version: string;\n readonly description?: string | undefined;\n // Where Dash lists the dot: one or more, with none repeated.\n readonly categories?: readonly DotCategory[] | undefined;\n // A PNG file, relative to the project root.\n readonly icon?: string | undefined;\n // Each permission the dot uses, with the reason Dash shows the person.\n readonly permissions?: Readonly<Partial<Record<DotPermission, string>>> | undefined;\n readonly network?: NetworkAccess | undefined;\n readonly dash?: DashRequirements | undefined;\n}\n\n// Where the dot may send requests with dash.network. Without it, it may send none.\ninterface NetworkAccess {\n // Host names, such as \"api.example.com\", or \"*.example.com\" for any subdomain.\n readonly hosts: readonly string[];\n}\n\n// What a dot needs from Dash, the app it runs in.\ninterface DashRequirements {\n // The oldest Dash version the dot runs in.\n readonly minVersion?: string | undefined;\n}\n\nexport async function readDotJson(file: string): Promise<DotJson> {\n let dotJson: unknown;\n try {\n dotJson = JSON.parse(await readFile(file, 'utf8'));\n } catch (error) {\n throw new CompileError(`This isn't valid JSON: ${(error as Error).message}`, file);\n }\n if (!isObject(dotJson)) {\n throw new CompileError(\n 'dot.json must be an object, such as { \"id\": \"com.example.shop\", \"name\": \"Shop\", \"version\": \"1.0.0\" }',\n file,\n );\n }\n if ('pages' in dotJson) {\n throw new CompileError('List pages in dot.ts, not here: defineDot({ pages: [...] })', file);\n }\n rejectUnknownFields(dotJson, DOT_JSON_FIELDS, file);\n for (const field of REQUIRED_DOT_JSON_FIELDS) {\n if (dotJson[field] === undefined) {\n throw new CompileError(`\"${field}\" is required`, file);\n }\n }\n\n const { $schema, id, name, version, description, categories, icon, permissions, network, dash } =\n dotJson;\n if ($schema !== undefined && typeof $schema !== 'string') {\n throw new CompileError('\"$schema\" must be a string', file);\n }\n if (typeof id !== 'string' || !DOT_ID_PATTERN.test(id)) {\n throw new CompileError(\n '\"id\" must be a reverse domain name, such as \"com.example.counter\". Use lowercase letters, ' +\n \"digits and hyphens, with dots between words. A word can't start or end with a hyphen, \" +\n 'or be longer than 63 characters.',\n file,\n );\n }\n if (id.length > DOT_ID_MAX_LENGTH) {\n throw new CompileError(`\"id\" must be at most ${DOT_ID_MAX_LENGTH} characters`, file);\n }\n if (!isNonEmptyString(name)) {\n throw new CompileError('\"name\" must be text, and not empty', file);\n }\n if (!isSemanticVersion(version)) {\n throw new CompileError('\"version\" must be a semantic version, such as \"1.0.0\"', file);\n }\n if (description !== undefined && !isNonEmptyString(description)) {\n throw new CompileError('\"description\" must be text, and not empty', file);\n }\n if (icon !== undefined && !(isNonEmptyString(icon) && icon.endsWith('.png'))) {\n throw new CompileError('\"icon\" must be the path to a PNG file, such as \"icon.png\"', file);\n }\n return {\n id,\n name,\n version,\n description,\n categories: readCategories(categories, file),\n icon,\n permissions: readPermissions(permissions, file),\n network: readNetworkAccess(network, file),\n dash: readDashRequirements(dash, file),\n };\n}\n\nfunction readCategories(categories: unknown, file: string): DotCategory[] | undefined {\n if (categories === undefined) {\n return undefined;\n }\n if (!Array.isArray(categories) || categories.length === 0) {\n throw new CompileError(\n '\"categories\" must list one or more categories, such as [\"finance\", \"savings\"]',\n file,\n );\n }\n const listed: DotCategory[] = [];\n for (const category of categories as unknown[]) {\n if (!isCategory(category)) {\n throw new CompileError(\n `\"${String(category)}\" is not a category. The categories are ${quotedList(CATEGORIES)}`,\n file,\n );\n }\n if (listed.includes(category)) {\n throw new CompileError(`\"categories\" lists \"${category}\" more than once`, file);\n }\n listed.push(category);\n }\n return listed;\n}\n\nfunction readPermissions(\n permissions: unknown,\n file: string,\n): Partial<Record<DotPermission, string>> | undefined {\n if (permissions === undefined) {\n return undefined;\n }\n if (!isObject(permissions)) {\n throw new CompileError(\n '\"permissions\" must be an object, such as { \"account\": \"Show your name on receipts\" }',\n file,\n );\n }\n for (const [permission, reason] of Object.entries(permissions)) {\n if (!isPermission(permission)) {\n throw new CompileError(\n `Unknown permission \"${permission}\". The permissions are ${quotedList(PERMISSIONS)}`,\n file,\n );\n }\n if (!isNonEmptyString(reason)) {\n throw new CompileError(\n `\"permissions.${permission}\" must say why the dot needs it, such as \"Show your name on receipts\"`,\n file,\n );\n }\n }\n return permissions;\n}\n\nfunction readNetworkAccess(network: unknown, file: string): NetworkAccess | undefined {\n if (network === undefined) {\n return undefined;\n }\n if (!isObject(network)) {\n throw new CompileError(\n '\"network\" must be an object, such as { \"hosts\": [\"api.example.com\"] }',\n file,\n );\n }\n rejectUnknownFields(network, NETWORK_FIELDS, file, 'network');\n const { hosts } = network;\n if (!Array.isArray(hosts) || hosts.length === 0) {\n throw new CompileError(\n '\"network.hosts\" must list the hosts the dot sends requests to, such as [\"api.example.com\"]',\n file,\n );\n }\n const listed: string[] = [];\n for (const host of hosts as unknown[]) {\n if (typeof host !== 'string' || !isNetworkHost(host)) {\n throw new CompileError(\n `\"${String(host)}\" is not a host. Use a lowercase host name, such as \"api.example.com\", ` +\n 'or \"*.example.com\" for all its subdomains. Leave out https://, the port and the path.',\n file,\n );\n }\n if (listed.includes(host)) {\n throw new CompileError(`\"network.hosts\" lists \"${host}\" more than once`, file);\n }\n listed.push(host);\n }\n return { hosts: listed };\n}\n\nfunction readDashRequirements(dash: unknown, file: string): DashRequirements | undefined {\n if (dash === undefined) {\n return undefined;\n }\n if (!isObject(dash)) {\n throw new CompileError('\"dash\" must be an object, such as { \"minVersion\": \"2.4.0\" }', file);\n }\n rejectUnknownFields(dash, DASH_FIELDS, file, 'dash');\n const { minVersion } = dash;\n if (minVersion !== undefined && !isSemanticVersion(minVersion)) {\n throw new CompileError('\"dash.minVersion\" must be a semantic version, such as \"2.4.0\"', file);\n }\n return { minVersion };\n}\n\nfunction rejectUnknownFields(\n object: Record<string, unknown>,\n allowed: readonly string[],\n file: string,\n parent?: string,\n): void {\n for (const field of Object.keys(object)) {\n if (allowed.includes(field)) {\n continue;\n }\n if (parent === undefined) {\n throw new CompileError(\n `Unknown field \"${field}\". The fields allowed are ${quotedList(allowed)}`,\n file,\n );\n }\n throw new CompileError(\n `Unknown field \"${parent}.${field}\". The fields allowed in \"${parent}\" are ${quotedList(allowed)}`,\n file,\n );\n }\n}\n\nfunction quotedList(values: readonly string[]): string {\n return values.map((value) => `\"${value}\"`).join(', ');\n}\n\nfunction isObject(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\nfunction isNonEmptyString(value: unknown): value is string {\n return typeof value === 'string' && value !== '';\n}\n","import { existsSync } from 'node:fs';\nimport { readdir, readFile, stat } from 'node:fs/promises';\nimport { extname, join, relative, sep } from 'node:path';\n\nimport { CompileError } from '../errors';\n\n// Images a dot shows. The build copies them next to the webview as they are, and markup and state\n// refer to them as `statics/<path>`, for example `statics/logo.png`.\nexport const STATICS_DIR = 'statics';\n\nexport const MAX_STATIC_ASSET_BYTES = 1024 * 1024;\n\nconst PNG_SIGNATURE = Buffer.from([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a]);\n\n// Raster images only, each recognised by its first bytes, so a file is the image its extension\n// says. SVG is left out: it is a document that can carry scripts.\nconst IMAGE_FORMATS: Readonly<Record<string, (header: Buffer) => boolean>> = {\n '.png': (header) => header.subarray(0, 8).equals(PNG_SIGNATURE),\n '.jpg': isJpeg,\n '.jpeg': isJpeg,\n '.gif': (header) => ['GIF87a', 'GIF89a'].includes(header.toString('latin1', 0, 6)),\n '.webp': (header) =>\n header.toString('latin1', 0, 4) === 'RIFF' && header.toString('latin1', 8, 12) === 'WEBP',\n};\n\nconst STATIC_ASSET_EXTENSIONS: readonly string[] = Object.keys(IMAGE_FORMATS);\n\n// The images under statics/, as `statics/<path>`. Anything else fails the build.\nexport async function listStatics(root: string): Promise<string[]> {\n const staticsDir = join(root, STATICS_DIR);\n if (!existsSync(staticsDir)) {\n return [];\n }\n const statics: string[] = [];\n for (const entry of await readdir(staticsDir, { recursive: true, withFileTypes: true })) {\n // Hidden files, such as the .DS_Store macOS leaves in folders, are not part of the dot.\n if (!entry.isFile() || entry.name.startsWith('.')) {\n continue;\n }\n const file = join(entry.parentPath, entry.name);\n await checkImage(file, extname(entry.name).toLowerCase());\n statics.push(`${STATICS_DIR}/${relative(staticsDir, file).split(sep).join('/')}`);\n }\n return statics.sort();\n}\n\nasync function checkImage(file: string, extension: string): Promise<void> {\n const isFormat = IMAGE_FORMATS[extension];\n if (!isFormat) {\n throw new CompileError(\n `${STATICS_DIR}/ can only hold images: ${STATIC_ASSET_EXTENSIONS.join(', ')}`,\n file,\n );\n }\n const { size } = await stat(file);\n if (size > MAX_STATIC_ASSET_BYTES) {\n const megabytes = (size / MAX_STATIC_ASSET_BYTES).toFixed(1);\n throw new CompileError(`The image is ${megabytes} MB. Images must be 1 MB or smaller`, file);\n }\n if (!isFormat(await readFile(file))) {\n throw new CompileError(`The file isn't really a ${extension.slice(1)} image`, file);\n }\n}\n\nfunction isJpeg(header: Buffer): boolean {\n return header[0] === 0xff && header[1] === 0xd8 && header[2] === 0xff;\n}\n","import { existsSync } from 'node:fs';\nimport { join, resolve } from 'node:path';\nimport { readFile } from 'node:fs/promises';\n\nimport { parseDotModule, parseLogicModule } from '@monimesl/dot-script-parser';\n\nimport { CompileError } from '../errors';\nimport type { DotJson } from './dot-json';\nimport { DOT_JSON_FILE, readDotJson } from './dot-json';\nimport { listStatics } from './statics';\n\nconst DOT_FILE = 'dot.ts';\nexport const PAGES_DIR = 'pages';\nconst PAGE_FILES = {\n view: 'view.dml',\n state: 'state.ts',\n controller: 'controller.ts',\n} as const;\n\nexport interface PageSource {\n // The page's folder under `pages/`, `edsa-meter/confirmation`.\n readonly path: string;\n // The id pages navigate to: `definePage({ id })`, or else the path.\n readonly id: string;\n readonly view: string;\n readonly state: string;\n readonly controller: string;\n}\n\nexport interface Project {\n readonly root: string;\n // Checked, and its icon found.\n readonly dotJson: DotJson;\n // `dot.ts`, which has `export default defineDot({ pages, theme, plugins, hooks })`.\n readonly dot: string;\n // In the order of `defineDot({ pages })`; the first is the entry page.\n readonly pages: readonly PageSource[];\n // The assets under statics/, as `statics/logo.png`.\n readonly statics: readonly string[];\n}\n\nexport async function loadProject(root: string): Promise<Project> {\n root = resolve(root);\n const dotJsonFile = join(root, DOT_JSON_FILE);\n if (!existsSync(dotJsonFile)) {\n throw new CompileError(`There is no ${DOT_JSON_FILE} in this folder`, root);\n }\n const dotJson = await readDotJson(dotJsonFile);\n if (dotJson.icon !== undefined && !existsSync(join(root, dotJson.icon))) {\n throw new CompileError(`The icon \"${dotJson.icon}\" is missing`, dotJsonFile);\n }\n const dotFile = join(root, DOT_FILE);\n if (!existsSync(dotFile)) {\n throw new CompileError(\n `There is no ${DOT_FILE}. Add one: export default defineDot({ pages: [...] })`,\n root,\n );\n }\n const { pages } = parseDotModule(await readFile(dotFile, 'utf8'), dotFile);\n\n const sources = await Promise.all(pages.map((path) => loadPage(root, path, dotFile)));\n const seen = new Map<string, PageSource>();\n for (const page of sources) {\n const other = seen.get(page.id.toLowerCase());\n if (other) {\n // Point at where the clashing id is written: a custom id in controller.ts, or dot.ts\n // when both ids are folder names.\n let file = dotFile;\n if (page.id !== page.path) {\n file = page.controller;\n } else if (other.id !== other.path) {\n file = other.controller;\n }\n throw new CompileError(\n `Page id \"${page.id}\" (${page.path}) is already used by ${other.path}`,\n file,\n );\n }\n seen.set(page.id.toLowerCase(), page);\n }\n return { root, dotJson, dot: dotFile, pages: sources, statics: await listStatics(root) };\n}\n\nasync function loadPage(root: string, path: string, dotFile: string): Promise<PageSource> {\n const dir = join(root, PAGES_DIR, path);\n const files = {\n view: join(dir, PAGE_FILES.view),\n state: join(dir, PAGE_FILES.state),\n controller: join(dir, PAGE_FILES.controller),\n };\n const missing = Object.values(files).filter((file) => !existsSync(file));\n if (missing.length > 0) {\n throw new CompileError(`Page \"${path}\" is missing ${missing.join(', ')}`, dotFile);\n }\n const { id } = parseLogicModule(await readFile(files.controller, 'utf8'), files.controller);\n return { path, id: id ?? path, ...files };\n}\n","import type { PublicEnv } from './project/env';\nimport type { Project } from './project/project';\n\nexport const RUNTIME_PACKAGE = '@monimesl/dot-runtime';\n\nconst RUNTIME = {\n ui: `${RUNTIME_PACKAGE}/ui`,\n logic: `${RUNTIME_PACKAGE}/logic`,\n renderer: `${RUNTIME_PACKAGE}/react`,\n} as const;\n\nexport const ENTRY = { ui: 'dot:ui', logic: 'dot:logic' } as const;\n\n// The dot's public configuration, which the global `env` imports.\nexport const ENV_MODULE = 'dot:env';\n\nexport const viewModuleId = (id: string) => `dot:view/${id}`;\n\nfunction literal(value: string): string {\n return JSON.stringify(value);\n}\n\nfunction pageTable(project: Project, binding: string): string {\n const rows = project.pages.map((page, index) => `\\n ${literal(page.id)}: ${binding}${index},`);\n return `{${rows.join('')}\\n }`;\n}\n\n// With the components the pages use, the renderer brings only those; without, it brings them all.\nexport function uiEntry(project: Project, components?: readonly string[]): string {\n const renderer = components\n ? [\n `import { createReactRenderer, ${components.join(', ')} } from ${literal(RUNTIME.renderer)};`,\n `const createRenderer = () => createReactRenderer({ components: { ${components.join(', ')} } });`,\n ]\n : [`import createRenderer from ${literal(RUNTIME.renderer)};`];\n return [\n `import { runWebview } from ${literal(RUNTIME.ui)};`,\n ...renderer,\n ...project.pages.map(\n (page, index) => `import view${index} from ${literal(viewModuleId(page.id))};`,\n ),\n '',\n // Runs again for each hot reload while developing, which the running webview takes in place.\n 'runWebview({',\n ' renderer: createRenderer,',\n ` views: ${pageTable(project, 'view')},`,\n '});',\n '',\n ].join('\\n');\n}\n\nexport function logicEntry(project: Project, { strict }: { strict: boolean }): string {\n const options = [' dot,', ` pages: ${pageTable(project, 'page')},`];\n if (strict) {\n options.push(' strict: true,');\n }\n return [\n `import { runService } from ${literal(RUNTIME.logic)};`,\n `import dot from ${literal(project.dot)};`,\n ...project.pages.map((page, index) => `import page${index} from ${literal(page.controller)};`),\n '',\n // Runs again for each hot reload while developing, which the running service takes in place.\n 'runService({',\n ...options,\n '});',\n '',\n ].join('\\n');\n}\n\nexport function envModule(env: PublicEnv): string {\n return `export const env = Object.freeze(${JSON.stringify(env, null, 2)});\\n`;\n}\n","import { realpathSync } from 'node:fs';\nimport { join, relative, sep } from 'node:path';\n\nimport type { Plugin } from 'rolldown';\nimport { unboundReferences } from '@monimesl/dot-script-parser';\n\nimport { ENV_MODULE } from './entries';\nimport { PAGES_DIR } from './project/project';\n\nexport const DOT_GLOBALS = {\n '@monimesl/dot-runtime': ['definePage', 'defineDot', 'createEvent', 'dash'],\n [ENV_MODULE]: ['env'],\n} as const satisfies Record<string, readonly string[]>;\n\nconst SCRIPT = /\\.[cm]?[jt]sx?$/;\n\n// Imports the globals each module of a dot uses. Pages call Dash as `this.dash`, which stops their\n// calls when they close, so the global `dash` is refused in them.\nexport function dotGlobals(root: string): Plugin {\n // The bundler names modules by their real path, such as /private/var/… for /var/… on macOS.\n const realRoot = realpathSync(root);\n const isDotModule = (id: string) =>\n id.startsWith(realRoot + sep) && !id.includes(`${sep}node_modules${sep}`) && SCRIPT.test(id);\n const isPageModule = (id: string) => id.startsWith(join(realRoot, PAGES_DIR) + sep);\n return {\n name: 'dot:globals',\n transform(code, id) {\n if (!isDotModule(id)) {\n return null;\n }\n const used = new Set(unboundReferences(code, Object.values(DOT_GLOBALS).flat(), id));\n if (isPageModule(id) && used.has('dash')) {\n this.error(\n `${relative(realRoot, id)} uses the global \\`dash\\`. In a page, use \\`this.dash\\` in actions ` +\n 'and hooks, so its calls stop when the page closes.',\n );\n }\n const imports: string[] = [];\n for (const [source, names] of Object.entries(DOT_GLOBALS)) {\n const imported = names.filter((name) => used.has(name));\n if (imported.length > 0) {\n imports.push(`import { ${imported.join(', ')} } from ${JSON.stringify(source)};`);\n }\n }\n if (imports.length === 0) {\n return null;\n }\n return { code: `${code}\\n${imports.join('\\n')}\\n`, map: null };\n },\n };\n}\n","import { join } from 'node:path';\nimport { rm } from 'node:fs/promises';\n\nimport type { Plugin } from 'rolldown';\nimport { rolldown } from 'rolldown';\nimport { esmExternalRequirePlugin } from 'rolldown/plugins';\n\nimport { dotGlobals } from '../globals';\nimport { DOT_JSON_FILE } from '../project/dot-json';\n\ninterface BundleOptions {\n root: string;\n modules: ReadonlyMap<string, string>;\n entry: string;\n outDir: string;\n fileName: string;\n format: 'esm' | 'iife';\n mode: 'production' | 'development';\n minify: boolean;\n conditions: readonly string[];\n // Imports left for the page to resolve, such as React, which hosts serve.\n external: readonly string[];\n}\n\n/** Bundles one entry with rolldown, replacing `outDir`. Returns the written files. */\nexport async function bundle(options: BundleOptions): Promise<string[]> {\n await rm(options.outDir, { recursive: true, force: true });\n const plugins: Plugin[] = [];\n if (options.external.length > 0) {\n plugins.push(esmExternalRequirePlugin({ external: [...options.external] }));\n }\n plugins.push(generatedModules(options.root, options.modules), dotGlobals(options.root));\n const bundler = await rolldown({\n input: options.entry,\n cwd: options.root,\n platform: 'browser',\n plugins,\n resolve: {\n conditionNames: [...options.conditions, 'browser', 'import', 'require', 'default'],\n },\n transform: { define: { 'process.env.NODE_ENV': JSON.stringify(options.mode) } },\n onLog(level, log, defaultHandler) {\n if (log.code !== 'MODULE_LEVEL_DIRECTIVE') {\n defaultHandler(level, log);\n }\n },\n });\n try {\n const { output } = await bundler.write({\n dir: options.outDir,\n format: options.format,\n entryFileNames: options.fileName,\n sourcemap: options.mode === 'development',\n sourcemapExcludeSources: options.mode === 'production',\n minify: options.minify,\n });\n return output.map((chunk) => join(options.outDir, chunk.fileName));\n } finally {\n await bundler.close();\n }\n}\n\n/** Serves generated modules and resolves their imports as if they lived in the project root. */\nfunction generatedModules(root: string, modules: ReadonlyMap<string, string>): Plugin {\n const importer = join(root, DOT_JSON_FILE);\n return {\n name: 'dot:generated',\n // The `\\0` prefix is rolldown's mark for virtual modules: other plugins leave them alone.\n async resolveId(source, from) {\n if (modules.has(source)) {\n return `\\0${source}`;\n }\n if (!from?.startsWith('\\0dot:')) {\n return null;\n }\n const resolved = await this.resolve(source, importer, { skipSelf: true });\n if (!resolved) {\n this.error(`Can't find \"${source}\" from ${root}. Is it installed?`);\n }\n return resolved;\n },\n load(id) {\n if (!id.startsWith('\\0')) {\n return null;\n }\n return modules.get(id.slice(1)) ?? null;\n },\n };\n}\n","import { readFileSync } from 'node:fs';\nimport { createRequire } from 'node:module';\nimport { dirname, join } from 'node:path';\n\nimport { CompileError } from '../errors';\nimport { RUNTIME_PACKAGE } from '../entries';\n\nconst RENDERER_PACKAGE = '@monimesl/dot-renderer-react';\n\n// The @monimesl/dot-runtime a project builds with.\nexport interface InstalledRuntime {\n readonly version: string;\n readonly react: ReactHost;\n // The installed React renderer's stylesheet.\n readonly stylesheet: string;\n}\n\n// The React the runtime's renderer uses, which hosts serve rather than each dot bundling it.\nexport interface ReactHost {\n readonly version: string;\n // The built files, in a folder per version: `<dir>/<version>/react.js`.\n readonly dir: string;\n}\n\ninterface InstalledPackage {\n readonly dir: string;\n readonly version: string;\n}\n\nexport function installedRuntime(root: string): InstalledRuntime {\n const runtime = requirePackage(root, RUNTIME_PACKAGE, 'the project');\n const renderer = requirePackage(runtime.dir, RENDERER_PACKAGE, RUNTIME_PACKAGE);\n const react = requirePackage(renderer.dir, 'react', RENDERER_PACKAGE);\n return {\n version: runtime.version,\n react: { version: react.version, dir: join(renderer.dir, 'dist', 'react-host') },\n stylesheet: join(renderer.dir, 'dist', 'styles.css'),\n };\n}\n\nfunction requirePackage(fromDir: string, name: string, installedIn: string): InstalledPackage {\n const require = createRequire(join(fromDir, 'package.json'));\n let packageFile: string;\n try {\n packageFile = require.resolve(`${name}/package.json`);\n } catch {\n throw new CompileError(`${name} is not installed in ${installedIn}`, fromDir);\n }\n const { version } = JSON.parse(readFileSync(packageFile, 'utf8')) as { version: string };\n return { dir: dirname(packageFile), version };\n}\n","import type { ExprNode } from '@monimesl/dot-script-parser';\n\nexport type Scope = ReadonlyMap<string, string>;\n\nconst IDENTIFIER = /^[A-Za-z_$][\\w$]*$/;\n\nexport const EMPTY_SCOPE: Scope = new Map();\n\nexport function isIdentifier(name: string): boolean {\n return IDENTIFIER.test(name);\n}\n\n// Loop variables read their local; everything else reads page data from `d`.\nexport function printExpr(expr: ExprNode, scope: Scope): string {\n const print = (node: ExprNode) => printExpr(node, scope);\n switch (expr.type) {\n case 'Literal':\n return JSON.stringify(expr.value);\n case 'Identifier':\n return scope.get(expr.name) ?? `d.${expr.name}`;\n case 'Member':\n return expr.property.type === 'Literal' && typeof expr.property.value === 'string'\n ? `${print(expr.object)}?.${expr.property.value}`\n : `${print(expr.object)}?.[${print(expr.property)}]`;\n case 'Unary':\n return `(${expr.operator}${expr.operator === 'typeof' ? ' ' : ''}${print(expr.argument)})`;\n case 'Binary':\n return `(${print(expr.left)} ${expr.operator} ${print(expr.right)})`;\n case 'Conditional':\n return `(${print(expr.test)} ? ${print(expr.consequent)} : ${print(expr.alternate)})`;\n }\n}\n\nexport function stateReads(expr: ExprNode, scope: Scope): string[] {\n const reads = (node: ExprNode) => stateReads(node, scope);\n switch (expr.type) {\n case 'Literal':\n return [];\n case 'Identifier':\n return scope.has(expr.name) ? [] : [expr.name];\n case 'Member':\n return [...reads(expr.object), ...reads(expr.property)];\n case 'Unary':\n return reads(expr.argument);\n case 'Binary':\n return [...reads(expr.left), ...reads(expr.right)];\n case 'Conditional':\n return [...reads(expr.test), ...reads(expr.consequent), ...reads(expr.alternate)];\n }\n}\n","import type { ComponentManifest } from '@monimesl/dot-core';\nimport type { DmlElementNode } from '@monimesl/dot-dml-parser';\n\nimport type { ViewCompiler } from './compiler';\nimport { isIdentifier, type Scope } from './expression';\n\nexport const CONTROL_FLOW_TAGS: Readonly<\n Record<string, { readonly attributes: readonly string[]; readonly required: readonly string[] }>\n> = {\n If: { attributes: ['condition'], required: ['condition'] },\n ElseIf: { attributes: ['condition'], required: ['condition'] },\n Else: { attributes: [], required: [] },\n For: { attributes: ['each', 'item', 'index', 'key'], required: ['each'] },\n};\n\n// `<If>`, `<ElseIf>`s and an optional `<Else>`, as siblings: `...(a ? [...] : b ? [...] : [...])`.\nexport function compileConditional(\n compiler: ViewCompiler,\n branches: readonly DmlElementNode[],\n parent: ComponentManifest | null,\n scope: Scope,\n): string {\n let code = '[]';\n for (const branch of [...branches].reverse()) {\n const body = `[${compiler.compile(branch.children, parent, scope).join(', ')}]`;\n compiler.checkControlFlowElementAttributes(branch);\n if (branch.tag === 'Else') {\n code = body;\n } else {\n const condition = compiler.compileSingleBinding(branch, 'condition', scope);\n code = `${condition} ? ${body} : ${code}`;\n }\n }\n return `...(${code})`;\n}\n\n// `<For each=\"{{ list }}\" item index key>`: `each(list, (item, index) => [...], key?)`.\nexport function compileLoop(\n compiler: ViewCompiler,\n node: DmlElementNode,\n parent: ComponentManifest | null,\n scope: Scope,\n): string {\n compiler.checkControlFlowElementAttributes(node);\n const list = compiler.compileSingleBinding(node, 'each', scope);\n const { item = 'item', index = 'index', key } = node.attributes;\n\n const checkVariableName = (attribute: string, name: string) => {\n if (!isIdentifier(name)) {\n compiler.fail(`<For> \\`${attribute}\\` must be a variable name, got \"${name}\"`, node.loc);\n }\n };\n checkVariableName('item', item);\n checkVariableName('index', index);\n if (item === index) {\n compiler.fail('<For> `item` and `index` need different names', node.loc);\n }\n // Loop variables get a `$` prefix in the generated code, so they never clash with its own names.\n const itemVar = '$' + item;\n const indexVar = '$' + index;\n const inner = new Map(scope).set(item, itemVar).set(index, indexVar);\n const body = compiler.compile(node.children, parent, inner).join(', ');\n const args = [list, `(${itemVar}, ${indexVar}) => [${body}]`];\n if (key === undefined) {\n compiler.warn(\n '<For> has no `key`, so items are matched by position. Add key=\"<property>\" (or \"*this\") ' +\n 'so rows stay right when items move, or key=\"*index\" to keep matching by position.',\n node.loc,\n );\n } else if (key === '*index') {\n // Matching by position is what `each` does without a key.\n } else if (key === '*this') {\n args.push(`(${itemVar}) => ${itemVar}`);\n } else if (isIdentifier(key)) {\n args.push(`(${itemVar}) => ${itemVar}?.${key}`);\n } else {\n compiler.fail(`<For> \\`key\\` must be a property name, *this or *index, got \"${key}\"`, node.loc);\n }\n return `each(${args.join(', ')})`;\n}\n","import {\n builtinRegistry,\n eventName,\n type ComponentManifest,\n type ComponentRegistry,\n type PropType,\n type SourceLocation,\n} from '@monimesl/dot-core';\nimport type { DmlElementNode, DmlNode } from '@monimesl/dot-dml-parser';\nimport { type ExprNode, parseExpression, ScriptParseError } from '@monimesl/dot-script-parser';\n\nimport { CompileError, CompileWarning } from '../errors';\nimport { STATICS_DIR } from '../project/statics';\nimport { compileConditional, compileLoop, CONTROL_FLOW_TAGS } from './control-flow';\nimport { EMPTY_SCOPE, isIdentifier, printExpr, type Scope, stateReads } from './expression';\n\nexport interface ViewContext {\n file: string;\n components?: ComponentRegistry | undefined;\n // mutations and actions declared in controller.ts; event attributes must name one of them\n handlers?: readonly string[] | undefined;\n // keys declared in state.ts; omitted skips the check\n stateKeys?: readonly string[] | undefined;\n // the assets under statics/, as `statics/logo.png`; a written reference to any other is an error\n statics?: readonly string[] | undefined;\n onWarning?: ((warning: CompileWarning) => void) | undefined;\n // receives the name of each component the markup uses\n onComponent?: ((name: string) => void) | undefined;\n}\n\n// Every page's markup is one root <View>. Unless the markup says otherwise, it fills the page and\n// puts space at the page's edges and between its children.\nexport const ROOT_TAG = 'View';\nconst ROOT_DEFAULTS = { padding: 'large', gap: 'large', grow: true };\n\ntype Segment = { kind: 'static'; value: string } | { kind: 'binding'; code: string };\nconst SEGMENT_BINDING_REGEXP = /{{([\\s\\S]*?)}}/g;\n\nexport const DATA_PREFIX = 'data-';\nconst DATA_ATTRIBUTE = /^data-[a-z][a-z0-9]*(-[a-z0-9]+)*$/;\n\nexport class ViewCompiler {\n readonly #context: ViewContext;\n readonly #components: ComponentRegistry;\n\n constructor(context: ViewContext) {\n this.#context = context;\n this.#components = context.components ?? builtinRegistry;\n for (const name of Object.keys(CONTROL_FLOW_TAGS)) {\n if (name in this.#components) {\n this.fail(`<${name}> is reserved for control flow`);\n }\n }\n }\n\n compile(nodes: readonly DmlNode[], parent: ComponentManifest | null, scope: Scope): string[] {\n const out: string[] = [];\n for (let i = 0; i < nodes.length; i++) {\n const node = nodes[i]!;\n if (node.type === 'Text') {\n out.push(...this.#compileText(node.content, parent, scope, node.loc));\n continue;\n }\n switch (node.tag) {\n case 'If': {\n const branches = ifChain(node, nodes.slice(i + 1));\n out.push(compileConditional(this, branches, parent, scope));\n i += branches.length - 1;\n break;\n }\n case 'ElseIf':\n case 'Else':\n return this.fail(`<${node.tag}> must come right after <If> or <ElseIf>`, node.loc);\n case 'For':\n out.push(compileLoop(this, node, parent, scope));\n break;\n default:\n out.push(`h(${this.#compileElementArgs(node, parent, scope).join(', ')})`);\n }\n }\n return out;\n }\n\n #compileText(\n content: string,\n parent: ComponentManifest | null,\n scope: Scope,\n loc?: SourceLocation,\n ): string[] {\n if (parent?.children !== 'text') {\n // Text first, as what loose text usually wants.\n const inline = Object.values(this.#components)\n .filter((component) => component.inline && component.children === 'text')\n .sort((a, b) => Number(b.name === 'Text') - Number(a.name === 'Text'));\n const wrappers = inline.map((component) => `<${component.name}>`).join(' or ');\n this.fail(`'${content}' must be inside ${wrappers || 'an inline component'}`, loc);\n }\n return this.#compileAsSegments(content, scope, loc).map(printSegment);\n }\n\n // A page's markup is one root <View>; this returns the arguments of its `h` call.\n compileRoot(nodes: readonly DmlNode[]): string[] {\n if (!this.#components[ROOT_TAG]) {\n this.fail(`The components must include <${ROOT_TAG}>, which is the root of every page`);\n }\n const [root, outside] = nodes;\n if (root?.type !== 'Element' || root.tag !== ROOT_TAG) {\n return this.fail(`A page must have one <${ROOT_TAG}> around everything else`, root?.loc);\n }\n if (outside) {\n const name = outside.type === 'Text' ? `'${outside.content}'` : `<${outside.tag}>`;\n this.fail(`${name} must be inside the page's <${ROOT_TAG}>`, outside.loc);\n }\n return this.#compileElementArgs(root, null, EMPTY_SCOPE, ROOT_DEFAULTS);\n }\n\n #compileElementArgs(\n node: DmlElementNode,\n parent: ComponentManifest | null,\n scope: Scope,\n defaults: Readonly<Record<string, unknown>> = {},\n ): string[] {\n const component = this.#components[node.tag];\n if (!component) {\n return this.fail(`Unknown component <${node.tag}>.`, node.loc);\n }\n this.#context.onComponent?.(component.name);\n if (parent && !canHaveChildren(parent, component)) {\n this.fail(`<${node.tag}> can't go inside <${parent.name}>`, node.loc);\n }\n const attributes = Object.entries(node.attributes);\n for (const { prop, ignores, because } of component.overrides ?? []) {\n if (prop in node.attributes && ignores in node.attributes) {\n this.warn(\n `<${component.name}> ignores \\`${ignores}\\` with \\`${prop}\\`: ${because}`,\n node.loc,\n );\n }\n }\n const data = this.#compileData(\n attributes.filter(([name]) => name.startsWith(DATA_PREFIX)),\n scope,\n node.loc,\n );\n const props = [\n ...attributes\n .filter(([name]) => !name.startsWith(DATA_PREFIX))\n .map(([name, value]) => this.#compileProp(component, name, value, scope, node.loc)),\n ...Object.entries(node.events).map(([name, handler]) =>\n this.#compileEvent(component, name, handler, data, node.loc),\n ),\n ...Object.entries(defaults)\n .filter(([name]) => !(name in node.attributes))\n .map(([name, value]) => `${name}: ${JSON.stringify(value)}`),\n ];\n return [\n JSON.stringify(component.name),\n props.length ? `{ ${props.join(', ')} }` : 'null',\n ...this.compile(node.children, component, scope),\n ];\n }\n\n #compileProp(\n component: ComponentManifest,\n name: string,\n value: string,\n scope: Scope,\n loc?: SourceLocation,\n ): string {\n const type = component.props[name];\n if (!type) {\n return this.fail(`<${component.name}> has no \\`${name}\\` attribute`, loc);\n }\n const segments = this.#compileAsSegments(value, scope, loc);\n const bindingCode = singleBindingCode(segments);\n if (bindingCode !== null) {\n return `${name}: ${bindingCode}`;\n }\n if (segments.every((segment) => segment.kind === 'static')) {\n this.#checkStaticsReference(value, loc);\n return `${name}: ${this.#compileLiteral(type, value, loc)}`;\n }\n if (type !== 'string' && type !== 'any') {\n const kind = typeof type === 'string' ? `a ${type}` : 'an enum';\n this.fail(`\\`${name}\\` is ${kind}, so set it with one {{ }} binding`, loc);\n }\n return `${name}: ${segments.map(printSegment).join(' + ')}`;\n }\n\n // `data-account-id=\"{{ account.id }}\"` becomes `{ accountId: d.account.id }`, which the element's\n // events carry to their handlers.\n #compileData(\n attributes: readonly [string, string][],\n scope: Scope,\n loc?: SourceLocation,\n ): string | null {\n if (attributes.length === 0) {\n return null;\n }\n const entries = attributes.map(([name, value]) => {\n if (!DATA_ATTRIBUTE.test(name)) {\n this.fail(\n `\\`${name}\\` must start with data- and use lowercase words joined by -, such as data-account-id`,\n loc,\n );\n }\n return `${dataKey(name)}: ${this.#compileDataValue(value, scope, loc)}`;\n });\n return `{ ${entries.join(', ')} }`;\n }\n\n // A single binding keeps its value's type, such as a number; anything else is a string.\n #compileDataValue(value: string, scope: Scope, loc?: SourceLocation): string {\n const segments = this.#compileAsSegments(value, scope, loc);\n const bindingCode = singleBindingCode(segments);\n if (bindingCode !== null) {\n return bindingCode;\n }\n if (segments.length === 0) {\n return '\"\"';\n }\n return segments.map(printSegment).join(' + ');\n }\n\n #checkStaticsReference(value: string, loc: SourceLocation | undefined): void {\n const { statics } = this.#context;\n if (statics && value.startsWith(`${STATICS_DIR}/`) && !statics.includes(value)) {\n this.fail(`\"${value}\" is not in ${STATICS_DIR}/`, loc);\n }\n }\n\n #compileEvent(\n component: ComponentManifest,\n attribute: string,\n handler: string,\n data: string | null,\n loc?: SourceLocation,\n ): string {\n const event = eventName(attribute);\n if (!component.events.includes(event)) {\n this.fail(`<${component.name}> has no \\`${attribute}\\` event`, loc);\n }\n if (!isIdentifier(handler)) {\n this.fail(\n `\\`${attribute}\\` must name a mutation or action in controller.ts, got \"${handler}\"`,\n loc,\n );\n }\n const { handlers } = this.#context;\n if (handlers && !handlers.includes(handler)) {\n this.fail(`\\`${handler}\\` is not a mutation or action in controller.ts`, loc);\n }\n const args = data ? `${JSON.stringify(handler)}, ${data}` : JSON.stringify(handler);\n return `${attribute}: on(${args})`;\n }\n\n checkControlFlowElementAttributes(node: DmlElementNode): void {\n const { attributes: allowed, required } = CONTROL_FLOW_TAGS[node.tag]!;\n const [event] = Object.keys(node.events);\n if (event) {\n this.fail(\n `<${node.tag}> can't handle events such as \\`${event}\\`. ` +\n 'Put the handler on a component inside it.',\n node.loc,\n );\n }\n for (const name of Object.keys(node.attributes)) {\n if (!allowed.includes(name)) {\n this.fail(`<${node.tag}> has no \\`${name}\\` attribute`, node.loc);\n }\n }\n for (const name of required) {\n if (!(name in node.attributes)) {\n this.fail(`<${node.tag}> requires \\`${name}\\``, node.loc);\n }\n }\n }\n\n compileSingleBinding(node: DmlElementNode, attribute: string, scope: Scope): string {\n const segments = this.#compileAsSegments(node.attributes[attribute] ?? '', scope, node.loc);\n const bindingCode = singleBindingCode(segments);\n if (bindingCode === null) {\n return this.fail(`<${node.tag}> \\`${attribute}\\` must be one {{ }} binding`, node.loc);\n }\n return bindingCode;\n }\n\n #compileLiteral(type: PropType, value: string, loc?: SourceLocation): string {\n if (typeof type !== 'string') {\n if (type.includes(value)) {\n return JSON.stringify(value);\n }\n return this.fail(\n `Expected one of ${type.map((option) => `\"${option}\"`).join(', ')}, got \"${value}\"`,\n loc,\n );\n }\n switch (type) {\n case 'boolean':\n if (value === '' || value === 'true') {\n return 'true';\n }\n if (value === 'false') {\n return 'false';\n }\n return this.fail(`Expected true or false, got \"${value}\"`, loc);\n case 'number':\n if (value.trim() !== '' && Number.isFinite(Number(value))) {\n return String(Number(value));\n }\n return this.fail(`Expected a number, got \"${value}\"`, loc);\n case 'string':\n case 'any':\n return JSON.stringify(value);\n }\n }\n\n #compileAsSegments(input: string, scope: Scope, loc?: SourceLocation): Segment[] {\n const segments: Segment[] = [];\n let last = 0;\n for (const match of input.matchAll(SEGMENT_BINDING_REGEXP)) {\n if (match.index > last) {\n segments.push({ kind: 'static', value: input.slice(last, match.index) });\n }\n const expr = this.#parseBindingExpression(match[1]!, scope, loc);\n segments.push({ kind: 'binding', code: printExpr(expr, scope) });\n last = match.index + match[0].length;\n }\n const rest = input.slice(last);\n if (rest.includes('{{')) {\n this.fail('A binding is missing its closing `}}`', loc);\n }\n if (rest) {\n segments.push({ kind: 'static', value: rest });\n }\n return segments;\n }\n\n #parseBindingExpression(source: string, scope: Scope, loc?: SourceLocation): ExprNode {\n let expr: ExprNode;\n try {\n expr = parseExpression(source);\n } catch (error) {\n if (!(error instanceof ScriptParseError)) {\n throw error;\n }\n return this.fail(`Invalid binding \\`{{${source}}}\\`: ${error.reason}`, loc);\n }\n const { stateKeys } = this.#context;\n if (stateKeys) {\n const undeclared = stateReads(expr, scope).find((name) => !stateKeys.includes(name));\n if (undeclared) {\n const declared = stateKeys.join(', ') || 'none';\n this.fail(`\\`${undeclared}\\` isn't in state.ts. It has: ${declared}`, loc);\n }\n }\n return expr;\n }\n\n warn(message: string, loc?: SourceLocation): void {\n this.#context.onWarning?.(new CompileWarning(message, this.#context.file, loc));\n }\n\n fail(message: string, loc?: SourceLocation): never {\n throw new CompileError(message, this.#context.file, loc);\n }\n}\n\n// The <If>, then the <ElseIf> and <Else> siblings that continue it.\nfunction ifChain(ifNode: DmlElementNode, nextSiblings: readonly DmlNode[]): DmlElementNode[] {\n const branches = [ifNode];\n for (const sibling of nextSiblings) {\n if (branches.at(-1)!.tag === 'Else' || !continuesIf(sibling)) {\n break;\n }\n branches.push(sibling);\n }\n return branches;\n}\n\nfunction continuesIf(node: DmlNode): node is DmlElementNode {\n return node.type === 'Element' && (node.tag === 'ElseIf' || node.tag === 'Else');\n}\n\nfunction singleBindingCode(segments: readonly Segment[]): string | null {\n const [first] = segments;\n if (segments.length === 1 && first?.kind === 'binding') {\n return first.code;\n }\n return null;\n}\n\n// `data-account-id` -> `accountId`\nexport function dataKey(attribute: string): string {\n const [first = '', ...rest] = attribute.slice(DATA_PREFIX.length).split('-');\n return first + rest.map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join('');\n}\n\nfunction printSegment(segment: Segment): string {\n return segment.kind === 'static' ? JSON.stringify(segment.value) : `text(${segment.code})`;\n}\n\nexport function canHaveChildren(parent: ComponentManifest, child: ComponentManifest): boolean {\n switch (parent.children) {\n case 'none':\n return false;\n case 'text':\n return child.inline === true;\n case 'elements':\n return true;\n }\n}\n","import { parseDmlToIR } from '@monimesl/dot-dml-parser';\n\nimport { ViewCompiler, type ViewContext } from './compiler';\n\n// A module whose render function returns the root <View>, built with the renderer's `h`.\nexport function compileView(source: string, context: ViewContext): string {\n const nodes = parseDmlToIR(source, {\n locations: true,\n file: context.file,\n whitespace: 'collapse',\n });\n const compiler = new ViewCompiler(context);\n const rootArgs = compiler.compileRoot(nodes);\n return [\n 'export default function render(d, { h, on, text, each }) {',\n ` return h(${rootArgs.map((arg) => `\\n ${arg},`).join('')}\\n );`,\n '}',\n '',\n ].join('\\n');\n}\n","import { mkdir, readFile, writeFile } from 'node:fs/promises';\nimport { dirname, join } from 'node:path';\n\nimport type { PublicEnv } from './project/env';\nimport type { Project } from './project/project';\n\nconst PAGE_TYPES_FILE = '.dot/pages.d.ts';\nconst ENV_TYPES_FILE = '.dot/env.d.ts';\n\n// Page ids, so navigation targets are typed.\nexport async function writePageTypes(project: Project): Promise<string> {\n const members = project.pages.map((page) => `${JSON.stringify(page.id)}: true;`);\n return writeIfChanged(\n join(project.root, PAGE_TYPES_FILE),\n globalInterface('dot.ts', 'DotPageIds', members),\n );\n}\n\n// The names in the global `env`, so a misspelled one is a type error.\nexport async function writeEnvTypes(project: Project, env: PublicEnv): Promise<string> {\n const members = Object.keys(env).map((name) => `readonly ${name}: string;`);\n return writeIfChanged(\n join(project.root, ENV_TYPES_FILE),\n globalInterface('the variables in .env files', 'DotPublicEnv', members),\n );\n}\n\n// A declaration file that fills in one of the runtime's global interfaces.\nfunction globalInterface(source: string, name: string, members: readonly string[]): string {\n return [\n `// Generated by dotctl from ${source}. Do not edit.`,\n 'declare global {',\n ` interface ${name} {`,\n ...members.map((member) => ` ${member}`),\n ' }',\n '}',\n '',\n 'export {};',\n '',\n ].join('\\n');\n}\n\n// Leaves an unchanged file alone, so editors and watchers don't see a change.\nasync function writeIfChanged(file: string, content: string): Promise<string> {\n const current = await readFile(file, 'utf8').catch(() => null);\n if (current !== content) {\n await mkdir(dirname(file), { recursive: true });\n await writeFile(file, content);\n }\n return file;\n}\n","import { existsSync } from 'node:fs';\nimport { readFile } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport { parseEnv } from 'node:util';\n\nimport { CompileError } from '../errors';\n\n// Every variable in the .env files reaches the dot, where anyone who has it can read them: a\n// secret never belongs in them.\nconst PUBLIC_ENV_NAME = /^[A-Z][A-Z0-9_]*$/;\n\nexport type PublicEnv = Readonly<Record<string, string>>;\n\n// The dot's public configuration, from `.env`, then `.env.<mode>`; each later one wins. The shell's\n// environment then overrides the variables the files declare, so CI can set a value, but it never\n// adds one: the rest of the shell, such as PATH or a CI secret, stays out of the dot.\nexport async function loadPublicEnv(\n root: string,\n mode: string,\n shellEnv: Readonly<Record<string, string | undefined>> = process.env,\n): Promise<PublicEnv> {\n const env: Record<string, string> = {};\n for (const fileName of ['.env', `.env.${mode}`]) {\n const file = join(root, fileName);\n if (existsSync(file)) {\n addVariables(env, parseEnv(await readFile(file, 'utf8')), file);\n }\n }\n // Sorted, so the bundle and the generated types don't change with the order of the files.\n const sorted: Record<string, string> = {};\n for (const name of Object.keys(env).sort()) {\n sorted[name] = shellEnv[name] ?? env[name]!;\n }\n return sorted;\n}\n\nfunction addVariables(\n env: Record<string, string>,\n variables: Readonly<Record<string, string | undefined>>,\n file: string,\n): void {\n for (const [name, value] of Object.entries(variables)) {\n if (value === undefined) {\n continue;\n }\n if (!PUBLIC_ENV_NAME.test(name)) {\n throw new CompileError(\n `\"${name}\" must be capital letters, digits and underscores, starting with a letter`,\n file,\n );\n }\n env[name] = value;\n }\n}\n","import { copyFile, mkdir, readdir, readFile, rm, writeFile } from 'node:fs/promises';\nimport { dirname, join, relative, resolve, sep } from 'node:path';\n\nimport { zipSync } from 'fflate';\n\nimport type { ComponentManifest, ComponentRegistry } from '@monimesl/dot-core';\nimport {\n REACT_HOST_MODULES,\n builtinComponents,\n componentRegistry,\n reactHostUrl,\n} from '@monimesl/dot-core';\nimport { parseLogicModule, parseStateModule } from '@monimesl/dot-script-parser';\n\nimport { bundle } from './bundle';\nimport type { InstalledRuntime } from './packages';\nimport { installedRuntime } from './packages';\nimport { compileView } from '../view';\nimport type { ViewContext } from '../view';\nimport { writeEnvTypes, writePageTypes } from '../types';\nimport type { PublicEnv } from '../project/env';\nimport { loadPublicEnv } from '../project/env';\nimport type { CompileWarning } from '../errors';\nimport type { DotJson } from '../project/dot-json';\nimport type { PageSource, Project } from '../project/project';\nimport { loadProject } from '../project/project';\nimport { ENTRY, ENV_MODULE, envModule, logicEntry, uiEntry, viewModuleId } from '../entries';\n\nconst MANIFEST_FILE = 'manifest.json';\nconst ICON_FILE = 'icon.png';\nconst STYLES_FILE = 'dot.css';\n// The earliest date a ZIP file can hold.\nconst ARCHIVE_DATE = new Date(1980, 0, 1);\n\nexport interface BuildOptions {\n root: string;\n // defaults to `<root>/dist`\n outDir?: string | undefined;\n // `production` (the default) is always minified; `development` never is, so it reads in a debugger.\n mode?: 'production' | 'development' | undefined;\n components?: readonly ComponentManifest[] | undefined;\n conditions?: readonly string[] | undefined;\n}\n\nexport interface BuildResult {\n readonly outDir: string;\n readonly files: readonly string[];\n readonly warnings: readonly CompileWarning[];\n}\n\n/** Compiles and bundles the dot into `outDir` for hosts, and refreshes the types in `.dot/`. */\nexport async function build(options: BuildOptions): Promise<BuildResult> {\n const project = await loadProject(options.root);\n const mode = options.mode ?? 'production';\n const env = await loadPublicEnv(project.root, mode);\n const runtime = installedRuntime(project.root);\n await writePageTypes(project);\n await writeEnvTypes(project, env);\n\n const warnings: CompileWarning[] = [];\n const components = componentRegistry([\n ...Object.values(builtinComponents),\n ...(options.components ?? []),\n ]);\n const modules = await compileModules(project, env, components, mode, warnings);\n\n const outDir = resolve(project.root, options.outDir ?? 'dist');\n const uiDir = join(outDir, 'ui');\n const bundleLayer = (\n entry: string,\n layerDir: string,\n format: 'esm' | 'iife',\n external: readonly string[],\n ) =>\n bundle({\n root: project.root,\n modules,\n entry,\n outDir: layerDir,\n fileName: 'dot.js',\n format,\n mode,\n minify: mode === 'production',\n conditions: options.conditions ?? [],\n external,\n });\n // Each bundle empties its folder first, so the files that go next to it are written after.\n const files = [\n // React stays out of the webview bundle: the host serves it, and the import map points at it.\n ...(await bundleLayer(ENTRY.ui, uiDir, 'esm', Object.keys(REACT_HOST_MODULES))),\n ...(await bundleLayer(ENTRY.logic, join(outDir, 'logic'), 'iife', [])),\n await copyRendererStyles(runtime, uiDir),\n await writeIndexHtml(project, uiDir, runtime.react.version),\n ...(await copyStatics(project, uiDir)),\n await writeManifest(project, outDir, runtime),\n ];\n const icon = await copyIcon(project, outDir);\n if (icon) {\n files.push(icon);\n }\n if (mode === 'production') {\n files.push(await writeArchive(project, outDir, files));\n }\n return { outDir, files, warnings };\n}\n\nasync function copyRendererStyles(runtime: InstalledRuntime, uiDir: string): Promise<string> {\n const file = join(uiDir, STYLES_FILE);\n await mkdir(uiDir, { recursive: true });\n await copyFile(runtime.stylesheet, file);\n return file;\n}\n\n// The build as the one file Dash downloads, `<id>-<version>.zip`, without source maps. Its files\n// all get the same date, so the same build always makes the same archive.\nasync function writeArchive(project: Project, outDir: string, files: string[]): Promise<string> {\n const entries: Record<string, [Uint8Array, { mtime: Date }]> = {};\n for (const file of files) {\n if (!file.endsWith('.map')) {\n const name = relative(outDir, file).split(sep).join('/');\n entries[name] = [await readFile(file), { mtime: ARCHIVE_DATE }];\n }\n }\n const { id, version } = project.dotJson;\n // An earlier version's archive would be left beside it.\n for (const name of await readdir(outDir)) {\n if (name.startsWith(`${id}-`) && name.endsWith('.zip')) {\n await rm(join(outDir, name));\n }\n }\n const file = join(outDir, `${id}-${version}.zip`);\n await writeFile(file, zipSync(entries, { level: 9 }));\n return file;\n}\n\nasync function compileModules(\n project: Project,\n env: PublicEnv,\n components: ComponentRegistry,\n mode: 'production' | 'development',\n warnings: CompileWarning[],\n): Promise<Map<string, string>> {\n const modules = new Map<string, string>();\n const used = new Set<string>();\n for (const page of project.pages) {\n const view = await compilePage(page, components, project.statics, warnings, used);\n modules.set(viewModuleId(page.id), view);\n }\n // While developing, a hot reload keeps the renderer, so it has every component a page may add.\n const builtins = [...used].filter((name) => name in builtinComponents).sort();\n modules.set(ENTRY.ui, uiEntry(project, mode === 'production' ? builtins : undefined));\n modules.set(ENTRY.logic, logicEntry(project, { strict: mode === 'development' }));\n modules.set(ENV_MODULE, envModule(env));\n return modules;\n}\n\nasync function writeIndexHtml(\n project: Project,\n uiDir: string,\n reactVersion: string,\n): Promise<string> {\n const file = join(uiDir, 'index.html');\n await mkdir(uiDir, { recursive: true });\n await writeFile(file, indexHtml(project.dotJson.name, reactVersion));\n return file;\n}\n\nasync function copyStatics(project: Project, uiDir: string): Promise<string[]> {\n const copies: string[] = [];\n for (const file of project.statics) {\n const copy = join(uiDir, file);\n await mkdir(dirname(copy), { recursive: true });\n await copyFile(join(project.root, file), copy);\n copies.push(copy);\n }\n return copies;\n}\n\nasync function writeManifest(\n project: Project,\n outDir: string,\n runtime: InstalledRuntime,\n): Promise<string> {\n const file = join(outDir, MANIFEST_FILE);\n const manifest = dotManifest(project, runtime);\n await writeFile(file, `${JSON.stringify(manifest, null, 2)}\\n`);\n return file;\n}\n\n// The icon goes next to the manifest under one name, so hosts always find it in the same place.\nasync function copyIcon(project: Project, outDir: string): Promise<string | null> {\n if (project.dotJson.icon === undefined) {\n return null;\n }\n const file = join(outDir, ICON_FILE);\n await copyFile(join(project.root, project.dotJson.icon), file);\n return file;\n}\n\n/** What a host needs to run the build: dot.json without `$schema`, where it starts and its pages. */\nexport interface DotManifest extends Omit<DotJson, 'icon'> {\n // The version of @monimesl/dot-runtime in the bundles, so a host can tell whether it runs them.\n readonly runtime: string;\n // The React version the webview imports from the host.\n readonly react: string;\n readonly entry: string;\n readonly pages: readonly string[];\n // `icon.png` next to the manifest, when dot.json names an icon.\n readonly icon: string | undefined;\n}\n\nfunction dotManifest(project: Project, runtime: InstalledRuntime): DotManifest {\n const { id, name, version, description, categories, permissions, network, dash } =\n project.dotJson;\n const pages = project.pages.map((page) => page.id);\n return {\n id,\n name,\n version,\n runtime: runtime.version,\n react: runtime.react.version,\n description,\n categories,\n icon: project.dotJson.icon === undefined ? undefined : ICON_FILE,\n permissions,\n network,\n dash,\n entry: pages[0]!,\n pages,\n };\n}\n\nasync function compilePage(\n page: PageSource,\n components: ComponentRegistry,\n statics: readonly string[],\n warnings: CompileWarning[],\n used: Set<string>,\n): Promise<string> {\n const [view, state, controller] = await Promise.all([\n readFile(page.view, 'utf8'),\n readFile(page.state, 'utf8'),\n readFile(page.controller, 'utf8'),\n ]);\n const context: ViewContext = {\n file: page.view,\n components,\n statics,\n onWarning: (warning) => warnings.push(warning),\n onComponent: (name) => used.add(name),\n };\n // State keys are checked only when state.ts is a plain object literal.\n const { keys } = parseStateModule(state, page.state);\n if (keys) {\n context.stateKeys = keys;\n }\n // Handler names are checked only when both lists could be read without running the code.\n const { mutations, actions } = parseLogicModule(controller, page.controller);\n if (mutations && actions) {\n context.handlers = [...mutations, ...actions];\n }\n return compileView(view, context);\n}\n\nfunction indexHtml(title: string, reactVersion: string): string {\n const escaped = title.replace(/[&<>\"]/g, (char) => `&#${char.charCodeAt(0)};`);\n const imports: Record<string, string> = {};\n for (const [name, file] of Object.entries(REACT_HOST_MODULES)) {\n imports[name] = reactHostUrl(reactVersion, file);\n }\n return `<!doctype html>\n<html lang=\"en\">\n <head>\n <meta charset=\"UTF-8\" />\n <meta\n name=\"viewport\"\n content=\"width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no\"\n />\n <title>${escaped}</title>\n <link rel=\"stylesheet\" href=\"./${STYLES_FILE}\" />\n <style>\n body {\n margin: 0;\n }\n </style>\n </head>\n <body>\n <div id=\"root\"></div>\n <script type=\"importmap\">${JSON.stringify({ imports })}</script>\n <script type=\"module\" src=\"./dot.js\"></script>\n </body>\n</html>\n`;\n}\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@monimesl/dot-compiler",
|
|
3
|
-
"version": "0.1.0-alpha.
|
|
3
|
+
"version": "0.1.0-alpha.9",
|
|
4
4
|
"description": "Compiles Dot mini apps into a webview bundle and a service bundle",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"repository": {
|
|
@@ -26,9 +26,9 @@
|
|
|
26
26
|
"dependencies": {
|
|
27
27
|
"fflate": "^0.8.3",
|
|
28
28
|
"rolldown": "^1.2.9",
|
|
29
|
-
"@monimesl/dot-
|
|
30
|
-
"@monimesl/dot-
|
|
31
|
-
"@monimesl/dot-dml-parser": "0.1.0-alpha.
|
|
29
|
+
"@monimesl/dot-core": "0.1.0-alpha.9",
|
|
30
|
+
"@monimesl/dot-script-parser": "0.1.0-alpha.9",
|
|
31
|
+
"@monimesl/dot-dml-parser": "0.1.0-alpha.9"
|
|
32
32
|
},
|
|
33
33
|
"devDependencies": {
|
|
34
34
|
"@types/node": "^22.20.4",
|