@gtkx/cli 0.21.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 (346) 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 -56
  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 -8
  224. package/package.json +32 -38
  225. package/src/babel-modules.d.ts +13 -0
  226. package/src/builder.ts +28 -84
  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 -55
  282. package/dist/create.d.ts.map +0 -1
  283. package/dist/create.js +0 -236
  284. package/dist/create.js.map +0 -1
  285. package/dist/dev-server.d.ts +0 -37
  286. package/dist/dev-server.d.ts.map +0 -1
  287. package/dist/dev-server.js +0 -130
  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-gsettings.d.ts +0 -28
  309. package/dist/vite-plugin-gtkx-gsettings.d.ts.map +0 -1
  310. package/dist/vite-plugin-gtkx-gsettings.js +0 -147
  311. package/dist/vite-plugin-gtkx-gsettings.js.map +0 -1
  312. package/dist/vite-plugin-gtkx-native.d.ts +0 -14
  313. package/dist/vite-plugin-gtkx-native.d.ts.map +0 -1
  314. package/dist/vite-plugin-gtkx-native.js +0 -53
  315. package/dist/vite-plugin-gtkx-native.js.map +0 -1
  316. package/dist/vite-plugin-gtkx-refresh.d.ts +0 -8
  317. package/dist/vite-plugin-gtkx-refresh.d.ts.map +0 -1
  318. package/dist/vite-plugin-gtkx-refresh.js +0 -37
  319. package/dist/vite-plugin-gtkx-refresh.js.map +0 -1
  320. package/dist/vite-plugin-swc-ssr-refresh.d.ts +0 -8
  321. package/dist/vite-plugin-swc-ssr-refresh.d.ts.map +0 -1
  322. package/dist/vite-plugin-swc-ssr-refresh.js +0 -46
  323. package/dist/vite-plugin-swc-ssr-refresh.js.map +0 -1
  324. package/src/cli.tsx +0 -154
  325. package/src/create.ts +0 -326
  326. package/src/dev-server.tsx +0 -162
  327. package/src/index.ts +0 -3
  328. package/src/mcp-client.ts +0 -518
  329. package/src/templates.ts +0 -26
  330. package/src/vite-plugin-gtkx-assets.ts +0 -69
  331. package/src/vite-plugin-gtkx-gsettings.ts +0 -168
  332. package/src/vite-plugin-gtkx-native.ts +0 -64
  333. package/src/vite-plugin-gtkx-refresh.ts +0 -54
  334. package/src/vite-plugin-swc-ssr-refresh.ts +0 -61
  335. package/templates/claude/EXAMPLES.md.ejs +0 -744
  336. package/templates/claude/SKILL.md.ejs +0 -93
  337. package/templates/claude/WIDGETS.md.ejs +0 -820
  338. package/templates/config/vitest.config.ts.ejs +0 -11
  339. package/templates/gitignore.ejs +0 -4
  340. package/templates/package.json.ejs +0 -16
  341. package/templates/src/app.tsx.ejs +0 -32
  342. package/templates/src/dev.tsx.ejs +0 -5
  343. package/templates/src/gtkx-env.d.ts.ejs +0 -1
  344. package/templates/src/index.tsx.ejs +0 -5
  345. package/templates/tests/app.test.tsx.ejs +0 -16
  346. package/templates/tsconfig.json.ejs +0 -14
