@loadbare/app 0.21.0 → 0.23.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 (63) hide show
  1. package/README.md +18 -0
  2. package/dist/build/assemble.d.ts.map +1 -1
  3. package/dist/build/assemble.js +8 -3
  4. package/dist/build/assemble.js.map +1 -1
  5. package/dist/build/build.d.ts +78 -0
  6. package/dist/build/build.d.ts.map +1 -0
  7. package/dist/build/build.js +220 -0
  8. package/dist/build/build.js.map +1 -0
  9. package/dist/build/cli.d.ts +6 -1
  10. package/dist/build/cli.d.ts.map +1 -1
  11. package/dist/build/cli.js +25 -78
  12. package/dist/build/cli.js.map +1 -1
  13. package/dist/build/declared.d.ts.map +1 -1
  14. package/dist/build/declared.js +3 -0
  15. package/dist/build/declared.js.map +1 -1
  16. package/dist/build/skills-cli.d.ts +2 -1
  17. package/dist/build/skills-cli.d.ts.map +1 -1
  18. package/dist/build/skills-cli.js +8 -1
  19. package/dist/build/skills-cli.js.map +1 -1
  20. package/dist/build/skills.d.ts +2 -0
  21. package/dist/build/skills.d.ts.map +1 -1
  22. package/dist/build/skills.js +2 -0
  23. package/dist/build/skills.js.map +1 -1
  24. package/dist/core/lb-constants.d.ts +14 -0
  25. package/dist/core/lb-constants.d.ts.map +1 -1
  26. package/dist/core/lb-constants.js +25 -0
  27. package/dist/core/lb-constants.js.map +1 -1
  28. package/dist/dev/lb-dev.d.ts +128 -0
  29. package/dist/dev/lb-dev.d.ts.map +1 -0
  30. package/dist/dev/lb-dev.js +289 -0
  31. package/dist/dev/lb-dev.js.map +1 -0
  32. package/dist/hub/lb-apply.d.ts.map +1 -1
  33. package/dist/hub/lb-apply.js +12 -6
  34. package/dist/hub/lb-apply.js.map +1 -1
  35. package/dist/hub/lb-hub.browser.d.ts.map +1 -1
  36. package/dist/hub/lb-hub.browser.js +119 -15
  37. package/dist/hub/lb-hub.browser.js.map +1 -1
  38. package/docs/comparison.md +4 -3
  39. package/docs/miswarts.md +3 -2
  40. package/docs/reference/builder.md +12 -4
  41. package/docs/reference/chrome.md +52 -1
  42. package/docs/reference/data-binding.md +28 -18
  43. package/docs/reference/dev-server.md +178 -0
  44. package/docs/reference/names.md +14 -1
  45. package/docs/reference/overview.md +1 -0
  46. package/docs/reference/server.md +8 -3
  47. package/docs/reference/widgets.md +10 -277
  48. package/docs/temp-proposals.md +251 -0
  49. package/docs/testing.md +2 -1
  50. package/docs/tutorials/000-getting-started.md +1 -1
  51. package/docs/tutorials/040-displaying-data.md +1 -1
  52. package/docs/tutorials/090-using-widget-libraries.md +1 -1
  53. package/package.json +4 -2
  54. package/skills/loadbare-app/SKILL.md +30 -11
  55. package/skills/loadbare-app/references/builder.md +12 -4
  56. package/skills/loadbare-app/references/chrome.md +52 -1
  57. package/skills/loadbare-app/references/data-binding.md +28 -18
  58. package/skills/loadbare-app/references/dev-server.md +178 -0
  59. package/skills/loadbare-app/references/miswarts.md +3 -2
  60. package/skills/loadbare-app/references/names.md +14 -1
  61. package/skills/loadbare-app/references/overview.md +1 -0
  62. package/skills/loadbare-app/references/server.md +8 -3
  63. package/skills/loadbare-app/references/widgets.md +10 -277
package/README.md CHANGED
@@ -46,6 +46,24 @@ shows and sends, and
46
46
  [Using Widget Libraries](./docs/tutorials/090-using-widget-libraries.md) for
47
47
  what listing a package does.
48
48
 
49
+ ## Agent skills
50
+
51
+ The package ships an agent skill, `loadbare-app`, written for a coding agent working
52
+ in a project that depends on `@loadbare/app`. Install it with:
53
+
54
+ ```bash
55
+ npx loadbare-app skills install
56
+ ```
57
+
58
+ It links the skill into `.agents/skills/` and `.claude/skills/`, where agents
59
+ look for skills. `--copy` writes copies instead of links, and `--dir <path>`
60
+ installs into one directory of your choosing. The command prints the paths to
61
+ add to `.gitignore` and writes nothing there itself.
62
+
63
+ `npx loadbare-app skills install`, `npx loadbare-widgets skills install` and
64
+ `npx loadbare-db skills install` install the three loadbare skills, one per
65
+ package.
66
+
49
67
  ## Tutorials
50
68
 
51
69
  [Getting Started](./docs/tutorials/000-getting-started.md): a step-by-step
