@gtkx/cli 1.3.0 → 1.5.0

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 (299) hide show
  1. package/README.md +5 -4
  2. package/dist/builder.d.ts +1 -1
  3. package/dist/builder.d.ts.map +1 -1
  4. package/dist/builder.js +6 -6
  5. package/dist/builder.js.map +1 -1
  6. package/dist/codegen/run-codegen.d.ts +3 -2
  7. package/dist/codegen/run-codegen.d.ts.map +1 -1
  8. package/dist/codegen/run-codegen.js +41 -12
  9. package/dist/codegen/run-codegen.js.map +1 -1
  10. package/dist/commands/build.d.ts +0 -4
  11. package/dist/commands/build.d.ts.map +1 -1
  12. package/dist/commands/build.js +0 -5
  13. package/dist/commands/build.js.map +1 -1
  14. package/dist/commands/codegen.d.ts.map +1 -1
  15. package/dist/commands/codegen.js +2 -4
  16. package/dist/commands/codegen.js.map +1 -1
  17. package/dist/deploy/depends.d.ts +2 -2
  18. package/dist/deploy/depends.d.ts.map +1 -1
  19. package/dist/deploy/depends.js +30 -15
  20. package/dist/deploy/depends.js.map +1 -1
  21. package/dist/deploy/freedesktop/localize.d.ts +6 -0
  22. package/dist/deploy/freedesktop/localize.d.ts.map +1 -0
  23. package/dist/deploy/freedesktop/localize.js +107 -0
  24. package/dist/deploy/freedesktop/localize.js.map +1 -0
  25. package/dist/deploy/install-hints.d.ts.map +1 -1
  26. package/dist/deploy/install-hints.js +5 -0
  27. package/dist/deploy/install-hints.js.map +1 -1
  28. package/dist/deploy/nfpm/config.js +1 -1
  29. package/dist/deploy/nfpm/config.js.map +1 -1
  30. package/dist/deploy/node-runtime/elf.d.ts +1 -1
  31. package/dist/deploy/node-runtime/elf.d.ts.map +1 -1
  32. package/dist/deploy/node-runtime/elf.js +2 -2
  33. package/dist/deploy/node-runtime/elf.js.map +1 -1
  34. package/dist/deploy/node-runtime/index.js +2 -2
  35. package/dist/deploy/node-runtime/index.js.map +1 -1
  36. package/dist/deploy/notices/collect.d.ts +3 -1
  37. package/dist/deploy/notices/collect.d.ts.map +1 -1
  38. package/dist/deploy/notices/collect.js +2 -2
  39. package/dist/deploy/notices/collect.js.map +1 -1
  40. package/dist/deploy/notices/packages.d.ts +2 -1
  41. package/dist/deploy/notices/packages.d.ts.map +1 -1
  42. package/dist/deploy/notices/packages.js +4 -27
  43. package/dist/deploy/notices/packages.js.map +1 -1
  44. package/dist/deploy/payload/icons.d.ts +7 -3
  45. package/dist/deploy/payload/icons.d.ts.map +1 -1
  46. package/dist/deploy/payload/icons.js +46 -28
  47. package/dist/deploy/payload/icons.js.map +1 -1
  48. package/dist/deploy/payload/launcher.d.ts.map +1 -1
  49. package/dist/deploy/payload/launcher.js +2 -0
  50. package/dist/deploy/payload/launcher.js.map +1 -1
  51. package/dist/deploy/payload/stage.d.ts +1 -1
  52. package/dist/deploy/payload/stage.d.ts.map +1 -1
  53. package/dist/deploy/payload/stage.js +23 -13
  54. package/dist/deploy/payload/stage.js.map +1 -1
  55. package/dist/deploy/run-deploy.d.ts.map +1 -1
  56. package/dist/deploy/run-deploy.js +149 -25
  57. package/dist/deploy/run-deploy.js.map +1 -1
  58. package/dist/deploy/settings/build-manifest.d.ts +9 -0
  59. package/dist/deploy/settings/build-manifest.d.ts.map +1 -0
  60. package/dist/deploy/settings/build-manifest.js +80 -0
  61. package/dist/deploy/settings/build-manifest.js.map +1 -0
  62. package/dist/deploy/settings/index.d.ts.map +1 -1
  63. package/dist/deploy/settings/index.js +9 -3
  64. package/dist/deploy/settings/index.js.map +1 -1
  65. package/dist/deploy/settings/libraries.d.ts +8 -0
  66. package/dist/deploy/settings/libraries.d.ts.map +1 -0
  67. package/dist/deploy/settings/libraries.js +39 -0
  68. package/dist/deploy/settings/libraries.js.map +1 -0
  69. package/dist/deploy/settings/paths.d.ts +3 -0
  70. package/dist/deploy/settings/paths.d.ts.map +1 -1
  71. package/dist/deploy/settings/paths.js +6 -32
  72. package/dist/deploy/settings/paths.js.map +1 -1
  73. package/dist/deploy/targets/appimage.d.ts.map +1 -1
  74. package/dist/deploy/targets/appimage.js +19 -13
  75. package/dist/deploy/targets/appimage.js.map +1 -1
  76. package/dist/deploy/targets/flatpak-manifest.d.ts +2 -1
  77. package/dist/deploy/targets/flatpak-manifest.d.ts.map +1 -1
  78. package/dist/deploy/targets/flatpak-manifest.js +5 -1
  79. package/dist/deploy/targets/flatpak-manifest.js.map +1 -1
  80. package/dist/deploy/targets/flatpak-source.d.ts.map +1 -1
  81. package/dist/deploy/targets/flatpak-source.js +30 -16
  82. package/dist/deploy/targets/flatpak-source.js.map +1 -1
  83. package/dist/deploy/tools.d.ts +6 -1
  84. package/dist/deploy/tools.d.ts.map +1 -1
  85. package/dist/deploy/tools.js +26 -1
  86. package/dist/deploy/tools.js.map +1 -1
  87. package/dist/deploy/types.d.ts +4 -4
  88. package/dist/deploy/types.d.ts.map +1 -1
  89. package/dist/deploy/types.js.map +1 -1
  90. package/dist/dev/icon-dir.d.ts +1 -1
  91. package/dist/dev/icon-dir.d.ts.map +1 -1
  92. package/dist/dev/icon-dir.js +20 -7
  93. package/dist/dev/icon-dir.js.map +1 -1
  94. package/dist/dev/locale-dir.d.ts +3 -0
  95. package/dist/dev/locale-dir.d.ts.map +1 -0
  96. package/dist/dev/locale-dir.js +17 -0
  97. package/dist/dev/locale-dir.js.map +1 -0
  98. package/dist/dev/runner-deps.d.ts.map +1 -1
  99. package/dist/dev/runner-deps.js +5 -1
  100. package/dist/dev/runner-deps.js.map +1 -1
  101. package/dist/dev/runner-main.d.ts.map +1 -1
  102. package/dist/dev/runner-main.js +6 -4
  103. package/dist/dev/runner-main.js.map +1 -1
  104. package/dist/dev/runner.d.ts +1 -1
  105. package/dist/dev/runner.d.ts.map +1 -1
  106. package/dist/dev/runner.js +7 -1
  107. package/dist/dev/runner.js.map +1 -1
  108. package/dist/dev/schema-dir.d.ts +1 -1
  109. package/dist/dev/schema-dir.d.ts.map +1 -1
  110. package/dist/dev/schema-dir.js +3 -1
  111. package/dist/dev/schema-dir.js.map +1 -1
  112. package/dist/dev/vite-dev-server.d.ts +1 -0
  113. package/dist/dev/vite-dev-server.d.ts.map +1 -1
  114. package/dist/dev/vite-dev-server.js.map +1 -1
  115. package/dist/i18n/binding-extractor.d.ts +30 -0
  116. package/dist/i18n/binding-extractor.d.ts.map +1 -0
  117. package/dist/i18n/binding-extractor.js +1324 -0
  118. package/dist/i18n/binding-extractor.js.map +1 -0
  119. package/dist/i18n/catalog-template.d.ts +3 -0
  120. package/dist/i18n/catalog-template.d.ts.map +1 -0
  121. package/dist/i18n/catalog-template.js +18 -0
  122. package/dist/i18n/catalog-template.js.map +1 -0
  123. package/dist/i18n/catalogs.d.ts +21 -0
  124. package/dist/i18n/catalogs.d.ts.map +1 -0
  125. package/dist/i18n/catalogs.js +139 -0
  126. package/dist/i18n/catalogs.js.map +1 -0
  127. package/dist/i18n/extractor-plugin.d.ts +8 -0
  128. package/dist/i18n/extractor-plugin.d.ts.map +1 -0
  129. package/dist/i18n/extractor-plugin.js +247 -0
  130. package/dist/i18n/extractor-plugin.js.map +1 -0
  131. package/dist/i18n/metadata-templates.d.ts +11 -0
  132. package/dist/i18n/metadata-templates.d.ts.map +1 -0
  133. package/dist/i18n/metadata-templates.js +11 -0
  134. package/dist/i18n/metadata-templates.js.map +1 -0
  135. package/dist/i18n/source-messages.d.ts +23 -0
  136. package/dist/i18n/source-messages.d.ts.map +1 -0
  137. package/dist/i18n/source-messages.js +403 -0
  138. package/dist/i18n/source-messages.js.map +1 -0
  139. package/dist/i18n/types.d.ts +10 -0
  140. package/dist/i18n/types.d.ts.map +1 -0
  141. package/dist/i18n/types.js +108 -0
  142. package/dist/i18n/types.js.map +1 -0
  143. package/dist/internal/build-manifest.d.ts +20 -0
  144. package/dist/internal/build-manifest.d.ts.map +1 -0
  145. package/dist/internal/build-manifest.js +6 -0
  146. package/dist/internal/build-manifest.js.map +1 -0
  147. package/dist/internal/icon-path.d.ts +13 -0
  148. package/dist/internal/icon-path.d.ts.map +1 -0
  149. package/dist/internal/icon-path.js +86 -0
  150. package/dist/internal/icon-path.js.map +1 -0
  151. package/dist/internal/source-imports.d.ts +12 -0
  152. package/dist/internal/source-imports.d.ts.map +1 -0
  153. package/dist/internal/source-imports.js +123 -0
  154. package/dist/internal/source-imports.js.map +1 -0
  155. package/dist/mcp/client.d.ts +0 -1
  156. package/dist/mcp/client.d.ts.map +1 -1
  157. package/dist/mcp/client.js +11 -39
  158. package/dist/mcp/client.js.map +1 -1
  159. package/dist/mcp/handlers.d.ts +2 -1
  160. package/dist/mcp/handlers.d.ts.map +1 -1
  161. package/dist/mcp/handlers.js.map +1 -1
  162. package/dist/mcp/widget-properties.d.ts.map +1 -1
  163. package/dist/mcp/widget-properties.js +3 -12
  164. package/dist/mcp/widget-properties.js.map +1 -1
  165. package/dist/settings/render.d.ts +10 -1
  166. package/dist/settings/render.d.ts.map +1 -1
  167. package/dist/settings/render.js +78 -20
  168. package/dist/settings/render.js.map +1 -1
  169. package/dist/settings/schema.d.ts +4 -3
  170. package/dist/settings/schema.d.ts.map +1 -1
  171. package/dist/settings/schema.js +124 -15
  172. package/dist/settings/schema.js.map +1 -1
  173. package/dist/vite-plugins/asset-emitter.d.ts +1 -1
  174. package/dist/vite-plugins/asset-emitter.d.ts.map +1 -1
  175. package/dist/vite-plugins/asset-emitter.js.map +1 -1
  176. package/dist/vite-plugins/asset-extensions.d.ts.map +1 -1
  177. package/dist/vite-plugins/asset-extensions.js +1 -0
  178. package/dist/vite-plugins/asset-extensions.js.map +1 -1
  179. package/dist/vite-plugins/asset-imports.d.ts +2 -1
  180. package/dist/vite-plugins/asset-imports.d.ts.map +1 -1
  181. package/dist/vite-plugins/asset-imports.js +39 -28
  182. package/dist/vite-plugins/asset-imports.js.map +1 -1
  183. package/dist/vite-plugins/asset-specifier.d.ts +13 -1
  184. package/dist/vite-plugins/asset-specifier.d.ts.map +1 -1
  185. package/dist/vite-plugins/asset-specifier.js +39 -1
  186. package/dist/vite-plugins/asset-specifier.js.map +1 -1
  187. package/dist/vite-plugins/build-manifest.d.ts +5 -0
  188. package/dist/vite-plugins/build-manifest.d.ts.map +1 -0
  189. package/dist/vite-plugins/{bundled-packages.js → build-manifest.js} +20 -14
  190. package/dist/vite-plugins/build-manifest.js.map +1 -0
  191. package/dist/vite-plugins/built-url.d.ts +1 -1
  192. package/dist/vite-plugins/built-url.d.ts.map +1 -1
  193. package/dist/vite-plugins/built-url.js +37 -24
  194. package/dist/vite-plugins/built-url.js.map +1 -1
  195. package/dist/vite-plugins/i18n.d.ts +5 -0
  196. package/dist/vite-plugins/i18n.d.ts.map +1 -0
  197. package/dist/vite-plugins/i18n.js +88 -0
  198. package/dist/vite-plugins/i18n.js.map +1 -0
  199. package/dist/vite-plugins/icons.d.ts +2 -1
  200. package/dist/vite-plugins/icons.d.ts.map +1 -1
  201. package/dist/vite-plugins/icons.js +25 -12
  202. package/dist/vite-plugins/icons.js.map +1 -1
  203. package/dist/vite-plugins/index.d.ts +2 -1
  204. package/dist/vite-plugins/index.d.ts.map +1 -1
  205. package/dist/vite-plugins/index.js +9 -5
  206. package/dist/vite-plugins/index.js.map +1 -1
  207. package/dist/vite-plugins/native.d.ts.map +1 -1
  208. package/dist/vite-plugins/native.js +23 -4
  209. package/dist/vite-plugins/native.js.map +1 -1
  210. package/dist/vite-plugins/resource-init-module.d.ts +7 -3
  211. package/dist/vite-plugins/resource-init-module.d.ts.map +1 -1
  212. package/dist/vite-plugins/resource-init-module.js +39 -14
  213. package/dist/vite-plugins/resource-init-module.js.map +1 -1
  214. package/dist/vite-plugins/resource-shared.d.ts +3 -1
  215. package/dist/vite-plugins/resource-shared.d.ts.map +1 -1
  216. package/dist/vite-plugins/resource-shared.js +3 -1
  217. package/dist/vite-plugins/resource-shared.js.map +1 -1
  218. package/dist/vite-plugins/resources.d.ts +1 -1
  219. package/dist/vite-plugins/resources.d.ts.map +1 -1
  220. package/dist/vite-plugins/resources.js +877 -86
  221. package/dist/vite-plugins/resources.js.map +1 -1
  222. package/dist/vite-plugins/self-contained.d.ts.map +1 -1
  223. package/dist/vite-plugins/self-contained.js +128 -135
  224. package/dist/vite-plugins/self-contained.js.map +1 -1
  225. package/dist/vite-plugins/settings-worker-env.d.ts.map +1 -1
  226. package/dist/vite-plugins/settings-worker-env.js +6 -3
  227. package/dist/vite-plugins/settings-worker-env.js.map +1 -1
  228. package/dist/vite-plugins/settings.d.ts +3 -1
  229. package/dist/vite-plugins/settings.d.ts.map +1 -1
  230. package/dist/vite-plugins/settings.js +63 -23
  231. package/dist/vite-plugins/settings.js.map +1 -1
  232. package/env.d.ts +11 -192
  233. package/package.json +11 -10
  234. package/src/builder.ts +7 -7
  235. package/src/codegen/run-codegen.ts +64 -15
  236. package/src/commands/build.ts +0 -5
  237. package/src/commands/codegen.ts +2 -4
  238. package/src/deploy/depends.ts +47 -18
  239. package/src/deploy/freedesktop/localize.ts +144 -0
  240. package/src/deploy/install-hints.ts +5 -0
  241. package/src/deploy/nfpm/config.ts +1 -1
  242. package/src/deploy/node-runtime/elf.ts +3 -3
  243. package/src/deploy/node-runtime/index.ts +2 -2
  244. package/src/deploy/notices/collect.ts +4 -2
  245. package/src/deploy/notices/packages.ts +5 -43
  246. package/src/deploy/payload/icons.ts +64 -34
  247. package/src/deploy/payload/launcher.ts +2 -0
  248. package/src/deploy/payload/stage.ts +40 -12
  249. package/src/deploy/run-deploy.ts +214 -24
  250. package/src/deploy/settings/build-manifest.ts +122 -0
  251. package/src/deploy/settings/index.ts +9 -4
  252. package/src/deploy/settings/libraries.ts +57 -0
  253. package/src/deploy/settings/paths.ts +10 -47
  254. package/src/deploy/targets/appimage.ts +32 -15
  255. package/src/deploy/targets/flatpak-manifest.ts +7 -1
  256. package/src/deploy/targets/flatpak-source.ts +42 -20
  257. package/src/deploy/tools.ts +35 -0
  258. package/src/deploy/types.ts +4 -4
  259. package/src/dev/icon-dir.ts +31 -7
  260. package/src/dev/locale-dir.ts +22 -0
  261. package/src/dev/runner-deps.ts +5 -1
  262. package/src/dev/runner-main.ts +6 -4
  263. package/src/dev/runner.ts +10 -2
  264. package/src/dev/schema-dir.ts +3 -1
  265. package/src/dev/vite-dev-server.ts +1 -0
  266. package/src/i18n/binding-extractor.ts +1969 -0
  267. package/src/i18n/catalog-template.ts +24 -0
  268. package/src/i18n/catalogs.ts +216 -0
  269. package/src/i18n/extractor-plugin.ts +413 -0
  270. package/src/i18n/metadata-templates.ts +29 -0
  271. package/src/i18n/source-messages.ts +608 -0
  272. package/src/i18n/types.ts +154 -0
  273. package/src/internal/build-manifest.ts +32 -0
  274. package/src/internal/icon-path.ts +127 -0
  275. package/src/internal/source-imports.ts +171 -0
  276. package/src/mcp/client.ts +17 -44
  277. package/src/mcp/handlers.ts +7 -6
  278. package/src/mcp/widget-properties.ts +3 -14
  279. package/src/settings/render.ts +113 -21
  280. package/src/settings/schema.ts +192 -17
  281. package/src/vite-plugins/asset-emitter.ts +1 -1
  282. package/src/vite-plugins/asset-extensions.ts +1 -0
  283. package/src/vite-plugins/asset-imports.ts +62 -33
  284. package/src/vite-plugins/asset-specifier.ts +73 -1
  285. package/src/vite-plugins/{bundled-packages.ts → build-manifest.ts} +30 -19
  286. package/src/vite-plugins/built-url.ts +49 -28
  287. package/src/vite-plugins/i18n.ts +146 -0
  288. package/src/vite-plugins/icons.ts +33 -14
  289. package/src/vite-plugins/index.ts +15 -5
  290. package/src/vite-plugins/native.ts +31 -4
  291. package/src/vite-plugins/resource-init-module.ts +44 -15
  292. package/src/vite-plugins/resource-shared.ts +4 -0
  293. package/src/vite-plugins/resources.ts +1397 -100
  294. package/src/vite-plugins/self-contained.ts +150 -169
  295. package/src/vite-plugins/settings-worker-env.ts +7 -3
  296. package/src/vite-plugins/settings.ts +89 -21
  297. package/dist/vite-plugins/bundled-packages.d.ts +0 -5
  298. package/dist/vite-plugins/bundled-packages.d.ts.map +0 -1
  299. package/dist/vite-plugins/bundled-packages.js.map +0 -1
