@loadbare/app 0.10.0 → 0.12.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.
Files changed (38) hide show
  1. package/dist/build/assemble.d.ts.map +1 -1
  2. package/dist/build/assemble.js +51 -38
  3. package/dist/build/assemble.js.map +1 -1
  4. package/dist/build/expand.d.ts +6 -1
  5. package/dist/build/expand.d.ts.map +1 -1
  6. package/dist/build/expand.js +92 -7
  7. package/dist/build/expand.js.map +1 -1
  8. package/dist/core/lb-constants.d.ts +4 -0
  9. package/dist/core/lb-constants.d.ts.map +1 -1
  10. package/dist/core/lb-constants.js +16 -0
  11. package/dist/core/lb-constants.js.map +1 -1
  12. package/dist/core/lb-types.d.ts +11 -0
  13. package/dist/core/lb-types.d.ts.map +1 -1
  14. package/dist/core/lb-types.js.map +1 -1
  15. package/dist/hub/lb-apply.d.ts +7 -1
  16. package/dist/hub/lb-apply.d.ts.map +1 -1
  17. package/dist/hub/lb-apply.js +136 -18
  18. package/dist/hub/lb-apply.js.map +1 -1
  19. package/dist/hub/lb-hub.browser.d.ts.map +1 -1
  20. package/dist/hub/lb-hub.browser.js +121 -37
  21. package/dist/hub/lb-hub.browser.js.map +1 -1
  22. package/dist/server/lb-server.d.ts.map +1 -1
  23. package/dist/server/lb-server.js +17 -0
  24. package/dist/server/lb-server.js.map +1 -1
  25. package/docs/TECHREF-1.0.md +151 -10
  26. package/docs/comparison.md +30 -5
  27. package/docs/reference/chrome.md +15 -0
  28. package/docs/reference/custom-elements.md +163 -9
  29. package/docs/reference/data-binding.md +45 -2
  30. package/docs/reference/page-files.md +59 -2
  31. package/docs/what-does-loadbare-extend.md +124 -0
  32. package/package.json +1 -1
  33. package/skills/loadbare-app/SKILL.md +86 -10
  34. package/skills/loadbare-app/references/TECHREF-1.0.md +151 -10
  35. package/skills/loadbare-app/references/chrome.md +15 -0
  36. package/skills/loadbare-app/references/custom-elements.md +163 -9
  37. package/skills/loadbare-app/references/data-binding.md +45 -2
  38. package/skills/loadbare-app/references/page-files.md +59 -2
