@gtkx/cli 0.20.0 → 1.0.0-rc.1

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 (341) hide show
  1. package/README.md +173 -0
  2. package/bin/gtkx-dev-runner.js +9 -0
  3. package/dist/builder.d.ts +2 -50
  4. package/dist/builder.d.ts.map +1 -1
  5. package/dist/builder.js +22 -48
  6. package/dist/builder.js.map +1 -1
  7. package/dist/cli.d.ts +1 -2
  8. package/dist/cli.d.ts.map +1 -1
  9. package/dist/cli.js +11 -124
  10. package/dist/cli.js.map +1 -1
  11. package/dist/codegen/freshness.d.ts +11 -0
  12. package/dist/codegen/freshness.d.ts.map +1 -0
  13. package/dist/codegen/freshness.js +42 -0
  14. package/dist/codegen/freshness.js.map +1 -0
  15. package/dist/codegen/report.d.ts +3 -0
  16. package/dist/codegen/report.d.ts.map +1 -0
  17. package/dist/codegen/report.js +16 -0
  18. package/dist/codegen/report.js.map +1 -0
  19. package/dist/codegen/run-codegen.d.ts +35 -0
  20. package/dist/codegen/run-codegen.d.ts.map +1 -0
  21. package/dist/codegen/run-codegen.js +133 -0
  22. package/dist/codegen/run-codegen.js.map +1 -0
  23. package/dist/codegen/store-resolver.d.ts +21 -0
  24. package/dist/codegen/store-resolver.d.ts.map +1 -0
  25. package/dist/codegen/store-resolver.js +52 -0
  26. package/dist/codegen/store-resolver.js.map +1 -0
  27. package/dist/commands/build.d.ts +9 -0
  28. package/dist/commands/build.d.ts.map +1 -0
  29. package/dist/commands/build.js +33 -0
  30. package/dist/commands/build.js.map +1 -0
  31. package/dist/commands/codegen.d.ts +9 -0
  32. package/dist/commands/codegen.d.ts.map +1 -0
  33. package/dist/commands/codegen.js +40 -0
  34. package/dist/commands/codegen.js.map +1 -0
  35. package/dist/commands/dev.d.ts +5 -0
  36. package/dist/commands/dev.d.ts.map +1 -0
  37. package/dist/commands/dev.js +21 -0
  38. package/dist/commands/dev.js.map +1 -0
  39. package/dist/commands/docs.d.ts +19 -0
  40. package/dist/commands/docs.d.ts.map +1 -0
  41. package/dist/commands/docs.js +59 -0
  42. package/dist/commands/docs.js.map +1 -0
  43. package/dist/dev/icon-dir.d.ts +2 -0
  44. package/dist/dev/icon-dir.d.ts.map +1 -0
  45. package/dist/dev/icon-dir.js +13 -0
  46. package/dist/dev/icon-dir.js.map +1 -0
  47. package/dist/dev/refresh-tracker.d.ts +6 -0
  48. package/dist/dev/refresh-tracker.d.ts.map +1 -0
  49. package/dist/dev/refresh-tracker.js +18 -0
  50. package/dist/dev/refresh-tracker.js.map +1 -0
  51. package/dist/dev/runner-deps.d.ts +3 -0
  52. package/dist/dev/runner-deps.d.ts.map +1 -0
  53. package/dist/dev/runner-deps.js +39 -0
  54. package/dist/dev/runner-deps.js.map +1 -0
  55. package/dist/dev/runner-main.d.ts +2 -0
  56. package/dist/dev/runner-main.d.ts.map +1 -0
  57. package/dist/dev/runner-main.js +28 -0
  58. package/dist/dev/runner-main.js.map +1 -0
  59. package/dist/dev/runner.d.ts +23 -0
  60. package/dist/dev/runner.d.ts.map +1 -0
  61. package/dist/dev/runner.js +88 -0
  62. package/dist/dev/runner.js.map +1 -0
  63. package/dist/dev/schema-dir.d.ts +2 -0
  64. package/dist/dev/schema-dir.d.ts.map +1 -0
  65. package/dist/dev/schema-dir.js +10 -0
  66. package/dist/dev/schema-dir.js.map +1 -0
  67. package/dist/dev/supervisor.d.ts +17 -0
  68. package/dist/dev/supervisor.d.ts.map +1 -0
  69. package/dist/dev/supervisor.js +142 -0
  70. package/dist/dev/supervisor.js.map +1 -0
  71. package/dist/dev/vite-dev-server.d.ts +21 -0
  72. package/dist/dev/vite-dev-server.d.ts.map +1 -0
  73. package/dist/dev/vite-dev-server.js +12 -0
  74. package/dist/dev/vite-dev-server.js.map +1 -0
  75. package/dist/internal/banner.d.ts +3 -0
  76. package/dist/internal/banner.d.ts.map +1 -0
  77. package/dist/internal/banner.js +8 -0
  78. package/dist/internal/banner.js.map +1 -0
  79. package/dist/internal/data-dir.d.ts +3 -0
  80. package/dist/internal/data-dir.d.ts.map +1 -0
  81. package/dist/internal/data-dir.js +37 -0
  82. package/dist/internal/data-dir.js.map +1 -0
  83. package/dist/internal/entry-arg.d.ts +19 -0
  84. package/dist/internal/entry-arg.d.ts.map +1 -0
  85. package/dist/internal/entry-arg.js +33 -0
  86. package/dist/internal/entry-arg.js.map +1 -0
  87. package/dist/internal/errors.d.ts +4 -0
  88. package/dist/internal/errors.d.ts.map +1 -0
  89. package/dist/internal/errors.js +27 -0
  90. package/dist/internal/errors.js.map +1 -0
  91. package/dist/internal/list-files.d.ts +6 -0
  92. package/dist/internal/list-files.d.ts.map +1 -0
  93. package/dist/internal/list-files.js +13 -0
  94. package/dist/internal/list-files.js.map +1 -0
  95. package/dist/internal/resolve-cli-tool.d.ts +2 -0
  96. package/dist/internal/resolve-cli-tool.d.ts.map +1 -0
  97. package/dist/internal/resolve-cli-tool.js +20 -0
  98. package/dist/internal/resolve-cli-tool.js.map +1 -0
  99. package/dist/internal/staging-dir.d.ts +3 -0
  100. package/dist/internal/staging-dir.d.ts.map +1 -0
  101. package/dist/internal/staging-dir.js +16 -0
  102. package/dist/internal/staging-dir.js.map +1 -0
  103. package/dist/internal/xdg-data-dirs.d.ts +2 -0
  104. package/dist/internal/xdg-data-dirs.d.ts.map +1 -0
  105. package/dist/internal/xdg-data-dirs.js +8 -0
  106. package/dist/internal/xdg-data-dirs.js.map +1 -0
  107. package/dist/mcp/client.d.ts +23 -0
  108. package/dist/mcp/client.d.ts.map +1 -0
  109. package/dist/mcp/client.js +157 -0
  110. package/dist/mcp/client.js.map +1 -0
  111. package/dist/mcp/handlers.d.ts +8 -0
  112. package/dist/mcp/handlers.d.ts.map +1 -0
  113. package/dist/mcp/handlers.js +119 -0
  114. package/dist/mcp/handlers.js.map +1 -0
  115. package/dist/mcp/index.d.ts +4 -0
  116. package/dist/mcp/index.d.ts.map +1 -0
  117. package/dist/mcp/index.js +17 -0
  118. package/dist/mcp/index.js.map +1 -0
  119. package/dist/mcp/serialize-widget.d.ts +10 -0
  120. package/dist/mcp/serialize-widget.d.ts.map +1 -0
  121. package/dist/mcp/serialize-widget.js +20 -0
  122. package/dist/mcp/serialize-widget.js.map +1 -0
  123. package/dist/mcp/testing-loader.d.ts +6 -0
  124. package/dist/mcp/testing-loader.d.ts.map +1 -0
  125. package/dist/mcp/testing-loader.js +25 -0
  126. package/dist/mcp/testing-loader.js.map +1 -0
  127. package/dist/mcp/widget-registry.d.ts +13 -0
  128. package/dist/mcp/widget-registry.d.ts.map +1 -0
  129. package/dist/mcp/widget-registry.js +38 -0
  130. package/dist/mcp/widget-registry.js.map +1 -0
  131. package/dist/refresh-runtime.d.ts +10 -20
  132. package/dist/refresh-runtime.d.ts.map +1 -1
  133. package/dist/refresh-runtime.js +12 -36
  134. package/dist/refresh-runtime.js.map +1 -1
  135. package/dist/settings/compile.d.ts +2 -0
  136. package/dist/settings/compile.d.ts.map +1 -0
  137. package/dist/settings/compile.js +19 -0
  138. package/dist/settings/compile.js.map +1 -0
  139. package/dist/settings/parser.d.ts +23 -0
  140. package/dist/settings/parser.d.ts.map +1 -0
  141. package/dist/settings/parser.js +117 -0
  142. package/dist/settings/parser.js.map +1 -0
  143. package/dist/settings/render.d.ts +4 -0
  144. package/dist/settings/render.d.ts.map +1 -0
  145. package/dist/settings/render.js +148 -0
  146. package/dist/settings/render.js.map +1 -0
  147. package/dist/settings/schema.d.ts +13 -0
  148. package/dist/settings/schema.d.ts.map +1 -0
  149. package/dist/settings/schema.js +103 -0
  150. package/dist/settings/schema.js.map +1 -0
  151. package/dist/vite-plugins/asset-extensions.d.ts +4 -0
  152. package/dist/vite-plugins/asset-extensions.d.ts.map +1 -0
  153. package/dist/vite-plugins/asset-extensions.js +27 -0
  154. package/dist/vite-plugins/asset-extensions.js.map +1 -0
  155. package/dist/vite-plugins/built-url.d.ts +3 -0
  156. package/dist/vite-plugins/built-url.d.ts.map +1 -0
  157. package/dist/{vite-plugin-gtkx-built-url.js → vite-plugins/built-url.js} +4 -17
  158. package/dist/vite-plugins/built-url.js.map +1 -0
  159. package/dist/vite-plugins/css.d.ts +3 -0
  160. package/dist/vite-plugins/css.d.ts.map +1 -0
  161. package/dist/vite-plugins/css.js +30 -0
  162. package/dist/vite-plugins/css.js.map +1 -0
  163. package/dist/vite-plugins/fast-refresh/refresh-filter.d.ts +7 -0
  164. package/dist/vite-plugins/fast-refresh/refresh-filter.d.ts.map +1 -0
  165. package/dist/vite-plugins/fast-refresh/refresh-filter.js +15 -0
  166. package/dist/vite-plugins/fast-refresh/refresh-filter.js.map +1 -0
  167. package/dist/vite-plugins/fast-refresh/swc-refresh.d.ts +5 -0
  168. package/dist/vite-plugins/fast-refresh/swc-refresh.d.ts.map +1 -0
  169. package/dist/vite-plugins/fast-refresh/swc-refresh.js +66 -0
  170. package/dist/vite-plugins/fast-refresh/swc-refresh.js.map +1 -0
  171. package/dist/vite-plugins/icons.d.ts +3 -0
  172. package/dist/vite-plugins/icons.d.ts.map +1 -0
  173. package/dist/vite-plugins/icons.js +47 -0
  174. package/dist/vite-plugins/icons.js.map +1 -0
  175. package/dist/vite-plugins/index.d.ts +3 -0
  176. package/dist/vite-plugins/index.d.ts.map +1 -0
  177. package/dist/vite-plugins/index.js +21 -0
  178. package/dist/vite-plugins/index.js.map +1 -0
  179. package/dist/vite-plugins/native.d.ts +3 -0
  180. package/dist/vite-plugins/native.d.ts.map +1 -0
  181. package/dist/vite-plugins/native.js +60 -0
  182. package/dist/vite-plugins/native.js.map +1 -0
  183. package/dist/vite-plugins/react-compiler.d.ts +4 -0
  184. package/dist/vite-plugins/react-compiler.d.ts.map +1 -0
  185. package/dist/vite-plugins/react-compiler.js +56 -0
  186. package/dist/vite-plugins/react-compiler.js.map +1 -0
  187. package/dist/vite-plugins/react-dom-prebundle.d.ts +3 -0
  188. package/dist/vite-plugins/react-dom-prebundle.d.ts.map +1 -0
  189. package/dist/vite-plugins/react-dom-prebundle.js +11 -0
  190. package/dist/vite-plugins/react-dom-prebundle.js.map +1 -0
  191. package/dist/vite-plugins/resource-init-module.d.ts +5 -0
  192. package/dist/vite-plugins/resource-init-module.d.ts.map +1 -0
  193. package/dist/vite-plugins/resource-init-module.js +50 -0
  194. package/dist/vite-plugins/resource-init-module.js.map +1 -0
  195. package/dist/vite-plugins/resource-shared.d.ts +6 -0
  196. package/dist/vite-plugins/resource-shared.d.ts.map +1 -0
  197. package/dist/vite-plugins/resource-shared.js +21 -0
  198. package/dist/vite-plugins/resource-shared.js.map +1 -0
  199. package/dist/vite-plugins/resources.d.ts +4 -0
  200. package/dist/vite-plugins/resources.d.ts.map +1 -0
  201. package/dist/vite-plugins/resources.js +220 -0
  202. package/dist/vite-plugins/resources.js.map +1 -0
  203. package/dist/vite-plugins/settings-worker-env.d.ts +3 -0
  204. package/dist/vite-plugins/settings-worker-env.d.ts.map +1 -0
  205. package/dist/vite-plugins/settings-worker-env.js +21 -0
  206. package/dist/vite-plugins/settings-worker-env.js.map +1 -0
  207. package/dist/vite-plugins/settings.d.ts +3 -0
  208. package/dist/vite-plugins/settings.d.ts.map +1 -0
  209. package/dist/vite-plugins/settings.js +190 -0
  210. package/dist/vite-plugins/settings.js.map +1 -0
  211. package/dist/vite-plugins/undeclared-library.d.ts +3 -0
  212. package/dist/vite-plugins/undeclared-library.d.ts.map +1 -0
  213. package/dist/vite-plugins/undeclared-library.js +47 -0
  214. package/dist/vite-plugins/undeclared-library.js.map +1 -0
  215. package/dist/vite-plugins/virtual-module.d.ts +22 -0
  216. package/dist/vite-plugins/virtual-module.d.ts.map +1 -0
  217. package/dist/vite-plugins/virtual-module.js +18 -0
  218. package/dist/vite-plugins/virtual-module.js.map +1 -0
  219. package/dist/vitest-plugin.d.ts +10 -0
  220. package/dist/vitest-plugin.d.ts.map +1 -0
  221. package/dist/vitest-plugin.js +12 -0
  222. package/dist/vitest-plugin.js.map +1 -0
  223. package/env.d.ts +131 -3
  224. package/package.json +32 -38
  225. package/src/babel-modules.d.ts +13 -0
  226. package/src/builder.ts +28 -76
  227. package/src/cli.ts +22 -0
  228. package/src/codegen/freshness.ts +52 -0
  229. package/src/codegen/report.ts +20 -0
  230. package/src/codegen/run-codegen.ts +185 -0
  231. package/src/codegen/store-resolver.ts +75 -0
  232. package/src/commands/build.ts +37 -0
  233. package/src/commands/codegen.ts +44 -0
  234. package/src/commands/dev.ts +25 -0
  235. package/src/commands/docs.ts +66 -0
  236. package/src/dev/icon-dir.ts +11 -0
  237. package/src/dev/refresh-tracker.ts +21 -0
  238. package/src/dev/runner-deps.ts +42 -0
  239. package/src/dev/runner-main.ts +33 -0
  240. package/src/dev/runner.ts +126 -0
  241. package/src/dev/schema-dir.ts +11 -0
  242. package/src/dev/supervisor.ts +178 -0
  243. package/src/dev/vite-dev-server.ts +34 -0
  244. package/src/internal/banner.ts +9 -0
  245. package/src/internal/data-dir.ts +39 -0
  246. package/src/internal/entry-arg.ts +38 -0
  247. package/src/internal/errors.ts +28 -0
  248. package/src/internal/list-files.ts +17 -0
  249. package/src/internal/resolve-cli-tool.ts +21 -0
  250. package/src/internal/staging-dir.ts +16 -0
  251. package/src/internal/xdg-data-dirs.ts +7 -0
  252. package/src/mcp/client.ts +177 -0
  253. package/src/mcp/handlers.ts +156 -0
  254. package/src/mcp/index.ts +22 -0
  255. package/src/mcp/serialize-widget.ts +34 -0
  256. package/src/mcp/testing-loader.ts +32 -0
  257. package/src/mcp/widget-registry.ts +43 -0
  258. package/src/refresh-runtime.ts +12 -41
  259. package/src/settings/compile.ts +19 -0
  260. package/src/settings/parser.ts +154 -0
  261. package/src/settings/render.ts +167 -0
  262. package/src/settings/schema.ts +115 -0
  263. package/src/vite-plugins/asset-extensions.ts +28 -0
  264. package/src/{vite-plugin-gtkx-built-url.ts → vite-plugins/built-url.ts} +3 -16
  265. package/src/vite-plugins/css.ts +38 -0
  266. package/src/vite-plugins/fast-refresh/refresh-filter.ts +18 -0
  267. package/src/vite-plugins/fast-refresh/swc-refresh.ts +79 -0
  268. package/src/vite-plugins/icons.ts +58 -0
  269. package/src/vite-plugins/index.ts +22 -0
  270. package/src/vite-plugins/native.ts +74 -0
  271. package/src/vite-plugins/react-compiler.ts +70 -0
  272. package/src/vite-plugins/react-dom-prebundle.ts +14 -0
  273. package/src/vite-plugins/resource-init-module.ts +54 -0
  274. package/src/vite-plugins/resource-shared.ts +27 -0
  275. package/src/vite-plugins/resources.ts +272 -0
  276. package/src/vite-plugins/settings-worker-env.ts +25 -0
  277. package/src/vite-plugins/settings.ts +221 -0
  278. package/src/vite-plugins/undeclared-library.ts +69 -0
  279. package/src/vite-plugins/virtual-module.ts +37 -0
  280. package/src/vitest-plugin.ts +14 -0
  281. package/dist/create.d.ts +0 -54
  282. package/dist/create.d.ts.map +0 -1
  283. package/dist/create.js +0 -221
  284. package/dist/create.js.map +0 -1
  285. package/dist/dev-server.d.ts +0 -38
  286. package/dist/dev-server.d.ts.map +0 -1
  287. package/dist/dev-server.js +0 -128
  288. package/dist/dev-server.js.map +0 -1
  289. package/dist/index.d.ts +0 -4
  290. package/dist/index.d.ts.map +0 -1
  291. package/dist/index.js +0 -4
  292. package/dist/index.js.map +0 -1
  293. package/dist/mcp-client.d.ts +0 -31
  294. package/dist/mcp-client.d.ts.map +0 -1
  295. package/dist/mcp-client.js +0 -430
  296. package/dist/mcp-client.js.map +0 -1
  297. package/dist/templates.d.ts +0 -9
  298. package/dist/templates.d.ts.map +0 -1
  299. package/dist/templates.js +0 -16
  300. package/dist/templates.js.map +0 -1
  301. package/dist/vite-plugin-gtkx-assets.d.ts +0 -20
  302. package/dist/vite-plugin-gtkx-assets.d.ts.map +0 -1
  303. package/dist/vite-plugin-gtkx-assets.js +0 -57
  304. package/dist/vite-plugin-gtkx-assets.js.map +0 -1
  305. package/dist/vite-plugin-gtkx-built-url.d.ts +0 -18
  306. package/dist/vite-plugin-gtkx-built-url.d.ts.map +0 -1
  307. package/dist/vite-plugin-gtkx-built-url.js.map +0 -1
  308. package/dist/vite-plugin-gtkx-native.d.ts +0 -14
  309. package/dist/vite-plugin-gtkx-native.d.ts.map +0 -1
  310. package/dist/vite-plugin-gtkx-native.js +0 -53
  311. package/dist/vite-plugin-gtkx-native.js.map +0 -1
  312. package/dist/vite-plugin-gtkx-refresh.d.ts +0 -8
  313. package/dist/vite-plugin-gtkx-refresh.d.ts.map +0 -1
  314. package/dist/vite-plugin-gtkx-refresh.js +0 -37
  315. package/dist/vite-plugin-gtkx-refresh.js.map +0 -1
  316. package/dist/vite-plugin-swc-ssr-refresh.d.ts +0 -8
  317. package/dist/vite-plugin-swc-ssr-refresh.d.ts.map +0 -1
  318. package/dist/vite-plugin-swc-ssr-refresh.js +0 -46
  319. package/dist/vite-plugin-swc-ssr-refresh.js.map +0 -1
  320. package/src/cli.tsx +0 -154
  321. package/src/create.ts +0 -310
  322. package/src/dev-server.tsx +0 -162
  323. package/src/index.ts +0 -3
  324. package/src/mcp-client.ts +0 -518
  325. package/src/templates.ts +0 -26
  326. package/src/vite-plugin-gtkx-assets.ts +0 -69
  327. package/src/vite-plugin-gtkx-native.ts +0 -64
  328. package/src/vite-plugin-gtkx-refresh.ts +0 -54
  329. package/src/vite-plugin-swc-ssr-refresh.ts +0 -61
  330. package/templates/claude/EXAMPLES.md.ejs +0 -744
  331. package/templates/claude/SKILL.md.ejs +0 -93
  332. package/templates/claude/WIDGETS.md.ejs +0 -820
  333. package/templates/config/vitest.config.ts.ejs +0 -11
  334. package/templates/gitignore.ejs +0 -4
  335. package/templates/package.json.ejs +0 -16
  336. package/templates/src/app.tsx.ejs +0 -32
  337. package/templates/src/dev.tsx.ejs +0 -5
  338. package/templates/src/gtkx-env.d.ts.ejs +0 -1
  339. package/templates/src/index.tsx.ejs +0 -5
  340. package/templates/tests/app.test.tsx.ejs +0 -16
  341. package/templates/tsconfig.json.ejs +0 -14