@@ -1,9 +1,81 @@
1
1
  import { DATA_IMPORT_PREFIX } from "../internal/data-dir.js";
2
2
  import { ASSET_PATH_RE, ASSET_RE } from "./asset-extensions.js";
3
+ import { stripQuery } from "./strip-query.js";
4
+
5
+ type ResourceSpecifier = {
6
+ assetSource: string;
7
+ resourcePath: string | null;
8
+ };
9
+
10
+ type IconSpecifier = {
11
+ assetSource: string;
12
+ iconName: string | null;
13
+ };
3
14
 
4
15
  const DATA_PREFIX = `${DATA_IMPORT_PREFIX}/`;
16
+ const RELATIVE_PREFIX_RE = /^\.\.?(?:\/|$)/;
17
+ const RESOURCE_QUERY_RE = /^resource(?:=([^&#]+))?$/;
18
+ const ICON_QUERY_RE = /^icon(?:=([^&#]+))?$/;
19
+ const URL_QUERY_RE = /(?:^|&)url(?:&|$)/;
20
+
21
+ const getQuery = (source: string): string | null => {
22
+ const queryIndex = source.indexOf("?");
23
+
24
+ return queryIndex === -1 ? null : source.slice(queryIndex + 1);
25
+ };
5
26
 
6
27
  const isAssetSpecifier = (source: string): boolean => ASSET_PATH_RE.test(source);
7
28
  const isDataAsset = (source: string): boolean => source.startsWith(DATA_PREFIX) && ASSET_RE.test(source);
8
29
 
9
- export { DATA_PREFIX, isAssetSpecifier, isDataAsset };
30
+ const isBareRelativeAsset = (source: string): boolean =>
31
+ getQuery(source) === null && RELATIVE_PREFIX_RE.test(source) && ASSET_RE.test(source);
32
+
33
+ const parseResourceSpecifier = (source: string): ResourceSpecifier | null => {
34
+ const query = getQuery(source);
35
+ const assetSource = stripQuery(source);
36
+
37
+ if (query === null || !ASSET_RE.test(assetSource)) {
38
+ return null;
39
+ }
40
+
41
+ const match = RESOURCE_QUERY_RE.exec(query);
42
+
43
+ if (match === null) {
44
+ return null;
45
+ }
46
+
47
+ return { assetSource, resourcePath: match[1] ?? null };
48
+ };
49
+
50
+ const parseIconSpecifier = (source: string): IconSpecifier | null => {
51
+ const query = getQuery(source);
52
+ const assetSource = stripQuery(source);
53
+
54
+ if (query === null || !ASSET_RE.test(assetSource)) {
55
+ return null;
56
+ }
57
+
58
+ const match = ICON_QUERY_RE.exec(query);
59
+
60
+ if (match === null) {
61
+ return null;
62
+ }
63
+
64
+ return { assetSource, iconName: match[1] ?? null };
65
+ };
66
+
67
+ const isUrlSpecifier = (source: string): boolean => {
68
+ const query = getQuery(source);
69
+
70
+ return query !== null && URL_QUERY_RE.test(query);
71
+ };
72
+
73
+ export {
74
+ DATA_PREFIX,
75
+ isAssetSpecifier,
76
+ isBareRelativeAsset,
77
+ isDataAsset,
78
+ isUrlSpecifier,
79
+ parseIconSpecifier,
80
+ parseResourceSpecifier,
81
+ };
@@ -2,6 +2,14 @@ import type { Plugin } from "vite";
2
2
  import { isRecord, sortStringsBy } from "@gtkx/utils";
3
3
  import { readFileSync } from "node:fs";
4
4
  import { dirname, join, relative, resolve } from "node:path";
5
+ import {
6
+ BUILD_MANIFEST_FILENAME,
7
+ BUILD_MANIFEST_FORMAT_VERSION,
8
+ BUILD_MANIFEST_GENERATOR,
9
+ type BuildManifest,
10
+ type BuildManifestCollector,
11
+ type RecordedPackage,
12
+ } from "../internal/build-manifest.js";
5
13
  import { stripQuery } from "./strip-query.js";
6
14
 
7
15
  type PackageIdentity = {
@@ -9,22 +17,17 @@ type PackageIdentity = {
9
17
  version: string | null;
10
18
  };
11
19
 
12
- type RecordedPackage = PackageIdentity & {
13
- dir: string;
14
- };
15
-
16
- type PackagesState = {
20
+ type ManifestState = {
17
21
  outDir: string;
18
22
  };
19
23
 
20
- const BUNDLED_PACKAGES_FILENAME = "gtkx-packages.json";
21
- const MANIFEST_FILENAME = "package.json";
24
+ const PACKAGE_MANIFEST_FILENAME = "package.json";
22
25
  const DEFAULT_OUT_DIR = "dist";
23
26
  const JSON_INDENT = 4;
24
27
 
25
28
  const identityIn = (dir: string): PackageIdentity | null => {
26
29
  try {
27
- const parsed: unknown = JSON.parse(readFileSync(join(dir, MANIFEST_FILENAME), "utf8"));
30
+ const parsed: unknown = JSON.parse(readFileSync(join(dir, PACKAGE_MANIFEST_FILENAME), "utf8"));
28
31
 
29
32
  if (!isRecord(parsed) || typeof parsed.name !== "string") {
30
33
  return null;
@@ -54,26 +57,27 @@ const packageForModule = (id: string): RecordedPackage | null => {
54
57
  return path.startsWith("/") ? packageIn(dirname(path)) : null;
55
58
  };
56
59
 
57
- const keyFor = (entry: RecordedPackage): string => `${entry.name}@${entry.version ?? ""}`;
60
+ const packageKey = (entry: RecordedPackage): string => `${entry.name}@${entry.version ?? ""}`;
58
61
 
59
62
  const relativeTo = (outDir: string, entry: RecordedPackage): RecordedPackage => ({
60
63
  ...entry,
61
64
  dir: relative(outDir, entry.dir),
62
65
  });
63
66
 
64
- const renderPackages = (outDir: string, ids: string[]): string => {
67
+ const packagesFor = (outDir: string, ids: string[]): RecordedPackage[] => {
65
68
  const found = ids.map((id) => packageForModule(id)).filter((entry) => entry !== null);
66
- const unique: Map<string, RecordedPackage> = new Map(found.map((entry) => [keyFor(entry), entry]));
67
- const packages = sortStringsBy(unique.values(), keyFor).map((entry) => relativeTo(outDir, entry));
69
+ const unique: Map<string, RecordedPackage> = new Map(found.map((entry) => [packageKey(entry), entry]));
68
70
 
69
- return `${JSON.stringify({ packages }, null, JSON_INDENT)}\n`;
71
+ return sortStringsBy(unique.values(), packageKey).map((entry) => relativeTo(outDir, entry));
70
72
  };
71
73
 
72
- function gtkxBundledPackages(root: string): Plugin {
73
- const state: PackagesState = { outDir: join(root, DEFAULT_OUT_DIR) };
74
+ const renderManifest = (manifest: BuildManifest): string => `${JSON.stringify(manifest, null, JSON_INDENT)}\n`;
75
+
76
+ function gtkxBuildManifest(root: string, collector: BuildManifestCollector): Plugin {
77
+ const state: ManifestState = { outDir: join(root, DEFAULT_OUT_DIR) };
74
78
 
75
79
  return {
76
- name: "gtkx:bundled-packages",
80
+ name: "gtkx:build-manifest",
77
81
  apply: "build",
78
82
 
79
83
  configResolved(config) {
@@ -83,13 +87,20 @@ function gtkxBundledPackages(root: string): Plugin {
83
87
  generateBundle(_options, bundle) {
84
88
  const ids = Object.values(bundle).flatMap((output) => (output.type === "chunk" ? output.moduleIds : []));
85
89
 
90
+ const manifest: BuildManifest = {
91
+ generator: BUILD_MANIFEST_GENERATOR,
92
+ formatVersion: BUILD_MANIFEST_FORMAT_VERSION,
93
+ schemas: collector.schemas,
94
+ packages: packagesFor(state.outDir, ids),
95
+ };
96
+
86
97
  this.emitFile({
87
98
  type: "asset",
88
- fileName: BUNDLED_PACKAGES_FILENAME,
89
- source: renderPackages(state.outDir, ids),
99
+ fileName: BUILD_MANIFEST_FILENAME,
100
+ source: renderManifest(manifest),
90
101
  });
91
102
  },
92
103
  };
93
104
  }
94
105
 
95
- export { BUNDLED_PACKAGES_FILENAME, gtkxBundledPackages };
106
+ export { gtkxBuildManifest };
@@ -1,14 +1,12 @@
1
- import type { Plugin } from "vite";
2
- import { posix } from "node:path";
1
+ import type { Plugin, UserConfig } from "vite";
2
+ import { existsSync } from "node:fs";
3
+ import { isAbsolute } from "node:path";
4
+ import { stripQuery } from "./strip-query.js";
3
5
 
4
- const EXECUTABLE_URL_BASE = "`file://${process.execPath}`";
5
- const LEADING_SLASHES = /^\/+/;
6
+ type PluginState = { isBuild: boolean };
7
+ type WatchContext = { addWatchFile: (file: string) => void };
6
8
 
7
- const executableRelativeUrl = (target: string): string => {
8
- const specifier = JSON.stringify(target.replace(LEADING_SLASHES, "./"));
9
-
10
- return `decodeURIComponent(new URL(${specifier}, ${EXECUTABLE_URL_BASE}).pathname)`;
11
- };
9
+ const URL_QUERY_RE = /\?url(?:&t=\d+)?$/;
12
10
 
13
11
  const bundleRelativeUrl = (filename: string): string => {
14
12
  const specifier = JSON.stringify(`./${filename}`);
@@ -19,36 +17,59 @@ const bundleRelativeUrl = (filename: string): string => {
19
17
  const renderAssetUrl = (
20
18
  filename: string,
21
19
  type: string,
22
- assetBase: string | undefined,
23
20
  ): { runtime: string } | undefined => {
24
21
  if (type !== "asset") {
25
22
  return undefined;
26
23
  }
27
24
 
28
- if (assetBase === undefined) {
29
- return { runtime: bundleRelativeUrl(filename) };
25
+ return { runtime: bundleRelativeUrl(filename) };
26
+ };
27
+
28
+ const configureBuiltUrl = (userConfig: UserConfig, command: string): UserConfig | undefined => {
29
+ if (command !== "build" || userConfig.experimental?.renderBuiltUrl) {
30
+ return undefined;
30
31
  }
31
32
 
32
- return { runtime: executableRelativeUrl(posix.join(assetBase, filename)) };
33
+ return {
34
+ experimental: {
35
+ renderBuiltUrl(filename, { type }) {
36
+ return renderAssetUrl(filename, type);
37
+ },
38
+ },
39
+ };
33
40
  };
34
41
 
35
- function gtkxBuiltUrl(assetBase?: string): Plugin {
42
+ const loadDevFileUrl = (ctx: WatchContext, state: PluginState, id: string): string | undefined => {
43
+ if (state.isBuild || !URL_QUERY_RE.test(id)) {
44
+ return undefined;
45
+ }
46
+
47
+ const file = stripQuery(id);
48
+
49
+ if (!isAbsolute(file) || !existsSync(file)) {
50
+ return undefined;
51
+ }
52
+
53
+ ctx.addWatchFile(file);
54
+
55
+ return `export default ${JSON.stringify(file)};`;
56
+ };
57
+
58
+ function gtkxBuiltUrl(): Plugin {
59
+ const state: PluginState = { isBuild: false };
60
+
36
61
  return {
37
62
  name: "gtkx:built-url",
38
- apply: "build",
39
-
40
- config(userConfig) {
41
- if (userConfig.experimental?.renderBuiltUrl) {
42
- return;
43
- }
44
-
45
- return {
46
- experimental: {
47
- renderBuiltUrl(filename, { type }) {
48
- return renderAssetUrl(filename, type, assetBase);
49
- },
50
- },
51
- };
63
+ enforce: "pre",
64
+
65
+ config: (userConfig, { command }) => configureBuiltUrl(userConfig, command),
66
+
67
+ configResolved(config) {
68
+ state.isBuild = config.command === "build";
69
+ },
70
+
71
+ load(id) {
72
+ return loadDevFileUrl(this, state, id);
52
73
  },
53
74
  };
54
75
  }
@@ -0,0 +1,146 @@
1
+ import type { ConfigLoader } from "@gtkx/config";
2
+ import type { Plugin, ResolvedConfig } from "vite";
3
+ import { createConfigLoader } from "@gtkx/config/internal";
4
+ import { existsSync, mkdirSync } from "node:fs";
5
+ import { createRequire } from "node:module";
6
+ import { dirname, join, posix, relative, resolve } from "node:path";
7
+ import {
8
+ type CatalogProject,
9
+ compileCatalogs,
10
+ LOCALE_DIRNAME,
11
+ resolveCatalogProject,
12
+ synchronizeCatalogs,
13
+ } from "../i18n/catalogs.js";
14
+ import { extractSourceCatalog } from "../i18n/source-messages.js";
15
+ import { discoverSourceFiles } from "../internal/source-imports.js";
16
+ import { stripQuery } from "./strip-query.js";
17
+
18
+ type I18nState = {
19
+ entryPath: string;
20
+ i18nRoot: string;
21
+ outDir: string;
22
+ project: CatalogProject | null;
23
+ };
24
+
25
+ const BOOTSTRAP_SPECIFIER = "@gtkx/i18n/bootstrap";
26
+ const LOCALE_URL_PLACEHOLDER = "__GTKX_BUNDLE_LOCALE_DIR__";
27
+ const LOCALE_MODULE_PATHS = new Set(["dist/locale.js", "src/locale.ts"]);
28
+ const LOCALE_URL_PATTERN = /new URL\((["'])locale\1,\s*import\.meta\.url\)/;
29
+
30
+ const isI18nLocaleModule = (state: I18nState, id: string): boolean => {
31
+ if (state.i18nRoot.length === 0) {
32
+ return false;
33
+ }
34
+
35
+ const path = relative(state.i18nRoot, stripQuery(id)).replaceAll("\\", "/");
36
+
37
+ return LOCALE_MODULE_PATHS.has(path);
38
+ };
39
+
40
+ const markLocaleUrl = (code: string): string =>
41
+ code.replace(
42
+ LOCALE_URL_PATTERN,
43
+ () => `new URL(${JSON.stringify(LOCALE_URL_PLACEHOLDER)}, import.meta.url)`,
44
+ );
45
+
46
+ const localeUrlForChunk = (fileName: string): string => {
47
+ const path = posix.relative(posix.dirname(fileName), LOCALE_DIRNAME);
48
+
49
+ return path.length === 0 ? "." : path;
50
+ };
51
+
52
+ const rebaseLocaleUrl = (state: I18nState, code: string, fileName: string, moduleIds: string[]): string | null => {
53
+ if (moduleIds.every((id) => !isI18nLocaleModule(state, id))) {
54
+ return null;
55
+ }
56
+
57
+ const placeholder = JSON.stringify(LOCALE_URL_PLACEHOLDER);
58
+
59
+ if (!code.includes(placeholder)) {
60
+ throw new Error(`Unable to locate the @gtkx/i18n locale fallback in ${fileName}`);
61
+ }
62
+
63
+ return code.replace(placeholder, () => JSON.stringify(localeUrlForChunk(fileName)));
64
+ };
65
+
66
+ const applyResolvedConfig = async (
67
+ state: I18nState,
68
+ config: ResolvedConfig,
69
+ loadConfig: ConfigLoader,
70
+ ): Promise<void> => {
71
+ state.entryPath = resolve(config.root, state.entryPath);
72
+ state.outDir = resolve(config.root, config.build.outDir);
73
+ const application = await loadConfig.resolve(config.root);
74
+ state.project = resolveCatalogProject(config.root, application.applicationId);
75
+
76
+ if (state.project !== null) {
77
+ const projectRequire = createRequire(resolve(config.root, "package.json"));
78
+ state.i18nRoot = dirname(projectRequire.resolve("@gtkx/i18n/package.json"));
79
+ }
80
+ };
81
+
82
+ const buildCatalogs = (state: I18nState): void => {
83
+ const project = state.project;
84
+
85
+ if (project === null) {
86
+ return;
87
+ }
88
+
89
+ mkdirSync(state.outDir, { recursive: true });
90
+ compileCatalogs(project, resolve(state.outDir, LOCALE_DIRNAME));
91
+ };
92
+
93
+ const extractProjectMessages = async (
94
+ state: I18nState,
95
+ shouldPreserveMetadataMessages: boolean,
96
+ ): Promise<void> => {
97
+ const project = state.project;
98
+
99
+ if (project === null) {
100
+ return;
101
+ }
102
+
103
+ const srcDir = join(project.root, "src");
104
+ const sourceFiles = discoverSourceFiles(existsSync(srcDir) ? srcDir : project.root);
105
+ await extractSourceCatalog(project, sourceFiles, shouldPreserveMetadataMessages);
106
+ synchronizeCatalogs(project);
107
+ };
108
+
109
+ const gtkxI18n = (
110
+ entryPath: string,
111
+ loadConfig: ConfigLoader = createConfigLoader(),
112
+ shouldPreserveMetadataMessages = true,
113
+ ): Plugin => {
114
+ const state: I18nState = { entryPath, i18nRoot: "", outDir: "", project: null };
115
+
116
+ return {
117
+ name: "gtkx:i18n",
118
+ enforce: "pre",
119
+
120
+ configResolved: (config) => applyResolvedConfig(state, config, loadConfig),
121
+
122
+ buildStart: () => extractProjectMessages(state, shouldPreserveMetadataMessages),
123
+
124
+ transform(code, id) {
125
+ if (isI18nLocaleModule(state, id)) {
126
+ return markLocaleUrl(code);
127
+ }
128
+
129
+ if (state.project === null || resolve(stripQuery(id)) !== state.entryPath) {
130
+ return;
131
+ }
132
+
133
+ return `import ${JSON.stringify(BOOTSTRAP_SPECIFIER)};\n${code}`;
134
+ },
135
+
136
+ renderChunk(code, chunk) {
137
+ return rebaseLocaleUrl(state, code, chunk.fileName, chunk.moduleIds);
138
+ },
139
+
140
+ generateBundle() {
141
+ buildCatalogs(state);
142
+ },
143
+ };
144
+ };
145
+
146
+ export { gtkxI18n };
@@ -1,14 +1,21 @@
1
+ import type { ConfigLoader } from "@gtkx/config";
1
2
  import type { Plugin, UserConfig } from "vite";
3
+ import { createConfigLoader } from "@gtkx/config/internal";
2
4
  import { info } from "@gtkx/utils";
3
5
  import { readFileSync } from "node:fs";
4
6
  import { join } from "node:path";
5
7
  import type { AssetEmitter } from "./asset-emitter.js";
6
8
  import { prependBanner } from "../internal/banner.js";
7
- import { resolveDataDir } from "../internal/data-dir.js";
9
+ import {
10
+ relativeIconPath,
11
+ resolveApplicationIcon,
12
+ type ResolvedApplicationIcon,
13
+ } from "../internal/icon-path.js";
8
14
  import { type ListedFile, listFilesRecursive } from "../internal/list-files.js";
9
15
 
10
16
  type PluginState = {
11
- iconsDir: string | null;
17
+ applicationId: string;
18
+ source: ResolvedApplicationIcon;
12
19
  };
13
20
 
14
21
  const ICONS_DIR = "icons";
@@ -20,14 +27,25 @@ const XDG_ENV_BANNER = [
20
27
  "].join(\":\");",
21
28
  ].join("\n");
22
29
 
23
- const findIconFiles = (iconsDir: string | null): ListedFile[] =>
24
- iconsDir === null ? [] : listFilesRecursive(iconsDir);
30
+ const findIconFiles = (state: PluginState): ListedFile[] => {
31
+ if (state.source.kind === "theme") {
32
+ return listFilesRecursive(state.source.path);
33
+ }
34
+
35
+ if (state.source.kind === "none") {
36
+ return [];
37
+ }
25
38
 
26
- const resolveIconsDir = (config: UserConfig): string | null => {
27
- const root = config.root ?? process.cwd();
28
- const dataDir = resolveDataDir(root);
39
+ return [{
40
+ absPath: state.source.path,
41
+ rel: relativeIconPath(state.applicationId, state.source.path),
42
+ }];
43
+ };
29
44
 
30
- return dataDir === null ? null : join(root, dataDir, ICONS_DIR);
45
+ const applyUserConfig = async (state: PluginState, config: UserConfig, loadConfig: ConfigLoader): Promise<void> => {
46
+ const { config: gtkxConfig, root } = await loadConfig.load(config.root ?? process.cwd());
47
+ state.applicationId = gtkxConfig.applicationId;
48
+ state.source = resolveApplicationIcon(root, gtkxConfig.applicationId, gtkxConfig.applicationIcon);
31
49
  };
32
50
 
33
51
  const emitIcons = (ctx: AssetEmitter, icons: ListedFile[]): void => {
@@ -40,9 +58,10 @@ const emitIcons = (ctx: AssetEmitter, icons: ListedFile[]): void => {
40
58
  }
41
59
  };
42
60
 
43
- function gtkxIcons(): Plugin {
61
+ function gtkxIcons(loadConfig: ConfigLoader = createConfigLoader()): Plugin {
44
62
  const state: PluginState = {
45
- iconsDir: null,
63
+ applicationId: "",
64
+ source: { kind: "none" },
46
65
  };
47
66
 
48
67
  return {
@@ -50,12 +69,12 @@ function gtkxIcons(): Plugin {
50
69
  enforce: "pre",
51
70
  apply: "build",
52
71
 
53
- config(config: UserConfig) {
54
- state.iconsDir = resolveIconsDir(config);
72
+ async config(config: UserConfig) {
73
+ await applyUserConfig(state, config, loadConfig);
55
74
  },
56
75
 
57
76
  outputOptions(options) {
58
- if (findIconFiles(state.iconsDir).length === 0) {
77
+ if (findIconFiles(state).length === 0) {
59
78
  return;
60
79
  }
61
80
 
@@ -63,7 +82,7 @@ function gtkxIcons(): Plugin {
63
82
  },
64
83
 
65
84
  buildEnd() {
66
- emitIcons(this, findIconFiles(state.iconsDir));
85
+ emitIcons(this, findIconFiles(state));
67
86
  },
68
87
  };
69
88
  }
@@ -1,8 +1,11 @@
1
1
  import type { Plugin } from "vite";
2
2
  import { createConfigLoader } from "@gtkx/config/internal";
3
3
  import createConfigPlugin from "@gtkx/config/vite-plugin";
4
+ import type { BuildManifestCollector } from "../internal/build-manifest.js";
4
5
  import { gtkxAssetImports } from "./asset-imports.js";
6
+ import { gtkxBuiltUrl } from "./built-url.js";
5
7
  import { gtkxCss } from "./css.js";
8
+ import { gtkxI18n } from "./i18n.js";
6
9
  import { gtkxIcons } from "./icons.js";
7
10
  import { gtkxReactCompiler } from "./react-compiler.js";
8
11
  import { gtkxResources } from "./resources.js";
@@ -10,17 +13,24 @@ import { gtkxSettings } from "./settings.js";
10
13
  import { gtkxStoreLinks } from "./store-links.js";
11
14
  import { gtkxUndeclaredLibrary } from "./undeclared-library.js";
12
15
 
13
- const gtkxVitePlugins = (mode?: string): Plugin[] => {
16
+ const gtkxVitePlugins = (
17
+ mode?: string,
18
+ entryPath?: string,
19
+ buildManifest?: BuildManifestCollector,
20
+ shouldPreserveI18nMetadata = true,
21
+ ): Plugin[] => {
14
22
  const loadConfig = createConfigLoader(mode === undefined ? {} : { mode });
15
23
 
16
24
  return [
17
25
  createConfigPlugin({ name: "gtkx:config", loadConfig }),
26
+ ...(entryPath === undefined ? [] : [gtkxI18n(entryPath, loadConfig, shouldPreserveI18nMetadata)]),
18
27
  gtkxStoreLinks(),
19
28
  gtkxUndeclaredLibrary(loadConfig),
20
- gtkxSettings(),
21
- gtkxIcons(),
22
- gtkxAssetImports(),
23
- gtkxResources(loadConfig),
29
+ gtkxSettings(loadConfig, buildManifest),
30
+ gtkxIcons(loadConfig),
31
+ gtkxAssetImports(loadConfig),
32
+ gtkxBuiltUrl(),
33
+ gtkxResources(loadConfig, entryPath),
24
34
  gtkxCss(),
25
35
  gtkxReactCompiler(loadConfig),
26
36
  ];
@@ -6,6 +6,7 @@ import { dirname, join } from "node:path";
6
6
  import { stripQuery } from "./strip-query.js";
7
7
 
8
8
  const EMITTED_BINDING_SPECIFIER = "./gtkx.node";
9
+ const BINDING_FILENAME = "gtkx.node";
9
10
 
10
11
  function resolveBinaryPath(projectRequire: ReturnType<typeof createRequire>, currentArch: string): string {
11
12
  const nativeRoot = dirname(projectRequire.resolve("@gtkx/native/package.json"));
@@ -30,7 +31,7 @@ function resolvePlatformBinary(projectRequire: ReturnType<typeof createRequire>)
30
31
  return readFileSync(resolveBinaryPath(projectRequire, currentArch));
31
32
  }
32
33
 
33
- function rewriteLoader(code: string): string {
34
+ function rewriteLoader(code: string, bindingReferenceId: string): string {
34
35
  const idents: string[] = [];
35
36
 
36
37
  for (const match of code.matchAll(/export\s*\{\s*([A-Za-z0-9_$]+)\s*\}/g)) {
@@ -45,15 +46,37 @@ function rewriteLoader(code: string): string {
45
46
 
46
47
  return [
47
48
  "import { createRequire as __gtkxCreateRequire } from \"node:module\";",
48
- `const __gtkxNative = __gtkxCreateRequire(import.meta.url)(${JSON.stringify(EMITTED_BINDING_SPECIFIER)});`,
49
+ "import { fileURLToPath as __gtkxFileURLToPath } from \"node:url\";",
50
+ `const __gtkxNativeLocation = import.meta.ROLLUP_FILE_URL_${bindingReferenceId};`,
51
+ "const __gtkxNativePath = __gtkxNativeLocation.startsWith(\"file:\") " +
52
+ "? __gtkxFileURLToPath(__gtkxNativeLocation) : __gtkxNativeLocation;",
53
+ "const __gtkxNative = __gtkxCreateRequire(import.meta.url)(__gtkxNativePath);",
49
54
  `const { ${bindings} } = __gtkxNative;`,
50
55
  `export { ${bindings} };`,
51
56
  ].join("\n");
52
57
  }
53
58
 
59
+ const transformLoader = (
60
+ code: string,
61
+ id: string,
62
+ loaderPath: string,
63
+ bindingReferenceId: string | null,
64
+ ): string | null => {
65
+ if (stripQuery(id) !== loaderPath) {
66
+ return null;
67
+ }
68
+
69
+ if (bindingReferenceId === null) {
70
+ throw new Error("Cannot render the native loader before its binary has been emitted");
71
+ }
72
+
73
+ return rewriteLoader(code, bindingReferenceId);
74
+ };
75
+
54
76
  function gtkxNative(root: string): Plugin {
55
77
  const projectRequire = createRequire(join(root, "package.json"));
56
78
  let cachedLoaderPath: string | undefined;
79
+ let bindingReferenceId: string | null = null;
57
80
 
58
81
  const loaderPath = (): string => {
59
82
  cachedLoaderPath ??= join(dirname(projectRequire.resolve("@gtkx/native/package.json")), "index.js");
@@ -67,7 +90,11 @@ function gtkxNative(root: string): Plugin {
67
90
  apply: "build",
68
91
 
69
92
  buildStart() {
70
- this.emitFile({ type: "asset", fileName: "gtkx.node", source: resolvePlatformBinary(projectRequire) });
93
+ bindingReferenceId = this.emitFile({
94
+ type: "asset",
95
+ fileName: BINDING_FILENAME,
96
+ source: resolvePlatformBinary(projectRequire),
97
+ });
71
98
  },
72
99
 
73
100
  resolveId(id) {
@@ -75,7 +102,7 @@ function gtkxNative(root: string): Plugin {
75
102
  },
76
103
 
77
104
  transform(code, id) {
78
- return stripQuery(id) === loaderPath() ? rewriteLoader(code) : null;
105
+ return transformLoader(code, id, loaderPath(), bindingReferenceId);
79
106
  },
80
107
  };
81
108
  }