@loadbare/app 0.24.0 → 0.25.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.
@@ -1 +1 @@
1
- {"version":3,"file":"assemble.d.ts","sourceRoot":"","sources":["../../build/assemble.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AA4CH,OAAO,KAAK,EAAY,aAAa,EAAE,MAAM,eAAe,CAAC;AAC7D,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAC/C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAigB/C,MAAM,WAAW,eAAe;IAC9B,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,QAAQ,EAAE,CAAC;IAClB,8EAA8E;IAC9E,KAAK,EAAE,UAAU,EAAE,CAAC;IACpB;;;OAGG;IACH,QAAQ,CAAC,EAAE,aAAa,CAAC;CAC1B;AAED,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,4EAA4E;IAC5E,QAAQ,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;CACvB;AAED,wBAAsB,QAAQ,CAAC,EAC7B,UAAU,EACV,KAAK,EACL,KAAK,EACL,QAAQ,GACT,EAAE,eAAe,GAAG,OAAO,CAAC,SAAS,CAAC,CAgEtC"}
1
+ {"version":3,"file":"assemble.d.ts","sourceRoot":"","sources":["../../build/assemble.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AA6CH,OAAO,KAAK,EAAY,aAAa,EAAE,MAAM,eAAe,CAAC;AAC7D,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAC/C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAsjB/C,MAAM,WAAW,eAAe;IAC9B,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,QAAQ,EAAE,CAAC;IAClB,8EAA8E;IAC9E,KAAK,EAAE,UAAU,EAAE,CAAC;IACpB;;;OAGG;IACH,QAAQ,CAAC,EAAE,aAAa,CAAC;CAC1B;AAED,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,4EAA4E;IAC5E,QAAQ,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;CACvB;AAED,wBAAsB,QAAQ,CAAC,EAC7B,UAAU,EACV,KAAK,EACL,KAAK,EACL,QAAQ,GACT,EAAE,eAAe,GAAG,OAAO,CAAC,SAAS,CAAC,CAgEtC"}
@@ -19,7 +19,7 @@
19
19
  import { readFile } from "node:fs/promises";
20
20
  import { addDefinitions, customElementTags, expandTree, } from "./expand.js";
21
21
  import { formatHtml } from "./format.js";
22
- import { AGGREGATES, ATTR_FILTER, ATTR_MANUAL, ATTR_ORDER, MANUAL_FILTER, MANUAL_ORDER, ATTR_COLUMN, ATTR_COUNT, ATTR_GROUP, ATTR_PAGE, ATTR_PAGE_TITLE, ATTR_QUERY, ATTR_REQUEST, ATTR_SELECT, ATTR_SHOW, ATTR_URL_LINK, ATTR_URL_PUSH, ATTR_URL_PARM, ATTR_URL_UNKNOWN, CHOICES_QUERY, HUB_QUERY, DEVELOPER_ATTRIBUTES, HUB_TAG_NAME, LB_COMMAND_PREFIX, LB_COMMANDS, LB_RESERVED_PREFIX, REQUEST_ROW_DELETE, REQUEST_ROW_INSERT, REQUEST_ROW_UPDATE, SHOW_NOT, URL_QUERY, } from "../core/lb-constants.js";
22
+ import { AGGREGATES, ATTR_FILTER, ATTR_MANUAL, ATTR_ORDER, MANUAL_FILTER, MANUAL_ORDER, ATTR_COLUMN, ATTR_COUNT, ATTR_GROUP, ATTR_PAGE, ATTR_PAGE_TITLE, ATTR_QUERY, ATTR_REQUEST, ATTR_SELECT, ATTR_SHOW, ATTR_URL_LINK, ATTR_URL_PUSH, ATTR_URL_PARM, ATTR_URL_UNKNOWN, CHOICES_QUERY, HUB_QUERY, DEVELOPER_ATTRIBUTES, HUB_TAG_NAME, LB_COMMAND_PREFIX, COMMAND_NEW, LB_COMMANDS, LB_RESERVED_PREFIX, REQUEST_ROW_DELETE, REQUEST_ROW_INSERT, REQUEST_ROW_UPDATE, SHOW_NOT, URL_QUERY, } from "../core/lb-constants.js";
23
23
  import { isRowRequest } from "../core/lb-types.js";
24
24
  /**
25
25
  * The hub is one element, and it swaps each page into the one `<main>`
@@ -388,10 +388,60 @@ function expandFile(file, source, defs) {
388
388
  function checkMarkup(file, root, names) {
389
389
  const problems = [];
390
390
  walkMarkup(root, null, names, problems);
391
+ checkNewTargets(root, problems);
391
392
  if (problems.length > 0) {
392
393
  throw new Error(`assemble: '${file}' markup:\n ` + problems.join(`\n `));
393
394
  }
394
395
  }
396
+ /** Every element under root, template content included. */
397
+ function everyElement(root) {
398
+ const found = [];
399
+ for (const el of root.querySelectorAll("*")) {
400
+ found.push(el);
401
+ if (el.localName === "template") {
402
+ found.push(...everyElement(el.content));
403
+ }
404
+ }
405
+ return found;
406
+ }
407
+ /**
408
+ * `--lb-new` aimed at a list rather than a form puts a row not yet entered
409
+ * in it, which only the submit of the form around it carries: the list is a
410
+ * query nested directly in a form's own query. A target the markup does not
411
+ * hold, as a page's own element named from the chrome, is not judged.
412
+ */
413
+ function checkNewTargets(root, problems) {
414
+ const all = everyElement(root);
415
+ const byId = new Map();
416
+ for (const el of all) {
417
+ const id = el.getAttribute("id");
418
+ if (id !== null && !byId.has(id))
419
+ byId.set(id, el);
420
+ }
421
+ for (const button of all) {
422
+ if (button.getAttribute("command") !== COMMAND_NEW)
423
+ continue;
424
+ const target = byId.get(button.getAttribute("commandfor") ?? "");
425
+ if (!target || target.localName === "form")
426
+ continue;
427
+ const tag = target.localName;
428
+ if (!target.hasAttribute(ATTR_QUERY)) {
429
+ problems.push(`<${tag} id="${target.id}"> is aimed at by ${COMMAND_NEW}, and is ` +
430
+ `neither a <form> nor a list carrying ${ATTR_QUERY}`);
431
+ continue;
432
+ }
433
+ const form = target.closest("form");
434
+ const own = form?.hasAttribute(ATTR_QUERY)
435
+ ? form
436
+ : form?.parentElement?.closest(`[${ATTR_QUERY}]`);
437
+ const around = target.parentElement?.closest(`[${ATTR_QUERY}]`);
438
+ if (!form || !own || around !== own) {
439
+ problems.push(`<${tag} id="${target.id}"> is aimed at by ${COMMAND_NEW}, and is ` +
440
+ `not a query nested directly in a form's own; a row not yet ` +
441
+ `entered goes with the submit of the form around it`);
442
+ }
443
+ }
444
+ }
395
445
  /**
396
446
  * A page file's `<title>` names the page. It is lifted out, since a page is
397
447
  * a fragment inside `<main>` and a `<title>` belongs in a document's head,
@@ -1 +1 @@
1
- {"version":3,"file":"assemble.js","sourceRoot":"","sources":["../../build/assemble.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Assembling one document out of a chrome file and a set of pages.\n *\n * Every `.html` file in play — the chrome and each page — goes through the\n * same `expand()` (see build/expand.ts) against the same definitions: the\n * built-in widgets and whatever definitions were found in the caller's own\n * source tree. `expand()` only ever sees a fragment, so the chrome's own is\n * the text between its `<body>` tags; a page's is the whole file. A page's\n * expanded host is wrapped in `<template lb-page=\"<name>\">` and, together\n * with the chrome's own expanded body content, spliced back into the\n * chrome's `<body>`. The result is the one document a server has to serve\n * for every route — see docs/tutorials/000-getting-started.md and\n * docs/reference/builder.md.\n *\n * Every path here is a parameter, resolved once by build/locations.ts — this\n * module never reconstructs one of its own, and never reads a directory\n * looking for pages or widgets; it is handed exactly which files those are.\n */\n\nimport { readFile } from \"node:fs/promises\";\nimport {\n addDefinitions,\n customElementTags,\n expandTree,\n type Definitions,\n} from \"./expand.js\";\nimport { formatHtml } from \"./format.js\";\nimport {\n AGGREGATES,\n ATTR_FILTER,\n ATTR_MANUAL,\n ATTR_ORDER,\n MANUAL_FILTER,\n MANUAL_ORDER,\n ATTR_COLUMN,\n ATTR_COUNT,\n ATTR_GROUP,\n ATTR_PAGE,\n ATTR_PAGE_TITLE,\n ATTR_QUERY,\n ATTR_REQUEST,\n ATTR_SELECT,\n ATTR_SHOW,\n ATTR_URL_LINK,\n ATTR_URL_PUSH,\n ATTR_URL_PARM,\n ATTR_URL_UNKNOWN,\n CHOICES_QUERY,\n HUB_QUERY,\n DEVELOPER_ATTRIBUTES,\n HUB_TAG_NAME,\n LB_COMMAND_PREFIX,\n LB_COMMANDS,\n LB_RESERVED_PREFIX,\n REQUEST_ROW_DELETE,\n REQUEST_ROW_INSERT,\n REQUEST_ROW_UPDATE,\n SHOW_NOT,\n URL_QUERY,\n} from \"../core/lb-constants.js\";\nimport { isRowRequest } from \"../core/lb-types.js\";\nimport type { Declared, DeclaredPages } from \"./declared.js\";\nimport type { PageFile } from \"./locations.js\";\nimport type { OriginScan } from \"./origins.js\";\n\n/**\n * The hub is one element, and it swaps each page into the one `<main>`\n * inside it (hub/lb-hub.browser.ts), so the chrome holds exactly one of each,\n * the `<main>` inside the hub and empty, since whatever it held would be\n * replaced by the first page. A page is swapped into that `<main>`, so it\n * carries neither.\n */\nfunction checkChrome(chromeFile: string, body: ParentNode): void {\n const where = `assemble: '${chromeFile}'`;\n const hubs = body.querySelectorAll(HUB_TAG_NAME).length;\n if (hubs !== 1) {\n throw new Error(\n `${where} has ${hubs} <${HUB_TAG_NAME}>; it needs exactly one`,\n );\n }\n const mains = body.querySelectorAll(\"main\");\n if (mains.length !== 1) {\n throw new Error(\n `${where} has ${mains.length} <main>; it needs exactly one`,\n );\n }\n const main = mains[0]!;\n if (!main.closest(HUB_TAG_NAME)) {\n throw new Error(\n `${where} has its <main> outside <${HUB_TAG_NAME}>, where the hub cannot reach it`,\n );\n }\n if (main.children.length > 0 || main.textContent!.trim() !== \"\") {\n throw new Error(\n `${where} has content in its <main>, which the first page replaces; it goes empty`,\n );\n }\n}\n\n/** A page lands inside the chrome's `<main>`, so it brings no second one and no hub. */\nfunction checkPage(file: string, root: ParentNode): void {\n for (const tag of [HUB_TAG_NAME, \"main\"]) {\n if (root.querySelector(tag)) {\n throw new Error(\n `assemble: '${file}' carries a <${tag}>, which belongs to the chrome alone`,\n );\n }\n }\n}\n\n/**\n * The hub calls showModal() on the `<dialog>` carrying lb-url-unknown\n * (hub/lb-hub.browser.ts), and looks for it inside itself, as it does for\n * everything else it acts on. A dialog elsewhere in the body would never open\n * and would say nothing about why.\n */\nfunction checkUnknownPageDialog(chromeFile: string, body: ParentNode): void {\n const el = body.querySelector(`[${ATTR_URL_UNKNOWN}]`);\n if (!el) return;\n if (!el.closest(HUB_TAG_NAME)) {\n throw new Error(\n `assemble: '${chromeFile}' declares ${ATTR_URL_UNKNOWN} outside ` +\n `<${HUB_TAG_NAME}>, where the hub cannot reach it`,\n );\n }\n}\n\n/**\n * Where the markup stands while the check walks it: inside which `lb-query`,\n * and whether there is a row there to take a value from. Inside a row\n * template there always is. Inside an `lb-query` outside one, there is when\n * the query is of kind `row`, which only the server knows.\n */\ninterface Scope {\n query: string;\n row: boolean | \"unknown\";\n}\n\n/** The queries the hub serves, which every page and the chrome may name. */\nconst HUB_QUERIES: readonly string[] = [URL_QUERY, CHOICES_QUERY, HUB_QUERY];\n\n/** The `Crud` key each request Loadbare provides runs. */\nconst CRUD_KEYS: Record<string, string> = {\n [REQUEST_ROW_INSERT]: \"rowInsert\",\n [REQUEST_ROW_UPDATE]: \"rowUpdate\",\n [REQUEST_ROW_DELETE]: \"rowDelete\",\n};\n\n/**\n * What a file's names are checked against: a page's declarations, or `null`\n * for the chrome, which belongs to no page and so names only what the hub\n * serves.\n */\ntype Names = { declared: Declared | null };\n\n/**\n * An `lb-query` or `lb-request` naming nothing the page declares would be\n * found only on the first round trip, as a server warning and an empty\n * answer, so a typo would build cleanly.\n *\n * A request Loadbare provides is for the query of what it gathers: a form's\n * own when the form carries `lb-query`, otherwise the nearest around the\n * element, as the hub decides it (hub/lb-hub.browser.ts). With no query\n * around it, the hub reports it in the browser.\n */\nfunction checkNames(\n el: Element,\n scope: Scope | null,\n names: Names,\n problems: string[],\n): void {\n const tag = el.localName;\n const { declared } = names;\n\n const query = el.getAttribute(ATTR_QUERY);\n if (query !== null && !HUB_QUERIES.includes(query)) {\n if (declared === null) {\n problems.push(\n `<${tag}> carries ${ATTR_QUERY}=\"${query}\"; the chrome belongs to ` +\n `no page, and names only ${HUB_QUERIES.slice(0, -1).join(\", \")} and ${HUB_QUERIES.at(-1)}`,\n );\n } else if (!declared.queries.has(query)) {\n problems.push(\n `<${tag}> carries ${ATTR_QUERY}=\"${query}\", which the page's ` +\n `queries do not declare`,\n );\n }\n }\n\n const request = el.getAttribute(ATTR_REQUEST);\n if (!request) return;\n if (isRowRequest(request)) {\n const target =\n tag === \"form\" && query !== null ? query : (scope?.query ?? null);\n if (target === null) return;\n if (target === HUB_QUERY) {\n problems.push(\n `<${tag}> carries ${ATTR_REQUEST}=\"${request}\" for '${target}'; ` +\n `the hub's own state is written by the hub alone`,\n );\n return;\n }\n if (HUB_QUERIES.includes(target)) {\n if (request !== REQUEST_ROW_UPDATE) {\n problems.push(\n `<${tag}> carries ${ATTR_REQUEST}=\"${request}\" for '${target}'; ` +\n `the hub answers only ${REQUEST_ROW_UPDATE} for it`,\n );\n }\n return;\n }\n // A query the page does not declare is reported once, where it is named.\n if (declared === null || !declared.queries.has(target)) return;\n const key = CRUD_KEYS[request]!;\n if (!declared.crud.get(target)?.has(key)) {\n problems.push(\n `<${tag}> carries ${ATTR_REQUEST}=\"${request}\" for '${target}', ` +\n `whose crud entry the page does not declare with ${key}`,\n );\n }\n } else if (request.startsWith(LB_RESERVED_PREFIX)) {\n // Reported by walkMarkup as naming no request Loadbare provides.\n } else if (declared === null) {\n problems.push(\n `<${tag}> carries ${ATTR_REQUEST}=\"${request}\"; the chrome belongs ` +\n `to no page, and sends no declared request`,\n );\n } else if (!declared.handlers.has(request)) {\n problems.push(\n `<${tag}> carries ${ATTR_REQUEST}=\"${request}\", which the page's ` +\n `handlers do not declare`,\n );\n }\n}\n\n/**\n * The markup, checked once against the finished document rather than\n * discovered one console error at a time in a browser.\n *\n * Every rule here is a fact the markup states about itself. The developer\n * writes `lb-*` attributes in markup, and script never assigns them, which is\n * what makes checking the markup sound. See docs/reference/data-binding.md.\n *\n * A `<template>`'s children are walked in the scope that encloses the\n * template, because a row template's clones land in the element around it.\n */\nfunction walkMarkup(\n node: ParentNode,\n scope: Scope | null,\n names: Names | null,\n problems: string[],\n): void {\n for (const el of node.children) {\n const tag = el.localName;\n let here = scope;\n const query = el.getAttribute(ATTR_QUERY);\n if (query !== null) here = { query, row: \"unknown\" };\n if (names) checkNames(el, scope, names, problems);\n\n for (const { name } of el.attributes) {\n if (\n name.startsWith(LB_RESERVED_PREFIX) &&\n !(DEVELOPER_ATTRIBUTES as readonly string[]).includes(name)\n ) {\n problems.push(\n `<${tag}> carries ${name}, which is not an attribute a developer ` +\n `writes`,\n );\n }\n }\n\n const request = el.getAttribute(ATTR_REQUEST);\n if (\n request !== null &&\n request.startsWith(LB_RESERVED_PREFIX) &&\n !isRowRequest(request)\n ) {\n problems.push(\n `<${tag}> carries ${ATTR_REQUEST}=\"${request}\", which begins with ` +\n `'${LB_RESERVED_PREFIX}' but names no request Loadbare provides`,\n );\n }\n if (el.hasAttribute(ATTR_URL_LINK) && tag !== \"a\") {\n problems.push(`<${tag}> carries ${ATTR_URL_LINK}; it goes on an <a>`);\n }\n // A link's parms are columns of the row around it, and only a link the\n // hub handles takes them: the parms are the application's own URL.\n const parms = el.getAttribute(ATTR_URL_PARM);\n if (parms !== null) {\n const columns = parms.split(\",\").map((column) => column.trim());\n if (tag !== \"a\" || !el.hasAttribute(ATTR_URL_LINK)) {\n problems.push(\n `<${tag}> carries ${ATTR_URL_PARM}; it goes on an <a> carrying ` +\n `${ATTR_URL_LINK}`,\n );\n } else if (columns.some((column) => column === \"\")) {\n problems.push(\n `<${tag}> carries ${ATTR_URL_PARM}=\"${parms}\", which names no ` +\n `column between two commas or at either end`,\n );\n } else if (columns.some((column) => /\\s/.test(column))) {\n problems.push(\n `<${tag}> carries ${ATTR_URL_PARM}=\"${parms}\"; columns are ` +\n `separated by commas`,\n );\n } else if (columns.some((c) => c.startsWith(LB_RESERVED_PREFIX))) {\n problems.push(\n `<${tag}> carries ${ATTR_URL_PARM}=\"${parms}\"; a parm beginning ` +\n `'${LB_RESERVED_PREFIX}' is the hub's own`,\n );\n } else if (scope === null || scope.row === false) {\n problems.push(\n `<${tag}> carries ${ATTR_URL_PARM}=\"${parms}\" with no row around ` +\n `it to take the values from`,\n );\n }\n }\n if (el.hasAttribute(ATTR_URL_PUSH) && request === null) {\n problems.push(\n `<${tag}> carries ${ATTR_URL_PUSH} and no ${ATTR_REQUEST}; it goes ` +\n `on an element with ${ATTR_REQUEST}`,\n );\n }\n // A column holds one value, except a query parm, which a URL may repeat.\n // The column belongs to the query around the element, even one that\n // names a query of its own, as a picker does.\n if (\n el.hasAttribute(ATTR_COLUMN) &&\n (el.hasAttribute(\"multiple\") ||\n (tag === \"input\" &&\n el.getAttribute(\"type\") === \"checkbox\" &&\n el.hasAttribute(\"value\"))) &&\n scope?.query !== URL_QUERY\n ) {\n problems.push(\n `<${tag}> carries ${ATTR_COLUMN} and holds several values; only a ` +\n `column of ${URL_QUERY} holds several, so it goes inside ` +\n `${ATTR_QUERY}=\"${URL_QUERY}\"`,\n );\n }\n // A selector is a checkbox in a live row, and selects that row. It is no\n // column, and commits nothing of its own.\n if (el.hasAttribute(ATTR_SELECT)) {\n if (tag !== \"input\" || el.getAttribute(\"type\") !== \"checkbox\") {\n problems.push(\n `<${tag}> carries ${ATTR_SELECT}; it goes on an ` +\n `<input type=\"checkbox\">`,\n );\n } else if (scope?.row !== true) {\n problems.push(\n `<input> carries ${ATTR_SELECT} outside a row template; it ` +\n `selects the row it sits in`,\n );\n }\n for (const name of [ATTR_COLUMN, ATTR_REQUEST]) {\n if (el.hasAttribute(name)) {\n problems.push(\n `<${tag}> carries ${ATTR_SELECT} and ${name}; a selector is no ` +\n `column and sends nothing`,\n );\n }\n }\n }\n // HTML's own command attribute, whose `--lb-` values are Loadbare's.\n const command = el.getAttribute(\"command\");\n if (command !== null && command.startsWith(LB_COMMAND_PREFIX)) {\n if (!(LB_COMMANDS as readonly string[]).includes(command)) {\n problems.push(\n `<${tag}> carries command=\"${command}\", which begins with ` +\n `'${LB_COMMAND_PREFIX}' but names no command Loadbare provides`,\n );\n } else if (!el.hasAttribute(\"commandfor\")) {\n problems.push(\n `<${tag}> carries command=\"${command}\" and no commandfor; it ` +\n `names the element the command is for`,\n );\n }\n }\n if (el.hasAttribute(ATTR_URL_UNKNOWN) && tag !== \"dialog\") {\n problems.push(\n `<${tag}> carries ${ATTR_URL_UNKNOWN}; it goes on a <dialog>`,\n );\n }\n\n // A group is a template holding a heading and the template its\n // contents go in; an aggregate is set on an element, from the column it\n // names.\n if (el.hasAttribute(ATTR_GROUP)) {\n if (tag !== \"template\") {\n problems.push(\n `<${tag}> carries ${ATTR_GROUP}; it goes on a <template>`,\n );\n } else if (\n !(el as HTMLTemplateElement).content.querySelector(\n `template:not([${ATTR_SHOW}])`,\n )\n ) {\n problems.push(\n `a <template ${ATTR_GROUP}> holds no <template> for its contents`,\n );\n }\n }\n for (const name of AGGREGATES) {\n if (!el.hasAttribute(name)) continue;\n if (tag === \"template\") {\n problems.push(`a <template> carries ${name}; it goes on an element`);\n } else if (name !== ATTR_COUNT && !el.getAttribute(name)) {\n problems.push(`<${tag}> carries ${name} naming no column`);\n }\n }\n\n // The user's order and filter for the rows an element holds, each read\n // from a column of lb-choices it names.\n for (const name of [ATTR_ORDER, ATTR_FILTER]) {\n const value = el.getAttribute(name);\n if (value === null) continue;\n if (!el.hasAttribute(ATTR_QUERY)) {\n problems.push(\n `<${tag}> carries ${name}; it goes on the element carrying ` +\n `${ATTR_QUERY}`,\n );\n } else if (value.trim() === \"\") {\n problems.push(`<${tag}> carries ${name} naming no column`);\n }\n }\n const manual = el.getAttribute(ATTR_MANUAL);\n if (manual !== null) {\n const sides = manual.trim().split(/\\s+/).filter((side) => side !== \"\");\n const known = [MANUAL_ORDER, MANUAL_FILTER];\n if (!el.hasAttribute(ATTR_QUERY)) {\n problems.push(\n `<${tag}> carries ${ATTR_MANUAL}; it goes on the element carrying ` +\n `${ATTR_QUERY}`,\n );\n } else if (\n sides.length === 0 ||\n sides.some((side) => !known.includes(side))\n ) {\n problems.push(\n `<${tag}> carries ${ATTR_MANUAL}=\"${manual}\"; it names ` +\n `${MANUAL_ORDER}, ${MANUAL_FILTER} or both, separated by a space`,\n );\n }\n }\n\n // Judged by the scope around the element, not one the element carries:\n // its condition is a column of the row around it. With no lb-query\n // around it at all, nothing ever lands on it, and the element never\n // shows.\n const show = el.getAttribute(ATTR_SHOW);\n if (show !== null) {\n if (tag === \"template\") {\n problems.push(\n `a <template> carries ${ATTR_SHOW}=\"${show}\"; write it on the ` +\n `element it shows, and Loadbare supplies the template`,\n );\n } else if (scope === null || scope.row === false) {\n problems.push(\n `<${tag}> carries ${ATTR_SHOW}=\"${show}\" with no row around it ` +\n `to take the value from`,\n );\n } else if (show === SHOW_NOT || show.startsWith(SHOW_NOT + SHOW_NOT)) {\n problems.push(\n `<${tag}> carries ${ATTR_SHOW}=\"${show}\"; write one ` +\n `${SHOW_NOT} and then the column it reverses`,\n );\n }\n }\n\n walkMarkup(el, here, names, problems);\n // By tag name rather than by instanceof: template content belongs to a\n // document with no window, so the constructor is not reachable from the\n // node.\n if (tag === \"template\") {\n const content = (el as HTMLTemplateElement).content;\n const isRowTemplate =\n scope !== null && !!el.parentElement?.closest(`[${ATTR_QUERY}]`);\n if (isRowTemplate) {\n for (const root of content.children) {\n if (root.hasAttribute(ATTR_SHOW)) {\n problems.push(\n `<${root.localName}> is a row template's root and carries ` +\n `${ATTR_SHOW}; a row that should not show is left out by ` +\n `the query`,\n );\n }\n }\n }\n walkMarkup(\n content,\n isRowTemplate ? { query: scope!.query, row: true } : here,\n names,\n problems,\n );\n }\n }\n}\n\n/**\n * Ship every `lb-show` element already inside the template the hub keeps an\n * absent branch in, so nothing conditional shows until its value says so.\n * A page is in the document before its data arrives, and a value that has not\n * arrived is no value.\n *\n * Elements are collected before any is moved, and a wrapped element is still\n * reached once its outer one has gone into a template, because the references\n * are held.\n */\nfunction wrapShown(holder: HTMLTemplateElement): void {\n const doc = holder.ownerDocument;\n const shown: Element[] = [];\n const collect = (root: ParentNode): void => {\n shown.push(...root.querySelectorAll(`[${ATTR_SHOW}]`));\n for (const t of root.querySelectorAll(\"template\")) {\n collect((t as HTMLTemplateElement).content);\n }\n };\n collect(holder.content);\n for (const el of shown) {\n const parked = doc.createElement(\"template\");\n parked.setAttribute(ATTR_SHOW, el.getAttribute(ATTR_SHOW)!);\n el.replaceWith(parked);\n parked.content.append(el);\n }\n}\n\n/**\n * Expand one file's markup, naming the file if expansion refuses it.\n *\n * What comes back is the tree the browser will build, so every pass after\n * this reads and changes it in place, and it is serialized once, when it\n * ships.\n */\nfunction expandFile(\n file: string,\n source: string,\n defs: Definitions,\n): HTMLTemplateElement {\n try {\n return expandTree(source, defs);\n } catch (e) {\n throw new Error(`assemble: '${file}': ${(e as Error).message}`);\n }\n}\n\n/** Check one expanded fragment, and name the file if anything is wrong. */\nfunction checkMarkup(\n file: string,\n root: ParentNode,\n names: Names | null,\n): void {\n const problems: string[] = [];\n walkMarkup(root, null, names, problems);\n if (problems.length > 0) {\n throw new Error(`assemble: '${file}' markup:\\n ` + problems.join(`\\n `));\n }\n}\n\n/**\n * A page file's `<title>` names the page. It is lifted out, since a page is\n * a fragment inside `<main>` and a `<title>` belongs in a document's head,\n * and its text content is stamped on the page as ATTR_PAGE_TITLE.\n */\nfunction liftTitle(holder: HTMLTemplateElement): string | null {\n const title = holder.content.querySelector(\"title\");\n if (!title) return null;\n const text = title.textContent?.trim() ?? \"\";\n title.remove();\n return text;\n}\n\n/** An attribute value, escaped for a double-quoted attribute. */\nfunction attributeValue(text: string): string {\n return text.replace(/&/g, \"&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 * What each page declares — see build/declared.ts. Without it, the names\n * `lb-query` and `lb-request` carry are not checked.\n */\n declared?: DeclaredPages;\n}\n\nexport interface Assembled {\n html: string;\n /** Every custom element tag left in the document once expansion is done. */\n usedTags: Set<string>;\n}\n\nexport async function assemble({\n chromeFile,\n pages,\n scans,\n declared,\n}: AssembleOptions): Promise<Assembled> {\n // One layer per origin, in order, so a later origin's definition overrides\n // an earlier one's for the same tag. Each layer is checked for cycles as it\n // lands, against everything merged so far.\n let defs: Definitions = {};\n for (const scan of scans) {\n defs = await addDefinitions(defs, scan.definitions);\n }\n\n // Tags are collected from each tree as it goes by. What ships is these\n // trees and the chrome around its body, which is collected on its own.\n const usedTags = new Set<string>();\n const templates: string[] = [];\n for (const { name, file } of pages) {\n const authored = await readFile(file, \"utf-8\");\n const host = expandFile(file, authored, defs);\n const title = liftTitle(host);\n checkPage(file, host.content);\n checkMarkup(\n file,\n host.content,\n declared ? { declared: declared.get(name)! } : null,\n );\n wrapShown(host);\n customElementTags(host.content, usedTags);\n const titled =\n title === null ? \"\" : ` ${ATTR_PAGE_TITLE}=\"${attributeValue(title)}\"`;\n templates.push(\n `<template ${ATTR_PAGE}=\"${name}\"${titled}>${host.innerHTML}</template>`,\n );\n }\n\n let chromeSource: string;\n try {\n chromeSource = await readFile(chromeFile, \"utf-8\");\n } catch {\n throw new Error(`assemble: no chrome file at '${chromeFile}'`);\n }\n\n const body = /<body([^>]*)>([\\s\\S]*)<\\/body>/i.exec(chromeSource);\n if (!body) {\n throw new Error(\n `assemble: '${chromeFile}' has no <body>...</body> to expand`,\n );\n }\n const [whole, bodyAttrs, bodyContent] = body;\n const chromeBody = expandFile(chromeFile, bodyContent!, defs);\n checkChrome(chromeFile, chromeBody.content);\n checkUnknownPageDialog(chromeFile, chromeBody.content);\n checkMarkup(\n chromeFile,\n chromeBody.content,\n declared ? { declared: null } : null,\n );\n wrapShown(chromeBody);\n customElementTags(chromeBody.content, usedTags);\n customElementTags(chromeSource.replace(whole!, \"\"), usedTags);\n const withPages = chromeSource.replace(\n whole!,\n () =>\n `<body${bodyAttrs}>${chromeBody.innerHTML}\\n${templates.join(\"\\n\")}\\n</body>`,\n );\n\n return { html: await formatHtml(withPages), usedTags };\n}\n"]}
1
+ {"version":3,"file":"assemble.js","sourceRoot":"","sources":["../../build/assemble.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Assembling one document out of a chrome file and a set of pages.\n *\n * Every `.html` file in play — the chrome and each page — goes through the\n * same `expand()` (see build/expand.ts) against the same definitions: the\n * built-in widgets and whatever definitions were found in the caller's own\n * source tree. `expand()` only ever sees a fragment, so the chrome's own is\n * the text between its `<body>` tags; a page's is the whole file. A page's\n * expanded host is wrapped in `<template lb-page=\"<name>\">` and, together\n * with the chrome's own expanded body content, spliced back into the\n * chrome's `<body>`. The result is the one document a server has to serve\n * for every route — see docs/tutorials/000-getting-started.md and\n * docs/reference/builder.md.\n *\n * Every path here is a parameter, resolved once by build/locations.ts — this\n * module never reconstructs one of its own, and never reads a directory\n * looking for pages or widgets; it is handed exactly which files those are.\n */\n\nimport { readFile } from \"node:fs/promises\";\nimport {\n addDefinitions,\n customElementTags,\n expandTree,\n type Definitions,\n} from \"./expand.js\";\nimport { formatHtml } from \"./format.js\";\nimport {\n AGGREGATES,\n ATTR_FILTER,\n ATTR_MANUAL,\n ATTR_ORDER,\n MANUAL_FILTER,\n MANUAL_ORDER,\n ATTR_COLUMN,\n ATTR_COUNT,\n ATTR_GROUP,\n ATTR_PAGE,\n ATTR_PAGE_TITLE,\n ATTR_QUERY,\n ATTR_REQUEST,\n ATTR_SELECT,\n ATTR_SHOW,\n ATTR_URL_LINK,\n ATTR_URL_PUSH,\n ATTR_URL_PARM,\n ATTR_URL_UNKNOWN,\n CHOICES_QUERY,\n HUB_QUERY,\n DEVELOPER_ATTRIBUTES,\n HUB_TAG_NAME,\n LB_COMMAND_PREFIX,\n COMMAND_NEW,\n LB_COMMANDS,\n LB_RESERVED_PREFIX,\n REQUEST_ROW_DELETE,\n REQUEST_ROW_INSERT,\n REQUEST_ROW_UPDATE,\n SHOW_NOT,\n URL_QUERY,\n} from \"../core/lb-constants.js\";\nimport { isRowRequest } from \"../core/lb-types.js\";\nimport type { Declared, DeclaredPages } from \"./declared.js\";\nimport type { PageFile } from \"./locations.js\";\nimport type { OriginScan } from \"./origins.js\";\n\n/**\n * The hub is one element, and it swaps each page into the one `<main>`\n * inside it (hub/lb-hub.browser.ts), so the chrome holds exactly one of each,\n * the `<main>` inside the hub and empty, since whatever it held would be\n * replaced by the first page. A page is swapped into that `<main>`, so it\n * carries neither.\n */\nfunction checkChrome(chromeFile: string, body: ParentNode): void {\n const where = `assemble: '${chromeFile}'`;\n const hubs = body.querySelectorAll(HUB_TAG_NAME).length;\n if (hubs !== 1) {\n throw new Error(\n `${where} has ${hubs} <${HUB_TAG_NAME}>; it needs exactly one`,\n );\n }\n const mains = body.querySelectorAll(\"main\");\n if (mains.length !== 1) {\n throw new Error(\n `${where} has ${mains.length} <main>; it needs exactly one`,\n );\n }\n const main = mains[0]!;\n if (!main.closest(HUB_TAG_NAME)) {\n throw new Error(\n `${where} has its <main> outside <${HUB_TAG_NAME}>, where the hub cannot reach it`,\n );\n }\n if (main.children.length > 0 || main.textContent!.trim() !== \"\") {\n throw new Error(\n `${where} has content in its <main>, which the first page replaces; it goes empty`,\n );\n }\n}\n\n/** A page lands inside the chrome's `<main>`, so it brings no second one and no hub. */\nfunction checkPage(file: string, root: ParentNode): void {\n for (const tag of [HUB_TAG_NAME, \"main\"]) {\n if (root.querySelector(tag)) {\n throw new Error(\n `assemble: '${file}' carries a <${tag}>, which belongs to the chrome alone`,\n );\n }\n }\n}\n\n/**\n * The hub calls showModal() on the `<dialog>` carrying lb-url-unknown\n * (hub/lb-hub.browser.ts), and looks for it inside itself, as it does for\n * everything else it acts on. A dialog elsewhere in the body would never open\n * and would say nothing about why.\n */\nfunction checkUnknownPageDialog(chromeFile: string, body: ParentNode): void {\n const el = body.querySelector(`[${ATTR_URL_UNKNOWN}]`);\n if (!el) return;\n if (!el.closest(HUB_TAG_NAME)) {\n throw new Error(\n `assemble: '${chromeFile}' declares ${ATTR_URL_UNKNOWN} outside ` +\n `<${HUB_TAG_NAME}>, where the hub cannot reach it`,\n );\n }\n}\n\n/**\n * Where the markup stands while the check walks it: inside which `lb-query`,\n * and whether there is a row there to take a value from. Inside a row\n * template there always is. Inside an `lb-query` outside one, there is when\n * the query is of kind `row`, which only the server knows.\n */\ninterface Scope {\n query: string;\n row: boolean | \"unknown\";\n}\n\n/** The queries the hub serves, which every page and the chrome may name. */\nconst HUB_QUERIES: readonly string[] = [URL_QUERY, CHOICES_QUERY, HUB_QUERY];\n\n/** The `Crud` key each request Loadbare provides runs. */\nconst CRUD_KEYS: Record<string, string> = {\n [REQUEST_ROW_INSERT]: \"rowInsert\",\n [REQUEST_ROW_UPDATE]: \"rowUpdate\",\n [REQUEST_ROW_DELETE]: \"rowDelete\",\n};\n\n/**\n * What a file's names are checked against: a page's declarations, or `null`\n * for the chrome, which belongs to no page and so names only what the hub\n * serves.\n */\ntype Names = { declared: Declared | null };\n\n/**\n * An `lb-query` or `lb-request` naming nothing the page declares would be\n * found only on the first round trip, as a server warning and an empty\n * answer, so a typo would build cleanly.\n *\n * A request Loadbare provides is for the query of what it gathers: a form's\n * own when the form carries `lb-query`, otherwise the nearest around the\n * element, as the hub decides it (hub/lb-hub.browser.ts). With no query\n * around it, the hub reports it in the browser.\n */\nfunction checkNames(\n el: Element,\n scope: Scope | null,\n names: Names,\n problems: string[],\n): void {\n const tag = el.localName;\n const { declared } = names;\n\n const query = el.getAttribute(ATTR_QUERY);\n if (query !== null && !HUB_QUERIES.includes(query)) {\n if (declared === null) {\n problems.push(\n `<${tag}> carries ${ATTR_QUERY}=\"${query}\"; the chrome belongs to ` +\n `no page, and names only ${HUB_QUERIES.slice(0, -1).join(\", \")} and ${HUB_QUERIES.at(-1)}`,\n );\n } else if (!declared.queries.has(query)) {\n problems.push(\n `<${tag}> carries ${ATTR_QUERY}=\"${query}\", which the page's ` +\n `queries do not declare`,\n );\n }\n }\n\n const request = el.getAttribute(ATTR_REQUEST);\n if (!request) return;\n if (isRowRequest(request)) {\n const target =\n tag === \"form\" && query !== null ? query : (scope?.query ?? null);\n if (target === null) return;\n if (target === HUB_QUERY) {\n problems.push(\n `<${tag}> carries ${ATTR_REQUEST}=\"${request}\" for '${target}'; ` +\n `the hub's own state is written by the hub alone`,\n );\n return;\n }\n if (HUB_QUERIES.includes(target)) {\n if (request !== REQUEST_ROW_UPDATE) {\n problems.push(\n `<${tag}> carries ${ATTR_REQUEST}=\"${request}\" for '${target}'; ` +\n `the hub answers only ${REQUEST_ROW_UPDATE} for it`,\n );\n }\n return;\n }\n // A query the page does not declare is reported once, where it is named.\n if (declared === null || !declared.queries.has(target)) return;\n const key = CRUD_KEYS[request]!;\n if (!declared.crud.get(target)?.has(key)) {\n problems.push(\n `<${tag}> carries ${ATTR_REQUEST}=\"${request}\" for '${target}', ` +\n `whose crud entry the page does not declare with ${key}`,\n );\n }\n } else if (request.startsWith(LB_RESERVED_PREFIX)) {\n // Reported by walkMarkup as naming no request Loadbare provides.\n } else if (declared === null) {\n problems.push(\n `<${tag}> carries ${ATTR_REQUEST}=\"${request}\"; the chrome belongs ` +\n `to no page, and sends no declared request`,\n );\n } else if (!declared.handlers.has(request)) {\n problems.push(\n `<${tag}> carries ${ATTR_REQUEST}=\"${request}\", which the page's ` +\n `handlers do not declare`,\n );\n }\n}\n\n/**\n * The markup, checked once against the finished document rather than\n * discovered one console error at a time in a browser.\n *\n * Every rule here is a fact the markup states about itself. The developer\n * writes `lb-*` attributes in markup, and script never assigns them, which is\n * what makes checking the markup sound. See docs/reference/data-binding.md.\n *\n * A `<template>`'s children are walked in the scope that encloses the\n * template, because a row template's clones land in the element around it.\n */\nfunction walkMarkup(\n node: ParentNode,\n scope: Scope | null,\n names: Names | null,\n problems: string[],\n): void {\n for (const el of node.children) {\n const tag = el.localName;\n let here = scope;\n const query = el.getAttribute(ATTR_QUERY);\n if (query !== null) here = { query, row: \"unknown\" };\n if (names) checkNames(el, scope, names, problems);\n\n for (const { name } of el.attributes) {\n if (\n name.startsWith(LB_RESERVED_PREFIX) &&\n !(DEVELOPER_ATTRIBUTES as readonly string[]).includes(name)\n ) {\n problems.push(\n `<${tag}> carries ${name}, which is not an attribute a developer ` +\n `writes`,\n );\n }\n }\n\n const request = el.getAttribute(ATTR_REQUEST);\n if (\n request !== null &&\n request.startsWith(LB_RESERVED_PREFIX) &&\n !isRowRequest(request)\n ) {\n problems.push(\n `<${tag}> carries ${ATTR_REQUEST}=\"${request}\", which begins with ` +\n `'${LB_RESERVED_PREFIX}' but names no request Loadbare provides`,\n );\n }\n if (el.hasAttribute(ATTR_URL_LINK) && tag !== \"a\") {\n problems.push(`<${tag}> carries ${ATTR_URL_LINK}; it goes on an <a>`);\n }\n // A link's parms are columns of the row around it, and only a link the\n // hub handles takes them: the parms are the application's own URL.\n const parms = el.getAttribute(ATTR_URL_PARM);\n if (parms !== null) {\n const columns = parms.split(\",\").map((column) => column.trim());\n if (tag !== \"a\" || !el.hasAttribute(ATTR_URL_LINK)) {\n problems.push(\n `<${tag}> carries ${ATTR_URL_PARM}; it goes on an <a> carrying ` +\n `${ATTR_URL_LINK}`,\n );\n } else if (columns.some((column) => column === \"\")) {\n problems.push(\n `<${tag}> carries ${ATTR_URL_PARM}=\"${parms}\", which names no ` +\n `column between two commas or at either end`,\n );\n } else if (columns.some((column) => /\\s/.test(column))) {\n problems.push(\n `<${tag}> carries ${ATTR_URL_PARM}=\"${parms}\"; columns are ` +\n `separated by commas`,\n );\n } else if (columns.some((c) => c.startsWith(LB_RESERVED_PREFIX))) {\n problems.push(\n `<${tag}> carries ${ATTR_URL_PARM}=\"${parms}\"; a parm beginning ` +\n `'${LB_RESERVED_PREFIX}' is the hub's own`,\n );\n } else if (scope === null || scope.row === false) {\n problems.push(\n `<${tag}> carries ${ATTR_URL_PARM}=\"${parms}\" with no row around ` +\n `it to take the values from`,\n );\n }\n }\n if (el.hasAttribute(ATTR_URL_PUSH) && request === null) {\n problems.push(\n `<${tag}> carries ${ATTR_URL_PUSH} and no ${ATTR_REQUEST}; it goes ` +\n `on an element with ${ATTR_REQUEST}`,\n );\n }\n // A column holds one value, except a query parm, which a URL may repeat.\n // The column belongs to the query around the element, even one that\n // names a query of its own, as a picker does.\n if (\n el.hasAttribute(ATTR_COLUMN) &&\n (el.hasAttribute(\"multiple\") ||\n (tag === \"input\" &&\n el.getAttribute(\"type\") === \"checkbox\" &&\n el.hasAttribute(\"value\"))) &&\n scope?.query !== URL_QUERY\n ) {\n problems.push(\n `<${tag}> carries ${ATTR_COLUMN} and holds several values; only a ` +\n `column of ${URL_QUERY} holds several, so it goes inside ` +\n `${ATTR_QUERY}=\"${URL_QUERY}\"`,\n );\n }\n // A selector is a checkbox in a live row, and selects that row. It is no\n // column, and commits nothing of its own.\n if (el.hasAttribute(ATTR_SELECT)) {\n if (tag !== \"input\" || el.getAttribute(\"type\") !== \"checkbox\") {\n problems.push(\n `<${tag}> carries ${ATTR_SELECT}; it goes on an ` +\n `<input type=\"checkbox\">`,\n );\n } else if (scope?.row !== true) {\n problems.push(\n `<input> carries ${ATTR_SELECT} outside a row template; it ` +\n `selects the row it sits in`,\n );\n }\n for (const name of [ATTR_COLUMN, ATTR_REQUEST]) {\n if (el.hasAttribute(name)) {\n problems.push(\n `<${tag}> carries ${ATTR_SELECT} and ${name}; a selector is no ` +\n `column and sends nothing`,\n );\n }\n }\n }\n // HTML's own command attribute, whose `--lb-` values are Loadbare's.\n const command = el.getAttribute(\"command\");\n if (command !== null && command.startsWith(LB_COMMAND_PREFIX)) {\n if (!(LB_COMMANDS as readonly string[]).includes(command)) {\n problems.push(\n `<${tag}> carries command=\"${command}\", which begins with ` +\n `'${LB_COMMAND_PREFIX}' but names no command Loadbare provides`,\n );\n } else if (!el.hasAttribute(\"commandfor\")) {\n problems.push(\n `<${tag}> carries command=\"${command}\" and no commandfor; it ` +\n `names the element the command is for`,\n );\n }\n }\n if (el.hasAttribute(ATTR_URL_UNKNOWN) && tag !== \"dialog\") {\n problems.push(\n `<${tag}> carries ${ATTR_URL_UNKNOWN}; it goes on a <dialog>`,\n );\n }\n\n // A group is a template holding a heading and the template its\n // contents go in; an aggregate is set on an element, from the column it\n // names.\n if (el.hasAttribute(ATTR_GROUP)) {\n if (tag !== \"template\") {\n problems.push(\n `<${tag}> carries ${ATTR_GROUP}; it goes on a <template>`,\n );\n } else if (\n !(el as HTMLTemplateElement).content.querySelector(\n `template:not([${ATTR_SHOW}])`,\n )\n ) {\n problems.push(\n `a <template ${ATTR_GROUP}> holds no <template> for its contents`,\n );\n }\n }\n for (const name of AGGREGATES) {\n if (!el.hasAttribute(name)) continue;\n if (tag === \"template\") {\n problems.push(`a <template> carries ${name}; it goes on an element`);\n } else if (name !== ATTR_COUNT && !el.getAttribute(name)) {\n problems.push(`<${tag}> carries ${name} naming no column`);\n }\n }\n\n // The user's order and filter for the rows an element holds, each read\n // from a column of lb-choices it names.\n for (const name of [ATTR_ORDER, ATTR_FILTER]) {\n const value = el.getAttribute(name);\n if (value === null) continue;\n if (!el.hasAttribute(ATTR_QUERY)) {\n problems.push(\n `<${tag}> carries ${name}; it goes on the element carrying ` +\n `${ATTR_QUERY}`,\n );\n } else if (value.trim() === \"\") {\n problems.push(`<${tag}> carries ${name} naming no column`);\n }\n }\n const manual = el.getAttribute(ATTR_MANUAL);\n if (manual !== null) {\n const sides = manual.trim().split(/\\s+/).filter((side) => side !== \"\");\n const known = [MANUAL_ORDER, MANUAL_FILTER];\n if (!el.hasAttribute(ATTR_QUERY)) {\n problems.push(\n `<${tag}> carries ${ATTR_MANUAL}; it goes on the element carrying ` +\n `${ATTR_QUERY}`,\n );\n } else if (\n sides.length === 0 ||\n sides.some((side) => !known.includes(side))\n ) {\n problems.push(\n `<${tag}> carries ${ATTR_MANUAL}=\"${manual}\"; it names ` +\n `${MANUAL_ORDER}, ${MANUAL_FILTER} or both, separated by a space`,\n );\n }\n }\n\n // Judged by the scope around the element, not one the element carries:\n // its condition is a column of the row around it. With no lb-query\n // around it at all, nothing ever lands on it, and the element never\n // shows.\n const show = el.getAttribute(ATTR_SHOW);\n if (show !== null) {\n if (tag === \"template\") {\n problems.push(\n `a <template> carries ${ATTR_SHOW}=\"${show}\"; write it on the ` +\n `element it shows, and Loadbare supplies the template`,\n );\n } else if (scope === null || scope.row === false) {\n problems.push(\n `<${tag}> carries ${ATTR_SHOW}=\"${show}\" with no row around it ` +\n `to take the value from`,\n );\n } else if (show === SHOW_NOT || show.startsWith(SHOW_NOT + SHOW_NOT)) {\n problems.push(\n `<${tag}> carries ${ATTR_SHOW}=\"${show}\"; write one ` +\n `${SHOW_NOT} and then the column it reverses`,\n );\n }\n }\n\n walkMarkup(el, here, names, problems);\n // By tag name rather than by instanceof: template content belongs to a\n // document with no window, so the constructor is not reachable from the\n // node.\n if (tag === \"template\") {\n const content = (el as HTMLTemplateElement).content;\n const isRowTemplate =\n scope !== null && !!el.parentElement?.closest(`[${ATTR_QUERY}]`);\n if (isRowTemplate) {\n for (const root of content.children) {\n if (root.hasAttribute(ATTR_SHOW)) {\n problems.push(\n `<${root.localName}> is a row template's root and carries ` +\n `${ATTR_SHOW}; a row that should not show is left out by ` +\n `the query`,\n );\n }\n }\n }\n walkMarkup(\n content,\n isRowTemplate ? { query: scope!.query, row: true } : here,\n names,\n problems,\n );\n }\n }\n}\n\n/**\n * Ship every `lb-show` element already inside the template the hub keeps an\n * absent branch in, so nothing conditional shows until its value says so.\n * A page is in the document before its data arrives, and a value that has not\n * arrived is no value.\n *\n * Elements are collected before any is moved, and a wrapped element is still\n * reached once its outer one has gone into a template, because the references\n * are held.\n */\nfunction wrapShown(holder: HTMLTemplateElement): void {\n const doc = holder.ownerDocument;\n const shown: Element[] = [];\n const collect = (root: ParentNode): void => {\n shown.push(...root.querySelectorAll(`[${ATTR_SHOW}]`));\n for (const t of root.querySelectorAll(\"template\")) {\n collect((t as HTMLTemplateElement).content);\n }\n };\n collect(holder.content);\n for (const el of shown) {\n const parked = doc.createElement(\"template\");\n parked.setAttribute(ATTR_SHOW, el.getAttribute(ATTR_SHOW)!);\n el.replaceWith(parked);\n parked.content.append(el);\n }\n}\n\n/**\n * Expand one file's markup, naming the file if expansion refuses it.\n *\n * What comes back is the tree the browser will build, so every pass after\n * this reads and changes it in place, and it is serialized once, when it\n * ships.\n */\nfunction expandFile(\n file: string,\n source: string,\n defs: Definitions,\n): HTMLTemplateElement {\n try {\n return expandTree(source, defs);\n } catch (e) {\n throw new Error(`assemble: '${file}': ${(e as Error).message}`);\n }\n}\n\n/** Check one expanded fragment, and name the file if anything is wrong. */\nfunction checkMarkup(\n file: string,\n root: ParentNode,\n names: Names | null,\n): void {\n const problems: string[] = [];\n walkMarkup(root, null, names, problems);\n checkNewTargets(root, problems);\n if (problems.length > 0) {\n throw new Error(`assemble: '${file}' markup:\\n ` + problems.join(`\\n `));\n }\n}\n\n/** Every element under root, template content included. */\nfunction everyElement(root: ParentNode): Element[] {\n const found: Element[] = [];\n for (const el of root.querySelectorAll(\"*\")) {\n found.push(el);\n if (el.localName === \"template\") {\n found.push(...everyElement((el as HTMLTemplateElement).content));\n }\n }\n return found;\n}\n\n/**\n * `--lb-new` aimed at a list rather than a form puts a row not yet entered\n * in it, which only the submit of the form around it carries: the list is a\n * query nested directly in a form's own query. A target the markup does not\n * hold, as a page's own element named from the chrome, is not judged.\n */\nfunction checkNewTargets(root: ParentNode, problems: string[]): void {\n const all = everyElement(root);\n const byId = new Map<string, Element>();\n for (const el of all) {\n const id = el.getAttribute(\"id\");\n if (id !== null && !byId.has(id)) byId.set(id, el);\n }\n for (const button of all) {\n if (button.getAttribute(\"command\") !== COMMAND_NEW) continue;\n const target = byId.get(button.getAttribute(\"commandfor\") ?? \"\");\n if (!target || target.localName === \"form\") continue;\n const tag = target.localName;\n if (!target.hasAttribute(ATTR_QUERY)) {\n problems.push(\n `<${tag} id=\"${target.id}\"> is aimed at by ${COMMAND_NEW}, and is ` +\n `neither a <form> nor a list carrying ${ATTR_QUERY}`,\n );\n continue;\n }\n const form = target.closest(\"form\");\n const own = form?.hasAttribute(ATTR_QUERY)\n ? form\n : form?.parentElement?.closest(`[${ATTR_QUERY}]`);\n const around = target.parentElement?.closest(`[${ATTR_QUERY}]`);\n if (!form || !own || around !== own) {\n problems.push(\n `<${tag} id=\"${target.id}\"> is aimed at by ${COMMAND_NEW}, and is ` +\n `not a query nested directly in a form's own; a row not yet ` +\n `entered goes with the submit of the form around it`,\n );\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 * What each page declares — see build/declared.ts. Without it, the names\n * `lb-query` and `lb-request` carry are not checked.\n */\n declared?: DeclaredPages;\n}\n\nexport interface Assembled {\n html: string;\n /** Every custom element tag left in the document once expansion is done. */\n usedTags: Set<string>;\n}\n\nexport async function assemble({\n chromeFile,\n pages,\n scans,\n declared,\n}: AssembleOptions): Promise<Assembled> {\n // One layer per origin, in order, so a later origin's definition overrides\n // an earlier one's for the same tag. Each layer is checked for cycles as it\n // lands, against everything merged so far.\n let defs: Definitions = {};\n for (const scan of scans) {\n defs = await addDefinitions(defs, scan.definitions);\n }\n\n // Tags are collected from each tree as it goes by. What ships is these\n // trees and the chrome around its body, which is collected on its own.\n const usedTags = new Set<string>();\n const templates: string[] = [];\n for (const { name, file } of pages) {\n const authored = await readFile(file, \"utf-8\");\n const host = expandFile(file, authored, defs);\n const title = liftTitle(host);\n checkPage(file, host.content);\n checkMarkup(\n file,\n host.content,\n declared ? { declared: declared.get(name)! } : null,\n );\n wrapShown(host);\n customElementTags(host.content, usedTags);\n const titled =\n title === null ? \"\" : ` ${ATTR_PAGE_TITLE}=\"${attributeValue(title)}\"`;\n templates.push(\n `<template ${ATTR_PAGE}=\"${name}\"${titled}>${host.innerHTML}</template>`,\n );\n }\n\n let chromeSource: string;\n try {\n chromeSource = await readFile(chromeFile, \"utf-8\");\n } catch {\n throw new Error(`assemble: no chrome file at '${chromeFile}'`);\n }\n\n const body = /<body([^>]*)>([\\s\\S]*)<\\/body>/i.exec(chromeSource);\n if (!body) {\n throw new Error(\n `assemble: '${chromeFile}' has no <body>...</body> to expand`,\n );\n }\n const [whole, bodyAttrs, bodyContent] = body;\n const chromeBody = expandFile(chromeFile, bodyContent!, defs);\n checkChrome(chromeFile, chromeBody.content);\n checkUnknownPageDialog(chromeFile, chromeBody.content);\n checkMarkup(\n chromeFile,\n chromeBody.content,\n declared ? { declared: null } : null,\n );\n wrapShown(chromeBody);\n customElementTags(chromeBody.content, usedTags);\n customElementTags(chromeSource.replace(whole!, \"\"), usedTags);\n const withPages = chromeSource.replace(\n whole!,\n () =>\n `<body${bodyAttrs}>${chromeBody.innerHTML}\\n${templates.join(\"\\n\")}\\n</body>`,\n );\n\n return { html: await formatHtml(withPages), usedTags };\n}\n"]}
@@ -73,6 +73,19 @@ export declare function templateIn(el: Element): HTMLTemplateElement | null;
73
73
  * `lb-show` branch is passed over.
