@loadbare/app 0.22.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.
@@ -59,4 +59,20 @@ export interface AppWatcher {
59
59
  * never overlap and never fall behind.
60
60
  */
61
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
+ };
62
78
  //# sourceMappingURL=build.d.ts.map
@@ -1 +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"}
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"}
@@ -10,7 +10,7 @@
10
10
  * leaves the last good build in place, so a server reading `dist/` keeps
11
11
  * serving something coherent while the developer fixes the mistake.
12
12
  */
13
- import { watch as watchFiles } from "node:fs";
13
+ import { readdirSync, statSync, watch as watchDir } from "node:fs";
14
14
  import { mkdir, writeFile } from "node:fs/promises";
15
15
  import path from "node:path";
16
16
  import * as esbuild from "esbuild";
@@ -121,8 +121,8 @@ export function watchApp(options = {}) {
121
121
  void drain();
122
122
  return outcome;
123
123
  });
124
- const watcher = watchFiles(src, { recursive: true }, (_event, file) => {
125
- if (closed || !file || !BUILT_FROM.test(file) || inOut(file))
124
+ const tree = watchTree(src, (file) => {
125
+ if (closed || !BUILT_FROM.test(file) || inOut(file))
126
126
  return;
127
127
  pending.add(file.split(path.sep).join("/"));
128
128
  if (running || timer)
@@ -135,14 +135,86 @@ export function watchApp(options = {}) {
135
135
  void drain();
136
136
  });
137
137
  }, SETTLE_MS);
138
- });
138
+ }, inOut);
139
139
  return {
140
140
  ready,
141
141
  close() {
142
142
  closed = true;
143
143
  clearTimeout(timer);
144
- watcher.close();
144
+ tree.close();
145
145
  },
146
146
  };
