@gtkx/cli 0.21.0 → 1.0.0-rc.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.
Files changed (358) hide show
  1. package/README.md +173 -0
  2. package/bin/gtkx-dev-runner.js +9 -0
  3. package/dist/builder.d.ts +5 -52
  4. package/dist/builder.d.ts.map +1 -1
  5. package/dist/builder.js +24 -57
  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 +24 -122
  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 +45 -0
  14. package/dist/codegen/freshness.js.map +1 -0
  15. package/dist/codegen/report.d.ts +4 -0
  16. package/dist/codegen/report.d.ts.map +1 -0
  17. package/dist/codegen/report.js +18 -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 +162 -0
  22. package/dist/codegen/run-codegen.js.map +1 -0
  23. package/dist/codegen/store-resolver.d.ts +24 -0
  24. package/dist/codegen/store-resolver.d.ts.map +1 -0
  25. package/dist/codegen/store-resolver.js +72 -0
  26. package/dist/codegen/store-resolver.js.map +1 -0
  27. package/dist/commands/build.d.ts +10 -0
  28. package/dist/commands/build.d.ts.map +1 -0
  29. package/dist/commands/build.js +34 -0
  30. package/dist/commands/build.js.map +1 -0
  31. package/dist/commands/codegen.d.ts +10 -0
  32. package/dist/commands/codegen.d.ts.map +1 -0
  33. package/dist/commands/codegen.js +42 -0
  34. package/dist/commands/codegen.js.map +1 -0
  35. package/dist/commands/dev.d.ts +6 -0
  36. package/dist/commands/dev.d.ts.map +1 -0
  37. package/dist/commands/dev.js +22 -0
  38. package/dist/commands/dev.js.map +1 -0
  39. package/dist/commands/docs.d.ts +20 -0
  40. package/dist/commands/docs.d.ts.map +1 -0
  41. package/dist/commands/docs.js +74 -0
  42. package/dist/commands/docs.js.map +1 -0
  43. package/dist/dev/icon-dir.d.ts +3 -0
  44. package/dist/dev/icon-dir.d.ts.map +1 -0
  45. package/dist/dev/icon-dir.js +16 -0
  46. package/dist/dev/icon-dir.js.map +1 -0
  47. package/dist/dev/refresh-tracker.d.ts +7 -0
  48. package/dist/dev/refresh-tracker.d.ts.map +1 -0
  49. package/dist/dev/refresh-tracker.js +19 -0
  50. package/dist/dev/refresh-tracker.js.map +1 -0
  51. package/dist/dev/runner-deps.d.ts +4 -0
  52. package/dist/dev/runner-deps.d.ts.map +1 -0
  53. package/dist/dev/runner-deps.js +55 -0
  54. package/dist/dev/runner-deps.js.map +1 -0
  55. package/dist/dev/runner-main.d.ts +3 -0
  56. package/dist/dev/runner-main.d.ts.map +1 -0
  57. package/dist/dev/runner-main.js +29 -0
  58. package/dist/dev/runner-main.js.map +1 -0
  59. package/dist/dev/runner.d.ts +24 -0
  60. package/dist/dev/runner.d.ts.map +1 -0
  61. package/dist/dev/runner.js +119 -0
  62. package/dist/dev/runner.js.map +1 -0
  63. package/dist/dev/schema-dir.d.ts +3 -0
  64. package/dist/dev/schema-dir.d.ts.map +1 -0
  65. package/dist/dev/schema-dir.js +12 -0
  66. package/dist/dev/schema-dir.js.map +1 -0
  67. package/dist/dev/supervisor.d.ts +18 -0
  68. package/dist/dev/supervisor.d.ts.map +1 -0
  69. package/dist/dev/supervisor.js +182 -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 +13 -0
  74. package/dist/dev/vite-dev-server.js.map +1 -0
  75. package/dist/internal/banner.d.ts +4 -0
  76. package/dist/internal/banner.d.ts.map +1 -0
  77. package/dist/internal/banner.js +9 -0
  78. package/dist/internal/banner.js.map +1 -0
  79. package/dist/internal/data-dir.d.ts +4 -0
  80. package/dist/internal/data-dir.d.ts.map +1 -0
  81. package/dist/internal/data-dir.js +52 -0
  82. package/dist/internal/data-dir.js.map +1 -0
  83. package/dist/internal/entry-arg.d.ts +20 -0
  84. package/dist/internal/entry-arg.d.ts.map +1 -0
  85. package/dist/internal/entry-arg.js +36 -0
  86. package/dist/internal/entry-arg.js.map +1 -0
  87. package/dist/internal/errors.d.ts +5 -0
  88. package/dist/internal/errors.d.ts.map +1 -0
  89. package/dist/internal/errors.js +29 -0
  90. package/dist/internal/errors.js.map +1 -0
  91. package/dist/internal/list-files.d.ts +7 -0
  92. package/dist/internal/list-files.d.ts.map +1 -0
  93. package/dist/internal/list-files.js +15 -0
  94. package/dist/internal/list-files.js.map +1 -0
  95. package/dist/internal/resolve-cli-tool.d.ts +3 -0
  96. package/dist/internal/resolve-cli-tool.d.ts.map +1 -0
  97. package/dist/internal/resolve-cli-tool.js +27 -0
  98. package/dist/internal/resolve-cli-tool.js.map +1 -0
  99. package/dist/internal/staging-dir.d.ts +4 -0
  100. package/dist/internal/staging-dir.d.ts.map +1 -0
  101. package/dist/internal/staging-dir.js +17 -0
  102. package/dist/internal/staging-dir.js.map +1 -0
  103. package/dist/internal/xdg-data-dirs.d.ts +3 -0
  104. package/dist/internal/xdg-data-dirs.d.ts.map +1 -0
  105. package/dist/internal/xdg-data-dirs.js +10 -0
  106. package/dist/internal/xdg-data-dirs.js.map +1 -0
  107. package/dist/mcp/client.d.ts +28 -0
  108. package/dist/mcp/client.d.ts.map +1 -0
  109. package/dist/mcp/client.js +182 -0
  110. package/dist/mcp/client.js.map +1 -0
  111. package/dist/mcp/handlers.d.ts +9 -0
  112. package/dist/mcp/handlers.d.ts.map +1 -0
  113. package/dist/mcp/handlers.js +147 -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 +34 -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 +23 -0
  122. package/dist/mcp/serialize-widget.js.map +1 -0
  123. package/dist/mcp/testing-loader.d.ts +5 -0
  124. package/dist/mcp/testing-loader.d.ts.map +1 -0
  125. package/dist/mcp/testing-loader.js +47 -0
  126. package/dist/mcp/testing-loader.js.map +1 -0
  127. package/dist/mcp/widget-registry.d.ts +14 -0
  128. package/dist/mcp/widget-registry.d.ts.map +1 -0
  129. package/dist/mcp/widget-registry.js +39 -0
  130. package/dist/mcp/widget-registry.js.map +1 -0
  131. package/dist/refresh-globals.d.ts +2 -0
  132. package/dist/refresh-globals.d.ts.map +1 -0
  133. package/dist/refresh-globals.js +8 -0
  134. package/dist/refresh-globals.js.map +1 -0
  135. package/dist/refresh-runtime.d.ts +10 -26
  136. package/dist/refresh-runtime.d.ts.map +1 -1
  137. package/dist/refresh-runtime.js +26 -48
  138. package/dist/refresh-runtime.js.map +1 -1
  139. package/dist/settings/compile.d.ts +3 -0
  140. package/dist/settings/compile.d.ts.map +1 -0
  141. package/dist/settings/compile.js +21 -0
  142. package/dist/settings/compile.js.map +1 -0
  143. package/dist/settings/parser.d.ts +24 -0
  144. package/dist/settings/parser.d.ts.map +1 -0
  145. package/dist/settings/parser.js +121 -0
  146. package/dist/settings/parser.js.map +1 -0
  147. package/dist/settings/render.d.ts +5 -0
  148. package/dist/settings/render.d.ts.map +1 -0
  149. package/dist/settings/render.js +122 -0
  150. package/dist/settings/render.js.map +1 -0
  151. package/dist/settings/schema.d.ts +13 -0
  152. package/dist/settings/schema.d.ts.map +1 -0
  153. package/dist/settings/schema.js +121 -0
  154. package/dist/settings/schema.js.map +1 -0
  155. package/dist/vite-plugins/asset-extensions.d.ts +5 -0
  156. package/dist/vite-plugins/asset-extensions.d.ts.map +1 -0
  157. package/dist/vite-plugins/asset-extensions.js +28 -0
  158. package/dist/vite-plugins/asset-extensions.js.map +1 -0
  159. package/dist/vite-plugins/built-url.d.ts +4 -0
  160. package/dist/vite-plugins/built-url.d.ts.map +1 -0
  161. package/dist/vite-plugins/built-url.js +36 -0
  162. package/dist/vite-plugins/built-url.js.map +1 -0
  163. package/dist/vite-plugins/css.d.ts +4 -0
  164. package/dist/vite-plugins/css.d.ts.map +1 -0
  165. package/dist/vite-plugins/css.js +38 -0
  166. package/dist/vite-plugins/css.js.map +1 -0
  167. package/dist/vite-plugins/fast-refresh/refresh-filter.d.ts +8 -0
  168. package/dist/vite-plugins/fast-refresh/refresh-filter.d.ts.map +1 -0
  169. package/dist/vite-plugins/fast-refresh/refresh-filter.js +19 -0
  170. package/dist/vite-plugins/fast-refresh/refresh-filter.js.map +1 -0
  171. package/dist/vite-plugins/fast-refresh/swc-refresh.d.ts +6 -0
  172. package/dist/vite-plugins/fast-refresh/swc-refresh.d.ts.map +1 -0
  173. package/dist/vite-plugins/fast-refresh/swc-refresh.js +70 -0
  174. package/dist/vite-plugins/fast-refresh/swc-refresh.js.map +1 -0
  175. package/dist/vite-plugins/icons.d.ts +4 -0
  176. package/dist/vite-plugins/icons.d.ts.map +1 -0
  177. package/dist/vite-plugins/icons.js +51 -0
  178. package/dist/vite-plugins/icons.js.map +1 -0
  179. package/dist/vite-plugins/index.d.ts +4 -0
  180. package/dist/vite-plugins/index.d.ts.map +1 -0
  181. package/dist/vite-plugins/index.js +22 -0
  182. package/dist/vite-plugins/index.js.map +1 -0
  183. package/dist/vite-plugins/native.d.ts +4 -0
  184. package/dist/vite-plugins/native.d.ts.map +1 -0
  185. package/dist/vite-plugins/native.js +62 -0
  186. package/dist/vite-plugins/native.js.map +1 -0
  187. package/dist/vite-plugins/react-compiler.d.ts +5 -0
  188. package/dist/vite-plugins/react-compiler.d.ts.map +1 -0
  189. package/dist/vite-plugins/react-compiler.js +62 -0
  190. package/dist/vite-plugins/react-compiler.js.map +1 -0
  191. package/dist/vite-plugins/react-dom-prebundle.d.ts +4 -0
  192. package/dist/vite-plugins/react-dom-prebundle.d.ts.map +1 -0
  193. package/dist/vite-plugins/react-dom-prebundle.js +12 -0
  194. package/dist/vite-plugins/react-dom-prebundle.js.map +1 -0
  195. package/dist/vite-plugins/resource-init-module.d.ts +6 -0
  196. package/dist/vite-plugins/resource-init-module.d.ts.map +1 -0
  197. package/dist/vite-plugins/resource-init-module.js +51 -0
  198. package/dist/vite-plugins/resource-init-module.js.map +1 -0
  199. package/dist/vite-plugins/resource-shared.d.ts +8 -0
  200. package/dist/vite-plugins/resource-shared.d.ts.map +1 -0
  201. package/dist/vite-plugins/resource-shared.js +28 -0
  202. package/dist/vite-plugins/resource-shared.js.map +1 -0
  203. package/dist/vite-plugins/resources.d.ts +5 -0
  204. package/dist/vite-plugins/resources.d.ts.map +1 -0
  205. package/dist/vite-plugins/resources.js +260 -0
  206. package/dist/vite-plugins/resources.js.map +1 -0
  207. package/dist/vite-plugins/settings-worker-env.d.ts +4 -0
  208. package/dist/vite-plugins/settings-worker-env.d.ts.map +1 -0
  209. package/dist/vite-plugins/settings-worker-env.js +23 -0
  210. package/dist/vite-plugins/settings-worker-env.js.map +1 -0
  211. package/dist/vite-plugins/settings.d.ts +4 -0
  212. package/dist/vite-plugins/settings.d.ts.map +1 -0
  213. package/dist/vite-plugins/settings.js +214 -0
  214. package/dist/vite-plugins/settings.js.map +1 -0
  215. package/dist/vite-plugins/strip-query.d.ts +3 -0
  216. package/dist/vite-plugins/strip-query.d.ts.map +1 -0
  217. package/dist/vite-plugins/strip-query.js +6 -0
  218. package/dist/vite-plugins/strip-query.js.map +1 -0
  219. package/dist/vite-plugins/undeclared-library.d.ts +4 -0
  220. package/dist/vite-plugins/undeclared-library.d.ts.map +1 -0
  221. package/dist/vite-plugins/undeclared-library.js +50 -0
  222. package/dist/vite-plugins/undeclared-library.js.map +1 -0
  223. package/dist/vite-plugins/virtual-module.d.ts +22 -0
  224. package/dist/vite-plugins/virtual-module.d.ts.map +1 -0
  225. package/dist/vite-plugins/virtual-module.js +20 -0
  226. package/dist/vite-plugins/virtual-module.js.map +1 -0
  227. package/dist/vitest-plugin.d.ts +10 -0
  228. package/dist/vitest-plugin.d.ts.map +1 -0
  229. package/dist/vitest-plugin.js +12 -0
  230. package/dist/vitest-plugin.js.map +1 -0
  231. package/env.d.ts +91 -8
  232. package/package.json +30 -38
  233. package/src/babel-modules.d.ts +11 -0
  234. package/src/builder.ts +31 -86
  235. package/src/cli.ts +42 -0
  236. package/src/codegen/freshness.ts +65 -0
  237. package/src/codegen/report.ts +28 -0
  238. package/src/codegen/run-codegen.ts +260 -0
  239. package/src/codegen/store-resolver.ts +116 -0
  240. package/src/commands/build.ts +38 -0
  241. package/src/commands/codegen.ts +49 -0
  242. package/src/commands/dev.ts +24 -0
  243. package/src/commands/docs.ts +95 -0
  244. package/src/dev/icon-dir.ts +21 -0
  245. package/src/dev/refresh-tracker.ts +25 -0
  246. package/src/dev/runner-deps.ts +65 -0
  247. package/src/dev/runner-main.ts +34 -0
  248. package/src/dev/runner.ts +185 -0
  249. package/src/dev/schema-dir.ts +16 -0
  250. package/src/dev/supervisor.ts +276 -0
  251. package/src/dev/vite-dev-server.ts +36 -0
  252. package/src/internal/banner.ts +13 -0
  253. package/src/internal/data-dir.ts +70 -0
  254. package/src/internal/entry-arg.ts +46 -0
  255. package/src/internal/errors.ts +36 -0
  256. package/src/internal/list-files.ts +23 -0
  257. package/src/internal/resolve-cli-tool.ts +37 -0
  258. package/src/internal/staging-dir.ts +19 -0
  259. package/src/internal/xdg-data-dirs.ts +13 -0
  260. package/src/mcp/client.ts +235 -0
  261. package/src/mcp/handlers.ts +213 -0
  262. package/src/mcp/index.ts +46 -0
  263. package/src/mcp/serialize-widget.ts +41 -0
  264. package/src/mcp/testing-loader.ts +73 -0
  265. package/src/mcp/widget-registry.ts +49 -0
  266. package/src/refresh-globals.ts +10 -0
  267. package/src/refresh-runtime.ts +30 -55
  268. package/src/settings/compile.ts +22 -0
  269. package/src/settings/parser.ts +193 -0
  270. package/src/settings/render.ts +167 -0
  271. package/src/settings/schema.ts +169 -0
  272. package/src/vite-plugins/asset-extensions.ts +29 -0
  273. package/src/vite-plugins/built-url.ts +50 -0
  274. package/src/vite-plugins/css.ts +53 -0
  275. package/src/vite-plugins/fast-refresh/refresh-filter.ts +26 -0
  276. package/src/vite-plugins/fast-refresh/swc-refresh.ts +92 -0
  277. package/src/vite-plugins/icons.ts +74 -0
  278. package/src/vite-plugins/index.ts +25 -0
  279. package/src/vite-plugins/native.ts +83 -0
  280. package/src/vite-plugins/react-compiler.ts +84 -0
  281. package/src/vite-plugins/react-dom-prebundle.ts +17 -0
  282. package/src/vite-plugins/resource-init-module.ts +57 -0
  283. package/src/vite-plugins/resource-shared.ts +40 -0
  284. package/src/vite-plugins/resources.ts +366 -0
  285. package/src/vite-plugins/settings-worker-env.ts +30 -0
  286. package/src/vite-plugins/settings.ts +286 -0
  287. package/src/vite-plugins/strip-query.ts +7 -0
  288. package/src/vite-plugins/undeclared-library.ts +78 -0
  289. package/src/vite-plugins/virtual-module.ts +44 -0
  290. package/src/vitest-plugin.ts +13 -0
  291. package/dist/create.d.ts +0 -55
  292. package/dist/create.d.ts.map +0 -1
  293. package/dist/create.js +0 -236
  294. package/dist/create.js.map +0 -1
  295. package/dist/dev-server.d.ts +0 -37
  296. package/dist/dev-server.d.ts.map +0 -1
  297. package/dist/dev-server.js +0 -130
  298. package/dist/dev-server.js.map +0 -1
  299. package/dist/index.d.ts +0 -4
  300. package/dist/index.d.ts.map +0 -1
  301. package/dist/index.js +0 -4
  302. package/dist/index.js.map +0 -1
  303. package/dist/mcp-client.d.ts +0 -31
  304. package/dist/mcp-client.d.ts.map +0 -1
  305. package/dist/mcp-client.js +0 -430
  306. package/dist/mcp-client.js.map +0 -1
  307. package/dist/templates.d.ts +0 -9
  308. package/dist/templates.d.ts.map +0 -1
  309. package/dist/templates.js +0 -16
  310. package/dist/templates.js.map +0 -1
  311. package/dist/vite-plugin-gtkx-assets.d.ts +0 -20
  312. package/dist/vite-plugin-gtkx-assets.d.ts.map +0 -1
  313. package/dist/vite-plugin-gtkx-assets.js +0 -57
  314. package/dist/vite-plugin-gtkx-assets.js.map +0 -1
  315. package/dist/vite-plugin-gtkx-built-url.d.ts +0 -18
  316. package/dist/vite-plugin-gtkx-built-url.d.ts.map +0 -1
  317. package/dist/vite-plugin-gtkx-built-url.js +0 -43
  318. package/dist/vite-plugin-gtkx-built-url.js.map +0 -1
  319. package/dist/vite-plugin-gtkx-gsettings.d.ts +0 -28
  320. package/dist/vite-plugin-gtkx-gsettings.d.ts.map +0 -1
  321. package/dist/vite-plugin-gtkx-gsettings.js +0 -147
  322. package/dist/vite-plugin-gtkx-gsettings.js.map +0 -1
  323. package/dist/vite-plugin-gtkx-native.d.ts +0 -14
  324. package/dist/vite-plugin-gtkx-native.d.ts.map +0 -1
  325. package/dist/vite-plugin-gtkx-native.js +0 -53
  326. package/dist/vite-plugin-gtkx-native.js.map +0 -1
  327. package/dist/vite-plugin-gtkx-refresh.d.ts +0 -8
  328. package/dist/vite-plugin-gtkx-refresh.d.ts.map +0 -1
  329. package/dist/vite-plugin-gtkx-refresh.js +0 -37
  330. package/dist/vite-plugin-gtkx-refresh.js.map +0 -1
  331. package/dist/vite-plugin-swc-ssr-refresh.d.ts +0 -8
  332. package/dist/vite-plugin-swc-ssr-refresh.d.ts.map +0 -1
  333. package/dist/vite-plugin-swc-ssr-refresh.js +0 -46
  334. package/dist/vite-plugin-swc-ssr-refresh.js.map +0 -1
  335. package/src/cli.tsx +0 -154
  336. package/src/create.ts +0 -326
  337. package/src/dev-server.tsx +0 -162
  338. package/src/index.ts +0 -3
  339. package/src/mcp-client.ts +0 -518
  340. package/src/templates.ts +0 -26
  341. package/src/vite-plugin-gtkx-assets.ts +0 -69
  342. package/src/vite-plugin-gtkx-built-url.ts +0 -48
  343. package/src/vite-plugin-gtkx-gsettings.ts +0 -168
  344. package/src/vite-plugin-gtkx-native.ts +0 -64
  345. package/src/vite-plugin-gtkx-refresh.ts +0 -54
  346. package/src/vite-plugin-swc-ssr-refresh.ts +0 -61
  347. package/templates/claude/EXAMPLES.md.ejs +0 -744
  348. package/templates/claude/SKILL.md.ejs +0 -93
  349. package/templates/claude/WIDGETS.md.ejs +0 -820
  350. package/templates/config/vitest.config.ts.ejs +0 -11
  351. package/templates/gitignore.ejs +0 -4
  352. package/templates/package.json.ejs +0 -16
  353. package/templates/src/app.tsx.ejs +0 -32
  354. package/templates/src/dev.tsx.ejs +0 -5
  355. package/templates/src/gtkx-env.d.ts.ejs +0 -1
  356. package/templates/src/index.tsx.ejs +0 -5
  357. package/templates/tests/app.test.tsx.ejs +0 -16
  358. package/templates/tsconfig.json.ejs +0 -14
