@loadbare/app 0.20.0 → 0.22.0
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 +18 -0
- package/dist/build/assemble.d.ts.map +1 -1
- package/dist/build/assemble.js +8 -3
- package/dist/build/assemble.js.map +1 -1
- package/dist/build/build.d.ts +62 -0
- package/dist/build/build.d.ts.map +1 -0
- package/dist/build/build.js +148 -0
- package/dist/build/build.js.map +1 -0
- package/dist/build/cli.d.ts +6 -1
- package/dist/build/cli.d.ts.map +1 -1
- package/dist/build/cli.js +25 -78
- package/dist/build/cli.js.map +1 -1
- package/dist/build/declared.d.ts.map +1 -1
- package/dist/build/declared.js +3 -0
- package/dist/build/declared.js.map +1 -1
- package/dist/build/skills-cli.d.ts +2 -1
- package/dist/build/skills-cli.d.ts.map +1 -1
- package/dist/build/skills-cli.js +8 -1
- package/dist/build/skills-cli.js.map +1 -1
- package/dist/build/skills.d.ts +2 -0
- package/dist/build/skills.d.ts.map +1 -1
- package/dist/build/skills.js +2 -0
- package/dist/build/skills.js.map +1 -1
- package/dist/core/lb-constants.d.ts +14 -0
- package/dist/core/lb-constants.d.ts.map +1 -1
- package/dist/core/lb-constants.js +25 -0
- package/dist/core/lb-constants.js.map +1 -1
- package/dist/dev/lb-dev.d.ts +128 -0
- package/dist/dev/lb-dev.d.ts.map +1 -0
- package/dist/dev/lb-dev.js +289 -0
- package/dist/dev/lb-dev.js.map +1 -0
- package/dist/hub/lb-apply.d.ts +10 -2
- package/dist/hub/lb-apply.d.ts.map +1 -1
- package/dist/hub/lb-apply.js +8 -2
- package/dist/hub/lb-apply.js.map +1 -1
- package/dist/hub/lb-hub.browser.d.ts.map +1 -1
- package/dist/hub/lb-hub.browser.js +127 -19
- package/dist/hub/lb-hub.browser.js.map +1 -1
- package/docs/comparison.md +4 -3
- package/docs/reference/builder.md +12 -4
- package/docs/reference/chrome.md +52 -1
- package/docs/reference/data-binding.md +27 -12
- package/docs/reference/dev-server.md +171 -0
- package/docs/reference/names.md +14 -1
- package/docs/reference/overview.md +1 -0
- package/docs/reference/server.md +8 -3
- package/docs/reference/widgets.md +10 -277
- package/docs/testing.md +2 -1
- package/docs/tutorials/000-getting-started.md +1 -1
- package/docs/tutorials/040-displaying-data.md +1 -1
- package/docs/tutorials/090-using-widget-libraries.md +1 -1
- package/package.json +4 -2
- package/skills/loadbare-app/SKILL.md +30 -12
- package/skills/loadbare-app/references/builder.md +12 -4
- package/skills/loadbare-app/references/chrome.md +52 -1
- package/skills/loadbare-app/references/data-binding.md +27 -12
- package/skills/loadbare-app/references/dev-server.md +171 -0
- package/skills/loadbare-app/references/names.md +14 -1
- package/skills/loadbare-app/references/overview.md +1 -0
- package/skills/loadbare-app/references/server.md +8 -3
- package/skills/loadbare-app/references/widgets.md +10 -277
package/README.md
CHANGED
|
@@ -46,6 +46,24 @@ shows and sends, and
|
|
|
46
46
|
[Using Widget Libraries](./docs/tutorials/090-using-widget-libraries.md) for
|
|
47
47
|
what listing a package does.
|
|
48
48
|
|
|
49
|
+
## Agent skills
|
|
50
|
+
|
|
51
|
+
The package ships an agent skill, `loadbare-app`, written for a coding agent working
|
|
52
|
+
in a project that depends on `@loadbare/app`. Install it with:
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
npx loadbare-app skills install
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
It links the skill into `.agents/skills/` and `.claude/skills/`, where agents
|
|
59
|
+
look for skills. `--copy` writes copies instead of links, and `--dir <path>`
|
|
60
|
+
installs into one directory of your choosing. The command prints the paths to
|
|
61
|
+
add to `.gitignore` and writes nothing there itself.
|
|
62
|
+
|
|
63
|
+
`npx loadbare-app skills install`, `npx loadbare-widgets skills install` and
|
|
64
|
+
`npx loadbare-db skills install` install the three loadbare skills, one per
|
|
65
|
+
package.
|
|
66
|
+
|
|
49
67
|
## Tutorials
|
|
50
68
|
|
|
51
69
|
[Getting Started](./docs/tutorials/000-getting-started.md): a step-by-step
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"assemble.d.ts","sourceRoot":"","sources":["../../build/assemble.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;
|
|
1
|
+
{"version":3,"file":"assemble.d.ts","sourceRoot":"","sources":["../../build/assemble.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAyCH,OAAO,KAAK,EAAY,aAAa,EAAE,MAAM,eAAe,CAAC;AAC7D,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAC/C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AA6d/C,MAAM,WAAW,eAAe;IAC9B,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,QAAQ,EAAE,CAAC;IAClB,8EAA8E;IAC9E,KAAK,EAAE,UAAU,EAAE,CAAC;IACpB;;;OAGG;IACH,QAAQ,CAAC,EAAE,aAAa,CAAC;CAC1B;AAED,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,4EAA4E;IAC5E,QAAQ,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;CACvB;AAED,wBAAsB,QAAQ,CAAC,EAC7B,UAAU,EACV,KAAK,EACL,KAAK,EACL,QAAQ,GACT,EAAE,eAAe,GAAG,OAAO,CAAC,SAAS,CAAC,CAgEtC"}
|
package/dist/build/assemble.js
CHANGED
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
import { readFile } from "node:fs/promises";
|
|
20
20
|
import { addDefinitions, customElementTags, expandTree, } from "./expand.js";
|
|
21
21
|
import { formatHtml } from "./format.js";
|
|
22
|
-
import { AGGREGATES, ATTR_FILTER, ATTR_ORDER, ATTR_COLUMN, ATTR_COUNT, ATTR_GROUP, ATTR_PAGE, ATTR_PAGE_TITLE, ATTR_QUERY, ATTR_REQUEST, ATTR_SELECT, ATTR_SHOW, ATTR_URL_LINK, ATTR_URL_PUSH, ATTR_URL_PARM, ATTR_URL_UNKNOWN, CHOICES_QUERY, DEVELOPER_ATTRIBUTES, HUB_TAG_NAME, LB_COMMAND_PREFIX, LB_COMMANDS, LB_RESERVED_PREFIX, REQUEST_ROW_DELETE, REQUEST_ROW_INSERT, REQUEST_ROW_UPDATE, SHOW_NOT, URL_QUERY, } from "../core/lb-constants.js";
|
|
22
|
+
import { AGGREGATES, ATTR_FILTER, ATTR_ORDER, ATTR_COLUMN, ATTR_COUNT, ATTR_GROUP, ATTR_PAGE, ATTR_PAGE_TITLE, ATTR_QUERY, ATTR_REQUEST, ATTR_SELECT, ATTR_SHOW, ATTR_URL_LINK, ATTR_URL_PUSH, ATTR_URL_PARM, ATTR_URL_UNKNOWN, CHOICES_QUERY, HUB_QUERY, DEVELOPER_ATTRIBUTES, HUB_TAG_NAME, LB_COMMAND_PREFIX, LB_COMMANDS, LB_RESERVED_PREFIX, REQUEST_ROW_DELETE, REQUEST_ROW_INSERT, REQUEST_ROW_UPDATE, SHOW_NOT, URL_QUERY, } from "../core/lb-constants.js";
|
|
23
23
|
import { isRowRequest } from "../core/lb-types.js";
|
|
24
24
|
/**
|
|
25
25
|
* The hub is one element, and it swaps each page into the one `<main>`
|
|
@@ -70,7 +70,7 @@ function checkUnknownPageDialog(chromeFile, body) {
|
|
|
70
70
|
}
|
|
71
71
|
}
|
|
72
72
|
/** The queries the hub serves, which every page and the chrome may name. */
|
|
73
|
-
const HUB_QUERIES = [URL_QUERY, CHOICES_QUERY];
|
|
73
|
+
const HUB_QUERIES = [URL_QUERY, CHOICES_QUERY, HUB_QUERY];
|
|
74
74
|
/** The `Crud` key each request Loadbare provides runs. */
|
|
75
75
|
const CRUD_KEYS = {
|
|
76
76
|
[REQUEST_ROW_INSERT]: "rowInsert",
|
|
@@ -94,7 +94,7 @@ function checkNames(el, scope, names, problems) {
|
|
|
94
94
|
if (query !== null && !HUB_QUERIES.includes(query)) {
|
|
95
95
|
if (declared === null) {
|
|
96
96
|
problems.push(`<${tag}> carries ${ATTR_QUERY}="${query}"; the chrome belongs to ` +
|
|
97
|
-
`no page, and names only ${HUB_QUERIES.join(" and
|
|
97
|
+
`no page, and names only ${HUB_QUERIES.slice(0, -1).join(", ")} and ${HUB_QUERIES.at(-1)}`);
|
|
98
98
|
}
|
|
99
99
|
else if (!declared.queries.has(query)) {
|
|
100
100
|
problems.push(`<${tag}> carries ${ATTR_QUERY}="${query}", which the page's ` +
|
|
@@ -108,6 +108,11 @@ function checkNames(el, scope, names, problems) {
|
|
|
108
108
|
const target = tag === "form" && query !== null ? query : (scope?.query ?? null);
|
|
109
109
|
if (target === null)
|
|
110
110
|
return;
|
|
111
|
+
if (target === HUB_QUERY) {
|
|
112
|
+
problems.push(`<${tag}> carries ${ATTR_REQUEST}="${request}" for '${target}'; ` +
|
|
113
|
+
`the hub's own state is written by the hub alone`);
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
111
116
|
if (HUB_QUERIES.includes(target)) {
|
|
112
117
|
if (request !== REQUEST_ROW_UPDATE) {
|
|
113
118
|
problems.push(`<${tag}> carries ${ATTR_REQUEST}="${request}" for '${target}'; ` +
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"assemble.js","sourceRoot":"","sources":["../../build/assemble.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Assembling one document out of a chrome file and a set of pages.\n *\n * Every `.html` file in play — the chrome and each page — goes through the\n * same `expand()` (see build/expand.ts) against the same definitions: the\n * built-in widgets and whatever definitions were found in the caller's own\n * source tree. `expand()` only ever sees a fragment, so the chrome's own is\n * the text between its `<body>` tags; a page's is the whole file. A page's\n * expanded host is wrapped in `<template lb-page=\"<name>\">` and, together\n * with the chrome's own expanded body content, spliced back into the\n * chrome's `<body>`. The result is the one document a server has to serve\n * for every route — see docs/tutorials/000-getting-started.md and\n * docs/reference/builder.md.\n *\n * Every path here is a parameter, resolved once by build/locations.ts — this\n * module never reconstructs one of its own, and never reads a directory\n * looking for pages or widgets; it is handed exactly which files those are.\n */\n\nimport { readFile } from \"node:fs/promises\";\nimport {\n addDefinitions,\n customElementTags,\n expandTree,\n type Definitions,\n} from \"./expand.js\";\nimport { formatHtml } from \"./format.js\";\nimport {\n AGGREGATES,\n ATTR_FILTER,\n ATTR_ORDER,\n ATTR_COLUMN,\n ATTR_COUNT,\n ATTR_GROUP,\n ATTR_PAGE,\n ATTR_PAGE_TITLE,\n ATTR_QUERY,\n ATTR_REQUEST,\n ATTR_SELECT,\n ATTR_SHOW,\n ATTR_URL_LINK,\n ATTR_URL_PUSH,\n ATTR_URL_PARM,\n ATTR_URL_UNKNOWN,\n CHOICES_QUERY,\n DEVELOPER_ATTRIBUTES,\n HUB_TAG_NAME,\n LB_COMMAND_PREFIX,\n LB_COMMANDS,\n LB_RESERVED_PREFIX,\n REQUEST_ROW_DELETE,\n REQUEST_ROW_INSERT,\n REQUEST_ROW_UPDATE,\n SHOW_NOT,\n URL_QUERY,\n} from \"../core/lb-constants.js\";\nimport { isRowRequest } from \"../core/lb-types.js\";\nimport type { Declared, DeclaredPages } from \"./declared.js\";\nimport type { PageFile } from \"./locations.js\";\nimport type { OriginScan } from \"./origins.js\";\n\n/**\n * The hub is one element, and it swaps each page into the one `<main>`\n * inside it (hub/lb-hub.browser.ts), so the chrome holds exactly one of each,\n * the `<main>` inside the hub and empty, since whatever it held would be\n * replaced by the first page. A page is swapped into that `<main>`, so it\n * carries neither.\n */\nfunction checkChrome(chromeFile: string, body: ParentNode): void {\n const where = `assemble: '${chromeFile}'`;\n const hubs = body.querySelectorAll(HUB_TAG_NAME).length;\n if (hubs !== 1) {\n throw new Error(\n `${where} has ${hubs} <${HUB_TAG_NAME}>; it needs exactly one`,\n );\n }\n const mains = body.querySelectorAll(\"main\");\n if (mains.length !== 1) {\n throw new Error(\n `${where} has ${mains.length} <main>; it needs exactly one`,\n );\n }\n const main = mains[0]!;\n if (!main.closest(HUB_TAG_NAME)) {\n throw new Error(\n `${where} has its <main> outside <${HUB_TAG_NAME}>, where the hub cannot reach it`,\n );\n }\n if (main.children.length > 0 || main.textContent!.trim() !== \"\") {\n throw new Error(\n `${where} has content in its <main>, which the first page replaces; it goes empty`,\n );\n }\n}\n\n/** A page lands inside the chrome's `<main>`, so it brings no second one and no hub. */\nfunction checkPage(file: string, root: ParentNode): void {\n for (const tag of [HUB_TAG_NAME, \"main\"]) {\n if (root.querySelector(tag)) {\n throw new Error(\n `assemble: '${file}' carries a <${tag}>, which belongs to the chrome alone`,\n );\n }\n }\n}\n\n/**\n * The hub calls showModal() on the `<dialog>` carrying lb-url-unknown\n * (hub/lb-hub.browser.ts), and looks for it inside itself, as it does for\n * everything else it acts on. A dialog elsewhere in the body would never open\n * and would say nothing about why.\n */\nfunction checkUnknownPageDialog(chromeFile: string, body: ParentNode): void {\n const el = body.querySelector(`[${ATTR_URL_UNKNOWN}]`);\n if (!el) return;\n if (!el.closest(HUB_TAG_NAME)) {\n throw new Error(\n `assemble: '${chromeFile}' declares ${ATTR_URL_UNKNOWN} outside ` +\n `<${HUB_TAG_NAME}>, where the hub cannot reach it`,\n );\n }\n}\n\n/**\n * Where the markup stands while the check walks it: inside which `lb-query`,\n * and whether there is a row there to take a value from. Inside a row\n * template there always is. Inside an `lb-query` outside one, there is when\n * the query is of kind `row`, which only the server knows.\n */\ninterface Scope {\n query: string;\n row: boolean | \"unknown\";\n}\n\n/** The queries the hub serves, which every page and the chrome may name. */\nconst HUB_QUERIES: readonly string[] = [URL_QUERY, CHOICES_QUERY];\n\n/** The `Crud` key each request Loadbare provides runs. */\nconst CRUD_KEYS: Record<string, string> = {\n [REQUEST_ROW_INSERT]: \"rowInsert\",\n [REQUEST_ROW_UPDATE]: \"rowUpdate\",\n [REQUEST_ROW_DELETE]: \"rowDelete\",\n};\n\n/**\n * What a file's names are checked against: a page's declarations, or `null`\n * for the chrome, which belongs to no page and so names only what the hub\n * serves.\n */\ntype Names = { declared: Declared | null };\n\n/**\n * An `lb-query` or `lb-request` naming nothing the page declares would be\n * found only on the first round trip, as a server warning and an empty\n * answer, so a typo would build cleanly.\n *\n * A request Loadbare provides is for the query of what it gathers: a form's\n * own when the form carries `lb-query`, otherwise the nearest around the\n * element, as the hub decides it (hub/lb-hub.browser.ts). With no query\n * around it, the hub reports it in the browser.\n */\nfunction checkNames(\n el: Element,\n scope: Scope | null,\n names: Names,\n problems: string[],\n): void {\n const tag = el.localName;\n const { declared } = names;\n\n const query = el.getAttribute(ATTR_QUERY);\n if (query !== null && !HUB_QUERIES.includes(query)) {\n if (declared === null) {\n problems.push(\n `<${tag}> carries ${ATTR_QUERY}=\"${query}\"; the chrome belongs to ` +\n `no page, and names only ${HUB_QUERIES.join(\" and \")}`,\n );\n } else if (!declared.queries.has(query)) {\n problems.push(\n `<${tag}> carries ${ATTR_QUERY}=\"${query}\", which the page's ` +\n `queries do not declare`,\n );\n }\n }\n\n const request = el.getAttribute(ATTR_REQUEST);\n if (!request) return;\n if (isRowRequest(request)) {\n const target =\n tag === \"form\" && query !== null ? query : (scope?.query ?? null);\n if (target === null) return;\n if (HUB_QUERIES.includes(target)) {\n if (request !== REQUEST_ROW_UPDATE) {\n problems.push(\n `<${tag}> carries ${ATTR_REQUEST}=\"${request}\" for '${target}'; ` +\n `the hub answers only ${REQUEST_ROW_UPDATE} for it`,\n );\n }\n return;\n }\n // A query the page does not declare is reported once, where it is named.\n if (declared === null || !declared.queries.has(target)) return;\n const key = CRUD_KEYS[request]!;\n if (!declared.crud.get(target)?.has(key)) {\n problems.push(\n `<${tag}> carries ${ATTR_REQUEST}=\"${request}\" for '${target}', ` +\n `whose crud entry the page does not declare with ${key}`,\n );\n }\n } else if (request.startsWith(LB_RESERVED_PREFIX)) {\n // Reported by walkMarkup as naming no request Loadbare provides.\n } else if (declared === null) {\n problems.push(\n `<${tag}> carries ${ATTR_REQUEST}=\"${request}\"; the chrome belongs ` +\n `to no page, and sends no declared request`,\n );\n } else if (!declared.handlers.has(request)) {\n problems.push(\n `<${tag}> carries ${ATTR_REQUEST}=\"${request}\", which the page's ` +\n `handlers do not declare`,\n );\n }\n}\n\n/**\n * The markup, checked once against the finished document rather than\n * discovered one console error at a time in a browser.\n *\n * Every rule here is a fact the markup states about itself. The developer\n * writes `lb-*` attributes in markup, and script never assigns them, which is\n * what makes checking the markup sound. See docs/reference/data-binding.md.\n *\n * A `<template>`'s children are walked in the scope that encloses the\n * template, because a row template's clones land in the element around it.\n */\nfunction walkMarkup(\n node: ParentNode,\n scope: Scope | null,\n names: Names | null,\n problems: string[],\n): void {\n for (const el of node.children) {\n const tag = el.localName;\n let here = scope;\n const query = el.getAttribute(ATTR_QUERY);\n if (query !== null) here = { query, row: \"unknown\" };\n if (names) checkNames(el, scope, names, problems);\n\n for (const { name } of el.attributes) {\n if (\n name.startsWith(LB_RESERVED_PREFIX) &&\n !(DEVELOPER_ATTRIBUTES as readonly string[]).includes(name)\n ) {\n problems.push(\n `<${tag}> carries ${name}, which is not an attribute a developer ` +\n `writes`,\n );\n }\n }\n\n const request = el.getAttribute(ATTR_REQUEST);\n if (\n request !== null &&\n request.startsWith(LB_RESERVED_PREFIX) &&\n !isRowRequest(request)\n ) {\n problems.push(\n `<${tag}> carries ${ATTR_REQUEST}=\"${request}\", which begins with ` +\n `'${LB_RESERVED_PREFIX}' but names no request Loadbare provides`,\n );\n }\n if (el.hasAttribute(ATTR_URL_LINK) && tag !== \"a\") {\n problems.push(`<${tag}> carries ${ATTR_URL_LINK}; it goes on an <a>`);\n }\n // A link's parms are columns of the row around it, and only a link the\n // hub handles takes them: the parms are the application's own URL.\n const parms = el.getAttribute(ATTR_URL_PARM);\n if (parms !== null) {\n const columns = parms.split(\",\").map((column) => column.trim());\n if (tag !== \"a\" || !el.hasAttribute(ATTR_URL_LINK)) {\n problems.push(\n `<${tag}> carries ${ATTR_URL_PARM}; it goes on an <a> carrying ` +\n `${ATTR_URL_LINK}`,\n );\n } else if (columns.some((column) => column === \"\")) {\n problems.push(\n `<${tag}> carries ${ATTR_URL_PARM}=\"${parms}\", which names no ` +\n `column between two commas or at either end`,\n );\n } else if (columns.some((column) => /\\s/.test(column))) {\n problems.push(\n `<${tag}> carries ${ATTR_URL_PARM}=\"${parms}\"; columns are ` +\n `separated by commas`,\n );\n } else if (columns.some((c) => c.startsWith(LB_RESERVED_PREFIX))) {\n problems.push(\n `<${tag}> carries ${ATTR_URL_PARM}=\"${parms}\"; a parm beginning ` +\n `'${LB_RESERVED_PREFIX}' is the hub's own`,\n );\n } else if (scope === null || scope.row === false) {\n problems.push(\n `<${tag}> carries ${ATTR_URL_PARM}=\"${parms}\" with no row around ` +\n `it to take the values from`,\n );\n }\n }\n if (el.hasAttribute(ATTR_URL_PUSH) && request === null) {\n problems.push(\n `<${tag}> carries ${ATTR_URL_PUSH} and no ${ATTR_REQUEST}; it goes ` +\n `on an element with ${ATTR_REQUEST}`,\n );\n }\n // A selector is a checkbox in a live row, and selects that row. It is no\n // column, and commits nothing of its own.\n if (el.hasAttribute(ATTR_SELECT)) {\n if (tag !== \"input\" || el.getAttribute(\"type\") !== \"checkbox\") {\n problems.push(\n `<${tag}> carries ${ATTR_SELECT}; it goes on an ` +\n `<input type=\"checkbox\">`,\n );\n } else if (scope?.row !== true) {\n problems.push(\n `<input> carries ${ATTR_SELECT} outside a row template; it ` +\n `selects the row it sits in`,\n );\n }\n for (const name of [ATTR_COLUMN, ATTR_REQUEST]) {\n if (el.hasAttribute(name)) {\n problems.push(\n `<${tag}> carries ${ATTR_SELECT} and ${name}; a selector is no ` +\n `column and sends nothing`,\n );\n }\n }\n }\n // HTML's own command attribute, whose `--lb-` values are Loadbare's.\n const command = el.getAttribute(\"command\");\n if (command !== null && command.startsWith(LB_COMMAND_PREFIX)) {\n if (!(LB_COMMANDS as readonly string[]).includes(command)) {\n problems.push(\n `<${tag}> carries command=\"${command}\", which begins with ` +\n `'${LB_COMMAND_PREFIX}' but names no command Loadbare provides`,\n );\n } else if (!el.hasAttribute(\"commandfor\")) {\n problems.push(\n `<${tag}> carries command=\"${command}\" and no commandfor; it ` +\n `names the element the command is for`,\n );\n }\n }\n if (el.hasAttribute(ATTR_URL_UNKNOWN) && tag !== \"dialog\") {\n problems.push(\n `<${tag}> carries ${ATTR_URL_UNKNOWN}; it goes on a <dialog>`,\n );\n }\n\n // A group is a template holding a heading and the template its\n // contents go in; an aggregate is set on an element, from the column it\n // names.\n if (el.hasAttribute(ATTR_GROUP)) {\n if (tag !== \"template\") {\n problems.push(\n `<${tag}> carries ${ATTR_GROUP}; it goes on a <template>`,\n );\n } else if (\n !(el as HTMLTemplateElement).content.querySelector(\n `template:not([${ATTR_SHOW}])`,\n )\n ) {\n problems.push(\n `a <template ${ATTR_GROUP}> holds no <template> for its contents`,\n );\n }\n }\n for (const name of AGGREGATES) {\n if (!el.hasAttribute(name)) continue;\n if (tag === \"template\") {\n problems.push(`a <template> carries ${name}; it goes on an element`);\n } else if (name !== ATTR_COUNT && !el.getAttribute(name)) {\n problems.push(`<${tag}> carries ${name} naming no column`);\n }\n }\n\n // The user's order and filter for the rows an element holds, each read\n // from a column of lb-choices it names.\n for (const name of [ATTR_ORDER, ATTR_FILTER]) {\n const value = el.getAttribute(name);\n if (value === null) continue;\n if (!el.hasAttribute(ATTR_QUERY)) {\n problems.push(\n `<${tag}> carries ${name}; it goes on the element carrying ` +\n `${ATTR_QUERY}`,\n );\n } else if (value.trim() === \"\") {\n problems.push(`<${tag}> carries ${name} naming no column`);\n }\n }\n\n // Judged by the scope around the element, not one the element carries:\n // its condition is a column of the row around it. With no lb-query\n // around it at all, nothing ever lands on it, and the element never\n // shows.\n const show = el.getAttribute(ATTR_SHOW);\n if (show !== null) {\n if (tag === \"template\") {\n problems.push(\n `a <template> carries ${ATTR_SHOW}=\"${show}\"; write it on the ` +\n `element it shows, and Loadbare supplies the template`,\n );\n } else if (scope === null || scope.row === false) {\n problems.push(\n `<${tag}> carries ${ATTR_SHOW}=\"${show}\" with no row around it ` +\n `to take the value from`,\n );\n } else if (show === SHOW_NOT || show.startsWith(SHOW_NOT + SHOW_NOT)) {\n problems.push(\n `<${tag}> carries ${ATTR_SHOW}=\"${show}\"; write one ` +\n `${SHOW_NOT} and then the column it reverses`,\n );\n }\n }\n\n walkMarkup(el, here, names, problems);\n // By tag name rather than by instanceof: template content belongs to a\n // document with no window, so the constructor is not reachable from the\n // node.\n if (tag === \"template\") {\n const content = (el as HTMLTemplateElement).content;\n const isRowTemplate =\n scope !== null && !!el.parentElement?.closest(`[${ATTR_QUERY}]`);\n if (isRowTemplate) {\n for (const root of content.children) {\n if (root.hasAttribute(ATTR_SHOW)) {\n problems.push(\n `<${root.localName}> is a row template's root and carries ` +\n `${ATTR_SHOW}; a row that should not show is left out by ` +\n `the query`,\n );\n }\n }\n }\n walkMarkup(\n content,\n isRowTemplate ? { query: scope!.query, row: true } : here,\n names,\n problems,\n );\n }\n }\n}\n\n/**\n * Ship every `lb-show` element already inside the template the hub keeps an\n * absent branch in, so nothing conditional shows until its value says so.\n * A page is in the document before its data arrives, and a value that has not\n * arrived is no value.\n *\n * Elements are collected before any is moved, and a wrapped element is still\n * reached once its outer one has gone into a template, because the references\n * are held.\n */\nfunction wrapShown(holder: HTMLTemplateElement): void {\n const doc = holder.ownerDocument;\n const shown: Element[] = [];\n const collect = (root: ParentNode): void => {\n shown.push(...root.querySelectorAll(`[${ATTR_SHOW}]`));\n for (const t of root.querySelectorAll(\"template\")) {\n collect((t as HTMLTemplateElement).content);\n }\n };\n collect(holder.content);\n for (const el of shown) {\n const parked = doc.createElement(\"template\");\n parked.setAttribute(ATTR_SHOW, el.getAttribute(ATTR_SHOW)!);\n el.replaceWith(parked);\n parked.content.append(el);\n }\n}\n\n/**\n * Expand one file's markup, naming the file if expansion refuses it.\n *\n * What comes back is the tree the browser will build, so every pass after\n * this reads and changes it in place, and it is serialized once, when it\n * ships.\n */\nfunction expandFile(\n file: string,\n source: string,\n defs: Definitions,\n): HTMLTemplateElement {\n try {\n return expandTree(source, defs);\n } catch (e) {\n throw new Error(`assemble: '${file}': ${(e as Error).message}`);\n }\n}\n\n/** Check one expanded fragment, and name the file if anything is wrong. */\nfunction checkMarkup(\n file: string,\n root: ParentNode,\n names: Names | null,\n): void {\n const problems: string[] = [];\n walkMarkup(root, null, names, problems);\n if (problems.length > 0) {\n throw new Error(`assemble: '${file}' markup:\\n ` + problems.join(`\\n `));\n }\n}\n\n/**\n * A page file's `<title>` names the page. It is lifted out, since a page is\n * a fragment inside `<main>` and a `<title>` belongs in a document's head,\n * and its text content is stamped on the page as ATTR_PAGE_TITLE.\n */\nfunction liftTitle(holder: HTMLTemplateElement): string | null {\n const title = holder.content.querySelector(\"title\");\n if (!title) return null;\n const text = title.textContent?.trim() ?? \"\";\n title.remove();\n return text;\n}\n\n/** An attribute value, escaped for a double-quoted attribute. */\nfunction attributeValue(text: string): string {\n return text.replace(/&/g, \"&\").replace(/\"/g, \""\");\n}\n\nexport interface AssembleOptions {\n chromeFile: string;\n pages: PageFile[];\n /** Every origin, already scanned, in cascade order — see build/origins.ts. */\n scans: OriginScan[];\n /**\n * What each page declares — see build/declared.ts. Without it, the names\n * `lb-query` and `lb-request` carry are not checked.\n */\n declared?: DeclaredPages;\n}\n\nexport interface Assembled {\n html: string;\n /** Every custom element tag left in the document once expansion is done. */\n usedTags: Set<string>;\n}\n\nexport async function assemble({\n chromeFile,\n pages,\n scans,\n declared,\n}: AssembleOptions): Promise<Assembled> {\n // One layer per origin, in order, so a later origin's definition overrides\n // an earlier one's for the same tag. Each layer is checked for cycles as it\n // lands, against everything merged so far.\n let defs: Definitions = {};\n for (const scan of scans) {\n defs = await addDefinitions(defs, scan.definitions);\n }\n\n // Tags are collected from each tree as it goes by. What ships is these\n // trees and the chrome around its body, which is collected on its own.\n const usedTags = new Set<string>();\n const templates: string[] = [];\n for (const { name, file } of pages) {\n const authored = await readFile(file, \"utf-8\");\n const host = expandFile(file, authored, defs);\n const title = liftTitle(host);\n checkPage(file, host.content);\n checkMarkup(\n file,\n host.content,\n declared ? { declared: declared.get(name)! } : null,\n );\n wrapShown(host);\n customElementTags(host.content, usedTags);\n const titled =\n title === null ? \"\" : ` ${ATTR_PAGE_TITLE}=\"${attributeValue(title)}\"`;\n templates.push(\n `<template ${ATTR_PAGE}=\"${name}\"${titled}>${host.innerHTML}</template>`,\n );\n }\n\n let chromeSource: string;\n try {\n chromeSource = await readFile(chromeFile, \"utf-8\");\n } catch {\n throw new Error(`assemble: no chrome file at '${chromeFile}'`);\n }\n\n const body = /<body([^>]*)>([\\s\\S]*)<\\/body>/i.exec(chromeSource);\n if (!body) {\n throw new Error(\n `assemble: '${chromeFile}' has no <body>...</body> to expand`,\n );\n }\n const [whole, bodyAttrs, bodyContent] = body;\n const chromeBody = expandFile(chromeFile, bodyContent!, defs);\n checkChrome(chromeFile, chromeBody.content);\n checkUnknownPageDialog(chromeFile, chromeBody.content);\n checkMarkup(\n chromeFile,\n chromeBody.content,\n declared ? { declared: null } : null,\n );\n wrapShown(chromeBody);\n customElementTags(chromeBody.content, usedTags);\n customElementTags(chromeSource.replace(whole!, \"\"), usedTags);\n const withPages = chromeSource.replace(\n whole!,\n () =>\n `<body${bodyAttrs}>${chromeBody.innerHTML}\\n${templates.join(\"\\n\")}\\n</body>`,\n );\n\n return { html: await formatHtml(withPages), usedTags };\n}\n"]}
|
|
1
|
+
{"version":3,"file":"assemble.js","sourceRoot":"","sources":["../../build/assemble.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Assembling one document out of a chrome file and a set of pages.\n *\n * Every `.html` file in play — the chrome and each page — goes through the\n * same `expand()` (see build/expand.ts) against the same definitions: the\n * built-in widgets and whatever definitions were found in the caller's own\n * source tree. `expand()` only ever sees a fragment, so the chrome's own is\n * the text between its `<body>` tags; a page's is the whole file. A page's\n * expanded host is wrapped in `<template lb-page=\"<name>\">` and, together\n * with the chrome's own expanded body content, spliced back into the\n * chrome's `<body>`. The result is the one document a server has to serve\n * for every route — see docs/tutorials/000-getting-started.md and\n * docs/reference/builder.md.\n *\n * Every path here is a parameter, resolved once by build/locations.ts — this\n * module never reconstructs one of its own, and never reads a directory\n * looking for pages or widgets; it is handed exactly which files those are.\n */\n\nimport { readFile } from \"node:fs/promises\";\nimport {\n addDefinitions,\n customElementTags,\n expandTree,\n type Definitions,\n} from \"./expand.js\";\nimport { formatHtml } from \"./format.js\";\nimport {\n AGGREGATES,\n ATTR_FILTER,\n ATTR_ORDER,\n ATTR_COLUMN,\n ATTR_COUNT,\n ATTR_GROUP,\n ATTR_PAGE,\n ATTR_PAGE_TITLE,\n ATTR_QUERY,\n ATTR_REQUEST,\n ATTR_SELECT,\n ATTR_SHOW,\n ATTR_URL_LINK,\n ATTR_URL_PUSH,\n ATTR_URL_PARM,\n ATTR_URL_UNKNOWN,\n CHOICES_QUERY,\n HUB_QUERY,\n DEVELOPER_ATTRIBUTES,\n HUB_TAG_NAME,\n LB_COMMAND_PREFIX,\n LB_COMMANDS,\n LB_RESERVED_PREFIX,\n REQUEST_ROW_DELETE,\n REQUEST_ROW_INSERT,\n REQUEST_ROW_UPDATE,\n SHOW_NOT,\n URL_QUERY,\n} from \"../core/lb-constants.js\";\nimport { isRowRequest } from \"../core/lb-types.js\";\nimport type { Declared, DeclaredPages } from \"./declared.js\";\nimport type { PageFile } from \"./locations.js\";\nimport type { OriginScan } from \"./origins.js\";\n\n/**\n * The hub is one element, and it swaps each page into the one `<main>`\n * inside it (hub/lb-hub.browser.ts), so the chrome holds exactly one of each,\n * the `<main>` inside the hub and empty, since whatever it held would be\n * replaced by the first page. A page is swapped into that `<main>`, so it\n * carries neither.\n */\nfunction checkChrome(chromeFile: string, body: ParentNode): void {\n const where = `assemble: '${chromeFile}'`;\n const hubs = body.querySelectorAll(HUB_TAG_NAME).length;\n if (hubs !== 1) {\n throw new Error(\n `${where} has ${hubs} <${HUB_TAG_NAME}>; it needs exactly one`,\n );\n }\n const mains = body.querySelectorAll(\"main\");\n if (mains.length !== 1) {\n throw new Error(\n `${where} has ${mains.length} <main>; it needs exactly one`,\n );\n }\n const main = mains[0]!;\n if (!main.closest(HUB_TAG_NAME)) {\n throw new Error(\n `${where} has its <main> outside <${HUB_TAG_NAME}>, where the hub cannot reach it`,\n );\n }\n if (main.children.length > 0 || main.textContent!.trim() !== \"\") {\n throw new Error(\n `${where} has content in its <main>, which the first page replaces; it goes empty`,\n );\n }\n}\n\n/** A page lands inside the chrome's `<main>`, so it brings no second one and no hub. */\nfunction checkPage(file: string, root: ParentNode): void {\n for (const tag of [HUB_TAG_NAME, \"main\"]) {\n if (root.querySelector(tag)) {\n throw new Error(\n `assemble: '${file}' carries a <${tag}>, which belongs to the chrome alone`,\n );\n }\n }\n}\n\n/**\n * The hub calls showModal() on the `<dialog>` carrying lb-url-unknown\n * (hub/lb-hub.browser.ts), and looks for it inside itself, as it does for\n * everything else it acts on. A dialog elsewhere in the body would never open\n * and would say nothing about why.\n */\nfunction checkUnknownPageDialog(chromeFile: string, body: ParentNode): void {\n const el = body.querySelector(`[${ATTR_URL_UNKNOWN}]`);\n if (!el) return;\n if (!el.closest(HUB_TAG_NAME)) {\n throw new Error(\n `assemble: '${chromeFile}' declares ${ATTR_URL_UNKNOWN} outside ` +\n `<${HUB_TAG_NAME}>, where the hub cannot reach it`,\n );\n }\n}\n\n/**\n * Where the markup stands while the check walks it: inside which `lb-query`,\n * and whether there is a row there to take a value from. Inside a row\n * template there always is. Inside an `lb-query` outside one, there is when\n * the query is of kind `row`, which only the server knows.\n */\ninterface Scope {\n query: string;\n row: boolean | \"unknown\";\n}\n\n/** The queries the hub serves, which every page and the chrome may name. */\nconst HUB_QUERIES: readonly string[] = [URL_QUERY, CHOICES_QUERY, HUB_QUERY];\n\n/** The `Crud` key each request Loadbare provides runs. */\nconst CRUD_KEYS: Record<string, string> = {\n [REQUEST_ROW_INSERT]: \"rowInsert\",\n [REQUEST_ROW_UPDATE]: \"rowUpdate\",\n [REQUEST_ROW_DELETE]: \"rowDelete\",\n};\n\n/**\n * What a file's names are checked against: a page's declarations, or `null`\n * for the chrome, which belongs to no page and so names only what the hub\n * serves.\n */\ntype Names = { declared: Declared | null };\n\n/**\n * An `lb-query` or `lb-request` naming nothing the page declares would be\n * found only on the first round trip, as a server warning and an empty\n * answer, so a typo would build cleanly.\n *\n * A request Loadbare provides is for the query of what it gathers: a form's\n * own when the form carries `lb-query`, otherwise the nearest around the\n * element, as the hub decides it (hub/lb-hub.browser.ts). With no query\n * around it, the hub reports it in the browser.\n */\nfunction checkNames(\n el: Element,\n scope: Scope | null,\n names: Names,\n problems: string[],\n): void {\n const tag = el.localName;\n const { declared } = names;\n\n const query = el.getAttribute(ATTR_QUERY);\n if (query !== null && !HUB_QUERIES.includes(query)) {\n if (declared === null) {\n problems.push(\n `<${tag}> carries ${ATTR_QUERY}=\"${query}\"; the chrome belongs to ` +\n `no page, and names only ${HUB_QUERIES.slice(0, -1).join(\", \")} and ${HUB_QUERIES.at(-1)}`,\n );\n } else if (!declared.queries.has(query)) {\n problems.push(\n `<${tag}> carries ${ATTR_QUERY}=\"${query}\", which the page's ` +\n `queries do not declare`,\n );\n }\n }\n\n const request = el.getAttribute(ATTR_REQUEST);\n if (!request) return;\n if (isRowRequest(request)) {\n const target =\n tag === \"form\" && query !== null ? query : (scope?.query ?? null);\n if (target === null) return;\n if (target === HUB_QUERY) {\n problems.push(\n `<${tag}> carries ${ATTR_REQUEST}=\"${request}\" for '${target}'; ` +\n `the hub's own state is written by the hub alone`,\n );\n return;\n }\n if (HUB_QUERIES.includes(target)) {\n if (request !== REQUEST_ROW_UPDATE) {\n problems.push(\n `<${tag}> carries ${ATTR_REQUEST}=\"${request}\" for '${target}'; ` +\n `the hub answers only ${REQUEST_ROW_UPDATE} for it`,\n );\n }\n return;\n }\n // A query the page does not declare is reported once, where it is named.\n if (declared === null || !declared.queries.has(target)) return;\n const key = CRUD_KEYS[request]!;\n if (!declared.crud.get(target)?.has(key)) {\n problems.push(\n `<${tag}> carries ${ATTR_REQUEST}=\"${request}\" for '${target}', ` +\n `whose crud entry the page does not declare with ${key}`,\n );\n }\n } else if (request.startsWith(LB_RESERVED_PREFIX)) {\n // Reported by walkMarkup as naming no request Loadbare provides.\n } else if (declared === null) {\n problems.push(\n `<${tag}> carries ${ATTR_REQUEST}=\"${request}\"; the chrome belongs ` +\n `to no page, and sends no declared request`,\n );\n } else if (!declared.handlers.has(request)) {\n problems.push(\n `<${tag}> carries ${ATTR_REQUEST}=\"${request}\", which the page's ` +\n `handlers do not declare`,\n );\n }\n}\n\n/**\n * The markup, checked once against the finished document rather than\n * discovered one console error at a time in a browser.\n *\n * Every rule here is a fact the markup states about itself. The developer\n * writes `lb-*` attributes in markup, and script never assigns them, which is\n * what makes checking the markup sound. See docs/reference/data-binding.md.\n *\n * A `<template>`'s children are walked in the scope that encloses the\n * template, because a row template's clones land in the element around it.\n */\nfunction walkMarkup(\n node: ParentNode,\n scope: Scope | null,\n names: Names | null,\n problems: string[],\n): void {\n for (const el of node.children) {\n const tag = el.localName;\n let here = scope;\n const query = el.getAttribute(ATTR_QUERY);\n if (query !== null) here = { query, row: \"unknown\" };\n if (names) checkNames(el, scope, names, problems);\n\n for (const { name } of el.attributes) {\n if (\n name.startsWith(LB_RESERVED_PREFIX) &&\n !(DEVELOPER_ATTRIBUTES as readonly string[]).includes(name)\n ) {\n problems.push(\n `<${tag}> carries ${name}, which is not an attribute a developer ` +\n `writes`,\n );\n }\n }\n\n const request = el.getAttribute(ATTR_REQUEST);\n if (\n request !== null &&\n request.startsWith(LB_RESERVED_PREFIX) &&\n !isRowRequest(request)\n ) {\n problems.push(\n `<${tag}> carries ${ATTR_REQUEST}=\"${request}\", which begins with ` +\n `'${LB_RESERVED_PREFIX}' but names no request Loadbare provides`,\n );\n }\n if (el.hasAttribute(ATTR_URL_LINK) && tag !== \"a\") {\n problems.push(`<${tag}> carries ${ATTR_URL_LINK}; it goes on an <a>`);\n }\n // A link's parms are columns of the row around it, and only a link the\n // hub handles takes them: the parms are the application's own URL.\n const parms = el.getAttribute(ATTR_URL_PARM);\n if (parms !== null) {\n const columns = parms.split(\",\").map((column) => column.trim());\n if (tag !== \"a\" || !el.hasAttribute(ATTR_URL_LINK)) {\n problems.push(\n `<${tag}> carries ${ATTR_URL_PARM}; it goes on an <a> carrying ` +\n `${ATTR_URL_LINK}`,\n );\n } else if (columns.some((column) => column === \"\")) {\n problems.push(\n `<${tag}> carries ${ATTR_URL_PARM}=\"${parms}\", which names no ` +\n `column between two commas or at either end`,\n );\n } else if (columns.some((column) => /\\s/.test(column))) {\n problems.push(\n `<${tag}> carries ${ATTR_URL_PARM}=\"${parms}\"; columns are ` +\n `separated by commas`,\n );\n } else if (columns.some((c) => c.startsWith(LB_RESERVED_PREFIX))) {\n problems.push(\n `<${tag}> carries ${ATTR_URL_PARM}=\"${parms}\"; a parm beginning ` +\n `'${LB_RESERVED_PREFIX}' is the hub's own`,\n );\n } else if (scope === null || scope.row === false) {\n problems.push(\n `<${tag}> carries ${ATTR_URL_PARM}=\"${parms}\" with no row around ` +\n `it to take the values from`,\n );\n }\n }\n if (el.hasAttribute(ATTR_URL_PUSH) && request === null) {\n problems.push(\n `<${tag}> carries ${ATTR_URL_PUSH} and no ${ATTR_REQUEST}; it goes ` +\n `on an element with ${ATTR_REQUEST}`,\n );\n }\n // A selector is a checkbox in a live row, and selects that row. It is no\n // column, and commits nothing of its own.\n if (el.hasAttribute(ATTR_SELECT)) {\n if (tag !== \"input\" || el.getAttribute(\"type\") !== \"checkbox\") {\n problems.push(\n `<${tag}> carries ${ATTR_SELECT}; it goes on an ` +\n `<input type=\"checkbox\">`,\n );\n } else if (scope?.row !== true) {\n problems.push(\n `<input> carries ${ATTR_SELECT} outside a row template; it ` +\n `selects the row it sits in`,\n );\n }\n for (const name of [ATTR_COLUMN, ATTR_REQUEST]) {\n if (el.hasAttribute(name)) {\n problems.push(\n `<${tag}> carries ${ATTR_SELECT} and ${name}; a selector is no ` +\n `column and sends nothing`,\n );\n }\n }\n }\n // HTML's own command attribute, whose `--lb-` values are Loadbare's.\n const command = el.getAttribute(\"command\");\n if (command !== null && command.startsWith(LB_COMMAND_PREFIX)) {\n if (!(LB_COMMANDS as readonly string[]).includes(command)) {\n problems.push(\n `<${tag}> carries command=\"${command}\", which begins with ` +\n `'${LB_COMMAND_PREFIX}' but names no command Loadbare provides`,\n );\n } else if (!el.hasAttribute(\"commandfor\")) {\n problems.push(\n `<${tag}> carries command=\"${command}\" and no commandfor; it ` +\n `names the element the command is for`,\n );\n }\n }\n if (el.hasAttribute(ATTR_URL_UNKNOWN) && tag !== \"dialog\") {\n problems.push(\n `<${tag}> carries ${ATTR_URL_UNKNOWN}; it goes on a <dialog>`,\n );\n }\n\n // A group is a template holding a heading and the template its\n // contents go in; an aggregate is set on an element, from the column it\n // names.\n if (el.hasAttribute(ATTR_GROUP)) {\n if (tag !== \"template\") {\n problems.push(\n `<${tag}> carries ${ATTR_GROUP}; it goes on a <template>`,\n );\n } else if (\n !(el as HTMLTemplateElement).content.querySelector(\n `template:not([${ATTR_SHOW}])`,\n )\n ) {\n problems.push(\n `a <template ${ATTR_GROUP}> holds no <template> for its contents`,\n );\n }\n }\n for (const name of AGGREGATES) {\n if (!el.hasAttribute(name)) continue;\n if (tag === \"template\") {\n problems.push(`a <template> carries ${name}; it goes on an element`);\n } else if (name !== ATTR_COUNT && !el.getAttribute(name)) {\n problems.push(`<${tag}> carries ${name} naming no column`);\n }\n }\n\n // The user's order and filter for the rows an element holds, each read\n // from a column of lb-choices it names.\n for (const name of [ATTR_ORDER, ATTR_FILTER]) {\n const value = el.getAttribute(name);\n if (value === null) continue;\n if (!el.hasAttribute(ATTR_QUERY)) {\n problems.push(\n `<${tag}> carries ${name}; it goes on the element carrying ` +\n `${ATTR_QUERY}`,\n );\n } else if (value.trim() === \"\") {\n problems.push(`<${tag}> carries ${name} naming no column`);\n }\n }\n\n // Judged by the scope around the element, not one the element carries:\n // its condition is a column of the row around it. With no lb-query\n // around it at all, nothing ever lands on it, and the element never\n // shows.\n const show = el.getAttribute(ATTR_SHOW);\n if (show !== null) {\n if (tag === \"template\") {\n problems.push(\n `a <template> carries ${ATTR_SHOW}=\"${show}\"; write it on the ` +\n `element it shows, and Loadbare supplies the template`,\n );\n } else if (scope === null || scope.row === false) {\n problems.push(\n `<${tag}> carries ${ATTR_SHOW}=\"${show}\" with no row around it ` +\n `to take the value from`,\n );\n } else if (show === SHOW_NOT || show.startsWith(SHOW_NOT + SHOW_NOT)) {\n problems.push(\n `<${tag}> carries ${ATTR_SHOW}=\"${show}\"; write one ` +\n `${SHOW_NOT} and then the column it reverses`,\n );\n }\n }\n\n walkMarkup(el, here, names, problems);\n // By tag name rather than by instanceof: template content belongs to a\n // document with no window, so the constructor is not reachable from the\n // node.\n if (tag === \"template\") {\n const content = (el as HTMLTemplateElement).content;\n const isRowTemplate =\n scope !== null && !!el.parentElement?.closest(`[${ATTR_QUERY}]`);\n if (isRowTemplate) {\n for (const root of content.children) {\n if (root.hasAttribute(ATTR_SHOW)) {\n problems.push(\n `<${root.localName}> is a row template's root and carries ` +\n `${ATTR_SHOW}; a row that should not show is left out by ` +\n `the query`,\n );\n }\n }\n }\n walkMarkup(\n content,\n isRowTemplate ? { query: scope!.query, row: true } : here,\n names,\n problems,\n );\n }\n }\n}\n\n/**\n * Ship every `lb-show` element already inside the template the hub keeps an\n * absent branch in, so nothing conditional shows until its value says so.\n * A page is in the document before its data arrives, and a value that has not\n * arrived is no value.\n *\n * Elements are collected before any is moved, and a wrapped element is still\n * reached once its outer one has gone into a template, because the references\n * are held.\n */\nfunction wrapShown(holder: HTMLTemplateElement): void {\n const doc = holder.ownerDocument;\n const shown: Element[] = [];\n const collect = (root: ParentNode): void => {\n shown.push(...root.querySelectorAll(`[${ATTR_SHOW}]`));\n for (const t of root.querySelectorAll(\"template\")) {\n collect((t as HTMLTemplateElement).content);\n }\n };\n collect(holder.content);\n for (const el of shown) {\n const parked = doc.createElement(\"template\");\n parked.setAttribute(ATTR_SHOW, el.getAttribute(ATTR_SHOW)!);\n el.replaceWith(parked);\n parked.content.append(el);\n }\n}\n\n/**\n * Expand one file's markup, naming the file if expansion refuses it.\n *\n * What comes back is the tree the browser will build, so every pass after\n * this reads and changes it in place, and it is serialized once, when it\n * ships.\n */\nfunction expandFile(\n file: string,\n source: string,\n defs: Definitions,\n): HTMLTemplateElement {\n try {\n return expandTree(source, defs);\n } catch (e) {\n throw new Error(`assemble: '${file}': ${(e as Error).message}`);\n }\n}\n\n/** Check one expanded fragment, and name the file if anything is wrong. */\nfunction checkMarkup(\n file: string,\n root: ParentNode,\n names: Names | null,\n): void {\n const problems: string[] = [];\n walkMarkup(root, null, names, problems);\n if (problems.length > 0) {\n throw new Error(`assemble: '${file}' markup:\\n ` + problems.join(`\\n `));\n }\n}\n\n/**\n * A page file's `<title>` names the page. It is lifted out, since a page is\n * a fragment inside `<main>` and a `<title>` belongs in a document's head,\n * and its text content is stamped on the page as ATTR_PAGE_TITLE.\n */\nfunction liftTitle(holder: HTMLTemplateElement): string | null {\n const title = holder.content.querySelector(\"title\");\n if (!title) return null;\n const text = title.textContent?.trim() ?? \"\";\n title.remove();\n return text;\n}\n\n/** An attribute value, escaped for a double-quoted attribute. */\nfunction attributeValue(text: string): string {\n return text.replace(/&/g, \"&\").replace(/\"/g, \""\");\n}\n\nexport interface AssembleOptions {\n chromeFile: string;\n pages: PageFile[];\n /** Every origin, already scanned, in cascade order — see build/origins.ts. */\n scans: OriginScan[];\n /**\n * What each page declares — see build/declared.ts. Without it, the names\n * `lb-query` and `lb-request` carry are not checked.\n */\n declared?: DeclaredPages;\n}\n\nexport interface Assembled {\n html: string;\n /** Every custom element tag left in the document once expansion is done. */\n usedTags: Set<string>;\n}\n\nexport async function assemble({\n chromeFile,\n pages,\n scans,\n declared,\n}: AssembleOptions): Promise<Assembled> {\n // One layer per origin, in order, so a later origin's definition overrides\n // an earlier one's for the same tag. Each layer is checked for cycles as it\n // lands, against everything merged so far.\n let defs: Definitions = {};\n for (const scan of scans) {\n defs = await addDefinitions(defs, scan.definitions);\n }\n\n // Tags are collected from each tree as it goes by. What ships is these\n // trees and the chrome around its body, which is collected on its own.\n const usedTags = new Set<string>();\n const templates: string[] = [];\n for (const { name, file } of pages) {\n const authored = await readFile(file, \"utf-8\");\n const host = expandFile(file, authored, defs);\n const title = liftTitle(host);\n checkPage(file, host.content);\n checkMarkup(\n file,\n host.content,\n declared ? { declared: declared.get(name)! } : null,\n );\n wrapShown(host);\n customElementTags(host.content, usedTags);\n const titled =\n title === null ? \"\" : ` ${ATTR_PAGE_TITLE}=\"${attributeValue(title)}\"`;\n templates.push(\n `<template ${ATTR_PAGE}=\"${name}\"${titled}>${host.innerHTML}</template>`,\n );\n }\n\n let chromeSource: string;\n try {\n chromeSource = await readFile(chromeFile, \"utf-8\");\n } catch {\n throw new Error(`assemble: no chrome file at '${chromeFile}'`);\n }\n\n const body = /<body([^>]*)>([\\s\\S]*)<\\/body>/i.exec(chromeSource);\n if (!body) {\n throw new Error(\n `assemble: '${chromeFile}' has no <body>...</body> to expand`,\n );\n }\n const [whole, bodyAttrs, bodyContent] = body;\n const chromeBody = expandFile(chromeFile, bodyContent!, defs);\n checkChrome(chromeFile, chromeBody.content);\n checkUnknownPageDialog(chromeFile, chromeBody.content);\n checkMarkup(\n chromeFile,\n chromeBody.content,\n declared ? { declared: null } : null,\n );\n wrapShown(chromeBody);\n customElementTags(chromeBody.content, usedTags);\n customElementTags(chromeSource.replace(whole!, \"\"), usedTags);\n const withPages = chromeSource.replace(\n whole!,\n () =>\n `<body${bodyAttrs}>${chromeBody.innerHTML}\\n${templates.join(\"\\n\")}\\n</body>`,\n );\n\n return { html: await formatHtml(withPages), usedTags };\n}\n"]}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The build, as a library: one build, or a watch that rebuilds and says when
|
|
3
|
+
* each build has ended. `loadbare-app-build` (build/cli.ts) is a thin
|
|
4
|
+
* command over these, and `@loadbare/app/dev` runs the watch in process.
|
|
5
|
+
*
|
|
6
|
+
* Paths resolve against the working directory, as the command's do, so a
|
|
7
|
+
* caller runs from the application's root.
|
|
8
|
+
*
|
|
9
|
+
* A build writes nothing until every output is in hand. One that fails
|
|
10
|
+
* leaves the last good build in place, so a server reading `dist/` keeps
|
|
11
|
+
* serving something coherent while the developer fixes the mistake.
|
|
12
|
+
*/
|
|
13
|
+
export interface BuildOptions {
|
|
14
|
+
/** The application's source tree, `src` when absent. */
|
|
15
|
+
src?: string;
|
|
16
|
+
/** Where the output lands, `dist` when absent. */
|
|
17
|
+
out?: string;
|
|
18
|
+
/** Minify `client.js` and `app.css`. */
|
|
19
|
+
minify?: boolean;
|
|
20
|
+
}
|
|
21
|
+
export interface Built {
|
|
22
|
+
/** Every file the build wrote. */
|
|
23
|
+
files: string[];
|
|
24
|
+
/** How many custom elements `client.js` registers. */
|
|
25
|
+
elements: number;
|
|
26
|
+
/** One line saying what was written, as the command prints it. */
|
|
27
|
+
summary: string;
|
|
28
|
+
}
|
|
29
|
+
/** Build once. Throws, having written nothing, when the build fails. */
|
|
30
|
+
export declare function buildApp(options?: BuildOptions): Promise<Built>;
|
|
31
|
+
/** How one build of a watch ended. */
|
|
32
|
+
export type BuildOutcome = {
|
|
33
|
+
ok: true;
|
|
34
|
+
changed: string[];
|
|
35
|
+
built: Built;
|
|
36
|
+
} | {
|
|
37
|
+
ok: false;
|
|
38
|
+
changed: string[];
|
|
39
|
+
error: Error;
|
|
40
|
+
};
|
|
41
|
+
export interface WatchOptions extends BuildOptions {
|
|
42
|
+
/**
|
|
43
|
+
* A build is starting, for these changed files under `src`, relative to
|
|
44
|
+
* it. Empty for the first build.
|
|
45
|
+
*/
|
|
46
|
+
onStart?: (changed: string[]) => void;
|
|
47
|
+
/** A build has ended, well or badly. A failed build does not stop the watch. */
|
|
48
|
+
onBuilt?: (outcome: BuildOutcome) => void;
|
|
49
|
+
}
|
|
50
|
+
export interface AppWatcher {
|
|
51
|
+
/** How the first build ended. */
|
|
52
|
+
ready: Promise<BuildOutcome>;
|
|
53
|
+
/** Stop watching. A build already running finishes. */
|
|
54
|
+
close(): void;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Build, then build again whenever a file under `src` changes. Changes that
|
|
58
|
+
* arrive while a build runs are gathered into one build after it, so builds
|
|
59
|
+
* never overlap and never fall behind.
|
|
60
|
+
*/
|
|
61
|
+
export declare function watchApp(options?: WatchOptions): AppWatcher;
|
|
62
|
+
//# sourceMappingURL=build.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"build.d.ts","sourceRoot":"","sources":["../../build/build.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAkBH,MAAM,WAAW,YAAY;IAC3B,wDAAwD;IACxD,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,kDAAkD;IAClD,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,wCAAwC;IACxC,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED,MAAM,WAAW,KAAK;IACpB,kCAAkC;IAClC,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,sDAAsD;IACtD,QAAQ,EAAE,MAAM,CAAC;IACjB,kEAAkE;IAClE,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,wEAAwE;AACxE,wBAAsB,QAAQ,CAAC,OAAO,GAAE,YAAiB,GAAG,OAAO,CAAC,KAAK,CAAC,CAgEzE;AAED,sCAAsC;AACtC,MAAM,MAAM,YAAY,GACpB;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,OAAO,EAAE,MAAM,EAAE,CAAC;IAAC,KAAK,EAAE,KAAK,CAAA;CAAE,GAC7C;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,OAAO,EAAE,MAAM,EAAE,CAAC;IAAC,KAAK,EAAE,KAAK,CAAA;CAAE,CAAC;AAEnD,MAAM,WAAW,YAAa,SAAQ,YAAY;IAChD;;;OAGG;IACH,OAAO,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,IAAI,CAAC;IACtC,gFAAgF;IAChF,OAAO,CAAC,EAAE,CAAC,OAAO,EAAE,YAAY,KAAK,IAAI,CAAC;CAC3C;AAED,MAAM,WAAW,UAAU;IACzB,iCAAiC;IACjC,KAAK,EAAE,OAAO,CAAC,YAAY,CAAC,CAAC;IAC7B,uDAAuD;IACvD,KAAK,IAAI,IAAI,CAAC;CACf;AAYD;;;;GAIG;AACH,wBAAgB,QAAQ,CAAC,OAAO,GAAE,YAAiB,GAAG,UAAU,CA6D/D"}
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The build, as a library: one build, or a watch that rebuilds and says when
|
|
3
|
+
* each build has ended. `loadbare-app-build` (build/cli.ts) is a thin
|
|
4
|
+
* command over these, and `@loadbare/app/dev` runs the watch in process.
|
|
5
|
+
*
|
|
6
|
+
* Paths resolve against the working directory, as the command's do, so a
|
|
7
|
+
* caller runs from the application's root.
|
|
8
|
+
*
|
|
9
|
+
* A build writes nothing until every output is in hand. One that fails
|
|
10
|
+
* leaves the last good build in place, so a server reading `dist/` keeps
|
|
11
|
+
* serving something coherent while the developer fixes the mistake.
|
|
12
|
+
*/
|
|
13
|
+
import { watch as watchFiles } from "node:fs";
|
|
14
|
+
import { mkdir, writeFile } from "node:fs/promises";
|
|
15
|
+
import path from "node:path";
|
|
16
|
+
import * as esbuild from "esbuild";
|
|
17
|
+
import { assemble } from "./assemble.js";
|
|
18
|
+
import { loadDeclared } from "./declared.js";
|
|
19
|
+
import { checkReservedMethods, clientEntrySource, resolveElements, } from "./elements.js";
|
|
20
|
+
import { resolveLocations } from "./locations.js";
|
|
21
|
+
import { cssFrom, scanAll } from "./origins.js";
|
|
22
|
+
import { pagesSource } from "./pages.js";
|
|
23
|
+
import { stylesSource } from "./styles.js";
|
|
24
|
+
/** Build once. Throws, having written nothing, when the build fails. */
|
|
25
|
+
export async function buildApp(options = {}) {
|
|
26
|
+
const src = options.src ?? "src";
|
|
27
|
+
const minify = options.minify ?? false;
|
|
28
|
+
const locations = await resolveLocations({ src, out: options.out ?? "dist" });
|
|
29
|
+
const { outDir } = locations;
|
|
30
|
+
const entryFile = path.join(outDir, "client-entry.ts");
|
|
31
|
+
const scans = await scanAll(locations, src, locations.importsFile);
|
|
32
|
+
await mkdir(outDir, { recursive: true });
|
|
33
|
+
const declared = await loadDeclared(locations.pages, locations.pageSources, outDir);
|
|
34
|
+
const { html, usedTags } = await assemble({ ...locations, scans, declared });
|
|
35
|
+
const { resolved } = resolveElements({ used: usedTags, scans });
|
|
36
|
+
await checkReservedMethods(resolved);
|
|
37
|
+
// The entry is read by the bundler and never served, so it is written now.
|
|
38
|
+
await writeFile(entryFile, clientEntrySource(resolved, outDir), "utf-8");
|
|
39
|
+
const bundle = await esbuild.build({
|
|
40
|
+
entryPoints: [entryFile],
|
|
41
|
+
bundle: true,
|
|
42
|
+
format: "iife",
|
|
43
|
+
outfile: path.join(outDir, "client.js"),
|
|
44
|
+
minify,
|
|
45
|
+
write: false,
|
|
46
|
+
// esbuild keeps the directory it started in; the paths are the caller's.
|
|
47
|
+
absWorkingDir: process.cwd(),
|
|
48
|
+
});
|
|
49
|
+
const outputs = new Map();
|
|
50
|
+
outputs.set(path.join(outDir, "app.html"), html);
|
|
51
|
+
for (const file of bundle.outputFiles)
|
|
52
|
+
outputs.set(file.path, file.contents);
|
|
53
|
+
const pages = pagesSource(locations.pages, locations.pageSources, outDir, locations.packageName);
|
|
54
|
+
if (pages)
|
|
55
|
+
outputs.set(path.join(outDir, "pages.ts"), pages);
|
|
56
|
+
const css = await stylesSource(cssFrom(scans));
|
|
57
|
+
if (css) {
|
|
58
|
+
const code = minify
|
|
59
|
+
? (await esbuild.transform(css, { loader: "css", minify: true })).code
|
|
60
|
+
: css;
|
|
61
|
+
outputs.set(path.join(outDir, "app.css"), code);
|
|
62
|
+
}
|
|
63
|
+
for (const [file, content] of outputs)
|
|
64
|
+
await writeFile(file, content);
|
|
65
|
+
const files = [...outputs.keys()].map((file) => path.relative(process.cwd(), file));
|
|
66
|
+
const elements = resolved.size;
|
|
67
|
+
return {
|
|
68
|
+
files,
|
|
69
|
+
elements,
|
|
70
|
+
summary: `wrote ${files.join(" and ")} ` +
|
|
71
|
+
`(${elements} custom element${elements === 1 ? "" : "s"})`,
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
/** The files a build reads. A change to any other file starts no build. */
|
|
75
|
+
const BUILT_FROM = /\.(html|ts|css)$/;
|
|
76
|
+
/**
|
|
77
|
+
* Edits arrive in bursts: an editor's save is often several events, and a
|
|
78
|
+
* branch switch is hundreds. Waiting this long after the first lets the
|
|
79
|
+
* burst finish, so it starts one build.
|
|
80
|
+
*/
|
|
81
|
+
const SETTLE_MS = 20;
|
|
82
|
+
/**
|
|
83
|
+
* Build, then build again whenever a file under `src` changes. Changes that
|
|
84
|
+
* arrive while a build runs are gathered into one build after it, so builds
|
|
85
|
+
* never overlap and never fall behind.
|
|
86
|
+
*/
|
|
87
|
+
export function watchApp(options = {}) {
|
|
88
|
+
const src = options.src ?? "src";
|
|
89
|
+
// An output directory inside the source tree is written by every build,
|
|
90
|
+
// and a change there must not start the next one.
|
|
91
|
+
const out = path.relative(path.resolve(src), path.resolve(options.out ?? "dist"));
|
|
92
|
+
const inOut = (file) => out !== "" && !out.startsWith("..") && (file === out || file.startsWith(out + path.sep));
|
|
93
|
+
let pending = new Set();
|
|
94
|
+
let running = false;
|
|
95
|
+
let timer;
|
|
96
|
+
let closed = false;
|
|
97
|
+
const once = async (changed) => {
|
|
98
|
+
options.onStart?.(changed);
|
|
99
|
+
let outcome;
|
|
100
|
+
try {
|
|
101
|
+
outcome = { ok: true, changed, built: await buildApp(options) };
|
|
102
|
+
}
|
|
103
|
+
catch (err) {
|
|
104
|
+
const error = err instanceof Error ? err : new Error(String(err));
|
|
105
|
+
outcome = { ok: false, changed, error };
|
|
106
|
+
}
|
|
107
|
+
options.onBuilt?.(outcome);
|
|
108
|
+
return outcome;
|
|
109
|
+
};
|
|
110
|
+
const drain = async () => {
|
|
111
|
+
running = true;
|
|
112
|
+
while (pending.size > 0 && !closed) {
|
|
113
|
+
const changed = [...pending].sort();
|
|
114
|
+
pending = new Set();
|
|
115
|
+
await once(changed);
|
|
116
|
+
}
|
|
117
|
+
running = false;
|
|
118
|
+
};
|
|
119
|
+
const ready = once([]).then((outcome) => {
|
|
120
|
+
if (pending.size > 0)
|
|
121
|
+
void drain();
|
|
122
|
+
return outcome;
|
|
123
|
+
});
|
|
124
|
+
const watcher = watchFiles(src, { recursive: true }, (_event, file) => {
|
|
125
|
+
if (closed || !file || !BUILT_FROM.test(file) || inOut(file))
|
|
126
|
+
return;
|
|
127
|
+
pending.add(file.split(path.sep).join("/"));
|
|
128
|
+
if (running || timer)
|
|
129
|
+
return;
|
|
130
|
+
timer = setTimeout(() => {
|
|
131
|
+
timer = undefined;
|
|
132
|
+
// The first build is still running: it starts the next when it ends.
|
|
133
|
+
void ready.then(() => {
|
|
134
|
+
if (!running)
|
|
135
|
+
void drain();
|
|
136
|
+
});
|
|
137
|
+
}, SETTLE_MS);
|
|
138
|
+
});
|
|
139
|
+
return {
|
|
140
|
+
ready,
|
|
141
|
+
close() {
|
|
142
|
+
closed = true;
|
|
143
|
+
clearTimeout(timer);
|
|
144
|
+
watcher.close();
|
|
145
|
+
},
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
//# sourceMappingURL=build.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"build.js","sourceRoot":"","sources":["../../build/build.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The build, as a library: one build, or a watch that rebuilds and says when\n * each build has ended. `loadbare-app-build` (build/cli.ts) is a thin\n * command over these, and `@loadbare/app/dev` runs the watch in process.\n *\n * Paths resolve against the working directory, as the command's do, so a\n * caller runs from the application's root.\n *\n * A build writes nothing until every output is in hand. One that fails\n * leaves the last good build in place, so a server reading `dist/` keeps\n * serving something coherent while the developer fixes the mistake.\n */\n\nimport { watch as watchFiles } from \"node:fs\";\nimport { mkdir, writeFile } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport * as esbuild from \"esbuild\";\nimport { assemble } from \"./assemble.js\";\nimport { loadDeclared } from \"./declared.js\";\nimport {\n checkReservedMethods,\n clientEntrySource,\n resolveElements,\n} from \"./elements.js\";\nimport { resolveLocations } from \"./locations.js\";\nimport { cssFrom, scanAll } from \"./origins.js\";\nimport { pagesSource } from \"./pages.js\";\nimport { stylesSource } from \"./styles.js\";\n\nexport interface BuildOptions {\n /** The application's source tree, `src` when absent. */\n src?: string;\n /** Where the output lands, `dist` when absent. */\n out?: string;\n /** Minify `client.js` and `app.css`. */\n minify?: boolean;\n}\n\nexport interface Built {\n /** Every file the build wrote. */\n files: string[];\n /** How many custom elements `client.js` registers. */\n elements: number;\n /** One line saying what was written, as the command prints it. */\n summary: string;\n}\n\n/** Build once. Throws, having written nothing, when the build fails. */\nexport async function buildApp(options: BuildOptions = {}): Promise<Built> {\n const src = options.src ?? \"src\";\n const minify = options.minify ?? false;\n const locations = await resolveLocations({ src, out: options.out ?? \"dist\" });\n const { outDir } = locations;\n const entryFile = path.join(outDir, \"client-entry.ts\");\n\n const scans = await scanAll(locations, src, locations.importsFile);\n await mkdir(outDir, { recursive: true });\n const declared = await loadDeclared(\n locations.pages,\n locations.pageSources,\n outDir,\n );\n const { html, usedTags } = await assemble({ ...locations, scans, declared });\n\n const { resolved } = resolveElements({ used: usedTags, scans });\n await checkReservedMethods(resolved);\n // The entry is read by the bundler and never served, so it is written now.\n await writeFile(entryFile, clientEntrySource(resolved, outDir), \"utf-8\");\n const bundle = await esbuild.build({\n entryPoints: [entryFile],\n bundle: true,\n format: \"iife\",\n outfile: path.join(outDir, \"client.js\"),\n minify,\n write: false,\n // esbuild keeps the directory it started in; the paths are the caller's.\n absWorkingDir: process.cwd(),\n });\n\n const outputs = new Map<string, string | Uint8Array>();\n outputs.set(path.join(outDir, \"app.html\"), html);\n for (const file of bundle.outputFiles) outputs.set(file.path, file.contents);\n\n const pages = pagesSource(\n locations.pages,\n locations.pageSources,\n outDir,\n locations.packageName,\n );\n if (pages) outputs.set(path.join(outDir, \"pages.ts\"), pages);\n\n const css = await stylesSource(cssFrom(scans));\n if (css) {\n const code = minify\n ? (await esbuild.transform(css, { loader: \"css\", minify: true })).code\n : css;\n outputs.set(path.join(outDir, \"app.css\"), code);\n }\n\n for (const [file, content] of outputs) await writeFile(file, content);\n\n const files = [...outputs.keys()].map((file) =>\n path.relative(process.cwd(), file),\n );\n const elements = resolved.size;\n return {\n files,\n elements,\n summary:\n `wrote ${files.join(\" and \")} ` +\n `(${elements} custom element${elements === 1 ? \"\" : \"s\"})`,\n };\n}\n\n/** How one build of a watch ended. */\nexport type BuildOutcome =\n | { ok: true; changed: string[]; built: Built }\n | { ok: false; changed: string[]; error: Error };\n\nexport interface WatchOptions extends BuildOptions {\n /**\n * A build is starting, for these changed files under `src`, relative to\n * it. Empty for the first build.\n */\n onStart?: (changed: string[]) => void;\n /** A build has ended, well or badly. A failed build does not stop the watch. */\n onBuilt?: (outcome: BuildOutcome) => void;\n}\n\nexport interface AppWatcher {\n /** How the first build ended. */\n ready: Promise<BuildOutcome>;\n /** Stop watching. A build already running finishes. */\n close(): void;\n}\n\n/** The files a build reads. A change to any other file starts no build. */\nconst BUILT_FROM = /\\.(html|ts|css)$/;\n\n/**\n * Edits arrive in bursts: an editor's save is often several events, and a\n * branch switch is hundreds. Waiting this long after the first lets the\n * burst finish, so it starts one build.\n */\nconst SETTLE_MS = 20;\n\n/**\n * Build, then build again whenever a file under `src` changes. Changes that\n * arrive while a build runs are gathered into one build after it, so builds\n * never overlap and never fall behind.\n */\nexport function watchApp(options: WatchOptions = {}): AppWatcher {\n const src = options.src ?? \"src\";\n // An output directory inside the source tree is written by every build,\n // and a change there must not start the next one.\n const out = path.relative(path.resolve(src), path.resolve(options.out ?? \"dist\"));\n const inOut = (file: string) =>\n out !== \"\" && !out.startsWith(\"..\") && (file === out || file.startsWith(out + path.sep));\n let pending = new Set<string>();\n let running = false;\n let timer: NodeJS.Timeout | undefined;\n let closed = false;\n\n const once = async (changed: string[]): Promise<BuildOutcome> => {\n options.onStart?.(changed);\n let outcome: BuildOutcome;\n try {\n outcome = { ok: true, changed, built: await buildApp(options) };\n } catch (err) {\n const error = err instanceof Error ? err : new Error(String(err));\n outcome = { ok: false, changed, error };\n }\n options.onBuilt?.(outcome);\n return outcome;\n };\n\n const drain = async (): Promise<void> => {\n running = true;\n while (pending.size > 0 && !closed) {\n const changed = [...pending].sort();\n pending = new Set();\n await once(changed);\n }\n running = false;\n };\n\n const ready = once([]).then((outcome) => {\n if (pending.size > 0) void drain();\n return outcome;\n });\n\n const watcher = watchFiles(src, { recursive: true }, (_event, file) => {\n if (closed || !file || !BUILT_FROM.test(file) || inOut(file)) return;\n pending.add(file.split(path.sep).join(\"/\"));\n if (running || timer) return;\n timer = setTimeout(() => {\n timer = undefined;\n // The first build is still running: it starts the next when it ends.\n void ready.then(() => {\n if (!running) void drain();\n });\n }, SETTLE_MS);\n });\n\n return {\n ready,\n close() {\n closed = true;\n clearTimeout(timer);\n watcher.close();\n },\n };\n}\n"]}
|
package/dist/build/cli.d.ts
CHANGED
|
@@ -35,7 +35,12 @@
|
|
|
35
35
|
* markup, a widget's class, a stylesheet, and `imports.ts` all feed the
|
|
36
36
|
* same build — and a rebuild that throws is reported without stopping the
|
|
37
37
|
* watch. There is no dev server and no browser reload here, only the files
|
|
38
|
-
* on disk staying current for the next manual refresh
|
|
38
|
+
* on disk staying current for the next manual refresh; `@loadbare/app/dev`
|
|
39
|
+
* is the dev server.
|
|
40
|
+
*
|
|
41
|
+
* The build itself is build/build.ts. This file is the command: its flags,
|
|
42
|
+
* and the lines it prints, one per build, `wrote ...` on stdout or the
|
|
43
|
+
* failure on stderr.
|
|
39
44
|
*/
|
|
40
45
|
export {};
|
|
41
46
|
//# sourceMappingURL=cli.d.ts.map
|
package/dist/build/cli.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../../build/cli.ts"],"names":[],"mappings":";AACA
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../../build/cli.ts"],"names":[],"mappings":";AACA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0CG"}
|