@gusnips/native 0.1.1 → 0.1.2

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.
package/README.md CHANGED
@@ -128,7 +128,9 @@ Five things worth knowing:
128
128
  `package.json`: a devDependency is not enough, because react-native-css loads the copy under
129
129
  `@expo/metro-config` first.
130
130
  - **A `className` change hot-reloads.** Only a change to `global.css` or your tokens needs the
131
- build to run again.
131
+ build to run again. While you work on those, keep `gusnips-native-css --watch` running beside
132
+ Metro: it builds again when `global.css` or a file it imports changes (your token files, followed
133
+ through their own imports), and not when you edit a component.
132
134
  - **There is no global font.** react-native-css drops element and `*` selectors, so a text without
133
135
  a `font-*` class renders in the system font.
134
136
 
@@ -149,7 +151,8 @@ each class compiles to. The colours come from the stylesheet itself, so a change
149
151
  icons with the text. Tailwind writes only classes it finds in your files, so keep each palette
150
152
  class in your source; the build stops and names any it cannot find.
151
153
 
152
- From a script, the same build is `buildNativeCss({ palette })` from `@gusnips/native/css`.
154
+ From a script, the same build is `buildNativeCss({ palette })` from `@gusnips/native/css`, and the
155
+ watch is `watchNativeCss(options, { onBuild, onError })`.
153
156
 
154
157
  ## Streams
155
158
 
@@ -294,6 +297,20 @@ and under the default flow that URL carries the refresh token.
294
297
  resolver protects the bundle from bun's store layout; the catalog stops the versions drifting in
295
298
  the first place.
296
299
 
300
+ ## Testing an app that uses this
301
+
302
+ Mock the kit's import, not `react-native` under it:
303
+
304
+ ```ts
305
+ vi.mock("@gusnips/native/network", () => ({ useIsOffline: () => false }));
306
+ vi.mock("@gusnips/native/query", () => ({ installQueryManagers: () => {} }));
307
+ ```
308
+
309
+ Vitest runs a package from `node_modules` as it is, outside its own module graph, so a
310
+ `vi.mock("react-native")` or `vi.mock("@react-native-community/netinfo")` in your test never
311
+ reaches the imports inside `@gusnips/native`. Those load the real packages, which do not run under
312
+ Node. Mocking the subpath your code imports replaces the whole thing, and nothing under it loads.
313
+
297
314
  ## Why the dependencies are peers
298
315
 
299
316
  There are no `dependencies`. Each package here is one your app already has, or one that carries
@@ -8,7 +8,7 @@
8
8
  import { readFile } from "node:fs/promises";
9
9
  import { resolve } from "node:path";
10
10
  import { parseArgs } from "node:util";
11
- import { buildNativeCss } from "../css.js";
11
+ import { buildNativeCss, watchNativeCss } from "../css.js";
12
12
  const HELP = `gusnips-native-css: compile global.css for React Native, before Metro starts.
13
13
 
14
14
  Usage: gusnips-native-css [options]
@@ -20,9 +20,11 @@ Usage: gusnips-native-css [options]
20
20
  { "text": { "light": "text-ink", "dark": "dark:text-ink-dark" } }
21
21
  writes colors.ts beside stylesheet.ts
22
22
  --rem <px> pixels per rem (default: 16, as on the web)
23
+ --watch build again when global.css or a file it imports changes
23
24
  -h, --help show this
24
25
 
25
26
  Run it before Metro, in the same script: "start": "gusnips-native-css && react-native start"
27
+ While you edit the CSS or the tokens, keep "gusnips-native-css --watch" open beside Metro.
26
28
  `;