@@ -0,0 +1,366 @@
1
+ import type { Plugin, ResolvedConfig, UserConfig, ViteDevServer } from "vite";
2
+ import { type ConfigLoader, createConfigLoader } from "@gtkx/config/internal";
3
+ import { error, formatChildProcessError, info, sortStrings } from "@gtkx/utils";
4
+ import { execFileSync } from "node:child_process";
5
+ import { copyFileSync, mkdirSync, mkdtempSync, readFileSync, writeFileSync } from "node:fs";
6
+ import { tmpdir } from "node:os";
7
+ import { dirname, join } from "node:path";
8
+ import { DATA_IMPORT_PREFIX, resolveDataDir } from "../internal/data-dir.js";
9
+ import { type ListedFile, listFilesRecursive } from "../internal/list-files.js";
10
+ import { resolveCliTool } from "../internal/resolve-cli-tool.js";
11
+ import { withStagingDir } from "../internal/staging-dir.js";
12
+ import { ASSET_PATH_RE, ASSET_RE } from "./asset-extensions.js";
13
+ import { renderInitModule } from "./resource-init-module.js";
14
+ import {
15
+ BUNDLE_FILENAME,
16
+ escapeXml,
17
+ fromVirtualId,
18
+ isVirtual,
19
+ REFRESH_EXPORT,
20
+ REL_SEPARATOR,
21
+ toVirtualId,
22
+ VIRTUAL_INIT,
23
+ } from "./resource-shared.js";
24
+ import { stripQuery } from "./strip-query.js";
25
+
26
+ type ResourceEntry = {
27
+ sourcePath: string;
28
+ stagedRelPath: string;
29
+ resourcePath: string;
30
+ };
31
+
32
+ type PluginState = {
33
+ prefix: string;
34
+ isBuild: boolean;
35
+ entries: Map<string, ResourceEntry>;
36
+ sourcePaths: Set<string>;
37
+ devStagingDir: string | null;
38
+ devBundlePath: string;
39
+ server: ViteDevServer | null;
40
+ compiledSignature: string;
41
+ dataDir: string | null;
42
+ };
43
+
44
+ const DATA_PREFIX = `${DATA_IMPORT_PREFIX}/`;
45
+ const RESOURCE_COMPILER = "glib-compile-resources";
46
+ const MANIFEST_PREFIX = "/";
47
+
48
+ const deriveResourcePrefix = (applicationId: string): string => `/${applicationId.replaceAll(".", "/")}`;
49
+
50
+ const compileBundle = (state: PluginState, outputPath: string): Buffer =>
51
+ withStagingDir("resources", (dir) => {
52
+ const manifest = stageBundle(dir, state.entries);
53
+
54
+ return runCompiler(dir, manifest, outputPath);
55
+ });
56
+
57
+ const stageBundle = (dir: string, entries: Map<string, ResourceEntry>): string => {
58
+ const fileNodes: string[] = [];
59
+
60
+ for (const entry of entries.values()) {
61
+ const targetPath = join(dir, entry.stagedRelPath);
62
+ mkdirSync(dirname(targetPath), { recursive: true });
63
+ copyFileSync(entry.sourcePath, targetPath);
64
+ fileNodes.push(` <file>${escapeXml(entry.stagedRelPath)}</file>`);
65
+ }
66
+
67
+ const prefix = escapeXml(MANIFEST_PREFIX);
68
+ const manifest = join(dir, "gtkx.gresource.xml");
69
+
70
+ const xml = [
71
+ "<?xml version=\"1.0\" encoding=\"UTF-8\"?>",
72
+ "<gresources>",
73
+ ` <gresource prefix="${prefix}">`,
74
+ ...fileNodes,
75
+ " </gresource>",
76
+ "</gresources>",
77
+ "",
78
+ ].join("\n");
79
+
80
+ writeFileSync(manifest, xml);
81
+
82
+ return manifest;
83
+ };
84
+
85
+ const runCompiler = (sourceDir: string, manifest: string, outputPath: string): Buffer => {
86
+ try {
87
+ execFileSync(resolveCliTool(RESOURCE_COMPILER), [
88
+ `--sourcedir=${sourceDir}`,
89
+ `--target=${outputPath}`,
90
+ manifest,
91
+ ]);
92
+ } catch (error) {
93
+ const details = formatChildProcessError(error);
94
+ const suffix = details ? `:\n${details}` : "";
95
+ throw new Error(`${RESOURCE_COMPILER} failed${suffix}`, { cause: error });
96
+ }
97
+
98
+ return readFileSync(outputPath);
99
+ };
100
+
101
+ const ensureStagingDir = (state: PluginState): void => {
102
+ if (state.devStagingDir) {
103
+ return;
104
+ }
105
+
106
+ state.devStagingDir = mkdtempSync(join(tmpdir(), "gtkx-resources-dev-"));
107
+ state.devBundlePath = join(state.devStagingDir, BUNDLE_FILENAME);
108
+ };
109
+
110
+ const entriesSignature = (state: PluginState): string => sortStrings(state.entries.keys()).join("\0");
111
+
112
+ const compileDevBundle = (state: PluginState): void => {
113
+ ensureStagingDir(state);
114
+
115
+ if (state.entries.size === 0) {
116
+ return;
117
+ }
118
+
119
+ compileBundle(state, state.devBundlePath);
120
+ state.compiledSignature = entriesSignature(state);
121
+ };
122
+
123
+ const isRefreshHook = (value: unknown): value is () => void => typeof value === "function";
124
+
125
+ const reregisterDevBundle = async (state: PluginState): Promise<void> => {
126
+ const server = state.server;
127
+
128
+ if (!server) {
129
+ return;
130
+ }
131
+
132
+ const mod = await server.ssrLoadModule(VIRTUAL_INIT);
133
+ const refresh: unknown = mod[REFRESH_EXPORT];
134
+
135
+ if (isRefreshHook(refresh)) {
136
+ refresh();
137
+ }
138
+ };
139
+
140
+ const scanDataAssets = (dataDir: string): ListedFile[] => listFilesRecursive(dataDir, (name) => ASSET_RE.test(name));
141
+
142
+ const primeDevBundle = (state: PluginState): void => {
143
+ if (state.dataDir === null) {
144
+ return;
145
+ }
146
+
147
+ for (const { absPath, rel } of scanDataAssets(state.dataDir)) {
148
+ registerEntry(state, absPath, rel);
149
+ }
150
+
151
+ compileDevBundle(state);
152
+ };
153
+
154
+ const registerDevAsset = (state: PluginState): void => {
155
+ if (entriesSignature(state) !== state.compiledSignature) {
156
+ compileDevBundle(state);
157
+ }
158
+ };
159
+
160
+ const registerEntry = (state: PluginState, absPath: string, rel: string): ResourceEntry => {
161
+ const existing = state.entries.get(absPath);
162
+
163
+ if (existing) {
164
+ return existing;
165
+ }
166
+
167
+ const resourcePath = `${state.prefix}/${rel}`;
168
+
169
+ const entry: ResourceEntry = {
170
+ sourcePath: absPath,
171
+ stagedRelPath: resourcePath.replace(/^\/+/, ""),
172
+ resourcePath,
173
+ };
174
+
175
+ state.entries.set(absPath, entry);
176
+ state.sourcePaths.add(absPath);
177
+
178
+ return entry;
179
+ };
180
+
181
+ const isTrackedSource = (state: PluginState, file: string): boolean => state.sourcePaths.has(file);
182
+
183
+ const dataAssetSource = (source: string): string | null => {
184
+ const clean = stripQuery(source);
185
+
186
+ if (!clean.startsWith(DATA_PREFIX) || !ASSET_PATH_RE.test(clean)) {
187
+ return null;
188
+ }
189
+
190
+ return clean;
191
+ };
192
+
193
+ const resolvedAssetId = (
194
+ resolved: { id: string; external: boolean | string } | null,
195
+ clean: string,
196
+ ): string | undefined => {
197
+ if (!resolved || resolved.external) {
198
+ return undefined;
199
+ }
200
+
201
+ return toVirtualId(resolved.id) + REL_SEPARATOR + clean.slice(DATA_PREFIX.length);
202
+ };
203
+
204
+ const loadInitModule = (state: PluginState): string => {
205
+ if (!state.isBuild) {
206
+ ensureStagingDir(state);
207
+ }
208
+
209
+ return renderInitModule({ isBuild: state.isBuild, devBundlePath: state.devBundlePath });
210
+ };
211
+
212
+ const loadAssetModule = (state: PluginState, virtualId: string): string => {
213
+ const rest = fromVirtualId(virtualId);
214
+ const separatorIndex = rest.indexOf(REL_SEPARATOR);
215
+ const absPath = rest.slice(0, separatorIndex);
216
+ const rel = rest.slice(separatorIndex + REL_SEPARATOR.length);
217
+ const entry = registerEntry(state, absPath, rel);
218
+ const uri = `resource://${entry.resourcePath}`;
219
+
220
+ if (!state.isBuild) {
221
+ registerDevAsset(state);
222
+ }
223
+
224
+ return [
225
+ `import { ensureRegistered } from ${JSON.stringify(VIRTUAL_INIT)};`,
226
+ "ensureRegistered();",
227
+ `export default ${JSON.stringify(uri)};`,
228
+ `export const path = ${JSON.stringify(entry.resourcePath)};`,
229
+ ].join("\n");
230
+ };
231
+
232
+ const emitBuildBundle = (
233
+ ctx: { emitFile: (asset: { type: "asset"; fileName: string; source: Buffer }) => string },
234
+ state: PluginState,
235
+ ): void => {
236
+ if (!state.isBuild || state.entries.size === 0) {
237
+ return;
238
+ }
239
+
240
+ const compiled = withStagingDir("resources-out", (outDir) => compileBundle(state, join(outDir, BUNDLE_FILENAME)));
241
+ ctx.emitFile({ type: "asset", fileName: BUNDLE_FILENAME, source: compiled });
242
+ info(`Compiled ${String(state.entries.size)} resource(s) into ${BUNDLE_FILENAME}`);
243
+ };
244
+
245
+ const refreshDevRegistration = async (state: PluginState): Promise<void> => {
246
+ compileDevBundle(state);
247
+
248
+ try {
249
+ await reregisterDevBundle(state);
250
+ } catch (error_) {
251
+ error("Failed to refresh GResource bundle:", error_);
252
+ }
253
+ };
254
+
255
+ const resolveResourceConfig = async (state: PluginState, config: UserConfig, loadConfig: ConfigLoader) => {
256
+ const { applicationId } = await loadConfig(config.root ?? process.cwd());
257
+ state.prefix = deriveResourcePrefix(applicationId);
258
+
259
+ return {
260
+ assetsInclude: [ASSET_RE],
261
+ };
262
+ };
263
+
264
+ const refreshTrackedSource = async (state: PluginState, file: string): Promise<void> => {
265
+ if (!isTrackedSource(state, file)) {
266
+ return;
267
+ }
268
+
269
+ try {
270
+ await refreshDevRegistration(state);
271
+ } catch (error_) {
272
+ error("GResource refresh failed:", error_);
273
+ }
274
+ };
275
+
276
+ const attachResourceWatcher = (state: PluginState, server: ViteDevServer): void => {
277
+ state.server = server;
278
+
279
+ const onFileEvent = (file: string): void => {
280
+ void refreshTrackedSource(state, file);
281
+ };
282
+
283
+ server.watcher.on("change", onFileEvent);
284
+ server.watcher.on("add", onFileEvent);
285
+ };
286
+
287
+ const applyResolvedConfig = (state: PluginState, config: ResolvedConfig): void => {
288
+ state.isBuild = config.command === "build";
289
+
290
+ if (state.isBuild) {
291
+ return;
292
+ }
293
+
294
+ const relativeDataDir = resolveDataDir(config.root);
295
+ state.dataDir = relativeDataDir === null ? null : join(config.root, relativeDataDir);
296
+ };
297
+
298
+ const loadResourceModule = (state: PluginState, id: string): string | undefined => {
299
+ if (id === VIRTUAL_INIT) {
300
+ return loadInitModule(state);
301
+ }
302
+
303
+ if (!isVirtual(id)) {
304
+ return undefined;
305
+ }
306
+
307
+ return loadAssetModule(state, id);
308
+ };
309
+
310
+ function gtkxResources(loadConfig: ConfigLoader = createConfigLoader()): Plugin {
311
+ const state: PluginState = {
312
+ prefix: "",
313
+ isBuild: false,
314
+ entries: new Map(),
315
+ sourcePaths: new Set(),
316
+ devStagingDir: null,
317
+ devBundlePath: "",
318
+ server: null,
319
+ compiledSignature: "",
320
+ dataDir: null,
321
+ };
322
+
323
+ return {
324
+ name: "gtkx:resources",
325
+ enforce: "pre",
326
+
327
+ config(config: UserConfig) {
328
+ return resolveResourceConfig(state, config, loadConfig);
329
+ },
330
+
331
+ configResolved(config: ResolvedConfig) {
332
+ applyResolvedConfig(state, config);
333
+ },
334
+
335
+ configureServer(server) {
336
+ attachResourceWatcher(state, server);
337
+ primeDevBundle(state);
338
+ },
339
+
340
+ async resolveId(source, importer, opts) {
341
+ if (source === VIRTUAL_INIT) {
342
+ return VIRTUAL_INIT;
343
+ }
344
+
345
+ const clean = dataAssetSource(source);
346
+
347
+ if (clean === null) {
348
+ return;
349
+ }
350
+
351
+ const resolved = await this.resolve(clean, importer, { ...opts, skipSelf: true });
352
+
353
+ return resolvedAssetId(resolved, clean);
354
+ },
355
+
356
+ load(id) {
357
+ return loadResourceModule(state, id);
358
+ },
359
+
360
+ buildEnd() {
361
+ emitBuildBundle(this, state);
362
+ },
363
+ };
364
+ }
365
+
366
+ export { gtkxResources };
@@ -0,0 +1,30 @@
1
+ import type { UserConfig } from "vite";
2
+ import type { Plugin } from "vitest/config";
3
+ import { resolveDataDir } from "../internal/data-dir.js";
4
+ import { prependSchemaDir, stageAndCompileProjectSchemas } from "../settings/schema.js";
5
+
6
+ function gtkxSettingsWorkerEnv(): Plugin {
7
+ return {
8
+ name: "gtkx:settings-worker-env",
9
+ enforce: "pre",
10
+
11
+ config(config: UserConfig) {
12
+ const root = config.root ?? process.cwd();
13
+ const dir = stageAndCompileProjectSchemas(root, resolveDataDir(root));
14
+
15
+ if (dir === null) {
16
+ return;
17
+ }
18
+
19
+ process.env.GTKX_DEV_SCHEMA_DIR = dir;
20
+
21
+ return {
22
+ test: {
23
+ env: { GSETTINGS_SCHEMA_DIR: prependSchemaDir(dir, process.env.GSETTINGS_SCHEMA_DIR) },
24
+ },
25
+ };
26
+ },
27
+ };
28
+ }
29
+
30
+ export { gtkxSettingsWorkerEnv };
@@ -0,0 +1,286 @@
1
+ import type { ModuleNode, Plugin, ResolvedConfig, UserConfig, ViteDevServer } from "vite";
2
+ import { error, errorMessage, info } from "@gtkx/utils";
3
+ import { mkdtempSync, readFileSync } from "node:fs";
4
+ import { tmpdir } from "node:os";
5
+ import { basename, join } from "node:path";
6
+ import { prependBanner } from "../internal/banner.js";
7
+ import { resolveDataDir } from "../internal/data-dir.js";
8
+ import { removeTempDir, withStagingDir } from "../internal/staging-dir.js";
9
+ import { compileSchemas } from "../settings/compile.js";
10
+ import { parseSchemaXml, SchemaParseError } from "../settings/parser.js";
11
+ import { renderRuntimeModule } from "../settings/render.js";
12
+ import { emitSchemaEnv, prependSchemaDir, SCHEMA_SUFFIX, stageSchema } from "../settings/schema.js";
13
+ import { createVirtualNamespace } from "./virtual-module.js";
14
+
15
+ type PluginState = {
16
+ schemaDir: string | null;
17
+ rootDir: string | null;
18
+ dataDir: string | null;
19
+ isBuild: boolean;
20
+ trackedSchemas: Map<string, string>;
21
+ buildSchemas: Set<string>;
22
+ cleanupProcessExit: (() => void) | null;
23
+ };
24
+
25
+ type PluginContext = {
26
+ error: (message: string) => never;
27
+ emitFile: (file: { type: "asset"; fileName: string; source: Buffer }) => void;
28
+ };
29
+
30
+ const VIRTUAL_PREFIX = "\0gtkx-settings:";
31
+ const { isVirtual, fromVirtualId, resolveToVirtual } = createVirtualNamespace(VIRTUAL_PREFIX);
32
+
33
+ const SCHEMA_ENV_BANNER = [
34
+ "process.env.GSETTINGS_SCHEMA_DIR = [",
35
+ " decodeURIComponent(new URL(\".\", import.meta.url).pathname),",
36
+ " process.env.GSETTINGS_SCHEMA_DIR,",
37
+ "]",
38
+ " .filter(Boolean)",
39
+ " .join(\":\");",
40
+ ].join("\n");
41
+
42
+ const ensureSchemaDir = (state: PluginState): string => {
43
+ if (!state.schemaDir) {
44
+ const runnerDir = process.env.GTKX_DEV_SCHEMA_DIR;
45
+
46
+ if (runnerDir) {
47
+ state.schemaDir = runnerDir;
48
+
49
+ return runnerDir;
50
+ }
51
+
52
+ const dir = mkdtempSync(join(tmpdir(), "gtkx-schemas-"));
53
+ state.schemaDir = dir;
54
+
55
+ const cleanup = (): void => {
56
+ removeTempDir(dir);
57
+ };
58
+
59
+ state.cleanupProcessExit = cleanup;
60
+ process.once("exit", cleanup);
61
+ }
62
+
63
+ return state.schemaDir;
64
+ };
65
+
66
+ const releaseSchemaDir = (state: PluginState): void => {
67
+ if (!state.schemaDir) {
68
+ return;
69
+ }
70
+
71
+ if (state.cleanupProcessExit) {
72
+ removeTempDir(state.schemaDir);
73
+ process.removeListener("exit", state.cleanupProcessExit);
74
+ state.cleanupProcessExit = null;
75
+ }
76
+
77
+ state.schemaDir = null;
78
+ };
79
+
80
+ const compileSchemaDir = (state: PluginState): void => {
81
+ if (!state.schemaDir) {
82
+ return;
83
+ }
84
+
85
+ compileSchemas(state.schemaDir);
86
+ process.env.GSETTINGS_SCHEMA_DIR = prependSchemaDir(state.schemaDir, process.env.GSETTINGS_SCHEMA_DIR);
87
+ };
88
+
89
+ const syncSchemaEnv = (state: PluginState): void => {
90
+ if (state.rootDir === null) {
91
+ return;
92
+ }
93
+
94
+ try {
95
+ emitSchemaEnv(state.rootDir, state.dataDir);
96
+ } catch (error_) {
97
+ error(`Failed to generate GSettings schema types: ${errorMessage(error_)}`);
98
+ }
99
+ };
100
+
101
+ const applyUserConfig = (state: PluginState, config: UserConfig): void => {
102
+ state.dataDir = resolveDataDir(config.root ?? process.cwd());
103
+ };
104
+
105
+ const applyResolvedConfig = (state: PluginState, config: ResolvedConfig): void => {
106
+ state.isBuild = config.command === "build";
107
+ state.rootDir = typeof config.root === "string" ? config.root : null;
108
+ syncSchemaEnv(state);
109
+ };
110
+
111
+ const registerSchemaForMode = (state: PluginState, filePath: string, id: string): void => {
112
+ const fileName = basename(filePath);
113
+
114
+ if (state.isBuild) {
115
+ state.buildSchemas.add(filePath);
116
+ info(`Queued GSettings schema: ${fileName}`);
117
+
118
+ return;
119
+ }
120
+
121
+ state.trackedSchemas.set(filePath, id);
122
+ const dir = ensureSchemaDir(state);
123
+ stageSchema(dir, filePath);
124
+ compileSchemaDir(state);
125
+ info(`Compiled GSettings schema: ${fileName}`);
126
+ };
127
+
128
+ const loadSchemaModule = (ctx: PluginContext, state: PluginState, id: string): string | undefined => {
129
+ if (!isVirtual(id)) {
130
+ return undefined;
131
+ }
132
+
133
+ const filePath = fromVirtualId(id);
134
+ const xml = readFileSync(filePath, "utf8");
135
+ const fileName = basename(filePath);
136
+ registerSchemaForMode(state, filePath, id);
137
+ let parsed: ReturnType<typeof parseSchemaXml>;
138
+
139
+ try {
140
+ parsed = parseSchemaXml(xml, fileName);
141
+ } catch (error) {
142
+ if (!(error instanceof SchemaParseError)) {
143
+ throw error;
144
+ }
145
+
146
+ ctx.error(error.message);
147
+ }
148
+
149
+ if (parsed.schemas.length === 0) {
150
+ ctx.error(`No <schema id="..."> found in ${fileName}`);
151
+ }
152
+
153
+ return renderRuntimeModule(parsed);
154
+ };
155
+
156
+ const emitCompiledSchemas = (ctx: PluginContext, state: PluginState): void => {
157
+ if (!state.isBuild || state.buildSchemas.size === 0) {
158
+ return;
159
+ }
160
+
161
+ const compiled = withStagingDir("schemas-build", (dir) => {
162
+ for (const filePath of state.buildSchemas) {
163
+ stageSchema(dir, filePath);
164
+ }
165
+
166
+ compileSchemas(dir);
167
+
168
+ return readFileSync(join(dir, "gschemas.compiled"));
169
+ });
170
+
171
+ ctx.emitFile({
172
+ type: "asset",
173
+ fileName: "gschemas.compiled",
174
+ source: compiled,
175
+ });
176
+
177
+ info(`Compiled ${String(state.buildSchemas.size)} GSettings schema(s)`);
178
+ };
179
+
180
+ const handleSchemaHotUpdate = (state: PluginState, file: string, server: ViteDevServer): ModuleNode[] | undefined => {
181
+ if (file.endsWith(SCHEMA_SUFFIX)) {
182
+ syncSchemaEnv(state);
183
+ }
184
+
185
+ const virtualId = state.trackedSchemas.get(file);
186
+
187
+ if (!virtualId) {
188
+ return;
189
+ }
190
+
191
+ const dir = ensureSchemaDir(state);
192
+ stageSchema(dir, file);
193
+ compileSchemaDir(state);
194
+ info(`Recompiled GSettings schema: ${basename(file)}`);
195
+ const mod = server.moduleGraph.getModuleById(virtualId);
196
+
197
+ if (mod) {
198
+ server.moduleGraph.invalidateModule(mod);
199
+
200
+ return [mod];
201
+ }
202
+
203
+ return undefined;
204
+ };
205
+
206
+ const watchSchemaFiles = (state: PluginState, server: ViteDevServer): void => {
207
+ server.httpServer?.once("close", () => {
208
+ releaseSchemaDir(state);
209
+ });
210
+
211
+ server.watcher.once("close", () => {
212
+ releaseSchemaDir(state);
213
+ });
214
+
215
+ const refreshSchemaTypes = (file: string): void => {
216
+ if (file.endsWith(SCHEMA_SUFFIX)) {
217
+ syncSchemaEnv(state);
218
+ }
219
+ };
220
+
221
+ server.watcher.on("add", refreshSchemaTypes);
222
+ server.watcher.on("unlink", refreshSchemaTypes);
223
+ };
224
+
225
+ function gtkxSettings(): Plugin {
226
+ const state: PluginState = {
227
+ schemaDir: null,
228
+ rootDir: null,
229
+ dataDir: null,
230
+ isBuild: false,
231
+ trackedSchemas: new Map(),
232
+ buildSchemas: new Set(),
233
+ cleanupProcessExit: null,
234
+ };
235
+
236
+ return {
237
+ name: "gtkx:settings",
238
+ enforce: "pre",
239
+
240
+ config(config: UserConfig) {
241
+ applyUserConfig(state, config);
242
+ },
243
+
244
+ configResolved(config) {
245
+ applyResolvedConfig(state, config);
246
+ },
247
+
248
+ configureServer(server) {
249
+ watchSchemaFiles(state, server);
250
+ },
251
+
252
+ outputOptions(options) {
253
+ if (!state.isBuild) {
254
+ return;
255
+ }
256
+
257
+ return prependBanner(options, SCHEMA_ENV_BANNER);
258
+ },
259
+
260
+ async resolveId(source, importer, options) {
261
+ if (!source.endsWith(SCHEMA_SUFFIX)) {
262
+ return;
263
+ }
264
+
265
+ return resolveToVirtual(this, { source, importer, options });
266
+ },
267
+
268
+ load(id) {
269
+ return loadSchemaModule(this, state, id);
270
+ },
271
+
272
+ buildEnd() {
273
+ emitCompiledSchemas(this, state);
274
+ },
275
+
276
+ closeBundle() {
277
+ releaseSchemaDir(state);
278
+ },
279
+
280
+ handleHotUpdate({ file, server }) {
281
+ return handleSchemaHotUpdate(state, file, server);
282
+ },
283
+ };
284
+ }
285
+
286
+ export { gtkxSettings };
@@ -0,0 +1,7 @@
1
+ const stripQuery = (source: string): string => {
2
+ const queryIndex = source.indexOf("?");
3
+
4
+ return queryIndex === -1 ? source : source.slice(0, queryIndex);
5
+ };
6
+
7
+ export { stripQuery };