147
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
+ }
148
220
  //# sourceMappingURL=build.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"build.js","sourceRoot":"","sources":["../../build/build.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EAAE,KAAK,IAAI,UAAU,EAAE,MAAM,SAAS,CAAC;AAC9C,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AACpD,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,KAAK,OAAO,MAAM,SAAS,CAAC;AACnC,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAC7C,OAAO,EACL,oBAAoB,EACpB,iBAAiB,EACjB,eAAe,GAChB,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAClD,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAChD,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AACzC,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAoB3C,wEAAwE;AACxE,MAAM,CAAC,KAAK,UAAU,QAAQ,CAAC,UAAwB,EAAE;IACvD,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,IAAI,KAAK,CAAC;IACjC,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,IAAI,KAAK,CAAC;IACvC,MAAM,SAAS,GAAG,MAAM,gBAAgB,CAAC,EAAE,GAAG,EAAE,GAAG,EAAE,OAAO,CAAC,GAAG,IAAI,MAAM,EAAE,CAAC,CAAC;IAC9E,MAAM,EAAE,MAAM,EAAE,GAAG,SAAS,CAAC;IAC7B,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,iBAAiB,CAAC,CAAC;IAEvD,MAAM,KAAK,GAAG,MAAM,OAAO,CAAC,SAAS,EAAE,GAAG,EAAE,SAAS,CAAC,WAAW,CAAC,CAAC;IACnE,MAAM,KAAK,CAAC,MAAM,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACzC,MAAM,QAAQ,GAAG,MAAM,YAAY,CACjC,SAAS,CAAC,KAAK,EACf,SAAS,CAAC,WAAW,EACrB,MAAM,CACP,CAAC;IACF,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAE,GAAG,MAAM,QAAQ,CAAC,EAAE,GAAG,SAAS,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC,CAAC;IAE7E,MAAM,EAAE,QAAQ,EAAE,GAAG,eAAe,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC,CAAC;IAChE,MAAM,oBAAoB,CAAC,QAAQ,CAAC,CAAC;IACrC,2EAA2E;IAC3E,MAAM,SAAS,CAAC,SAAS,EAAE,iBAAiB,CAAC,QAAQ,EAAE,MAAM,CAAC,EAAE,OAAO,CAAC,CAAC;IACzE,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,KAAK,CAAC;QACjC,WAAW,EAAE,CAAC,SAAS,CAAC;QACxB,MAAM,EAAE,IAAI;QACZ,MAAM,EAAE,MAAM;QACd,OAAO,EAAE,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,WAAW,CAAC;QACvC,MAAM;QACN,KAAK,EAAE,KAAK;QACZ,yEAAyE;QACzE,aAAa,EAAE,OAAO,CAAC,GAAG,EAAE;KAC7B,CAAC,CAAC;IAEH,MAAM,OAAO,GAAG,IAAI,GAAG,EAA+B,CAAC;IACvD,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,UAAU,CAAC,EAAE,IAAI,CAAC,CAAC;IACjD,KAAK,MAAM,IAAI,IAAI,MAAM,CAAC,WAAW;QAAE,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC;IAE7E,MAAM,KAAK,GAAG,WAAW,CACvB,SAAS,CAAC,KAAK,EACf,SAAS,CAAC,WAAW,EACrB,MAAM,EACN,SAAS,CAAC,WAAW,CACtB,CAAC;IACF,IAAI,KAAK;QAAE,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,UAAU,CAAC,EAAE,KAAK,CAAC,CAAC;IAE7D,MAAM,GAAG,GAAG,MAAM,YAAY,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC;IAC/C,IAAI,GAAG,EAAE,CAAC;QACR,MAAM,IAAI,GAAG,MAAM;YACjB,CAAC,CAAC,CAAC,MAAM,OAAO,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,IAAI;YACtE,CAAC,CAAC,GAAG,CAAC;QACR,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,SAAS,CAAC,EAAE,IAAI,CAAC,CAAC;IAClD,CAAC;IAED,KAAK,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,IAAI,OAAO;QAAE,MAAM,SAAS,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;IAEtE,MAAM,KAAK,GAAG,CAAC,GAAG,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAC7C,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,IAAI,CAAC,CACnC,CAAC;IACF,MAAM,QAAQ,GAAG,QAAQ,CAAC,IAAI,CAAC;IAC/B,OAAO;QACL,KAAK;QACL,QAAQ;QACR,OAAO,EACL,SAAS,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG;YAC/B,IAAI,QAAQ,kBAAkB,QAAQ,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,GAAG;KAC7D,CAAC;AACJ,CAAC;AAwBD,2EAA2E;AAC3E,MAAM,UAAU,GAAG,kBAAkB,CAAC;AAEtC;;;;GAIG;AACH,MAAM,SAAS,GAAG,EAAE,CAAC;AAErB;;;;GAIG;AACH,MAAM,UAAU,QAAQ,CAAC,UAAwB,EAAE;IACjD,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,IAAI,KAAK,CAAC;IACjC,wEAAwE;IACxE,kDAAkD;IAClD,MAAM,GAAG,GAAG,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,IAAI,MAAM,CAAC,CAAC,CAAC;IAClF,MAAM,KAAK,GAAG,CAAC,IAAY,EAAE,EAAE,CAC7B,GAAG,KAAK,EAAE,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,KAAK,GAAG,IAAI,IAAI,CAAC,UAAU,CAAC,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;IAC3F,IAAI,OAAO,GAAG,IAAI,GAAG,EAAU,CAAC;IAChC,IAAI,OAAO,GAAG,KAAK,CAAC;IACpB,IAAI,KAAiC,CAAC;IACtC,IAAI,MAAM,GAAG,KAAK,CAAC;IAEnB,MAAM,IAAI,GAAG,KAAK,EAAE,OAAiB,EAAyB,EAAE;QAC9D,OAAO,CAAC,OAAO,EAAE,CAAC,OAAO,CAAC,CAAC;QAC3B,IAAI,OAAqB,CAAC;QAC1B,IAAI,CAAC;YACH,OAAO,GAAG,EAAE,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC;QAClE,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,MAAM,KAAK,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;YAClE,OAAO,GAAG,EAAE,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;QAC1C,CAAC;QACD,OAAO,CAAC,OAAO,EAAE,CAAC,OAAO,CAAC,CAAC;QAC3B,OAAO,OAAO,CAAC;IACjB,CAAC,CAAC;IAEF,MAAM,KAAK,GAAG,KAAK,IAAmB,EAAE;QACtC,OAAO,GAAG,IAAI,CAAC;QACf,OAAO,OAAO,CAAC,IAAI,GAAG,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC;YACnC,MAAM,OAAO,GAAG,CAAC,GAAG,OAAO,CAAC,CAAC,IAAI,EAAE,CAAC;YACpC,OAAO,GAAG,IAAI,GAAG,EAAE,CAAC;YACpB,MAAM,IAAI,CAAC,OAAO,CAAC,CAAC;QACtB,CAAC;QACD,OAAO,GAAG,KAAK,CAAC;IAClB,CAAC,CAAC;IAEF,MAAM,KAAK,GAAG,IAAI,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,EAAE;QACtC,IAAI,OAAO,CAAC,IAAI,GAAG,CAAC;YAAE,KAAK,KAAK,EAAE,CAAC;QACnC,OAAO,OAAO,CAAC;IACjB,CAAC,CAAC,CAAC;IAEH,MAAM,OAAO,GAAG,UAAU,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,EAAE,CAAC,MAAM,EAAE,IAAI,EAAE,EAAE;QACpE,IAAI,MAAM,IAAI,CAAC,IAAI,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,KAAK,CAAC,IAAI,CAAC;YAAE,OAAO;QACrE,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;QAC5C,IAAI,OAAO,IAAI,KAAK;YAAE,OAAO;QAC7B,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE;YACtB,KAAK,GAAG,SAAS,CAAC;YAClB,qEAAqE;YACrE,KAAK,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE;gBACnB,IAAI,CAAC,OAAO;oBAAE,KAAK,KAAK,EAAE,CAAC;YAC7B,CAAC,CAAC,CAAC;QACL,CAAC,EAAE,SAAS,CAAC,CAAC;IAChB,CAAC,CAAC,CAAC;IAEH,OAAO;QACL,KAAK;QACL,KAAK;YACH,MAAM,GAAG,IAAI,CAAC;YACd,YAAY,CAAC,KAAK,CAAC,CAAC;YACpB,OAAO,CAAC,KAAK,EAAE,CAAC;QAClB,CAAC;KACF,CAAC;AACJ,CAAC","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 { watch as watchFiles } 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 watcher = watchFiles(src, { recursive: true }, (_event, file) => {\n if (closed || !file || !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 });\n\n return {\n ready,\n close() {\n closed = true;\n clearTimeout(timer);\n watcher.close();\n },\n };\n}\n"]}
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"]}
@@ -1 +1 @@
1
- {"version":3,"file":"lb-apply.d.ts","sourceRoot":"","sources":["../../hub/lb-apply.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"lb-apply.d.ts","sourceRoot":"","sources":["../../hub/lb-apply.ts"],"names":[],"mappings":"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"}
@@ -141,9 +141,11 @@ function land(el, value) {
141
141
  return;
142
142
  }
143
143
  if (isCheckbox(el)) {
144
- // Checked by the rule `lb-show` is on by: only null and false are off,
145
- // and no string is read.
146
- el.checked = value !== false;
144
+ // Off for false, and for what an unchecked checkbox gathers or an absent
145
+ // query parm lands as, so a checkbox lands what it gathered even from a
146
+ // query whose values are strings, such as lb-url.
147
+ el.checked =
148
+ value !== false && value !== "false" && value !== "";
147
149
  }
148
150
  else if (isRadio(el)) {
149
151
  el.checked =
@@ -1014,7 +1016,8 @@ function hide(el, hidden) {
1014
1016
  }
1015
1017
  /**
1016
1018
  * Finish a group and everything under it, from the rows it shows: each
1017
- * heading filled from the first, as it was when last placed by the order,
1019
+ * heading filled and linked from the first, as it was when last placed by
1020
+ * the order,
1018
1021
  * each aggregate set from all of them, and a group hidden when a filter hides
1019
1022
  * every row in it. The rows it shows are returned.
1020
1023
  */
@@ -1032,9 +1035,12 @@ function finish(group, types) {
1032
1035
  }
1033
1036
  }
1034
1037
  }
1035
- if (shown[0])
1036
- for (const head of group.heads)
1038
+ if (shown[0]) {
1039
+ for (const head of group.heads) {
1037
1040
  fill(head, shown[0].placed);
1041
+ link(head, shown[0].placed);
1042
+ }
1043
+ }
1038
1044
  aggregate(group.aggregates, rows, types);
1039
1045
  return shown;
1040
1046
  }