@@ -1 +1 @@
1
- {"version":3,"file":"assemble.d.ts","sourceRoot":"","sources":["../../build/assemble.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAwCH,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;AAsd/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;AAyCH,OAAO,KAAK,EAAY,aAAa,EAAE,MAAM,eAAe,CAAC;AAC7D,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAC/C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AA6d/C,MAAM,WAAW,eAAe;IAC9B,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,QAAQ,EAAE,CAAC;IAClB,8EAA8E;IAC9E,KAAK,EAAE,UAAU,EAAE,CAAC;IACpB;;;OAGG;IACH,QAAQ,CAAC,EAAE,aAAa,CAAC;CAC1B;AAED,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,4EAA4E;IAC5E,QAAQ,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;CACvB;AAED,wBAAsB,QAAQ,CAAC,EAC7B,UAAU,EACV,KAAK,EACL,KAAK,EACL,QAAQ,GACT,EAAE,eAAe,GAAG,OAAO,CAAC,SAAS,CAAC,CAgEtC"}
@@ -19,7 +19,7 @@
19
19
  import { readFile } from "node:fs/promises";
20
20
  import { addDefinitions, customElementTags, expandTree, } from "./expand.js";
21
21
  import { formatHtml } from "./format.js";
22
- import { AGGREGATES, ATTR_FILTER, ATTR_ORDER, ATTR_COLUMN, ATTR_COUNT, ATTR_GROUP, ATTR_PAGE, ATTR_PAGE_TITLE, ATTR_QUERY, ATTR_REQUEST, ATTR_SELECT, ATTR_SHOW, ATTR_URL_LINK, ATTR_URL_PUSH, ATTR_URL_PARM, ATTR_URL_UNKNOWN, CHOICES_QUERY, DEVELOPER_ATTRIBUTES, HUB_TAG_NAME, LB_COMMAND_PREFIX, LB_COMMANDS, LB_RESERVED_PREFIX, REQUEST_ROW_DELETE, REQUEST_ROW_INSERT, REQUEST_ROW_UPDATE, SHOW_NOT, URL_QUERY, } from "../core/lb-constants.js";
22
+ import { AGGREGATES, ATTR_FILTER, ATTR_ORDER, ATTR_COLUMN, ATTR_COUNT, ATTR_GROUP, ATTR_PAGE, ATTR_PAGE_TITLE, ATTR_QUERY, ATTR_REQUEST, ATTR_SELECT, ATTR_SHOW, ATTR_URL_LINK, ATTR_URL_PUSH, ATTR_URL_PARM, ATTR_URL_UNKNOWN, CHOICES_QUERY, HUB_QUERY, DEVELOPER_ATTRIBUTES, HUB_TAG_NAME, LB_COMMAND_PREFIX, LB_COMMANDS, LB_RESERVED_PREFIX, REQUEST_ROW_DELETE, REQUEST_ROW_INSERT, REQUEST_ROW_UPDATE, SHOW_NOT, URL_QUERY, } from "../core/lb-constants.js";
23
23
  import { isRowRequest } from "../core/lb-types.js";
24
24
  /**
25
25
  * The hub is one element, and it swaps each page into the one `<main>`
@@ -70,7 +70,7 @@ function checkUnknownPageDialog(chromeFile, body) {
70
70
  }
71
71
  }
72
72
  /** The queries the hub serves, which every page and the chrome may name. */
73
- const HUB_QUERIES = [URL_QUERY, CHOICES_QUERY];
73
+ const HUB_QUERIES = [URL_QUERY, CHOICES_QUERY, HUB_QUERY];
74
74
  /** The `Crud` key each request Loadbare provides runs. */
75
75
  const CRUD_KEYS = {
76
76
  [REQUEST_ROW_INSERT]: "rowInsert",
@@ -94,7 +94,7 @@ function checkNames(el, scope, names, problems) {
94
94
  if (query !== null && !HUB_QUERIES.includes(query)) {
95
95
  if (declared === null) {
96
96
  problems.push(`<${tag}> carries ${ATTR_QUERY}="${query}"; the chrome belongs to ` +
97
- `no page, and names only ${HUB_QUERIES.join(" and ")}`);
97
+ `no page, and names only ${HUB_QUERIES.slice(0, -1).join(", ")} and ${HUB_QUERIES.at(-1)}`);
98
98
  }
99
99
  else if (!declared.queries.has(query)) {
100
100
  problems.push(`<${tag}> carries ${ATTR_QUERY}="${query}", which the page's ` +
@@ -108,6 +108,11 @@ function checkNames(el, scope, names, problems) {
108
108
  const target = tag === "form" && query !== null ? query : (scope?.query ?? null);
109
109
  if (target === null)
110
110
  return;
111
+ if (target === HUB_QUERY) {
112
+ problems.push(`<${tag}> carries ${ATTR_REQUEST}="${request}" for '${target}'; ` +
113
+ `the hub's own state is written by the hub alone`);
114
+ return;
115
+ }
111
116
  if (HUB_QUERIES.includes(target)) {
112
117
  if (request !== REQUEST_ROW_UPDATE) {
113
118
  problems.push(`<${tag}> carries ${ATTR_REQUEST}="${request}" for '${target}'; ` +
@@ -1 +1 @@
1
- {"version":3,"file":"assemble.js","sourceRoot":"","sources":["../../build/assemble.ts"],"names":[],"mappings":"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,UAAU,EACV,WAAW,EACX,UAAU,EACV,WAAW,EACX,UAAU,EACV,UAAU,EACV,SAAS,EACT,eAAe,EACf,UAAU,EACV,YAAY,EACZ,WAAW,EACX,SAAS,EACT,aAAa,EACb,aAAa,EACb,aAAa,EACb,gBAAgB,EAChB,aAAa,EACb,oBAAoB,EACpB,YAAY,EACZ,iBAAiB,EACjB,WAAW,EACX,kBAAkB,EAClB,kBAAkB,EAClB,kBAAkB,EAClB,kBAAkB,EAClB,QAAQ,EACR,SAAS,GACV,MAAM,yBAAyB,CAAC;AACjC,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAKnD;;;;;;GAMG;AACH,SAAS,WAAW,CAAC,UAAkB,EAAE,IAAgB;IACvD,MAAM,KAAK,GAAG,cAAc,UAAU,GAAG,CAAC;IAC1C,MAAM,IAAI,GAAG,IAAI,CAAC,gBAAgB,CAAC,YAAY,CAAC,CAAC,MAAM,CAAC;IACxD,IAAI,IAAI,KAAK,CAAC,EAAE,CAAC;QACf,MAAM,IAAI,KAAK,CACb,GAAG,KAAK,QAAQ,IAAI,KAAK,YAAY,yBAAyB,CAC/D,CAAC;IACJ,CAAC;IACD,MAAM,KAAK,GAAG,IAAI,CAAC,gBAAgB,CAAC,MAAM,CAAC,CAAC;IAC5C,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACvB,MAAM,IAAI,KAAK,CACb,GAAG,KAAK,QAAQ,KAAK,CAAC,MAAM,+BAA+B,CAC5D,CAAC;IACJ,CAAC;IACD,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,CAAE,CAAC;IACvB,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,YAAY,CAAC,EAAE,CAAC;QAChC,MAAM,IAAI,KAAK,CACb,GAAG,KAAK,4BAA4B,YAAY,kCAAkC,CACnF,CAAC;IACJ,CAAC;IACD,IAAI,IAAI,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,IAAI,IAAI,CAAC,WAAY,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC;QAChE,MAAM,IAAI,KAAK,CACb,GAAG,KAAK,0EAA0E,CACnF,CAAC;IACJ,CAAC;AACH,CAAC;AAED,wFAAwF;AACxF,SAAS,SAAS,CAAC,IAAY,EAAE,IAAgB;IAC/C,KAAK,MAAM,GAAG,IAAI,CAAC,YAAY,EAAE,MAAM,CAAC,EAAE,CAAC;QACzC,IAAI,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,EAAE,CAAC;YAC5B,MAAM,IAAI,KAAK,CACb,cAAc,IAAI,gBAAgB,GAAG,sCAAsC,CAC5E,CAAC;QACJ,CAAC;IACH,CAAC;AACH,CAAC;AAED;;;;;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;AAaD,4EAA4E;AAC5E,MAAM,WAAW,GAAsB,CAAC,SAAS,EAAE,aAAa,CAAC,CAAC;AAElE,0DAA0D;AAC1D,MAAM,SAAS,GAA2B;IACxC,CAAC,kBAAkB,CAAC,EAAE,WAAW;IACjC,CAAC,kBAAkB,CAAC,EAAE,WAAW;IACjC,CAAC,kBAAkB,CAAC,EAAE,WAAW;CAClC,CAAC;AASF;;;;;;;;;GASG;AACH,SAAS,UAAU,CACjB,EAAW,EACX,KAAmB,EACnB,KAAY,EACZ,QAAkB;IAElB,MAAM,GAAG,GAAG,EAAE,CAAC,SAAS,CAAC;IACzB,MAAM,EAAE,QAAQ,EAAE,GAAG,KAAK,CAAC;IAE3B,MAAM,KAAK,GAAG,EAAE,CAAC,YAAY,CAAC,UAAU,CAAC,CAAC;IAC1C,IAAI,KAAK,KAAK,IAAI,IAAI,CAAC,WAAW,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;QACnD,IAAI,QAAQ,KAAK,IAAI,EAAE,CAAC;YACtB,QAAQ,CAAC,IAAI,CACX,IAAI,GAAG,aAAa,UAAU,KAAK,KAAK,2BAA2B;gBACjE,2BAA2B,WAAW,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CACzD,CAAC;QACJ,CAAC;aAAM,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC;YACxC,QAAQ,CAAC,IAAI,CACX,IAAI,GAAG,aAAa,UAAU,KAAK,KAAK,sBAAsB;gBAC5D,wBAAwB,CAC3B,CAAC;QACJ,CAAC;IACH,CAAC;IAED,MAAM,OAAO,GAAG,EAAE,CAAC,YAAY,CAAC,YAAY,CAAC,CAAC;IAC9C,IAAI,CAAC,OAAO;QAAE,OAAO;IACrB,IAAI,YAAY,CAAC,OAAO,CAAC,EAAE,CAAC;QAC1B,MAAM,MAAM,GACV,GAAG,KAAK,MAAM,IAAI,KAAK,KAAK,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,EAAE,KAAK,IAAI,IAAI,CAAC,CAAC;QACpE,IAAI,MAAM,KAAK,IAAI;YAAE,OAAO;QAC5B,IAAI,WAAW,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;YACjC,IAAI,OAAO,KAAK,kBAAkB,EAAE,CAAC;gBACnC,QAAQ,CAAC,IAAI,CACX,IAAI,GAAG,aAAa,YAAY,KAAK,OAAO,UAAU,MAAM,KAAK;oBAC/D,wBAAwB,kBAAkB,SAAS,CACtD,CAAC;YACJ,CAAC;YACD,OAAO;QACT,CAAC;QACD,yEAAyE;QACzE,IAAI,QAAQ,KAAK,IAAI,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC;YAAE,OAAO;QAC/D,MAAM,GAAG,GAAG,SAAS,CAAC,OAAO,CAAE,CAAC;QAChC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC;YACzC,QAAQ,CAAC,IAAI,CACX,IAAI,GAAG,aAAa,YAAY,KAAK,OAAO,UAAU,MAAM,KAAK;gBAC/D,mDAAmD,GAAG,EAAE,CAC3D,CAAC;QACJ,CAAC;IACH,CAAC;SAAM,IAAI,OAAO,CAAC,UAAU,CAAC,kBAAkB,CAAC,EAAE,CAAC;QAClD,iEAAiE;IACnE,CAAC;SAAM,IAAI,QAAQ,KAAK,IAAI,EAAE,CAAC;QAC7B,QAAQ,CAAC,IAAI,CACX,IAAI,GAAG,aAAa,YAAY,KAAK,OAAO,wBAAwB;YAClE,2CAA2C,CAC9C,CAAC;IACJ,CAAC;SAAM,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC;QAC3C,QAAQ,CAAC,IAAI,CACX,IAAI,GAAG,aAAa,YAAY,KAAK,OAAO,sBAAsB;YAChE,yBAAyB,CAC5B,CAAC;IACJ,CAAC;AACH,CAAC;AAED;;;;;;;;;;GAUG;AACH,SAAS,UAAU,CACjB,IAAgB,EAChB,KAAmB,EACnB,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,MAAM,KAAK,GAAG,EAAE,CAAC,YAAY,CAAC,UAAU,CAAC,CAAC;QAC1C,IAAI,KAAK,KAAK,IAAI;YAAE,IAAI,GAAG,EAAE,KAAK,EAAE,GAAG,EAAE,SAAS,EAAE,CAAC;QACrD,IAAI,KAAK;YAAE,UAAU,CAAC,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,QAAQ,CAAC,CAAC;QAElD,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,uEAAuE;QACvE,mEAAmE;QACnE,MAAM,KAAK,GAAG,EAAE,CAAC,YAAY,CAAC,aAAa,CAAC,CAAC;QAC7C,IAAI,KAAK,KAAK,IAAI,EAAE,CAAC;YACnB,MAAM,OAAO,GAAG,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC;YAChE,IAAI,GAAG,KAAK,GAAG,IAAI,CAAC,EAAE,CAAC,YAAY,CAAC,aAAa,CAAC,EAAE,CAAC;gBACnD,QAAQ,CAAC,IAAI,CACX,IAAI,GAAG,aAAa,aAAa,+BAA+B;oBAC9D,GAAG,aAAa,EAAE,CACrB,CAAC;YACJ,CAAC;iBAAM,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,KAAK,EAAE,CAAC,EAAE,CAAC;gBACnD,QAAQ,CAAC,IAAI,CACX,IAAI,GAAG,aAAa,aAAa,KAAK,KAAK,oBAAoB;oBAC7D,4CAA4C,CAC/C,CAAC;YACJ,CAAC;iBAAM,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC;gBACvD,QAAQ,CAAC,IAAI,CACX,IAAI,GAAG,aAAa,aAAa,KAAK,KAAK,iBAAiB;oBAC1D,qBAAqB,CACxB,CAAC;YACJ,CAAC;iBAAM,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC,kBAAkB,CAAC,CAAC,EAAE,CAAC;gBACjE,QAAQ,CAAC,IAAI,CACX,IAAI,GAAG,aAAa,aAAa,KAAK,KAAK,sBAAsB;oBAC/D,IAAI,kBAAkB,oBAAoB,CAC7C,CAAC;YACJ,CAAC;iBAAM,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,CAAC,GAAG,KAAK,KAAK,EAAE,CAAC;gBACjD,QAAQ,CAAC,IAAI,CACX,IAAI,GAAG,aAAa,aAAa,KAAK,KAAK,uBAAuB;oBAChE,4BAA4B,CAC/B,CAAC;YACJ,CAAC;QACH,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,yEAAyE;QACzE,0CAA0C;QAC1C,IAAI,EAAE,CAAC,YAAY,CAAC,WAAW,CAAC,EAAE,CAAC;YACjC,IAAI,GAAG,KAAK,OAAO,IAAI,EAAE,CAAC,YAAY,CAAC,MAAM,CAAC,KAAK,UAAU,EAAE,CAAC;gBAC9D,QAAQ,CAAC,IAAI,CACX,IAAI,GAAG,aAAa,WAAW,kBAAkB;oBAC/C,yBAAyB,CAC5B,CAAC;YACJ,CAAC;iBAAM,IAAI,KAAK,EAAE,GAAG,KAAK,IAAI,EAAE,CAAC;gBAC/B,QAAQ,CAAC,IAAI,CACX,mBAAmB,WAAW,8BAA8B;oBAC1D,4BAA4B,CAC/B,CAAC;YACJ,CAAC;YACD,KAAK,MAAM,IAAI,IAAI,CAAC,WAAW,EAAE,YAAY,CAAC,EAAE,CAAC;gBAC/C,IAAI,EAAE,CAAC,YAAY,CAAC,IAAI,CAAC,EAAE,CAAC;oBAC1B,QAAQ,CAAC,IAAI,CACX,IAAI,GAAG,aAAa,WAAW,QAAQ,IAAI,qBAAqB;wBAC9D,0BAA0B,CAC7B,CAAC;gBACJ,CAAC;YACH,CAAC;QACH,CAAC;QACD,qEAAqE;QACrE,MAAM,OAAO,GAAG,EAAE,CAAC,YAAY,CAAC,SAAS,CAAC,CAAC;QAC3C,IAAI,OAAO,KAAK,IAAI,IAAI,OAAO,CAAC,UAAU,CAAC,iBAAiB,CAAC,EAAE,CAAC;YAC9D,IAAI,CAAE,WAAiC,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC;gBAC1D,QAAQ,CAAC,IAAI,CACX,IAAI,GAAG,sBAAsB,OAAO,uBAAuB;oBACzD,IAAI,iBAAiB,0CAA0C,CAClE,CAAC;YACJ,CAAC;iBAAM,IAAI,CAAC,EAAE,CAAC,YAAY,CAAC,YAAY,CAAC,EAAE,CAAC;gBAC1C,QAAQ,CAAC,IAAI,CACX,IAAI,GAAG,sBAAsB,OAAO,0BAA0B;oBAC5D,sCAAsC,CACzC,CAAC;YACJ,CAAC;QACH,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,+DAA+D;QAC/D,wEAAwE;QACxE,SAAS;QACT,IAAI,EAAE,CAAC,YAAY,CAAC,UAAU,CAAC,EAAE,CAAC;YAChC,IAAI,GAAG,KAAK,UAAU,EAAE,CAAC;gBACvB,QAAQ,CAAC,IAAI,CACX,IAAI,GAAG,aAAa,UAAU,2BAA2B,CAC1D,CAAC;YACJ,CAAC;iBAAM,IACL,CAAE,EAA0B,CAAC,OAAO,CAAC,aAAa,CAChD,iBAAiB,SAAS,IAAI,CAC/B,EACD,CAAC;gBACD,QAAQ,CAAC,IAAI,CACX,eAAe,UAAU,wCAAwC,CAClE,CAAC;YACJ,CAAC;QACH,CAAC;QACD,KAAK,MAAM,IAAI,IAAI,UAAU,EAAE,CAAC;YAC9B,IAAI,CAAC,EAAE,CAAC,YAAY,CAAC,IAAI,CAAC;gBAAE,SAAS;YACrC,IAAI,GAAG,KAAK,UAAU,EAAE,CAAC;gBACvB,QAAQ,CAAC,IAAI,CAAC,wBAAwB,IAAI,yBAAyB,CAAC,CAAC;YACvE,CAAC;iBAAM,IAAI,IAAI,KAAK,UAAU,IAAI,CAAC,EAAE,CAAC,YAAY,CAAC,IAAI,CAAC,EAAE,CAAC;gBACzD,QAAQ,CAAC,IAAI,CAAC,IAAI,GAAG,aAAa,IAAI,mBAAmB,CAAC,CAAC;YAC7D,CAAC;QACH,CAAC;QAED,uEAAuE;QACvE,wCAAwC;QACxC,KAAK,MAAM,IAAI,IAAI,CAAC,UAAU,EAAE,WAAW,CAAC,EAAE,CAAC;YAC7C,MAAM,KAAK,GAAG,EAAE,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC;YACpC,IAAI,KAAK,KAAK,IAAI;gBAAE,SAAS;YAC7B,IAAI,CAAC,EAAE,CAAC,YAAY,CAAC,UAAU,CAAC,EAAE,CAAC;gBACjC,QAAQ,CAAC,IAAI,CACX,IAAI,GAAG,aAAa,IAAI,oCAAoC;oBAC1D,GAAG,UAAU,EAAE,CAClB,CAAC;YACJ,CAAC;iBAAM,IAAI,KAAK,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC;gBAC/B,QAAQ,CAAC,IAAI,CAAC,IAAI,GAAG,aAAa,IAAI,mBAAmB,CAAC,CAAC;YAC7D,CAAC;QACH,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,KAAK,EAAE,QAAQ,CAAC,CAAC;QACtC,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,CACR,OAAO,EACP,aAAa,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,KAAM,CAAC,KAAK,EAAE,GAAG,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,IAAI,EACzD,KAAK,EACL,QAAQ,CACT,CAAC;QACJ,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,CAClB,IAAY,EACZ,IAAgB,EAChB,KAAmB;IAEnB,MAAM,QAAQ,GAAa,EAAE,CAAC;IAC9B,UAAU,CAAC,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,QAAQ,CAAC,CAAC;IACxC,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;AAoBD,MAAM,CAAC,KAAK,UAAU,QAAQ,CAAC,EAC7B,UAAU,EACV,KAAK,EACL,KAAK,EACL,QAAQ,GACQ;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,SAAS,CAAC,IAAI,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC;QAC9B,WAAW,CACT,IAAI,EACJ,IAAI,CAAC,OAAO,EACZ,QAAQ,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CACpD,CAAC;QACF,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,WAAW,CAAC,UAAU,EAAE,UAAU,CAAC,OAAO,CAAC,CAAC;IAC5C,sBAAsB,CAAC,UAAU,EAAE,UAAU,CAAC,OAAO,CAAC,CAAC;IACvD,WAAW,CACT,UAAU,EACV,UAAU,CAAC,OAAO,EAClB,QAAQ,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,IAAI,CACrC,CAAC;IACF,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 AGGREGATES,\n ATTR_FILTER,\n ATTR_ORDER,\n ATTR_COLUMN,\n ATTR_COUNT,\n ATTR_GROUP,\n ATTR_PAGE,\n ATTR_PAGE_TITLE,\n ATTR_QUERY,\n ATTR_REQUEST,\n ATTR_SELECT,\n ATTR_SHOW,\n ATTR_URL_LINK,\n ATTR_URL_PUSH,\n ATTR_URL_PARM,\n ATTR_URL_UNKNOWN,\n CHOICES_QUERY,\n DEVELOPER_ATTRIBUTES,\n HUB_TAG_NAME,\n LB_COMMAND_PREFIX,\n LB_COMMANDS,\n LB_RESERVED_PREFIX,\n REQUEST_ROW_DELETE,\n REQUEST_ROW_INSERT,\n REQUEST_ROW_UPDATE,\n SHOW_NOT,\n URL_QUERY,\n} from \"../core/lb-constants.js\";\nimport { isRowRequest } from \"../core/lb-types.js\";\nimport type { Declared, DeclaredPages } from \"./declared.js\";\nimport type { PageFile } from \"./locations.js\";\nimport type { OriginScan } from \"./origins.js\";\n\n/**\n * The hub is one element, and it swaps each page into the one `<main>`\n * inside it (hub/lb-hub.browser.ts), so the chrome holds exactly one of each,\n * the `<main>` inside the hub and empty, since whatever it held would be\n * replaced by the first page. A page is swapped into that `<main>`, so it\n * carries neither.\n */\nfunction checkChrome(chromeFile: string, body: ParentNode): void {\n const where = `assemble: '${chromeFile}'`;\n const hubs = body.querySelectorAll(HUB_TAG_NAME).length;\n if (hubs !== 1) {\n throw new Error(\n `${where} has ${hubs} <${HUB_TAG_NAME}>; it needs exactly one`,\n );\n }\n const mains = body.querySelectorAll(\"main\");\n if (mains.length !== 1) {\n throw new Error(\n `${where} has ${mains.length} <main>; it needs exactly one`,\n );\n }\n const main = mains[0]!;\n if (!main.closest(HUB_TAG_NAME)) {\n throw new Error(\n `${where} has its <main> outside <${HUB_TAG_NAME}>, where the hub cannot reach it`,\n );\n }\n if (main.children.length > 0 || main.textContent!.trim() !== \"\") {\n throw new Error(\n `${where} has content in its <main>, which the first page replaces; it goes empty`,\n );\n }\n}\n\n/** A page lands inside the chrome's `<main>`, so it brings no second one and no hub. */\nfunction checkPage(file: string, root: ParentNode): void {\n for (const tag of [HUB_TAG_NAME, \"main\"]) {\n if (root.querySelector(tag)) {\n throw new Error(\n `assemble: '${file}' carries a <${tag}>, which belongs to the chrome alone`,\n );\n }\n }\n}\n\n/**\n * The hub calls showModal() on the `<dialog>` carrying lb-url-unknown\n * (hub/lb-hub.browser.ts), and looks for it inside itself, as it does for\n * everything else it acts on. A dialog elsewhere in the body would never open\n * and would say nothing about why.\n */\nfunction checkUnknownPageDialog(chromeFile: string, body: ParentNode): void {\n const el = body.querySelector(`[${ATTR_URL_UNKNOWN}]`);\n if (!el) return;\n if (!el.closest(HUB_TAG_NAME)) {\n throw new Error(\n `assemble: '${chromeFile}' declares ${ATTR_URL_UNKNOWN} outside ` +\n `<${HUB_TAG_NAME}>, where the hub cannot reach it`,\n );\n }\n}\n\n/**\n * Where the markup stands while the check walks it: inside which `lb-query`,\n * and whether there is a row there to take a value from. Inside a row\n * template there always is. Inside an `lb-query` outside one, there is when\n * the query is of kind `row`, which only the server knows.\n */\ninterface Scope {\n query: string;\n row: boolean | \"unknown\";\n}\n\n/** The queries the hub serves, which every page and the chrome may name. */\nconst HUB_QUERIES: readonly string[] = [URL_QUERY, CHOICES_QUERY];\n\n/** The `Crud` key each request Loadbare provides runs. */\nconst CRUD_KEYS: Record<string, string> = {\n [REQUEST_ROW_INSERT]: \"rowInsert\",\n [REQUEST_ROW_UPDATE]: \"rowUpdate\",\n [REQUEST_ROW_DELETE]: \"rowDelete\",\n};\n\n/**\n * What a file's names are checked against: a page's declarations, or `null`\n * for the chrome, which belongs to no page and so names only what the hub\n * serves.\n */\ntype Names = { declared: Declared | null };\n\n/**\n * An `lb-query` or `lb-request` naming nothing the page declares would be\n * found only on the first round trip, as a server warning and an empty\n * answer, so a typo would build cleanly.\n *\n * A request Loadbare provides is for the query of what it gathers: a form's\n * own when the form carries `lb-query`, otherwise the nearest around the\n * element, as the hub decides it (hub/lb-hub.browser.ts). With no query\n * around it, the hub reports it in the browser.\n */\nfunction checkNames(\n el: Element,\n scope: Scope | null,\n names: Names,\n problems: string[],\n): void {\n const tag = el.localName;\n const { declared } = names;\n\n const query = el.getAttribute(ATTR_QUERY);\n if (query !== null && !HUB_QUERIES.includes(query)) {\n if (declared === null) {\n problems.push(\n `<${tag}> carries ${ATTR_QUERY}=\"${query}\"; the chrome belongs to ` +\n `no page, and names only ${HUB_QUERIES.join(\" and \")}`,\n );\n } else if (!declared.queries.has(query)) {\n problems.push(\n `<${tag}> carries ${ATTR_QUERY}=\"${query}\", which the page's ` +\n `queries do not declare`,\n );\n }\n }\n\n const request = el.getAttribute(ATTR_REQUEST);\n if (!request) return;\n if (isRowRequest(request)) {\n const target =\n tag === \"form\" && query !== null ? query : (scope?.query ?? null);\n if (target === null) return;\n if (HUB_QUERIES.includes(target)) {\n if (request !== REQUEST_ROW_UPDATE) {\n problems.push(\n `<${tag}> carries ${ATTR_REQUEST}=\"${request}\" for '${target}'; ` +\n `the hub answers only ${REQUEST_ROW_UPDATE} for it`,\n );\n }\n return;\n }\n // A query the page does not declare is reported once, where it is named.\n if (declared === null || !declared.queries.has(target)) return;\n const key = CRUD_KEYS[request]!;\n if (!declared.crud.get(target)?.has(key)) {\n problems.push(\n `<${tag}> carries ${ATTR_REQUEST}=\"${request}\" for '${target}', ` +\n `whose crud entry the page does not declare with ${key}`,\n );\n }\n } else if (request.startsWith(LB_RESERVED_PREFIX)) {\n // Reported by walkMarkup as naming no request Loadbare provides.\n } else if (declared === null) {\n problems.push(\n `<${tag}> carries ${ATTR_REQUEST}=\"${request}\"; the chrome belongs ` +\n `to no page, and sends no declared request`,\n );\n } else if (!declared.handlers.has(request)) {\n problems.push(\n `<${tag}> carries ${ATTR_REQUEST}=\"${request}\", which the page's ` +\n `handlers do not declare`,\n );\n }\n}\n\n/**\n * The markup, checked once against the finished document rather than\n * discovered one console error at a time in a browser.\n *\n * Every rule here is a fact the markup states about itself. The developer\n * writes `lb-*` attributes in markup, and script never assigns them, which is\n * what makes checking the markup sound. See docs/reference/data-binding.md.\n *\n * A `<template>`'s children are walked in the scope that encloses the\n * template, because a row template's clones land in the element around it.\n */\nfunction walkMarkup(\n node: ParentNode,\n scope: Scope | null,\n names: Names | null,\n problems: string[],\n): void {\n for (const el of node.children) {\n const tag = el.localName;\n let here = scope;\n const query = el.getAttribute(ATTR_QUERY);\n if (query !== null) here = { query, row: \"unknown\" };\n if (names) checkNames(el, scope, names, problems);\n\n for (const { name } of el.attributes) {\n if (\n name.startsWith(LB_RESERVED_PREFIX) &&\n !(DEVELOPER_ATTRIBUTES as readonly string[]).includes(name)\n ) {\n problems.push(\n `<${tag}> carries ${name}, which is not an attribute a developer ` +\n `writes`,\n );\n }\n }\n\n const request = el.getAttribute(ATTR_REQUEST);\n if (\n request !== null &&\n request.startsWith(LB_RESERVED_PREFIX) &&\n !isRowRequest(request)\n ) {\n problems.push(\n `<${tag}> carries ${ATTR_REQUEST}=\"${request}\", which begins with ` +\n `'${LB_RESERVED_PREFIX}' but names no request Loadbare provides`,\n );\n }\n if (el.hasAttribute(ATTR_URL_LINK) && tag !== \"a\") {\n problems.push(`<${tag}> carries ${ATTR_URL_LINK}; it goes on an <a>`);\n }\n // A link's parms are columns of the row around it, and only a link the\n // hub handles takes them: the parms are the application's own URL.\n const parms = el.getAttribute(ATTR_URL_PARM);\n if (parms !== null) {\n const columns = parms.split(\",\").map((column) => column.trim());\n if (tag !== \"a\" || !el.hasAttribute(ATTR_URL_LINK)) {\n problems.push(\n `<${tag}> carries ${ATTR_URL_PARM}; it goes on an <a> carrying ` +\n `${ATTR_URL_LINK}`,\n );\n } else if (columns.some((column) => column === \"\")) {\n problems.push(\n `<${tag}> carries ${ATTR_URL_PARM}=\"${parms}\", which names no ` +\n `column between two commas or at either end`,\n );\n } else if (columns.some((column) => /\\s/.test(column))) {\n problems.push(\n `<${tag}> carries ${ATTR_URL_PARM}=\"${parms}\"; columns are ` +\n `separated by commas`,\n );\n } else if (columns.some((c) => c.startsWith(LB_RESERVED_PREFIX))) {\n problems.push(\n `<${tag}> carries ${ATTR_URL_PARM}=\"${parms}\"; a parm beginning ` +\n `'${LB_RESERVED_PREFIX}' is the hub's own`,\n );\n } else if (scope === null || scope.row === false) {\n problems.push(\n `<${tag}> carries ${ATTR_URL_PARM}=\"${parms}\" with no row around ` +\n `it to take the values from`,\n );\n }\n }\n if (el.hasAttribute(ATTR_URL_PUSH) && request === null) {\n problems.push(\n `<${tag}> carries ${ATTR_URL_PUSH} and no ${ATTR_REQUEST}; it goes ` +\n `on an element with ${ATTR_REQUEST}`,\n );\n }\n // A selector is a checkbox in a live row, and selects that row. It is no\n // column, and commits nothing of its own.\n if (el.hasAttribute(ATTR_SELECT)) {\n if (tag !== \"input\" || el.getAttribute(\"type\") !== \"checkbox\") {\n problems.push(\n `<${tag}> carries ${ATTR_SELECT}; it goes on an ` +\n `<input type=\"checkbox\">`,\n );\n } else if (scope?.row !== true) {\n problems.push(\n `<input> carries ${ATTR_SELECT} outside a row template; it ` +\n `selects the row it sits in`,\n );\n }\n for (const name of [ATTR_COLUMN, ATTR_REQUEST]) {\n if (el.hasAttribute(name)) {\n problems.push(\n `<${tag}> carries ${ATTR_SELECT} and ${name}; a selector is no ` +\n `column and sends nothing`,\n );\n }\n }\n }\n // HTML's own command attribute, whose `--lb-` values are Loadbare's.\n const command = el.getAttribute(\"command\");\n if (command !== null && command.startsWith(LB_COMMAND_PREFIX)) {\n if (!(LB_COMMANDS as readonly string[]).includes(command)) {\n problems.push(\n `<${tag}> carries command=\"${command}\", which begins with ` +\n `'${LB_COMMAND_PREFIX}' but names no command Loadbare provides`,\n );\n } else if (!el.hasAttribute(\"commandfor\")) {\n problems.push(\n `<${tag}> carries command=\"${command}\" and no commandfor; it ` +\n `names the element the command is for`,\n );\n }\n }\n if (el.hasAttribute(ATTR_URL_UNKNOWN) && tag !== \"dialog\") {\n problems.push(\n `<${tag}> carries ${ATTR_URL_UNKNOWN}; it goes on a <dialog>`,\n );\n }\n\n // A group is a template holding a heading and the template its\n // contents go in; an aggregate is set on an element, from the column it\n // names.\n if (el.hasAttribute(ATTR_GROUP)) {\n if (tag !== \"template\") {\n problems.push(\n `<${tag}> carries ${ATTR_GROUP}; it goes on a <template>`,\n );\n } else if (\n !(el as HTMLTemplateElement).content.querySelector(\n `template:not([${ATTR_SHOW}])`,\n )\n ) {\n problems.push(\n `a <template ${ATTR_GROUP}> holds no <template> for its contents`,\n );\n }\n }\n for (const name of AGGREGATES) {\n if (!el.hasAttribute(name)) continue;\n if (tag === \"template\") {\n problems.push(`a <template> carries ${name}; it goes on an element`);\n } else if (name !== ATTR_COUNT && !el.getAttribute(name)) {\n problems.push(`<${tag}> carries ${name} naming no column`);\n }\n }\n\n // The user's order and filter for the rows an element holds, each read\n // from a column of lb-choices it names.\n for (const name of [ATTR_ORDER, ATTR_FILTER]) {\n const value = el.getAttribute(name);\n if (value === null) continue;\n if (!el.hasAttribute(ATTR_QUERY)) {\n problems.push(\n `<${tag}> carries ${name}; it goes on the element carrying ` +\n `${ATTR_QUERY}`,\n );\n } else if (value.trim() === \"\") {\n problems.push(`<${tag}> carries ${name} naming no column`);\n }\n }\n\n // Judged by the scope around the element, not one the element carries:\n // its condition is a column of the row around it. With no lb-query\n // around it at all, nothing ever lands on it, and the element never\n // shows.\n const show = el.getAttribute(ATTR_SHOW);\n if (show !== null) {\n if (tag === \"template\") {\n problems.push(\n `a <template> carries ${ATTR_SHOW}=\"${show}\"; write it on the ` +\n `element it shows, and Loadbare supplies the template`,\n );\n } else if (scope === null || scope.row === false) {\n problems.push(\n `<${tag}> carries ${ATTR_SHOW}=\"${show}\" with no row around it ` +\n `to take the value from`,\n );\n } else if (show === SHOW_NOT || show.startsWith(SHOW_NOT + SHOW_NOT)) {\n problems.push(\n `<${tag}> carries ${ATTR_SHOW}=\"${show}\"; write one ` +\n `${SHOW_NOT} and then the column it reverses`,\n );\n }\n }\n\n walkMarkup(el, here, names, problems);\n // By tag name rather than by instanceof: template content belongs to a\n // document with no window, so the constructor is not reachable from the\n // node.\n if (tag === \"template\") {\n const content = (el as HTMLTemplateElement).content;\n const isRowTemplate =\n scope !== null && !!el.parentElement?.closest(`[${ATTR_QUERY}]`);\n if (isRowTemplate) {\n for (const root of content.children) {\n if (root.hasAttribute(ATTR_SHOW)) {\n problems.push(\n `<${root.localName}> is a row template's root and carries ` +\n `${ATTR_SHOW}; a row that should not show is left out by ` +\n `the query`,\n );\n }\n }\n }\n walkMarkup(\n content,\n isRowTemplate ? { query: scope!.query, row: true } : here,\n names,\n problems,\n );\n }\n }\n}\n\n/**\n * Ship every `lb-show` element already inside the template the hub keeps an\n * absent branch in, so nothing conditional shows until its value says so.\n * A page is in the document before its data arrives, and a value that has not\n * arrived is no value.\n *\n * Elements are collected before any is moved, and a wrapped element is still\n * reached once its outer one has gone into a template, because the references\n * are held.\n */\nfunction wrapShown(holder: HTMLTemplateElement): void {\n const doc = holder.ownerDocument;\n const shown: Element[] = [];\n const collect = (root: ParentNode): void => {\n shown.push(...root.querySelectorAll(`[${ATTR_SHOW}]`));\n for (const t of root.querySelectorAll(\"template\")) {\n collect((t as HTMLTemplateElement).content);\n }\n };\n collect(holder.content);\n for (const el of shown) {\n const parked = doc.createElement(\"template\");\n parked.setAttribute(ATTR_SHOW, el.getAttribute(ATTR_SHOW)!);\n el.replaceWith(parked);\n parked.content.append(el);\n }\n}\n\n/**\n * Expand one file's markup, naming the file if expansion refuses it.\n *\n * What comes back is the tree the browser will build, so every pass after\n * this reads and changes it in place, and it is serialized once, when it\n * ships.\n */\nfunction expandFile(\n file: string,\n source: string,\n defs: Definitions,\n): HTMLTemplateElement {\n try {\n return expandTree(source, defs);\n } catch (e) {\n throw new Error(`assemble: '${file}': ${(e as Error).message}`);\n }\n}\n\n/** Check one expanded fragment, and name the file if anything is wrong. */\nfunction checkMarkup(\n file: string,\n root: ParentNode,\n names: Names | null,\n): void {\n const problems: string[] = [];\n walkMarkup(root, null, names, problems);\n if (problems.length > 0) {\n throw new Error(`assemble: '${file}' markup:\\n ` + problems.join(`\\n `));\n }\n}\n\n/**\n * A page file's `<title>` names the page. It is lifted out, since a page is\n * a fragment inside `<main>` and a `<title>` belongs in a document's head,\n * and its text content is stamped on the page as ATTR_PAGE_TITLE.\n */\nfunction liftTitle(holder: HTMLTemplateElement): string | null {\n const title = holder.content.querySelector(\"title\");\n if (!title) return null;\n const text = title.textContent?.trim() ?? \"\";\n title.remove();\n return text;\n}\n\n/** An attribute value, escaped for a double-quoted attribute. */\nfunction attributeValue(text: string): string {\n return text.replace(/&/g, \"&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_ORDER,\n ATTR_COLUMN,\n ATTR_COUNT,\n ATTR_GROUP,\n ATTR_PAGE,\n ATTR_PAGE_TITLE,\n ATTR_QUERY,\n ATTR_REQUEST,\n ATTR_SELECT,\n ATTR_SHOW,\n ATTR_URL_LINK,\n ATTR_URL_PUSH,\n ATTR_URL_PARM,\n ATTR_URL_UNKNOWN,\n CHOICES_QUERY,\n HUB_QUERY,\n DEVELOPER_ATTRIBUTES,\n HUB_TAG_NAME,\n LB_COMMAND_PREFIX,\n LB_COMMANDS,\n LB_RESERVED_PREFIX,\n REQUEST_ROW_DELETE,\n REQUEST_ROW_INSERT,\n REQUEST_ROW_UPDATE,\n SHOW_NOT,\n URL_QUERY,\n} from \"../core/lb-constants.js\";\nimport { isRowRequest } from \"../core/lb-types.js\";\nimport type { Declared, DeclaredPages } from \"./declared.js\";\nimport type { PageFile } from \"./locations.js\";\nimport type { OriginScan } from \"./origins.js\";\n\n/**\n * The hub is one element, and it swaps each page into the one `<main>`\n * inside it (hub/lb-hub.browser.ts), so the chrome holds exactly one of each,\n * the `<main>` inside the hub and empty, since whatever it held would be\n * replaced by the first page. A page is swapped into that `<main>`, so it\n * carries neither.\n */\nfunction checkChrome(chromeFile: string, body: ParentNode): void {\n const where = `assemble: '${chromeFile}'`;\n const hubs = body.querySelectorAll(HUB_TAG_NAME).length;\n if (hubs !== 1) {\n throw new Error(\n `${where} has ${hubs} <${HUB_TAG_NAME}>; it needs exactly one`,\n );\n }\n const mains = body.querySelectorAll(\"main\");\n if (mains.length !== 1) {\n throw new Error(\n `${where} has ${mains.length} <main>; it needs exactly one`,\n );\n }\n const main = mains[0]!;\n if (!main.closest(HUB_TAG_NAME)) {\n throw new Error(\n `${where} has its <main> outside <${HUB_TAG_NAME}>, where the hub cannot reach it`,\n );\n }\n if (main.children.length > 0 || main.textContent!.trim() !== \"\") {\n throw new Error(\n `${where} has content in its <main>, which the first page replaces; it goes empty`,\n );\n }\n}\n\n/** A page lands inside the chrome's `<main>`, so it brings no second one and no hub. */\nfunction checkPage(file: string, root: ParentNode): void {\n for (const tag of [HUB_TAG_NAME, \"main\"]) {\n if (root.querySelector(tag)) {\n throw new Error(\n `assemble: '${file}' carries a <${tag}>, which belongs to the chrome alone`,\n );\n }\n }\n}\n\n/**\n * The hub calls showModal() on the `<dialog>` carrying lb-url-unknown\n * (hub/lb-hub.browser.ts), and looks for it inside itself, as it does for\n * everything else it acts on. A dialog elsewhere in the body would never open\n * and would say nothing about why.\n */\nfunction checkUnknownPageDialog(chromeFile: string, body: ParentNode): void {\n const el = body.querySelector(`[${ATTR_URL_UNKNOWN}]`);\n if (!el) return;\n if (!el.closest(HUB_TAG_NAME)) {\n throw new Error(\n `assemble: '${chromeFile}' declares ${ATTR_URL_UNKNOWN} outside ` +\n `<${HUB_TAG_NAME}>, where the hub cannot reach it`,\n );\n }\n}\n\n/**\n * Where the markup stands while the check walks it: inside which `lb-query`,\n * and whether there is a row there to take a value from. Inside a row\n * template there always is. Inside an `lb-query` outside one, there is when\n * the query is of kind `row`, which only the server knows.\n */\ninterface Scope {\n query: string;\n row: boolean | \"unknown\";\n}\n\n/** The queries the hub serves, which every page and the chrome may name. */\nconst HUB_QUERIES: readonly string[] = [URL_QUERY, CHOICES_QUERY, HUB_QUERY];\n\n/** The `Crud` key each request Loadbare provides runs. */\nconst CRUD_KEYS: Record<string, string> = {\n [REQUEST_ROW_INSERT]: \"rowInsert\",\n [REQUEST_ROW_UPDATE]: \"rowUpdate\",\n [REQUEST_ROW_DELETE]: \"rowDelete\",\n};\n\n/**\n * What a file's names are checked against: a page's declarations, or `null`\n * for the chrome, which belongs to no page and so names only what the hub\n * serves.\n */\ntype Names = { declared: Declared | null };\n\n/**\n * An `lb-query` or `lb-request` naming nothing the page declares would be\n * found only on the first round trip, as a server warning and an empty\n * answer, so a typo would build cleanly.\n *\n * A request Loadbare provides is for the query of what it gathers: a form's\n * own when the form carries `lb-query`, otherwise the nearest around the\n * element, as the hub decides it (hub/lb-hub.browser.ts). With no query\n * around it, the hub reports it in the browser.\n */\nfunction checkNames(\n el: Element,\n scope: Scope | null,\n names: Names,\n problems: string[],\n): void {\n const tag = el.localName;\n const { declared } = names;\n\n const query = el.getAttribute(ATTR_QUERY);\n if (query !== null && !HUB_QUERIES.includes(query)) {\n if (declared === null) {\n problems.push(\n `<${tag}> carries ${ATTR_QUERY}=\"${query}\"; the chrome belongs to ` +\n `no page, and names only ${HUB_QUERIES.slice(0, -1).join(\", \")} and ${HUB_QUERIES.at(-1)}`,\n );\n } else if (!declared.queries.has(query)) {\n problems.push(\n `<${tag}> carries ${ATTR_QUERY}=\"${query}\", which the page's ` +\n `queries do not declare`,\n );\n }\n }\n\n const request = el.getAttribute(ATTR_REQUEST);\n if (!request) return;\n if (isRowRequest(request)) {\n const target =\n tag === \"form\" && query !== null ? query : (scope?.query ?? null);\n if (target === null) return;\n if (target === HUB_QUERY) {\n problems.push(\n `<${tag}> carries ${ATTR_REQUEST}=\"${request}\" for '${target}'; ` +\n `the hub's own state is written by the hub alone`,\n );\n return;\n }\n if (HUB_QUERIES.includes(target)) {\n if (request !== REQUEST_ROW_UPDATE) {\n problems.push(\n `<${tag}> carries ${ATTR_REQUEST}=\"${request}\" for '${target}'; ` +\n `the hub answers only ${REQUEST_ROW_UPDATE} for it`,\n );\n }\n return;\n }\n // A query the page does not declare is reported once, where it is named.\n if (declared === null || !declared.queries.has(target)) return;\n const key = CRUD_KEYS[request]!;\n if (!declared.crud.get(target)?.has(key)) {\n problems.push(\n `<${tag}> carries ${ATTR_REQUEST}=\"${request}\" for '${target}', ` +\n `whose crud entry the page does not declare with ${key}`,\n );\n }\n } else if (request.startsWith(LB_RESERVED_PREFIX)) {\n // Reported by walkMarkup as naming no request Loadbare provides.\n } else if (declared === null) {\n problems.push(\n `<${tag}> carries ${ATTR_REQUEST}=\"${request}\"; the chrome belongs ` +\n `to no page, and sends no declared request`,\n );\n } else if (!declared.handlers.has(request)) {\n problems.push(\n `<${tag}> carries ${ATTR_REQUEST}=\"${request}\", which the page's ` +\n `handlers do not declare`,\n );\n }\n}\n\n/**\n * The markup, checked once against the finished document rather than\n * discovered one console error at a time in a browser.\n *\n * Every rule here is a fact the markup states about itself. The developer\n * writes `lb-*` attributes in markup, and script never assigns them, which is\n * what makes checking the markup sound. See docs/reference/data-binding.md.\n *\n * A `<template>`'s children are walked in the scope that encloses the\n * template, because a row template's clones land in the element around it.\n */\nfunction walkMarkup(\n node: ParentNode,\n scope: Scope | null,\n names: Names | null,\n problems: string[],\n): void {\n for (const el of node.children) {\n const tag = el.localName;\n let here = scope;\n const query = el.getAttribute(ATTR_QUERY);\n if (query !== null) here = { query, row: \"unknown\" };\n if (names) checkNames(el, scope, names, problems);\n\n for (const { name } of el.attributes) {\n if (\n name.startsWith(LB_RESERVED_PREFIX) &&\n !(DEVELOPER_ATTRIBUTES as readonly string[]).includes(name)\n ) {\n problems.push(\n `<${tag}> carries ${name}, which is not an attribute a developer ` +\n `writes`,\n );\n }\n }\n\n const request = el.getAttribute(ATTR_REQUEST);\n if (\n request !== null &&\n request.startsWith(LB_RESERVED_PREFIX) &&\n !isRowRequest(request)\n ) {\n problems.push(\n `<${tag}> carries ${ATTR_REQUEST}=\"${request}\", which begins with ` +\n `'${LB_RESERVED_PREFIX}' but names no request Loadbare provides`,\n );\n }\n if (el.hasAttribute(ATTR_URL_LINK) && tag !== \"a\") {\n problems.push(`<${tag}> carries ${ATTR_URL_LINK}; it goes on an <a>`);\n }\n // A link's parms are columns of the row around it, and only a link the\n // hub handles takes them: the parms are the application's own URL.\n const parms = el.getAttribute(ATTR_URL_PARM);\n if (parms !== null) {\n const columns = parms.split(\",\").map((column) => column.trim());\n if (tag !== \"a\" || !el.hasAttribute(ATTR_URL_LINK)) {\n problems.push(\n `<${tag}> carries ${ATTR_URL_PARM}; it goes on an <a> carrying ` +\n `${ATTR_URL_LINK}`,\n );\n } else if (columns.some((column) => column === \"\")) {\n problems.push(\n `<${tag}> carries ${ATTR_URL_PARM}=\"${parms}\", which names no ` +\n `column between two commas or at either end`,\n );\n } else if (columns.some((column) => /\\s/.test(column))) {\n problems.push(\n `<${tag}> carries ${ATTR_URL_PARM}=\"${parms}\"; columns are ` +\n `separated by commas`,\n );\n } else if (columns.some((c) => c.startsWith(LB_RESERVED_PREFIX))) {\n problems.push(\n `<${tag}> carries ${ATTR_URL_PARM}=\"${parms}\"; a parm beginning ` +\n `'${LB_RESERVED_PREFIX}' is the hub's own`,\n );\n } else if (scope === null || scope.row === false) {\n problems.push(\n `<${tag}> carries ${ATTR_URL_PARM}=\"${parms}\" with no row around ` +\n `it to take the values from`,\n );\n }\n }\n if (el.hasAttribute(ATTR_URL_PUSH) && request === null) {\n problems.push(\n `<${tag}> carries ${ATTR_URL_PUSH} and no ${ATTR_REQUEST}; it goes ` +\n `on an element with ${ATTR_REQUEST}`,\n );\n }\n // A selector is a checkbox in a live row, and selects that row. It is no\n // column, and commits nothing of its own.\n if (el.hasAttribute(ATTR_SELECT)) {\n if (tag !== \"input\" || el.getAttribute(\"type\") !== \"checkbox\") {\n problems.push(\n `<${tag}> carries ${ATTR_SELECT}; it goes on an ` +\n `<input type=\"checkbox\">`,\n );\n } else if (scope?.row !== true) {\n problems.push(\n `<input> carries ${ATTR_SELECT} outside a row template; it ` +\n `selects the row it sits in`,\n );\n }\n for (const name of [ATTR_COLUMN, ATTR_REQUEST]) {\n if (el.hasAttribute(name)) {\n problems.push(\n `<${tag}> carries ${ATTR_SELECT} and ${name}; a selector is no ` +\n `column and sends nothing`,\n );\n }\n }\n }\n // HTML's own command attribute, whose `--lb-` values are Loadbare's.\n const command = el.getAttribute(\"command\");\n if (command !== null && command.startsWith(LB_COMMAND_PREFIX)) {\n if (!(LB_COMMANDS as readonly string[]).includes(command)) {\n problems.push(\n `<${tag}> carries command=\"${command}\", which begins with ` +\n `'${LB_COMMAND_PREFIX}' but names no command Loadbare provides`,\n );\n } else if (!el.hasAttribute(\"commandfor\")) {\n problems.push(\n `<${tag}> carries command=\"${command}\" and no commandfor; it ` +\n `names the element the command is for`,\n );\n }\n }\n if (el.hasAttribute(ATTR_URL_UNKNOWN) && tag !== \"dialog\") {\n problems.push(\n `<${tag}> carries ${ATTR_URL_UNKNOWN}; it goes on a <dialog>`,\n );\n }\n\n // A group is a template holding a heading and the template its\n // contents go in; an aggregate is set on an element, from the column it\n // names.\n if (el.hasAttribute(ATTR_GROUP)) {\n if (tag !== \"template\") {\n problems.push(\n `<${tag}> carries ${ATTR_GROUP}; it goes on a <template>`,\n );\n } else if (\n !(el as HTMLTemplateElement).content.querySelector(\n `template:not([${ATTR_SHOW}])`,\n )\n ) {\n problems.push(\n `a <template ${ATTR_GROUP}> holds no <template> for its contents`,\n );\n }\n }\n for (const name of AGGREGATES) {\n if (!el.hasAttribute(name)) continue;\n if (tag === \"template\") {\n problems.push(`a <template> carries ${name}; it goes on an element`);\n } else if (name !== ATTR_COUNT && !el.getAttribute(name)) {\n problems.push(`<${tag}> carries ${name} naming no column`);\n }\n }\n\n // The user's order and filter for the rows an element holds, each read\n // from a column of lb-choices it names.\n for (const name of [ATTR_ORDER, ATTR_FILTER]) {\n const value = el.getAttribute(name);\n if (value === null) continue;\n if (!el.hasAttribute(ATTR_QUERY)) {\n problems.push(\n `<${tag}> carries ${name}; it goes on the element carrying ` +\n `${ATTR_QUERY}`,\n );\n } else if (value.trim() === \"\") {\n problems.push(`<${tag}> carries ${name} naming no column`);\n }\n }\n\n // Judged by the scope around the element, not one the element carries:\n // its condition is a column of the row around it. With no lb-query\n // around it at all, nothing ever lands on it, and the element never\n // shows.\n const show = el.getAttribute(ATTR_SHOW);\n if (show !== null) {\n if (tag === \"template\") {\n problems.push(\n `a <template> carries ${ATTR_SHOW}=\"${show}\"; write it on the ` +\n `element it shows, and Loadbare supplies the template`,\n );\n } else if (scope === null || scope.row === false) {\n problems.push(\n `<${tag}> carries ${ATTR_SHOW}=\"${show}\" with no row around it ` +\n `to take the value from`,\n );\n } else if (show === SHOW_NOT || show.startsWith(SHOW_NOT + SHOW_NOT)) {\n problems.push(\n `<${tag}> carries ${ATTR_SHOW}=\"${show}\"; write one ` +\n `${SHOW_NOT} and then the column it reverses`,\n );\n }\n }\n\n walkMarkup(el, here, names, problems);\n // By tag name rather than by instanceof: template content belongs to a\n // document with no window, so the constructor is not reachable from the\n // node.\n if (tag === \"template\") {\n const content = (el as HTMLTemplateElement).content;\n const isRowTemplate =\n scope !== null && !!el.parentElement?.closest(`[${ATTR_QUERY}]`);\n if (isRowTemplate) {\n for (const root of content.children) {\n if (root.hasAttribute(ATTR_SHOW)) {\n problems.push(\n `<${root.localName}> is a row template's root and carries ` +\n `${ATTR_SHOW}; a row that should not show is left out by ` +\n `the query`,\n );\n }\n }\n }\n walkMarkup(\n content,\n isRowTemplate ? { query: scope!.query, row: true } : here,\n names,\n problems,\n );\n }\n }\n}\n\n/**\n * Ship every `lb-show` element already inside the template the hub keeps an\n * absent branch in, so nothing conditional shows until its value says so.\n * A page is in the document before its data arrives, and a value that has not\n * arrived is no value.\n *\n * Elements are collected before any is moved, and a wrapped element is still\n * reached once its outer one has gone into a template, because the references\n * are held.\n */\nfunction wrapShown(holder: HTMLTemplateElement): void {\n const doc = holder.ownerDocument;\n const shown: Element[] = [];\n const collect = (root: ParentNode): void => {\n shown.push(...root.querySelectorAll(`[${ATTR_SHOW}]`));\n for (const t of root.querySelectorAll(\"template\")) {\n collect((t as HTMLTemplateElement).content);\n }\n };\n collect(holder.content);\n for (const el of shown) {\n const parked = doc.createElement(\"template\");\n parked.setAttribute(ATTR_SHOW, el.getAttribute(ATTR_SHOW)!);\n el.replaceWith(parked);\n parked.content.append(el);\n }\n}\n\n/**\n * Expand one file's markup, naming the file if expansion refuses it.\n *\n * What comes back is the tree the browser will build, so every pass after\n * this reads and changes it in place, and it is serialized once, when it\n * ships.\n */\nfunction expandFile(\n file: string,\n source: string,\n defs: Definitions,\n): HTMLTemplateElement {\n try {\n return expandTree(source, defs);\n } catch (e) {\n throw new Error(`assemble: '${file}': ${(e as Error).message}`);\n }\n}\n\n/** Check one expanded fragment, and name the file if anything is wrong. */\nfunction checkMarkup(\n file: string,\n root: ParentNode,\n names: Names | null,\n): void {\n const problems: string[] = [];\n walkMarkup(root, null, names, problems);\n if (problems.length > 0) {\n throw new Error(`assemble: '${file}' markup:\\n ` + problems.join(`\\n `));\n }\n}\n\n/**\n * A page file's `<title>` names the page. It is lifted out, since a page is\n * a fragment inside `<main>` and a `<title>` belongs in a document's head,\n * and its text content is stamped on the page as ATTR_PAGE_TITLE.\n */\nfunction liftTitle(holder: HTMLTemplateElement): string | null {\n const title = holder.content.querySelector(\"title\");\n if (!title) return null;\n const text = title.textContent?.trim() ?? \"\";\n title.remove();\n return text;\n}\n\n/** An attribute value, escaped for a double-quoted attribute. */\nfunction attributeValue(text: string): string {\n return text.replace(/&/g, \"&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"]}
@@ -0,0 +1,78 @@
1
+ /**
2
+ * The build, as a library: one build, or a watch that rebuilds and says when
3
+ * each build has ended. `loadbare-app-build` (build/cli.ts) is a thin
4
+ * command over these, and `@loadbare/app/dev` runs the watch in process.
5
+ *
6
+ * Paths resolve against the working directory, as the command's do, so a
7
+ * caller runs from the application's root.
8
+ *
9
+ * A build writes nothing until every output is in hand. One that fails
10
+ * leaves the last good build in place, so a server reading `dist/` keeps
11
+ * serving something coherent while the developer fixes the mistake.
12
+ */
13
+ export interface BuildOptions {
14
+ /** The application's source tree, `src` when absent. */
15
+ src?: string;
16
+ /** Where the output lands, `dist` when absent. */
17
+ out?: string;
18
+ /** Minify `client.js` and `app.css`. */
19
+ minify?: boolean;
20
+ }
21
+ export interface Built {
22
+ /** Every file the build wrote. */
23
+ files: string[];
24
+ /** How many custom elements `client.js` registers. */
25
+ elements: number;
26
+ /** One line saying what was written, as the command prints it. */
27
+ summary: string;
28
+ }
29
+ /** Build once. Throws, having written nothing, when the build fails. */
30
+ export declare function buildApp(options?: BuildOptions): Promise<Built>;
31
+ /** How one build of a watch ended. */
32
+ export type BuildOutcome = {
33
+ ok: true;
34
+ changed: string[];
35
+ built: Built;
36
+ } | {
37
+ ok: false;
38
+ changed: string[];
39
+ error: Error;
40
+ };
41
+ export interface WatchOptions extends BuildOptions {
42
+ /**
43
+ * A build is starting, for these changed files under `src`, relative to
44
+ * it. Empty for the first build.
45
+ */
46
+ onStart?: (changed: string[]) => void;
47
+ /** A build has ended, well or badly. A failed build does not stop the watch. */
48
+ onBuilt?: (outcome: BuildOutcome) => void;
49
+ }
50
+ export interface AppWatcher {
51
+ /** How the first build ended. */
52
+ ready: Promise<BuildOutcome>;
53
+ /** Stop watching. A build already running finishes. */
54
+ close(): void;
55
+ }
56
+ /**
57
+ * Build, then build again whenever a file under `src` changes. Changes that
58
+ * arrive while a build runs are gathered into one build after it, so builds
59
+ * never overlap and never fall behind.
60
+ */
61
+ export declare function watchApp(options?: WatchOptions): AppWatcher;
62
+ /**
63
+ * Watch every directory under `root`, and each one made later, and report
64
+ * each file an event names, relative to `root`. A directory `skip` answers
65
+ * true for, relative to `root`, is not watched, nor is anything under it.
66
+ *
67
+ * Directories rather than `recursive: true`: on Linux, Node's recursive
68
+ * watch follows each file, so a file saved by replacing it, as `sed -i` and
69
+ * many editors save, is never heard from again. A directory's watch outlives
70
+ * every file in it.
71
+ *
72
+ * A directory made after the watch began may hold files before its own
73
+ * watch starts, so each file found in it is reported.
74
+ */
75
+ export declare function watchTree(root: string, onFile: (file: string) => void, skip?: (dir: string) => boolean): {
76
+ close(): void;
77
+ };
78
+ //# sourceMappingURL=build.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"build.d.ts","sourceRoot":"","sources":["../../build/build.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAkBH,MAAM,WAAW,YAAY;IAC3B,wDAAwD;IACxD,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,kDAAkD;IAClD,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,wCAAwC;IACxC,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED,MAAM,WAAW,KAAK;IACpB,kCAAkC;IAClC,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,sDAAsD;IACtD,QAAQ,EAAE,MAAM,CAAC;IACjB,kEAAkE;IAClE,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,wEAAwE;AACxE,wBAAsB,QAAQ,CAAC,OAAO,GAAE,YAAiB,GAAG,OAAO,CAAC,KAAK,CAAC,CAgEzE;AAED,sCAAsC;AACtC,MAAM,MAAM,YAAY,GACpB;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,OAAO,EAAE,MAAM,EAAE,CAAC;IAAC,KAAK,EAAE,KAAK,CAAA;CAAE,GAC7C;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,OAAO,EAAE,MAAM,EAAE,CAAC;IAAC,KAAK,EAAE,KAAK,CAAA;CAAE,CAAC;AAEnD,MAAM,WAAW,YAAa,SAAQ,YAAY;IAChD;;;OAGG;IACH,OAAO,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,IAAI,CAAC;IACtC,gFAAgF;IAChF,OAAO,CAAC,EAAE,CAAC,OAAO,EAAE,YAAY,KAAK,IAAI,CAAC;CAC3C;AAED,MAAM,WAAW,UAAU;IACzB,iCAAiC;IACjC,KAAK,EAAE,OAAO,CAAC,YAAY,CAAC,CAAC;IAC7B,uDAAuD;IACvD,KAAK,IAAI,IAAI,CAAC;CACf;AAYD;;;;GAIG;AACH,wBAAgB,QAAQ,CAAC,OAAO,GAAE,YAAiB,GAAG,UAAU,CA6D/D;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,SAAS,CACvB,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,EAC9B,IAAI,GAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAqB,GAC3C;IAAE,KAAK,IAAI,IAAI,CAAA;CAAE,CAqDnB"}
@@ -0,0 +1,220 @@
1
+ /**
2
+ * The build, as a library: one build, or a watch that rebuilds and says when
3
+ * each build has ended. `loadbare-app-build` (build/cli.ts) is a thin
4
+ * command over these, and `@loadbare/app/dev` runs the watch in process.
5
+ *
6
+ * Paths resolve against the working directory, as the command's do, so a
7
+ * caller runs from the application's root.
8
+ *
9
+ * A build writes nothing until every output is in hand. One that fails
10
+ * leaves the last good build in place, so a server reading `dist/` keeps
11
+ * serving something coherent while the developer fixes the mistake.
12
+ */
13
+ import { readdirSync, statSync, watch as watchDir } from "node:fs";
14
+ import { mkdir, writeFile } from "node:fs/promises";
15
+ import path from "node:path";
16
+ import * as esbuild from "esbuild";
17
+ import { assemble } from "./assemble.js";
18
+ import { loadDeclared } from "./declared.js";
19
+ import { checkReservedMethods, clientEntrySource, resolveElements, } from "./elements.js";
20
+ import { resolveLocations } from "./locations.js";
21
+ import { cssFrom, scanAll } from "./origins.js";
22
+ import { pagesSource } from "./pages.js";
23
+ import { stylesSource } from "./styles.js";
24
+ /** Build once. Throws, having written nothing, when the build fails. */
25
+ export async function buildApp(options = {}) {
26
+ const src = options.src ?? "src";
27
+ const minify = options.minify ?? false;
28
+ const locations = await resolveLocations({ src, out: options.out ?? "dist" });
29
+ const { outDir } = locations;
30
+ const entryFile = path.join(outDir, "client-entry.ts");
31
+ const scans = await scanAll(locations, src, locations.importsFile);
32
+ await mkdir(outDir, { recursive: true });
33
+ const declared = await loadDeclared(locations.pages, locations.pageSources, outDir);
34
+ const { html, usedTags } = await assemble({ ...locations, scans, declared });
35
+ const { resolved } = resolveElements({ used: usedTags, scans });
36
+ await checkReservedMethods(resolved);
37
+ // The entry is read by the bundler and never served, so it is written now.
38
+ await writeFile(entryFile, clientEntrySource(resolved, outDir), "utf-8");
39
+ const bundle = await esbuild.build({
40
+ entryPoints: [entryFile],
41
+ bundle: true,
42
+ format: "iife",
43
+ outfile: path.join(outDir, "client.js"),
44
+ minify,
45
+ write: false,
46
+ // esbuild keeps the directory it started in; the paths are the caller's.
47
+ absWorkingDir: process.cwd(),
48
+ });
49
+ const outputs = new Map();
50
+ outputs.set(path.join(outDir, "app.html"), html);
51
+ for (const file of bundle.outputFiles)
52
+ outputs.set(file.path, file.contents);
53
+ const pages = pagesSource(locations.pages, locations.pageSources, outDir, locations.packageName);
54
+ if (pages)
55
+ outputs.set(path.join(outDir, "pages.ts"), pages);
56
+ const css = await stylesSource(cssFrom(scans));
57
+ if (css) {
58
+ const code = minify
59
+ ? (await esbuild.transform(css, { loader: "css", minify: true })).code
60
+ : css;
61
+ outputs.set(path.join(outDir, "app.css"), code);
62
+ }
63
+ for (const [file, content] of outputs)
64
+ await writeFile(file, content);
65
+ const files = [...outputs.keys()].map((file) => path.relative(process.cwd(), file));
66
+ const elements = resolved.size;
67
+ return {
68
+ files,
69
+ elements,
70
+ summary: `wrote ${files.join(" and ")} ` +
71
+ `(${elements} custom element${elements === 1 ? "" : "s"})`,
72
+ };
73
+ }
74
+ /** The files a build reads. A change to any other file starts no build. */
75
+ const BUILT_FROM = /\.(html|ts|css)$/;
76
+ /**
77
+ * Edits arrive in bursts: an editor's save is often several events, and a
78
+ * branch switch is hundreds. Waiting this long after the first lets the
79
+ * burst finish, so it starts one build.
80
+ */
81
+ const SETTLE_MS = 20;
82
+ /**
83
+ * Build, then build again whenever a file under `src` changes. Changes that
84
+ * arrive while a build runs are gathered into one build after it, so builds
85
+ * never overlap and never fall behind.
86
+ */
87
+ export function watchApp(options = {}) {
88
+ const src = options.src ?? "src";
89
+ // An output directory inside the source tree is written by every build,
90
+ // and a change there must not start the next one.
91
+ const out = path.relative(path.resolve(src), path.resolve(options.out ?? "dist"));
92
+ const inOut = (file) => out !== "" && !out.startsWith("..") && (file === out || file.startsWith(out + path.sep));
93
+ let pending = new Set();
94
+ let running = false;
95
+ let timer;
96
+ let closed = false;
97
+ const once = async (changed) => {
98
+ options.onStart?.(changed);
99
+ let outcome;
100
+ try {
101
+ outcome = { ok: true, changed, built: await buildApp(options) };
102
+ }
103
+ catch (err) {
104
+ const error = err instanceof Error ? err : new Error(String(err));
105
+ outcome = { ok: false, changed, error };
106
+ }
107
+ options.onBuilt?.(outcome);
108
+ return outcome;
109
+ };
110
+ const drain = async () => {
111
+ running = true;
112
+ while (pending.size > 0 && !closed) {
113
+ const changed = [...pending].sort();
114
+ pending = new Set();
115
+ await once(changed);
116
+ }
117
+ running = false;
118
+ };
119
+ const ready = once([]).then((outcome) => {
120
+ if (pending.size > 0)
121
+ void drain();
122
+ return outcome;
123
+ });
124
+ const tree = watchTree(src, (file) => {
125
+ if (closed || !BUILT_FROM.test(file) || inOut(file))
126
+ return;
127
+ pending.add(file.split(path.sep).join("/"));
128
+ if (running || timer)
129
+ return;
130
+ timer = setTimeout(() => {
131
+ timer = undefined;
132
+ // The first build is still running: it starts the next when it ends.
133
+ void ready.then(() => {
134
+ if (!running)
135
+ void drain();
136
+ });
137
+ }, SETTLE_MS);
138
+ }, inOut);
139
+ return {
140
+ ready,
141
+ close() {
142
+ closed = true;
143
+ clearTimeout(timer);
144
+ tree.close();
145
+ },
146
+ };
147
+ }
148
+ /**
149
+ * Watch every directory under `root`, and each one made later, and report
150
+ * each file an event names, relative to `root`. A directory `skip` answers
151
+ * true for, relative to `root`, is not watched, nor is anything under it.
152
+ *
153
+ * Directories rather than `recursive: true`: on Linux, Node's recursive
154
+ * watch follows each file, so a file saved by replacing it, as `sed -i` and
155
+ * many editors save, is never heard from again. A directory's watch outlives
156
+ * every file in it.
157
+ *
158
+ * A directory made after the watch began may hold files before its own
159
+ * watch starts, so each file found in it is reported.
160
+ */
161
+ export function watchTree(root, onFile, skip = () => false) {
162
+ const watchers = new Map();
163
+ const drop = (dir) => {
164
+ for (const [watched, watcher] of watchers) {
165
+ if (dir === "" || watched === dir || watched.startsWith(dir + path.sep)) {
166
+ watcher.close();
167
+ watchers.delete(watched);
168
+ }
169
+ }
170
+ };
171
+ const changed = (file) => {
172
+ let isDir = false;
173
+ try {
174
+ isDir = statSync(path.join(root, file)).isDirectory();
175
+ }
176
+ catch {
177
+ // Gone: a deleted file is a change, and a deleted directory's watch ends.
178
+ }
179
+ if (isDir) {
180
+ if (!watchers.has(file))
181
+ add(file, true);
182
+ }
183
+ else {
184
+ drop(file);
185
+ onFile(file);
186
+ }
187
+ };
188
+ const add = (dir, report) => {
189
+ if (skip(dir))
190
+ return;
191
+ const full = path.join(root, dir);
192
+ let watcher;
193
+ let entries;
194
+ try {
195
+ watcher = watchDir(full, (_event, name) => {
196
+ if (name)
197
+ changed(path.join(dir, name.toString()));
198
+ });
199
+ entries = readdirSync(full, { withFileTypes: true });
200
+ }
201
+ catch (err) {
202
+ // The root must exist; a directory below it may be gone already.
203
+ if (dir === "")
204
+ throw err;
205
+ return;
206
+ }
207
+ watcher.on("error", () => drop(dir));
208
+ watchers.set(dir, watcher);
209
+ for (const entry of entries) {
210
+ const child = path.join(dir, entry.name);
211
+ if (entry.isDirectory())
212
+ add(child, report);
213
+ else if (report)
214
+ onFile(child);
215
+ }
216
+ };
217
+ add("", false);
218
+ return { close: () => drop("") };
219
+ }
220
+ //# sourceMappingURL=build.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"build.js","sourceRoot":"","sources":["../../build/build.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The build, as a library: one build, or a watch that rebuilds and says when\n * each build has ended. `loadbare-app-build` (build/cli.ts) is a thin\n * command over these, and `@loadbare/app/dev` runs the watch in process.\n *\n * Paths resolve against the working directory, as the command's do, so a\n * caller runs from the application's root.\n *\n * A build writes nothing until every output is in hand. One that fails\n * leaves the last good build in place, so a server reading `dist/` keeps\n * serving something coherent while the developer fixes the mistake.\n */\n\nimport { readdirSync, statSync, watch as watchDir, type FSWatcher } from \"node:fs\";\nimport { mkdir, writeFile } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport * as esbuild from \"esbuild\";\nimport { assemble } from \"./assemble.js\";\nimport { loadDeclared } from \"./declared.js\";\nimport {\n checkReservedMethods,\n clientEntrySource,\n resolveElements,\n} from \"./elements.js\";\nimport { resolveLocations } from \"./locations.js\";\nimport { cssFrom, scanAll } from \"./origins.js\";\nimport { pagesSource } from \"./pages.js\";\nimport { stylesSource } from \"./styles.js\";\n\nexport interface BuildOptions {\n /** The application's source tree, `src` when absent. */\n src?: string;\n /** Where the output lands, `dist` when absent. */\n out?: string;\n /** Minify `client.js` and `app.css`. */\n minify?: boolean;\n}\n\nexport interface Built {\n /** Every file the build wrote. */\n files: string[];\n /** How many custom elements `client.js` registers. */\n elements: number;\n /** One line saying what was written, as the command prints it. */\n summary: string;\n}\n\n/** Build once. Throws, having written nothing, when the build fails. */\nexport async function buildApp(options: BuildOptions = {}): Promise<Built> {\n const src = options.src ?? \"src\";\n const minify = options.minify ?? false;\n const locations = await resolveLocations({ src, out: options.out ?? \"dist\" });\n const { outDir } = locations;\n const entryFile = path.join(outDir, \"client-entry.ts\");\n\n const scans = await scanAll(locations, src, locations.importsFile);\n await mkdir(outDir, { recursive: true });\n const declared = await loadDeclared(\n locations.pages,\n locations.pageSources,\n outDir,\n );\n const { html, usedTags } = await assemble({ ...locations, scans, declared });\n\n const { resolved } = resolveElements({ used: usedTags, scans });\n await checkReservedMethods(resolved);\n // The entry is read by the bundler and never served, so it is written now.\n await writeFile(entryFile, clientEntrySource(resolved, outDir), \"utf-8\");\n const bundle = await esbuild.build({\n entryPoints: [entryFile],\n bundle: true,\n format: \"iife\",\n outfile: path.join(outDir, \"client.js\"),\n minify,\n write: false,\n // esbuild keeps the directory it started in; the paths are the caller's.\n absWorkingDir: process.cwd(),\n });\n\n const outputs = new Map<string, string | Uint8Array>();\n outputs.set(path.join(outDir, \"app.html\"), html);\n for (const file of bundle.outputFiles) outputs.set(file.path, file.contents);\n\n const pages = pagesSource(\n locations.pages,\n locations.pageSources,\n outDir,\n locations.packageName,\n );\n if (pages) outputs.set(path.join(outDir, \"pages.ts\"), pages);\n\n const css = await stylesSource(cssFrom(scans));\n if (css) {\n const code = minify\n ? (await esbuild.transform(css, { loader: \"css\", minify: true })).code\n : css;\n outputs.set(path.join(outDir, \"app.css\"), code);\n }\n\n for (const [file, content] of outputs) await writeFile(file, content);\n\n const files = [...outputs.keys()].map((file) =>\n path.relative(process.cwd(), file),\n );\n const elements = resolved.size;\n return {\n files,\n elements,\n summary:\n `wrote ${files.join(\" and \")} ` +\n `(${elements} custom element${elements === 1 ? \"\" : \"s\"})`,\n };\n}\n\n/** How one build of a watch ended. */\nexport type BuildOutcome =\n | { ok: true; changed: string[]; built: Built }\n | { ok: false; changed: string[]; error: Error };\n\nexport interface WatchOptions extends BuildOptions {\n /**\n * A build is starting, for these changed files under `src`, relative to\n * it. Empty for the first build.\n */\n onStart?: (changed: string[]) => void;\n /** A build has ended, well or badly. A failed build does not stop the watch. */\n onBuilt?: (outcome: BuildOutcome) => void;\n}\n\nexport interface AppWatcher {\n /** How the first build ended. */\n ready: Promise<BuildOutcome>;\n /** Stop watching. A build already running finishes. */\n close(): void;\n}\n\n/** The files a build reads. A change to any other file starts no build. */\nconst BUILT_FROM = /\\.(html|ts|css)$/;\n\n/**\n * Edits arrive in bursts: an editor's save is often several events, and a\n * branch switch is hundreds. Waiting this long after the first lets the\n * burst finish, so it starts one build.\n */\nconst SETTLE_MS = 20;\n\n/**\n * Build, then build again whenever a file under `src` changes. Changes that\n * arrive while a build runs are gathered into one build after it, so builds\n * never overlap and never fall behind.\n */\nexport function watchApp(options: WatchOptions = {}): AppWatcher {\n const src = options.src ?? \"src\";\n // An output directory inside the source tree is written by every build,\n // and a change there must not start the next one.\n const out = path.relative(path.resolve(src), path.resolve(options.out ?? \"dist\"));\n const inOut = (file: string) =>\n out !== \"\" && !out.startsWith(\"..\") && (file === out || file.startsWith(out + path.sep));\n let pending = new Set<string>();\n let running = false;\n let timer: NodeJS.Timeout | undefined;\n let closed = false;\n\n const once = async (changed: string[]): Promise<BuildOutcome> => {\n options.onStart?.(changed);\n let outcome: BuildOutcome;\n try {\n outcome = { ok: true, changed, built: await buildApp(options) };\n } catch (err) {\n const error = err instanceof Error ? err : new Error(String(err));\n outcome = { ok: false, changed, error };\n }\n options.onBuilt?.(outcome);\n return outcome;\n };\n\n const drain = async (): Promise<void> => {\n running = true;\n while (pending.size > 0 && !closed) {\n const changed = [...pending].sort();\n pending = new Set();\n await once(changed);\n }\n running = false;\n };\n\n const ready = once([]).then((outcome) => {\n if (pending.size > 0) void drain();\n return outcome;\n });\n\n const tree = watchTree(src, (file) => {\n if (closed || !BUILT_FROM.test(file) || inOut(file)) return;\n pending.add(file.split(path.sep).join(\"/\"));\n if (running || timer) return;\n timer = setTimeout(() => {\n timer = undefined;\n // The first build is still running: it starts the next when it ends.\n void ready.then(() => {\n if (!running) void drain();\n });\n }, SETTLE_MS);\n }, inOut);\n\n return {\n ready,\n close() {\n closed = true;\n clearTimeout(timer);\n tree.close();\n },\n };\n}\n\n/**\n * Watch every directory under `root`, and each one made later, and report\n * each file an event names, relative to `root`. A directory `skip` answers\n * true for, relative to `root`, is not watched, nor is anything under it.\n *\n * Directories rather than `recursive: true`: on Linux, Node's recursive\n * watch follows each file, so a file saved by replacing it, as `sed -i` and\n * many editors save, is never heard from again. A directory's watch outlives\n * every file in it.\n *\n * A directory made after the watch began may hold files before its own\n * watch starts, so each file found in it is reported.\n */\nexport function watchTree(\n root: string,\n onFile: (file: string) => void,\n skip: (dir: string) => boolean = () => false,\n): { close(): void } {\n const watchers = new Map<string, FSWatcher>();\n\n const drop = (dir: string) => {\n for (const [watched, watcher] of watchers) {\n if (dir === \"\" || watched === dir || watched.startsWith(dir + path.sep)) {\n watcher.close();\n watchers.delete(watched);\n }\n }\n };\n\n const changed = (file: string) => {\n let isDir = false;\n try {\n isDir = statSync(path.join(root, file)).isDirectory();\n } catch {\n // Gone: a deleted file is a change, and a deleted directory's watch ends.\n }\n if (isDir) {\n if (!watchers.has(file)) add(file, true);\n } else {\n drop(file);\n onFile(file);\n }\n };\n\n const add = (dir: string, report: boolean) => {\n if (skip(dir)) return;\n const full = path.join(root, dir);\n let watcher: FSWatcher;\n let entries;\n try {\n watcher = watchDir(full, (_event, name) => {\n if (name) changed(path.join(dir, name.toString()));\n });\n entries = readdirSync(full, { withFileTypes: true });\n } catch (err) {\n // The root must exist; a directory below it may be gone already.\n if (dir === \"\") throw err;\n return;\n }\n watcher.on(\"error\", () => drop(dir));\n watchers.set(dir, watcher);\n for (const entry of entries) {\n const child = path.join(dir, entry.name);\n if (entry.isDirectory()) add(child, report);\n else if (report) onFile(child);\n }\n };\n\n add(\"\", false);\n return { close: () => drop(\"\") };\n}\n"]}
@@ -35,7 +35,12 @@
35
35
  * markup, a widget's class, a stylesheet, and `imports.ts` all feed the
36
36
  * same build — and a rebuild that throws is reported without stopping the
37
37
  * watch. There is no dev server and no browser reload here, only the files
38
- * on disk staying current for the next manual refresh.
38
+ * on disk staying current for the next manual refresh; `@loadbare/app/dev`
39
+ * is the dev server.
40
+ *
41
+ * The build itself is build/build.ts. This file is the command: its flags,
42
+ * and the lines it prints, one per build, `wrote ...` on stdout or the
43
+ * failure on stderr.
39
44
  */
40
45
  export {};
41
46
  //# sourceMappingURL=cli.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../../build/cli.ts"],"names":[],"mappings":";AACA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG"}
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../../build/cli.ts"],"names":[],"mappings":";AACA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0CG"}