@@ -0,0 +1,58 @@
1
+ import { readFileSync } from "node:fs";
2
+ import { join } from "node:path";
3
+ import { info } from "@gtkx/utils";
4
+ import type { Plugin, UserConfig } from "vite";
5
+ import { prependBanner } from "../internal/banner.js";
6
+ import { resolveDataDir } from "../internal/data-dir.js";
7
+ import { type ListedFile, listFilesRecursive } from "../internal/list-files.js";
8
+
9
+ const ICONS_DIR = "icons";
10
+
11
+ const XDG_ENV_BANNER = [
12
+ `process.env.XDG_DATA_DIRS = [`,
13
+ ` decodeURIComponent(new URL(".", import.meta.url).pathname),`,
14
+ ` process.env.XDG_DATA_DIRS || "/usr/local/share:/usr/share",`,
15
+ `].join(":");`,
16
+ ].join("\n");
17
+
18
+ type PluginState = {
19
+ iconsDir: string | null;
20
+ };
21
+
22
+ const findIconFiles = (iconsDir: string | null): ListedFile[] =>
23
+ iconsDir === null ? [] : listFilesRecursive(iconsDir);
24
+
25
+ export function gtkxIcons(): Plugin {
26
+ const state: PluginState = {
27
+ iconsDir: null,
28
+ };
29
+
30
+ return {
31
+ name: "gtkx:icons",
32
+ enforce: "pre",
33
+ apply: "build",
34
+
35
+ config(config: UserConfig) {
36
+ const root = config.root ?? process.cwd();
37
+ const dataDir = resolveDataDir(root);
38
+ state.iconsDir = dataDir === null ? null : join(root, dataDir, ICONS_DIR);
39
+ },
40
+
41
+ outputOptions(options) {
42
+ if (findIconFiles(state.iconsDir).length === 0) return;
43
+ return prependBanner(options, XDG_ENV_BANNER);
44
+ },
45
+
46
+ buildEnd() {
47
+ const icons = findIconFiles(state.iconsDir);
48
+ for (const { absPath, rel } of icons) {
49
+ this.emitFile({
50
+ type: "asset",
51
+ fileName: join(ICONS_DIR, rel),
52
+ source: readFileSync(absPath),
53
+ });
54
+ }
55
+ if (icons.length > 0) info(`Copied ${icons.length} icon(s) into ${ICONS_DIR}/`);
56
+ },
57
+ };
58
+ }
@@ -0,0 +1,22 @@
1
+ import { createConfigLoader } from "@gtkx/config/internal";
2
+ import createConfigPlugin from "@gtkx/config/vite-plugin";
3
+ import type { Plugin } from "vite";
4
+ import { gtkxCss } from "./css.js";
5
+ import { gtkxIcons } from "./icons.js";
6
+ import { gtkxReactCompiler } from "./react-compiler.js";
7
+ import { gtkxResources } from "./resources.js";
8
+ import { gtkxSettings } from "./settings.js";
9
+ import { gtkxUndeclaredLibrary } from "./undeclared-library.js";
10
+
11
+ export const gtkxVitePlugins = (mode?: string): Plugin[] => {
12
+ const loadConfig = createConfigLoader(mode !== undefined ? { mode } : {});
13
+ return [
14
+ createConfigPlugin({ name: "gtkx:config", loadConfig }),
15
+ gtkxUndeclaredLibrary(mode),
16
+ gtkxSettings(),
17
+ gtkxIcons(),
18
+ gtkxResources(loadConfig),
19
+ gtkxCss(),
20
+ gtkxReactCompiler(loadConfig),
21
+ ];
22
+ };
@@ -0,0 +1,74 @@
1
+ import { existsSync, readFileSync } from "node:fs";
2
+ import { createRequire } from "node:module";
3
+ import { arch, platform } from "node:os";
4
+ import { dirname, join } from "node:path";
5
+ import type { Plugin } from "vite";
6
+
7
+ const EMITTED_BINDING_SPECIFIER = "./gtkx.node";
8
+
9
+ function resolveBinaryPath(projectRequire: ReturnType<typeof createRequire>, currentArch: string): string {
10
+ const nativeRoot = dirname(projectRequire.resolve("@gtkx/native/package.json"));
11
+ const localBinary = join(nativeRoot, `native.linux-${currentArch}-gnu.node`);
12
+ return existsSync(localBinary) ? localBinary : projectRequire.resolve(`@gtkx/native-linux-${currentArch}-gnu`);
13
+ }
14
+
15
+ function resolvePlatformBinary(projectRequire: ReturnType<typeof createRequire>): Buffer {
16
+ const currentPlatform = platform();
17
+ const currentArch = arch();
18
+
19
+ if (currentPlatform !== "linux") {
20
+ throw new Error(`Unsupported build platform: ${currentPlatform}, only Linux is supported`);
21
+ }
22
+
23
+ if (currentArch !== "x64" && currentArch !== "arm64") {
24
+ throw new Error(`Unsupported build architecture: ${currentArch}, only x64 and arm64 are supported`);
25
+ }
26
+
27
+ return readFileSync(resolveBinaryPath(projectRequire, currentArch));
28
+ }
29
+
30
+ function rewriteLoader(code: string): string {
31
+ const idents: string[] = [];
32
+ for (const match of code.matchAll(/export\s*\{\s*([A-Za-z0-9_$]+)\s*\}/g)) {
33
+ const ident = match[1];
34
+ if (ident) {
35
+ idents.push(ident);
36
+ }
37
+ }
38
+ const bindings = idents.join(", ");
39
+
40
+ return [
41
+ `import { createRequire as __gtkxCreateRequire } from "node:module";`,
42
+ `const __gtkxNative = __gtkxCreateRequire(import.meta.url)(${JSON.stringify(EMITTED_BINDING_SPECIFIER)});`,
43
+ `const { ${bindings} } = __gtkxNative;`,
44
+ `export { ${bindings} };`,
45
+ ].join("\n");
46
+ }
47
+
48
+ export function gtkxNative(root: string): Plugin {
49
+ const projectRequire = createRequire(join(root, "package.json"));
50
+ let cachedLoaderPath: string | undefined;
51
+
52
+ const loaderPath = (): string => {
53
+ cachedLoaderPath ??= join(dirname(projectRequire.resolve("@gtkx/native/package.json")), "index.js");
54
+ return cachedLoaderPath;
55
+ };
56
+
57
+ return {
58
+ name: "gtkx:native",
59
+ enforce: "pre",
60
+ apply: "build",
61
+
62
+ buildStart() {
63
+ this.emitFile({ type: "asset", fileName: "gtkx.node", source: resolvePlatformBinary(projectRequire) });
64
+ },
65
+
66
+ resolveId(id) {
67
+ return id === EMITTED_BINDING_SPECIFIER ? { id, external: true } : null;
68
+ },
69
+
70
+ transform(code, id) {
71
+ return id.replace(/\?.*$/, "") === loaderPath() ? rewriteLoader(code) : null;
72
+ },
73
+ };
74
+ }
@@ -0,0 +1,70 @@
1
+ /// <reference path="../babel-modules.d.ts" />
2
+ import { transformAsync } from "@babel/core";
3
+ import babelPresetTypescriptNs from "@babel/preset-typescript";
4
+ import type { ResolvedReactCompilerOptions } from "@gtkx/config";
5
+ import { type ConfigLoader, createConfigLoader } from "@gtkx/config/internal";
6
+ import babelPluginReactCompilerNs from "babel-plugin-react-compiler";
7
+ import type { Plugin, ResolvedConfig, UserConfig } from "vite";
8
+
9
+ const babelPresetTypescript = babelPresetTypescriptNs.default ?? babelPresetTypescriptNs;
10
+ const babelPluginReactCompiler = babelPluginReactCompilerNs.default ?? babelPluginReactCompilerNs;
11
+
12
+ const SOURCE_EXTENSION = /\.[jt]sx?$/;
13
+ const TYPESCRIPT_EXTENSION = /\.tsx?$/;
14
+ const TYPE_ONLY_EXTENSION = /\.ts$/;
15
+ const NODE_MODULES = /(?:^|\/)node_modules\//;
16
+
17
+ type ReactCompilerState = {
18
+ root: string;
19
+ options: ResolvedReactCompilerOptions | null;
20
+ };
21
+
22
+ const isProjectSource = (root: string, id: string): boolean => {
23
+ if (!SOURCE_EXTENSION.test(id)) return false;
24
+ if (NODE_MODULES.test(id)) return false;
25
+ if (root !== "" && !id.startsWith(`${root}/`)) return false;
26
+ return true;
27
+ };
28
+
29
+ export function gtkxReactCompiler(loadConfig: ConfigLoader = createConfigLoader()): Plugin {
30
+ const state: ReactCompilerState = {
31
+ root: "",
32
+ options: null,
33
+ };
34
+
35
+ return {
36
+ name: "gtkx:react-compiler",
37
+ enforce: "pre",
38
+
39
+ async config(config: UserConfig) {
40
+ state.options = (await loadConfig(config.root ?? process.cwd())).reactCompiler;
41
+ },
42
+
43
+ configResolved(config: ResolvedConfig) {
44
+ state.root = config.root;
45
+ },
46
+
47
+ async transform(code, id) {
48
+ const options = state.options;
49
+ if (options === null || !isProjectSource(state.root, id)) {
50
+ return;
51
+ }
52
+
53
+ const result = await transformAsync(code, {
54
+ filename: id,
55
+ babelrc: false,
56
+ configFile: false,
57
+ sourceMaps: true,
58
+ parserOpts: { plugins: TYPE_ONLY_EXTENSION.test(id) ? [] : ["jsx"] },
59
+ presets: TYPESCRIPT_EXTENSION.test(id) ? [babelPresetTypescript] : [],
60
+ plugins: [[babelPluginReactCompiler, options]],
61
+ });
62
+
63
+ if (!result?.code) {
64
+ return;
65
+ }
66
+
67
+ return result.map == null ? { code: result.code } : { code: result.code, map: JSON.stringify(result.map) };
68
+ },
69
+ };
70
+ }
@@ -0,0 +1,14 @@
1
+ import type { Plugin } from "vite";
2
+
3
+ export const gtkxReactDomPrebundle = (): Plugin => ({
4
+ name: "gtkx:react-dom-prebundle",
5
+ enforce: "post",
6
+ config(config) {
7
+ config.optimizeDeps ??= {};
8
+ if (config.optimizeDeps.include) {
9
+ config.optimizeDeps.include = config.optimizeDeps.include.filter(
10
+ (dep) => dep !== "react-dom" && !dep.startsWith("react-dom/"),
11
+ );
12
+ }
13
+ },
14
+ });
@@ -0,0 +1,54 @@
1
+ import { BUNDLE_FILENAME } from "./resource-shared.js";
2
+
3
+ const buildInitModuleSource = (): string =>
4
+ [
5
+ `import { dirname, join } from "node:path";`,
6
+ `import { fileURLToPath } from "node:url";`,
7
+ `import { resourceLoad, resourcesRegister } from "@gtkx/gi/gio";`,
8
+ ``,
9
+ `const bundleDir = dirname(fileURLToPath(import.meta.url));`,
10
+ `const resource = resourceLoad(join(bundleDir, ${JSON.stringify(BUNDLE_FILENAME)}));`,
11
+ `resourcesRegister(resource);`,
12
+ ``,
13
+ `export function ensureRegistered() {}`,
14
+ `export function __refresh() {}`,
15
+ ].join("\n");
16
+
17
+ const devInitModuleSource = (bundlePath: string): string => {
18
+ const bundlePathLiteral = JSON.stringify(bundlePath);
19
+ return [
20
+ `import { existsSync, statSync } from "node:fs";`,
21
+ `import { resourceLoad, resourcesRegister, resourcesUnregister } from "@gtkx/gi/gio";`,
22
+ ``,
23
+ `let current = null;`,
24
+ `let lastSig = "";`,
25
+ ``,
26
+ `function register() {`,
27
+ ` const next = resourceLoad(${bundlePathLiteral});`,
28
+ ` if (current) resourcesUnregister(current);`,
29
+ ` resourcesRegister(next);`,
30
+ ` current = next;`,
31
+ `}`,
32
+ ``,
33
+ `export function ensureRegistered() {`,
34
+ ` if (!existsSync(${bundlePathLiteral})) return;`,
35
+ ` const { size, mtimeMs } = statSync(${bundlePathLiteral});`,
36
+ ` const sig = size + ":" + mtimeMs;`,
37
+ ` if (sig === lastSig) return;`,
38
+ ` register();`,
39
+ ` lastSig = sig;`,
40
+ `}`,
41
+ ``,
42
+ `ensureRegistered();`,
43
+ ``,
44
+ `export function __refresh() {`,
45
+ ` if (!existsSync(${bundlePathLiteral})) return;`,
46
+ ` register();`,
47
+ ` const { size, mtimeMs } = statSync(${bundlePathLiteral});`,
48
+ ` lastSig = size + ":" + mtimeMs;`,
49
+ `}`,
50
+ ].join("\n");
51
+ };
52
+
53
+ export const renderInitModule = (options: { isBuild: boolean; devBundlePath: string }): string =>
54
+ options.isBuild ? buildInitModuleSource() : devInitModuleSource(options.devBundlePath);
@@ -0,0 +1,27 @@
1
+ import { createVirtualNamespace } from "./virtual-module.js";
2
+
3
+ const VIRTUAL_PREFIX = "\0gtkx-resources:";
4
+
5
+ export const VIRTUAL_INIT = "\0gtkx-resources-init";
6
+
7
+ export const BUNDLE_FILENAME = "gtkx.gresource";
8
+
9
+ export const REL_SEPARATOR = "\0rel=";
10
+
11
+ export const { isVirtual, toVirtualId, fromVirtualId } = createVirtualNamespace(VIRTUAL_PREFIX);
12
+
13
+ export const escapeXml = (value: string): string =>
14
+ value.replaceAll(/[<>&"']/g, (char) => {
15
+ switch (char) {
16
+ case "<":
17
+ return "&lt;";
18
+ case ">":
19
+ return "&gt;";
20
+ case "&":
21
+ return "&amp;";
22
+ case '"':
23
+ return "&quot;";
24
+ default:
25
+ return "&apos;";
26
+ }
27
+ });
@@ -0,0 +1,272 @@
1
+ import { execFileSync } from "node:child_process";
2
+ import { copyFileSync, mkdirSync, mkdtempSync, readFileSync, writeFileSync } from "node:fs";
3
+ import { tmpdir } from "node:os";
4
+ import { dirname, join } from "node:path";
5
+ import { type ConfigLoader, createConfigLoader } from "@gtkx/config/internal";
6
+ import { error, formatChildProcessError, info } from "@gtkx/utils";
7
+ import type { Plugin, ResolvedConfig, UserConfig, ViteDevServer } from "vite";
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
+ REL_SEPARATOR,
20
+ toVirtualId,
21
+ VIRTUAL_INIT,
22
+ } from "./resource-shared.js";
23
+
24
+ const DATA_PREFIX = `${DATA_IMPORT_PREFIX}/`;
25
+
26
+ const RESOURCE_COMPILER = "glib-compile-resources";
27
+ const MANIFEST_PREFIX = "/";
28
+
29
+ const deriveResourcePrefix = (applicationId: string): string => `/${applicationId.replaceAll(".", "/")}`;
30
+
31
+ const stripQuery = (source: string): string => {
32
+ const queryIndex = source.indexOf("?");
33
+ return queryIndex === -1 ? source : source.slice(0, queryIndex);
34
+ };
35
+
36
+ type ResourceEntry = {
37
+ sourcePath: string;
38
+ stagedRelPath: string;
39
+ resourcePath: string;
40
+ };
41
+
42
+ type PluginState = {
43
+ prefix: string;
44
+ isBuild: boolean;
45
+ entries: Map<string, ResourceEntry>;
46
+ sourcePaths: Set<string>;
47
+ devStagingDir: string | null;
48
+ devBundlePath: string;
49
+ server: ViteDevServer | null;
50
+ compiledSignature: string;
51
+ dataDir: string | null;
52
+ };
53
+
54
+ const compileBundle = (state: PluginState, outputPath: string): Buffer =>
55
+ withStagingDir("resources", (dir) => {
56
+ const manifest = stageBundle(dir, state.entries);
57
+ return runCompiler(dir, manifest, outputPath);
58
+ });
59
+
60
+ const stageBundle = (dir: string, entries: Map<string, ResourceEntry>): string => {
61
+ const fileNodes: string[] = [];
62
+ for (const entry of entries.values()) {
63
+ const targetPath = join(dir, entry.stagedRelPath);
64
+ mkdirSync(dirname(targetPath), { recursive: true });
65
+ copyFileSync(entry.sourcePath, targetPath);
66
+ fileNodes.push(` <file>${escapeXml(entry.stagedRelPath)}</file>`);
67
+ }
68
+ const prefix = escapeXml(MANIFEST_PREFIX);
69
+ const manifest = join(dir, "gtkx.gresource.xml");
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
+ writeFileSync(manifest, xml);
80
+ return manifest;
81
+ };
82
+
83
+ const runCompiler = (sourceDir: string, manifest: string, outputPath: string): Buffer => {
84
+ try {
85
+ execFileSync(resolveCliTool(RESOURCE_COMPILER), [
86
+ `--sourcedir=${sourceDir}`,
87
+ `--target=${outputPath}`,
88
+ manifest,
89
+ ]);
90
+ } catch (error) {
91
+ const details = formatChildProcessError(error);
92
+ throw new Error(`${RESOURCE_COMPILER} failed${details ? `:\n${details}` : ""}`, { cause: error });
93
+ }
94
+ return readFileSync(outputPath);
95
+ };
96
+
97
+ const ensureStagingDir = (state: PluginState): void => {
98
+ if (!state.devStagingDir) {
99
+ state.devStagingDir = mkdtempSync(join(tmpdir(), "gtkx-resources-dev-"));
100
+ state.devBundlePath = join(state.devStagingDir, BUNDLE_FILENAME);
101
+ }
102
+ };
103
+
104
+ const entriesSignature = (state: PluginState): string =>
105
+ [...state.entries.keys()].sort((a, b) => a.localeCompare(b)).join("\0");
106
+
107
+ const compileDevBundle = (state: PluginState): void => {
108
+ ensureStagingDir(state);
109
+ if (state.entries.size === 0) return;
110
+ compileBundle(state, state.devBundlePath);
111
+ state.compiledSignature = entriesSignature(state);
112
+ };
113
+
114
+ const reregisterDevBundle = async (state: PluginState): Promise<void> => {
115
+ const server = state.server;
116
+ if (!server) return;
117
+ const mod = (await server.ssrLoadModule(VIRTUAL_INIT)) as { __refresh?: () => void };
118
+ mod.__refresh?.();
119
+ };
120
+
121
+ const scanDataAssets = (dataDir: string): ListedFile[] => listFilesRecursive(dataDir, (name) => ASSET_RE.test(name));
122
+
123
+ const primeDevBundle = (state: PluginState): void => {
124
+ if (state.dataDir === null) return;
125
+ for (const { absPath, rel } of scanDataAssets(state.dataDir)) {
126
+ registerEntry(state, absPath, rel);
127
+ }
128
+ compileDevBundle(state);
129
+ };
130
+
131
+ const registerDevAsset = (state: PluginState): void => {
132
+ if (entriesSignature(state) !== state.compiledSignature) {
133
+ compileDevBundle(state);
134
+ }
135
+ };
136
+
137
+ const registerEntry = (state: PluginState, absPath: string, rel: string): ResourceEntry => {
138
+ const existing = state.entries.get(absPath);
139
+ if (existing) return existing;
140
+
141
+ const resourcePath = `${state.prefix}/${rel}`;
142
+ const entry: ResourceEntry = {
143
+ sourcePath: absPath,
144
+ stagedRelPath: resourcePath.replace(/^\/+/, ""),
145
+ resourcePath,
146
+ };
147
+ state.entries.set(absPath, entry);
148
+ state.sourcePaths.add(absPath);
149
+ return entry;
150
+ };
151
+
152
+ const isTrackedSource = (state: PluginState, file: string): boolean => state.sourcePaths.has(file);
153
+
154
+ const loadAssetModule = (state: PluginState, virtualId: string): string => {
155
+ const rest = fromVirtualId(virtualId);
156
+ const separatorIndex = rest.indexOf(REL_SEPARATOR);
157
+ const absPath = rest.slice(0, separatorIndex);
158
+ const rel = rest.slice(separatorIndex + REL_SEPARATOR.length);
159
+ const entry = registerEntry(state, absPath, rel);
160
+ const uri = `resource://${entry.resourcePath}`;
161
+
162
+ if (!state.isBuild) {
163
+ registerDevAsset(state);
164
+ }
165
+
166
+ return [
167
+ `import { ensureRegistered } from ${JSON.stringify(VIRTUAL_INIT)};`,
168
+ `ensureRegistered();`,
169
+ `export default ${JSON.stringify(uri)};`,
170
+ `export const path = ${JSON.stringify(entry.resourcePath)};`,
171
+ ].join("\n");
172
+ };
173
+
174
+ const emitBuildBundle = (
175
+ ctx: { emitFile: (asset: { type: "asset"; fileName: string; source: Buffer }) => string },
176
+ state: PluginState,
177
+ ): void => {
178
+ if (!state.isBuild || state.entries.size === 0) return;
179
+ const compiled = withStagingDir("resources-out", (outDir) => compileBundle(state, join(outDir, BUNDLE_FILENAME)));
180
+ ctx.emitFile({ type: "asset", fileName: BUNDLE_FILENAME, source: compiled });
181
+ info(`Compiled ${state.entries.size} resource(s) into ${BUNDLE_FILENAME}`);
182
+ };
183
+
184
+ const refreshDevRegistration = async (state: PluginState): Promise<void> => {
185
+ compileDevBundle(state);
186
+ try {
187
+ await reregisterDevBundle(state);
188
+ } catch (cause) {
189
+ error("Failed to refresh GResource bundle:", cause);
190
+ }
191
+ };
192
+
193
+ const resolveResourceConfig = async (state: PluginState, config: UserConfig, loadConfig: ConfigLoader) => {
194
+ const { applicationId } = await loadConfig(config.root ?? process.cwd());
195
+ state.prefix = deriveResourcePrefix(applicationId);
196
+ return {
197
+ assetsInclude: [ASSET_RE],
198
+ };
199
+ };
200
+
201
+ const attachResourceWatcher = (state: PluginState, server: ViteDevServer): void => {
202
+ state.server = server;
203
+ const onFileEvent = (file: string): void => {
204
+ if (!isTrackedSource(state, file)) return;
205
+ refreshDevRegistration(state).catch((cause) => {
206
+ error("GResource refresh failed:", cause);
207
+ });
208
+ };
209
+ server.watcher.on("change", onFileEvent);
210
+ server.watcher.on("add", onFileEvent);
211
+ };
212
+
213
+ export function gtkxResources(loadConfig: ConfigLoader = createConfigLoader()): Plugin {
214
+ const state: PluginState = {
215
+ prefix: "",
216
+ isBuild: false,
217
+ entries: new Map(),
218
+ sourcePaths: new Set(),
219
+ devStagingDir: null,
220
+ devBundlePath: "",
221
+ server: null,
222
+ compiledSignature: "",
223
+ dataDir: null,
224
+ };
225
+
226
+ return {
227
+ name: "gtkx:resources",
228
+ enforce: "pre",
229
+
230
+ config(config: UserConfig) {
231
+ return resolveResourceConfig(state, config, loadConfig);
232
+ },
233
+
234
+ configResolved(config: ResolvedConfig) {
235
+ state.isBuild = config.command === "build";
236
+ if (!state.isBuild) {
237
+ const relativeDataDir = resolveDataDir(config.root);
238
+ state.dataDir = relativeDataDir === null ? null : join(config.root, relativeDataDir);
239
+ }
240
+ },
241
+
242
+ configureServer(server) {
243
+ attachResourceWatcher(state, server);
244
+ primeDevBundle(state);
245
+ },
246
+
247
+ async resolveId(source, importer, opts) {
248
+ if (source === VIRTUAL_INIT) return VIRTUAL_INIT;
249
+ const clean = stripQuery(source);
250
+ if (!clean.startsWith(DATA_PREFIX) || !ASSET_PATH_RE.test(clean)) return;
251
+
252
+ const resolved = await this.resolve(clean, importer, { ...opts, skipSelf: true });
253
+ if (!resolved || resolved.external) return;
254
+
255
+ const rel = clean.slice(DATA_PREFIX.length);
256
+ return toVirtualId(resolved.id) + REL_SEPARATOR + rel;
257
+ },
258
+
259
+ load(id) {
260
+ if (id === VIRTUAL_INIT) {
261
+ if (!state.isBuild) ensureStagingDir(state);
262
+ return renderInitModule({ isBuild: state.isBuild, devBundlePath: state.devBundlePath });
263
+ }
264
+ if (!isVirtual(id)) return;
265
+ return loadAssetModule(state, id);
266
+ },
267
+
268
+ buildEnd() {
269
+ emitBuildBundle(this, state);
270
+ },
271
+ };
272
+ }
@@ -0,0 +1,25 @@
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
+ export 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
+ if (dir === null) return;
15
+
16
+ process.env.GTKX_DEV_SCHEMA_DIR = dir;
17
+
18
+ return {
19
+ test: {
20
+ env: { GSETTINGS_SCHEMA_DIR: prependSchemaDir(dir, process.env.GSETTINGS_SCHEMA_DIR) },
21
+ },
22
+ };
23
+ },
24
+ };
25
+ }