@loadbare/app 0.4.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 (171) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +95 -0
  3. package/dist/build/assemble.d.ts +31 -0
  4. package/dist/build/assemble.d.ts.map +1 -0
  5. package/dist/build/assemble.js +53 -0
  6. package/dist/build/cli.d.ts +30 -0
  7. package/dist/build/cli.d.ts.map +1 -0
  8. package/dist/build/cli.js +107 -0
  9. package/dist/build/elements.d.ts +61 -0
  10. package/dist/build/elements.d.ts.map +1 -0
  11. package/dist/build/elements.js +158 -0
  12. package/dist/build/expand.d.ts +45 -0
  13. package/dist/build/expand.d.ts.map +1 -0
  14. package/dist/build/expand.js +386 -0
  15. package/dist/build/format.d.ts +28 -0
  16. package/dist/build/format.d.ts.map +1 -0
  17. package/dist/build/format.js +42 -0
  18. package/dist/build/locations.d.ts +87 -0
  19. package/dist/build/locations.d.ts.map +1 -0
  20. package/dist/build/locations.js +173 -0
  21. package/dist/build/package-root.d.ts +9 -0
  22. package/dist/build/package-root.d.ts.map +1 -0
  23. package/dist/build/package-root.js +24 -0
  24. package/dist/build/pages.d.ts +25 -0
  25. package/dist/build/pages.d.ts.map +1 -0
  26. package/dist/build/pages.js +54 -0
  27. package/dist/build/styles.d.ts +13 -0
  28. package/dist/build/styles.d.ts.map +1 -0
  29. package/dist/build/styles.js +18 -0
  30. package/dist/client.js +522 -0
  31. package/dist/core/lb-constants.d.ts +23 -0
  32. package/dist/core/lb-constants.d.ts.map +1 -0
  33. package/dist/core/lb-constants.js +95 -0
  34. package/dist/core/lb-types.d.ts +88 -0
  35. package/dist/core/lb-types.d.ts.map +1 -0
  36. package/dist/core/lb-types.js +5 -0
  37. package/dist/demo-static/src/widgets/app-box.d.ts +15 -0
  38. package/dist/demo-static/src/widgets/app-box.d.ts.map +1 -0
  39. package/dist/demo-static/src/widgets/app-box.js +19 -0
  40. package/dist/hub/lb-apply.d.ts +13 -0
  41. package/dist/hub/lb-apply.d.ts.map +1 -0
  42. package/dist/hub/lb-apply.js +77 -0
  43. package/dist/hub/lb-hub.d.ts +2 -0
  44. package/dist/hub/lb-hub.d.ts.map +1 -0
  45. package/dist/hub/lb-hub.js +242 -0
  46. package/dist/hub/lb-rows.d.ts +18 -0
  47. package/dist/hub/lb-rows.d.ts.map +1 -0
  48. package/dist/hub/lb-rows.js +106 -0
  49. package/dist/server/lb-express.d.ts +28 -0
  50. package/dist/server/lb-express.d.ts.map +1 -0
  51. package/dist/server/lb-express.js +77 -0
  52. package/dist/server/lb-server.d.ts +174 -0
  53. package/dist/server/lb-server.d.ts.map +1 -0
  54. package/dist/server/lb-server.js +79 -0
  55. package/dist/tests/assemble.test.d.ts +8 -0
  56. package/dist/tests/assemble.test.d.ts.map +1 -0
  57. package/dist/tests/assemble.test.js +51 -0
  58. package/dist/tests/elements.test.d.ts +8 -0
  59. package/dist/tests/elements.test.d.ts.map +1 -0
  60. package/dist/tests/elements.test.js +111 -0
  61. package/dist/tests/expand.test.d.ts +10 -0
  62. package/dist/tests/expand.test.d.ts.map +1 -0
  63. package/dist/tests/expand.test.js +226 -0
  64. package/dist/tests/fixtures/elements/collision/elements.d.ts +5 -0
  65. package/dist/tests/fixtures/elements/collision/elements.d.ts.map +1 -0
  66. package/dist/tests/fixtures/elements/collision/elements.js +3 -0
  67. package/dist/tests/fixtures/elements/collision/widgets/acme-widget.d.ts +2 -0
  68. package/dist/tests/fixtures/elements/collision/widgets/acme-widget.d.ts.map +1 -0
  69. package/dist/tests/fixtures/elements/collision/widgets/acme-widget.js +1 -0
  70. package/dist/tests/fixtures/elements/local/widgets/app-box.d.ts +2 -0
  71. package/dist/tests/fixtures/elements/local/widgets/app-box.d.ts.map +1 -0
  72. package/dist/tests/fixtures/elements/local/widgets/app-box.js +1 -0
  73. package/dist/tests/fixtures/elements/manifest/elements.d.ts +5 -0
  74. package/dist/tests/fixtures/elements/manifest/elements.d.ts.map +1 -0
  75. package/dist/tests/fixtures/elements/manifest/elements.js +3 -0
  76. package/dist/tests/fixtures/elements/manifest-bad-tag/elements.d.ts +5 -0
  77. package/dist/tests/fixtures/elements/manifest-bad-tag/elements.d.ts.map +1 -0
  78. package/dist/tests/fixtures/elements/manifest-bad-tag/elements.js +3 -0
  79. package/dist/tests/fixtures/elements/manifest-bad-value/elements.d.ts +5 -0
  80. package/dist/tests/fixtures/elements/manifest-bad-value/elements.d.ts.map +1 -0
  81. package/dist/tests/fixtures/elements/manifest-bad-value/elements.js +3 -0
  82. package/dist/tests/golden.test.d.ts +19 -0
  83. package/dist/tests/golden.test.d.ts.map +1 -0
  84. package/dist/tests/golden.test.js +60 -0
  85. package/dist/tests/helpers/console.d.ts +20 -0
  86. package/dist/tests/helpers/console.d.ts.map +1 -0
  87. package/dist/tests/helpers/console.js +28 -0
  88. package/dist/tests/helpers/dom.d.ts +18 -0
  89. package/dist/tests/helpers/dom.d.ts.map +1 -0
  90. package/dist/tests/helpers/dom.js +22 -0
  91. package/dist/tests/helpers/window.d.ts +43 -0
  92. package/dist/tests/helpers/window.d.ts.map +1 -0
  93. package/dist/tests/helpers/window.js +78 -0
  94. package/dist/tests/lb-apply.test.d.ts +8 -0
  95. package/dist/tests/lb-apply.test.d.ts.map +1 -0
  96. package/dist/tests/lb-apply.test.js +153 -0
  97. package/dist/tests/lb-express.test.d.ts +14 -0
  98. package/dist/tests/lb-express.test.d.ts.map +1 -0
  99. package/dist/tests/lb-express.test.js +238 -0
  100. package/dist/tests/lb-input.test.d.ts +9 -0
  101. package/dist/tests/lb-input.test.d.ts.map +1 -0
  102. package/dist/tests/lb-input.test.js +78 -0
  103. package/dist/tests/lb-list.test.d.ts +12 -0
  104. package/dist/tests/lb-list.test.d.ts.map +1 -0
  105. package/dist/tests/lb-list.test.js +44 -0
  106. package/dist/tests/lb-options.test.d.ts +10 -0
  107. package/dist/tests/lb-options.test.d.ts.map +1 -0
  108. package/dist/tests/lb-options.test.js +121 -0
  109. package/dist/tests/lb-picker.test.d.ts +14 -0
  110. package/dist/tests/lb-picker.test.d.ts.map +1 -0
  111. package/dist/tests/lb-picker.test.js +59 -0
  112. package/dist/tests/lb-rows.test.d.ts +12 -0
  113. package/dist/tests/lb-rows.test.d.ts.map +1 -0
  114. package/dist/tests/lb-rows.test.js +336 -0
  115. package/dist/tests/lb-select.test.d.ts +9 -0
  116. package/dist/tests/lb-select.test.d.ts.map +1 -0
  117. package/dist/tests/lb-select.test.js +71 -0
  118. package/dist/tests/lb-server.test.d.ts +9 -0
  119. package/dist/tests/lb-server.test.d.ts.map +1 -0
  120. package/dist/tests/lb-server.test.js +495 -0
  121. package/dist/tests/lb-table.test.d.ts +15 -0
  122. package/dist/tests/lb-table.test.d.ts.map +1 -0
  123. package/dist/tests/lb-table.test.js +205 -0
  124. package/dist/tests/pages.test.d.ts +6 -0
  125. package/dist/tests/pages.test.d.ts.map +1 -0
  126. package/dist/tests/pages.test.js +98 -0
  127. package/dist/tests/styles.test.d.ts +7 -0
  128. package/dist/tests/styles.test.d.ts.map +1 -0
  129. package/dist/tests/styles.test.js +73 -0
  130. package/dist/widgets/index.d.ts +7 -0
  131. package/dist/widgets/index.d.ts.map +1 -0
  132. package/dist/widgets/index.js +6 -0
  133. package/dist/widgets/lb-input.d.ts +2 -0
  134. package/dist/widgets/lb-input.d.ts.map +1 -0
  135. package/dist/widgets/lb-input.js +48 -0
  136. package/dist/widgets/lb-list.d.ts +2 -0
  137. package/dist/widgets/lb-list.d.ts.map +1 -0
  138. package/dist/widgets/lb-list.js +17 -0
  139. package/dist/widgets/lb-options.d.ts +26 -0
  140. package/dist/widgets/lb-options.d.ts.map +1 -0
  141. package/dist/widgets/lb-options.js +72 -0
  142. package/dist/widgets/lb-picker.d.ts +2 -0
  143. package/dist/widgets/lb-picker.d.ts.map +1 -0
  144. package/dist/widgets/lb-picker.js +25 -0
  145. package/dist/widgets/lb-select.d.ts +2 -0
  146. package/dist/widgets/lb-select.d.ts.map +1 -0
  147. package/dist/widgets/lb-select.js +43 -0
  148. package/dist/widgets/lb-table.d.ts +2 -0
  149. package/dist/widgets/lb-table.d.ts.map +1 -0
  150. package/dist/widgets/lb-table.js +113 -0
  151. package/docs/application-chrome.md +36 -0
  152. package/docs/building-html-pages.md +130 -0
  153. package/docs/getting-started.md +120 -0
  154. package/docs/guide.md +1164 -0
  155. package/docs/hosting.md +218 -0
  156. package/docs/latent-risks.md +20 -0
  157. package/docs/theory.md +226 -0
  158. package/package.json +85 -0
  159. package/widgets/index.ts +6 -0
  160. package/widgets/lb-input.html +1 -0
  161. package/widgets/lb-input.ts +64 -0
  162. package/widgets/lb-list.html +1 -0
  163. package/widgets/lb-list.ts +21 -0
  164. package/widgets/lb-options.html +4 -0
  165. package/widgets/lb-options.ts +88 -0
  166. package/widgets/lb-picker.html +7 -0
  167. package/widgets/lb-picker.ts +27 -0
  168. package/widgets/lb-select.html +4 -0
  169. package/widgets/lb-select.ts +55 -0
  170. package/widgets/lb-table.html +8 -0
  171. package/widgets/lb-table.ts +126 -0