27
29
  async function main() {
28
30
  const { values } = parseArgs({
@@ -32,6 +34,7 @@ async function main() {
32
34
  out: { type: "string" },
33
35
  palette: { type: "string" },
34
36
  rem: { type: "string" },
37
+ watch: { type: "boolean" },
35
38
  help: { type: "boolean", short: "h" },
36
39
  },
37
40
  });
@@ -39,17 +42,29 @@ async function main() {
39
42
  process.stdout.write(HELP);
40
43
  return 0;
41
44
  }
42
- const root = resolve(values.root ?? ".");
43
- const result = await buildNativeCss({
44
- root,
45
+ const options = {
46
+ root: resolve(values.root ?? "."),
45
47
  input: values.input,
46
48
  outDir: values.out,
47
49
  rem: values.rem === undefined ? undefined : Number(values.rem),
48
50
  palette: values.palette === undefined ? undefined : await readPalette(resolve(values.palette)),
51
+ };
52
+ if (!values.watch) {
53
+ report(await buildNativeCss(options));
54
+ return 0;
55
+ }
56
+ // The folder watchers keep the process running until Ctrl-C. A failed build is printed and
57
+ // the watch goes on.
58
+ await watchNativeCss(options, {
59
+ onBuild: report,
60
+ onError: (error) => console.error(`native css: ${error.message}`),
49
61
  });
50
- console.log(`native css: ${result.classes} classes → ${result.files.join(", ")}`);
62
+ console.log("native css: watching global.css and the files it imports. Ctrl-C stops.");
51
63
  return 0;
52
64
  }
65
+ function report(result) {
66
+ console.log(`native css: ${result.classes} classes → ${result.files.join(", ")}`);
67
+ }
53
68
  /** The palette file is the app's own, but a typo in it should read as one, not as a crash. */
54
69
  async function readPalette(file) {
55
70
  const parsed = JSON.parse(await readFile(file, "utf-8"));
@@ -1 +1 @@
1
- {"version":3,"file":"gusnips-native-css.js","sourceRoot":"","sources":["../../../src/node/bin/gusnips-native-css.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"gusnips-native-css.js","sourceRoot":"","sources":["../../../src/node/bin/gusnips-native-css.ts"],"names":[],"mappings":"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"}
@@ -47,4 +47,34 @@ export declare function buildNativeCss(options?: NativeCssOptions): Promise<Nati
47
47
  * same way, so every release after 1.30.1 counts as broken until one is measured working.
48
48
  */
49
49
  export declare function isBrokenLightningcss(version: string): boolean;
50
+ /**
51
+ * The files whose change means the stylesheet must be built again: the entry, and every file of the
52
+ * app's it imports, followed through their own imports. That is `global.css` and the token files
53
+ * it pulls in. A package import (`tailwindcss/theme.css`, `nativewind/theme`) is left out, because it
54
+ * changes only on an install. Source files are left out on purpose too: a `className` edit
55
+ * hot-reloads without a rebuild, and a rebuild on every save would cost a compile per keystroke.
56
+ *
57
+ * An import is the app's own when it names a file that exists from the importing file's folder,
58
+ * which is how Tailwind tries it first.
59
+ */
60
+ export declare function cssInputs(entry: string): Promise<string[]>;
61
+ export interface NativeCssWatchHandlers {
62
+ /** A build finished. Also called for the first build. */
63
+ onBuild(result: NativeCssResult): void;
64
+ /** A build failed. Watching goes on, so fixing the file builds again. */
65
+ onError(error: Error): void;
66
+ }
67
+ /**
68
+ * Build, then build again whenever `global.css` or a file it imports changes. Stop it with
69
+ * `close()`. A first build that fails is reported and watched like any other, so saving the fix is
70
+ * enough.
71
+ *
72
+ * It watches each FOLDER and filters by name, not each file: an editor that saves by writing a new
73
+ * file and renaming it over the old one leaves a per-file watcher on the deleted file, which then
74
+ * never fires again. The import list is read again after every build, so a new token file starts
75
+ * being watched as soon as `global.css` imports it.
76
+ */
77
+ export declare function watchNativeCss(options: NativeCssOptions, { onBuild, onError }: NativeCssWatchHandlers): Promise<{
78
+ close(): void;
79
+ }>;
50
80
  //# sourceMappingURL=css.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"css.d.ts","sourceRoot":"","sources":["../../src/node/css.ts"],"names":[],"mappings":"AA8BA,4FAA4F;AAC5F,MAAM,WAAW,YAAY;IAC3B,6EAA6E;IAC7E,KAAK,EAAE,MAAM,CAAC;IACd,+DAA+D;IAC/D,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,gBAAgB;IAC/B,uFAAuF;IACvF,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,iDAAiD;IACjD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,qFAAqF;IACrF,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;;;;;;OAOG;IACH,GAAG,CAAC,EAAE,MAAM,CAAC;IACb;;;;;OAKG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;CACxC;AAED,MAAM,WAAW,eAAe;IAC9B,6CAA6C;IAC7C,OAAO,EAAE,MAAM,CAAC;IAChB,4CAA4C;IAC5C,KAAK,EAAE,MAAM,EAAE,CAAC;CACjB;AAED;;;;GAIG;AACH,wBAAsB,cAAc,CAAC,OAAO,GAAE,gBAAqB,GAAG,OAAO,CAAC,eAAe,CAAC,CA2C7F;AAuDD;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAK7D"}
1
+ {"version":3,"file":"css.d.ts","sourceRoot":"","sources":["../../src/node/css.ts"],"names":[],"mappings":"AA+BA,4FAA4F;AAC5F,MAAM,WAAW,YAAY;IAC3B,6EAA6E;IAC7E,KAAK,EAAE,MAAM,CAAC;IACd,+DAA+D;IAC/D,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,gBAAgB;IAC/B,uFAAuF;IACvF,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,iDAAiD;IACjD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,qFAAqF;IACrF,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;;;;;;OAOG;IACH,GAAG,CAAC,EAAE,MAAM,CAAC;IACb;;;;;OAKG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;CACxC;AAED,MAAM,WAAW,eAAe;IAC9B,6CAA6C;IAC7C,OAAO,EAAE,MAAM,CAAC;IAChB,4CAA4C;IAC5C,KAAK,EAAE,MAAM,EAAE,CAAC;CACjB;AAED;;;;GAIG;AACH,wBAAsB,cAAc,CAAC,OAAO,GAAE,gBAAqB,GAAG,OAAO,CAAC,eAAe,CAAC,CA2C7F;AAuDD;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAK7D;AA+ED;;;;;;;;;GASG;AACH,wBAAsB,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAehE;AAMD,MAAM,WAAW,sBAAsB;IACrC,yDAAyD;IACzD,OAAO,CAAC,MAAM,EAAE,eAAe,GAAG,IAAI,CAAC;IACvC,yEAAyE;IACzE,OAAO,CAAC,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;CAC7B;AAED;;;;;;;;;GASG;AACH,wBAAsB,cAAc,CAClC,OAAO,EAAE,gBAAgB,EACzB,EAAE,OAAO,EAAE,OAAO,EAAE,EAAE,sBAAsB,GAC3C,OAAO,CAAC;IAAE,KAAK,IAAI,IAAI,CAAA;CAAE,CAAC,CA0D5B"}
package/dist/node/css.js CHANGED
@@ -20,10 +20,11 @@
20
20
  * from where the app installed it.
21
21
  */
22
22
  import { spawnSync } from "node:child_process";
23
+ import { statSync, watch } from "node:fs";
23
24
  import { mkdir, mkdtemp, readFile, rm, writeFile } from "node:fs/promises";
24
25
  import { createRequire } from "node:module";
25
26
  import { tmpdir } from "node:os";
26
- import { dirname, join, relative, resolve } from "node:path";
27
+ import { basename, dirname, isAbsolute, join, relative, resolve } from "node:path";
27
28
  /**
28
29
  * ```ts
29
30
  * await buildNativeCss(); // global.css → src/generated/stylesheet.ts
@@ -192,4 +193,105 @@ function missing(name) {
192
193
  function isNotFound(error) {
193
194
  return error instanceof Error && "code" in error && error.code === "MODULE_NOT_FOUND";
194
195
  }
196
+ // ── Watching ─────────────────────────────────────────────────────────────────
197
+ /**
198
+ * The files whose change means the stylesheet must be built again: the entry, and every file of the
199
+ * app's it imports, followed through their own imports. That is `global.css` and the token files
200
+ * it pulls in. A package import (`tailwindcss/theme.css`, `nativewind/theme`) is left out, because it
201
+ * changes only on an install. Source files are left out on purpose too: a `className` edit
202
+ * hot-reloads without a rebuild, and a rebuild on every save would cost a compile per keystroke.
203
+ *
204
+ * An import is the app's own when it names a file that exists from the importing file's folder,
205
+ * which is how Tailwind tries it first.
206
+ */
207
+ export async function cssInputs(entry) {
208
+ const found = new Set();
209
+ async function visit(file) {
210
+ if (found.has(file))
211
+ return;
212
+ found.add(file);
213
+ const source = await readFile(file, "utf-8").catch(() => "");
214
+ const code = source.replace(/\/\*[\s\S]*?\*\//g, "");
215
+ for (const [, specifier] of code.matchAll(/@import\s+(?:url\(\s*)?["']([^"']+)["']/g)) {
216
+ if (specifier === undefined)
217
+ continue;
218
+ const target = isAbsolute(specifier) ? specifier : resolve(dirname(file), specifier);
219
+ if (isFile(target))
220
+ await visit(target);
221
+ }
222
+ }
223
+ await visit(resolve(entry));
224
+ return [...found];
225
+ }
226
+ function isFile(path) {
227
+ return statSync(path, { throwIfNoEntry: false })?.isFile() ?? false;
228
+ }
229
+ /**
230
+ * Build, then build again whenever `global.css` or a file it imports changes. Stop it with
231
+ * `close()`. A first build that fails is reported and watched like any other, so saving the fix is
232
+ * enough.
233
+ *
234
+ * It watches each FOLDER and filters by name, not each file: an editor that saves by writing a new
235
+ * file and renaming it over the old one leaves a per-file watcher on the deleted file, which then
236
+ * never fires again. The import list is read again after every build, so a new token file starts
237
+ * being watched as soon as `global.css` imports it.
238
+ */
239
+ export async function watchNativeCss(options, { onBuild, onError }) {
240
+ const root = resolve(options.root ?? ".");
241
+ const entry = resolve(root, options.input ?? "global.css");
242
+ let watchers = [];
243
+ let watched = new Set();
244
+ let timer;
245
+ let running = null;
246
+ let again = false;
247
+ let closed = false;
248
+ async function rewatch() {
249
+ const inputs = await cssInputs(entry);
250
+ for (const watcher of watchers)
251
+ watcher.close();
252
+ watched = new Set(inputs);
253
+ watchers = [...new Set(inputs.map((file) => dirname(file)))].map((folder) => watch(folder, (_event, name) => {
254
+ if (name !== null && watched.has(join(folder, basename(name))))
255
+ schedule();
256
+ }));
257
+ }
258
+ // One build at a time: a save during a build marks it to run once more after, so the last save
259
+ // always lands and two compiles never write the same files at once.
260
+ async function build() {
261
+ if (running) {
262
+ again = true;
263
+ return running;
264
+ }
265
+ running = (async () => {
266
+ do {
267
+ again = false;
268
+ try {
269
+ onBuild(await buildNativeCss(options));
270
+ }
271
+ catch (error) {
272
+ onError(error instanceof Error ? error : new Error(String(error)));
273
+ }
274
+ if (!closed)
275
+ await rewatch();
276
+ } while (again && !closed);
277
+ })().finally(() => {
278
+ running = null;
279
+ });
280
+ return running;
281
+ }
282
+ // Editors fire several events for one save. One build per burst.
283
+ function schedule() {
284
+ clearTimeout(timer);
285
+ timer = setTimeout(() => void build(), 50);
286
+ }
287
+ await build();
288
+ return {
289
+ close() {
290
+ closed = true;
291
+ clearTimeout(timer);
292
+ for (const watcher of watchers)
293
+ watcher.close();
294
+ },
295
+ };
296
+ }
195
297
  //# sourceMappingURL=css.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"css.js","sourceRoot":"","sources":["../../src/node/css.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"css.js","sourceRoot":"","sources":["../../src/node/css.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gusnips/native",
3
- "version": "0.1.1",
3
+ "version": "0.1.2",
4
4
  "description": "What a bare React Native app needs beside @gusnips/react: streams over XHR, React Query on app focus and network, theme and language that remember the choice, the Supabase session storage, the Metro config for a bun monorepo, and the Tailwind to react-native-css build.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -8,7 +8,7 @@
8
8
  import { readFile } from "node:fs/promises";
9
9
  import { resolve } from "node:path";
10
10
  import { parseArgs } from "node:util";
11
- import { buildNativeCss, type PaletteEntry } from "../css.ts";
11
+ import { buildNativeCss, watchNativeCss, type NativeCssResult, type PaletteEntry } from "../css.ts";
12
12
 
13
13
  const HELP = `gusnips-native-css: compile global.css for React Native, before Metro starts.
14
14
 
@@ -21,9 +21,11 @@ Usage: gusnips-native-css [options]
21
21
  { "text": { "light": "text-ink", "dark": "dark:text-ink-dark" } }
22
22
  writes colors.ts beside stylesheet.ts
23
23
  --rem <px> pixels per rem (default: 16, as on the web)
24
+ --watch build again when global.css or a file it imports changes
24
25
  -h, --help show this
25
26
 
26
27
  Run it before Metro, in the same script: "start": "gusnips-native-css && react-native start"
28
+ While you edit the CSS or the tokens, keep "gusnips-native-css --watch" open beside Metro.
27
29
  `;
28
30
 
29
31
  async function main(): Promise<number> {
@@ -34,6 +36,7 @@ async function main(): Promise<number> {
34
36
  out: { type: "string" },
35
37
  palette: { type: "string" },
36
38
  rem: { type: "string" },
39
+ watch: { type: "boolean" },
37
40
  help: { type: "boolean", short: "h" },
38
41
  },
39
42
  });
@@ -42,18 +45,31 @@ async function main(): Promise<number> {
42
45
  return 0;
43
46
  }
44
47
 
45
- const root = resolve(values.root ?? ".");
46
- const result = await buildNativeCss({
47
- root,
48
+ const options = {
49
+ root: resolve(values.root ?? "."),
48
50
  input: values.input,
49
51
  outDir: values.out,
50
52
  rem: values.rem === undefined ? undefined : Number(values.rem),
51
53
  palette: values.palette === undefined ? undefined : await readPalette(resolve(values.palette)),
54
+ };
55
+ if (!values.watch) {
56
+ report(await buildNativeCss(options));
57
+ return 0;
58
+ }
59
+ // The folder watchers keep the process running until Ctrl-C. A failed build is printed and
60
+ // the watch goes on.
61
+ await watchNativeCss(options, {
62
+ onBuild: report,
63
+ onError: (error) => console.error(`native css: ${error.message}`),
52
64
  });
53
- console.log(`native css: ${result.classes} classes → ${result.files.join(", ")}`);
65
+ console.log("native css: watching global.css and the files it imports. Ctrl-C stops.");
54
66
  return 0;
55
67
  }
56
68
 
69
+ function report(result: NativeCssResult): void {
70
+ console.log(`native css: ${result.classes} classes → ${result.files.join(", ")}`);
71
+ }
72
+
57
73
  /** The palette file is the app's own, but a typo in it should read as one, not as a crash. */
58
74
  async function readPalette(file: string): Promise<Record<string, PaletteEntry>> {
59
75
  const parsed: unknown = JSON.parse(await readFile(file, "utf-8"));
@@ -1,5 +1,8 @@
1
- import { describe, expect, it } from "vitest";
2
- import { isBrokenLightningcss } from "./css.ts";
1
+ import { mkdir, mkdtemp, rm, writeFile } from "node:fs/promises";
2
+ import { tmpdir } from "node:os";
3
+ import { join } from "node:path";
4
+ import { afterEach, describe, expect, it } from "vitest";
5
+ import { cssInputs, isBrokenLightningcss } from "./css.ts";
3
6
 
4
7
  describe("isBrokenLightningcss", () => {
5
8
  it("lets 1.30.1 and earlier through", () => {
@@ -17,3 +20,42 @@ describe("isBrokenLightningcss", () => {
17
20
  expect(isBrokenLightningcss("1.30.2+build.7")).toBe(true);
18
21
  });
19
22
  });
23
+
24
+ const made: string[] = [];
25
+ afterEach(async () => {
26
+ for (const dir of made.splice(0)) await rm(dir, { recursive: true, force: true });
27
+ });
28
+
29
+ describe("cssInputs: what makes --watch build again", () => {
30
+ it("is the entry and every local file it imports, followed through their own imports", async () => {
31
+ const app = await mkdtemp(join(tmpdir(), "native-css-inputs-"));
32
+ made.push(app);
33
+ const shared = join(app, "packages", "shared");
34
+ await mkdir(shared, { recursive: true });
35
+ await mkdir(join(app, "mobile"), { recursive: true });
36
+ await writeFile(
37
+ join(app, "mobile", "global.css"),
38
+ [
39
+ '@import "tailwindcss/theme.css" layer(theme);', // a package: changes only on install
40
+ '@import "nativewind/theme";',
41
+ '@import "../packages/shared/tokens.css";',
42
+ '/* @import "./commented-out.css"; */',
43
+ '@import url("./fonts.css");',
44
+ '@import "./missing.css";', // a typo is Tailwind's error to report, not a file to watch
45
+ ].join("\n"),
46
+ );
47
+ await writeFile(join(app, "mobile", "fonts.css"), ":root { --font-sans: Inter; }");
48
+ await writeFile(join(app, "mobile", "commented-out.css"), "");
49
+ await writeFile(join(shared, "tokens.css"), '@import "./colors.css";\n@theme {}');
50
+ await writeFile(join(shared, "colors.css"), '@import "./tokens.css";'); // a cycle ends
51
+
52
+ expect((await cssInputs(join(app, "mobile", "global.css"))).sort()).toEqual(
53
+ [
54
+ join(app, "mobile", "fonts.css"),
55
+ join(app, "mobile", "global.css"),
56
+ join(shared, "colors.css"),
57
+ join(shared, "tokens.css"),
58
+ ].sort(),
59
+ );
60
+ });
61
+ });
package/src/node/css.ts CHANGED
@@ -20,10 +20,11 @@
20
20
  * from where the app installed it.
21
21
  */
22
22
  import { spawnSync } from "node:child_process";
23
+ import { statSync, watch, type FSWatcher } from "node:fs";
23
24
  import { mkdir, mkdtemp, readFile, rm, writeFile } from "node:fs/promises";
24
25
  import { createRequire } from "node:module";
25
26
  import { tmpdir } from "node:os";
26
- import { dirname, join, relative, resolve } from "node:path";
27
+ import { basename, dirname, isAbsolute, join, relative, resolve } from "node:path";
27
28
  import type { ReactNativeCssStyleSheet } from "react-native-css/compiler";
28
29
 
29
30
  type Compiler = typeof import("react-native-css/compiler");
@@ -257,3 +258,116 @@ function missing(name: string): string {
257
258
  function isNotFound(error: unknown): boolean {
258
259
  return error instanceof Error && "code" in error && error.code === "MODULE_NOT_FOUND";
259
260
  }
261
+
262
+ // ── Watching ─────────────────────────────────────────────────────────────────
263
+
264
+ /**
265
+ * The files whose change means the stylesheet must be built again: the entry, and every file of the
266
+ * app's it imports, followed through their own imports. That is `global.css` and the token files
267
+ * it pulls in. A package import (`tailwindcss/theme.css`, `nativewind/theme`) is left out, because it
268
+ * changes only on an install. Source files are left out on purpose too: a `className` edit
269
+ * hot-reloads without a rebuild, and a rebuild on every save would cost a compile per keystroke.
270
+ *
271
+ * An import is the app's own when it names a file that exists from the importing file's folder,
272
+ * which is how Tailwind tries it first.
273
+ */
274
+ export async function cssInputs(entry: string): Promise<string[]> {
275
+ const found = new Set<string>();
276
+ async function visit(file: string): Promise<void> {
277
+ if (found.has(file)) return;
278
+ found.add(file);
279
+ const source = await readFile(file, "utf-8").catch(() => "");
280
+ const code = source.replace(/\/\*[\s\S]*?\*\//g, "");
281
+ for (const [, specifier] of code.matchAll(/@import\s+(?:url\(\s*)?["']([^"']+)["']/g)) {
282
+ if (specifier === undefined) continue;
283
+ const target = isAbsolute(specifier) ? specifier : resolve(dirname(file), specifier);
284
+ if (isFile(target)) await visit(target);
285
+ }
286
+ }
287
+ await visit(resolve(entry));
288
+ return [...found];
289
+ }
290
+
291
+ function isFile(path: string): boolean {
292
+ return statSync(path, { throwIfNoEntry: false })?.isFile() ?? false;
293
+ }
294
+
295
+ export interface NativeCssWatchHandlers {
296
+ /** A build finished. Also called for the first build. */
297
+ onBuild(result: NativeCssResult): void;
298
+ /** A build failed. Watching goes on, so fixing the file builds again. */
299
+ onError(error: Error): void;
300
+ }
301
+
302
+ /**
303
+ * Build, then build again whenever `global.css` or a file it imports changes. Stop it with
304
+ * `close()`. A first build that fails is reported and watched like any other, so saving the fix is
305
+ * enough.
306
+ *
307
+ * It watches each FOLDER and filters by name, not each file: an editor that saves by writing a new
308
+ * file and renaming it over the old one leaves a per-file watcher on the deleted file, which then
309
+ * never fires again. The import list is read again after every build, so a new token file starts
310
+ * being watched as soon as `global.css` imports it.
311
+ */
312
+ export async function watchNativeCss(
313
+ options: NativeCssOptions,
314
+ { onBuild, onError }: NativeCssWatchHandlers,
315
+ ): Promise<{ close(): void }> {
316
+ const root = resolve(options.root ?? ".");
317
+ const entry = resolve(root, options.input ?? "global.css");
318
+ let watchers: FSWatcher[] = [];
319
+ let watched = new Set<string>();
320
+ let timer: ReturnType<typeof setTimeout> | undefined;
321
+ let running: Promise<void> | null = null;
322
+ let again = false;
323
+ let closed = false;
324
+
325
+ async function rewatch(): Promise<void> {
326
+ const inputs = await cssInputs(entry);
327
+ for (const watcher of watchers) watcher.close();
328
+ watched = new Set(inputs);
329
+ watchers = [...new Set(inputs.map((file) => dirname(file)))].map((folder) =>
330
+ watch(folder, (_event, name) => {
331
+ if (name !== null && watched.has(join(folder, basename(name)))) schedule();
332
+ }),
333
+ );
334
+ }
335
+
336
+ // One build at a time: a save during a build marks it to run once more after, so the last save
337
+ // always lands and two compiles never write the same files at once.
338
+ async function build(): Promise<void> {
339
+ if (running) {
340
+ again = true;
341
+ return running;
342
+ }
343
+ running = (async () => {
344
+ do {
345
+ again = false;
346
+ try {
347
+ onBuild(await buildNativeCss(options));
348
+ } catch (error) {
349
+ onError(error instanceof Error ? error : new Error(String(error)));
350
+ }
351
+ if (!closed) await rewatch();
352
+ } while (again && !closed);
353
+ })().finally(() => {
354
+ running = null;
355
+ });
356
+ return running;
357
+ }
358
+
359
+ // Editors fire several events for one save. One build per burst.
360
+ function schedule(): void {
361
+ clearTimeout(timer);
362
+ timer = setTimeout(() => void build(), 50);
363
+ }
364
+
365
+ await build();
366
+ return {
367
+ close() {
368
+ closed = true;
369
+ clearTimeout(timer);
370
+ for (const watcher of watchers) watcher.close();
371
+ },
372
+ };
373
+ }