@gtkx/cli 1.0.0-rc.1 → 1.0.0-rc.3

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 (286) hide show
  1. package/dist/builder.d.ts +3 -2
  2. package/dist/builder.d.ts.map +1 -1
  3. package/dist/builder.js +2 -1
  4. package/dist/builder.js.map +1 -1
  5. package/dist/cli.d.ts +1 -1
  6. package/dist/cli.d.ts.map +1 -1
  7. package/dist/cli.js +21 -6
  8. package/dist/cli.js.map +1 -1
  9. package/dist/codegen/freshness.d.ts +5 -5
  10. package/dist/codegen/freshness.d.ts.map +1 -1
  11. package/dist/codegen/freshness.js +26 -23
  12. package/dist/codegen/freshness.js.map +1 -1
  13. package/dist/codegen/report.d.ts +2 -1
  14. package/dist/codegen/report.d.ts.map +1 -1
  15. package/dist/codegen/report.js +4 -2
  16. package/dist/codegen/report.js.map +1 -1
  17. package/dist/codegen/run-codegen.d.ts +10 -10
  18. package/dist/codegen/run-codegen.d.ts.map +1 -1
  19. package/dist/codegen/run-codegen.js +72 -47
  20. package/dist/codegen/run-codegen.js.map +1 -1
  21. package/dist/codegen/store-resolver.d.ts +9 -6
  22. package/dist/codegen/store-resolver.d.ts.map +1 -1
  23. package/dist/codegen/store-resolver.js +24 -7
  24. package/dist/codegen/store-resolver.js.map +1 -1
  25. package/dist/commands/build.d.ts +4 -3
  26. package/dist/commands/build.d.ts.map +1 -1
  27. package/dist/commands/build.js +2 -1
  28. package/dist/commands/build.js.map +1 -1
  29. package/dist/commands/codegen.d.ts +2 -1
  30. package/dist/commands/codegen.d.ts.map +1 -1
  31. package/dist/commands/codegen.js +6 -4
  32. package/dist/commands/codegen.js.map +1 -1
  33. package/dist/commands/dev.d.ts +2 -1
  34. package/dist/commands/dev.d.ts.map +1 -1
  35. package/dist/commands/dev.js +2 -1
  36. package/dist/commands/dev.js.map +1 -1
  37. package/dist/commands/docs.d.ts +2 -1
  38. package/dist/commands/docs.d.ts.map +1 -1
  39. package/dist/commands/docs.js +25 -7
  40. package/dist/commands/docs.js.map +1 -1
  41. package/dist/dev/icon-dir.d.ts +2 -1
  42. package/dist/dev/icon-dir.d.ts.map +1 -1
  43. package/dist/dev/icon-dir.js +6 -3
  44. package/dist/dev/icon-dir.js.map +1 -1
  45. package/dist/dev/refresh-tracker.d.ts +5 -4
  46. package/dist/dev/refresh-tracker.d.ts.map +1 -1
  47. package/dist/dev/refresh-tracker.js +6 -5
  48. package/dist/dev/refresh-tracker.js.map +1 -1
  49. package/dist/dev/runner-deps.d.ts +2 -1
  50. package/dist/dev/runner-deps.d.ts.map +1 -1
  51. package/dist/dev/runner-deps.js +20 -4
  52. package/dist/dev/runner-deps.js.map +1 -1
  53. package/dist/dev/runner-main.d.ts +2 -1
  54. package/dist/dev/runner-main.d.ts.map +1 -1
  55. package/dist/dev/runner-main.js +3 -2
  56. package/dist/dev/runner-main.js.map +1 -1
  57. package/dist/dev/runner.d.ts +6 -5
  58. package/dist/dev/runner.d.ts.map +1 -1
  59. package/dist/dev/runner.js +69 -38
  60. package/dist/dev/runner.js.map +1 -1
  61. package/dist/dev/schema-dir.d.ts +2 -1
  62. package/dist/dev/schema-dir.d.ts.map +1 -1
  63. package/dist/dev/schema-dir.js +4 -2
  64. package/dist/dev/schema-dir.js.map +1 -1
  65. package/dist/dev/supervisor.d.ts +7 -6
  66. package/dist/dev/supervisor.d.ts.map +1 -1
  67. package/dist/dev/supervisor.js +107 -67
  68. package/dist/dev/supervisor.js.map +1 -1
  69. package/dist/dev/vite-dev-server.d.ts +3 -3
  70. package/dist/dev/vite-dev-server.d.ts.map +1 -1
  71. package/dist/dev/vite-dev-server.js +2 -1
  72. package/dist/dev/vite-dev-server.js.map +1 -1
  73. package/dist/internal/banner.d.ts +2 -1
  74. package/dist/internal/banner.d.ts.map +1 -1
  75. package/dist/internal/banner.js +2 -1
  76. package/dist/internal/banner.js.map +1 -1
  77. package/dist/internal/data-dir.d.ts +3 -2
  78. package/dist/internal/data-dir.d.ts.map +1 -1
  79. package/dist/internal/data-dir.js +29 -14
  80. package/dist/internal/data-dir.js.map +1 -1
  81. package/dist/internal/entry-arg.d.ts +5 -4
  82. package/dist/internal/entry-arg.d.ts.map +1 -1
  83. package/dist/internal/entry-arg.js +10 -7
  84. package/dist/internal/entry-arg.js.map +1 -1
  85. package/dist/internal/errors.d.ts +3 -2
  86. package/dist/internal/errors.d.ts.map +1 -1
  87. package/dist/internal/errors.js +7 -5
  88. package/dist/internal/errors.js.map +1 -1
  89. package/dist/internal/list-files.d.ts +3 -2
  90. package/dist/internal/list-files.d.ts.map +1 -1
  91. package/dist/internal/list-files.js +5 -3
  92. package/dist/internal/list-files.js.map +1 -1
  93. package/dist/internal/resolve-cli-tool.d.ts +2 -1
  94. package/dist/internal/resolve-cli-tool.d.ts.map +1 -1
  95. package/dist/internal/resolve-cli-tool.js +17 -10
  96. package/dist/internal/resolve-cli-tool.js.map +1 -1
  97. package/dist/internal/staging-dir.d.ts +3 -2
  98. package/dist/internal/staging-dir.d.ts.map +1 -1
  99. package/dist/internal/staging-dir.js +3 -2
  100. package/dist/internal/staging-dir.js.map +1 -1
  101. package/dist/internal/xdg-data-dirs.d.ts +2 -1
  102. package/dist/internal/xdg-data-dirs.d.ts.map +1 -1
  103. package/dist/internal/xdg-data-dirs.js +4 -2
  104. package/dist/internal/xdg-data-dirs.js.map +1 -1
  105. package/dist/mcp/client.d.ts +9 -4
  106. package/dist/mcp/client.d.ts.map +1 -1
  107. package/dist/mcp/client.js +106 -81
  108. package/dist/mcp/client.js.map +1 -1
  109. package/dist/mcp/handlers.d.ts +3 -2
  110. package/dist/mcp/handlers.d.ts.map +1 -1
  111. package/dist/mcp/handlers.js +89 -61
  112. package/dist/mcp/handlers.js.map +1 -1
  113. package/dist/mcp/index.d.ts +2 -2
  114. package/dist/mcp/index.d.ts.map +1 -1
  115. package/dist/mcp/index.js +30 -13
  116. package/dist/mcp/index.js.map +1 -1
  117. package/dist/mcp/serialize-widget.d.ts +3 -3
  118. package/dist/mcp/serialize-widget.d.ts.map +1 -1
  119. package/dist/mcp/serialize-widget.js +9 -6
  120. package/dist/mcp/serialize-widget.js.map +1 -1
  121. package/dist/mcp/testing-loader.d.ts +3 -4
  122. package/dist/mcp/testing-loader.d.ts.map +1 -1
  123. package/dist/mcp/testing-loader.js +43 -21
  124. package/dist/mcp/testing-loader.js.map +1 -1
  125. package/dist/mcp/widget-registry.d.ts +3 -2
  126. package/dist/mcp/widget-registry.d.ts.map +1 -1
  127. package/dist/mcp/widget-registry.js +4 -3
  128. package/dist/mcp/widget-registry.js.map +1 -1
  129. package/dist/refresh-globals.d.ts +2 -0
  130. package/dist/refresh-globals.d.ts.map +1 -0
  131. package/dist/refresh-globals.js +8 -0
  132. package/dist/refresh-globals.js.map +1 -0
  133. package/dist/refresh-runtime.d.ts +5 -11
  134. package/dist/refresh-runtime.d.ts.map +1 -1
  135. package/dist/refresh-runtime.js +16 -14
  136. package/dist/refresh-runtime.js.map +1 -1
  137. package/dist/settings/compile.d.ts +2 -1
  138. package/dist/settings/compile.d.ts.map +1 -1
  139. package/dist/settings/compile.js +6 -4
  140. package/dist/settings/compile.js.map +1 -1
  141. package/dist/settings/parser.d.ts +6 -5
  142. package/dist/settings/parser.d.ts.map +1 -1
  143. package/dist/settings/parser.js +26 -22
  144. package/dist/settings/parser.js.map +1 -1
  145. package/dist/settings/render.d.ts +3 -2
  146. package/dist/settings/render.d.ts.map +1 -1
  147. package/dist/settings/render.js +38 -64
  148. package/dist/settings/render.js.map +1 -1
  149. package/dist/settings/schema.d.ts +8 -8
  150. package/dist/settings/schema.d.ts.map +1 -1
  151. package/dist/settings/schema.js +61 -43
  152. package/dist/settings/schema.js.map +1 -1
  153. package/dist/vite-plugins/asset-extensions.d.ts +4 -3
  154. package/dist/vite-plugins/asset-extensions.d.ts.map +1 -1
  155. package/dist/vite-plugins/asset-extensions.js +4 -3
  156. package/dist/vite-plugins/asset-extensions.js.map +1 -1
  157. package/dist/vite-plugins/built-url.d.ts +2 -1
  158. package/dist/vite-plugins/built-url.d.ts.map +1 -1
  159. package/dist/vite-plugins/built-url.js +19 -13
  160. package/dist/vite-plugins/built-url.js.map +1 -1
  161. package/dist/vite-plugins/css.d.ts +2 -1
  162. package/dist/vite-plugins/css.d.ts.map +1 -1
  163. package/dist/vite-plugins/css.js +23 -15
  164. package/dist/vite-plugins/css.js.map +1 -1
  165. package/dist/vite-plugins/fast-refresh/refresh-filter.d.ts +5 -4
  166. package/dist/vite-plugins/fast-refresh/refresh-filter.d.ts.map +1 -1
  167. package/dist/vite-plugins/fast-refresh/refresh-filter.js +11 -7
  168. package/dist/vite-plugins/fast-refresh/refresh-filter.js.map +1 -1
  169. package/dist/vite-plugins/fast-refresh/swc-refresh.d.ts +4 -3
  170. package/dist/vite-plugins/fast-refresh/swc-refresh.d.ts.map +1 -1
  171. package/dist/vite-plugins/fast-refresh/swc-refresh.js +45 -41
  172. package/dist/vite-plugins/fast-refresh/swc-refresh.js.map +1 -1
  173. package/dist/vite-plugins/icons.d.ts +2 -1
  174. package/dist/vite-plugins/icons.d.ts.map +1 -1
  175. package/dist/vite-plugins/icons.js +24 -20
  176. package/dist/vite-plugins/icons.js.map +1 -1
  177. package/dist/vite-plugins/index.d.ts +2 -1
  178. package/dist/vite-plugins/index.d.ts.map +1 -1
  179. package/dist/vite-plugins/index.js +4 -3
  180. package/dist/vite-plugins/index.js.map +1 -1
  181. package/dist/vite-plugins/native.d.ts +2 -1
  182. package/dist/vite-plugins/native.d.ts.map +1 -1
  183. package/dist/vite-plugins/native.js +6 -4
  184. package/dist/vite-plugins/native.js.map +1 -1
  185. package/dist/vite-plugins/react-compiler.d.ts +3 -2
  186. package/dist/vite-plugins/react-compiler.d.ts.map +1 -1
  187. package/dist/vite-plugins/react-compiler.js +26 -20
  188. package/dist/vite-plugins/react-compiler.js.map +1 -1
  189. package/dist/vite-plugins/react-dom-prebundle.d.ts +2 -1
  190. package/dist/vite-plugins/react-dom-prebundle.d.ts.map +1 -1
  191. package/dist/vite-plugins/react-dom-prebundle.js +2 -1
  192. package/dist/vite-plugins/react-dom-prebundle.js.map +1 -1
  193. package/dist/vite-plugins/resource-init-module.d.ts +2 -1
  194. package/dist/vite-plugins/resource-init-module.d.ts.map +1 -1
  195. package/dist/vite-plugins/resource-init-module.js +39 -38
  196. package/dist/vite-plugins/resource-init-module.js.map +1 -1
  197. package/dist/vite-plugins/resource-shared.d.ts +7 -5
  198. package/dist/vite-plugins/resource-shared.d.ts.map +1 -1
  199. package/dist/vite-plugins/resource-shared.js +17 -10
  200. package/dist/vite-plugins/resource-shared.js.map +1 -1
  201. package/dist/vite-plugins/resources.d.ts +3 -2
  202. package/dist/vite-plugins/resources.d.ts.map +1 -1
  203. package/dist/vite-plugins/resources.js +95 -55
  204. package/dist/vite-plugins/resources.js.map +1 -1
  205. package/dist/vite-plugins/settings-worker-env.d.ts +2 -1
  206. package/dist/vite-plugins/settings-worker-env.d.ts.map +1 -1
  207. package/dist/vite-plugins/settings-worker-env.js +4 -2
  208. package/dist/vite-plugins/settings-worker-env.js.map +1 -1
  209. package/dist/vite-plugins/settings.d.ts +2 -1
  210. package/dist/vite-plugins/settings.d.ts.map +1 -1
  211. package/dist/vite-plugins/settings.js +56 -32
  212. package/dist/vite-plugins/settings.js.map +1 -1
  213. package/dist/vite-plugins/strip-query.d.ts +3 -0
  214. package/dist/vite-plugins/strip-query.d.ts.map +1 -0
  215. package/dist/vite-plugins/strip-query.js +6 -0
  216. package/dist/vite-plugins/strip-query.js.map +1 -0
  217. package/dist/vite-plugins/undeclared-library.d.ts +3 -1
  218. package/dist/vite-plugins/undeclared-library.d.ts.map +1 -1
  219. package/dist/vite-plugins/undeclared-library.js +11 -8
  220. package/dist/vite-plugins/undeclared-library.js.map +1 -1
  221. package/dist/vite-plugins/virtual-module.d.ts +2 -2
  222. package/dist/vite-plugins/virtual-module.d.ts.map +1 -1
  223. package/dist/vite-plugins/virtual-module.js +5 -3
  224. package/dist/vite-plugins/virtual-module.js.map +1 -1
  225. package/dist/vitest-plugin.d.ts.map +1 -1
  226. package/dist/vitest-plugin.js.map +1 -1
  227. package/env.d.ts +21 -61
  228. package/package.json +12 -14
  229. package/src/babel-modules.d.ts +0 -2
  230. package/src/builder.ts +7 -6
  231. package/src/cli.ts +26 -6
  232. package/src/codegen/freshness.ts +39 -26
  233. package/src/codegen/report.ts +10 -2
  234. package/src/codegen/run-codegen.ts +131 -66
  235. package/src/codegen/store-resolver.ts +52 -15
  236. package/src/commands/build.ts +3 -2
  237. package/src/commands/codegen.ts +9 -4
  238. package/src/commands/dev.ts +3 -4
  239. package/src/commands/docs.ts +47 -7
  240. package/src/dev/icon-dir.ts +13 -3
  241. package/src/dev/refresh-tracker.ts +12 -8
  242. package/src/dev/runner-deps.ts +30 -7
  243. package/src/dev/runner-main.ts +4 -3
  244. package/src/dev/runner.ts +100 -41
  245. package/src/dev/schema-dir.ts +7 -2
  246. package/src/dev/supervisor.ts +169 -71
  247. package/src/dev/vite-dev-server.ts +4 -2
  248. package/src/internal/banner.ts +5 -1
  249. package/src/internal/data-dir.ts +47 -16
  250. package/src/internal/entry-arg.ts +16 -8
  251. package/src/internal/errors.ts +15 -7
  252. package/src/internal/list-files.ts +10 -4
  253. package/src/internal/resolve-cli-tool.ts +28 -12
  254. package/src/internal/staging-dir.ts +5 -2
  255. package/src/internal/xdg-data-dirs.ts +8 -2
  256. package/src/mcp/client.ts +143 -85
  257. package/src/mcp/handlers.ts +126 -69
  258. package/src/mcp/index.ts +38 -14
  259. package/src/mcp/serialize-widget.ts +15 -8
  260. package/src/mcp/testing-loader.ts +64 -23
  261. package/src/mcp/widget-registry.ts +11 -5
  262. package/src/refresh-globals.ts +10 -0
  263. package/src/refresh-runtime.ts +20 -16
  264. package/src/settings/compile.ts +7 -4
  265. package/src/settings/parser.ts +69 -30
  266. package/src/settings/render.ts +66 -66
  267. package/src/settings/schema.ts +100 -46
  268. package/src/vite-plugins/asset-extensions.ts +4 -3
  269. package/src/vite-plugins/built-url.ts +30 -15
  270. package/src/vite-plugins/css.ts +35 -20
  271. package/src/vite-plugins/fast-refresh/refresh-filter.ts +18 -10
  272. package/src/vite-plugins/fast-refresh/swc-refresh.ts +60 -47
  273. package/src/vite-plugins/icons.ts +40 -24
  274. package/src/vite-plugins/index.ts +7 -4
  275. package/src/vite-plugins/native.ts +14 -5
  276. package/src/vite-plugins/react-compiler.ts +44 -30
  277. package/src/vite-plugins/react-dom-prebundle.ts +4 -1
  278. package/src/vite-plugins/resource-init-module.ts +41 -38
  279. package/src/vite-plugins/resource-shared.ts +27 -14
  280. package/src/vite-plugins/resources.ts +154 -60
  281. package/src/vite-plugins/settings-worker-env.ts +7 -2
  282. package/src/vite-plugins/settings.ts +107 -42
  283. package/src/vite-plugins/strip-query.ts +7 -0
  284. package/src/vite-plugins/undeclared-library.ts +26 -16
  285. package/src/vite-plugins/virtual-module.ts +9 -2
  286. package/src/vitest-plugin.ts +1 -2
