@loadbare/app 0.4.0 → 0.5.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 (160) hide show
  1. package/README.md +53 -82
  2. package/dist/build/assemble.d.ts +7 -5
  3. package/dist/build/assemble.d.ts.map +1 -1
  4. package/dist/build/assemble.js +29 -9
  5. package/dist/build/cli.d.ts +20 -12
  6. package/dist/build/cli.d.ts.map +1 -1
  7. package/dist/build/cli.js +34 -16
  8. package/dist/build/elements.d.ts +15 -29
  9. package/dist/build/elements.d.ts.map +1 -1
  10. package/dist/build/elements.js +25 -111
  11. package/dist/build/expand.d.ts +1 -1
  12. package/dist/build/expand.js +1 -1
  13. package/dist/build/locations.d.ts +14 -37
  14. package/dist/build/locations.d.ts.map +1 -1
  15. package/dist/build/locations.js +24 -67
  16. package/dist/build/origins.d.ts +109 -0
  17. package/dist/build/origins.d.ts.map +1 -0
  18. package/dist/build/origins.js +270 -0
  19. package/dist/core/lb-constants.d.ts +1 -0
  20. package/dist/core/lb-constants.d.ts.map +1 -1
  21. package/dist/core/lb-constants.js +15 -8
  22. package/dist/core/lb-types.d.ts +2 -2
  23. package/dist/core/lb-types.d.ts.map +1 -1
  24. package/dist/hub/lb-apply.js +1 -1
  25. package/dist/hub/lb-hub.d.ts.map +1 -1
  26. package/dist/hub/lb-hub.js +44 -17
  27. package/dist/hub/lb-rows.js +3 -3
  28. package/dist/server/lb-server.d.ts +5 -4
  29. package/dist/server/lb-server.d.ts.map +1 -1
  30. package/dist/tests/assemble.test.js +11 -4
  31. package/dist/tests/elements.test.js +47 -51
  32. package/dist/tests/expand.test.d.ts +1 -1
  33. package/dist/tests/expand.test.js +2 -2
  34. package/dist/tests/fixtures/elements/collision/imports.d.ts +3 -0
  35. package/dist/tests/fixtures/elements/collision/imports.d.ts.map +1 -0
  36. package/dist/tests/fixtures/elements/collision/imports.js +1 -0
  37. package/dist/tests/fixtures/elements/manifest/imports.d.ts +3 -0
  38. package/dist/tests/fixtures/elements/manifest/imports.d.ts.map +1 -0
  39. package/dist/tests/fixtures/elements/manifest/imports.js +1 -0
  40. package/dist/tests/fixtures/elements/manifest-bad-entry/imports.d.ts +3 -0
  41. package/dist/tests/fixtures/elements/manifest-bad-entry/imports.d.ts.map +1 -0
  42. package/dist/tests/fixtures/elements/manifest-bad-entry/imports.js +1 -0
  43. package/dist/tests/fixtures/elements/{collision/elements.d.ts → manifest-not-array/imports.d.ts} +1 -1
  44. package/dist/tests/fixtures/elements/manifest-not-array/imports.d.ts.map +1 -0
  45. package/dist/tests/fixtures/elements/manifest-not-array/imports.js +1 -0
  46. package/dist/tests/fixtures/elements/pkg/acme-widget.d.ts +2 -0
  47. package/dist/tests/fixtures/elements/pkg/acme-widget.d.ts.map +1 -0
  48. package/dist/tests/fixtures/elements/pkg/acme-widget.js +1 -0
  49. package/dist/tests/lb-express.test.js +1 -1
  50. package/dist/tests/origins.test.d.ts +10 -0
  51. package/dist/tests/origins.test.d.ts.map +1 -0
  52. package/dist/tests/origins.test.js +326 -0
  53. package/dist/tests/pages.test.js +3 -3
  54. package/dist/tests/styles.test.js +7 -4
  55. package/docs/reference/builder.md +128 -0
  56. package/docs/reference/chrome.md +75 -0
  57. package/docs/reference/css.md +44 -0
  58. package/docs/reference/custom-elements.md +327 -0
  59. package/docs/reference/data-binding.md +240 -0
  60. package/docs/reference/overview.md +38 -0
  61. package/docs/reference/page-files.md +175 -0
  62. package/docs/reference/server.md +123 -0
  63. package/docs/reference/widgets.md +163 -0
  64. package/docs/roadmap.md +130 -0
  65. package/docs/testing.md +228 -0
  66. package/docs/theory.md +344 -223
  67. package/docs/tutorials/000-getting-started.md +86 -0
  68. package/docs/tutorials/010-pages-and-navigation.md +129 -0
  69. package/docs/tutorials/020-css.md +103 -0
  70. package/docs/tutorials/030-html-decomposition.md +79 -0
  71. package/docs/tutorials/040-displaying-data.md +169 -0
  72. package/docs/tutorials/050-actions.md +77 -0
  73. package/docs/tutorials/060-custom-element-code.md +73 -0
  74. package/docs/tutorials/065-conditional-rendering.md +161 -0
  75. package/docs/tutorials/070-displaying-a-list.md +137 -0
  76. package/docs/tutorials/072-inserting-into-a-list.md +88 -0
  77. package/docs/tutorials/074-deleting-from-a-list.md +77 -0
  78. package/docs/tutorials/076-updating-a-list-item.md +86 -0
  79. package/docs/tutorials/080-widget-requests.md +124 -0
  80. package/docs/tutorials/090-using-widget-libraries.md +75 -0
  81. package/package.json +4 -12
  82. package/dist/client.js +0 -522
  83. package/dist/demo-static/src/widgets/app-box.d.ts +0 -15
  84. package/dist/demo-static/src/widgets/app-box.d.ts.map +0 -1
  85. package/dist/demo-static/src/widgets/app-box.js +0 -19
  86. package/dist/tests/fixtures/elements/collision/elements.d.ts.map +0 -1
  87. package/dist/tests/fixtures/elements/collision/elements.js +0 -3
  88. package/dist/tests/fixtures/elements/manifest/elements.d.ts +0 -5
  89. package/dist/tests/fixtures/elements/manifest/elements.d.ts.map +0 -1
  90. package/dist/tests/fixtures/elements/manifest/elements.js +0 -3
  91. package/dist/tests/fixtures/elements/manifest-bad-tag/elements.d.ts +0 -5
  92. package/dist/tests/fixtures/elements/manifest-bad-tag/elements.d.ts.map +0 -1
  93. package/dist/tests/fixtures/elements/manifest-bad-tag/elements.js +0 -3
  94. package/dist/tests/fixtures/elements/manifest-bad-value/elements.d.ts +0 -5
  95. package/dist/tests/fixtures/elements/manifest-bad-value/elements.d.ts.map +0 -1
  96. package/dist/tests/fixtures/elements/manifest-bad-value/elements.js +0 -3
  97. package/dist/tests/golden.test.d.ts +0 -19
  98. package/dist/tests/golden.test.d.ts.map +0 -1
  99. package/dist/tests/golden.test.js +0 -60
  100. package/dist/tests/helpers/window.d.ts +0 -43
  101. package/dist/tests/helpers/window.d.ts.map +0 -1
  102. package/dist/tests/helpers/window.js +0 -78
  103. package/dist/tests/lb-input.test.d.ts +0 -9
  104. package/dist/tests/lb-input.test.d.ts.map +0 -1
  105. package/dist/tests/lb-input.test.js +0 -78
  106. package/dist/tests/lb-list.test.d.ts +0 -12
  107. package/dist/tests/lb-list.test.d.ts.map +0 -1
  108. package/dist/tests/lb-list.test.js +0 -44
  109. package/dist/tests/lb-options.test.d.ts +0 -10
  110. package/dist/tests/lb-options.test.d.ts.map +0 -1
  111. package/dist/tests/lb-options.test.js +0 -121
  112. package/dist/tests/lb-picker.test.d.ts +0 -14
  113. package/dist/tests/lb-picker.test.d.ts.map +0 -1
  114. package/dist/tests/lb-picker.test.js +0 -59
  115. package/dist/tests/lb-select.test.d.ts +0 -9
  116. package/dist/tests/lb-select.test.d.ts.map +0 -1
  117. package/dist/tests/lb-select.test.js +0 -71
  118. package/dist/tests/lb-table.test.d.ts +0 -15
  119. package/dist/tests/lb-table.test.d.ts.map +0 -1
  120. package/dist/tests/lb-table.test.js +0 -205
  121. package/dist/widgets/index.d.ts +0 -7
  122. package/dist/widgets/index.d.ts.map +0 -1
  123. package/dist/widgets/index.js +0 -6
  124. package/dist/widgets/lb-input.d.ts +0 -2
  125. package/dist/widgets/lb-input.d.ts.map +0 -1
  126. package/dist/widgets/lb-input.js +0 -48
  127. package/dist/widgets/lb-list.d.ts +0 -2
  128. package/dist/widgets/lb-list.d.ts.map +0 -1
  129. package/dist/widgets/lb-list.js +0 -17
  130. package/dist/widgets/lb-options.d.ts +0 -26
  131. package/dist/widgets/lb-options.d.ts.map +0 -1
  132. package/dist/widgets/lb-options.js +0 -72
  133. package/dist/widgets/lb-picker.d.ts +0 -2
  134. package/dist/widgets/lb-picker.d.ts.map +0 -1
  135. package/dist/widgets/lb-picker.js +0 -25
  136. package/dist/widgets/lb-select.d.ts +0 -2
  137. package/dist/widgets/lb-select.d.ts.map +0 -1
  138. package/dist/widgets/lb-select.js +0 -43
  139. package/dist/widgets/lb-table.d.ts +0 -2
  140. package/dist/widgets/lb-table.d.ts.map +0 -1
  141. package/dist/widgets/lb-table.js +0 -113
  142. package/docs/application-chrome.md +0 -36
  143. package/docs/building-html-pages.md +0 -130
  144. package/docs/getting-started.md +0 -120
  145. package/docs/guide.md +0 -1164
  146. package/docs/hosting.md +0 -218
  147. package/docs/latent-risks.md +0 -20
  148. package/widgets/index.ts +0 -6
  149. package/widgets/lb-input.html +0 -1
  150. package/widgets/lb-input.ts +0 -64
  151. package/widgets/lb-list.html +0 -1
  152. package/widgets/lb-list.ts +0 -21
  153. package/widgets/lb-options.html +0 -4
  154. package/widgets/lb-options.ts +0 -88
  155. package/widgets/lb-picker.html +0 -7
  156. package/widgets/lb-picker.ts +0 -27
  157. package/widgets/lb-select.html +0 -4
  158. package/widgets/lb-select.ts +0 -55
  159. package/widgets/lb-table.html +0 -8
  160. package/widgets/lb-table.ts +0 -126