package/src/builder.ts CHANGED
@@ -1,94 +1,46 @@
1
- import { type InlineConfig, build as viteBuild } from "vite";
2
- import { gtkxAssets } from "./vite-plugin-gtkx-assets.js";
3
- import { gtkxBuiltUrl } from "./vite-plugin-gtkx-built-url.js";
4
- import { gtkxNative } from "./vite-plugin-gtkx-native.js";
1
+ import { type InlineConfig, mergeConfig, build as viteBuild } from "vite";
2
+ import { gtkxBuiltUrl } from "./vite-plugins/built-url.js";
3
+ import { gtkxVitePlugins } from "./vite-plugins/index.js";
4
+ import { gtkxNative } from "./vite-plugins/native.js";
5
+
6
+ const BUILD_MODE = "production";
5
7
 
6
- /**
7
- * Options for building a GTKX application for production.
8
- */
9
8
  export type BuildOptions = {
10
- /** Path to the entry file (e.g., "src/index.tsx") */
11
9
  entry: string;
12
- /**
13
- * Base path for resolving asset imports at runtime, relative to the
14
- * executable directory.
15
- *
16
- * When set, asset imports resolve to
17
- * `path.join(path.dirname(process.execPath), assetBase, filename)`.
18
- * This is useful for FHS-compliant packaging where assets live under
19
- * a `share/` directory rather than next to the binary.
20
- *
21
- * When omitted, assets resolve relative to the bundle via
22
- * `import.meta.url`, which works when assets are co-located with
23
- * the executable (e.g., in `bin/assets/`).
24
- *
25
- * @example
26
- * ```ts
27
- * await build({
28
- * entry: "./src/index.tsx",
29
- * assetBase: "../share/my-app",
30
- * });
31
- * ```
32
- */
33
- assetBase?: string;
34
- /** Additional Vite configuration */
35
- vite?: InlineConfig;
10
+ assetBase?: string | undefined;
11
+ vite?: InlineConfig | undefined;
12
+ };
13
+
14
+ const buildDefaults: InlineConfig = {
15
+ build: {
16
+ ssrEmitAssets: true,
17
+ outDir: "dist",
18
+ minify: true,
19
+ cssMinify: false,
20
+ },
21
+ define: {
22
+ "process.env.NODE_ENV": JSON.stringify(BUILD_MODE),
23
+ },
36
24
  };
