@monimesl/dot-compiler 0.1.0-alpha.2
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/README.md +1 -0
- package/dist/index.d.ts +104 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +928 -0
- package/dist/index.js.map +1 -0
- package/package.json +50 -0
package/README.md
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# @monimesl/dot-compiler
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import "@monimesl/dot-script-parser";
|
|
2
|
+
import { ComponentManifest, ComponentRegistry, DotPermission, SourceError } from "@monimesl/dot-core";
|
|
3
|
+
import "@monimesl/dot-dml-parser";
|
|
4
|
+
//#region src/project/categories.d.ts
|
|
5
|
+
declare const CATEGORIES: readonly ["agriculture", "asset_finance", "donations", "education", "electricity", "entertainment", "events", "finance", "food_and_drink", "gambling", "games", "government", "health", "insurance", "internet_service_provider", "investments", "jobs", "lifestyle", "loans", "micro_credit", "microfinance", "news", "other", "productivity", "real_estate", "religion", "remittances", "savings", "shopping", "social", "sports", "telecom", "television", "transport", "travel", "utilities", "water"];
|
|
6
|
+
type DotCategory = (typeof CATEGORIES)[number];
|
|
7
|
+
//#endregion
|
|
8
|
+
//#region src/project/dot-json.d.ts
|
|
9
|
+
interface DotJson {
|
|
10
|
+
readonly id: string;
|
|
11
|
+
readonly name: string;
|
|
12
|
+
readonly version: string;
|
|
13
|
+
readonly description?: string | undefined;
|
|
14
|
+
readonly categories?: readonly DotCategory[] | undefined;
|
|
15
|
+
readonly icon?: string | undefined;
|
|
16
|
+
readonly permissions?: Readonly<Partial<Record<DotPermission, string>>> | undefined;
|
|
17
|
+
readonly dash?: DashRequirements | undefined;
|
|
18
|
+
}
|
|
19
|
+
interface DashRequirements {
|
|
20
|
+
readonly minVersion?: string | undefined;
|
|
21
|
+
}
|
|
22
|
+
//#endregion
|
|
23
|
+
//#region src/project/project.d.ts
|
|
24
|
+
interface PageSource {
|
|
25
|
+
readonly path: string;
|
|
26
|
+
readonly id: string;
|
|
27
|
+
readonly view: string;
|
|
28
|
+
readonly state: string;
|
|
29
|
+
readonly controller: string;
|
|
30
|
+
}
|
|
31
|
+
interface Project {
|
|
32
|
+
readonly root: string;
|
|
33
|
+
readonly dotJson: DotJson;
|
|
34
|
+
readonly dot: string;
|
|
35
|
+
readonly pages: readonly PageSource[];
|
|
36
|
+
readonly statics: readonly string[];
|
|
37
|
+
}
|
|
38
|
+
export declare function loadProject(root: string): Promise<Project>;
|
|
39
|
+
//#endregion
|
|
40
|
+
//#region src/errors.d.ts
|
|
41
|
+
export declare class CompileError extends SourceError {
|
|
42
|
+
readonly name = "CompileError";
|
|
43
|
+
}
|
|
44
|
+
/** A problem that does not stop compilation. */
|
|
45
|
+
export declare class CompileWarning extends SourceError {
|
|
46
|
+
readonly name = "CompileWarning";
|
|
47
|
+
}
|
|
48
|
+
//#endregion
|
|
49
|
+
//#region src/build/build.d.ts
|
|
50
|
+
interface BuildOptions {
|
|
51
|
+
root: string;
|
|
52
|
+
outDir?: string;
|
|
53
|
+
mode?: 'production' | 'development';
|
|
54
|
+
components?: readonly ComponentManifest[];
|
|
55
|
+
conditions?: readonly string[];
|
|
56
|
+
}
|
|
57
|
+
interface BuildResult {
|
|
58
|
+
readonly outDir: string;
|
|
59
|
+
readonly files: readonly string[];
|
|
60
|
+
readonly warnings: readonly CompileWarning[];
|
|
61
|
+
}
|
|
62
|
+
/** Compiles and bundles the dot into `outDir` for hosts, and refreshes the types in `.dot/`. */
|
|
63
|
+
export declare function build(options: BuildOptions): Promise<BuildResult>;
|
|
64
|
+
/** What a host needs to run the build: dot.json without `$schema`, where it starts and its pages. */
|
|
65
|
+
interface DotManifest extends Omit<DotJson, 'icon'> {
|
|
66
|
+
readonly runtime: string;
|
|
67
|
+
readonly react: string;
|
|
68
|
+
readonly entry: string;
|
|
69
|
+
readonly pages: readonly string[];
|
|
70
|
+
readonly icon: string | undefined;
|
|
71
|
+
}
|
|
72
|
+
//#endregion
|
|
73
|
+
//#region src/build/packages.d.ts
|
|
74
|
+
interface InstalledRuntime {
|
|
75
|
+
readonly version: string;
|
|
76
|
+
readonly react: ReactHost;
|
|
77
|
+
}
|
|
78
|
+
interface ReactHost {
|
|
79
|
+
readonly version: string;
|
|
80
|
+
readonly dir: string;
|
|
81
|
+
}
|
|
82
|
+
export declare function installedRuntime(root: string): InstalledRuntime;
|
|
83
|
+
//#endregion
|
|
84
|
+
//#region src/view/compiler.d.ts
|
|
85
|
+
interface ViewContext {
|
|
86
|
+
file: string;
|
|
87
|
+
components?: ComponentRegistry;
|
|
88
|
+
handlers?: readonly string[];
|
|
89
|
+
stateKeys?: readonly string[];
|
|
90
|
+
statics?: readonly string[];
|
|
91
|
+
onWarning?: (warning: CompileWarning) => void;
|
|
92
|
+
}
|
|
93
|
+
//#endregion
|
|
94
|
+
//#region src/view/compile.d.ts
|
|
95
|
+
/**
|
|
96
|
+
* Compiles `.dml` markup into a renderer-agnostic ES module:
|
|
97
|
+
* `export default function render(d, { h, on, text, each }) { return [...] }`.
|
|
98
|
+
*
|
|
99
|
+
* The 'h' will be a function from the renderer to create the virtual element for a component.
|
|
100
|
+
*/
|
|
101
|
+
export declare function compileView(source: string, context: ViewContext): string;
|
|
102
|
+
//#endregion
|
|
103
|
+
export type { BuildOptions, BuildResult, DotManifest, InstalledRuntime, PageSource, Project, ReactHost, ViewContext };
|
|
104
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +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"],"mappings":";;;;cACa;KAwCD,sBAAsB;;;UCNjB;WACN;WACA;WACA;WACA;WAEA,sBAAsB;WAEtB;WAEA,cAAc,SAAS,QAAQ,OAAO;WACtC,OAAO;;UAID;WAEN;;;;UCjCM;WAEN;WAEA;WACA;WACA;WACA;;UAGM;WACN;WAEA,SAAS;WAET;WAEA,gBAAgB;WAEhB;;wBAGW,YAAY,eAAe,QAAQ;;;qBCvC5C,qBAAqB;WACd;;;qBAIP,uBAAuB;WAChB;;;;UCqBH;EACf;EAEA;EAEA;EACA,sBAAsB;EACtB;;UAGe;WACN;WACA;WACA,mBAAmB;;;wBAIR,MAAM,SAAS,eAAe,QAAQ;;UAoH3C,oBAAoB,KAAK;WAE/B;WAEA;WACA;WACA;WAEA;;;;UChKM;WACN;WACA,OAAO;;UAID;WACN;WAEA;;wBAQK,iBAAiB,eAAe;;;UCZ/B;EACf;EACA,aAAa;EAEb;EAEA;EAEA;EAEA,aAAa,SAAS;;;;;;;;;;wBCfR,YAAY,gBAAgB,SAAS"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,928 @@
|
|
|
1
|
+
import { createRequire } from "node:module";
|
|
2
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
3
|
+
import { dirname, extname, join, relative, resolve, sep } from "node:path";
|
|
4
|
+
import { copyFile, mkdir, readFile, readdir, rm, stat, writeFile } from "node:fs/promises";
|
|
5
|
+
import { ScriptParseError, parseDotModule, parseExpression, parseLogicModule, parseStateModule, unboundReferences } from "@monimesl/dot-script-parser";
|
|
6
|
+
import { PERMISSIONS, REACT_HOST_MODULES, SourceError, builtinComponents, builtinRegistry, componentRegistry, eventName, isPermission, isSemanticVersion, reactHostUrl } from "@monimesl/dot-core";
|
|
7
|
+
import { rolldown } from "rolldown";
|
|
8
|
+
import { parseDmlToIR } from "@monimesl/dot-dml-parser";
|
|
9
|
+
import { parseEnv } from "node:util";
|
|
10
|
+
//#region src/errors.ts
|
|
11
|
+
var CompileError = class extends SourceError {
|
|
12
|
+
name = "CompileError";
|
|
13
|
+
};
|
|
14
|
+
/** A problem that does not stop compilation. */
|
|
15
|
+
var CompileWarning = class extends SourceError {
|
|
16
|
+
name = "CompileWarning";
|
|
17
|
+
};
|
|
18
|
+
//#endregion
|
|
19
|
+
//#region src/project/categories.ts
|
|
20
|
+
const CATEGORIES = [
|
|
21
|
+
"agriculture",
|
|
22
|
+
"asset_finance",
|
|
23
|
+
"donations",
|
|
24
|
+
"education",
|
|
25
|
+
"electricity",
|
|
26
|
+
"entertainment",
|
|
27
|
+
"events",
|
|
28
|
+
"finance",
|
|
29
|
+
"food_and_drink",
|
|
30
|
+
"gambling",
|
|
31
|
+
"games",
|
|
32
|
+
"government",
|
|
33
|
+
"health",
|
|
34
|
+
"insurance",
|
|
35
|
+
"internet_service_provider",
|
|
36
|
+
"investments",
|
|
37
|
+
"jobs",
|
|
38
|
+
"lifestyle",
|
|
39
|
+
"loans",
|
|
40
|
+
"micro_credit",
|
|
41
|
+
"microfinance",
|
|
42
|
+
"news",
|
|
43
|
+
"other",
|
|
44
|
+
"productivity",
|
|
45
|
+
"real_estate",
|
|
46
|
+
"religion",
|
|
47
|
+
"remittances",
|
|
48
|
+
"savings",
|
|
49
|
+
"shopping",
|
|
50
|
+
"social",
|
|
51
|
+
"sports",
|
|
52
|
+
"telecom",
|
|
53
|
+
"television",
|
|
54
|
+
"transport",
|
|
55
|
+
"travel",
|
|
56
|
+
"utilities",
|
|
57
|
+
"water"
|
|
58
|
+
];
|
|
59
|
+
function isCategory(value) {
|
|
60
|
+
return CATEGORIES.includes(value);
|
|
61
|
+
}
|
|
62
|
+
//#endregion
|
|
63
|
+
//#region src/project/dot-json.ts
|
|
64
|
+
const DOT_JSON_FILE = "dot.json";
|
|
65
|
+
const DOT_JSON_FIELDS = [
|
|
66
|
+
"$schema",
|
|
67
|
+
"id",
|
|
68
|
+
"name",
|
|
69
|
+
"version",
|
|
70
|
+
"description",
|
|
71
|
+
"categories",
|
|
72
|
+
"icon",
|
|
73
|
+
"permissions",
|
|
74
|
+
"dash"
|
|
75
|
+
];
|
|
76
|
+
const REQUIRED_DOT_JSON_FIELDS = [
|
|
77
|
+
"id",
|
|
78
|
+
"name",
|
|
79
|
+
"version"
|
|
80
|
+
];
|
|
81
|
+
const DASH_FIELDS = ["minVersion"];
|
|
82
|
+
const ID_WORD = "[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?";
|
|
83
|
+
const DOT_ID_PATTERN = new RegExp(`^(?=[a-z])${ID_WORD}(?:\\.${ID_WORD})+$`);
|
|
84
|
+
async function readDotJson(file) {
|
|
85
|
+
let dotJson;
|
|
86
|
+
try {
|
|
87
|
+
dotJson = JSON.parse(await readFile(file, "utf8"));
|
|
88
|
+
} catch (error) {
|
|
89
|
+
throw new CompileError(`Invalid JSON: ${error.message}`, file);
|
|
90
|
+
}
|
|
91
|
+
if (!isObject(dotJson)) throw new CompileError("Expected an object, such as { \"id\": \"com.example.shop\", \"name\": \"Shop\", \"version\": \"1.0.0\" }", file);
|
|
92
|
+
if ("pages" in dotJson) throw new CompileError("Pages are listed in dot.ts: defineDot({ pages: [...] })", file);
|
|
93
|
+
rejectUnknownFields(dotJson, DOT_JSON_FIELDS, file);
|
|
94
|
+
for (const field of REQUIRED_DOT_JSON_FIELDS) if (dotJson[field] === void 0) throw new CompileError(`"${field}" is required`, file);
|
|
95
|
+
const { $schema, id, name, version, description, categories, icon, permissions, dash } = dotJson;
|
|
96
|
+
if ($schema !== void 0 && typeof $schema !== "string") throw new CompileError("\"$schema\" must be a string", file);
|
|
97
|
+
if (typeof id !== "string" || !DOT_ID_PATTERN.test(id)) throw new CompileError("\"id\" must be a reverse domain name, such as \"com.example.counter\": lowercase words of letters, digits and hyphens, joined by dots. A word can't start or end with a hyphen, or be longer than 63 characters.", file);
|
|
98
|
+
if (id.length > 128) throw new CompileError(`"id" must be at most 128 characters`, file);
|
|
99
|
+
if (!isNonEmptyString(name)) throw new CompileError("\"name\" must be a non-empty string", file);
|
|
100
|
+
if (!isSemanticVersion(version)) throw new CompileError("\"version\" must be a semantic version, such as \"1.0.0\"", file);
|
|
101
|
+
if (description !== void 0 && !isNonEmptyString(description)) throw new CompileError("\"description\" must be a non-empty string", file);
|
|
102
|
+
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);
|
|
103
|
+
return {
|
|
104
|
+
id,
|
|
105
|
+
name,
|
|
106
|
+
version,
|
|
107
|
+
description,
|
|
108
|
+
categories: readCategories(categories, file),
|
|
109
|
+
icon,
|
|
110
|
+
permissions: readPermissions(permissions, file),
|
|
111
|
+
dash: readDashRequirements(dash, file)
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
function readCategories(categories, file) {
|
|
115
|
+
if (categories === void 0) return;
|
|
116
|
+
if (!Array.isArray(categories) || categories.length === 0) throw new CompileError("\"categories\" must list one or more categories, such as [\"finance\", \"savings\"]", file);
|
|
117
|
+
const listed = [];
|
|
118
|
+
for (const category of categories) {
|
|
119
|
+
if (!isCategory(category)) throw new CompileError(`"${String(category)}" is not a category. The categories are ${quotedList(CATEGORIES)}`, file);
|
|
120
|
+
if (listed.includes(category)) throw new CompileError(`"categories" lists "${category}" more than once`, file);
|
|
121
|
+
listed.push(category);
|
|
122
|
+
}
|
|
123
|
+
return listed;
|
|
124
|
+
}
|
|
125
|
+
function readPermissions(permissions, file) {
|
|
126
|
+
if (permissions === void 0) return;
|
|
127
|
+
if (!isObject(permissions)) throw new CompileError("\"permissions\" must be an object, such as { \"account\": \"Show your name on receipts\" }", file);
|
|
128
|
+
for (const [permission, reason] of Object.entries(permissions)) {
|
|
129
|
+
if (!isPermission(permission)) throw new CompileError(`Unknown permission "${permission}". The permissions are ${quotedList(PERMISSIONS)}`, file);
|
|
130
|
+
if (!isNonEmptyString(reason)) throw new CompileError(`"permissions.${permission}" must say why the dot needs it, such as "Show your name on receipts"`, file);
|
|
131
|
+
}
|
|
132
|
+
return permissions;
|
|
133
|
+
}
|
|
134
|
+
function readDashRequirements(dash, file) {
|
|
135
|
+
if (dash === void 0) return;
|
|
136
|
+
if (!isObject(dash)) throw new CompileError("\"dash\" must be an object, such as { \"minVersion\": \"2.4.0\" }", file);
|
|
137
|
+
rejectUnknownFields(dash, DASH_FIELDS, file, "dash");
|
|
138
|
+
const { minVersion } = dash;
|
|
139
|
+
if (minVersion !== void 0 && !isSemanticVersion(minVersion)) throw new CompileError("\"dash.minVersion\" must be a semantic version, such as \"2.4.0\"", file);
|
|
140
|
+
return { minVersion };
|
|
141
|
+
}
|
|
142
|
+
function rejectUnknownFields(object, allowed, file, parent) {
|
|
143
|
+
for (const field of Object.keys(object)) {
|
|
144
|
+
if (allowed.includes(field)) continue;
|
|
145
|
+
if (parent === void 0) throw new CompileError(`Unknown field "${field}". The fields allowed are ${quotedList(allowed)}`, file);
|
|
146
|
+
throw new CompileError(`Unknown field "${parent}.${field}". The fields allowed in "${parent}" are ${quotedList(allowed)}`, file);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
function quotedList(values) {
|
|
150
|
+
return values.map((value) => `"${value}"`).join(", ");
|
|
151
|
+
}
|
|
152
|
+
function isObject(value) {
|
|
153
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
154
|
+
}
|
|
155
|
+
function isNonEmptyString(value) {
|
|
156
|
+
return typeof value === "string" && value !== "";
|
|
157
|
+
}
|
|
158
|
+
//#endregion
|
|
159
|
+
//#region src/project/statics.ts
|
|
160
|
+
const STATICS_DIR = "statics";
|
|
161
|
+
const MAX_STATIC_ASSET_BYTES = 1048576;
|
|
162
|
+
const PNG_SIGNATURE = Buffer.from([
|
|
163
|
+
137,
|
|
164
|
+
80,
|
|
165
|
+
78,
|
|
166
|
+
71,
|
|
167
|
+
13,
|
|
168
|
+
10,
|
|
169
|
+
26,
|
|
170
|
+
10
|
|
171
|
+
]);
|
|
172
|
+
const IMAGE_FORMATS = {
|
|
173
|
+
".png": (header) => header.subarray(0, 8).equals(PNG_SIGNATURE),
|
|
174
|
+
".jpg": isJpeg,
|
|
175
|
+
".jpeg": isJpeg,
|
|
176
|
+
".gif": (header) => ["GIF87a", "GIF89a"].includes(header.toString("latin1", 0, 6)),
|
|
177
|
+
".webp": (header) => header.toString("latin1", 0, 4) === "RIFF" && header.toString("latin1", 8, 12) === "WEBP"
|
|
178
|
+
};
|
|
179
|
+
const STATIC_ASSET_EXTENSIONS = Object.keys(IMAGE_FORMATS);
|
|
180
|
+
async function listStatics(root) {
|
|
181
|
+
const staticsDir = join(root, STATICS_DIR);
|
|
182
|
+
if (!existsSync(staticsDir)) return [];
|
|
183
|
+
const statics = [];
|
|
184
|
+
for (const entry of await readdir(staticsDir, {
|
|
185
|
+
recursive: true,
|
|
186
|
+
withFileTypes: true
|
|
187
|
+
})) {
|
|
188
|
+
if (!entry.isFile() || entry.name.startsWith(".")) continue;
|
|
189
|
+
const file = join(entry.parentPath, entry.name);
|
|
190
|
+
await checkImage(file, extname(entry.name).toLowerCase());
|
|
191
|
+
statics.push(`${STATICS_DIR}/${relative(staticsDir, file).split(sep).join("/")}`);
|
|
192
|
+
}
|
|
193
|
+
return statics.sort();
|
|
194
|
+
}
|
|
195
|
+
async function checkImage(file, extension) {
|
|
196
|
+
const isFormat = IMAGE_FORMATS[extension];
|
|
197
|
+
if (!isFormat) throw new CompileError(`Only images belong in ${STATICS_DIR}/: ${STATIC_ASSET_EXTENSIONS.join(", ")}`, file);
|
|
198
|
+
const { size } = await stat(file);
|
|
199
|
+
if (size > 1048576) throw new CompileError(`The image is ${(size / MAX_STATIC_ASSET_BYTES).toFixed(1)} MB; each one must be 1 MB or smaller`, file);
|
|
200
|
+
if (!isFormat(await readFile(file))) throw new CompileError(`The file is not the ${extension.slice(1)} image its name says`, file);
|
|
201
|
+
}
|
|
202
|
+
function isJpeg(header) {
|
|
203
|
+
return header[0] === 255 && header[1] === 216 && header[2] === 255;
|
|
204
|
+
}
|
|
205
|
+
//#endregion
|
|
206
|
+
//#region src/project/project.ts
|
|
207
|
+
const DOT_FILE = "dot.ts";
|
|
208
|
+
const PAGES_DIR = "pages";
|
|
209
|
+
const PAGE_FILES = {
|
|
210
|
+
view: "view.dml",
|
|
211
|
+
state: "state.ts",
|
|
212
|
+
controller: "controller.ts"
|
|
213
|
+
};
|
|
214
|
+
async function loadProject(root) {
|
|
215
|
+
root = resolve(root);
|
|
216
|
+
const dotJsonFile = join(root, DOT_JSON_FILE);
|
|
217
|
+
if (!existsSync(dotJsonFile)) throw new CompileError(`Missing ${DOT_JSON_FILE} file in project`, root);
|
|
218
|
+
const dotJson = await readDotJson(dotJsonFile);
|
|
219
|
+
if (dotJson.icon !== void 0 && !existsSync(join(root, dotJson.icon))) throw new CompileError(`The icon "${dotJson.icon}" is missing`, dotJsonFile);
|
|
220
|
+
const dotFile = join(root, DOT_FILE);
|
|
221
|
+
if (!existsSync(dotFile)) throw new CompileError(`Missing ${DOT_FILE}: export default defineDot({ pages: [...] })`, root);
|
|
222
|
+
const { pages } = parseDotModule(await readFile(dotFile, "utf8"), dotFile);
|
|
223
|
+
const sources = await Promise.all(pages.map((path) => loadPage(root, path, dotFile)));
|
|
224
|
+
const seen = /* @__PURE__ */ new Map();
|
|
225
|
+
for (const page of sources) {
|
|
226
|
+
const other = seen.get(page.id.toLowerCase());
|
|
227
|
+
if (other) {
|
|
228
|
+
let file = dotFile;
|
|
229
|
+
if (page.id !== page.path) file = page.controller;
|
|
230
|
+
else if (other.id !== other.path) file = other.controller;
|
|
231
|
+
throw new CompileError(`Page id "${page.id}" (${page.path}) is already used by ${other.path}`, file);
|
|
232
|
+
}
|
|
233
|
+
seen.set(page.id.toLowerCase(), page);
|
|
234
|
+
}
|
|
235
|
+
return {
|
|
236
|
+
root,
|
|
237
|
+
dotJson,
|
|
238
|
+
dot: dotFile,
|
|
239
|
+
pages: sources,
|
|
240
|
+
statics: await listStatics(root)
|
|
241
|
+
};
|
|
242
|
+
}
|
|
243
|
+
async function loadPage(root, path, dotFile) {
|
|
244
|
+
const dir = join(root, PAGES_DIR, path);
|
|
245
|
+
const files = {
|
|
246
|
+
view: join(dir, PAGE_FILES.view),
|
|
247
|
+
state: join(dir, PAGE_FILES.state),
|
|
248
|
+
controller: join(dir, PAGE_FILES.controller)
|
|
249
|
+
};
|
|
250
|
+
const missing = Object.values(files).filter((file) => !existsSync(file));
|
|
251
|
+
if (missing.length > 0) throw new CompileError(`Page "${path}" is missing ${missing.join(", ")}`, dotFile);
|
|
252
|
+
const { id } = parseLogicModule(await readFile(files.controller, "utf8"), files.controller);
|
|
253
|
+
return {
|
|
254
|
+
path,
|
|
255
|
+
id: id ?? path,
|
|
256
|
+
...files
|
|
257
|
+
};
|
|
258
|
+
}
|
|
259
|
+
//#endregion
|
|
260
|
+
//#region src/entries.ts
|
|
261
|
+
const RUNTIME_PACKAGE = "@monimesl/dot-runtime";
|
|
262
|
+
const RUNTIME = {
|
|
263
|
+
ui: `${RUNTIME_PACKAGE}/ui`,
|
|
264
|
+
logic: `${RUNTIME_PACKAGE}/logic`,
|
|
265
|
+
renderer: `${RUNTIME_PACKAGE}/react`
|
|
266
|
+
};
|
|
267
|
+
const ENTRY = {
|
|
268
|
+
ui: "dot:ui",
|
|
269
|
+
logic: "dot:logic"
|
|
270
|
+
};
|
|
271
|
+
const ENV_MODULE = "dot:env";
|
|
272
|
+
const viewModuleId = (id) => `dot:view/${id}`;
|
|
273
|
+
function literal(value) {
|
|
274
|
+
return JSON.stringify(value);
|
|
275
|
+
}
|
|
276
|
+
function pageTable(project, binding) {
|
|
277
|
+
return `{${project.pages.map((page, index) => `\n ${literal(page.id)}: ${binding}${index},`).join("")}\n }`;
|
|
278
|
+
}
|
|
279
|
+
function uiEntry(project) {
|
|
280
|
+
return [
|
|
281
|
+
`import { hostTransport, startWebview } from ${literal(RUNTIME.ui)};`,
|
|
282
|
+
`import createRenderer from ${literal(RUNTIME.renderer)};`,
|
|
283
|
+
...project.pages.map((page, index) => `import view${index} from ${literal(viewModuleId(page.id))};`),
|
|
284
|
+
"",
|
|
285
|
+
"startWebview({",
|
|
286
|
+
" container: document.getElementById(\"root\"),",
|
|
287
|
+
" transport: hostTransport(),",
|
|
288
|
+
" renderer: createRenderer(),",
|
|
289
|
+
` views: ${pageTable(project, "view")},`,
|
|
290
|
+
"});",
|
|
291
|
+
""
|
|
292
|
+
].join("\n");
|
|
293
|
+
}
|
|
294
|
+
function logicEntry(project, { strict }) {
|
|
295
|
+
const options = [
|
|
296
|
+
" transport: hostTransport(),",
|
|
297
|
+
" dot,",
|
|
298
|
+
` pages: ${pageTable(project, "page")},`
|
|
299
|
+
];
|
|
300
|
+
if (strict) options.push(" strict: true,");
|
|
301
|
+
return [
|
|
302
|
+
`import { createService, hostTransport } from ${literal(RUNTIME.logic)};`,
|
|
303
|
+
`import dot from ${literal(project.dot)};`,
|
|
304
|
+
...project.pages.map((page, index) => `import page${index} from ${literal(page.controller)};`),
|
|
305
|
+
"",
|
|
306
|
+
"createService({",
|
|
307
|
+
...options,
|
|
308
|
+
"});",
|
|
309
|
+
""
|
|
310
|
+
].join("\n");
|
|
311
|
+
}
|
|
312
|
+
function envModule(env) {
|
|
313
|
+
return `export const env = Object.freeze(${JSON.stringify(env, null, 2)});\n`;
|
|
314
|
+
}
|
|
315
|
+
//#endregion
|
|
316
|
+
//#region src/globals.ts
|
|
317
|
+
const DOT_GLOBALS = {
|
|
318
|
+
"@monimesl/dot-runtime": [
|
|
319
|
+
"definePage",
|
|
320
|
+
"defineDot",
|
|
321
|
+
"createEvent",
|
|
322
|
+
"dash"
|
|
323
|
+
],
|
|
324
|
+
[ENV_MODULE]: ["env"]
|
|
325
|
+
};
|
|
326
|
+
const SCRIPT = /\.[cm]?[jt]sx?$/;
|
|
327
|
+
function dotGlobals(root) {
|
|
328
|
+
const isDotModule = (id) => id.startsWith(root + sep) && !id.includes(`${sep}node_modules${sep}`) && SCRIPT.test(id);
|
|
329
|
+
return {
|
|
330
|
+
name: "dot:globals",
|
|
331
|
+
transform(code, id) {
|
|
332
|
+
if (!isDotModule(id)) return null;
|
|
333
|
+
const imports = [];
|
|
334
|
+
for (const [source, names] of Object.entries(DOT_GLOBALS)) {
|
|
335
|
+
const used = unboundReferences(code, names, id);
|
|
336
|
+
if (used.length > 0) imports.push(`import { ${used.join(", ")} } from ${JSON.stringify(source)};`);
|
|
337
|
+
}
|
|
338
|
+
if (imports.length === 0) return null;
|
|
339
|
+
return {
|
|
340
|
+
code: `${code}\n${imports.join("\n")}\n`,
|
|
341
|
+
map: null
|
|
342
|
+
};
|
|
343
|
+
}
|
|
344
|
+
};
|
|
345
|
+
}
|
|
346
|
+
//#endregion
|
|
347
|
+
//#region src/build/bundle.ts
|
|
348
|
+
/** Bundles one entry with rolldown, replacing `outDir`. Returns the written files. */
|
|
349
|
+
async function bundle(options) {
|
|
350
|
+
await rm(options.outDir, {
|
|
351
|
+
recursive: true,
|
|
352
|
+
force: true
|
|
353
|
+
});
|
|
354
|
+
const bundler = await rolldown({
|
|
355
|
+
input: options.entry,
|
|
356
|
+
cwd: options.root,
|
|
357
|
+
external: [...options.external],
|
|
358
|
+
platform: "browser",
|
|
359
|
+
plugins: [generatedModules(options.root, options.modules), dotGlobals(options.root)],
|
|
360
|
+
resolve: { conditionNames: [
|
|
361
|
+
...options.conditions,
|
|
362
|
+
"browser",
|
|
363
|
+
"import",
|
|
364
|
+
"require",
|
|
365
|
+
"default"
|
|
366
|
+
] },
|
|
367
|
+
transform: { define: { "process.env.NODE_ENV": JSON.stringify(options.mode) } }
|
|
368
|
+
});
|
|
369
|
+
try {
|
|
370
|
+
const { output } = await bundler.write({
|
|
371
|
+
dir: options.outDir,
|
|
372
|
+
format: options.format,
|
|
373
|
+
entryFileNames: options.fileName,
|
|
374
|
+
sourcemap: true,
|
|
375
|
+
minify: options.minify
|
|
376
|
+
});
|
|
377
|
+
return output.map((chunk) => join(options.outDir, chunk.fileName));
|
|
378
|
+
} finally {
|
|
379
|
+
await bundler.close();
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
/** Serves generated modules and resolves their imports as if they lived in the project root. */
|
|
383
|
+
function generatedModules(root, modules) {
|
|
384
|
+
const importer = join(root, DOT_JSON_FILE);
|
|
385
|
+
return {
|
|
386
|
+
name: "dot:generated",
|
|
387
|
+
async resolveId(source, from) {
|
|
388
|
+
if (modules.has(source)) return `\0${source}`;
|
|
389
|
+
if (!from?.startsWith("\0dot:")) return null;
|
|
390
|
+
const resolved = await this.resolve(source, importer, { skipSelf: true });
|
|
391
|
+
if (!resolved) this.error(`Cannot resolve "${source}" from ${root}. Is it installed in the project?`);
|
|
392
|
+
return resolved;
|
|
393
|
+
},
|
|
394
|
+
load(id) {
|
|
395
|
+
return id.startsWith("\0") ? modules.get(id.slice(1)) ?? null : null;
|
|
396
|
+
}
|
|
397
|
+
};
|
|
398
|
+
}
|
|
399
|
+
//#endregion
|
|
400
|
+
//#region src/build/packages.ts
|
|
401
|
+
const RENDERER_PACKAGE = "@monimesl/dot-renderer-react";
|
|
402
|
+
function installedRuntime(root) {
|
|
403
|
+
const runtime = requirePackage(root, RUNTIME_PACKAGE, "the project");
|
|
404
|
+
const renderer = requirePackage(runtime.dir, RENDERER_PACKAGE, RUNTIME_PACKAGE);
|
|
405
|
+
const react = requirePackage(renderer.dir, "react", RENDERER_PACKAGE);
|
|
406
|
+
return {
|
|
407
|
+
version: runtime.version,
|
|
408
|
+
react: {
|
|
409
|
+
version: react.version,
|
|
410
|
+
dir: join(renderer.dir, "dist", "react-host")
|
|
411
|
+
}
|
|
412
|
+
};
|
|
413
|
+
}
|
|
414
|
+
function requirePackage(fromDir, name, installedIn) {
|
|
415
|
+
const require = createRequire(join(fromDir, "package.json"));
|
|
416
|
+
let packageFile;
|
|
417
|
+
try {
|
|
418
|
+
packageFile = require.resolve(`${name}/package.json`);
|
|
419
|
+
} catch {
|
|
420
|
+
throw new CompileError(`${name} is not installed in ${installedIn}`, fromDir);
|
|
421
|
+
}
|
|
422
|
+
const { version } = JSON.parse(readFileSync(packageFile, "utf8"));
|
|
423
|
+
return {
|
|
424
|
+
dir: dirname(packageFile),
|
|
425
|
+
version
|
|
426
|
+
};
|
|
427
|
+
}
|
|
428
|
+
//#endregion
|
|
429
|
+
//#region src/view/expression.ts
|
|
430
|
+
const IDENTIFIER = /^[A-Za-z_$][\w$]*$/;
|
|
431
|
+
const EMPTY_SCOPE = /* @__PURE__ */ new Map();
|
|
432
|
+
function isIdentifier(name) {
|
|
433
|
+
return IDENTIFIER.test(name);
|
|
434
|
+
}
|
|
435
|
+
/**
|
|
436
|
+
* Prints a binding as JS. Loop variables read their local; everything else reads page data from
|
|
437
|
+
* `d`. Member access never throws on nullish values.
|
|
438
|
+
*/
|
|
439
|
+
function printExpr(expr, scope) {
|
|
440
|
+
const print = (node) => printExpr(node, scope);
|
|
441
|
+
switch (expr.type) {
|
|
442
|
+
case "Literal": return JSON.stringify(expr.value);
|
|
443
|
+
case "Identifier": return scope.get(expr.name) ?? `d.${expr.name}`;
|
|
444
|
+
case "Member": return expr.property.type === "Literal" && typeof expr.property.value === "string" ? `${print(expr.object)}?.${expr.property.value}` : `${print(expr.object)}?.[${print(expr.property)}]`;
|
|
445
|
+
case "Unary": return `(${expr.operator}${expr.operator === "typeof" ? " " : ""}${print(expr.argument)})`;
|
|
446
|
+
case "Binary": return `(${print(expr.left)} ${expr.operator} ${print(expr.right)})`;
|
|
447
|
+
case "Conditional": return `(${print(expr.test)} ? ${print(expr.consequent)} : ${print(expr.alternate)})`;
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
function stateReads(expr, scope) {
|
|
451
|
+
const reads = (node) => stateReads(node, scope);
|
|
452
|
+
switch (expr.type) {
|
|
453
|
+
case "Literal": return [];
|
|
454
|
+
case "Identifier": return scope.has(expr.name) ? [] : [expr.name];
|
|
455
|
+
case "Member": return [...reads(expr.object), ...reads(expr.property)];
|
|
456
|
+
case "Unary": return reads(expr.argument);
|
|
457
|
+
case "Binary": return [...reads(expr.left), ...reads(expr.right)];
|
|
458
|
+
case "Conditional": return [
|
|
459
|
+
...reads(expr.test),
|
|
460
|
+
...reads(expr.consequent),
|
|
461
|
+
...reads(expr.alternate)
|
|
462
|
+
];
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
//#endregion
|
|
466
|
+
//#region src/view/control-flow.ts
|
|
467
|
+
const CONTROL_FLOW = /* @__PURE__ */ new Set([
|
|
468
|
+
"If",
|
|
469
|
+
"ElseIf",
|
|
470
|
+
"Else",
|
|
471
|
+
"For"
|
|
472
|
+
]);
|
|
473
|
+
/**
|
|
474
|
+
* `<If condition>`, then any `<ElseIf condition>`, then an optional `<Else>`, as consecutive
|
|
475
|
+
* siblings compile to a spread conditional: `...(a ? [...]: b ? [...]: [...])`.
|
|
476
|
+
*/
|
|
477
|
+
function compileConditional(compiler, branches, parent, scope) {
|
|
478
|
+
let code = "[]";
|
|
479
|
+
for (const branch of [...branches].reverse()) {
|
|
480
|
+
const body = `[${compiler.compile(branch.children, parent, scope).join(", ")}]`;
|
|
481
|
+
if (branch.tag === "Else") {
|
|
482
|
+
compiler.checkControlFlowElementAttributes(branch, []);
|
|
483
|
+
code = body;
|
|
484
|
+
} else {
|
|
485
|
+
compiler.checkControlFlowElementAttributes(branch, ["condition"], ["condition"]);
|
|
486
|
+
code = `${compiler.compileSingleBinding(branch, "condition", scope)} ? ${body} : ${code}`;
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
return `...(${code})`;
|
|
490
|
+
}
|
|
491
|
+
/**
|
|
492
|
+
* `<For each="{{ list }}" item="item" index="index" key="id">`. `item` and `index` default to those
|
|
493
|
+
* names. `key` is a property of each item, `*this` for the item itself, or `*index` to match items
|
|
494
|
+
* by position; it is optional, but a warning is logged when missing. Compiles to
|
|
495
|
+
* `each(list, (item, index) => [...], key?)`.
|
|
496
|
+
*/
|
|
497
|
+
function compileLoop(compiler, node, parent, scope) {
|
|
498
|
+
compiler.checkControlFlowElementAttributes(node, [
|
|
499
|
+
"each",
|
|
500
|
+
"item",
|
|
501
|
+
"index",
|
|
502
|
+
"key"
|
|
503
|
+
], ["each"]);
|
|
504
|
+
const list = compiler.compileSingleBinding(node, "each", scope);
|
|
505
|
+
const { item = "item", index = "index", key } = node.attributes;
|
|
506
|
+
const checkVariableName = (attribute, name) => {
|
|
507
|
+
if (!isIdentifier(name)) compiler.fail(`<For> \`${attribute}\` must be a variable name, got "${name}"`, node.loc);
|
|
508
|
+
};
|
|
509
|
+
checkVariableName("item", item);
|
|
510
|
+
checkVariableName("index", index);
|
|
511
|
+
if (item === index) compiler.fail("<For> `item` and `index` must differ", node.loc);
|
|
512
|
+
const itemVar = "$" + item;
|
|
513
|
+
const indexVar = "$" + index;
|
|
514
|
+
const inner = new Map(scope).set(item, itemVar).set(index, indexVar);
|
|
515
|
+
const args = [list, `(${itemVar}, ${indexVar}) => [${compiler.compile(node.children, parent, inner).join(", ")}]`];
|
|
516
|
+
if (key === void 0) compiler.warn("<For> has no `key`, so items are matched by position. Add key=\"<property>\" (or \"*this\") to keep rows stable when items move, or key=\"*index\" to keep matching by position.", node.loc);
|
|
517
|
+
else if (key === "*index") {} else if (key === "*this") args.push(`(${itemVar}) => ${itemVar}`);
|
|
518
|
+
else if (isIdentifier(key)) args.push(`(${itemVar}) => ${itemVar}?.${key}`);
|
|
519
|
+
else compiler.fail(`<For> \`key\` must be a property name, *this or *index, got "${key}"`, node.loc);
|
|
520
|
+
return `each(${args.join(", ")})`;
|
|
521
|
+
}
|
|
522
|
+
//#endregion
|
|
523
|
+
//#region src/view/compiler.ts
|
|
524
|
+
const SEGMENT_BINDING_REGEXP = /{{([\s\S]*?)}}/g;
|
|
525
|
+
var ViewCompiler = class {
|
|
526
|
+
#context;
|
|
527
|
+
#components;
|
|
528
|
+
constructor(context) {
|
|
529
|
+
this.#context = context;
|
|
530
|
+
this.#components = context.components ?? builtinRegistry;
|
|
531
|
+
for (const name of CONTROL_FLOW) if (name in this.#components) this.fail(`<${name}> is reserved for control flow`);
|
|
532
|
+
}
|
|
533
|
+
compile(nodes, parent, scope) {
|
|
534
|
+
const out = [];
|
|
535
|
+
for (let i = 0; i < nodes.length; i++) {
|
|
536
|
+
const node = nodes[i];
|
|
537
|
+
if (node.type === "Text") {
|
|
538
|
+
out.push(...this.compileText(node.content, parent, scope, node.loc));
|
|
539
|
+
continue;
|
|
540
|
+
}
|
|
541
|
+
switch (node.tag) {
|
|
542
|
+
case "If": {
|
|
543
|
+
const branches = ifChain(node, nodes.slice(i + 1));
|
|
544
|
+
out.push(compileConditional(this, branches, parent, scope));
|
|
545
|
+
i += branches.length - 1;
|
|
546
|
+
break;
|
|
547
|
+
}
|
|
548
|
+
case "ElseIf":
|
|
549
|
+
case "Else": return this.fail(`<${node.tag}> must directly follow <If> or <ElseIf>`, node.loc);
|
|
550
|
+
case "For":
|
|
551
|
+
out.push(compileLoop(this, node, parent, scope));
|
|
552
|
+
break;
|
|
553
|
+
default: out.push(this.compileElement(node, parent, scope));
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
return out;
|
|
557
|
+
}
|
|
558
|
+
compileText(content, parent, scope, loc) {
|
|
559
|
+
if (parent?.children !== "text") {
|
|
560
|
+
const wrappers = Object.values(this.#components).filter((componentManifest) => componentManifest.inline).map((component) => `<${component.name}>`).join(" or ");
|
|
561
|
+
this.fail(`'${content}' must be wrapped in ${wrappers || "an inline component"}`, loc);
|
|
562
|
+
}
|
|
563
|
+
return this.compileAsSegments(content, scope, loc).map(printSegment);
|
|
564
|
+
}
|
|
565
|
+
compileElement(node, parent, scope) {
|
|
566
|
+
const component = this.#components[node.tag];
|
|
567
|
+
if (!component) return this.fail(`Unknown component <${node.tag}>.`, node.loc);
|
|
568
|
+
if (parent && !canHaveChildren(parent, component)) this.fail(`<${node.tag}> cannot be placed inside <${parent.name}>`, node.loc);
|
|
569
|
+
const props = [...Object.entries(node.attributes).map(([name, value]) => this.compileProp(component, name, value, scope, node.loc)), ...Object.entries(node.events).map(([name, handler]) => this.compileEvent(component, name, handler, node.loc))];
|
|
570
|
+
return `h(${[
|
|
571
|
+
JSON.stringify(component.name),
|
|
572
|
+
props.length ? `{ ${props.join(", ")} }` : "null",
|
|
573
|
+
...this.compile(node.children, component, scope)
|
|
574
|
+
].join(", ")})`;
|
|
575
|
+
}
|
|
576
|
+
compileProp(component, name, value, scope, loc) {
|
|
577
|
+
const type = component.props[name];
|
|
578
|
+
if (!type) return this.fail(`<${component.name}> does not support the \`${name}\` attribute`, loc);
|
|
579
|
+
const segments = this.compileAsSegments(value, scope, loc);
|
|
580
|
+
const bindingCode = singleBindingCode(segments);
|
|
581
|
+
if (bindingCode !== null) return `${name}: ${bindingCode}`;
|
|
582
|
+
if (segments.every((segment) => segment.kind === "static")) {
|
|
583
|
+
this.#checkStaticsReference(value, loc);
|
|
584
|
+
return `${name}: ${this.compileLiteral(type, value, loc)}`;
|
|
585
|
+
}
|
|
586
|
+
if (type !== "string" && type !== "any") {
|
|
587
|
+
const kind = typeof type === "string" ? `a ${type}` : "an enum";
|
|
588
|
+
this.fail(`\`${name}\` is ${kind}; bind it with a single {{ }} expression`, loc);
|
|
589
|
+
}
|
|
590
|
+
return `${name}: ${segments.map(printSegment).join(" + ")}`;
|
|
591
|
+
}
|
|
592
|
+
#checkStaticsReference(value, loc) {
|
|
593
|
+
const { statics } = this.#context;
|
|
594
|
+
if (statics && value.startsWith(`statics/`) && !statics.includes(value)) this.fail(`"${value}" is not in ${STATICS_DIR}/`, loc);
|
|
595
|
+
}
|
|
596
|
+
compileEvent(component, attribute, handler, loc) {
|
|
597
|
+
const event = eventName(attribute);
|
|
598
|
+
if (!component.events.includes(event)) this.fail(`<${component.name}> has no defined \`${attribute}\` event handler`, loc);
|
|
599
|
+
if (!isIdentifier(handler)) this.fail(`\`${attribute}\` must name a mutation or action in controller.ts, got "${handler}"`, loc);
|
|
600
|
+
const { handlers } = this.#context;
|
|
601
|
+
if (handlers && !handlers.includes(handler)) this.fail(`\`${handler}\` is not a mutation or action in controller.ts`, loc);
|
|
602
|
+
return `${attribute}: on(${JSON.stringify(handler)})`;
|
|
603
|
+
}
|
|
604
|
+
checkControlFlowElementAttributes(node, allowed, required = []) {
|
|
605
|
+
const [event] = Object.keys(node.events);
|
|
606
|
+
if (event) this.fail(`<${node.tag}> is control flow component and cannot handle events such as \`${event}\`. Put the handler on a component inside it.`, node.loc);
|
|
607
|
+
for (const name of Object.keys(node.attributes)) if (!allowed.includes(name)) this.fail(`<${node.tag}> does not support the \`${name}\` attribute`, node.loc);
|
|
608
|
+
for (const name of required) if (!(name in node.attributes)) this.fail(`<${node.tag}> requires \`${name}\``, node.loc);
|
|
609
|
+
}
|
|
610
|
+
compileSingleBinding(node, attribute, scope) {
|
|
611
|
+
const bindingCode = singleBindingCode(this.compileAsSegments(node.attributes[attribute] ?? "", scope, node.loc));
|
|
612
|
+
if (bindingCode === null) return this.fail(`<${node.tag}> \`${attribute}\` must be a single {{ }} binding`, node.loc);
|
|
613
|
+
return bindingCode;
|
|
614
|
+
}
|
|
615
|
+
compileLiteral(type, value, loc) {
|
|
616
|
+
if (typeof type !== "string") {
|
|
617
|
+
if (type.includes(value)) return JSON.stringify(value);
|
|
618
|
+
return this.fail(`Expected one of ${type.map((option) => `"${option}"`).join(", ")}, got "${value}"`, loc);
|
|
619
|
+
}
|
|
620
|
+
switch (type) {
|
|
621
|
+
case "boolean":
|
|
622
|
+
if (value === "" || value === "true") return "true";
|
|
623
|
+
if (value === "false") return "false";
|
|
624
|
+
return this.fail(`Expected a boolean, got "${value}"`, loc);
|
|
625
|
+
case "number":
|
|
626
|
+
if (value.trim() !== "" && Number.isFinite(Number(value))) return String(Number(value));
|
|
627
|
+
return this.fail(`Expected a number, got "${value}"`, loc);
|
|
628
|
+
case "string":
|
|
629
|
+
case "any": return JSON.stringify(value);
|
|
630
|
+
}
|
|
631
|
+
}
|
|
632
|
+
compileAsSegments(input, scope, loc) {
|
|
633
|
+
const segments = [];
|
|
634
|
+
let last = 0;
|
|
635
|
+
for (const match of input.matchAll(SEGMENT_BINDING_REGEXP)) {
|
|
636
|
+
if (match.index > last) segments.push({
|
|
637
|
+
kind: "static",
|
|
638
|
+
value: input.slice(last, match.index)
|
|
639
|
+
});
|
|
640
|
+
const expr = this.parseBindingExpression(match[1], scope, loc);
|
|
641
|
+
segments.push({
|
|
642
|
+
kind: "binding",
|
|
643
|
+
code: printExpr(expr, scope)
|
|
644
|
+
});
|
|
645
|
+
last = match.index + match[0].length;
|
|
646
|
+
}
|
|
647
|
+
const rest = input.slice(last);
|
|
648
|
+
if (rest.includes("{{")) this.fail("Unclosed binding: missing `}}`", loc);
|
|
649
|
+
if (rest) segments.push({
|
|
650
|
+
kind: "static",
|
|
651
|
+
value: rest
|
|
652
|
+
});
|
|
653
|
+
return segments;
|
|
654
|
+
}
|
|
655
|
+
parseBindingExpression(source, scope, loc) {
|
|
656
|
+
let expr;
|
|
657
|
+
try {
|
|
658
|
+
expr = parseExpression(source);
|
|
659
|
+
} catch (error) {
|
|
660
|
+
if (!(error instanceof ScriptParseError)) throw error;
|
|
661
|
+
return this.fail(`Invalid binding \`{{${source}}}\`: ${error.reason}`, loc);
|
|
662
|
+
}
|
|
663
|
+
const { stateKeys } = this.#context;
|
|
664
|
+
if (stateKeys) {
|
|
665
|
+
const undeclared = stateReads(expr, scope).find((name) => !stateKeys.includes(name));
|
|
666
|
+
if (undeclared) {
|
|
667
|
+
const declared = stateKeys.join(", ") || "none";
|
|
668
|
+
this.fail(`\`${undeclared}\` is not declared in state.ts (declared: ${declared})`, loc);
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
return expr;
|
|
672
|
+
}
|
|
673
|
+
warn(message, loc) {
|
|
674
|
+
this.#context.onWarning?.(new CompileWarning(message, this.#context.file, loc));
|
|
675
|
+
}
|
|
676
|
+
fail(message, loc) {
|
|
677
|
+
throw new CompileError(message, this.#context.file, loc);
|
|
678
|
+
}
|
|
679
|
+
};
|
|
680
|
+
function ifChain(ifNode, nextSiblings) {
|
|
681
|
+
const branches = [ifNode];
|
|
682
|
+
for (const sibling of nextSiblings) {
|
|
683
|
+
if (branches.at(-1).tag === "Else" || !continuesIf(sibling)) break;
|
|
684
|
+
branches.push(sibling);
|
|
685
|
+
}
|
|
686
|
+
return branches;
|
|
687
|
+
}
|
|
688
|
+
function continuesIf(node) {
|
|
689
|
+
return node.type === "Element" && (node.tag === "ElseIf" || node.tag === "Else");
|
|
690
|
+
}
|
|
691
|
+
function singleBindingCode(segments) {
|
|
692
|
+
const [first] = segments;
|
|
693
|
+
if (segments.length === 1 && first?.kind === "binding") return first.code;
|
|
694
|
+
return null;
|
|
695
|
+
}
|
|
696
|
+
function printSegment(segment) {
|
|
697
|
+
return segment.kind === "static" ? JSON.stringify(segment.value) : `text(${segment.code})`;
|
|
698
|
+
}
|
|
699
|
+
function canHaveChildren(parent, child) {
|
|
700
|
+
switch (parent.children) {
|
|
701
|
+
case "none": return false;
|
|
702
|
+
case "text": return child.inline === true;
|
|
703
|
+
case "elements": return true;
|
|
704
|
+
}
|
|
705
|
+
}
|
|
706
|
+
//#endregion
|
|
707
|
+
//#region src/view/compile.ts
|
|
708
|
+
/**
|
|
709
|
+
* Compiles `.dml` markup into a renderer-agnostic ES module:
|
|
710
|
+
* `export default function render(d, { h, on, text, each }) { return [...] }`.
|
|
711
|
+
*
|
|
712
|
+
* The 'h' will be a function from the renderer to create the virtual element for a component.
|
|
713
|
+
*/
|
|
714
|
+
function compileView(source, context) {
|
|
715
|
+
const nodes = parseDmlToIR(source, {
|
|
716
|
+
locations: true,
|
|
717
|
+
file: context.file,
|
|
718
|
+
whitespace: "collapse"
|
|
719
|
+
});
|
|
720
|
+
return [
|
|
721
|
+
"export default function render(d, { h, on, text, each }) {",
|
|
722
|
+
` return [${new ViewCompiler(context).compile(nodes, null, EMPTY_SCOPE).map((node) => `\n ${node},`).join("")}\n ];`,
|
|
723
|
+
"}",
|
|
724
|
+
""
|
|
725
|
+
].join("\n");
|
|
726
|
+
}
|
|
727
|
+
//#endregion
|
|
728
|
+
//#region src/types.ts
|
|
729
|
+
const PAGE_TYPES_FILE = ".dot/pages.d.ts";
|
|
730
|
+
const ENV_TYPES_FILE = ".dot/env.d.ts";
|
|
731
|
+
async function writePageTypes(project) {
|
|
732
|
+
const members = project.pages.map((page) => `${JSON.stringify(page.id)}: true;`);
|
|
733
|
+
return writeIfChanged(join(project.root, PAGE_TYPES_FILE), globalInterface("dot.ts", "DotPageIds", members));
|
|
734
|
+
}
|
|
735
|
+
async function writeEnvTypes(project, env) {
|
|
736
|
+
const members = Object.keys(env).map((name) => `readonly ${name}: string;`);
|
|
737
|
+
return writeIfChanged(join(project.root, ENV_TYPES_FILE), globalInterface("the DOT_PUBLIC_ variables in .env files", "DotPublicEnv", members));
|
|
738
|
+
}
|
|
739
|
+
function globalInterface(source, name, members) {
|
|
740
|
+
return [
|
|
741
|
+
`// Generated by dotctl from ${source}. Do not edit.`,
|
|
742
|
+
"declare global {",
|
|
743
|
+
` interface ${name} {`,
|
|
744
|
+
...members.map((member) => ` ${member}`),
|
|
745
|
+
" }",
|
|
746
|
+
"}",
|
|
747
|
+
"",
|
|
748
|
+
"export {};",
|
|
749
|
+
""
|
|
750
|
+
].join("\n");
|
|
751
|
+
}
|
|
752
|
+
async function writeIfChanged(file, content) {
|
|
753
|
+
if (await readFile(file, "utf8").catch(() => null) !== content) {
|
|
754
|
+
await mkdir(dirname(file), { recursive: true });
|
|
755
|
+
await writeFile(file, content);
|
|
756
|
+
}
|
|
757
|
+
return file;
|
|
758
|
+
}
|
|
759
|
+
//#endregion
|
|
760
|
+
//#region src/project/env.ts
|
|
761
|
+
const PUBLIC_ENV_PREFIX = "DOT_PUBLIC_";
|
|
762
|
+
const PUBLIC_ENV_NAME = /^DOT_PUBLIC_[A-Z0-9_]+$/;
|
|
763
|
+
async function loadPublicEnv(root, mode, shellEnv = process.env) {
|
|
764
|
+
const env = {};
|
|
765
|
+
for (const fileName of [".env", `.env.${mode}`]) {
|
|
766
|
+
const file = join(root, fileName);
|
|
767
|
+
if (existsSync(file)) addPublicVariables(env, parseEnv(await readFile(file, "utf8")), file);
|
|
768
|
+
}
|
|
769
|
+
addPublicVariables(env, shellEnv, "the shell environment");
|
|
770
|
+
const sorted = {};
|
|
771
|
+
for (const name of Object.keys(env).sort()) sorted[name] = env[name];
|
|
772
|
+
return sorted;
|
|
773
|
+
}
|
|
774
|
+
function addPublicVariables(env, variables, source) {
|
|
775
|
+
for (const [name, value] of Object.entries(variables)) {
|
|
776
|
+
if (!name.startsWith("DOT_PUBLIC_") || value === void 0) continue;
|
|
777
|
+
if (!PUBLIC_ENV_NAME.test(name)) throw new CompileError(`"${name}" must use only capital letters, digits and underscores after ${PUBLIC_ENV_PREFIX}`, source);
|
|
778
|
+
env[name] = value;
|
|
779
|
+
}
|
|
780
|
+
}
|
|
781
|
+
//#endregion
|
|
782
|
+
//#region src/build/build.ts
|
|
783
|
+
const MANIFEST_FILE = "manifest.json";
|
|
784
|
+
const ICON_FILE = "icon.png";
|
|
785
|
+
/** Compiles and bundles the dot into `outDir` for hosts, and refreshes the types in `.dot/`. */
|
|
786
|
+
async function build(options) {
|
|
787
|
+
const project = await loadProject(options.root);
|
|
788
|
+
const mode = options.mode ?? "production";
|
|
789
|
+
const env = await loadPublicEnv(project.root, mode);
|
|
790
|
+
const runtime = installedRuntime(project.root);
|
|
791
|
+
await writePageTypes(project);
|
|
792
|
+
await writeEnvTypes(project, env);
|
|
793
|
+
const warnings = [];
|
|
794
|
+
const modules = await compileModules(project, env, componentRegistry([...Object.values(builtinComponents), ...options.components ?? []]), mode, warnings);
|
|
795
|
+
const outDir = resolve(project.root, options.outDir ?? "dist");
|
|
796
|
+
const uiDir = join(outDir, "ui");
|
|
797
|
+
const bundleLayer = (entry, layerDir, format, external) => {
|
|
798
|
+
return bundle({
|
|
799
|
+
root: project.root,
|
|
800
|
+
modules,
|
|
801
|
+
entry,
|
|
802
|
+
outDir: layerDir,
|
|
803
|
+
fileName: "dot.js",
|
|
804
|
+
format,
|
|
805
|
+
mode,
|
|
806
|
+
minify: mode === "production",
|
|
807
|
+
conditions: options.conditions ?? [],
|
|
808
|
+
external
|
|
809
|
+
});
|
|
810
|
+
};
|
|
811
|
+
const files = [
|
|
812
|
+
...await bundleLayer(ENTRY.ui, uiDir, "esm", Object.keys(REACT_HOST_MODULES)),
|
|
813
|
+
...await bundleLayer(ENTRY.logic, join(outDir, "logic"), "iife", []),
|
|
814
|
+
await writeIndexHtml(project, uiDir, runtime.react.version),
|
|
815
|
+
...await copyStatics(project, uiDir),
|
|
816
|
+
await writeManifest(project, outDir, runtime)
|
|
817
|
+
];
|
|
818
|
+
const icon = await copyIcon(project, outDir);
|
|
819
|
+
if (icon) files.push(icon);
|
|
820
|
+
return {
|
|
821
|
+
outDir,
|
|
822
|
+
files,
|
|
823
|
+
warnings
|
|
824
|
+
};
|
|
825
|
+
}
|
|
826
|
+
async function compileModules(project, env, components, mode, warnings) {
|
|
827
|
+
const modules = /* @__PURE__ */ new Map();
|
|
828
|
+
for (const page of project.pages) {
|
|
829
|
+
const view = await compilePage(page, components, project.statics, warnings);
|
|
830
|
+
modules.set(viewModuleId(page.id), view);
|
|
831
|
+
}
|
|
832
|
+
modules.set(ENTRY.ui, uiEntry(project));
|
|
833
|
+
modules.set(ENTRY.logic, logicEntry(project, { strict: mode === "development" }));
|
|
834
|
+
modules.set(ENV_MODULE, envModule(env));
|
|
835
|
+
return modules;
|
|
836
|
+
}
|
|
837
|
+
async function writeIndexHtml(project, uiDir, reactVersion) {
|
|
838
|
+
const file = join(uiDir, "index.html");
|
|
839
|
+
await mkdir(uiDir, { recursive: true });
|
|
840
|
+
await writeFile(file, indexHtml(project.dotJson.name, reactVersion));
|
|
841
|
+
return file;
|
|
842
|
+
}
|
|
843
|
+
async function copyStatics(project, uiDir) {
|
|
844
|
+
const copies = [];
|
|
845
|
+
for (const file of project.statics) {
|
|
846
|
+
const copy = join(uiDir, file);
|
|
847
|
+
await mkdir(dirname(copy), { recursive: true });
|
|
848
|
+
await copyFile(join(project.root, file), copy);
|
|
849
|
+
copies.push(copy);
|
|
850
|
+
}
|
|
851
|
+
return copies;
|
|
852
|
+
}
|
|
853
|
+
async function writeManifest(project, outDir, runtime) {
|
|
854
|
+
const file = join(outDir, MANIFEST_FILE);
|
|
855
|
+
const manifest = dotManifest(project, runtime);
|
|
856
|
+
await writeFile(file, `${JSON.stringify(manifest, null, 2)}\n`);
|
|
857
|
+
return file;
|
|
858
|
+
}
|
|
859
|
+
async function copyIcon(project, outDir) {
|
|
860
|
+
if (project.dotJson.icon === void 0) return null;
|
|
861
|
+
const file = join(outDir, ICON_FILE);
|
|
862
|
+
await copyFile(join(project.root, project.dotJson.icon), file);
|
|
863
|
+
return file;
|
|
864
|
+
}
|
|
865
|
+
function dotManifest(project, runtime) {
|
|
866
|
+
const { id, name, version, description, categories, permissions, dash } = project.dotJson;
|
|
867
|
+
const pages = project.pages.map((page) => page.id);
|
|
868
|
+
return {
|
|
869
|
+
id,
|
|
870
|
+
name,
|
|
871
|
+
version,
|
|
872
|
+
runtime: runtime.version,
|
|
873
|
+
react: runtime.react.version,
|
|
874
|
+
description,
|
|
875
|
+
categories,
|
|
876
|
+
icon: project.dotJson.icon === void 0 ? void 0 : ICON_FILE,
|
|
877
|
+
permissions,
|
|
878
|
+
dash,
|
|
879
|
+
entry: pages[0],
|
|
880
|
+
pages
|
|
881
|
+
};
|
|
882
|
+
}
|
|
883
|
+
async function compilePage(page, components, statics, warnings) {
|
|
884
|
+
const [view, state, controller] = await Promise.all([
|
|
885
|
+
readFile(page.view, "utf8"),
|
|
886
|
+
readFile(page.state, "utf8"),
|
|
887
|
+
readFile(page.controller, "utf8")
|
|
888
|
+
]);
|
|
889
|
+
const context = {
|
|
890
|
+
file: page.view,
|
|
891
|
+
components,
|
|
892
|
+
statics,
|
|
893
|
+
onWarning: (warning) => warnings.push(warning)
|
|
894
|
+
};
|
|
895
|
+
const { keys } = parseStateModule(state, page.state);
|
|
896
|
+
if (keys) context.stateKeys = keys;
|
|
897
|
+
const { mutations, actions } = parseLogicModule(controller, page.controller);
|
|
898
|
+
if (mutations && actions) context.handlers = [...mutations, ...actions];
|
|
899
|
+
return compileView(view, context);
|
|
900
|
+
}
|
|
901
|
+
function indexHtml(title, reactVersion) {
|
|
902
|
+
const escaped = title.replace(/[&<>"]/g, (char) => `&#${char.charCodeAt(0)};`);
|
|
903
|
+
const imports = {};
|
|
904
|
+
for (const [name, file] of Object.entries(REACT_HOST_MODULES)) imports[name] = reactHostUrl(reactVersion, file);
|
|
905
|
+
return `<!doctype html>
|
|
906
|
+
<html lang="en">
|
|
907
|
+
<head>
|
|
908
|
+
<meta charset="UTF-8" />
|
|
909
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
910
|
+
<title>${escaped}</title>
|
|
911
|
+
<style>
|
|
912
|
+
body {
|
|
913
|
+
margin: 0;
|
|
914
|
+
}
|
|
915
|
+
</style>
|
|
916
|
+
</head>
|
|
917
|
+
<body>
|
|
918
|
+
<div id="root"></div>
|
|
919
|
+
<script type="importmap">${JSON.stringify({ imports })}<\/script>
|
|
920
|
+
<script type="module" src="./dot.js"><\/script>
|
|
921
|
+
</body>
|
|
922
|
+
</html>
|
|
923
|
+
`;
|
|
924
|
+
}
|
|
925
|
+
//#endregion
|
|
926
|
+
export { CompileError, CompileWarning, build, compileView, installedRuntime, loadProject };
|
|
927
|
+
|
|
928
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +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, 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 'dash',\n];\nexport const REQUIRED_DOT_JSON_FIELDS: readonly string[] = ['id', 'name', 'version'];\nexport const DASH_FIELDS: readonly string[] = ['minVersion'];\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 dash?: DashRequirements | undefined;\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, dash } = 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 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 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';\nconst 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 { hostTransport, startWebview } 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 'startWebview({',\n ' container: document.getElementById(\"root\"),',\n ' transport: hostTransport(),',\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 = [\n ' transport: hostTransport(),',\n ' dot,',\n ` pages: ${pageTable(project, 'page')},`,\n ];\n if (strict) {\n options.push(' strict: true,');\n }\n return [\n `import { createService, hostTransport } 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 'createService({',\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 { sep } from 'node:path';\n\nimport type { Plugin } from 'rolldown';\nimport { unboundReferences } from '@monimesl/dot-script-parser';\n\nimport { ENV_MODULE } from './entries';\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.\nexport function dotGlobals(root: string): Plugin {\n const isDotModule = (id: string) =>\n id.startsWith(root + sep) && !id.includes(`${sep}node_modules${sep}`) && SCRIPT.test(id);\n return {\n name: 'dot:globals',\n transform(code, id) {\n if (!isDotModule(id)) {\n return null;\n }\n const imports: string[] = [];\n for (const [source, names] of Object.entries(DOT_GLOBALS)) {\n const used = unboundReferences(code, names, id);\n if (used.length > 0) {\n imports.push(`import { ${used.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';\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 external: [...options.external],\n platform: 'browser',\n plugins: [generatedModules(options.root, options.modules), dotGlobals(options.root)],\n resolve: {\n conditionNames: [...options.conditions, 'browser', 'import', 'require', 'default'],\n },\n transform: { define: { 'process.env.NODE_ENV': JSON.stringify(options.mode) } },\n });\n try {\n const { output } = await bundler.write({\n dir: options.outDir,\n format: options.format,\n entryFileNames: options.fileName,\n sourcemap: true,\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}\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 };\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\nexport const CONTROL_FLOW = new Set(['If', 'ElseIf', 'Else', 'For']);\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 if (branch.tag === 'Else') {\n compiler.checkControlFlowElementAttributes(branch, []);\n code = body;\n } else {\n compiler.checkControlFlowElementAttributes(branch, ['condition'], ['condition']);\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, ['each', 'item', 'index', 'key'], ['each']);\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 { CompileError, CompileWarning } from '../errors';\nimport { isIdentifier, printExpr, type Scope, stateReads } from './expression';\nimport type { DmlElementNode, DmlNode } from '@monimesl/dot-dml-parser';\nimport { compileConditional, compileLoop, CONTROL_FLOW } from './control-flow';\nimport { STATICS_DIR } from '../project/statics';\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\ntype Segment = { kind: 'static'; value: string } | { kind: 'binding'; code: string };\nconst SEGMENT_BINDING_REGEXP = /{{([\\s\\S]*?)}}/g;\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,\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 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 props = [\n ...Object.entries(node.attributes).map(([name, value]) =>\n this.compileProp(component, name, value, scope, node.loc),\n ),\n ...Object.entries(node.events).map(([name, handler]) =>\n this.compileEvent(component, name, handler, node.loc),\n ),\n ];\n const args = [\n JSON.stringify(component.name),\n props.length ? `{ ${props.join(', ')} }` : 'null',\n ...this.compile(node.children, component, scope),\n ];\n return `h(${args.join(', ')})`;\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 #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 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 return `${attribute}: on(${JSON.stringify(handler)})`;\n }\n\n checkControlFlowElementAttributes(\n node: DmlElementNode,\n allowed: readonly string[],\n required: readonly string[] = [],\n ): void {\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\nfunction printSegment(segment: Segment): string {\n return segment.kind === 'static' ? JSON.stringify(segment.value) : `text(${segment.code})`;\n}\n\nfunction 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 { EMPTY_SCOPE } from './expression';\nimport { 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 [...] }`.\n *\n * The 'h' will be a function from the renderer to create the virtual element for a component.\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 body = compiler.compile(nodes, null, EMPTY_SCOPE);\n return [\n 'export default function render(d, { h, on, text, each }) {',\n ` return [${body.map((node) => `\\n ${node},`).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 DOT_PUBLIC_ 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// Only variables with this prefix reach the dot. Everything in the service bundle is readable by\n// anyone who has the dot, so these are public by name: a secret never belongs in them.\nexport const PUBLIC_ENV_PREFIX = 'DOT_PUBLIC_';\n\nconst PUBLIC_ENV_NAME = /^DOT_PUBLIC_[A-Z0-9_]+$/;\n\nexport type PublicEnv = Readonly<Record<string, string>>;\n\n// The dot's public configuration, from `.env`, then `.env.<mode>`, then the shell's environment;\n// each later one wins, so CI can set a value without a file.\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 addPublicVariables(env, parseEnv(await readFile(file, 'utf8')), file);\n }\n }\n addPublicVariables(env, shellEnv, 'the shell environment');\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] = env[name]!;\n }\n return sorted;\n}\n\nfunction addPublicVariables(\n env: Record<string, string>,\n variables: Readonly<Record<string, string | undefined>>,\n source: string,\n): void {\n for (const [name, value] of Object.entries(variables)) {\n if (!name.startsWith(PUBLIC_ENV_PREFIX) || value === undefined) {\n continue;\n }\n if (!PUBLIC_ENV_NAME.test(name)) {\n throw new CompileError(\n `\"${name}\" must use only capital letters, digits and underscores after ${PUBLIC_ENV_PREFIX}`,\n source,\n );\n }\n env[name] = value;\n }\n}\n","import { dirname, join, resolve } from 'node:path';\nimport { copyFile, mkdir, readFile, writeFile } from 'node:fs/promises';\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';\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 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 return { outDir, files, warnings };\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, dash } = 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 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 <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
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@monimesl/dot-compiler",
|
|
3
|
+
"version": "0.1.0-alpha.2",
|
|
4
|
+
"description": "Compiles Dot mini apps into a webview bundle and a service bundle",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "git+https://github.com/monime-lab/dot.git",
|
|
9
|
+
"directory": "packages/compiler"
|
|
10
|
+
},
|
|
11
|
+
"sideEffects": false,
|
|
12
|
+
"files": [
|
|
13
|
+
"dist"
|
|
14
|
+
],
|
|
15
|
+
"exports": {
|
|
16
|
+
".": {
|
|
17
|
+
"@monimesl/dot-source": "./src/index.ts",
|
|
18
|
+
"types": "./dist/index.d.ts",
|
|
19
|
+
"import": "./dist/index.js",
|
|
20
|
+
"default": "./dist/index.js"
|
|
21
|
+
},
|
|
22
|
+
"./package.json": "./package.json"
|
|
23
|
+
},
|
|
24
|
+
"main": "./dist/index.js",
|
|
25
|
+
"types": "./dist/index.d.ts",
|
|
26
|
+
"dependencies": {
|
|
27
|
+
"rolldown": "^1.2.9",
|
|
28
|
+
"@monimesl/dot-dml-parser": "0.1.0-alpha.2",
|
|
29
|
+
"@monimesl/dot-core": "0.1.0-alpha.2",
|
|
30
|
+
"@monimesl/dot-script-parser": "0.1.0-alpha.2"
|
|
31
|
+
},
|
|
32
|
+
"devDependencies": {
|
|
33
|
+
"@types/node": "^22.20.4",
|
|
34
|
+
"publint": "^0.3.24",
|
|
35
|
+
"rimraf": "^6.1.3",
|
|
36
|
+
"tsdown": "^0.23.0",
|
|
37
|
+
"typescript": "^5.9.3",
|
|
38
|
+
"vitest": "^5.0.1"
|
|
39
|
+
},
|
|
40
|
+
"publishConfig": {
|
|
41
|
+
"access": "public"
|
|
42
|
+
},
|
|
43
|
+
"scripts": {
|
|
44
|
+
"build": "tsdown",
|
|
45
|
+
"dev": "tsdown --watch",
|
|
46
|
+
"typecheck": "tsc --noEmit -p tsconfig.json",
|
|
47
|
+
"test": "vitest run",
|
|
48
|
+
"clean": "rimraf dist *.tsbuildinfo"
|
|
49
|
+
}
|
|
50
|
+
}
|