@@ -1 +1 @@
1
- {"version":3,"file":"assemble.d.ts","sourceRoot":"","sources":["../../build/assemble.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAyBH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAC/C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAsM/C,MAAM,WAAW,eAAe;IAC9B,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,QAAQ,EAAE,CAAC;IAClB,8EAA8E;IAC9E,KAAK,EAAE,UAAU,EAAE,CAAC;CACrB;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,GACN,EAAE,eAAe,GAAG,OAAO,CAAC,SAAS,CAAC,CAgDtC"}
1
+ {"version":3,"file":"assemble.d.ts","sourceRoot":"","sources":["../../build/assemble.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAyBH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAC/C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAkN/C,MAAM,WAAW,eAAe;IAC9B,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,QAAQ,EAAE,CAAC;IAClB,8EAA8E;IAC9E,KAAK,EAAE,UAAU,EAAE,CAAC;CACrB;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,GACN,EAAE,eAAe,GAAG,OAAO,CAAC,SAAS,CAAC,CAsDtC"}
@@ -17,10 +17,9 @@
17
17
  * looking for pages or widgets; it is handed exactly which files those are.
18
18
  */
19
19
  import { readFile } from "node:fs/promises";
20
- import { JSDOM } from "jsdom";
21
- import { addDefinitions, customElementTags, expand, } from "./expand.js";
20
+ import { addDefinitions, customElementTags, expandTree, } from "./expand.js";
22
21
  import { formatHtml } from "./format.js";
23
- import { ATTR_PAGE, ATTR_PAGE_TITLE, ATTR_QUERY, ATTR_REQUEST, ATTR_SHOW, ATTR_URL_LINK, ATTR_URL_PUSH, ATTR_URL_UNKNOWN, DEVELOPER_ATTRIBUTES, HUB_TAG_NAME, LB_RESERVED_PREFIX, } from "../core/lb-constants.js";
22
+ import { ATTR_PAGE, ATTR_PAGE_TITLE, ATTR_QUERY, ATTR_REQUEST, ATTR_SHOW, ATTR_URL_LINK, ATTR_URL_PUSH, ATTR_URL_UNKNOWN, DEVELOPER_ATTRIBUTES, HUB_TAG_NAME, LB_RESERVED_PREFIX, SHOW_NOT, } from "../core/lb-constants.js";
24
23
  import { isRowRequest } from "../core/lb-types.js";
25
24
  /**
26
25
  * The hub calls showModal() on the `<dialog>` carrying lb-url-unknown
@@ -28,9 +27,8 @@ import { isRowRequest } from "../core/lb-types.js";
28
27
  * everything else it acts on. A dialog elsewhere in the body would never open
29
28
  * and would say nothing about why.
30
29
  */
31
- function checkUnknownPageDialog(chromeFile, expandedBody) {
32
- const fragment = JSDOM.fragment(expandedBody);
33
- const el = fragment.querySelector(`[${ATTR_URL_UNKNOWN}]`);
30
+ function checkUnknownPageDialog(chromeFile, body) {
31
+ const el = body.querySelector(`[${ATTR_URL_UNKNOWN}]`);
34
32
  if (!el)
35
33
  return;
36
34
  if (!el.closest(HUB_TAG_NAME)) {
@@ -93,9 +91,13 @@ function walkMarkup(node, scope, problems) {
93
91
  problems.push(`<${tag}> carries ${ATTR_SHOW}="${show}" with no row around it ` +
94
92
  `to take the value from`);
95
93
  }
94
+ else if (show === SHOW_NOT || show.startsWith(SHOW_NOT + SHOW_NOT)) {
95
+ problems.push(`<${tag}> carries ${ATTR_SHOW}="${show}"; write one ` +
96
+ `${SHOW_NOT} and then the column it reverses`);
97
+ }
96
98
  }
97
99
  walkMarkup(el, here, problems);
98
- // By tag name rather than by instanceof: JSDOM.fragment() parses into a
100
+ // By tag name rather than by instanceof: template content belongs to a
99
101
  // document with no window, so the constructor is not reachable from the
100
102
  // node.
101
103
  if (tag === "template") {
@@ -120,15 +122,12 @@ function walkMarkup(node, scope, problems) {
120
122
  * A page is in the document before its data arrives, and a value that has not
121
123
  * arrived is no value.
122
124
  *
123
- * Parsed as template content, which accepts a `<tr>` or an `<option>` at the
124
- * top level where a `<div>` would drop it. Elements are collected before any
125
- * is moved, and a wrapped element is still reached once its outer one has
126
- * gone into a template, because the references are held.
125
+ * Elements are collected before any is moved, and a wrapped element is still
126
+ * reached once its outer one has gone into a template, because the references
127
+ * are held.
127
128
  */
128
- function wrapShown(html) {
129
- const doc = new JSDOM("").window.document;
130
- const holder = doc.createElement("template");
131
- holder.innerHTML = html;
129
+ function wrapShown(holder) {
130
+ const doc = holder.ownerDocument;
132
131
  const shown = [];
133
132
  const collect = (root) => {
134
133
  shown.push(...root.querySelectorAll(`[${ATTR_SHOW}]`));
@@ -137,20 +136,32 @@ function wrapShown(html) {
137
136
  }
138
137
  };
139
138
  collect(holder.content);
140
- if (shown.length === 0)
141
- return html;
142
139
  for (const el of shown) {
143
140
  const parked = doc.createElement("template");
144
141
  parked.setAttribute(ATTR_SHOW, el.getAttribute(ATTR_SHOW));
145
142
  el.replaceWith(parked);
146
143
  parked.content.append(el);
147
144
  }
148
- return holder.innerHTML;
145
+ }
146
+ /**
147
+ * Expand one file's markup, naming the file if expansion refuses it.
148
+ *
149
+ * What comes back is the tree the browser will build, so every pass after
150
+ * this reads and changes it in place, and it is serialized once, when it
151
+ * ships.
152
+ */
153
+ function expandFile(file, source, defs) {
154
+ try {
155
+ return expandTree(source, defs);
156
+ }
157
+ catch (e) {
158
+ throw new Error(`assemble: '${file}': ${e.message}`);
159
+ }
149
160
  }
150
161
  /** Check one expanded fragment, and name the file if anything is wrong. */
151
- function checkMarkup(file, html) {
162
+ function checkMarkup(file, root) {
152
163
  const problems = [];
153
- walkMarkup(JSDOM.fragment(html), null, problems);
164
+ walkMarkup(root, null, problems);
154
165
  if (problems.length > 0) {
155
166
  throw new Error(`assemble: '${file}' markup:\n ` + problems.join(`\n `));
156
167
  }
@@ -160,16 +171,13 @@ function checkMarkup(file, html) {
160
171
  * a fragment inside `<main>` and a `<title>` belongs in a document's head,
161
172
  * and its text content is stamped on the page as ATTR_PAGE_TITLE.
162
173
  */
163
- function liftTitle(html) {
164
- const doc = new JSDOM("").window.document;
165
- const holder = doc.createElement("template");
166
- holder.innerHTML = html;
174
+ function liftTitle(holder) {
167
175
  const title = holder.content.querySelector("title");
168
176
  if (!title)
169
- return { html, title: null };
177
+ return null;
170
178
  const text = title.textContent?.trim() ?? "";
171
179
  title.remove();
172
- return { html: holder.innerHTML, title: text };
180
+ return text;
173
181
  }
174
182
  /** An attribute value, escaped for a double-quoted attribute. */
175
183
  function attributeValue(text) {
@@ -183,13 +191,19 @@ export async function assemble({ chromeFile, pages, scans, }) {
183
191
  for (const scan of scans) {
184
192
  defs = await addDefinitions(defs, scan.definitions);
185
193
  }
194
+ // Tags are collected from each tree as it goes by. What ships is these
195
+ // trees and the chrome around its body, which is collected on its own.
196
+ const usedTags = new Set();
186
197
  const templates = [];
187
198
  for (const { name, file } of pages) {
188
199
  const authored = await readFile(file, "utf-8");
189
- const { html: host, title } = liftTitle(expand(authored, defs));
190
- checkMarkup(file, host);
200
+ const host = expandFile(file, authored, defs);
201
+ const title = liftTitle(host);
202
+ checkMarkup(file, host.content);
203
+ wrapShown(host);
204
+ customElementTags(host.content, usedTags);
191
205
  const titled = title === null ? "" : ` ${ATTR_PAGE_TITLE}="${attributeValue(title)}"`;
192
- templates.push(`<template ${ATTR_PAGE}="${name}"${titled}>${wrapShown(host)}</template>`);
206
+ templates.push(`<template ${ATTR_PAGE}="${name}"${titled}>${host.innerHTML}</template>`);
193
207
  }
194
208
  let chromeSource;
195
209
  try {
@@ -203,14 +217,13 @@ export async function assemble({ chromeFile, pages, scans, }) {
203
217
  throw new Error(`assemble: '${chromeFile}' has no <body>...</body> to expand`);
204
218
  }
205
219
  const [whole, bodyAttrs, bodyContent] = body;
206
- const expandedBody = expand(bodyContent, defs);
207
- checkUnknownPageDialog(chromeFile, expandedBody);
208
- checkMarkup(chromeFile, expandedBody);
209
- const shippedBody = wrapShown(expandedBody);
210
- const withPages = chromeSource.replace(whole, () => `<body${bodyAttrs}>${shippedBody}\n${templates.join("\n")}\n</body>`);
211
- return {
212
- html: await formatHtml(withPages),
213
- usedTags: customElementTags(withPages),
214
- };
220
+ const chromeBody = expandFile(chromeFile, bodyContent, defs);
221
+ checkUnknownPageDialog(chromeFile, chromeBody.content);
222
+ checkMarkup(chromeFile, chromeBody.content);
223
+ wrapShown(chromeBody);
224
+ customElementTags(chromeBody.content, usedTags);
225
+ customElementTags(chromeSource.replace(whole, ""), usedTags);
226
+ const withPages = chromeSource.replace(whole, () => `<body${bodyAttrs}>${chromeBody.innerHTML}\n${templates.join("\n")}\n</body>`);
227
+ return { html: await formatHtml(withPages), usedTags };
215
228
  }
216
229
  //# sourceMappingURL=assemble.js.map
@@ -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 { JSDOM } from \"jsdom\";\nimport {\n addDefinitions,\n customElementTags,\n expand,\n type Definitions,\n} from \"./expand.js\";\nimport { formatHtml } from \"./format.js\";\nimport {\n ATTR_PAGE,\n ATTR_PAGE_TITLE,\n ATTR_QUERY,\n ATTR_REQUEST,\n ATTR_SHOW,\n ATTR_URL_LINK,\n ATTR_URL_PUSH,\n ATTR_URL_UNKNOWN,\n DEVELOPER_ATTRIBUTES,\n HUB_TAG_NAME,\n LB_RESERVED_PREFIX,\n} from \"../core/lb-constants.js\";\nimport { isRowRequest } from \"../core/lb-types.js\";\nimport type { PageFile } from \"./locations.js\";\nimport type { OriginScan } from \"./origins.js\";\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(\n chromeFile: string,\n expandedBody: string,\n): void {\n const fragment = JSDOM.fragment(expandedBody);\n const el = fragment.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 some `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 row: boolean | \"unknown\";\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 problems: string[],\n): void {\n for (const el of node.children) {\n const tag = el.localName;\n let here = scope;\n if (el.hasAttribute(ATTR_QUERY)) here = { row: \"unknown\" };\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 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 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 // 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 }\n }\n\n walkMarkup(el, here, problems);\n // By tag name rather than by instanceof: JSDOM.fragment() parses into 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(content, isRowTemplate ? { row: true } : here, problems);\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 * Parsed as template content, which accepts a `<tr>` or an `<option>` at the\n * top level where a `<div>` would drop it. Elements are collected before any\n * is moved, and a wrapped element is still reached once its outer one has\n * gone into a template, because the references are held.\n */\nfunction wrapShown(html: string): string {\n const doc = new JSDOM(\"\").window.document;\n const holder = doc.createElement(\"template\");\n holder.innerHTML = html;\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 if (shown.length === 0) return html;\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 return holder.innerHTML;\n}\n\n/** Check one expanded fragment, and name the file if anything is wrong. */\nfunction checkMarkup(file: string, html: string): void {\n const problems: string[] = [];\n walkMarkup(JSDOM.fragment(html), null, 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(html: string): { html: string; title: string | null } {\n const doc = new JSDOM(\"\").window.document;\n const holder = doc.createElement(\"template\");\n holder.innerHTML = html;\n const title = holder.content.querySelector(\"title\");\n if (!title) return { html, title: null };\n const text = title.textContent?.trim() ?? \"\";\n title.remove();\n return { html: holder.innerHTML, title: text };\n}\n\n/** An attribute value, escaped for a double-quoted attribute. */\nfunction attributeValue(text: string): string {\n return text.replace(/&/g, \"&amp;\").replace(/\"/g, \"&quot;\");\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\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}: 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 const templates: string[] = [];\n for (const { name, file } of pages) {\n const authored = await readFile(file, \"utf-8\");\n const { html: host, title } = liftTitle(expand(authored, defs));\n checkMarkup(file, host);\n const titled =\n title === null ? \"\" : ` ${ATTR_PAGE_TITLE}=\"${attributeValue(title)}\"`;\n templates.push(\n `<template ${ATTR_PAGE}=\"${name}\"${titled}>${wrapShown(host)}</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 expandedBody = expand(bodyContent!, defs);\n checkUnknownPageDialog(chromeFile, expandedBody);\n checkMarkup(chromeFile, expandedBody);\n const shippedBody = wrapShown(expandedBody);\n const withPages = chromeSource.replace(\n whole!,\n () => `<body${bodyAttrs}>${shippedBody}\\n${templates.join(\"\\n\")}\\n</body>`,\n );\n\n return {\n html: await formatHtml(withPages),\n usedTags: customElementTags(withPages),\n };\n}\n"]}
1
+ {"version":3,"file":"assemble.js","sourceRoot":"","sources":["../../build/assemble.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,EACL,cAAc,EACd,iBAAiB,EACjB,UAAU,GAEX,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EACL,SAAS,EACT,eAAe,EACf,UAAU,EACV,YAAY,EACZ,SAAS,EACT,aAAa,EACb,aAAa,EACb,gBAAgB,EAChB,oBAAoB,EACpB,YAAY,EACZ,kBAAkB,EAClB,QAAQ,GACT,MAAM,yBAAyB,CAAC;AACjC,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAInD;;;;;GAKG;AACH,SAAS,sBAAsB,CAAC,UAAkB,EAAE,IAAgB;IAClE,MAAM,EAAE,GAAG,IAAI,CAAC,aAAa,CAAC,IAAI,gBAAgB,GAAG,CAAC,CAAC;IACvD,IAAI,CAAC,EAAE;QAAE,OAAO;IAChB,IAAI,CAAC,EAAE,CAAC,OAAO,CAAC,YAAY,CAAC,EAAE,CAAC;QAC9B,MAAM,IAAI,KAAK,CACb,cAAc,UAAU,cAAc,gBAAgB,WAAW;YAC/D,IAAI,YAAY,kCAAkC,CACrD,CAAC;IACJ,CAAC;AACH,CAAC;AAYD;;;;;;;;;;GAUG;AACH,SAAS,UAAU,CACjB,IAAgB,EAChB,KAAmB,EACnB,QAAkB;IAElB,KAAK,MAAM,EAAE,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;QAC/B,MAAM,GAAG,GAAG,EAAE,CAAC,SAAS,CAAC;QACzB,IAAI,IAAI,GAAG,KAAK,CAAC;QACjB,IAAI,EAAE,CAAC,YAAY,CAAC,UAAU,CAAC;YAAE,IAAI,GAAG,EAAE,GAAG,EAAE,SAAS,EAAE,CAAC;QAE3D,KAAK,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,UAAU,EAAE,CAAC;YACrC,IACE,IAAI,CAAC,UAAU,CAAC,kBAAkB,CAAC;gBACnC,CAAE,oBAA0C,CAAC,QAAQ,CAAC,IAAI,CAAC,EAC3D,CAAC;gBACD,QAAQ,CAAC,IAAI,CACX,IAAI,GAAG,aAAa,IAAI,0CAA0C;oBAChE,QAAQ,CACX,CAAC;YACJ,CAAC;QACH,CAAC;QAED,MAAM,OAAO,GAAG,EAAE,CAAC,YAAY,CAAC,YAAY,CAAC,CAAC;QAC9C,IACE,OAAO,KAAK,IAAI;YAChB,OAAO,CAAC,UAAU,CAAC,kBAAkB,CAAC;YACtC,CAAC,YAAY,CAAC,OAAO,CAAC,EACtB,CAAC;YACD,QAAQ,CAAC,IAAI,CACX,IAAI,GAAG,aAAa,YAAY,KAAK,OAAO,uBAAuB;gBACjE,IAAI,kBAAkB,0CAA0C,CACnE,CAAC;QACJ,CAAC;QACD,IAAI,EAAE,CAAC,YAAY,CAAC,aAAa,CAAC,IAAI,GAAG,KAAK,GAAG,EAAE,CAAC;YAClD,QAAQ,CAAC,IAAI,CAAC,IAAI,GAAG,aAAa,aAAa,qBAAqB,CAAC,CAAC;QACxE,CAAC;QACD,IAAI,EAAE,CAAC,YAAY,CAAC,aAAa,CAAC,IAAI,OAAO,KAAK,IAAI,EAAE,CAAC;YACvD,QAAQ,CAAC,IAAI,CACX,IAAI,GAAG,aAAa,aAAa,WAAW,YAAY,YAAY;gBAClE,sBAAsB,YAAY,EAAE,CACvC,CAAC;QACJ,CAAC;QACD,IAAI,EAAE,CAAC,YAAY,CAAC,gBAAgB,CAAC,IAAI,GAAG,KAAK,QAAQ,EAAE,CAAC;YAC1D,QAAQ,CAAC,IAAI,CACX,IAAI,GAAG,aAAa,gBAAgB,yBAAyB,CAC9D,CAAC;QACJ,CAAC;QAED,uEAAuE;QACvE,mEAAmE;QACnE,oEAAoE;QACpE,SAAS;QACT,MAAM,IAAI,GAAG,EAAE,CAAC,YAAY,CAAC,SAAS,CAAC,CAAC;QACxC,IAAI,IAAI,KAAK,IAAI,EAAE,CAAC;YAClB,IAAI,GAAG,KAAK,UAAU,EAAE,CAAC;gBACvB,QAAQ,CAAC,IAAI,CACX,wBAAwB,SAAS,KAAK,IAAI,qBAAqB;oBAC7D,sDAAsD,CACzD,CAAC;YACJ,CAAC;iBAAM,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,CAAC,GAAG,KAAK,KAAK,EAAE,CAAC;gBACjD,QAAQ,CAAC,IAAI,CACX,IAAI,GAAG,aAAa,SAAS,KAAK,IAAI,0BAA0B;oBAC9D,wBAAwB,CAC3B,CAAC;YACJ,CAAC;iBAAM,IAAI,IAAI,KAAK,QAAQ,IAAI,IAAI,CAAC,UAAU,CAAC,QAAQ,GAAG,QAAQ,CAAC,EAAE,CAAC;gBACrE,QAAQ,CAAC,IAAI,CACX,IAAI,GAAG,aAAa,SAAS,KAAK,IAAI,eAAe;oBACnD,GAAG,QAAQ,kCAAkC,CAChD,CAAC;YACJ,CAAC;QACH,CAAC;QAED,UAAU,CAAC,EAAE,EAAE,IAAI,EAAE,QAAQ,CAAC,CAAC;QAC/B,uEAAuE;QACvE,wEAAwE;QACxE,QAAQ;QACR,IAAI,GAAG,KAAK,UAAU,EAAE,CAAC;YACvB,MAAM,OAAO,GAAI,EAA0B,CAAC,OAAO,CAAC;YACpD,MAAM,aAAa,GACjB,KAAK,KAAK,IAAI,IAAI,CAAC,CAAC,EAAE,CAAC,aAAa,EAAE,OAAO,CAAC,IAAI,UAAU,GAAG,CAAC,CAAC;YACnE,IAAI,aAAa,EAAE,CAAC;gBAClB,KAAK,MAAM,IAAI,IAAI,OAAO,CAAC,QAAQ,EAAE,CAAC;oBACpC,IAAI,IAAI,CAAC,YAAY,CAAC,SAAS,CAAC,EAAE,CAAC;wBACjC,QAAQ,CAAC,IAAI,CACX,IAAI,IAAI,CAAC,SAAS,yCAAyC;4BACzD,GAAG,SAAS,8CAA8C;4BAC1D,WAAW,CACd,CAAC;oBACJ,CAAC;gBACH,CAAC;YACH,CAAC;YACD,UAAU,CAAC,OAAO,EAAE,aAAa,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;QACtE,CAAC;IACH,CAAC;AACH,CAAC;AAED;;;;;;;;;GASG;AACH,SAAS,SAAS,CAAC,MAA2B;IAC5C,MAAM,GAAG,GAAG,MAAM,CAAC,aAAa,CAAC;IACjC,MAAM,KAAK,GAAc,EAAE,CAAC;IAC5B,MAAM,OAAO,GAAG,CAAC,IAAgB,EAAQ,EAAE;QACzC,KAAK,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,gBAAgB,CAAC,IAAI,SAAS,GAAG,CAAC,CAAC,CAAC;QACvD,KAAK,MAAM,CAAC,IAAI,IAAI,CAAC,gBAAgB,CAAC,UAAU,CAAC,EAAE,CAAC;YAClD,OAAO,CAAE,CAAyB,CAAC,OAAO,CAAC,CAAC;QAC9C,CAAC;IACH,CAAC,CAAC;IACF,OAAO,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IACxB,KAAK,MAAM,EAAE,IAAI,KAAK,EAAE,CAAC;QACvB,MAAM,MAAM,GAAG,GAAG,CAAC,aAAa,CAAC,UAAU,CAAC,CAAC;QAC7C,MAAM,CAAC,YAAY,CAAC,SAAS,EAAE,EAAE,CAAC,YAAY,CAAC,SAAS,CAAE,CAAC,CAAC;QAC5D,EAAE,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC;QACvB,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;IAC5B,CAAC;AACH,CAAC;AAED;;;;;;GAMG;AACH,SAAS,UAAU,CACjB,IAAY,EACZ,MAAc,EACd,IAAiB;IAEjB,IAAI,CAAC;QACH,OAAO,UAAU,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;IAClC,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,MAAM,IAAI,KAAK,CAAC,cAAc,IAAI,MAAO,CAAW,CAAC,OAAO,EAAE,CAAC,CAAC;IAClE,CAAC;AACH,CAAC;AAED,2EAA2E;AAC3E,SAAS,WAAW,CAAC,IAAY,EAAE,IAAgB;IACjD,MAAM,QAAQ,GAAa,EAAE,CAAC;IAC9B,UAAU,CAAC,IAAI,EAAE,IAAI,EAAE,QAAQ,CAAC,CAAC;IACjC,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACxB,MAAM,IAAI,KAAK,CAAC,cAAc,IAAI,eAAe,GAAG,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC;IAC7E,CAAC;AACH,CAAC;AAED;;;;GAIG;AACH,SAAS,SAAS,CAAC,MAA2B;IAC5C,MAAM,KAAK,GAAG,MAAM,CAAC,OAAO,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC;IACpD,IAAI,CAAC,KAAK;QAAE,OAAO,IAAI,CAAC;IACxB,MAAM,IAAI,GAAG,KAAK,CAAC,WAAW,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;IAC7C,KAAK,CAAC,MAAM,EAAE,CAAC;IACf,OAAO,IAAI,CAAC;AACd,CAAC;AAED,iEAAiE;AACjE,SAAS,cAAc,CAAC,IAAY;IAClC,OAAO,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;AAC7D,CAAC;AAeD,MAAM,CAAC,KAAK,UAAU,QAAQ,CAAC,EAC7B,UAAU,EACV,KAAK,EACL,KAAK,GACW;IAChB,2EAA2E;IAC3E,4EAA4E;IAC5E,2CAA2C;IAC3C,IAAI,IAAI,GAAgB,EAAE,CAAC;IAC3B,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,IAAI,GAAG,MAAM,cAAc,CAAC,IAAI,EAAE,IAAI,CAAC,WAAW,CAAC,CAAC;IACtD,CAAC;IAED,uEAAuE;IACvE,uEAAuE;IACvE,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAU,CAAC;IACnC,MAAM,SAAS,GAAa,EAAE,CAAC;IAC/B,KAAK,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,KAAK,EAAE,CAAC;QACnC,MAAM,QAAQ,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;QAC/C,MAAM,IAAI,GAAG,UAAU,CAAC,IAAI,EAAE,QAAQ,EAAE,IAAI,CAAC,CAAC;QAC9C,MAAM,KAAK,GAAG,SAAS,CAAC,IAAI,CAAC,CAAC;QAC9B,WAAW,CAAC,IAAI,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC;QAChC,SAAS,CAAC,IAAI,CAAC,CAAC;QAChB,iBAAiB,CAAC,IAAI,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;QAC1C,MAAM,MAAM,GACV,KAAK,KAAK,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,eAAe,KAAK,cAAc,CAAC,KAAK,CAAC,GAAG,CAAC;QACzE,SAAS,CAAC,IAAI,CACZ,aAAa,SAAS,KAAK,IAAI,IAAI,MAAM,IAAI,IAAI,CAAC,SAAS,aAAa,CACzE,CAAC;IACJ,CAAC;IAED,IAAI,YAAoB,CAAC;IACzB,IAAI,CAAC;QACH,YAAY,GAAG,MAAM,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;IACrD,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,IAAI,KAAK,CAAC,gCAAgC,UAAU,GAAG,CAAC,CAAC;IACjE,CAAC;IAED,MAAM,IAAI,GAAG,iCAAiC,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;IAClE,IAAI,CAAC,IAAI,EAAE,CAAC;QACV,MAAM,IAAI,KAAK,CACb,cAAc,UAAU,qCAAqC,CAC9D,CAAC;IACJ,CAAC;IACD,MAAM,CAAC,KAAK,EAAE,SAAS,EAAE,WAAW,CAAC,GAAG,IAAI,CAAC;IAC7C,MAAM,UAAU,GAAG,UAAU,CAAC,UAAU,EAAE,WAAY,EAAE,IAAI,CAAC,CAAC;IAC9D,sBAAsB,CAAC,UAAU,EAAE,UAAU,CAAC,OAAO,CAAC,CAAC;IACvD,WAAW,CAAC,UAAU,EAAE,UAAU,CAAC,OAAO,CAAC,CAAC;IAC5C,SAAS,CAAC,UAAU,CAAC,CAAC;IACtB,iBAAiB,CAAC,UAAU,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;IAChD,iBAAiB,CAAC,YAAY,CAAC,OAAO,CAAC,KAAM,EAAE,EAAE,CAAC,EAAE,QAAQ,CAAC,CAAC;IAC9D,MAAM,SAAS,GAAG,YAAY,CAAC,OAAO,CACpC,KAAM,EACN,GAAG,EAAE,CACH,QAAQ,SAAS,IAAI,UAAU,CAAC,SAAS,KAAK,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,WAAW,CAChF,CAAC;IAEF,OAAO,EAAE,IAAI,EAAE,MAAM,UAAU,CAAC,SAAS,CAAC,EAAE,QAAQ,EAAE,CAAC;AACzD,CAAC","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 ATTR_PAGE,\n ATTR_PAGE_TITLE,\n ATTR_QUERY,\n ATTR_REQUEST,\n ATTR_SHOW,\n ATTR_URL_LINK,\n ATTR_URL_PUSH,\n ATTR_URL_UNKNOWN,\n DEVELOPER_ATTRIBUTES,\n HUB_TAG_NAME,\n LB_RESERVED_PREFIX,\n SHOW_NOT,\n} from \"../core/lb-constants.js\";\nimport { isRowRequest } from \"../core/lb-types.js\";\nimport type { PageFile } from \"./locations.js\";\nimport type { OriginScan } from \"./origins.js\";\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 some `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 row: boolean | \"unknown\";\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 problems: string[],\n): void {\n for (const el of node.children) {\n const tag = el.localName;\n let here = scope;\n if (el.hasAttribute(ATTR_QUERY)) here = { row: \"unknown\" };\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 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 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 // 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, 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(content, isRowTemplate ? { row: true } : here, problems);\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(file: string, root: ParentNode): void {\n const problems: string[] = [];\n walkMarkup(root, null, 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, \"&amp;\").replace(/\"/g, \"&quot;\");\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\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}: 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 checkMarkup(file, host.content);\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 checkUnknownPageDialog(chromeFile, chromeBody.content);\n checkMarkup(chromeFile, chromeBody.content);\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"]}
@@ -34,7 +34,7 @@ export declare function addDefinitions(defs: Definitions, files: Map<string, str
34
34
  * hyphen in the tag name is the one thing a custom element is guaranteed to
35
35
  * have, per the Custom Elements spec, and nothing native has one.
36
36
  */
37
- export declare function customElementTags(html: string): Set<string>;
37
+ export declare function customElementTags(from: string | ParentNode, tags?: Set<string>): Set<string>;
38
38
  /**
39
39
  * Expand every widget tag in an authored page. Throws rather than shipping.
40
40
  *
@@ -42,4 +42,9 @@ export declare function customElementTags(html: string): Set<string>;
42
42
  * checked for cycles. Handed a cyclic map directly, this recurses forever.
43
43
  */
44
44
  export declare function expand(source: string, defs: Definitions): string;
45
+ /**
46
+ * The same, as the tree the browser builds from it: a template, whose content
47
+ * the builder's later passes read and change before serializing it once.
48
+ */
49
+ export declare function expandTree(source: string, defs: Definitions): HTMLTemplateElement;
45
50
  //# sourceMappingURL=expand.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"expand.d.ts","sourceRoot":"","sources":["../../build/expand.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAkCH,MAAM,MAAM,WAAW,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;AA8BjD;;;;GAIG;AACH,wBAAsB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,WAAW,CAAC,CAmB1E;AAED;;;;;;GAMG;AACH,wBAAsB,cAAc,CAClC,IAAI,EAAE,WAAW,EACjB,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,GACzB,OAAO,CAAC,WAAW,CAAC,CAStB;AA4CD;;;;;;;;;GASG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,GAAG,CAAC,MAAM,CAAC,CAS3D;AA+OD;;;;;GAKG;AACH,wBAAgB,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,WAAW,GAAG,MAAM,CAIhE"}
1
+ {"version":3,"file":"expand.d.ts","sourceRoot":"","sources":["../../build/expand.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAkCH,MAAM,MAAM,WAAW,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;AA8CjD;;;;GAIG;AACH,wBAAsB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,WAAW,CAAC,CAmB1E;AAED;;;;;;GAMG;AACH,wBAAsB,cAAc,CAClC,IAAI,EAAE,WAAW,EACjB,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,GACzB,OAAO,CAAC,WAAW,CAAC,CAStB;AA4CD;;;;;;;;;GASG;AACH,wBAAgB,iBAAiB,CAC/B,IAAI,EAAE,MAAM,GAAG,UAAU,EACzB,IAAI,cAAoB,GACvB,GAAG,CAAC,MAAM,CAAC,CAQb;AA+OD;;;;;GAKG;AACH,wBAAgB,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,WAAW,GAAG,MAAM,CAEhE;AAED;;;GAGG;AACH,wBAAgB,UAAU,CACxB,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,WAAW,GAChB,mBAAmB,CAIrB"}
@@ -45,6 +45,20 @@ function parse(source) {
45
45
  holder.innerHTML = source;
46
46
  return holder;
47
47
  }
48
+ const parsedDefinitions = new Map();
49
+ /**
50
+ * A definition's tree, parsed once however many times it is checked or used.
51
+ * Keyed by the text, so an edited definition is a new entry. Read it, or
52
+ * clone it before changing anything.
53
+ */
54
+ function parseDefinition(source) {
55
+ let tree = parsedDefinitions.get(source);
56
+ if (tree === undefined) {
57
+ tree = parse(source);
58
+ parsedDefinitions.set(source, tree);
59
+ }
60
+ return tree;
61
+ }
48
62
  /**
49
63
  * A tree and the content of every template in it, however deep.
50
64
  *
@@ -109,7 +123,7 @@ export async function addDefinitions(defs, files) {
109
123
  /** Every placeholder a definition declares. */
110
124
  function placeholdersOf(source) {
111
125
  const names = [];
112
- for (const scope of scopes(parse(source))) {
126
+ for (const scope of scopes(parseDefinition(source))) {
113
127
  for (const el of scope.querySelectorAll("*")) {
114
128
  for (const attr of el.attributes) {
115
129
  const match = PLACEHOLDER.exec(attr.value.trim());
@@ -157,9 +171,8 @@ function checkNames(file, source) {
157
171
  * hyphen in the tag name is the one thing a custom element is guaranteed to
158
172
  * have, per the Custom Elements spec, and nothing native has one.
159
173
  */
160
- export function customElementTags(html) {
161
- const tags = new Set();
162
- for (const scope of scopes(parse(html))) {
174
+ export function customElementTags(from, tags = new Set()) {
175
+ for (const scope of scopes(typeof from === "string" ? parse(from) : from)) {
163
176
  for (const el of scope.querySelectorAll("*")) {
164
177
  const tag = el.tagName.toLowerCase();
165
178
  if (tag.includes("-"))
@@ -171,7 +184,7 @@ export function customElementTags(html) {
171
184
  /** Which definitions a definition uses. A tag with no definition expands to nothing and cannot cycle. */
172
185
  function referencesOf(source, defs) {
173
186
  const refs = new Set();
174
- for (const scope of scopes(parse(source))) {
187
+ for (const scope of scopes(parseDefinition(source))) {
175
188
  for (const el of scope.querySelectorAll("*")) {
176
189
  const tag = el.tagName.toUpperCase();
177
190
  if (CUSTOM_ELEMENT.test(tag) && tag in defs)
@@ -336,7 +349,7 @@ function expandElement(el, defs, source) {
336
349
  continue;
337
350
  attrs.set(attr.name.slice(EXP_PREFIX.length), attr.value);
338
351
  }
339
- const tree = parse(source);
352
+ const tree = parseDefinition(source).cloneNode(true);
340
353
  const asked = new Set();
341
354
  substitute(tree, attrs, asked);
342
355
  // A parameter the definition never declared is a name that resolves to
@@ -377,8 +390,80 @@ function walk(root, defs) {
377
390
  * checked for cycles. Handed a cyclic map directly, this recurses forever.
378
391
  */
379
392
  export function expand(source, defs) {
393
+ return expandTree(source, defs).innerHTML;
394
+ }
395
+ /**
396
+ * The same, as the tree the browser builds from it: a template, whose content
397
+ * the builder's later passes read and change before serializing it once.
398
+ */
399
+ export function expandTree(source, defs) {
380
400
  const tree = parse(source);
381
401
  walk(tree, defs);
382
- return tree.innerHTML;
402
+ return parsesBack(tree);
403
+ }
404
+ /**
405
+ * The tree the browser builds from the expanded tree's text, provided it is
406
+ * the same tree.
407
+ *
408
+ * Each file was parsed on its own, so everything inside one file already has
409
+ * the shape the parser gives it. Expansion then moves one file's nodes into
410
+ * another's through the DOM, which applies none of HTML's rules for what an
411
+ * element may hold. Where two files meet, the tree can hold what no parse
412
+ * would build: a definition's `<dialog>` inside a page's `<p>`. The browser
413
+ * reads the text and closes the `<p>` early, and the dialog lands outside its
414
+ * element with nothing logged.
415
+ *
416
+ * Reparsed in a template, the context a page ships in. Only a text that
417
+ * differs is walked, to name what moved.
418
+ */
419
+ function parsesBack(tree) {
420
+ const html = tree.innerHTML;
421
+ const reparsed = doc.createElement("template");
422
+ reparsed.innerHTML = html;
423
+ if (reparsed.innerHTML === html)
424
+ return reparsed;
425
+ const moved = firstMoved(tree, reparsed.content) ?? tree;
426
+ const path = [];
427
+ let owner = null;
428
+ for (let el = moved; el && el !== tree; el = el.parentNode) {
429
+ if (!(el instanceof Element))
430
+ continue;
431
+ const tag = el.tagName.toLowerCase();
432
+ path.unshift(tag);
433
+ if (owner === null && el !== moved && CUSTOM_ELEMENT.test(tag)) {
434
+ owner = tag;
435
+ }
436
+ }
437
+ const what = path.length > 0 ? `<${path.at(-1)}>` : "content";
438
+ const where = path.length > 0 ? path.join(" > ") : "the top level";
439
+ throw new Error(`expand: ${owner ? `<${owner}> puts` : "expansion puts"} ${what} ` +
440
+ `where the browser's parser would not keep it, at ${where}. HTML does ` +
441
+ `not let every element hold every other, and the browser moves what ` +
442
+ `breaks the rule.`);
443
+ }
444
+ /**
445
+ * The first node, in document order, that the reparse does not have in the
446
+ * same place. A node the reparse gained reports its parent.
447
+ */
448
+ function firstMoved(built, parsed) {
449
+ const ours = built.childNodes;
450
+ const theirs = parsed.childNodes;
451
+ for (let i = 0; i < ours.length; i++) {
452
+ const node = ours[i];
453
+ const other = theirs[i];
454
+ if (!other ||
455
+ node.nodeName !== other.nodeName ||
456
+ (node.nodeType !== Node.ELEMENT_NODE &&
457
+ node.nodeValue !== other.nodeValue)) {
458
+ return node.nodeType === Node.ELEMENT_NODE ? node : built;
459
+ }
460
+ const inner = firstMoved(node, other) ??
461
+ (node instanceof HTMLTemplateElement
462
+ ? firstMoved(node.content, other.content)
463
+ : null);
464
+ if (inner)
465
+ return inner;
466
+ }
467
+ return ours.length < theirs.length ? built : null;
383
468
  }
384
469
  //# sourceMappingURL=expand.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"expand.js","sourceRoot":"","sources":["../../build/expand.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AACrD,OAAO,EAAE,KAAK,EAAE,MAAM,OAAO,CAAC;AAC9B,OAAO,EAAE,aAAa,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAE3E,sEAAsE;AACtE,MAAM,cAAc,GAAG,GAAG,CAAC;AAE3B,yEAAyE;AACzE;;;;;;;GAOG;AACH,MAAM,WAAW,GAAG,uDAAuD,CAAC;AAE5E;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,GAAG,MAAM,CAAC;AAI1B,MAAM,GAAG,GAAG,IAAI,KAAK,CAAC,EAAE,CAAC,CAAC;AAC1B,MAAM,GAAG,GAAG,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC;AAChC,MAAM,EAAE,OAAO,EAAE,mBAAmB,EAAE,IAAI,EAAE,GAAG,GAAG,CAAC,MAAM,CAAC;AAE1D,SAAS,KAAK,CAAC,MAAc;IAC3B,MAAM,MAAM,GAAG,GAAG,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC;IACxC,MAAM,CAAC,SAAS,GAAG,MAAM,CAAC;IAC1B,OAAO,MAAgC,CAAC;AAC1C,CAAC;AAED;;;;;;;;;;GAUG;AACH,QAAQ,CAAC,CAAC,MAAM,CAAC,IAAgB;IAC/B,MAAM,IAAI,CAAC;IACX,KAAK,MAAM,EAAE,IAAI,IAAI,CAAC,gBAAgB,CAAC,UAAU,CAAC,EAAE,CAAC;QACnD,KAAK,CAAC,CAAC,MAAM,CAAE,EAA0B,CAAC,OAAO,CAAC,CAAC;IACrD,CAAC;AACH,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,IAAc;IAClD,MAAM,IAAI,GAAgB,EAAE,CAAC;IAC7B,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;QACvB,IAAI,KAAe,CAAC;QACpB,IAAI,CAAC;YACH,KAAK,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC,CAAC;QAC7B,CAAC;QAAC,MAAM,CAAC;YACP,SAAS;QACX,CAAC;QACD,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YACzB,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC;gBAAE,SAAS;YACtC,MAAM,IAAI,GAAG,GAAG,GAAG,IAAI,IAAI,EAAE,CAAC;YAC9B,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;YAC7C,UAAU,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;YACzB,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,WAAW,EAAE,CAAC,GAAG,MAAM,CAAC;QAC9D,CAAC;IACH,CAAC;IACD,YAAY,CAAC,IAAI,CAAC,CAAC;IACnB,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc,CAClC,IAAiB,EACjB,KAA0B;IAE1B,MAAM,MAAM,GAAG,EAAE,GAAG,IAAI,EAAE,CAAC;IAC3B,KAAK,MAAM,CAAC,GAAG,EAAE,IAAI,CAAC,IAAI,KAAK,EAAE,CAAC;QAChC,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;QAC7C,UAAU,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;QACzB,MAAM,CAAC,GAAG,CAAC,WAAW,EAAE,CAAC,GAAG,MAAM,CAAC;IACrC,CAAC;IACD,YAAY,CAAC,MAAM,CAAC,CAAC;IACrB,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,+CAA+C;AAC/C,SAAS,cAAc,CAAC,MAAc;IACpC,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,KAAK,MAAM,KAAK,IAAI,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC;QAC1C,KAAK,MAAM,EAAE,IAAI,KAAK,CAAC,gBAAgB,CAAC,GAAG,CAAC,EAAE,CAAC;YAC7C,KAAK,MAAM,IAAI,IAAI,EAAE,CAAC,UAAU,EAAE,CAAC;gBACjC,MAAM,KAAK,GAAG,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC;gBAClD,IAAI,KAAK;oBAAE,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAE,CAAC,CAAC;YACnC,CAAC;QACH,CAAC;QACD,MAAM,MAAM,GAAG,GAAG,CAAC,gBAAgB,CAAC,KAAwB,EAAE,CAAC,CAAC,UAAU,CAAC,CAAC;QAC5E,OAAO,MAAM,CAAC,QAAQ,EAAE,EAAE,CAAC;YACzB,MAAM,KAAK,GAAG,WAAW,CAAC,IAAI,CAAE,MAAM,CAAC,WAAoB,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;YACzE,IAAI,KAAK;gBAAE,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAE,CAAC,CAAC;QACnC,CAAC;IACH,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;;;;;;;;;;GAWG;AACH,SAAS,UAAU,CAAC,IAAY,EAAE,MAAc;IAC9C,KAAK,MAAM,IAAI,IAAI,cAAc,CAAC,MAAM,CAAC,EAAE,CAAC;QAC1C,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC;YAAE,SAAS;QAClC,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,oBAAoB,EAAE,OAAO,CAAC,CAAC,WAAW,EAAE,CAAC;QACxE,MAAM,IAAI,KAAK,CACb,WAAW,IAAI,eAAe,IAAI,oCAAoC;YACpE,2CAA2C,KAAK,KAAK,CACxD,CAAC;IACJ,CAAC;AACH,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,iBAAiB,CAAC,IAAY;IAC5C,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAC;IAC/B,KAAK,MAAM,KAAK,IAAI,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC;QACxC,KAAK,MAAM,EAAE,IAAI,KAAK,CAAC,gBAAgB,CAAC,GAAG,CAAC,EAAE,CAAC;YAC7C,MAAM,GAAG,GAAG,EAAE,CAAC,OAAO,CAAC,WAAW,EAAE,CAAC;YACrC,IAAI,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC;gBAAE,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QACvC,CAAC;IACH,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,yGAAyG;AACzG,SAAS,YAAY,CAAC,MAAc,EAAE,IAAiB;IACrD,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAC;IAC/B,KAAK,MAAM,KAAK,IAAI,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC;QAC1C,KAAK,MAAM,EAAE,IAAI,KAAK,CAAC,gBAAgB,CAAC,GAAG,CAAC,EAAE,CAAC;YAC7C,MAAM,GAAG,GAAG,EAAE,CAAC,OAAO,CAAC,WAAW,EAAE,CAAC;YACrC,IAAI,cAAc,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,GAAG,IAAI,IAAI;gBAAE,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QAC7D,CAAC;IACH,CAAC;IACD,OAAO,CAAC,GAAG,IAAI,CAAC,CAAC;AACnB,CAAC;AAED;;;GAGG;AACH,SAAS,YAAY,CAAC,IAAiB;IACrC,MAAM,IAAI,GAAG,IAAI,GAAG,EAAoB,CAAC;IACzC,KAAK,MAAM,CAAC,GAAG,EAAE,MAAM,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC;QACjD,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,YAAY,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC,CAAC;IAC5C,CAAC;IACD,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAC;IAC/B,MAAM,IAAI,GAAa,EAAE,CAAC;IAE1B,MAAM,KAAK,GAAG,CAAC,GAAW,EAAQ,EAAE;QAClC,IAAI,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC;YAAE,OAAO;QAC1B,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QAChC,IAAI,KAAK,KAAK,CAAC,CAAC,EAAE,CAAC;YACjB,MAAM,IAAI,KAAK,CACb,yCAAyC;gBACvC,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CACtE,CAAC;QACJ,CAAC;QACD,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QACf,KAAK,MAAM,GAAG,IAAI,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,EAAE;YAAE,KAAK,CAAC,GAAG,CAAC,CAAC;QAClD,IAAI,CAAC,GAAG,EAAE,CAAC;QACX,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IAChB,CAAC,CAAC;IAEF,KAAK,MAAM,GAAG,IAAI,IAAI,CAAC,IAAI,EAAE;QAAE,KAAK,CAAC,GAAG,CAAC,CAAC;AAC5C,CAAC;AAED;;;;;;GAMG;AACH,SAAS,OAAO,CACd,IAAY,EACZ,KAA0B,EAC1B,KAAkB;IAElB,MAAM,KAAK,GAAG,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;IAC5C,IAAI,CAAC,KAAK;QAAE,OAAO,IAAI,CAAC;IACxB,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,CAAE,CAAC;IACvB,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IAChB,OAAO,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,KAAK,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC;AAC7C,CAAC;AAED,SAAS,UAAU,CACjB,IAAgB,EAChB,KAA0B,EAC1B,KAAkB;IAElB,KAAK,MAAM,KAAK,IAAI,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC;QACjC,KAAK,MAAM,EAAE,IAAI,KAAK,CAAC,gBAAgB,CAAC,GAAG,CAAC,EAAE,CAAC;YAC7C,KAAK,MAAM,IAAI,IAAI,CAAC,GAAG,EAAE,CAAC,UAAU,CAAC,EAAE,CAAC;gBACtC,MAAM,KAAK,GAAG,OAAO,CAAC,IAAI,CAAC,KAAK,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC;gBAChD,sEAAsE;gBACtE,oEAAoE;gBACpE,uCAAuC;gBACvC,IAAI,KAAK,KAAK,IAAI;oBAAE,EAAE,CAAC,eAAe,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;qBAC7C,IAAI,KAAK,KAAK,IAAI,CAAC,KAAK;oBAAE,EAAE,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;YACnE,CAAC;QACH,CAAC;QAED,MAAM,MAAM,GAAG,GAAG,CAAC,gBAAgB,CAAC,KAAwB,EAAE,CAAC,CAAC,UAAU,CAAC,CAAC;QAC5E,MAAM,KAAK,GAAW,EAAE,CAAC;QACzB,OAAO,MAAM,CAAC,QAAQ,EAAE;YAAE,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,WAAmB,CAAC,CAAC;QACjE,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YACzB,MAAM,GAAG,GAAG,IAAI,CAAC,IAAI,CAAC;YACtB,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC;gBAAE,SAAS;YAC5C,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,EAAE,KAAK,EAAE,KAAK,CAAC,IAAI,EAAE,CAAC;YAC/C,uEAAuE;YACvE,6CAA6C;YAC7C,MAAM,IAAI,GAAG,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,MAAM,GAAG,GAAG,CAAC,SAAS,EAAE,CAAC,MAAM,CAAC,CAAC;YAC/D,MAAM,IAAI,GAAG,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC,MAAM,CAAC,CAAC;YAC7C,IAAI,CAAC,IAAI,GAAG,IAAI,GAAG,KAAK,GAAG,IAAI,CAAC;QAClC,CAAC;IACH,CAAC;AACH,CAAC;AAED;;;;;;;;;;;;;GAaG;AACH,SAAS,aAAa,CACpB,GAAW,EACX,IAAgB,EAChB,OAA0B;IAE1B,MAAM,YAAY,GAAG,IAAI,GAAG,EAAmB,CAAC;IAChD,KAAK,MAAM,EAAE,IAAI,IAAI,CAAC,gBAAgB,CAAC,IAAI,iBAAiB,GAAG,CAAC,EAAE,CAAC;QACjE,MAAM,IAAI,GAAG,EAAE,CAAC,YAAY,CAAC,iBAAiB,CAAE,CAAC;QACjD,IAAI,YAAY,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC;YAC3B,MAAM,IAAI,KAAK,CACb,YAAY,GAAG,CAAC,WAAW,EAAE,kBAAkB,iBAAiB,KAAK,IAAI,gBAAgB,CAC1F,CAAC;QACJ,CAAC;QACD,YAAY,CAAC,GAAG,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;QAC3B,EAAE,CAAC,eAAe,CAAC,iBAAiB,CAAC,CAAC;IACxC,CAAC;IAED,MAAM,IAAI,GAAsB,EAAE,CAAC;IACnC,MAAM,MAAM,GAAG,IAAI,GAAG,EAAU,CAAC;IACjC,KAAK,MAAM,IAAI,IAAI,OAAO,EAAE,CAAC;QAC3B,MAAM,KAAK,GACT,IAAI,YAAY,mBAAmB;YACnC,IAAI,CAAC,YAAY,CAAC,iBAAiB,CAAC,CAAC;QACvC,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAChB,SAAS;QACX,CAAC;QACD,MAAM,QAAQ,GAAG,IAA2B,CAAC;QAC7C,MAAM,IAAI,GAAG,QAAQ,CAAC,YAAY,CAAC,iBAAiB,CAAE,CAAC;QACvD,MAAM,WAAW,GAAG,YAAY,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QAC3C,IAAI,CAAC,WAAW,EAAE,CAAC;YACjB,MAAM,IAAI,KAAK,CACb,uBAAuB,iBAAiB,KAAK,IAAI,wBAAwB;gBACvE,IAAI,GAAG,CAAC,WAAW,EAAE,sDAAsD,CAC9E,CAAC;QACJ,CAAC;QACD,IAAI,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC;YACrB,MAAM,IAAI,KAAK,CACb,YAAY,GAAG,CAAC,WAAW,EAAE,kCAAkC,IAAI,GAAG,CACvE,CAAC;QACJ,CAAC;QACD,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QACjB,WAAW,CAAC,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;IACvC,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,kFAAkF;AAClF,SAAS,QAAQ,CACf,GAAW,EACX,IAAgB,EAChB,OAA0B;IAE1B,MAAM,KAAK,GAAG,IAAI,CAAC,gBAAgB,CAAC,IAAI,aAAa,GAAG,CAAC,CAAC;IAC1D,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACrB,MAAM,IAAI,KAAK,CACb,YAAY,GAAG,CAAC,WAAW,EAAE,4BAA4B,aAAa,EAAE,CACzE,CAAC;IACJ,CAAC;IACD,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACvB,uEAAuE;QACvE,mEAAmE;QACnE,2DAA2D;QAC3D,MAAM,KAAK,GAAG,OAAO,CAAC,IAAI,CACxB,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,QAAQ,KAAK,IAAI,CAAC,SAAS,IAAK,IAAa,CAAC,IAAI,CAAC,IAAI,EAAE,CACzE,CAAC;QACF,IAAI,KAAK,EAAE,CAAC;YACV,MAAM,IAAI,KAAK,CACb,uCAAuC,GAAG,CAAC,WAAW,EAAE,8BAA8B,aAAa,EAAE,CACtG,CAAC;QACJ,CAAC;QACD,OAAO;IACT,CAAC;IACD,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,CAAE,CAAC;IACvB,0EAA0E;IAC1E,yEAAyE;IACzE,sCAAsC;IACtC,IAAI,CAAC,MAAM,CAAC,GAAG,OAAO,CAAC,CAAC;IACxB,IAAI,CAAC,eAAe,CAAC,aAAa,CAAC,CAAC;AACtC,CAAC;AAED,SAAS,aAAa,CAAC,EAAW,EAAE,IAAiB,EAAE,MAAc;IACnE,MAAM,GAAG,GAAG,EAAE,CAAC,OAAO,CAAC,WAAW,EAAE,CAAC;IAErC,wEAAwE;IACxE,yEAAyE;IACzE,oDAAoD;IACpD,MAAM,KAAK,GAAG,IAAI,GAAG,EAAkB,CAAC;IACxC,KAAK,MAAM,IAAI,IAAI,EAAE,CAAC,UAAU,EAAE,CAAC;QACjC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC;YAAE,SAAS;QAChD,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,MAAM,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC;IAC5D,CAAC;IAED,MAAM,IAAI,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC;IAC3B,MAAM,KAAK,GAAG,IAAI,GAAG,EAAU,CAAC;IAChC,UAAU,CAAC,IAAI,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC;IAE/B,uEAAuE;IACvE,yEAAyE;IACzE,mEAAmE;IACnE,KAAK,MAAM,IAAI,IAAI,KAAK,CAAC,IAAI,EAAE,EAAE,CAAC;QAChC,IAAI,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC;YAAE,SAAS;QAC9B,MAAM,IAAI,KAAK,CACb,YAAY,GAAG,CAAC,WAAW,EAAE,eAAe,UAAU,GAAG,IAAI,IAAI;YAC/D,+BAA+B,IAAI,KAAK,CAC3C,CAAC;IACJ,CAAC;IACD,0EAA0E;IAC1E,8BAA8B;IAC9B,QAAQ,CAAC,GAAG,EAAE,IAAI,EAAE,aAAa,CAAC,GAAG,EAAE,IAAI,EAAE,CAAC,GAAG,EAAE,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;IAClE,EAAE,CAAC,eAAe,CAAC,GAAG,IAAI,CAAC,UAAU,CAAC,CAAC;AACzC,CAAC;AAED,SAAS,IAAI,CAAC,IAAgB,EAAE,IAAiB;IAC/C,KAAK,MAAM,KAAK,IAAI,CAAC,GAAG,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC;QACvC,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,WAAW,EAAE,CAAC,CAAC;QACjD,mEAAmE;QACnE,kEAAkE;QAClE,sEAAsE;QACtE,IAAI,MAAM,KAAK,SAAS;YAAE,aAAa,CAAC,KAAK,EAAE,IAAI,EAAE,MAAM,CAAC,CAAC;QAC7D,qEAAqE;QACrE,iDAAiD;QACjD,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;QAClB,wEAAwE;QACxE,4CAA4C;QAC5C,IAAI,KAAK,YAAY,mBAAmB;YAAE,IAAI,CAAC,KAAK,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;IACtE,CAAC;AACH,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,MAAM,CAAC,MAAc,EAAE,IAAiB;IACtD,MAAM,IAAI,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC;IAC3B,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;IACjB,OAAO,IAAI,CAAC,SAAS,CAAC;AACxB,CAAC","sourcesContent":["/**\n * Expansion — see docs/reference/custom-elements.md, \"HTML\".\n *\n * A page author writes one tag and gets the tree it stands for. Expansion\n * runs at build time, before any request exists, so it has no data. That is\n * why there is no control flow here: nothing to branch on.\n *\n * Substitution is applied to a parsed tree with setAttribute and node values,\n * never to a string. A value applied through the DOM API cannot be reparsed\n * as markup, so injection is impossible by construction.\n */\n\nimport { readdir, readFile } from \"node:fs/promises\";\nimport { JSDOM } from \"jsdom\";\nimport { ATTR_EXP_SLOT, ATTR_EXP_TEMPLATE } from \"../core/lb-constants.js\";\n\n/** Lowercase, hyphenated — the one shape a custom element tag has. */\nconst CUSTOM_ELEMENT = /-/;\n\n/** A placeholder is an entire attribute value or an entire text node. */\n/**\n * `{{name}}` or `{{name|default}}`. The default is a literal that stands in\n * when the tag supplies nothing; it is not evaluated and nothing branches on\n * it. Whitespace around the name and the default is not part of either, so\n * `{{button-text | OK}}` is the same placeholder as `{{button-text|OK}}` —\n * a spaced form that shipped as literal braces would be a silent failure\n * an author would find in the browser.\n */\nconst PLACEHOLDER = /^\\{\\{\\s*([A-Za-z][\\w-]*)\\s*(?:\\|\\s*([^}]*?)\\s*)?\\}\\}$/;\n\n/**\n * Marks an attribute as input to expansion. Three namespaces share the tag:\n * `lb-` holds the names Loadbare defines, `exp-` the names an element file\n * defines, and everything unprefixed is HTML's and means what HTML says it\n * means. The split is by owner rather than by phase — `lb-exp-slot` and\n * `lb-exp-template` are expansion's own and never reach the browser, while\n * `lb-query` and `lb-column` are read at both times. So an author is free to write\n * `class`, `title` or `hidden` on any widget, whatever its parameters happen\n * to be named. The prefix is needed on the tag, where a parameter and an\n * HTML attribute would otherwise share one namespace; inside a definition\n * `{{ }}` already marks a parameter, so `exp-label` fills `{{label}}`.\n */\nconst EXP_PREFIX = \"exp-\";\n\nexport type Definitions = Record<string, string>;\n\nconst dom = new JSDOM(\"\");\nconst doc = dom.window.document;\nconst { Element, HTMLTemplateElement, Node } = dom.window;\n\nfunction parse(source: string): HTMLElement {\n const holder = doc.createElement(\"div\");\n holder.innerHTML = source;\n return holder as unknown as HTMLElement;\n}\n\n/**\n * A tree and the content of every template in it, however deep.\n *\n * A template's children are parsed into a fragment rather than into the tree,\n * so `querySelectorAll` does not reach them. Everything expansion does has to\n * reach them anyway: a definition may supply its own row template, and\n * `lb-picker` does, so `{{key}}` written inside one has to be a\n * placeholder rather than literal text that ships. `walk` already descends\n * into template content to expand widget tags; this is the same rule stated\n * once for every pass that reads a tree.\n */\nfunction* scopes(root: ParentNode): Generator<ParentNode> {\n yield root;\n for (const el of root.querySelectorAll(\"template\")) {\n yield* scopes((el as HTMLTemplateElement).content);\n }\n}\n\n/**\n * Load one definition per file, named by the tag it defines.\n *\n * Later directories win, so an application can override a built-in widget.\n */\nexport async function loadDefinitions(dirs: string[]): Promise<Definitions> {\n const defs: Definitions = {};\n for (const dir of dirs) {\n let names: string[];\n try {\n names = await readdir(dir);\n } catch {\n continue;\n }\n for (const name of names) {\n if (!name.endsWith(\".html\")) continue;\n const file = `${dir}/${name}`;\n const source = await readFile(file, \"utf-8\");\n checkNames(file, source);\n defs[name.slice(0, -\".html\".length).toUpperCase()] = source;\n }\n }\n checkAcyclic(defs);\n return defs;\n}\n\n/**\n * Overlay definitions read from explicit files, each already keyed by the\n * tag it defines — for a source, like an application's own `src` tree, that\n * is discovered file by file rather than listed by directory. A tag here\n * overrides the same tag in `defs`, the same override `loadDefinitions`\n * gives a later directory over an earlier one.\n */\nexport async function addDefinitions(\n defs: Definitions,\n files: Map<string, string>,\n): Promise<Definitions> {\n const merged = { ...defs };\n for (const [tag, file] of files) {\n const source = await readFile(file, \"utf-8\");\n checkNames(file, source);\n merged[tag.toUpperCase()] = source;\n }\n checkAcyclic(merged);\n return merged;\n}\n\n/** Every placeholder a definition declares. */\nfunction placeholdersOf(source: string): string[] {\n const names: string[] = [];\n for (const scope of scopes(parse(source))) {\n for (const el of scope.querySelectorAll(\"*\")) {\n for (const attr of el.attributes) {\n const match = PLACEHOLDER.exec(attr.value.trim());\n if (match) names.push(match[1]!);\n }\n }\n const walker = doc.createTreeWalker(scope as globalThis.Node, 4 /* TEXT */);\n while (walker.nextNode()) {\n const match = PLACEHOLDER.exec((walker.currentNode as Text).data.trim());\n if (match) names.push(match[1]!);\n }\n }\n return names;\n}\n\n/**\n * A placeholder must be spelled the way a tag can supply it.\n *\n * HTML lowercases attribute names, so `exp-inputClass` reaches expansion as\n * `exp-inputclass` and could never fill `{{inputClass}}`. A definition is the\n * only file that can hold such a name and the only one that can fix it, so it\n * is rejected here, once, rather than at every page that uses the widget.\n *\n * Recognition stays broad on purpose. Narrowing PLACEHOLDER to lowercase would\n * make `{{inputClass}}` not a placeholder at all, and ship it to the browser as\n * literal text — the same silent failure somewhere new.\n */\nfunction checkNames(file: string, source: string): void {\n for (const name of placeholdersOf(source)) {\n if (!/[A-Z]/.test(name)) continue;\n const kebab = name.replace(/([a-z0-9])([A-Z])/g, \"$1-$2\").toLowerCase();\n throw new Error(\n `expand: ${file} declares {{${name}}}, but HTML lowercases attribute ` +\n `names, so no tag can supply it. Write {{${kebab}}}.`,\n );\n }\n}\n\n/**\n * Every hyphenated tag name left in a document, template content included.\n *\n * Meant to run on already-expanded output: an `LB-*` tag with a definition\n * is gone by then, replaced by the tree it stands for, so what remains is\n * whatever is left for the browser to upgrade itself — a built-in widget\n * class, an application's own custom element, or one from a package. A\n * hyphen in the tag name is the one thing a custom element is guaranteed to\n * have, per the Custom Elements spec, and nothing native has one.\n */\nexport function customElementTags(html: string): Set<string> {\n const tags = new Set<string>();\n for (const scope of scopes(parse(html))) {\n for (const el of scope.querySelectorAll(\"*\")) {\n const tag = el.tagName.toLowerCase();\n if (tag.includes(\"-\")) tags.add(tag);\n }\n }\n return tags;\n}\n\n/** Which definitions a definition uses. A tag with no definition expands to nothing and cannot cycle. */\nfunction referencesOf(source: string, defs: Definitions): string[] {\n const refs = new Set<string>();\n for (const scope of scopes(parse(source))) {\n for (const el of scope.querySelectorAll(\"*\")) {\n const tag = el.tagName.toUpperCase();\n if (CUSTOM_ELEMENT.test(tag) && tag in defs) refs.add(tag);\n }\n }\n return [...refs];\n}\n\n/**\n * Expansion is a fixpoint, so it terminates only on an acyclic graph. A depth\n * cap would hide the bug behind a limit instead of reporting it.\n */\nfunction checkAcyclic(defs: Definitions): void {\n const refs = new Map<string, string[]>();\n for (const [tag, source] of Object.entries(defs)) {\n refs.set(tag, referencesOf(source, defs));\n }\n const done = new Set<string>();\n const path: string[] = [];\n\n const visit = (tag: string): void => {\n if (done.has(tag)) return;\n const cycle = path.indexOf(tag);\n if (cycle !== -1) {\n throw new Error(\n `expand: cycle in the definition graph: ` +\n [...path.slice(cycle), tag].map((t) => t.toLowerCase()).join(\" → \"),\n );\n }\n path.push(tag);\n for (const ref of refs.get(tag) ?? []) visit(ref);\n path.pop();\n done.add(tag);\n };\n\n for (const tag of refs.keys()) visit(tag);\n}\n\n/**\n * Resolve a placeholder against what the author wrote: the supplied value,\n * else the definition's default, else absent.\n *\n * Every name the definition asks for is recorded, whether or not it was\n * supplied, so expansion can report a parameter the definition never declared.\n */\nfunction resolve(\n text: string,\n attrs: Map<string, string>,\n asked: Set<string>,\n): string | null {\n const match = PLACEHOLDER.exec(text.trim());\n if (!match) return text;\n const name = match[1]!;\n asked.add(name);\n return attrs.get(name) ?? match[2] ?? null;\n}\n\nfunction substitute(\n root: ParentNode,\n attrs: Map<string, string>,\n asked: Set<string>,\n): void {\n for (const scope of scopes(root)) {\n for (const el of scope.querySelectorAll(\"*\")) {\n for (const attr of [...el.attributes]) {\n const value = resolve(attr.value, attrs, asked);\n // Presence propagates: a placeholder with nothing behind it drops the\n // attribute rather than emitting it empty, which is what HTML's own\n // boolean-attribute semantics require.\n if (value === null) el.removeAttribute(attr.name);\n else if (value !== attr.value) el.setAttribute(attr.name, value);\n }\n }\n\n const walker = doc.createTreeWalker(scope as globalThis.Node, 4 /* TEXT */);\n const texts: Text[] = [];\n while (walker.nextNode()) texts.push(walker.currentNode as Text);\n for (const text of texts) {\n const raw = text.data;\n if (!PLACEHOLDER.test(raw.trim())) continue;\n const value = resolve(raw, attrs, asked) ?? \"\";\n // Keep the author's surrounding whitespace: \"{{label}} <input>\" should\n // still separate the label from the control.\n const lead = raw.slice(0, raw.length - raw.trimStart().length);\n const tail = raw.slice(raw.trimEnd().length);\n text.data = lead + value + tail;\n }\n }\n}\n\n/**\n * Fill the definition's named destinations from the authored templates that\n * name them, and hand back the content that is left for the slot.\n *\n * A destination is filled with the template's contents, not with the\n * template, so what ships is ordinary markup in an ordinary place — a real\n * `<tr>` inside a real `<thead>`, visible in view-source like everything\n * else. The wrapper existed only to survive the parser and is gone.\n *\n * Unlike the slot, there may be several, because the reason for wrapping is\n * per-destination: a table has a head and a body, and each needs its own\n * markup past the same obstacle. They are matched by name, and an empty name\n * is a name, so a widget with one destination costs the author nothing.\n */\nfunction fillTemplates(\n tag: string,\n root: ParentNode,\n content: globalThis.Node[],\n): globalThis.Node[] {\n const destinations = new Map<string, Element>();\n for (const el of root.querySelectorAll(`[${ATTR_EXP_TEMPLATE}]`)) {\n const name = el.getAttribute(ATTR_EXP_TEMPLATE)!;\n if (destinations.has(name)) {\n throw new Error(\n `expand: <${tag.toLowerCase()}> declares two ${ATTR_EXP_TEMPLATE}=\"${name}\" destinations`,\n );\n }\n destinations.set(name, el);\n el.removeAttribute(ATTR_EXP_TEMPLATE);\n }\n\n const rest: globalThis.Node[] = [];\n const filled = new Set<string>();\n for (const node of content) {\n const named =\n node instanceof HTMLTemplateElement &&\n node.hasAttribute(ATTR_EXP_TEMPLATE);\n if (!named) {\n rest.push(node);\n continue;\n }\n const template = node as HTMLTemplateElement;\n const name = template.getAttribute(ATTR_EXP_TEMPLATE)!;\n const destination = destinations.get(name);\n if (!destination) {\n throw new Error(\n `expand: a <template ${ATTR_EXP_TEMPLATE}=\"${name}\"> was written inside ` +\n `<${tag.toLowerCase()}>, whose definition has no destination by that name.`,\n );\n }\n if (filled.has(name)) {\n throw new Error(\n `expand: <${tag.toLowerCase()}> was given two templates for '${name}'`,\n );\n }\n filled.add(name);\n destination.append(template.content);\n }\n return rest;\n}\n\n/** Place authored inner content in the single slot, if the definition has one. */\nfunction fillSlot(\n tag: string,\n root: ParentNode,\n content: globalThis.Node[],\n): void {\n const slots = root.querySelectorAll(`[${ATTR_EXP_SLOT}]`);\n if (slots.length > 1) {\n throw new Error(\n `expand: <${tag.toLowerCase()}> declares more than one ${ATTR_EXP_SLOT}`,\n );\n }\n if (slots.length === 0) {\n // Whitespace is not content. An author who writes only named templates\n // still leaves the newlines between them, and those are the file's\n // formatting rather than something anybody meant to place.\n const wrote = content.some(\n (node) => node.nodeType !== Node.TEXT_NODE || (node as Text).data.trim(),\n );\n if (wrote) {\n throw new Error(\n `expand: content was written inside <${tag.toLowerCase()}>, whose definition has no ${ATTR_EXP_SLOT}`,\n );\n }\n return;\n }\n const slot = slots[0]!;\n // The authored nodes move rather than copy. The caller replaces the tag's\n // children with the expanded tree immediately after, so there is nothing\n // left behind for a clone to protect.\n slot.append(...content);\n slot.removeAttribute(ATTR_EXP_SLOT);\n}\n\nfunction expandElement(el: Element, defs: Definitions, source: string): void {\n const tag = el.tagName.toUpperCase();\n\n // The tag survives expansion carrying every attribute the author wrote,\n // `exp-` ones included. They cost nothing where they are, and what ships\n // shows what was asked for beside what it produced.\n const attrs = new Map<string, string>();\n for (const attr of el.attributes) {\n if (!attr.name.startsWith(EXP_PREFIX)) continue;\n attrs.set(attr.name.slice(EXP_PREFIX.length), attr.value);\n }\n\n const tree = parse(source);\n const asked = new Set<string>();\n substitute(tree, attrs, asked);\n\n // A parameter the definition never declared is a name that resolves to\n // nothing. Unprefixed attributes could not support this check, because a\n // stray one is indistinguishable from a deliberate HTML attribute.\n for (const name of attrs.keys()) {\n if (asked.has(name)) continue;\n throw new Error(\n `expand: <${tag.toLowerCase()}> was given ${EXP_PREFIX}${name}, ` +\n `but its definition has no {{${name}}}.`,\n );\n }\n // Named destinations first: what they take is addressed to them, and only\n // what is left is the slot's.\n fillSlot(tag, tree, fillTemplates(tag, tree, [...el.childNodes]));\n el.replaceChildren(...tree.childNodes);\n}\n\nfunction walk(root: ParentNode, defs: Definitions): void {\n for (const child of [...root.children]) {\n const source = defs[child.tagName.toUpperCase()];\n // A tag with no definition is left alone. It is a custom element a\n // script registers, and build/elements.ts is where a tag that has\n // neither a definition nor a script is refused — one rule, one place.\n if (source !== undefined) expandElement(child, defs, source);\n // Definitions may use other widgets, so descend after expanding. The\n // acyclicity check is what makes this terminate.\n walk(child, defs);\n // Template content is markup in a file like any other, and it is inert,\n // so a row template ships already expanded.\n if (child instanceof HTMLTemplateElement) walk(child.content, defs);\n }\n}\n\n/**\n * Expand every widget tag in an authored page. Throws rather than shipping.\n *\n * Definitions must come from `loadDefinitions`, which is where the graph is\n * checked for cycles. Handed a cyclic map directly, this recurses forever.\n */\nexport function expand(source: string, defs: Definitions): string {\n const tree = parse(source);\n walk(tree, defs);\n return tree.innerHTML;\n}\n"]}
1
+ {"version":3,"file":"expand.js","sourceRoot":"","sources":["../../build/expand.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Expansion — see docs/reference/custom-elements.md, \"HTML\".\n *\n * A page author writes one tag and gets the tree it stands for. Expansion\n * runs at build time, before any request exists, so it has no data. That is\n * why there is no control flow here: nothing to branch on.\n *\n * Substitution is applied to a parsed tree with setAttribute and node values,\n * never to a string. A value applied through the DOM API cannot be reparsed\n * as markup, so injection is impossible by construction.\n */\n\nimport { readdir, readFile } from \"node:fs/promises\";\nimport { JSDOM } from \"jsdom\";\nimport { ATTR_EXP_SLOT, ATTR_EXP_TEMPLATE } from \"../core/lb-constants.js\";\n\n/** Lowercase, hyphenated — the one shape a custom element tag has. */\nconst CUSTOM_ELEMENT = /-/;\n\n/** A placeholder is an entire attribute value or an entire text node. */\n/**\n * `{{name}}` or `{{name|default}}`. The default is a literal that stands in\n * when the tag supplies nothing; it is not evaluated and nothing branches on\n * it. Whitespace around the name and the default is not part of either, so\n * `{{button-text | OK}}` is the same placeholder as `{{button-text|OK}}` —\n * a spaced form that shipped as literal braces would be a silent failure\n * an author would find in the browser.\n */\nconst PLACEHOLDER = /^\\{\\{\\s*([A-Za-z][\\w-]*)\\s*(?:\\|\\s*([^}]*?)\\s*)?\\}\\}$/;\n\n/**\n * Marks an attribute as input to expansion. Three namespaces share the tag:\n * `lb-` holds the names Loadbare defines, `exp-` the names an element file\n * defines, and everything unprefixed is HTML's and means what HTML says it\n * means. The split is by owner rather than by phase — `lb-exp-slot` and\n * `lb-exp-template` are expansion's own and never reach the browser, while\n * `lb-query` and `lb-column` are read at both times. So an author is free to write\n * `class`, `title` or `hidden` on any widget, whatever its parameters happen\n * to be named. The prefix is needed on the tag, where a parameter and an\n * HTML attribute would otherwise share one namespace; inside a definition\n * `{{ }}` already marks a parameter, so `exp-label` fills `{{label}}`.\n */\nconst EXP_PREFIX = \"exp-\";\n\nexport type Definitions = Record<string, string>;\n\nconst dom = new JSDOM(\"\");\nconst doc = dom.window.document;\nconst { Element, HTMLTemplateElement, Node } = dom.window;\n\nfunction parse(source: string): HTMLElement {\n const holder = doc.createElement(\"div\");\n holder.innerHTML = source;\n return holder as unknown as HTMLElement;\n}\n\nconst parsedDefinitions = new Map<string, HTMLElement>();\n\n/**\n * A definition's tree, parsed once however many times it is checked or used.\n * Keyed by the text, so an edited definition is a new entry. Read it, or\n * clone it before changing anything.\n */\nfunction parseDefinition(source: string): HTMLElement {\n let tree = parsedDefinitions.get(source);\n if (tree === undefined) {\n tree = parse(source);\n parsedDefinitions.set(source, tree);\n }\n return tree;\n}\n\n/**\n * A tree and the content of every template in it, however deep.\n *\n * A template's children are parsed into a fragment rather than into the tree,\n * so `querySelectorAll` does not reach them. Everything expansion does has to\n * reach them anyway: a definition may supply its own row template, and\n * `lb-picker` does, so `{{key}}` written inside one has to be a\n * placeholder rather than literal text that ships. `walk` already descends\n * into template content to expand widget tags; this is the same rule stated\n * once for every pass that reads a tree.\n */\nfunction* scopes(root: ParentNode): Generator<ParentNode> {\n yield root;\n for (const el of root.querySelectorAll(\"template\")) {\n yield* scopes((el as HTMLTemplateElement).content);\n }\n}\n\n/**\n * Load one definition per file, named by the tag it defines.\n *\n * Later directories win, so an application can override a built-in widget.\n */\nexport async function loadDefinitions(dirs: string[]): Promise<Definitions> {\n const defs: Definitions = {};\n for (const dir of dirs) {\n let names: string[];\n try {\n names = await readdir(dir);\n } catch {\n continue;\n }\n for (const name of names) {\n if (!name.endsWith(\".html\")) continue;\n const file = `${dir}/${name}`;\n const source = await readFile(file, \"utf-8\");\n checkNames(file, source);\n defs[name.slice(0, -\".html\".length).toUpperCase()] = source;\n }\n }\n checkAcyclic(defs);\n return defs;\n}\n\n/**\n * Overlay definitions read from explicit files, each already keyed by the\n * tag it defines — for a source, like an application's own `src` tree, that\n * is discovered file by file rather than listed by directory. A tag here\n * overrides the same tag in `defs`, the same override `loadDefinitions`\n * gives a later directory over an earlier one.\n */\nexport async function addDefinitions(\n defs: Definitions,\n files: Map<string, string>,\n): Promise<Definitions> {\n const merged = { ...defs };\n for (const [tag, file] of files) {\n const source = await readFile(file, \"utf-8\");\n checkNames(file, source);\n merged[tag.toUpperCase()] = source;\n }\n checkAcyclic(merged);\n return merged;\n}\n\n/** Every placeholder a definition declares. */\nfunction placeholdersOf(source: string): string[] {\n const names: string[] = [];\n for (const scope of scopes(parseDefinition(source))) {\n for (const el of scope.querySelectorAll(\"*\")) {\n for (const attr of el.attributes) {\n const match = PLACEHOLDER.exec(attr.value.trim());\n if (match) names.push(match[1]!);\n }\n }\n const walker = doc.createTreeWalker(scope as globalThis.Node, 4 /* TEXT */);\n while (walker.nextNode()) {\n const match = PLACEHOLDER.exec((walker.currentNode as Text).data.trim());\n if (match) names.push(match[1]!);\n }\n }\n return names;\n}\n\n/**\n * A placeholder must be spelled the way a tag can supply it.\n *\n * HTML lowercases attribute names, so `exp-inputClass` reaches expansion as\n * `exp-inputclass` and could never fill `{{inputClass}}`. A definition is the\n * only file that can hold such a name and the only one that can fix it, so it\n * is rejected here, once, rather than at every page that uses the widget.\n *\n * Recognition stays broad on purpose. Narrowing PLACEHOLDER to lowercase would\n * make `{{inputClass}}` not a placeholder at all, and ship it to the browser as\n * literal text — the same silent failure somewhere new.\n */\nfunction checkNames(file: string, source: string): void {\n for (const name of placeholdersOf(source)) {\n if (!/[A-Z]/.test(name)) continue;\n const kebab = name.replace(/([a-z0-9])([A-Z])/g, \"$1-$2\").toLowerCase();\n throw new Error(\n `expand: ${file} declares {{${name}}}, but HTML lowercases attribute ` +\n `names, so no tag can supply it. Write {{${kebab}}}.`,\n );\n }\n}\n\n/**\n * Every hyphenated tag name left in a document, template content included.\n *\n * Meant to run on already-expanded output: an `LB-*` tag with a definition\n * is gone by then, replaced by the tree it stands for, so what remains is\n * whatever is left for the browser to upgrade itself — a built-in widget\n * class, an application's own custom element, or one from a package. A\n * hyphen in the tag name is the one thing a custom element is guaranteed to\n * have, per the Custom Elements spec, and nothing native has one.\n */\nexport function customElementTags(\n from: string | ParentNode,\n tags = new Set<string>(),\n): Set<string> {\n for (const scope of scopes(typeof from === \"string\" ? parse(from) : from)) {\n for (const el of scope.querySelectorAll(\"*\")) {\n const tag = el.tagName.toLowerCase();\n if (tag.includes(\"-\")) tags.add(tag);\n }\n }\n return tags;\n}\n\n/** Which definitions a definition uses. A tag with no definition expands to nothing and cannot cycle. */\nfunction referencesOf(source: string, defs: Definitions): string[] {\n const refs = new Set<string>();\n for (const scope of scopes(parseDefinition(source))) {\n for (const el of scope.querySelectorAll(\"*\")) {\n const tag = el.tagName.toUpperCase();\n if (CUSTOM_ELEMENT.test(tag) && tag in defs) refs.add(tag);\n }\n }\n return [...refs];\n}\n\n/**\n * Expansion is a fixpoint, so it terminates only on an acyclic graph. A depth\n * cap would hide the bug behind a limit instead of reporting it.\n */\nfunction checkAcyclic(defs: Definitions): void {\n const refs = new Map<string, string[]>();\n for (const [tag, source] of Object.entries(defs)) {\n refs.set(tag, referencesOf(source, defs));\n }\n const done = new Set<string>();\n const path: string[] = [];\n\n const visit = (tag: string): void => {\n if (done.has(tag)) return;\n const cycle = path.indexOf(tag);\n if (cycle !== -1) {\n throw new Error(\n `expand: cycle in the definition graph: ` +\n [...path.slice(cycle), tag].map((t) => t.toLowerCase()).join(\" → \"),\n );\n }\n path.push(tag);\n for (const ref of refs.get(tag) ?? []) visit(ref);\n path.pop();\n done.add(tag);\n };\n\n for (const tag of refs.keys()) visit(tag);\n}\n\n/**\n * Resolve a placeholder against what the author wrote: the supplied value,\n * else the definition's default, else absent.\n *\n * Every name the definition asks for is recorded, whether or not it was\n * supplied, so expansion can report a parameter the definition never declared.\n */\nfunction resolve(\n text: string,\n attrs: Map<string, string>,\n asked: Set<string>,\n): string | null {\n const match = PLACEHOLDER.exec(text.trim());\n if (!match) return text;\n const name = match[1]!;\n asked.add(name);\n return attrs.get(name) ?? match[2] ?? null;\n}\n\nfunction substitute(\n root: ParentNode,\n attrs: Map<string, string>,\n asked: Set<string>,\n): void {\n for (const scope of scopes(root)) {\n for (const el of scope.querySelectorAll(\"*\")) {\n for (const attr of [...el.attributes]) {\n const value = resolve(attr.value, attrs, asked);\n // Presence propagates: a placeholder with nothing behind it drops the\n // attribute rather than emitting it empty, which is what HTML's own\n // boolean-attribute semantics require.\n if (value === null) el.removeAttribute(attr.name);\n else if (value !== attr.value) el.setAttribute(attr.name, value);\n }\n }\n\n const walker = doc.createTreeWalker(scope as globalThis.Node, 4 /* TEXT */);\n const texts: Text[] = [];\n while (walker.nextNode()) texts.push(walker.currentNode as Text);\n for (const text of texts) {\n const raw = text.data;\n if (!PLACEHOLDER.test(raw.trim())) continue;\n const value = resolve(raw, attrs, asked) ?? \"\";\n // Keep the author's surrounding whitespace: \"{{label}} <input>\" should\n // still separate the label from the control.\n const lead = raw.slice(0, raw.length - raw.trimStart().length);\n const tail = raw.slice(raw.trimEnd().length);\n text.data = lead + value + tail;\n }\n }\n}\n\n/**\n * Fill the definition's named destinations from the authored templates that\n * name them, and hand back the content that is left for the slot.\n *\n * A destination is filled with the template's contents, not with the\n * template, so what ships is ordinary markup in an ordinary place — a real\n * `<tr>` inside a real `<thead>`, visible in view-source like everything\n * else. The wrapper existed only to survive the parser and is gone.\n *\n * Unlike the slot, there may be several, because the reason for wrapping is\n * per-destination: a table has a head and a body, and each needs its own\n * markup past the same obstacle. They are matched by name, and an empty name\n * is a name, so a widget with one destination costs the author nothing.\n */\nfunction fillTemplates(\n tag: string,\n root: ParentNode,\n content: globalThis.Node[],\n): globalThis.Node[] {\n const destinations = new Map<string, Element>();\n for (const el of root.querySelectorAll(`[${ATTR_EXP_TEMPLATE}]`)) {\n const name = el.getAttribute(ATTR_EXP_TEMPLATE)!;\n if (destinations.has(name)) {\n throw new Error(\n `expand: <${tag.toLowerCase()}> declares two ${ATTR_EXP_TEMPLATE}=\"${name}\" destinations`,\n );\n }\n destinations.set(name, el);\n el.removeAttribute(ATTR_EXP_TEMPLATE);\n }\n\n const rest: globalThis.Node[] = [];\n const filled = new Set<string>();\n for (const node of content) {\n const named =\n node instanceof HTMLTemplateElement &&\n node.hasAttribute(ATTR_EXP_TEMPLATE);\n if (!named) {\n rest.push(node);\n continue;\n }\n const template = node as HTMLTemplateElement;\n const name = template.getAttribute(ATTR_EXP_TEMPLATE)!;\n const destination = destinations.get(name);\n if (!destination) {\n throw new Error(\n `expand: a <template ${ATTR_EXP_TEMPLATE}=\"${name}\"> was written inside ` +\n `<${tag.toLowerCase()}>, whose definition has no destination by that name.`,\n );\n }\n if (filled.has(name)) {\n throw new Error(\n `expand: <${tag.toLowerCase()}> was given two templates for '${name}'`,\n );\n }\n filled.add(name);\n destination.append(template.content);\n }\n return rest;\n}\n\n/** Place authored inner content in the single slot, if the definition has one. */\nfunction fillSlot(\n tag: string,\n root: ParentNode,\n content: globalThis.Node[],\n): void {\n const slots = root.querySelectorAll(`[${ATTR_EXP_SLOT}]`);\n if (slots.length > 1) {\n throw new Error(\n `expand: <${tag.toLowerCase()}> declares more than one ${ATTR_EXP_SLOT}`,\n );\n }\n if (slots.length === 0) {\n // Whitespace is not content. An author who writes only named templates\n // still leaves the newlines between them, and those are the file's\n // formatting rather than something anybody meant to place.\n const wrote = content.some(\n (node) => node.nodeType !== Node.TEXT_NODE || (node as Text).data.trim(),\n );\n if (wrote) {\n throw new Error(\n `expand: content was written inside <${tag.toLowerCase()}>, whose definition has no ${ATTR_EXP_SLOT}`,\n );\n }\n return;\n }\n const slot = slots[0]!;\n // The authored nodes move rather than copy. The caller replaces the tag's\n // children with the expanded tree immediately after, so there is nothing\n // left behind for a clone to protect.\n slot.append(...content);\n slot.removeAttribute(ATTR_EXP_SLOT);\n}\n\nfunction expandElement(el: Element, defs: Definitions, source: string): void {\n const tag = el.tagName.toUpperCase();\n\n // The tag survives expansion carrying every attribute the author wrote,\n // `exp-` ones included. They cost nothing where they are, and what ships\n // shows what was asked for beside what it produced.\n const attrs = new Map<string, string>();\n for (const attr of el.attributes) {\n if (!attr.name.startsWith(EXP_PREFIX)) continue;\n attrs.set(attr.name.slice(EXP_PREFIX.length), attr.value);\n }\n\n const tree = parseDefinition(source).cloneNode(true) as HTMLElement;\n const asked = new Set<string>();\n substitute(tree, attrs, asked);\n\n // A parameter the definition never declared is a name that resolves to\n // nothing. Unprefixed attributes could not support this check, because a\n // stray one is indistinguishable from a deliberate HTML attribute.\n for (const name of attrs.keys()) {\n if (asked.has(name)) continue;\n throw new Error(\n `expand: <${tag.toLowerCase()}> was given ${EXP_PREFIX}${name}, ` +\n `but its definition has no {{${name}}}.`,\n );\n }\n // Named destinations first: what they take is addressed to them, and only\n // what is left is the slot's.\n fillSlot(tag, tree, fillTemplates(tag, tree, [...el.childNodes]));\n el.replaceChildren(...tree.childNodes);\n}\n\nfunction walk(root: ParentNode, defs: Definitions): void {\n for (const child of [...root.children]) {\n const source = defs[child.tagName.toUpperCase()];\n // A tag with no definition is left alone. It is a custom element a\n // script registers, and build/elements.ts is where a tag that has\n // neither a definition nor a script is refused — one rule, one place.\n if (source !== undefined) expandElement(child, defs, source);\n // Definitions may use other widgets, so descend after expanding. The\n // acyclicity check is what makes this terminate.\n walk(child, defs);\n // Template content is markup in a file like any other, and it is inert,\n // so a row template ships already expanded.\n if (child instanceof HTMLTemplateElement) walk(child.content, defs);\n }\n}\n\n/**\n * Expand every widget tag in an authored page. Throws rather than shipping.\n *\n * Definitions must come from `loadDefinitions`, which is where the graph is\n * checked for cycles. Handed a cyclic map directly, this recurses forever.\n */\nexport function expand(source: string, defs: Definitions): string {\n return expandTree(source, defs).innerHTML;\n}\n\n/**\n * The same, as the tree the browser builds from it: a template, whose content\n * the builder's later passes read and change before serializing it once.\n */\nexport function expandTree(\n source: string,\n defs: Definitions,\n): HTMLTemplateElement {\n const tree = parse(source);\n walk(tree, defs);\n return parsesBack(tree);\n}\n\n/**\n * The tree the browser builds from the expanded tree's text, provided it is\n * the same tree.\n *\n * Each file was parsed on its own, so everything inside one file already has\n * the shape the parser gives it. Expansion then moves one file's nodes into\n * another's through the DOM, which applies none of HTML's rules for what an\n * element may hold. Where two files meet, the tree can hold what no parse\n * would build: a definition's `<dialog>` inside a page's `<p>`. The browser\n * reads the text and closes the `<p>` early, and the dialog lands outside its\n * element with nothing logged.\n *\n * Reparsed in a template, the context a page ships in. Only a text that\n * differs is walked, to name what moved.\n */\nfunction parsesBack(tree: HTMLElement): HTMLTemplateElement {\n const html = tree.innerHTML;\n const reparsed = doc.createElement(\"template\");\n reparsed.innerHTML = html;\n if (reparsed.innerHTML === html) return reparsed;\n const moved = firstMoved(tree, reparsed.content) ?? tree;\n const path: string[] = [];\n let owner: string | null = null;\n for (let el: Node | null = moved; el && el !== tree; el = el.parentNode) {\n if (!(el instanceof Element)) continue;\n const tag = el.tagName.toLowerCase();\n path.unshift(tag);\n if (owner === null && el !== moved && CUSTOM_ELEMENT.test(tag)) {\n owner = tag;\n }\n }\n const what = path.length > 0 ? `<${path.at(-1)}>` : \"content\";\n const where = path.length > 0 ? path.join(\" > \") : \"the top level\";\n throw new Error(\n `expand: ${owner ? `<${owner}> puts` : \"expansion puts\"} ${what} ` +\n `where the browser's parser would not keep it, at ${where}. HTML does ` +\n `not let every element hold every other, and the browser moves what ` +\n `breaks the rule.`,\n );\n}\n\n/**\n * The first node, in document order, that the reparse does not have in the\n * same place. A node the reparse gained reports its parent.\n */\nfunction firstMoved(built: Node, parsed: Node): Node | null {\n const ours = built.childNodes;\n const theirs = parsed.childNodes;\n for (let i = 0; i < ours.length; i++) {\n const node = ours[i]!;\n const other = theirs[i];\n if (\n !other ||\n node.nodeName !== other.nodeName ||\n (node.nodeType !== Node.ELEMENT_NODE &&\n node.nodeValue !== other.nodeValue)\n ) {\n return node.nodeType === Node.ELEMENT_NODE ? node : built;\n }\n const inner =\n firstMoved(node, other) ??\n (node instanceof HTMLTemplateElement\n ? firstMoved(node.content, (other as HTMLTemplateElement).content)\n : null);\n if (inner) return inner;\n }\n return ours.length < theirs.length ? built : null;\n}\n"]}
@@ -1,11 +1,15 @@
1
1
  export declare const ATTR_QUERY = "lb-query";
2
2
  export declare const ATTR_COLUMN = "lb-column";
3
3
  export declare const ATTR_SHOW = "lb-show";
4
+ export declare const SHOW_NOT = "!";
4
5
  export declare const ATTR_COLUMN_VALUE = "lb-column-value";
5
6
  export declare const ATTR_KEY_VALUE = "lb-key-value";
7
+ export declare const ATTR_ROW_LIVE = "lb-row-live";
8
+ export declare const LIVE_ROW = "[lb-row-live]";
6
9
  export declare const ATTR_QUERY_ROW_COUNT = "lb-query-row-count";
7
10
  export declare const ATTR_REQUEST = "lb-request";
8
11
  export declare const LB_EVENT_NAME = "lb-request";
12
+ export declare const LB_DONE_EVENT_NAME = "lb-request-done";
9
13
  export declare const ATTR_REQUEST_PENDING = "lb-request-pending";
10
14
  export declare const ATTR_REQUEST_ERROR = "lb-request-error";
11
15
  export declare const LB_RESERVED_PREFIX = "lb-";
@@ -1 +1 @@
1
- {"version":3,"file":"lb-constants.d.ts","sourceRoot":"","sources":["../../core/lb-constants.ts"],"names":[],"mappings":"AAYA,eAAO,MAAM,UAAU,aAAa,CAAC;AAKrC,eAAO,MAAM,WAAW,cAAc,CAAC;AAavC,eAAO,MAAM,SAAS,YAAY,CAAC;AAKnC,eAAO,MAAM,iBAAiB,oBAAoB,CAAC;AAKnD,eAAO,MAAM,cAAc,iBAAiB,CAAC;AAK7C,eAAO,MAAM,oBAAoB,uBAAuB,CAAC;AAKzD,eAAO,MAAM,YAAY,eAAe,CAAC;AAIzC,eAAO,MAAM,aAAa,eAAe,CAAC;AAI1C,eAAO,MAAM,oBAAoB,uBAAuB,CAAC;AAKzD,eAAO,MAAM,kBAAkB,qBAAqB,CAAC;AAOrD,eAAO,MAAM,kBAAkB,QAAQ,CAAC;AAIxC,eAAO,MAAM,kBAAkB,kBAAkB,CAAC;AAClD,eAAO,MAAM,kBAAkB,kBAAkB,CAAC;AAClD,eAAO,MAAM,kBAAkB,kBAAkB,CAAC;AAElD,eAAO,MAAM,eAAe,8DAIlB,CAAC;AAGX,eAAO,MAAM,QAAQ,QAAQ,CAAC;AAC9B,eAAO,MAAM,SAAS,SAAS,CAAC;AAKhC,eAAO,MAAM,SAAS,WAAW,CAAC;AAClC,eAAO,MAAM,eAAe,YAAY,CAAC;AACzC,eAAO,MAAM,qBAAqB,kBAAkB,CAAC;AACrD,eAAO,MAAM,uBAAuB,oBAAoB,CAAC;AAGzD,eAAO,MAAM,aAAa,gBAAgB,CAAC;AAI3C,eAAO,MAAM,aAAa,gBAAgB,CAAC;AAG3C,eAAO,MAAM,gBAAgB,mBAAmB,CAAC;AAMjD,eAAO,MAAM,aAAa,gBAAgB,CAAC;AAK3C,eAAO,MAAM,iBAAiB,oBAAoB,CAAC;AAGnD,eAAO,MAAM,SAAS,YAAY,CAAC;AAInC,eAAO,MAAM,eAAe,kBAAkB,CAAC;AAK/C,eAAO,MAAM,oBAAoB,6GAQvB,CAAC;AAQX,eAAO,MAAM,YAAY,WAAW,CAAC;AASrC,eAAO,MAAM,WAAW,QAAQ,CAAC;AAIjC,eAAO,MAAM,qBAAqB,QAAS,CAAC"}
1
+ {"version":3,"file":"lb-constants.d.ts","sourceRoot":"","sources":["../../core/lb-constants.ts"],"names":[],"mappings":"AAYA,eAAO,MAAM,UAAU,aAAa,CAAC;AAKrC,eAAO,MAAM,WAAW,cAAc,CAAC;AAavC,eAAO,MAAM,SAAS,YAAY,CAAC;AAInC,eAAO,MAAM,QAAQ,MAAM,CAAC;AAK5B,eAAO,MAAM,iBAAiB,oBAAoB,CAAC;AAKnD,eAAO,MAAM,cAAc,iBAAiB,CAAC;AAM7C,eAAO,MAAM,aAAa,gBAAgB,CAAC;AAI3C,eAAO,MAAM,QAAQ,kBAAuB,CAAC;AAK7C,eAAO,MAAM,oBAAoB,uBAAuB,CAAC;AAKzD,eAAO,MAAM,YAAY,eAAe,CAAC;AAIzC,eAAO,MAAM,aAAa,eAAe,CAAC;AAM1C,eAAO,MAAM,kBAAkB,oBAAoB,CAAC;AAIpD,eAAO,MAAM,oBAAoB,uBAAuB,CAAC;AAKzD,eAAO,MAAM,kBAAkB,qBAAqB,CAAC;AAOrD,eAAO,MAAM,kBAAkB,QAAQ,CAAC;AAIxC,eAAO,MAAM,kBAAkB,kBAAkB,CAAC;AAClD,eAAO,MAAM,kBAAkB,kBAAkB,CAAC;AAClD,eAAO,MAAM,kBAAkB,kBAAkB,CAAC;AAElD,eAAO,MAAM,eAAe,8DAIlB,CAAC;AAGX,eAAO,MAAM,QAAQ,QAAQ,CAAC;AAC9B,eAAO,MAAM,SAAS,SAAS,CAAC;AAKhC,eAAO,MAAM,SAAS,WAAW,CAAC;AAClC,eAAO,MAAM,eAAe,YAAY,CAAC;AACzC,eAAO,MAAM,qBAAqB,kBAAkB,CAAC;AACrD,eAAO,MAAM,uBAAuB,oBAAoB,CAAC;AAGzD,eAAO,MAAM,aAAa,gBAAgB,CAAC;AAI3C,eAAO,MAAM,aAAa,gBAAgB,CAAC;AAG3C,eAAO,MAAM,gBAAgB,mBAAmB,CAAC;AAMjD,eAAO,MAAM,aAAa,gBAAgB,CAAC;AAK3C,eAAO,MAAM,iBAAiB,oBAAoB,CAAC;AAGnD,eAAO,MAAM,SAAS,YAAY,CAAC;AAInC,eAAO,MAAM,eAAe,kBAAkB,CAAC;AAK/C,eAAO,MAAM,oBAAoB,6GAQvB,CAAC;AAQX,eAAO,MAAM,YAAY,WAAW,CAAC;AASrC,eAAO,MAAM,WAAW,QAAQ,CAAC;AAIjC,eAAO,MAAM,qBAAqB,QAAS,CAAC"}