37
25
 
38
- /**
39
- * Builds a GTKX application for production using Vite's SSR build mode.
40
- *
41
- * Produces a single minified ESM bundle at `dist/bundle.js` with all
42
- * dependencies inlined. The native `.node` binary is copied into the
43
- * output directory as `gtkx.node`, making the bundle fully self-contained
44
- * with no `node_modules` dependency at runtime.
45
- *
46
- * @param options - Build configuration including entry point and Vite options
47
- *
48
- * @example
49
- * ```ts
50
- * import { build } from "@gtkx/cli";
51
- *
52
- * await build({
53
- * entry: "./src/index.tsx",
54
- * vite: { root: process.cwd() },
55
- * });
56
- * ```
57
- *
58
- * @see {@link BuildOptions} for configuration options
59
- */
60
26
  export const build = async (options: BuildOptions): Promise<void> => {
61
27
  const { entry, assetBase, vite: viteConfig } = options;
62
28
  const root = viteConfig?.root ?? process.cwd();
63
29
 
64
- await viteBuild({
65
- ...viteConfig,
66
- plugins: [...(viteConfig?.plugins ?? []), gtkxAssets(), gtkxBuiltUrl(assetBase), gtkxNative(root)],
30
+ const forced: InlineConfig = {
31
+ plugins: [...gtkxVitePlugins(BUILD_MODE), gtkxBuiltUrl(assetBase), gtkxNative(root)],
67
32
  build: {
68
- ...viteConfig?.build,
69
33
  ssr: entry,
70
- ssrEmitAssets: true,
71
34
  assetsInlineLimit: 0,
72
- outDir: viteConfig?.build?.outDir ?? "dist",
73
- minify: true,
74
- rollupOptions: {
75
- ...viteConfig?.build?.rollupOptions,
35
+ rolldownOptions: {
76
36
  output: {
77
- ...((viteConfig?.build?.rollupOptions?.output ?? {}) as Record<string, unknown>),
78
37
  entryFileNames: "bundle.js",
79
38
  },
80
39
  },
81
40
  },
82
- define: {
83
- ...viteConfig?.define,
84
- "process.env.NODE_ENV": JSON.stringify("production"),
85
- },
86
- ssr: {
87
- ...viteConfig?.ssr,
88
- noExternal: true,
89
- },
90
- experimental: {
91
- ...viteConfig?.experimental,
92
- },
93
- });
41
+ };
42
+
43
+ const merged = mergeConfig(mergeConfig(buildDefaults, viteConfig ?? {}), forced);
44
+
45
+ await viteBuild({ ...merged, ssr: { ...merged.ssr, noExternal: true } });
94
46
  };
package/src/cli.ts ADDED
@@ -0,0 +1,22 @@
1
+ import { packageVersion } from "@gtkx/utils";
2
+ import { defineCommand, runMain } from "citty";
3
+ import { withErrorBoundary } from "./internal/errors.js";
4
+
5
+ const version = packageVersion(import.meta.url);
6
+
7
+ export const main = defineCommand({
8
+ meta: {
9
+ name: "gtkx",
10
+ version,
11
+ description: "CLI for GTKX: create and develop GTK4 React applications",
12
+ },
13
+ subCommands: {
14
+ dev: () => import("./commands/dev.js").then((m) => withErrorBoundary(m.dev)),
15
+ build: () => import("./commands/build.js").then((m) => withErrorBoundary(m.build)),
16
+ codegen: () => import("./commands/codegen.js").then((m) => withErrorBoundary(m.codegen)),
17
+ docs: () => import("./commands/docs.js").then((m) => withErrorBoundary(m.docs)),
18
+ create: () => import("create-gtkx").then((m) => withErrorBoundary(m.createCommand)),
19
+ },
20
+ });
21
+
22
+ await runMain(main);
@@ -0,0 +1,52 @@
1
+ import { existsSync } from "node:fs";
2
+ import { join } from "node:path";
3
+ import { resolveGirPath, resolveLibraries } from "@gtkx/codegen";
4
+ import type { Config, ElementProp } from "@gtkx/config";
5
+ import { type CodegenStore, resolveCodegenStore } from "./store-resolver.js";
6
+
7
+ export type CodegenInputs = {
8
+ girPath: string[];
9
+ libraries: string[];
10
+ elementProps: Record<string, ElementProp[]>;
11
+ store: CodegenStore;
12
+ };
13
+
14
+ export const resolveCodegenInputs = (cwd: string, config: Config): CodegenInputs => {
15
+ const girPath = resolveGirPath(config.girPath);
16
+ const libraries = resolveLibraries(config.libraries, girPath);
17
+ const store = resolveCodegenStore(cwd);
18
+ return { girPath, libraries, elementProps: config.elementProps ?? {}, store };
19
+ };
20
+
21
+ const REACT_GENERATED_MODULES: string[] = ["metadata.js", join("gtk", "gtk.js")];
22
+
23
+ const namespaceBarrelPath = (giStoreDir: string, library: string): string => {
24
+ const separator = library.indexOf("-");
25
+ const namespace = (separator === -1 ? library : library.slice(0, separator)).toLowerCase();
26
+ return join(giStoreDir, namespace, "index.js");
27
+ };
28
+
29
+ const giStoreLinksResolve = (giStoreDir: string): boolean =>
30
+ existsSync(join(giStoreDir, "node_modules", "@gtkx", "gi", "package.json"));
31
+
32
+ export const isCodegenStale = (inputs: CodegenInputs): boolean => {
33
+ try {
34
+ const { store, libraries } = inputs;
35
+ if (!existsSync(store.giLinkDir) || !existsSync(store.giStoreDir)) {
36
+ return true;
37
+ }
38
+ if (!giStoreLinksResolve(store.giStoreDir)) {
39
+ return true;
40
+ }
41
+ if (libraries.some((library) => !existsSync(namespaceBarrelPath(store.giStoreDir, library)))) {
42
+ return true;
43
+ }
44
+ if (store.react !== null) {
45
+ if (!existsSync(store.jsxLinkDir)) return true;
46
+ if (REACT_GENERATED_MODULES.some((module) => !existsSync(join(store.jsxStoreDir, module)))) return true;
47
+ }
48
+ return false;
49
+ } catch {
50
+ return true;
51
+ }
52
+ };
@@ -0,0 +1,20 @@
1
+ import type { RunCodegenResult } from "./run-codegen.js";
2
+
3
+ const PREFIX = "codegen: ";
4
+
5
+ export const formatCodegenResult = (result: RunCodegenResult, totalMs: number): string[] => {
6
+ const details: string[] = [];
7
+ if (result.configFile) {
8
+ details.push(`config=${result.configFile}`);
9
+ }
10
+ if (result.libraries) {
11
+ details.push(`libraries=${result.libraries.join(", ")}`);
12
+ }
13
+ if (result.girPath) {
14
+ details.push(`girPath=${result.girPath.join(":")}`);
15
+ }
16
+ details.push(
17
+ `${result.namespaces} namespaces, ${result.intrinsicElements} intrinsic elements in ${result.duration}ms (total ${totalMs}ms)`,
18
+ );
19
+ return details.map((detail) => PREFIX + detail);
20
+ };
@@ -0,0 +1,185 @@
1
+ import { rmSync } from "node:fs";
2
+ import { resolve } from "node:path";
3
+ import { runCodegen as runCodegenCore } from "@gtkx/codegen";
4
+ import { type Config, type ElementProp, loadConfig } from "@gtkx/config";
5
+ import { info } from "@gtkx/utils";
6
+ import { resolveDataDir } from "../internal/data-dir.js";
7
+ import { emitSchemaEnv } from "../settings/schema.js";
8
+ import { type CodegenInputs, isCodegenStale, resolveCodegenInputs } from "./freshness.js";
9
+ import { type CodegenStore, resolveCodegenContext } from "./store-resolver.js";
10
+
11
+ export type RunCodegenOptions = {
12
+ cwd?: string;
13
+ mode?: string | undefined;
14
+ force?: boolean;
15
+ inputs?: CodegenInputs;
16
+ resolved?: LoadedConfig;
17
+ };
18
+
19
+ type LoadedConfig = {
20
+ config: Config;
21
+ configFile: string | undefined;
22
+ };
23
+
24
+ export type RunCodegenResult = {
25
+ regenerated: boolean;
26
+ namespaces: number;
27
+ intrinsicElements: number;
28
+ duration: number;
29
+ girPath?: string[] | undefined;
30
+ configFile?: string | undefined;
31
+ libraries?: string[] | undefined;
32
+ };
33
+
34
+ const removeSharedStoreShadow = (cwd: string): void => {
35
+ for (const path of [
36
+ resolve(cwd, "node_modules/.gtkx/gi"),
37
+ resolve(cwd, "node_modules/.gtkx/jsx"),
38
+ resolve(cwd, "node_modules/@gtkx/gi"),
39
+ resolve(cwd, "node_modules/@gtkx/jsx"),
40
+ ]) {
41
+ rmSync(path, { recursive: true, force: true });
42
+ }
43
+ };
44
+
45
+ type CodegenOptionsInput = {
46
+ store: CodegenStore;
47
+ libraries: string[];
48
+ girPath: string[];
49
+ elementProps: Record<string, ElementProp[]>;
50
+ };
51
+
52
+ const codegenOptions = ({ store, libraries, girPath, elementProps }: CodegenOptionsInput) => ({
53
+ libraries,
54
+ girPath,
55
+ elementProps,
56
+ gi: {
57
+ storeDir: store.giStoreDir,
58
+ linkDir: store.giLinkDir,
59
+ version: store.runtimeVersion,
60
+ },
61
+ jsx:
62
+ store.react !== null
63
+ ? {
64
+ storeDir: store.jsxStoreDir,
65
+ linkDir: store.jsxLinkDir,
66
+ version: store.react.version,
67
+ }
68
+ : undefined,
69
+ });
70
+
71
+ export const runCodegen = async (options: RunCodegenOptions = {}): Promise<RunCodegenResult> => {
72
+ const cwd = options.cwd ?? process.cwd();
73
+
74
+ const { config, configFile } = options.resolved ?? (await loadConfig(cwd, { mode: options.mode }));
75
+
76
+ if (config.codegen === false) {
77
+ removeSharedStoreShadow(cwd);
78
+ return {
79
+ regenerated: false,
80
+ namespaces: 0,
81
+ intrinsicElements: 0,
82
+ duration: 0,
83
+ girPath: [],
84
+ configFile,
85
+ libraries: [],
86
+ };
87
+ }
88
+
89
+ const { girPath, libraries, elementProps, store } = options.inputs ?? resolveCodegenInputs(cwd, config);
90
+
91
+ if (girPath.length === 0) {
92
+ throw new Error(
93
+ "No GIR search paths available. Install gobject-introspection (Linux: `sudo dnf install gobject-introspection-devel` or `sudo apt install libgirepository1.0-dev`), or set `girPath` in gtkx.config.ts.",
94
+ );
95
+ }
96
+
97
+ const force = options.force === true || isCodegenStale({ girPath, libraries, elementProps, store });
98
+ if (options.force) {
99
+ for (const path of [store.giStoreDir, store.giLinkDir, store.jsxStoreDir, store.jsxLinkDir]) {
100
+ rmSync(path, { recursive: true, force: true });
101
+ }
102
+ }
103
+
104
+ const result = await runCodegenCore({
105
+ ...codegenOptions({ store, libraries, girPath, elementProps }),
106
+ force,
107
+ });
108
+
109
+ return {
110
+ regenerated: result.regenerated,
111
+ namespaces: result.namespaces,
112
+ intrinsicElements: result.intrinsicElements,
113
+ duration: result.duration,
114
+ girPath,
115
+ configFile,
116
+ libraries,
117
+ };
118
+ };
119
+
120
+ export const isCodegenDisabled = async (cwd: string, mode?: string): Promise<boolean> => {
121
+ try {
122
+ const { config } = await loadConfig(cwd, { mode });
123
+ return config.codegen === false;
124
+ } catch {
125
+ return false;
126
+ }
127
+ };
128
+
129
+ export const syncSchemaEnv = (cwd: string): void => {
130
+ const dataDir = resolveDataDir(cwd);
131
+ if (dataDir === null) return;
132
+ emitSchemaEnv(cwd, dataDir);
133
+ };
134
+
135
+ const resolveInputsOrNull = (cwd: string, config: Config): CodegenInputs | null => {
136
+ try {
137
+ return resolveCodegenInputs(cwd, config);
138
+ } catch {
139
+ return null;
140
+ }
141
+ };
142
+
143
+ export const ensureGenerated = async (
144
+ cwd: string,
145
+ options: { announce?: boolean; mode?: string } = {},
146
+ ): Promise<boolean> => {
147
+ if (options.announce && process.env.GTKX_DISABLE_PREFLIGHT === "1") {
148
+ return false;
149
+ }
150
+
151
+ const context = await resolveCodegenContext(cwd, options.mode);
152
+ if (!context) {
153
+ return false;
154
+ }
155
+ syncSchemaEnv(cwd);
156
+ if (context.config.codegen === false) {
157
+ removeSharedStoreShadow(context.root);
158
+ return false;
159
+ }
160
+ const inputs = resolveInputsOrNull(context.root, context.config);
161
+ if (options.announce && (inputs === null || isCodegenStale(inputs))) {
162
+ info("generated bindings missing; running codegen...");
163
+ }
164
+ const resolved = { config: context.config, configFile: context.configFile };
165
+ const result = await runCodegen(
166
+ inputs === null
167
+ ? { cwd: context.root, mode: options.mode, resolved }
168
+ : { cwd: context.root, mode: options.mode, inputs, resolved },
169
+ );
170
+ return result.regenerated;
171
+ };
172
+
173
+ export const resolveConfigWatch = async (
174
+ cwd: string,
175
+ mode?: string,
176
+ ): Promise<{ paths: string[]; regenerate: () => Promise<void> } | undefined> => {
177
+ const { configFile, root } = await loadConfig(cwd, { mode });
178
+ if (configFile === undefined) return undefined;
179
+ return {
180
+ paths: [resolve(root, configFile)],
181
+ regenerate: async () => {
182
+ await runCodegen({ cwd: root, mode });
183
+ },
184
+ };
185
+ };
@@ -0,0 +1,75 @@
1
+ import { existsSync, readFileSync, realpathSync } from "node:fs";
2
+ import { createRequire } from "node:module";
3
+ import { dirname, join } from "node:path";
4
+ import { pathToFileURL } from "node:url";
5
+ import { type Config, loadConfig } from "@gtkx/config";
6
+
7
+ export type CodegenStore = {
8
+ giStoreDir: string;
9
+ giLinkDir: string;
10
+ jsxStoreDir: string;
11
+ jsxLinkDir: string;
12
+ runtimeVersion: string;
13
+ react: CodegenReactPackage | null;
14
+ };
15
+
16
+ type CodegenReactPackage = {
17
+ version: string;
18
+ };
19
+
20
+ type ResolvedPackage = { dir: string; version: string };
21
+
22
+ const readVersion = (packageJsonPath: string): string => {
23
+ const parsed = JSON.parse(readFileSync(packageJsonPath, "utf8")) as { version?: string };
24
+ return parsed.version ?? "0.0.0";
25
+ };
26
+
27
+ const resolvePackage = (require: NodeJS.Require, dir: string, packageName: string): ResolvedPackage | null => {
28
+ try {
29
+ const real = realpathSync(require.resolve(`${packageName}/package.json`));
30
+ return { dir: dirname(real), version: readVersion(real) };
31
+ } catch {
32
+ const unscoped = packageName.replace(/^@[^/]+\//, "");
33
+ const workspacePkg = join(dir, "packages", unscoped, "package.json");
34
+ if (!existsSync(workspacePkg)) return null;
35
+ const real = realpathSync(workspacePkg);
36
+ return { dir: dirname(real), version: readVersion(real) };
37
+ }
38
+ };
39
+
40
+ export const resolveCodegenStore = (dir: string): CodegenStore => {
41
+ const require = createRequire(pathToFileURL(join(dir, "__gtkx_resolver__.js")).href);
42
+
43
+ const runtime = resolvePackage(require, dir, "@gtkx/runtime");
44
+ if (runtime === null) {
45
+ throw new Error("Cannot resolve @gtkx/runtime from the project; is it installed?");
46
+ }
47
+ const native = resolvePackage(require, dir, "@gtkx/native");
48
+ if (native === null) {
49
+ throw new Error("Cannot resolve @gtkx/native from the project; is it installed?");
50
+ }
51
+ const react = resolvePackage(require, dir, "@gtkx/react");
52
+ const reactRuntime = resolvePackage(require, dir, "react");
53
+
54
+ const nodeModules = join(dir, "node_modules");
55
+ return {
56
+ giStoreDir: join(nodeModules, ".gtkx", "gi"),
57
+ giLinkDir: join(nodeModules, "@gtkx", "gi"),
58
+ jsxStoreDir: join(nodeModules, ".gtkx", "jsx"),
59
+ jsxLinkDir: join(nodeModules, "@gtkx", "jsx"),
60
+ runtimeVersion: runtime.version,
61
+ react: react !== null && reactRuntime !== null ? { version: react.version } : null,
62
+ };
63
+ };
64
+
65
+ export type CodegenContext = {
66
+ root: string;
67
+ config: Config;
68
+ configFile: string | undefined;
69
+ };
70
+
71
+ export const resolveCodegenContext = async (cwd: string, mode?: string): Promise<CodegenContext | null> => {
72
+ const { config, configFile } = await loadConfig(cwd, { mode });
73
+ if (configFile === undefined) return null;
74
+ return { root: cwd, config, configFile };
75
+ };
@@ -0,0 +1,37 @@
1
+ import { info } from "@gtkx/utils";
2
+ import { defineCommand } from "citty";
3
+ import { build as buildApp } from "../builder.js";
4
+ import { ensureGenerated } from "../codegen/run-codegen.js";
5
+ import { entryArg, resolveEntry } from "../internal/entry-arg.js";
6
+
7
+ const BUILD_MODE = "production";
8
+
9
+ export const build = defineCommand({
10
+ meta: {
11
+ name: "build",
12
+ description: "Build application for production",
13
+ },
14
+ args: {
15
+ ...entryArg,
16
+ "asset-base": {
17
+ type: "string",
18
+ description: "Asset base path relative to executable directory (e.g., ../share/my-app)",
19
+ },
20
+ },
21
+ async run({ args }) {
22
+ const { cwd, entry } = resolveEntry(args);
23
+ info(`Building ${entry}`);
24
+
25
+ await ensureGenerated(cwd, { announce: true, mode: BUILD_MODE });
26
+
27
+ await buildApp({
28
+ entry,
29
+ assetBase: args["asset-base"],
30
+ vite: {
31
+ root: cwd,
32
+ },
33
+ });
34
+
35
+ info("Build complete: dist/bundle.js");
36
+ },
37
+ });
@@ -0,0 +1,44 @@
1
+ import { info } from "@gtkx/utils";
2
+ import { defineCommand } from "citty";
3
+ import { formatCodegenResult } from "../codegen/report.js";
4
+ import { ensureGenerated, isCodegenDisabled, runCodegen, syncSchemaEnv } from "../codegen/run-codegen.js";
5
+ import { cwdArg, resolveCwd } from "../internal/entry-arg.js";
6
+
7
+ export const codegen = defineCommand({
8
+ meta: {
9
+ name: "codegen",
10
+ description: "Generate TypeScript bindings for the GIR libraries declared in gtkx.config.ts",
11
+ },
12
+ args: {
13
+ force: {
14
+ type: "boolean",
15
+ description: "Wipe the generated store and regenerate unconditionally (recover a corrupted store)",
16
+ default: false,
17
+ },
18
+ ...cwdArg,
19
+ },
20
+ async run({ args }) {
21
+ const cwd = resolveCwd(args);
22
+
23
+ if (await isCodegenDisabled(cwd)) {
24
+ await runCodegen({ cwd });
25
+ syncSchemaEnv(cwd);
26
+ info("codegen: disabled for this project; reusing an installed binding store");
27
+ return;
28
+ }
29
+
30
+ if (!args.force) {
31
+ const ran = await ensureGenerated(cwd);
32
+ info(ran ? "codegen: regenerated stale bindings" : "codegen: bindings up to date");
33
+ return;
34
+ }
35
+
36
+ const startedAt = Date.now();
37
+ const result = await runCodegen({ cwd, force: true });
38
+ syncSchemaEnv(cwd);
39
+
40
+ for (const line of formatCodegenResult(result, Date.now() - startedAt)) {
41
+ info(line);
42
+ }
43
+ },
44
+ });
@@ -0,0 +1,25 @@
1
+ import { defineCommand } from "citty";
2
+ import { ensureGenerated, resolveConfigWatch } from "../codegen/run-codegen.js";
3
+ import { type DevWatch, runDevSupervisor } from "../dev/supervisor.js";
4
+ import { entryArg, resolveEntry } from "../internal/entry-arg.js";
5
+
6
+ const DEV_MODE = "development";
7
+
8
+ export const dev = defineCommand({
9
+ meta: {
10
+ name: "dev",
11
+ description: "Start development server with HMR",
12
+ },
13
+ args: {
14
+ ...entryArg,
15
+ },
16
+ async run({ args }) {
17
+ const { cwd, entry: entryPath } = resolveEntry(args);
18
+
19
+ await ensureGenerated(cwd, { announce: true, mode: DEV_MODE });
20
+
21
+ const watch: DevWatch | undefined = await resolveConfigWatch(cwd, DEV_MODE);
22
+
23
+ await runDevSupervisor(entryPath, cwd, watch);
24
+ },
25
+ });
@@ -0,0 +1,66 @@
1
+ import { resolve } from "node:path";
2
+ import { resolveGirPath, resolveLibraries, writeDocs } from "@gtkx/codegen";
3
+ import { loadConfig } from "@gtkx/config";
4
+ import { info } from "@gtkx/utils";
5
+ import { defineCommand } from "citty";
6
+ import { cwdArg, resolveCwd } from "../internal/entry-arg.js";
7
+
8
+ export const docs = defineCommand({
9
+ meta: {
10
+ name: "docs",
11
+ description:
12
+ "Generate markdown reference pages for the JSX elements of the GIR libraries declared in gtkx.config.ts",
13
+ },
14
+ args: {
15
+ out: {
16
+ type: "string",
17
+ description: "Output directory for the generated markdown pages, relative to the project root",
18
+ default: "docs/reference",
19
+ },
20
+ "base-path": {
21
+ type: "string",
22
+ description: "URL base path used for links between generated pages",
23
+ default: "/reference",
24
+ },
25
+ force: {
26
+ type: "boolean",
27
+ description: "Regenerate even when the pages are up to date with the GIR libraries",
28
+ default: false,
29
+ },
30
+ ...cwdArg,
31
+ },
32
+ async run({ args }) {
33
+ const cwd = resolveCwd(args);
34
+ const { config } = await loadConfig(cwd);
35
+ if (config.codegen === false) {
36
+ throw new Error(
37
+ "codegen is disabled for this project, so there are no GIR libraries to document. Remove `codegen: false` from gtkx.config.ts to use `gtkx docs`.",
38
+ );
39
+ }
40
+ const girPath = resolveGirPath(config.girPath);
41
+ if (girPath.length === 0) {
42
+ throw new Error(
43
+ "No GIR search paths available. Install gobject-introspection (Linux: `sudo dnf install gobject-introspection-devel` or `sudo apt install libgirepository1.0-dev`), or set `girPath` in gtkx.config.ts.",
44
+ );
45
+ }
46
+ const libraries = resolveLibraries(config.libraries, girPath);
47
+ const startedAt = Date.now();
48
+ const outDir = resolve(cwd, args.out);
49
+ const { regenerated, namespaces } = writeDocs({
50
+ libraries,
51
+ girPath,
52
+ outDir,
53
+ basePath: args["base-path"],
54
+ elementProps: config.elementProps ?? {},
55
+ force: args.force,
56
+ });
57
+ if (!regenerated) {
58
+ info(`docs: pages in ${outDir} are up to date`);
59
+ return;
60
+ }
61
+ const count = namespaces.reduce((total, namespace) => total + namespace.elements.length, 0);
62
+ info(
63
+ `docs: wrote ${count} element pages across ${namespaces.length} namespaces to ${outDir} in ${Date.now() - startedAt}ms`,
64
+ );
65
+ },
66
+ });
@@ -0,0 +1,11 @@
1
+ import { existsSync } from "node:fs";
2
+ import { join } from "node:path";
3
+ import { prependXdgDataDir } from "../internal/xdg-data-dirs.js";
4
+
5
+ export const prepareDevIconDir = (root: string, dataDir: string | null): string | null => {
6
+ if (dataDir === null) return null;
7
+ const shareDir = join(root, dataDir);
8
+ if (!existsSync(join(shareDir, "icons"))) return null;
9
+ process.env.XDG_DATA_DIRS = prependXdgDataDir(shareDir, process.env.XDG_DATA_DIRS);
10
+ return shareDir;
11
+ };