@@ -1,16 +1,10 @@
1
+ import { error, exitCodeForSignal, info, installGracefulShutdown } from "@gtkx/utils";
1
2
  import { fork as nodeFork } from "node:child_process";
2
3
  import { type FSWatcher, watch as watchFs } from "node:fs";
3
4
  import { basename, dirname } from "node:path";
4
5
  import { fileURLToPath } from "node:url";
5
- import { error, exitCodeForSignal, info, installGracefulShutdown } from "@gtkx/utils";
6
-
7
- const DEV_RUNNER_URL = new URL("../../bin/gtkx-dev-runner.js", import.meta.url);
8
- const FORCE_KILL_TIMEOUT_MS = 5000;
9
- const CONFIG_DEBOUNCE_MS = 150;
10
-
11
- export const RESTART_EXIT_CODE = 75;
12
6
 
13
- export type SupervisedChild = {
7
+ type SupervisedChild = {
14
8
  killed: boolean;
15
9
  pid?: number | undefined;
16
10
  exitCode: number | null;
@@ -19,12 +13,9 @@ export type SupervisedChild = {
19
13
  once(event: "exit", listener: (code: number | null, signal: NodeJS.Signals | null) => void): void;
20
14
  };
21
15
 
22
- export type ForkRunner = (modulePath: string, args: string[], cwd: string) => SupervisedChild;
16
+ type ForkRunner = (modulePath: string, args: string[], cwd: string) => SupervisedChild;
23
17
 
24
- export const defaultForkRunner: ForkRunner = (modulePath, args, cwd) =>
25
- nodeFork(modulePath, [...args], { cwd, stdio: "inherit", detached: true });
26
-
27
- export type DevWatch = {
18
+ type DevWatch = {
28
19
  paths: string[];
29
20
  regenerate: () => Promise<void>;
30
21
  };
@@ -42,116 +33,221 @@ type SupervisorState = {
42
33
  capturedChildExit: number | undefined;
43
34
  };
44
35
 
36
+ type DebounceTimer = { handle: NodeJS.Timeout | null };
37
+
38
+ const DEV_RUNNER_URL = new URL("../../bin/gtkx-dev-runner.js", import.meta.url);
39
+ const FORCE_KILL_TIMEOUT_MS = 5000;
40
+ const CONFIG_DEBOUNCE_MS = 150;
41
+ const RESTART_EXIT_CODE = 75;
42
+
43
+ const defaultForkRunner: ForkRunner = (modulePath, args, cwd) =>
44
+ nodeFork(modulePath, [...args], { cwd, stdio: "inherit", detached: true });
45
+
45
46
  const forwardSignal = (child: SupervisedChild, signal: NodeJS.Signals): void => {
46
47
  if (!child.killed) {
47
48
  child.kill(signal);
48
49
  }
49
50
  };
50
51
 
51
- const forceKillChild = (child: SupervisedChild | null): void => {
52
- if (!child?.pid || child.exitCode !== null || child.killed) return;
52
+ const didForceKillChild = (child: SupervisedChild | null): boolean => {
53
+ if (!child?.pid || child.exitCode !== null || child.killed) {
54
+ return false;
55
+ }
56
+
53
57
  try {
54
- process.kill(child.pid, "SIGKILL");
55
- } catch {}
58
+ return process.kill(child.pid, "SIGKILL");
59
+ } catch {
60
+ return false;
61
+ }
62
+ };
63
+
64
+ const captureShutdownExit = (state: SupervisorState, code: number | null, signal: NodeJS.Signals | null): void => {
65
+ if (code !== null) {
66
+ state.capturedChildExit = code;
67
+ } else if (signal) {
68
+ state.capturedChildExit = exitCodeForSignal(signal);
69
+ }
70
+ };
71
+
72
+ const handleChildExit = (state: SupervisorState, code: number | null, signal: NodeJS.Signals | null): void => {
73
+ state.child = null;
74
+
75
+ if (state.restarting) {
76
+ return;
77
+ }
78
+
79
+ if (state.shuttingDown) {
80
+ captureShutdownExit(state, code, signal);
81
+
82
+ return;
83
+ }
84
+
85
+ if (code === RESTART_EXIT_CODE) {
86
+ info("Restarting dev runner...");
87
+ launch(state);
88
+
89
+ return;
90
+ }
91
+
92
+ process.exit(code ?? exitCodeForSignal(signal));
56
93
  };
57
94
 
58
95
  const launch = (state: SupervisorState): void => {
59
96
  const child = state.fork(state.runnerPath, [state.entryPath], state.cwd);
60
97
  state.child = child;
98
+
61
99
  child.on("exit", (code, signal) => {
62
- state.child = null;
63
- if (state.restarting) return;
64
- if (state.shuttingDown) {
65
- if (code !== null) {
66
- state.capturedChildExit = code;
67
- } else if (signal) {
68
- state.capturedChildExit = exitCodeForSignal(signal);
69
- }
70
- return;
71
- }
72
- if (code === RESTART_EXIT_CODE) {
73
- info("Restarting dev runner...");
74
- launch(state);
75
- return;
76
- }
77
- process.exit(code ?? exitCodeForSignal(signal));
100
+ handleChildExit(state, code, signal);
78
101
  });
79
102
  };
80
103
 
104
+ const isRestartBlocked = (state: SupervisorState): boolean => state.restarting || state.shuttingDown;
105
+
106
+ const relaunchAfterExit = (state: SupervisorState): void => {
107
+ state.restarting = false;
108
+
109
+ if (state.shuttingDown) {
110
+ return;
111
+ }
112
+
113
+ info("Restarting dev runner...");
114
+ launch(state);
115
+ };
116
+
81
117
  const restart = async (state: SupervisorState): Promise<void> => {
82
- if (state.restarting || state.shuttingDown || state.watch === undefined) return;
118
+ const watch = state.watch;
119
+
120
+ if (watch === undefined || isRestartBlocked(state)) {
121
+ return;
122
+ }
123
+
83
124
  state.restarting = true;
84
125
  info("gtkx.config.ts changed; regenerating bindings...");
126
+
85
127
  try {
86
- await state.watch.regenerate();
87
- } catch (cause) {
88
- error("Codegen failed; keeping the current dev runner. Fix the error and save again.", cause);
128
+ await watch.regenerate();
129
+ } catch (error_) {
130
+ error("Codegen failed; keeping the current dev runner. Fix the error and save again.", error_);
89
131
  state.restarting = false;
132
+
90
133
  return;
91
134
  }
135
+
92
136
  if (state.shuttingDown) {
93
137
  state.restarting = false;
138
+
94
139
  return;
95
140
  }
141
+
96
142
  const current = state.child;
143
+
97
144
  if (current === null) {
98
145
  state.restarting = false;
99
146
  launch(state);
147
+
100
148
  return;
101
149
  }
150
+
102
151
  current.once("exit", () => {
103
- state.restarting = false;
104
- if (!state.shuttingDown) {
105
- info("Restarting dev runner...");
106
- launch(state);
107
- }
152
+ relaunchAfterExit(state);
108
153
  });
154
+
109
155
  forwardSignal(current, "SIGTERM");
110
156
  };
111
157
 
112
- const installConfigWatchers = (state: SupervisorState): void => {
113
- const watch = state.watch;
114
- if (watch === undefined || watch.paths.length === 0) return;
115
- let timer: NodeJS.Timeout | null = null;
116
- const namesByDirectory = new Map<string, Set<string>>();
117
- for (const path of watch.paths) {
158
+ const scheduleRestart = (state: SupervisorState, timer: DebounceTimer): void => {
159
+ if (timer.handle !== null) {
160
+ clearTimeout(timer.handle);
161
+ }
162
+
163
+ timer.handle = setTimeout(() => void restart(state), CONFIG_DEBOUNCE_MS);
164
+ };
165
+
166
+ const isWatchedChange = (state: SupervisorState, names: Set<string>, filename: string | Buffer | null): boolean => {
167
+ if (filename === null || state.shuttingDown) {
168
+ return false;
169
+ }
170
+
171
+ return names.has(basename(filename.toString()));
172
+ };
173
+
174
+ const groupWatchNamesByDirectory = (paths: string[]): Map<string, Set<string>> => {
175
+ const namesByDirectory: Map<string, Set<string>> = new Map();
176
+
177
+ for (const path of paths) {
118
178
  const directory = dirname(path);
119
179
  const names = namesByDirectory.get(directory) ?? new Set<string>();
120
180
  names.add(basename(path));
121
181
  namesByDirectory.set(directory, names);
122
182
  }
123
- for (const [directory, names] of namesByDirectory) {
124
- const watcher = watchFs(directory, (_event, filename) => {
125
- if (state.shuttingDown) return;
126
- if (filename === null || !names.has(basename(filename.toString()))) return;
127
- if (timer !== null) clearTimeout(timer);
128
- timer = setTimeout(() => void restart(state), CONFIG_DEBOUNCE_MS);
129
- });
130
- watcher.on("error", () => {});
131
- state.watchers.push(watcher);
183
+
184
+ return namesByDirectory;
185
+ };
186
+
187
+ const watchConfigDirectory = (
188
+ state: SupervisorState,
189
+ directory: string,
190
+ names: Set<string>,
191
+ timer: DebounceTimer,
192
+ ): void => {
193
+ const watcher = watchFs(directory, (_event, filename) => {
194
+ if (isWatchedChange(state, names, filename)) {
195
+ scheduleRestart(state, timer);
196
+ }
197
+ });
198
+
199
+ watcher.on("error", (): void => undefined);
200
+ state.watchers.push(watcher);
201
+ };
202
+
203
+ const installConfigWatchers = (state: SupervisorState): void => {
204
+ const watch = state.watch;
205
+
206
+ if (watch === undefined || watch.paths.length === 0) {
207
+ return;
208
+ }
209
+
210
+ const timer: DebounceTimer = { handle: null };
211
+
212
+ for (const [directory, names] of groupWatchNamesByDirectory(watch.paths)) {
213
+ watchConfigDirectory(state, directory, names, timer);
132
214
  }
133
215
  };
134
216
 
217
+ const closeWatchers = (state: SupervisorState): void => {
218
+ for (const watcher of state.watchers) {
219
+ watcher.close();
220
+ }
221
+ };
222
+
223
+ const shutdownOnSignal = (state: SupervisorState, signal: NodeJS.Signals): Promise<void> =>
224
+ new Promise<void>((resolve) => {
225
+ state.shuttingDown = true;
226
+ closeWatchers(state);
227
+
228
+ if (!state.child) {
229
+ resolve();
230
+
231
+ return;
232
+ }
233
+
234
+ state.child.once("exit", () => {
235
+ resolve();
236
+ });
237
+
238
+ forwardSignal(state.child, signal);
239
+ });
240
+
135
241
  const installShutdown = (state: SupervisorState): void => {
136
242
  installGracefulShutdown({
137
- onSignal: (signal) =>
138
- new Promise<void>((resolve) => {
139
- state.shuttingDown = true;
140
- for (const watcher of state.watchers) watcher.close();
141
- if (!state.child) {
142
- resolve();
143
- return;
144
- }
145
- state.child.once("exit", () => resolve());
146
- forwardSignal(state.child, signal);
147
- }),
148
- onForce: () => forceKillChild(state.child),
243
+ onSignal: (signal) => shutdownOnSignal(state, signal),
244
+ onForce: () => didForceKillChild(state.child),
149
245
  forceKillAfterMs: FORCE_KILL_TIMEOUT_MS,
150
246
  exitCode: (signal, graceful) => state.capturedChildExit ?? (graceful ? 0 : exitCodeForSignal(signal)),
151
247
  });
152
248
  };
153
249
 
154
- export const runDevSupervisor = async (
250
+ const runDevSupervisor = async (
155
251
  entryPath: string,
156
252
  cwd: string,
157
253
  watch?: DevWatch,
@@ -174,5 +270,7 @@ export const runDevSupervisor = async (
174
270
  installShutdown(state);
175
271
  launch(state);
176
272
 
177
- return new Promise<never>(() => {});
273
+ return new Promise<never>((): void => undefined);
178
274
  };
275
+
276
+ export { RESTART_EXIT_CODE, defaultForkRunner, runDevSupervisor, type SupervisedChild, type ForkRunner, type DevWatch };
@@ -12,7 +12,7 @@ type DevServerModuleGraph = {
12
12
  invalidateModule(module: DevServerModule): void;
13
13
  };
14
14
 
15
- export type DevServer = {
15
+ type DevServer = {
16
16
  close(): Promise<void>;
17
17
  moduleGraph: DevServerModuleGraph;
18
18
  ssrLoadModule(id: string): Promise<Record<string, unknown>>;
@@ -21,7 +21,7 @@ export type DevServer = {
21
21
  };
22
22
  };
23
23
 
24
- export const createDevServerConfig = (root: string, plugins: Plugin[]): InlineConfig => ({
24
+ const createDevServerConfig = (root: string, plugins: Plugin[]): InlineConfig => ({
25
25
  root,
26
26
  appType: "custom",
27
27
  plugins,
@@ -32,3 +32,5 @@ export const createDevServerConfig = (root: string, plugins: Plugin[]): InlineCo
32
32
  noExternal: [/^@gtkx\/(?!(?:native|gi|gl|runtime|utils|css)(?:\/|$))/, /[/\\]\.gtkx[/\\]/],
33
33
  },
34
34
  });
35
+
36
+ export { createDevServerConfig, type DevServer };
@@ -1,9 +1,13 @@
1
1
  import type { Rollup } from "vite";
2
2
 
3
- export const prependBanner = (options: Rollup.OutputOptions, banner: string): Rollup.OutputOptions => {
3
+ const prependBanner = (options: Rollup.OutputOptions, banner: string): Rollup.OutputOptions => {
4
4
  const existing = options.banner;
5
+
5
6
  if (typeof existing === "function") {
6
7
  return { ...options, banner: async (chunk) => `${banner}\n${await existing(chunk)}` };
7
8
  }
9
+
8
10
  return { ...options, banner: existing ? `${banner}\n${existing}` : banner };
9
11
  };
12
+
13
+ export { prependBanner };
@@ -1,39 +1,70 @@
1
1
  import { readFileSync } from "node:fs";
2
2
  import { join } from "node:path";
3
3
 
4
- export const DATA_IMPORT_PREFIX = "#data";
4
+ const DATA_IMPORT_PREFIX = "#data";
5
+ const DATA_IMPORT_KEY = `${DATA_IMPORT_PREFIX}/*`;
6
+ const CONDITION_PRIORITY = ["default", "import", "node"] as const;
7
+ const DATA_TARGET_PATTERN = /^\.?\/?(.+?)\/\*$/;
5
8
 
6
- const DATA_IMPORT_KEY: string = `${DATA_IMPORT_PREFIX}/*`;
9
+ const isConditionMap = (entry: unknown): entry is Record<string, unknown> =>
10
+ entry !== null && typeof entry === "object" && !Array.isArray(entry);
7
11
 
8
- const CONDITION_PRIORITY = ["default", "import", "node"] as const;
12
+ const conditionTarget = (conditions: Record<string, unknown>): string | null => {
13
+ for (const condition of CONDITION_PRIORITY) {
14
+ const value = conditions[condition];
9
15
 
10
- const targetString = (entry: unknown): string | null => {
11
- if (typeof entry === "string") return entry;
12
- if (entry !== null && typeof entry === "object" && !Array.isArray(entry)) {
13
- const conditions = entry as Record<string, unknown>;
14
- for (const condition of CONDITION_PRIORITY) {
15
- const value = conditions[condition];
16
- if (typeof value === "string") return value;
16
+ if (typeof value === "string") {
17
+ return value;
17
18
  }
18
19
  }
20
+
19
21
  return null;
20
22
  };
21
23
 
22
- const DATA_TARGET_PATTERN = /^\.?\/?(.+?)\/\*$/;
24
+ const targetString = (entry: unknown): string | null => {
25
+ if (typeof entry === "string") {
26
+ return entry;
27
+ }
28
+
29
+ if (isConditionMap(entry)) {
30
+ return conditionTarget(entry);
31
+ }
32
+
33
+ return null;
34
+ };
23
35
 
24
36
  const directoryFromTarget = (target: string): string | null => {
25
37
  const match = DATA_TARGET_PATTERN.exec(target);
38
+
26
39
  return match?.[1] ?? null;
27
40
  };
28
41
 
29
- export const resolveDataDir = (root: string): string | null => {
30
- let manifest: { imports?: Record<string, unknown> };
42
+ const resolveDataDir = (root: string): string | null => {
43
+ let manifest: unknown;
44
+
31
45
  try {
32
- manifest = JSON.parse(readFileSync(join(root, "package.json"), "utf-8"));
46
+ manifest = JSON.parse(readFileSync(join(root, "package.json"), "utf8"));
33
47
  } catch {
34
48
  return null;
35
49
  }
36
- const target = targetString(manifest.imports?.[DATA_IMPORT_KEY]);
37
- if (target === null) return null;
50
+
51
+ if (!isConditionMap(manifest)) {
52
+ return null;
53
+ }
54
+
55
+ const imports = manifest.imports;
56
+
57
+ if (!isConditionMap(imports)) {
58
+ return null;
59
+ }
60
+
61
+ const target = targetString(imports[DATA_IMPORT_KEY]);
62
+
63
+ if (target === null) {
64
+ return null;
65
+ }
66
+
38
67
  return directoryFromTarget(target);
39
68
  };
69
+
70
+ export { DATA_IMPORT_PREFIX, resolveDataDir };
@@ -1,19 +1,20 @@
1
+ import type { PositionalArgDef, StringArgDef } from "citty";
1
2
  import { existsSync } from "node:fs";
2
3
  import { resolve } from "node:path";
3
- import type { PositionalArgDef, StringArgDef } from "citty";
4
4
 
5
5
  const DEFAULT_ENTRY_BASE = "src/index";
6
- const DEFAULT_ENTRY_EXTENSIONS = [".tsx", ".jsx", ".ts", ".js"];
6
+ const DEFAULT_ENTRY_EXTENSION = ".tsx";
7
+ const DEFAULT_ENTRY_EXTENSIONS = [DEFAULT_ENTRY_EXTENSION, ".jsx", ".ts", ".js"];
7
8
  const DEFAULT_ENTRY = `${DEFAULT_ENTRY_BASE}{${DEFAULT_ENTRY_EXTENSIONS.join(",")}}`;
8
9
 
9
- export const cwdArg: { cwd: StringArgDef } = {
10
+ const cwdArg: { cwd: StringArgDef } = {
10
11
  cwd: {
11
12
  type: "string",
12
13
  description: "Project root (default: current working directory)",
13
14
  },
14
15
  };
15
16
 
16
- export const entryArg: { entry: PositionalArgDef; cwd: StringArgDef } = {
17
+ const entryArg: { entry: PositionalArgDef; cwd: StringArgDef } = {
17
18
  entry: {
18
19
  type: "positional",
19
20
  description: `Entry file (default: ${DEFAULT_ENTRY})`,
@@ -22,17 +23,24 @@ export const entryArg: { entry: PositionalArgDef; cwd: StringArgDef } = {
22
23
  ...cwdArg,
23
24
  };
24
25
 
25
- export const resolveCwd = (args: { cwd?: string }): string => (args.cwd ? resolve(args.cwd) : process.cwd());
26
+ const resolveCwd = (args: { cwd?: string }): string => (args.cwd ? resolve(args.cwd) : process.cwd());
26
27
 
27
28
  const resolveDefaultEntry = (cwd: string): string => {
28
29
  for (const extension of DEFAULT_ENTRY_EXTENSIONS) {
29
30
  const candidate = resolve(cwd, `${DEFAULT_ENTRY_BASE}${extension}`);
30
- if (existsSync(candidate)) return candidate;
31
+
32
+ if (existsSync(candidate)) {
33
+ return candidate;
34
+ }
31
35
  }
32
- return resolve(cwd, `${DEFAULT_ENTRY_BASE}${DEFAULT_ENTRY_EXTENSIONS[0]}`);
36
+
37
+ return resolve(cwd, `${DEFAULT_ENTRY_BASE}${DEFAULT_ENTRY_EXTENSION}`);
33
38
  };
34
39
 
35
- export const resolveEntry = (args: { entry?: string; cwd?: string }): { cwd: string; entry: string } => {
40
+ const resolveEntry = (args: { entry?: string; cwd?: string }): { cwd: string; entry: string } => {
36
41
  const cwd = resolveCwd(args);
42
+
37
43
  return { cwd, entry: args.entry ? resolve(cwd, args.entry) : resolveDefaultEntry(cwd) };
38
44
  };
45
+
46
+ export { cwdArg, entryArg, resolveCwd, resolveEntry };
@@ -1,28 +1,36 @@
1
- import { logger } from "@gtkx/utils";
2
1
  import type { ArgsDef, CommandDef } from "citty";
2
+ import { logger } from "@gtkx/utils";
3
3
 
4
4
  const ERROR_EXIT_CODE = 1;
5
5
 
6
- export const printError = (cause: unknown): never => {
6
+ const printError = (cause: unknown): never => {
7
7
  const message = cause instanceof Error ? cause.message : String(cause);
8
8
  logger.error(message);
9
+
9
10
  if (cause instanceof Error && cause.stack !== undefined) {
10
11
  logger.debug(cause.stack);
11
12
  }
13
+
12
14
  process.exit(ERROR_EXIT_CODE);
13
15
  };
14
16
 
15
- export const withErrorBoundary = <T extends ArgsDef>(command: CommandDef<T>): CommandDef<T> => {
17
+ const withErrorBoundary = <T extends ArgsDef>(command: CommandDef<T>): CommandDef<T> => {
16
18
  const run = command.run;
17
- if (run === undefined) return command;
19
+
20
+ if (run === undefined) {
21
+ return command;
22
+ }
23
+
18
24
  return {
19
25
  ...command,
20
- run: async (context) => {
26
+ run: async (context): Promise<unknown> => {
21
27
  try {
22
28
  return await run(context);
23
- } catch (cause) {
24
- printError(cause);
29
+ } catch (error) {
30
+ return printError(error);
25
31
  }
26
32
  },
27
33
  };
28
34
  };
35
+
36
+ export { printError, withErrorBoundary };
@@ -1,17 +1,23 @@
1
1
  import { existsSync, readdirSync } from "node:fs";
2
2
  import { join, relative } from "node:path";
3
3
 
4
- export type ListedFile = {
4
+ type ListedFile = {
5
5
  absPath: string;
6
6
  rel: string;
7
7
  };
8
8
 
9
- export const listFilesRecursive = (dir: string, predicate?: (name: string) => boolean): ListedFile[] => {
10
- if (!existsSync(dir)) return [];
9
+ const listFilesRecursive = (dir: string, shouldInclude?: (name: string) => boolean): ListedFile[] => {
10
+ if (!existsSync(dir)) {
11
+ return [];
12
+ }
13
+
11
14
  return readdirSync(dir, { recursive: true, withFileTypes: true })
12
- .filter((entry) => entry.isFile() && (predicate === undefined || predicate(entry.name)))
15
+ .filter((entry) => entry.isFile() && (shouldInclude === undefined || shouldInclude(entry.name)))
13
16
  .map((entry) => {
14
17
  const absPath = join(entry.parentPath, entry.name);
18
+
15
19
  return { absPath, rel: relative(dir, absPath) };
16
20
  });
17
21
  };
22
+
23
+ export { listFilesRecursive, type ListedFile };
@@ -1,21 +1,37 @@
1
1
  import { existsSync, statSync } from "node:fs";
2
2
  import { delimiter, join } from "node:path";
3
3
 
4
- export const resolveCliTool = (() => {
5
- const cache = new Map<string, string>();
4
+ const resolveCliTool = (() => {
5
+ const cache: Map<string, string> = new Map();
6
6
 
7
7
  return (executable: string): string => {
8
8
  const cached = cache.get(executable);
9
- if (cached) return cached;
10
-
11
- const dirs = (process.env.PATH ?? "").split(delimiter).filter(Boolean);
12
- for (const dir of dirs) {
13
- const candidate = join(dir, executable);
14
- if (existsSync(candidate) && statSync(candidate).isFile()) {
15
- cache.set(executable, candidate);
16
- return candidate;
17
- }
9
+
10
+ if (cached) {
11
+ return cached;
18
12
  }
19
- throw new Error(`${executable} not found in PATH`);
13
+
14
+ const resolved = findInPath(executable);
15
+ cache.set(executable, resolved);
16
+
17
+ return resolved;
20
18
  };
21
19
  })();
20
+
21
+ const isExecutableFile = (candidate: string): boolean => existsSync(candidate) && statSync(candidate).isFile();
22
+
23
+ const findInPath = (executable: string): string => {
24
+ const dirs = (process.env.PATH ?? "").split(delimiter).filter(Boolean);
25
+
26
+ for (const dir of dirs) {
27
+ const candidate = join(dir, executable);
28
+
29
+ if (isExecutableFile(candidate)) {
30
+ return candidate;
31
+ }
32
+ }
33
+
34
+ throw new Error(`${executable} not found in PATH`);
35
+ };
36
+
37
+ export { resolveCliTool };
@@ -2,15 +2,18 @@ import { mkdtempSync, rmSync } from "node:fs";
2
2
  import { tmpdir } from "node:os";
3
3
  import { join } from "node:path";
4
4
 
5
- export const removeTempDir = (dir: string): void => {
5
+ const removeTempDir = (dir: string): void => {
6
6
  rmSync(dir, { recursive: true, force: true, maxRetries: 5 });
7
7
  };
8
8
 
9
- export const withStagingDir = <T>(prefix: string, produce: (dir: string) => T): T => {
9
+ const withStagingDir = <T>(prefix: string, produce: (dir: string) => T): T => {
10
10
  const dir = mkdtempSync(join(tmpdir(), `gtkx-${prefix}-`));
11
+
11
12
  try {
12
13
  return produce(dir);
13
14
  } finally {
14
15
  removeTempDir(dir);
15
16
  }
16
17
  };
18
+
19
+ export { removeTempDir, withStagingDir };
@@ -1,7 +1,13 @@
1
1
  const XDG_DATA_DIRS_DEFAULT = "/usr/local/share:/usr/share";
2
2
 
3
- export const prependXdgDataDir = (dir: string, existing: string | undefined): string => {
3
+ const prependXdgDataDir = (dir: string, existing: string | undefined): string => {
4
4
  const base = existing === undefined || existing.length === 0 ? XDG_DATA_DIRS_DEFAULT : existing;
5
- if (base.split(":").includes(dir)) return base;
5
+
6
+ if (base.split(":").includes(dir)) {
7
+ return base;
8
+ }
9
+
6
10
  return `${dir}:${base}`;
7
11
  };
12
+
13
+ export { prependXdgDataDir };