74
74
  */
75
75
  export declare function focusIn(root: Element): void;
76
+ /**
77
+ * Put a row not yet entered into a list: a live row copied from its row
78
+ * template, keyed by the empty key, its controls at their HTML defaults and
79
+ * its `lb-show` branches as for a row whose columns are all null. It sends
80
+ * nothing; the submit of the form around the list carries it. False when the
81
+ * element holds no row template.
82
+ */
83
+ export declare function addUnentered(el: Element): boolean;
84
+ /**
85
+ * Take a row not yet entered out of its list, as a row leaves. False when
86
+ * the row is not one `--lb-new` put in a list.
87
+ */
88
+ export declare function discardUnentered(row: Element): boolean;
76
89
  /** How a list of rows lands: in what order, and whether a request asked. */
77
90
  export interface RowsOptions {
78
91
  /** The order the server declared, as an order's text. */
@@ -1 +1 @@
1
- {"version":3,"file":"lb-apply.d.ts","sourceRoot":"","sources":["../../hub/lb-apply.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"lb-apply.d.ts","sourceRoot":"","sources":["../../hub/lb-apply.ts"],"names":[],"mappings":"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"}
@@ -693,6 +693,7 @@ function listFor(el, template) {
693
693
  levels,
694
694
  rowTemplate: at,
695
695
  byKey: new Map(),
696
+ unentered: [],
696
697
  root: {
697
698
  id: "",
698
699
  nodes: [],
@@ -766,6 +767,74 @@ function create(list, doc, key) {
766
767
  list.byKey.set(key, entry);
767
768
  return entry;
768
769
  }
770
+ /**
771
+ * Put the rows not yet entered after every row: immediately after the list's
772
+ * first template, which the rows and groups land in front of, so placing
773
+ * them never moves these.
774
+ */
775
+ function standLast(list) {
776
+ let at = list.root.slot;
777
+ for (const row of list.unentered) {
778
+ if (at.nextElementSibling !== row)
779
+ at.after(row);
780
+ at = row;
781
+ }
782
+ }
783
+ /** Stamp a list's count: the rows it shows, and its rows not yet entered. */
784
+ function recount(el, list) {
785
+ const showing = rowsUnder(list.root).filter((entry) => !entry.hidden);
786
+ setStamp(el, ATTR_QUERY_ROW_COUNT, String(showing.length + list.unentered.length));
787
+ }
788
+ /**
789
+ * Put a row not yet entered into a list: a live row copied from its row
790
+ * template, keyed by the empty key, its controls at their HTML defaults and
791
+ * its `lb-show` branches as for a row whose columns are all null. It sends
792
+ * nothing; the submit of the form around the list carries it. False when the
793
+ * element holds no row template.
794
+ */
795
+ export function addUnentered(el) {
796
+ const template = lists.get(el)?.top ?? templateIn(el);
797
+ if (!template)
798
+ return false;
799
+ const list = listFor(el, template);
800
+ if (!list)
801
+ return false;
802
+ const doc = el.ownerDocument;
803
+ const source = list.rowTemplate.content.firstElementChild;
804
+ list.paths ??= compile(source);
805
+ // Imported rather than cloned, for the reason a live row is.
806
+ const row = doc.importNode(source, true);
807
+ upgradeAbsent(row, doc);
808
+ placed(row);
809
+ row.setAttribute(ATTR_ROW_LIVE, "");
810
+ row.setAttribute(ATTR_KEY_VALUE, "");
811
+ nameRadios(row, "");
812
+ for (const [show, els] of placesBy(row, list.paths).shows) {
813
+ for (const shown of els)
814
+ presentPlaced(shown, show.startsWith(SHOW_NOT));
815
+ }
816
+ list.unentered.push(row);
817
+ standLast(list);
818
+ recount(el, list);
819
+ attachPlaced();
820
+ return true;
821
+ }
822
+ /**
823
+ * Take a row not yet entered out of its list, as a row leaves. False when
824
+ * the row is not one `--lb-new` put in a list.
825
+ */
826
+ export function discardUnentered(row) {
827
+ const el = row.parentElement?.closest(`[${ATTR_QUERY}]`);
828
+ const list = el ? lists.get(el) : undefined;
829
+ const at = list?.unentered.indexOf(row) ?? -1;
830
+ if (!el || !list || at < 0)
831
+ return false;
832
+ list.unentered.splice(at, 1);
833
+ leaving(row, ATTR_ROW_LIVE, ATTR_ROW_LEAVING);
834
+ depart([row]);
835
+ recount(el, list);
836
+ return true;
837
+ }
769
838
  /**
770
839
  * Give each radio button of a new live row a group of its own. The browser
771
840
  * groups radio buttons by `name`, and a row template repeats one `name` in
@@ -1354,7 +1423,8 @@ export function applyRows(el, template, keyColumn, result, options = {}) {
1354
1423
  }
1355
1424
  const rows = finish(list.root, options.types ?? {});
1356
1425
  // Counted from what the hub holds, which is what the rows show.
1357
- setStamp(el, ATTR_QUERY_ROW_COUNT, String(rows.length));
1426
+ setStamp(el, ATTR_QUERY_ROW_COUNT, String(rows.length + list.unentered.length));
1427
+ standLast(list);
1358
1428
  setStamp(el, ATTR_QUERY_UNPLACED_COUNT, String(unplaced));
1359
1429
  if (terms.length === 0)
1360
1430
  el.removeAttribute(ATTR_QUERY_ORDER);