package/README.md CHANGED
@@ -1,8 +1,13 @@
1
1
  # Loadbare App
2
2
 
3
- > **This project is being refactored.** While @loadbare/db is highly mature
4
- > and in use reliably by the author, this ui project is being built from
5
- > scratch. We know what it will do, but everything has to be built.
3
+ The fundamentals of @loadbare/app are fixed, we are now working
4
+ on completeness.
5
+
6
+ > This package is being prepped for 1.0, current work is:
7
+ > 1. docs (nearly done)
8
+ > 2. demos (must be normalized and checked against docs)
9
+ > 3. test a release/use cycle, make sure all instructions work
10
+
6
11
 
7
12
  ## Purpose
8
13
 
@@ -14,82 +19,48 @@ Loadbare App has two twin primary goals
14
19
  to near zero, so the app developer is spending maximum time on the app
15
20
  instead of the tooling.
16
21
  - To give the developer a fighting chance to provide complete server round
17
- trips, from request to final paint, in 300ms or less
18
-
19
- ## Current WIP
20
-
21
- Restructuring the docs. Right now "getting started"
22
- and other docs are very limited because they only document what we've
23
- tested and proven. Once the build is doing everything it needs to,
24
- then we can write the docs to flow more like:
25
- - setting up your infra:
26
- - add the package
27
- - chrome
28
- - express server
29
- - writing a page with mock db access
30
- - wiring express to call our handler
31
- - the html
32
- - hooks and handlers
33
- - plugging in your database, just replace mock with your
34
- own adapter.
35
-
36
-
37
- ## SLOP LINE
38
-
39
- Below here is LLM-generated slop, not yet edited to conform to our rules
40
- against LLM-generated markdown docs.
41
-
42
-
43
- ## Ideals
44
-
45
- Loadbare App should be fully inspectable. "View Source" should show the source, and
46
- what it shows should be what the developer tools show. Frameworks commonly
47
- lose this property as a side effect of how they generate markup; Loadbare App treats
48
- keeping it as a requirement.
49
-
50
- Stated precisely, because two things legitimately differ: no markup exists in
51
- the DOM that is not in view-source, and the difference between view-source and
52
- the developer tools is exactly the dynamic half. Navigation moves markup that
53
- is already in the document, and data lands on it as attributes. Neither
54
- invents markup.
55
-
56
- Some widgets, an enriched table being the obvious one, must clone a template
57
- once per row, and the number of rows is not known until the data arrives. That
58
- cloning is allowable: the markup is in view-source, and only the count comes
59
- from the data. Where the boundary of that allowance sits is stated in
60
- [Theory of Loadbare App](./docs/theory.md).
61
-
62
- Nothing here is about protecting the HTML, which is visible in the developer
63
- tools regardless. It is about the technology being open to inspection by
64
- whoever wants to look.
65
-
66
- ## Documentation
67
-
68
- [`docs/theory.md`](./docs/theory.md) states what Loadbare App is and
69
- why. It is authoritative; where any other document disagrees with it, the other
70
- document is wrong.
71
-
72
- - [Programmer's Guide](./docs/guide.md) — how to build an application with
73
- Loadbare App. Each section is marked with whether the code does it yet.
74
- - [Hosting a Loadbare App Application](./docs/hosting.md) — registering pages,
75
- assembling the shell, navigation, and serving the two endpoints from
76
- Express. Everything you do once per application rather than once per page.
77
- - [Theory of Loadbare App](./docs/theory.md) — scope, the performance budget,
78
- the thesis, and what is settled so far.
79
- - [Communication channels](./docs-llm-slop/communication-channels.md) — what
80
- the host/data split means on the wire.
81
- - [Data binding](./docs-llm-slop/data-binding.md) — the addressing vocabulary
82
- and how a data fragment lands on a widget.
83
- - [Requests](./docs-llm-slop/requests.md) — how a widget reaches the server,
84
- and what the server may trust.
85
- - [Bundling](./docs-llm-slop/bundling.md) — how the static half is packaged
86
- and shipped.
87
- - [Expansion](./docs-llm-slop/expansion.md) — how one authored tag becomes the
88
- tree it stands for, and why the definition language has no control flow.
89
- - [Coming from other frameworks](./docs-llm-slop/coming-from-other-frameworks.md)
90
- — what changes if you already know React, Angular, htmx or Datastar.
91
- - [Prior art](./docs-llm-slop/prior-art.md) — what has been done before, and
92
- why nothing was adopted off the shelf.
93
-
94
- The guide states the API as it should be and marks each section with whether
95
- the code does it yet. Where the two disagree, the code is what moves.
22
+ trips, from request to final paint, in 300ms or less.
23
+
24
+ ## Widgets
25
+
26
+ This package ships one custom element, `<lb-hub>`. The widgets an application
27
+ displays data with — `lb-input`, `lb-select`, `lb-list`, `lb-options`,
28
+ `lb-table`, `lb-picker` — ship separately, in
29
+ [@loadbare/widgets](../widgets/README.md):
30
+
31
+ ```
32
+ npm install @loadbare/app @loadbare/widgets
33
+ ```
34
+
35
+ ```ts
36
+ // src/imports.ts
37
+ export default ["@loadbare/widgets"];
38
+ ```
39
+
40
+ The builder resolves that package the way it resolves anyone else's, so a
41
+ widget library written by a third party, or by the application's own team,
42
+ arrives through the same one line. See
43
+ [The Basic Widget Library](./docs/reference/widgets.md) for what each widget
44
+ binds and sends, and
45
+ [Using Widget Libraries](./docs/tutorials/090-using-widget-libraries.md) for
46
+ what listing a package does.
47
+
48
+ ## Tutorials
49
+
50
+ [Getting Started](./docs/tutorials/000-getting-started.md): a step-by-step
51
+ guide to building a database app, introducing one new concept in
52
+ each lesson.
53
+
54
+ ## Reference
55
+
56
+ [The Elements of a Loadbare Application](./docs/reference/overview.md)
57
+
58
+ ## Theory
59
+
60
+ [Theory](./docs/theory.md): the design rationale behind Loadbare App.
61
+
62
+ ## Roadmap Items for 1.0 and 1.1
63
+
64
+ [Roadmap](./docs/roadmap.md)
65
+
66
+
@@ -9,23 +9,25 @@
9
9
  * expanded host is wrapped in `<template id="page-<name>">` and, together
10
10
  * with the chrome's own expanded body content, spliced back into the
11
11
  * chrome's `<body>`. The result is the one document a server has to serve
12
- * for every route — see docs/getting-started.md and docs/application-chrome.md.
12
+ * for every route — see docs/tutorials/000-getting-started.md and
13
+ * docs/reference/builder.md.
13
14
  *
14
15
  * Every path here is a parameter, resolved once by build/locations.ts — this
15
16
  * module never reconstructs one of its own, and never reads a directory
16
17
  * looking for pages or widgets; it is handed exactly which files those are.
17
18
  */
18
- import type { PageFile, WidgetSource } from "./locations.js";
19
+ import type { PageFile } from "./locations.js";
20
+ import type { OriginScan } from "./origins.js";
19
21
  export interface AssembleOptions {
20
22
  chromeFile: string;
21
23
  pages: PageFile[];
22
- widgets: Map<string, WidgetSource>;
23
- builtinWidgetsDir: string;
24
+ /** Every origin, already scanned, in cascade order — see build/origins.ts. */
25
+ scans: OriginScan[];
24
26
  }
25
27
  export interface Assembled {
26
28
  html: string;
27
29
  /** Every custom element tag left in the document once expansion is done. */
28
30
  usedTags: Set<string>;
29
31
  }
30
- export declare function assemble({ chromeFile, pages, widgets, builtinWidgetsDir, }: AssembleOptions): Promise<Assembled>;
32
+ export declare function assemble({ chromeFile, pages, scans, }: AssembleOptions): Promise<Assembled>;
31
33
  //# sourceMappingURL=assemble.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"assemble.d.ts","sourceRoot":"","sources":["../../build/assemble.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAWH,OAAO,KAAK,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAE7D,MAAM,WAAW,eAAe;IAC9B,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,QAAQ,EAAE,CAAC;IAClB,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;IACnC,iBAAiB,EAAE,MAAM,CAAC;CAC3B;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,OAAO,EACP,iBAAiB,GAClB,EAAE,eAAe,GAAG,OAAO,CAAC,SAAS,CAAC,CA2CtC"}
1
+ {"version":3,"file":"assemble.d.ts","sourceRoot":"","sources":["../../build/assemble.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAeH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAC/C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAuB/C,MAAM,WAAW,eAAe;IAC9B,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,QAAQ,EAAE,CAAC;IAClB,8EAA8E;IAC9E,KAAK,EAAE,UAAU,EAAE,CAAC;CACrB;AAED,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,4EAA4E;IAC5E,QAAQ,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;CACvB;AAED,wBAAsB,QAAQ,CAAC,EAC7B,UAAU,EACV,KAAK,EACL,KAAK,GACN,EAAE,eAAe,GAAG,OAAO,CAAC,SAAS,CAAC,CA2CtC"}
@@ -9,23 +9,42 @@
9
9
  * expanded host is wrapped in `<template id="page-<name>">` and, together
10
10
  * with the chrome's own expanded body content, spliced back into the
11
11
  * chrome's `<body>`. The result is the one document a server has to serve
12
- * for every route — see docs/getting-started.md and docs/application-chrome.md.
12
+ * for every route — see docs/tutorials/000-getting-started.md and
13
+ * docs/reference/builder.md.
13
14
  *
14
15
  * Every path here is a parameter, resolved once by build/locations.ts — this
15
16
  * module never reconstructs one of its own, and never reads a directory
16
17
  * looking for pages or widgets; it is handed exactly which files those are.
17
18
  */
18
19
  import { readFile } from "node:fs/promises";
19
- import { addDefinitions, customElementTags, expand, loadDefinitions, } from "./expand.js";
20
+ import { JSDOM } from "jsdom";
21
+ import { addDefinitions, customElementTags, expand, } from "./expand.js";
20
22
  import { formatHtml } from "./format.js";
21
- import { PAGE_TEMPLATE_PREFIX } from "../core/lb-constants.js";
22
- export async function assemble({ chromeFile, pages, widgets, builtinWidgetsDir, }) {
23
- const widgetHtmlFiles = new Map();
24
- for (const [tag, source] of widgets) {
25
- if (source.html)
26
- widgetHtmlFiles.set(tag, source.html);
23
+ import { ATTR_UNKNOWN_PAGE, PAGE_TEMPLATE_PREFIX, } from "../core/lb-constants.js";
24
+ /**
25
+ * The hub calls showModal() on whatever carries lb-unknown-page (hub/lb-hub.ts),
26
+ * so a chrome that puts the attribute on anything else would fail at
27
+ * runtime, on whichever page happens to 404 first, rather than at build
28
+ * time. The attribute is optional; only a wrong element on it is an error.
29
+ */
30
+ function checkUnknownPageDialog(chromeFile, expandedBody) {
31
+ const fragment = JSDOM.fragment(expandedBody);
32
+ const el = fragment.querySelector(`[${ATTR_UNKNOWN_PAGE}]`);
33
+ if (!el)
34
+ return;
35
+ if (el.tagName !== "DIALOG") {
36
+ throw new Error(`assemble: '${chromeFile}' declares ${ATTR_UNKNOWN_PAGE} on ` +
37
+ `<${el.tagName.toLowerCase()}>, must be a <dialog>`);
38
+ }
39
+ }
40
+ export async function assemble({ chromeFile, pages, scans, }) {
41
+ // One layer per origin, in order, so a later origin's definition overrides
42
+ // an earlier one's for the same tag. Each layer is checked for cycles as it
43
+ // lands, against everything merged so far.
44
+ let defs = {};
45
+ for (const scan of scans) {
46
+ defs = await addDefinitions(defs, scan.definitions);
27
47
  }
28
- const defs = await addDefinitions(await loadDefinitions([builtinWidgetsDir]), widgetHtmlFiles);
29
48
  const templates = [];
30
49
  for (const { name, file } of pages) {
31
50
  const authored = await readFile(file, "utf-8");
@@ -45,6 +64,7 @@ export async function assemble({ chromeFile, pages, widgets, builtinWidgetsDir,
45
64
  }
46
65
  const [whole, bodyAttrs, bodyContent] = body;
47
66
  const expandedBody = expand(bodyContent, defs);
67
+ checkUnknownPageDialog(chromeFile, expandedBody);
48
68
  const withPages = chromeSource.replace(whole, () => `<body${bodyAttrs}>${expandedBody}\n${templates.join("\n")}\n</body>`);
49
69
  return {
50
70
  html: await formatHtml(withPages),
@@ -2,26 +2,34 @@
2
2
  /**
3
3
  * loadbare-app-build — assemble a chrome file and its pages into one HTML
4
4
  * document, and bundle the client script for exactly the custom elements
5
- * that document uses. See docs/getting-started.md.
5
+ * that document uses. See docs/tutorials/000-getting-started.md.
6
6
  *
7
- * loadbare-app-build [--src src] [--out dist] [--watch]
7
+ * loadbare-app-build [--src src] [--out dist] [--watch] [--minify]
8
8
  *
9
- * `--src` names the tree scanned for everything: the one `*.chrome.html`
10
- * file, every `*.page.html` page, every other `.html`/`.ts` as a widget,
11
- * `*.hooks.ts`/`*.queries.ts` pairs, and `elements.ts` if there is one — see
12
- * build/locations.ts for how a file's name says what it is. The assembled
13
- * document is written to `<out>/app.html`; the client bundle to
14
- * `<out>/client.js`, by way of a generated `<out>/client-entry.ts` — see
9
+ * `--src` names the application's own tree: the one `chrome.html` file,
10
+ * every `*.page.html` page, its `*.hooks.ts`/`*.queries.ts` pairs, and
11
+ * `imports.ts` if there is one — see build/locations.ts. It is also the last
12
+ * origin in the cascade that supplies widget definitions, widget scripts and
13
+ * stylesheets; this package's own widgets come first and each package named
14
+ * in `imports.ts` in between, all scanned the same way — see
15
+ * build/origins.ts.
16
+ *
17
+ * The assembled document is written to `<out>/app.html`; the client bundle
18
+ * to `<out>/client.js`, by way of a generated `<out>/client-entry.ts` — see
15
19
  * build/elements.ts for how a tag is resolved to the script that registers
16
20
  * it. When any page has a hooks or queries file, the hub registry that used
17
21
  * to be hand-maintained is written to `<out>/pages.ts` — see build/pages.ts.
18
- * Every `.css` file anywhere under `<src>` is concatenated, in the order
19
- * described in build/locations.ts, and written to `<out>/app.css` — see
20
- * build/styles.ts.
22
+ * Every origin's stylesheets are concatenated in that same cascade order and
23
+ * written to `<out>/app.css` — see build/styles.ts.
24
+ *
25
+ * `--minify` minifies both output files: esbuild's own minifier for
26
+ * `client.js`, as part of the same bundling pass, and esbuild's CSS
27
+ * transform for `app.css`, as a pass over the concatenation above. `app.html`
28
+ * is never minified — it is markup an application may still want to read.
21
29
  *
22
30
  * Without `--watch`, this runs once and exits. With it, every `.html`,
23
31
  * `.ts` and `.css` file under `<src>` is watched — a page, a widget's
24
- * markup, a widget's class, a stylesheet, and `elements.ts` all feed the
32
+ * markup, a widget's class, a stylesheet, and `imports.ts` all feed the
25
33
  * same build — and a rebuild that throws is reported without stopping the
26
34
  * watch. There is no dev server and no browser reload here, only the files
27
35
  * on disk staying current for the next manual refresh.
@@ -1 +1 @@
1
- {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../../build/cli.ts"],"names":[],"mappings":";AACA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG"}
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../../build/cli.ts"],"names":[],"mappings":";AACA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG"}
package/dist/build/cli.js CHANGED
@@ -2,26 +2,34 @@
2
2
  /**
3
3
  * loadbare-app-build — assemble a chrome file and its pages into one HTML
4
4
  * document, and bundle the client script for exactly the custom elements
5
- * that document uses. See docs/getting-started.md.
5
+ * that document uses. See docs/tutorials/000-getting-started.md.
6
6
  *
7
- * loadbare-app-build [--src src] [--out dist] [--watch]
7
+ * loadbare-app-build [--src src] [--out dist] [--watch] [--minify]
8
8
  *
9
- * `--src` names the tree scanned for everything: the one `*.chrome.html`
10
- * file, every `*.page.html` page, every other `.html`/`.ts` as a widget,
11
- * `*.hooks.ts`/`*.queries.ts` pairs, and `elements.ts` if there is one — see
12
- * build/locations.ts for how a file's name says what it is. The assembled
13
- * document is written to `<out>/app.html`; the client bundle to
14
- * `<out>/client.js`, by way of a generated `<out>/client-entry.ts` — see
9
+ * `--src` names the application's own tree: the one `chrome.html` file,
10
+ * every `*.page.html` page, its `*.hooks.ts`/`*.queries.ts` pairs, and
11
+ * `imports.ts` if there is one — see build/locations.ts. It is also the last
12
+ * origin in the cascade that supplies widget definitions, widget scripts and
13
+ * stylesheets; this package's own widgets come first and each package named
14
+ * in `imports.ts` in between, all scanned the same way — see
15
+ * build/origins.ts.
16
+ *
17
+ * The assembled document is written to `<out>/app.html`; the client bundle
18
+ * to `<out>/client.js`, by way of a generated `<out>/client-entry.ts` — see
15
19
  * build/elements.ts for how a tag is resolved to the script that registers
16
20
  * it. When any page has a hooks or queries file, the hub registry that used
17
21
  * to be hand-maintained is written to `<out>/pages.ts` — see build/pages.ts.
18
- * Every `.css` file anywhere under `<src>` is concatenated, in the order
19
- * described in build/locations.ts, and written to `<out>/app.css` — see
20
- * build/styles.ts.
22
+ * Every origin's stylesheets are concatenated in that same cascade order and
23
+ * written to `<out>/app.css` — see build/styles.ts.
24
+ *
25
+ * `--minify` minifies both output files: esbuild's own minifier for
26
+ * `client.js`, as part of the same bundling pass, and esbuild's CSS
27
+ * transform for `app.css`, as a pass over the concatenation above. `app.html`
28
+ * is never minified — it is markup an application may still want to read.
21
29
  *
22
30
  * Without `--watch`, this runs once and exits. With it, every `.html`,
23
31
  * `.ts` and `.css` file under `<src>` is watched — a page, a widget's
24
- * markup, a widget's class, a stylesheet, and `elements.ts` all feed the
32
+ * markup, a widget's class, a stylesheet, and `imports.ts` all feed the
25
33
  * same build — and a rebuild that throws is reported without stopping the
26
34
  * watch. There is no dev server and no browser reload here, only the files
27
35
  * on disk staying current for the next manual refresh.
@@ -33,6 +41,7 @@ import * as esbuild from "esbuild";
33
41
  import { assemble } from "./assemble.js";
34
42
  import { clientEntrySource, resolveElements } from "./elements.js";
35
43
  import { resolveLocations } from "./locations.js";
44
+ import { cssFrom, scanAll } from "./origins.js";
36
45
  import { pagesSource } from "./pages.js";
37
46
  import { stylesSource } from "./styles.js";
38
47
  const { values } = parseArgs({
@@ -40,6 +49,7 @@ const { values } = parseArgs({
40
49
  src: { type: "string", default: "src" },
41
50
  out: { type: "string", default: "dist" },
42
51
  watch: { type: "boolean", default: false },
52
+ minify: { type: "boolean", default: false },
43
53
  },
44
54
  });
45
55
  const srcDir = values.src;
@@ -48,16 +58,18 @@ async function build() {
48
58
  const outFile = path.join(locations.outDir, "app.html");
49
59
  const entryFile = path.join(locations.outDir, "client-entry.ts");
50
60
  const clientFile = path.join(locations.outDir, "client.js");
51
- const { html, usedTags } = await assemble(locations);
61
+ const scans = await scanAll(locations, srcDir, locations.importsFile);
62
+ const { html, usedTags } = await assemble({ ...locations, scans });
52
63
  await mkdir(locations.outDir, { recursive: true });
53
64
  await writeFile(outFile, html, "utf-8");
54
- const { resolved } = await resolveElements({ used: usedTags, ...locations });
65
+ const { resolved } = resolveElements({ used: usedTags, scans });
55
66
  await writeFile(entryFile, clientEntrySource(resolved, locations.outDir), "utf-8");
56
67
  await esbuild.build({
57
68
  entryPoints: [entryFile],
58
69
  bundle: true,
59
70
  format: "iife",
60
71
  outfile: clientFile,
72
+ minify: values.minify,
61
73
  });
62
74
  const generatedPages = pagesSource(locations.pages, locations.pageSources, locations.outDir, locations.packageName);
63
75
  let pagesMessage = "";
@@ -66,10 +78,16 @@ async function build() {
66
78
  await writeFile(pagesFile, generatedPages, "utf-8");
67
79
  pagesMessage = ` and ${pagesFile}`;
68
80
  }
69
- const generatedCss = await stylesSource(locations.cssFiles);
81
+ const concatenatedCss = await stylesSource(cssFrom(scans));
70
82
  let cssMessage = "";
71
- if (generatedCss) {
83
+ if (concatenatedCss) {
72
84
  const cssFile = path.join(locations.outDir, "app.css");
85
+ const generatedCss = values.minify
86
+ ? (await esbuild.transform(concatenatedCss, {
87
+ loader: "css",
88
+ minify: true,
89
+ })).code
90
+ : concatenatedCss;
73
91
  await writeFile(cssFile, generatedCss, "utf-8");
74
92
  cssMessage = ` and ${cssFile}`;
75
93
  }
@@ -3,28 +3,23 @@
3
3
  * and finding the script to import for it when it can.
4
4
  *
5
5
  * A used tag is valid if it has a script, a definition, or both:
6
- * - a script registers the class that upgrades it. It can come from a
7
- * built-in source (this package's own widgets/ or hub/ — hub/ is where
8
- * lb-hub itself lives, discovered exactly the same way as any built-in
9
- * widget, no special case for it here), an application's own — found
10
- * anywhere in its source tree by build/locations.ts, not confined to a
11
- * directory — or a third party's, declared by tag in `elements.ts`.
12
- * - a definition (an `.html` file, built-in or local) expands the tag's
13
- * content at build time. A widget with only a definition and no script
14
- * is markup only: expansion already gave it everything it will ever
15
- * have, and the tag survives just as an inert wrapper (see expand.ts).
6
+ * - a script registers the class that upgrades it.
7
+ * - a definition (an `.html` file) expands the tag's content at build
8
+ * time. A widget with only a definition and no script is markup only:
9
+ * expansion already gave it everything it will ever have, and the tag
10
+ * survives as an inert wrapper (see expand.ts).
16
11
  *
17
12
  * A tag with neither — no script anywhere and no definition — would do
18
13
  * nothing were it ever to reach the browser, so that is an error.
19
14
  *
20
- * Every directory and file here is a parameter, resolved once by
21
- * build/locations.ts — this module never reconstructs one of its own, and
22
- * the application's own widgets are handed over already found, not scanned
23
- * again here.
15
+ * Both halves are looked for in the same ordered origins, and the later
16
+ * origin wins: an application's own script overrides one from a package it
17
+ * imported, which overrides a built-in. Two files claiming one tag *within*
18
+ * one origin is the error case, and build/origins.ts raises it while
19
+ * scanning. Nothing is scanned here.
24
20
  */
25
- import { type WidgetSource } from "./locations.js";
26
- /** The shape of an `elements.ts` module's default export. */
27
- export type ElementManifest = Record<string, string>;
21
+ import type { OriginScan } from "./origins.js";
22
+ export { loadElementManifest, type ElementManifest } from "./origins.js";
28
23
  /** A resolved tag: a bare specifier to import, or a local file to import by relative path. */
29
24
  export type Resolved = {
30
25
  kind: "specifier";
@@ -33,24 +28,15 @@ export type Resolved = {
33
28
  kind: "file";
34
29
  value: string;
35
30
  };
36
- /**
37
- * `elements.ts`, executed rather than parsed — it runs through the same
38
- * loader as the rest of the build, so it is ordinary TypeScript, not a data
39
- * format with its own rules.
40
- */
41
- export declare function loadElementManifest(file: string): Promise<ElementManifest>;
42
31
  export interface ResolveOptions {
43
32
  used: Set<string>;
44
- widgets: Map<string, WidgetSource>;
45
- elementsFile?: string;
46
- builtinWidgetsDir: string;
47
- builtinHubDir: string;
48
- packageName: string;
33
+ /** Every origin, already scanned, in cascade order — see build/origins.ts. */
34
+ scans: OriginScan[];
49
35
  }
50
36
  export interface ResolveResult {
51
37
  resolved: Map<string, Resolved>;
52
38
  }
53
- export declare function resolveElements({ used, widgets, elementsFile, builtinWidgetsDir, builtinHubDir, packageName, }: ResolveOptions): Promise<ResolveResult>;
39
+ export declare function resolveElements({ used, scans, }: ResolveOptions): ResolveResult;
54
40
  /**
55
41
  * The generated client entry point — every custom element the assembled
56
42
  * document actually uses, and nothing else. Written to `outDir` so a
@@ -1 +1 @@
1
- {"version":3,"file":"elements.d.ts","sourceRoot":"","sources":["../../build/elements.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAKH,OAAO,EAAsB,KAAK,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAEvE,6DAA6D;AAC7D,MAAM,MAAM,eAAe,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;AAErD,8FAA8F;AAC9F,MAAM,MAAM,QAAQ,GAClB;IAAE,IAAI,EAAE,WAAW,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAmCzE;;;;GAIG;AACH,wBAAsB,mBAAmB,CACvC,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,eAAe,CAAC,CA4B1B;AAED,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IAClB,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;IACnC,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;CACjC;AAED,wBAAsB,eAAe,CAAC,EACpC,IAAI,EACJ,OAAO,EACP,YAAY,EACZ,iBAAiB,EACjB,aAAa,EACb,WAAW,GACZ,EAAE,cAAc,GAAG,OAAO,CAAC,aAAa,CAAC,CAoEzC;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAC/B,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,QAAQ,CAAC,EAC/B,MAAM,EAAE,MAAM,GACb,MAAM,CAcR"}
1
+ {"version":3,"file":"elements.d.ts","sourceRoot":"","sources":["../../build/elements.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAGH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAE/C,OAAO,EAAE,mBAAmB,EAAE,KAAK,eAAe,EAAE,MAAM,cAAc,CAAC;AAEzE,8FAA8F;AAC9F,MAAM,MAAM,QAAQ,GAClB;IAAE,IAAI,EAAE,WAAW,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAEzE,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IAClB,8EAA8E;IAC9E,KAAK,EAAE,UAAU,EAAE,CAAC;CACrB;AAED,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;CACjC;AAED,wBAAgB,eAAe,CAAC,EAC9B,IAAI,EACJ,KAAK,GACN,EAAE,cAAc,GAAG,aAAa,CAiChC;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAC/B,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,QAAQ,CAAC,EAC/B,MAAM,EAAE,MAAM,GACb,MAAM,CAcR"}