@@ -0,0 +1,87 @@
1
+ /**
2
+ * Finding every file a build needs by what it's named, not by which
3
+ * directory it happens to sit in.
4
+ *
5
+ * A page and a widget definition can be byte-for-byte identical markup —
6
+ * the only thing that says which one a file is, is extrinsic to its
7
+ * content. `pages/` vs. `widgets/` used to carry that meaning; here a
8
+ * filename suffix does instead: `*.chrome.html` is the one chrome file,
9
+ * `*.page.html` is a page, and anything else `.html` is a widget
10
+ * definition. A `.ts` file shaped like a custom element tag is a widget
11
+ * script, found the same way regardless of where it lives. Both halves of
12
+ * one widget — its markup and its class — are free to sit side by side or
13
+ * apart; nothing here requires a `widgets/` directory to exist at all.
14
+ *
15
+ * A page's server half is named the same way: `*.hooks.ts` and
16
+ * `*.queries.ts` pair with a `*.page.html` by sharing its basename, and are
17
+ * free to sit anywhere too. Either may be absent — a page with no data needs
18
+ * neither — but one with no matching `*.page.html` is a naming mistake and
19
+ * is rejected below.
20
+ *
21
+ * A `.css` file is not paired with anything — it is not a widget's, a
22
+ * page's, or the chrome's, because build/styles.ts does not distinguish
23
+ * them. Every `.css` file anywhere under `src` is concatenated, in an order
24
+ * controlled entirely by the author: sorted by filename first, so a global
25
+ * stylesheet sorts to the front by naming itself `00-reset.css` or similar,
26
+ * regardless of which directory it lives in.
27
+ *
28
+ * `--src` names the one tree scanned this way; `--out` names where the
29
+ * build lands. Those, plus this package's own root (for its built-in
30
+ * widgets and hub/), are the only locations this module resolves — every
31
+ * other module takes the result as a plain parameter and never
32
+ * reconstructs one of its own.
33
+ */
34
+ /** Lowercase, hyphenated — the one shape every custom element tag has, and the one native elements never do. */
35
+ export declare const CUSTOM_ELEMENT_TAG: RegExp;
36
+ export interface PageFile {
37
+ name: string;
38
+ file: string;
39
+ }
40
+ export interface WidgetSource {
41
+ /** Absolute path to the `.html` definition, if this tag has one. */
42
+ html?: string;
43
+ /** Absolute path, without extension, to the `.ts` script, if this tag has one. */
44
+ script?: string;
45
+ }
46
+ export interface PageSource {
47
+ /** Absolute path, without extension, to the `.hooks.ts` module, if this page has one. */
48
+ hooks?: string;
49
+ /** Absolute path, without extension, to the `.queries.ts` module, if this page has one. */
50
+ queries?: string;
51
+ }
52
+ interface Discovered {
53
+ chromeCandidates: string[];
54
+ pages: PageFile[];
55
+ pageSources: Map<string, PageSource>;
56
+ widgets: Map<string, WidgetSource>;
57
+ elementsCandidates: string[];
58
+ cssFiles: string[];
59
+ }
60
+ /** The raw walk, with no uniqueness checks — usable on its own by anything that does not need a chrome file. */
61
+ export declare function discoverSrc(src: string): Promise<Discovered>;
62
+ export interface PackageInfo {
63
+ /** This package's own widgets/ — the built-in widget definitions and classes. */
64
+ builtinWidgetsDir: string;
65
+ /** This package's own hub/ — where lb-hub's script lives, found the same way as any built-in widget. */
66
+ builtinHubDir: string;
67
+ /** This package's own name, e.g. "@loadbare/app" — for naming a built-in's import specifier. */
68
+ packageName: string;
69
+ }
70
+ export declare function packageInfo(): Promise<PackageInfo>;
71
+ export interface LocationArgs {
72
+ /** The application's source tree, e.g. "src". Scanned as a whole — see this file's header. */
73
+ src: string;
74
+ out: string;
75
+ }
76
+ export interface Locations extends PackageInfo {
77
+ chromeFile: string;
78
+ pages: PageFile[];
79
+ pageSources: Map<string, PageSource>;
80
+ widgets: Map<string, WidgetSource>;
81
+ elementsFile?: string;
82
+ cssFiles: string[];
83
+ outDir: string;
84
+ }
85
+ export declare function resolveLocations(args: LocationArgs): Promise<Locations>;
86
+ export {};
87
+ //# sourceMappingURL=locations.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"locations.d.ts","sourceRoot":"","sources":["../../build/locations.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AAQH,gHAAgH;AAChH,eAAO,MAAM,kBAAkB,QAAkC,CAAC;AASlE,MAAM,WAAW,QAAQ;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,YAAY;IAC3B,oEAAoE;IACpE,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,kFAAkF;IAClF,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,UAAU;IACzB,yFAAyF;IACzF,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,2FAA2F;IAC3F,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,UAAU,UAAU;IAClB,gBAAgB,EAAE,MAAM,EAAE,CAAC;IAC3B,KAAK,EAAE,QAAQ,EAAE,CAAC;IAClB,WAAW,EAAE,GAAG,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;IACrC,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;IACnC,kBAAkB,EAAE,MAAM,EAAE,CAAC;IAC7B,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AAED,gHAAgH;AAChH,wBAAsB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,CA+ElE;AAED,MAAM,WAAW,WAAW;IAC1B,iFAAiF;IACjF,iBAAiB,EAAE,MAAM,CAAC;IAC1B,wGAAwG;IACxG,aAAa,EAAE,MAAM,CAAC;IACtB,gGAAgG;IAChG,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,wBAAsB,WAAW,IAAI,OAAO,CAAC,WAAW,CAAC,CAiBxD;AAED,MAAM,WAAW,YAAY;IAC3B,8FAA8F;IAC9F,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;CACb;AAED,MAAM,WAAW,SAAU,SAAQ,WAAW;IAC5C,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,QAAQ,EAAE,CAAC;IAClB,WAAW,EAAE,GAAG,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;IACrC,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;IACnC,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,wBAAsB,gBAAgB,CAAC,IAAI,EAAE,YAAY,GAAG,OAAO,CAAC,SAAS,CAAC,CAoD7E"}
@@ -0,0 +1,173 @@
1
+ /**
2
+ * Finding every file a build needs by what it's named, not by which
3
+ * directory it happens to sit in.
4
+ *
5
+ * A page and a widget definition can be byte-for-byte identical markup —
6
+ * the only thing that says which one a file is, is extrinsic to its
7
+ * content. `pages/` vs. `widgets/` used to carry that meaning; here a
8
+ * filename suffix does instead: `*.chrome.html` is the one chrome file,
9
+ * `*.page.html` is a page, and anything else `.html` is a widget
10
+ * definition. A `.ts` file shaped like a custom element tag is a widget
11
+ * script, found the same way regardless of where it lives. Both halves of
12
+ * one widget — its markup and its class — are free to sit side by side or
13
+ * apart; nothing here requires a `widgets/` directory to exist at all.
14
+ *
15
+ * A page's server half is named the same way: `*.hooks.ts` and
16
+ * `*.queries.ts` pair with a `*.page.html` by sharing its basename, and are
17
+ * free to sit anywhere too. Either may be absent — a page with no data needs
18
+ * neither — but one with no matching `*.page.html` is a naming mistake and
19
+ * is rejected below.
20
+ *
21
+ * A `.css` file is not paired with anything — it is not a widget's, a
22
+ * page's, or the chrome's, because build/styles.ts does not distinguish
23
+ * them. Every `.css` file anywhere under `src` is concatenated, in an order
24
+ * controlled entirely by the author: sorted by filename first, so a global
25
+ * stylesheet sorts to the front by naming itself `00-reset.css` or similar,
26
+ * regardless of which directory it lives in.
27
+ *
28
+ * `--src` names the one tree scanned this way; `--out` names where the
29
+ * build lands. Those, plus this package's own root (for its built-in
30
+ * widgets and hub/), are the only locations this module resolves — every
31
+ * other module takes the result as a plain parameter and never
32
+ * reconstructs one of its own.
33
+ */
34
+ import { readdir, readFile } from "node:fs/promises";
35
+ import path from "node:path";
36
+ import { fileURLToPath } from "node:url";
37
+ import { findPackageRoot } from "./package-root.js";
38
+ /** Lowercase, hyphenated — the one shape every custom element tag has, and the one native elements never do. */
39
+ export const CUSTOM_ELEMENT_TAG = /^[a-z][a-z0-9]*(-[a-z0-9]+)+$/;
40
+ const CHROME_SUFFIX = ".chrome.html";
41
+ const PAGE_SUFFIX = ".page.html";
42
+ const HOOKS_SUFFIX = ".hooks.ts";
43
+ const QUERIES_SUFFIX = ".queries.ts";
44
+ const CSS_SUFFIX = ".css";
45
+ const ELEMENTS_FILENAME = "elements.ts";
46
+ /** The raw walk, with no uniqueness checks — usable on its own by anything that does not need a chrome file. */
47
+ export async function discoverSrc(src) {
48
+ const chromeCandidates = [];
49
+ const pages = [];
50
+ const pageSources = new Map();
51
+ const widgets = new Map();
52
+ const elementsCandidates = [];
53
+ const cssFiles = [];
54
+ let entries;
55
+ try {
56
+ entries = await readdir(src, { withFileTypes: true, recursive: true });
57
+ }
58
+ catch {
59
+ entries = [];
60
+ }
61
+ const widget = (tag) => {
62
+ let w = widgets.get(tag);
63
+ if (!w) {
64
+ w = {};
65
+ widgets.set(tag, w);
66
+ }
67
+ return w;
68
+ };
69
+ const pageSource = (name) => {
70
+ let p = pageSources.get(name);
71
+ if (!p) {
72
+ p = {};
73
+ pageSources.set(name, p);
74
+ }
75
+ return p;
76
+ };
77
+ for (const entry of entries) {
78
+ if (!entry.isFile())
79
+ continue;
80
+ const file = path.join(entry.parentPath, entry.name);
81
+ if (entry.name === ELEMENTS_FILENAME) {
82
+ elementsCandidates.push(file);
83
+ }
84
+ else if (entry.name.endsWith(CHROME_SUFFIX)) {
85
+ chromeCandidates.push(file);
86
+ }
87
+ else if (entry.name.endsWith(PAGE_SUFFIX)) {
88
+ pages.push({ name: entry.name.slice(0, -PAGE_SUFFIX.length), file });
89
+ }
90
+ else if (entry.name.endsWith(HOOKS_SUFFIX)) {
91
+ pageSource(entry.name.slice(0, -HOOKS_SUFFIX.length)).hooks = file.slice(0, -".ts".length);
92
+ }
93
+ else if (entry.name.endsWith(QUERIES_SUFFIX)) {
94
+ pageSource(entry.name.slice(0, -QUERIES_SUFFIX.length)).queries =
95
+ file.slice(0, -".ts".length);
96
+ }
97
+ else if (entry.name.endsWith(CSS_SUFFIX)) {
98
+ cssFiles.push(file);
99
+ }
100
+ else if (entry.name.endsWith(".html")) {
101
+ widget(entry.name.slice(0, -".html".length).toLowerCase()).html = file;
102
+ }
103
+ else if (entry.name.endsWith(".ts")) {
104
+ const tag = entry.name.slice(0, -".ts".length).toLowerCase();
105
+ // Without the extension: this becomes an import specifier, and esbuild
106
+ // resolves ".ts" from the bare path the same way it resolves ".js".
107
+ if (CUSTOM_ELEMENT_TAG.test(tag))
108
+ widget(tag).script = file.slice(0, -".ts".length);
109
+ }
110
+ }
111
+ pages.sort((a, b) => a.name.localeCompare(b.name));
112
+ // By filename first, full path only to break a tie — see this file's
113
+ // header. A directory a file happens to sit in never affects the order.
114
+ cssFiles.sort((a, b) => path.basename(a).localeCompare(path.basename(b)) || a.localeCompare(b));
115
+ return {
116
+ chromeCandidates,
117
+ pages,
118
+ pageSources,
119
+ widgets,
120
+ elementsCandidates,
121
+ cssFiles,
122
+ };
123
+ }
124
+ export async function packageInfo() {
125
+ const root = await findPackageRoot(path.dirname(fileURLToPath(import.meta.url)));
126
+ let pkg;
127
+ try {
128
+ pkg = JSON.parse(await readFile(path.join(root, "package.json"), "utf-8"));
129
+ }
130
+ catch (err) {
131
+ throw new Error(`locations: could not read '${root}/package.json': ${err.message}`);
132
+ }
133
+ return {
134
+ builtinWidgetsDir: path.join(root, "widgets"),
135
+ builtinHubDir: path.join(root, "hub"),
136
+ packageName: pkg.name,
137
+ };
138
+ }
139
+ export async function resolveLocations(args) {
140
+ const [info, discovered] = await Promise.all([
141
+ packageInfo(),
142
+ discoverSrc(args.src),
143
+ ]);
144
+ const { chromeCandidates, elementsCandidates, pages, pageSources, widgets, cssFiles, } = discovered;
145
+ if (chromeCandidates.length === 0) {
146
+ throw new Error(`locations: no *${CHROME_SUFFIX} file found under '${args.src}'`);
147
+ }
148
+ if (chromeCandidates.length > 1) {
149
+ throw new Error(`locations: more than one *${CHROME_SUFFIX} file found under '${args.src}': ` +
150
+ chromeCandidates.join(", "));
151
+ }
152
+ if (elementsCandidates.length > 1) {
153
+ throw new Error(`locations: more than one ${ELEMENTS_FILENAME} found under '${args.src}': ` +
154
+ elementsCandidates.join(", "));
155
+ }
156
+ const pageNames = new Set(pages.map((p) => p.name));
157
+ for (const name of pageSources.keys()) {
158
+ if (!pageNames.has(name)) {
159
+ throw new Error(`locations: '${name}${HOOKS_SUFFIX}' or '${name}${QUERIES_SUFFIX}' found under ` +
160
+ `'${args.src}' but there is no '${name}${PAGE_SUFFIX}'`);
161
+ }
162
+ }
163
+ return {
164
+ ...info,
165
+ chromeFile: chromeCandidates[0],
166
+ pages,
167
+ pageSources,
168
+ widgets,
169
+ elementsFile: elementsCandidates[0],
170
+ cssFiles,
171
+ outDir: args.out,
172
+ };
173
+ }
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Walk up from `dir` to the nearest ancestor holding a package.json.
3
+ *
4
+ * Needed because a caller may run from source (build/*.ts) or compiled
5
+ * (dist/build/*.js) — both are the same distance from the package root, but
6
+ * nothing here should assume that stays true.
7
+ */
8
+ export declare function findPackageRoot(dir: string): Promise<string>;
9
+ //# sourceMappingURL=package-root.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"package-root.d.ts","sourceRoot":"","sources":["../../build/package-root.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAKH,wBAAsB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAalE"}
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Walk up from `dir` to the nearest ancestor holding a package.json.
3
+ *
4
+ * Needed because a caller may run from source (build/*.ts) or compiled
5
+ * (dist/build/*.js) — both are the same distance from the package root, but
6
+ * nothing here should assume that stays true.
7
+ */
8
+ import { readFile } from "node:fs/promises";
9
+ import path from "node:path";
10
+ export async function findPackageRoot(dir) {
11
+ for (;;) {
12
+ try {
13
+ await readFile(path.join(dir, "package.json"), "utf-8");
14
+ return dir;
15
+ }
16
+ catch {
17
+ const parent = path.dirname(dir);
18
+ if (parent === dir) {
19
+ throw new Error(`could not find a package.json above '${dir}'`);
20
+ }
21
+ dir = parent;
22
+ }
23
+ }
24
+ }
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Generating the hub's page registry — the server-side counterpart to
3
+ * build/elements.ts's client entry point.
4
+ *
5
+ * `*.hooks.ts` and `*.queries.ts` are ordinary TypeScript, found by
6
+ * build/locations.ts and handed here already paired by page name. What this
7
+ * module writes is the file an application used to hand-maintain: an import
8
+ * of each pair, a `Pages` object naming them by page, and the one
9
+ * `createHub()` call. It is written to `<out>/pages.ts` rather than compiled
10
+ * — the application runs it directly, exactly as it does its own server
11
+ * code, so it never needs a bundler step the way the browser-bound client
12
+ * entry does.
13
+ *
14
+ * Nothing here is emitted for a page with neither a hooks nor a queries
15
+ * file — a purely static page needs no hub entry, and createHub is only
16
+ * called at all when there is at least one.
17
+ */
18
+ import type { PageFile, PageSource } from "./locations.js";
19
+ /**
20
+ * `undefined` when no page has a hooks or queries file — the caller writes
21
+ * nothing in that case, the same way an app with no custom elements gets no
22
+ * client entry beyond the built-ins.
23
+ */
24
+ export declare function pagesSource(pages: PageFile[], pageSources: Map<string, PageSource>, outDir: string, packageName: string): string | undefined;
25
+ //# sourceMappingURL=pages.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pages.d.ts","sourceRoot":"","sources":["../../build/pages.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAGH,OAAO,KAAK,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAY3D;;;;GAIG;AACH,wBAAgB,WAAW,CACzB,KAAK,EAAE,QAAQ,EAAE,EACjB,WAAW,EAAE,GAAG,CAAC,MAAM,EAAE,UAAU,CAAC,EACpC,MAAM,EAAE,MAAM,EACd,WAAW,EAAE,MAAM,GAClB,MAAM,GAAG,SAAS,CAoCpB"}
@@ -0,0 +1,54 @@
1
+ /**
2
+ * Generating the hub's page registry — the server-side counterpart to
3
+ * build/elements.ts's client entry point.
4
+ *
5
+ * `*.hooks.ts` and `*.queries.ts` are ordinary TypeScript, found by
6
+ * build/locations.ts and handed here already paired by page name. What this
7
+ * module writes is the file an application used to hand-maintain: an import
8
+ * of each pair, a `Pages` object naming them by page, and the one
9
+ * `createHub()` call. It is written to `<out>/pages.ts` rather than compiled
10
+ * — the application runs it directly, exactly as it does its own server
11
+ * code, so it never needs a bundler step the way the browser-bound client
12
+ * entry does.
13
+ *
14
+ * Nothing here is emitted for a page with neither a hooks nor a queries
15
+ * file — a purely static page needs no hub entry, and createHub is only
16
+ * called at all when there is at least one.
17
+ */
18
+ import path from "node:path";
19
+ /** A page name may contain anything a filename can; an import identifier may not. */
20
+ function identifier(name, suffix) {
21
+ return `page_${name.replace(/[^a-zA-Z0-9]/g, "_")}_${suffix}`;
22
+ }
23
+ function relativeImport(outDir, file) {
24
+ const rel = path.relative(outDir, file).split(path.sep).join("/");
25
+ return rel.startsWith(".") ? rel : `./${rel}`;
26
+ }
27
+ /**
28
+ * `undefined` when no page has a hooks or queries file — the caller writes
29
+ * nothing in that case, the same way an app with no custom elements gets no
30
+ * client entry beyond the built-ins.
31
+ */
32
+ export function pagesSource(pages, pageSources, outDir, packageName) {
33
+ if (pageSources.size === 0)
34
+ return undefined;
35
+ const imports = [
36
+ `import { createHub } from ${JSON.stringify(`${packageName}/server`)};`,
37
+ ];
38
+ const entries = [];
39
+ for (const { name } of pages) {
40
+ const source = pageSources.get(name);
41
+ if (!source)
42
+ continue;
43
+ const hooksId = source.hooks ? identifier(name, "Hooks") : undefined;
44
+ const queriesId = source.queries ? identifier(name, "Queries") : undefined;
45
+ if (source.hooks) {
46
+ imports.push(`import { hooks as ${hooksId} } from ${JSON.stringify(relativeImport(outDir, source.hooks))};`);
47
+ }
48
+ if (source.queries) {
49
+ imports.push(`import { queries as ${queriesId} } from ${JSON.stringify(relativeImport(outDir, source.queries))};`);
50
+ }
51
+ entries.push(` ${JSON.stringify(name)}: { hooks: ${hooksId ?? "{}"}, queries: ${queriesId ?? "{}"} },`);
52
+ }
53
+ return ([...imports, "", "export const hub = createHub({", ...entries, "});"].join("\n") + "\n");
54
+ }
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Concatenating an application's CSS.
3
+ *
4
+ * There is no widget, page, or chrome concept here — every `.css` file
5
+ * anywhere under `src` is joined, in the order build/locations.ts already
6
+ * sorted them into, with nothing added, removed, or scoped. What ships is
7
+ * exactly the concatenation of what was authored, the same "view source is
8
+ * what ships" rule build/assemble.ts holds for markup. Ordering is the
9
+ * author's own, by filename — see build/locations.ts for the convention.
10
+ */
11
+ /** `undefined` when there is no CSS at all — the caller writes nothing in that case. */
12
+ export declare function stylesSource(cssFiles: string[]): Promise<string | undefined>;
13
+ //# sourceMappingURL=styles.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"styles.d.ts","sourceRoot":"","sources":["../../build/styles.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAIH,wFAAwF;AACxF,wBAAsB,YAAY,CAChC,QAAQ,EAAE,MAAM,EAAE,GACjB,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAM7B"}
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Concatenating an application's CSS.
3
+ *
4
+ * There is no widget, page, or chrome concept here — every `.css` file
5
+ * anywhere under `src` is joined, in the order build/locations.ts already
6
+ * sorted them into, with nothing added, removed, or scoped. What ships is
7
+ * exactly the concatenation of what was authored, the same "view source is
8
+ * what ships" rule build/assemble.ts holds for markup. Ordering is the
9
+ * author's own, by filename — see build/locations.ts for the convention.
10
+ */
11
+ import { readFile } from "node:fs/promises";
12
+ /** `undefined` when there is no CSS at all — the caller writes nothing in that case. */
13
+ export async function stylesSource(cssFiles) {
14
+ if (cssFiles.length === 0)
15
+ return undefined;
16
+ const parts = await Promise.all(cssFiles.map((file) => readFile(file, "utf-8")));
17
+ return parts.join("\n");
18
+ }