@gtkx/cli 1.3.0 → 1.4.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 (217) hide show
  1. package/README.md +5 -4
  2. package/dist/builder.d.ts +0 -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 +1 -2
  7. package/dist/codegen/run-codegen.d.ts.map +1 -1
  8. package/dist/codegen/run-codegen.js +11 -6
  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 +1 -3
  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/nfpm/config.js +1 -1
  22. package/dist/deploy/nfpm/config.js.map +1 -1
  23. package/dist/deploy/node-runtime/elf.d.ts +1 -1
  24. package/dist/deploy/node-runtime/elf.d.ts.map +1 -1
  25. package/dist/deploy/node-runtime/elf.js +2 -2
  26. package/dist/deploy/node-runtime/elf.js.map +1 -1
  27. package/dist/deploy/node-runtime/index.js +2 -2
  28. package/dist/deploy/node-runtime/index.js.map +1 -1
  29. package/dist/deploy/notices/collect.d.ts +3 -1
  30. package/dist/deploy/notices/collect.d.ts.map +1 -1
  31. package/dist/deploy/notices/collect.js +2 -2
  32. package/dist/deploy/notices/collect.js.map +1 -1
  33. package/dist/deploy/notices/packages.d.ts +2 -1
  34. package/dist/deploy/notices/packages.d.ts.map +1 -1
  35. package/dist/deploy/notices/packages.js +4 -27
  36. package/dist/deploy/notices/packages.js.map +1 -1
  37. package/dist/deploy/payload/icons.d.ts +7 -3
  38. package/dist/deploy/payload/icons.d.ts.map +1 -1
  39. package/dist/deploy/payload/icons.js +46 -28
  40. package/dist/deploy/payload/icons.js.map +1 -1
  41. package/dist/deploy/payload/stage.d.ts.map +1 -1
  42. package/dist/deploy/payload/stage.js +3 -2
  43. package/dist/deploy/payload/stage.js.map +1 -1
  44. package/dist/deploy/run-deploy.d.ts.map +1 -1
  45. package/dist/deploy/run-deploy.js +63 -11
  46. package/dist/deploy/run-deploy.js.map +1 -1
  47. package/dist/deploy/settings/build-manifest.d.ts +9 -0
  48. package/dist/deploy/settings/build-manifest.d.ts.map +1 -0
  49. package/dist/deploy/settings/build-manifest.js +80 -0
  50. package/dist/deploy/settings/build-manifest.js.map +1 -0
  51. package/dist/deploy/settings/index.d.ts.map +1 -1
  52. package/dist/deploy/settings/index.js +9 -3
  53. package/dist/deploy/settings/index.js.map +1 -1
  54. package/dist/deploy/settings/libraries.d.ts +8 -0
  55. package/dist/deploy/settings/libraries.d.ts.map +1 -0
  56. package/dist/deploy/settings/libraries.js +39 -0
  57. package/dist/deploy/settings/libraries.js.map +1 -0
  58. package/dist/deploy/settings/paths.d.ts +3 -0
  59. package/dist/deploy/settings/paths.d.ts.map +1 -1
  60. package/dist/deploy/settings/paths.js +6 -32
  61. package/dist/deploy/settings/paths.js.map +1 -1
  62. package/dist/deploy/targets/appimage.d.ts.map +1 -1
  63. package/dist/deploy/targets/appimage.js +19 -13
  64. package/dist/deploy/targets/appimage.js.map +1 -1
  65. package/dist/deploy/targets/flatpak-manifest.d.ts +2 -1
  66. package/dist/deploy/targets/flatpak-manifest.d.ts.map +1 -1
  67. package/dist/deploy/targets/flatpak-manifest.js +5 -1
  68. package/dist/deploy/targets/flatpak-manifest.js.map +1 -1
  69. package/dist/deploy/targets/flatpak-source.d.ts.map +1 -1
  70. package/dist/deploy/targets/flatpak-source.js +9 -6
  71. package/dist/deploy/targets/flatpak-source.js.map +1 -1
  72. package/dist/deploy/types.d.ts +4 -4
  73. package/dist/deploy/types.d.ts.map +1 -1
  74. package/dist/deploy/types.js.map +1 -1
  75. package/dist/dev/icon-dir.d.ts +1 -1
  76. package/dist/dev/icon-dir.d.ts.map +1 -1
  77. package/dist/dev/icon-dir.js +20 -7
  78. package/dist/dev/icon-dir.js.map +1 -1
  79. package/dist/dev/runner-deps.d.ts.map +1 -1
  80. package/dist/dev/runner-deps.js +5 -1
  81. package/dist/dev/runner-deps.js.map +1 -1
  82. package/dist/dev/runner-main.js +4 -4
  83. package/dist/dev/runner-main.js.map +1 -1
  84. package/dist/dev/runner.d.ts +1 -1
  85. package/dist/dev/runner.d.ts.map +1 -1
  86. package/dist/dev/runner.js +1 -1
  87. package/dist/dev/runner.js.map +1 -1
  88. package/dist/dev/schema-dir.d.ts +1 -1
  89. package/dist/dev/schema-dir.d.ts.map +1 -1
  90. package/dist/dev/schema-dir.js +3 -1
  91. package/dist/dev/schema-dir.js.map +1 -1
  92. package/dist/internal/build-manifest.d.ts +20 -0
  93. package/dist/internal/build-manifest.d.ts.map +1 -0
  94. package/dist/internal/build-manifest.js +6 -0
  95. package/dist/internal/build-manifest.js.map +1 -0
  96. package/dist/internal/icon-path.d.ts +13 -0
  97. package/dist/internal/icon-path.d.ts.map +1 -0
  98. package/dist/internal/icon-path.js +86 -0
  99. package/dist/internal/icon-path.js.map +1 -0
  100. package/dist/internal/source-imports.d.ts +8 -0
  101. package/dist/internal/source-imports.d.ts.map +1 -0
  102. package/dist/internal/source-imports.js +144 -0
  103. package/dist/internal/source-imports.js.map +1 -0
  104. package/dist/mcp/widget-properties.d.ts.map +1 -1
  105. package/dist/mcp/widget-properties.js +3 -12
  106. package/dist/mcp/widget-properties.js.map +1 -1
  107. package/dist/settings/render.d.ts +7 -1
  108. package/dist/settings/render.d.ts.map +1 -1
  109. package/dist/settings/render.js +69 -20
  110. package/dist/settings/render.js.map +1 -1
  111. package/dist/settings/schema.d.ts +4 -3
  112. package/dist/settings/schema.d.ts.map +1 -1
  113. package/dist/settings/schema.js +122 -15
  114. package/dist/settings/schema.js.map +1 -1
  115. package/dist/vite-plugins/asset-emitter.d.ts +1 -1
  116. package/dist/vite-plugins/asset-emitter.d.ts.map +1 -1
  117. package/dist/vite-plugins/asset-emitter.js.map +1 -1
  118. package/dist/vite-plugins/asset-extensions.d.ts.map +1 -1
  119. package/dist/vite-plugins/asset-extensions.js +1 -0
  120. package/dist/vite-plugins/asset-extensions.js.map +1 -1
  121. package/dist/vite-plugins/asset-imports.d.ts +2 -1
  122. package/dist/vite-plugins/asset-imports.d.ts.map +1 -1
  123. package/dist/vite-plugins/asset-imports.js +37 -16
  124. package/dist/vite-plugins/asset-imports.js.map +1 -1
  125. package/dist/vite-plugins/asset-specifier.d.ts +13 -1
  126. package/dist/vite-plugins/asset-specifier.d.ts.map +1 -1
  127. package/dist/vite-plugins/asset-specifier.js +39 -1
  128. package/dist/vite-plugins/asset-specifier.js.map +1 -1
  129. package/dist/vite-plugins/build-manifest.d.ts +5 -0
  130. package/dist/vite-plugins/build-manifest.d.ts.map +1 -0
  131. package/dist/vite-plugins/{bundled-packages.js → build-manifest.js} +20 -14
  132. package/dist/vite-plugins/build-manifest.js.map +1 -0
  133. package/dist/vite-plugins/built-url.d.ts +1 -1
  134. package/dist/vite-plugins/built-url.d.ts.map +1 -1
  135. package/dist/vite-plugins/built-url.js +37 -24
  136. package/dist/vite-plugins/built-url.js.map +1 -1
  137. package/dist/vite-plugins/icons.d.ts +2 -1
  138. package/dist/vite-plugins/icons.d.ts.map +1 -1
  139. package/dist/vite-plugins/icons.js +25 -12
  140. package/dist/vite-plugins/icons.js.map +1 -1
  141. package/dist/vite-plugins/index.d.ts +2 -1
  142. package/dist/vite-plugins/index.d.ts.map +1 -1
  143. package/dist/vite-plugins/index.js +7 -5
  144. package/dist/vite-plugins/index.js.map +1 -1
  145. package/dist/vite-plugins/native.d.ts.map +1 -1
  146. package/dist/vite-plugins/native.js +23 -4
  147. package/dist/vite-plugins/native.js.map +1 -1
  148. package/dist/vite-plugins/resource-init-module.d.ts +7 -3
  149. package/dist/vite-plugins/resource-init-module.d.ts.map +1 -1
  150. package/dist/vite-plugins/resource-init-module.js +39 -14
  151. package/dist/vite-plugins/resource-init-module.js.map +1 -1
  152. package/dist/vite-plugins/resource-shared.d.ts +3 -1
  153. package/dist/vite-plugins/resource-shared.d.ts.map +1 -1
  154. package/dist/vite-plugins/resource-shared.js +3 -1
  155. package/dist/vite-plugins/resource-shared.js.map +1 -1
  156. package/dist/vite-plugins/resources.d.ts +1 -1
  157. package/dist/vite-plugins/resources.d.ts.map +1 -1
  158. package/dist/vite-plugins/resources.js +922 -86
  159. package/dist/vite-plugins/resources.js.map +1 -1
  160. package/dist/vite-plugins/settings-worker-env.d.ts.map +1 -1
  161. package/dist/vite-plugins/settings-worker-env.js +6 -3
  162. package/dist/vite-plugins/settings-worker-env.js.map +1 -1
  163. package/dist/vite-plugins/settings.d.ts +3 -1
  164. package/dist/vite-plugins/settings.d.ts.map +1 -1
  165. package/dist/vite-plugins/settings.js +63 -23
  166. package/dist/vite-plugins/settings.js.map +1 -1
  167. package/env.d.ts +11 -192
  168. package/package.json +10 -10
  169. package/src/builder.ts +6 -7
  170. package/src/codegen/run-codegen.ts +10 -7
  171. package/src/commands/build.ts +0 -5
  172. package/src/commands/codegen.ts +1 -3
  173. package/src/deploy/depends.ts +47 -18
  174. package/src/deploy/nfpm/config.ts +1 -1
  175. package/src/deploy/node-runtime/elf.ts +3 -3
  176. package/src/deploy/node-runtime/index.ts +2 -2
  177. package/src/deploy/notices/collect.ts +4 -2
  178. package/src/deploy/notices/packages.ts +5 -43
  179. package/src/deploy/payload/icons.ts +64 -34
  180. package/src/deploy/payload/stage.ts +5 -2
  181. package/src/deploy/run-deploy.ts +87 -12
  182. package/src/deploy/settings/build-manifest.ts +122 -0
  183. package/src/deploy/settings/index.ts +9 -4
  184. package/src/deploy/settings/libraries.ts +57 -0
  185. package/src/deploy/settings/paths.ts +10 -47
  186. package/src/deploy/targets/appimage.ts +32 -15
  187. package/src/deploy/targets/flatpak-manifest.ts +7 -1
  188. package/src/deploy/targets/flatpak-source.ts +9 -10
  189. package/src/deploy/types.ts +4 -4
  190. package/src/dev/icon-dir.ts +31 -7
  191. package/src/dev/runner-deps.ts +5 -1
  192. package/src/dev/runner-main.ts +4 -4
  193. package/src/dev/runner.ts +2 -2
  194. package/src/dev/schema-dir.ts +3 -1
  195. package/src/internal/build-manifest.ts +32 -0
  196. package/src/internal/icon-path.ts +127 -0
  197. package/src/internal/source-imports.ts +200 -0
  198. package/src/mcp/widget-properties.ts +3 -14
  199. package/src/settings/render.ts +99 -21
  200. package/src/settings/schema.ts +190 -17
  201. package/src/vite-plugins/asset-emitter.ts +1 -1
  202. package/src/vite-plugins/asset-extensions.ts +1 -0
  203. package/src/vite-plugins/asset-imports.ts +60 -19
  204. package/src/vite-plugins/asset-specifier.ts +73 -1
  205. package/src/vite-plugins/{bundled-packages.ts → build-manifest.ts} +30 -19
  206. package/src/vite-plugins/built-url.ts +49 -28
  207. package/src/vite-plugins/icons.ts +33 -14
  208. package/src/vite-plugins/index.ts +12 -5
  209. package/src/vite-plugins/native.ts +31 -4
  210. package/src/vite-plugins/resource-init-module.ts +44 -15
  211. package/src/vite-plugins/resource-shared.ts +4 -0
  212. package/src/vite-plugins/resources.ts +1459 -100
  213. package/src/vite-plugins/settings-worker-env.ts +7 -3
  214. package/src/vite-plugins/settings.ts +89 -21
  215. package/dist/vite-plugins/bundled-packages.d.ts +0 -5
  216. package/dist/vite-plugins/bundled-packages.d.ts.map +0 -1
  217. 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
  }
@@ -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,7 +1,9 @@
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";
6
8
  import { gtkxIcons } from "./icons.js";
7
9
  import { gtkxReactCompiler } from "./react-compiler.js";
@@ -10,17 +12,22 @@ import { gtkxSettings } from "./settings.js";
10
12
  import { gtkxStoreLinks } from "./store-links.js";
11
13
  import { gtkxUndeclaredLibrary } from "./undeclared-library.js";
12
14
 
13
- const gtkxVitePlugins = (mode?: string): Plugin[] => {
15
+ const gtkxVitePlugins = (
16
+ mode?: string,
17
+ entryPath?: string,
18
+ buildManifest?: BuildManifestCollector,
19
+ ): Plugin[] => {
14
20
  const loadConfig = createConfigLoader(mode === undefined ? {} : { mode });
15
21
 
16
22
  return [
17
23
  createConfigPlugin({ name: "gtkx:config", loadConfig }),
18
24
  gtkxStoreLinks(),
19
25
  gtkxUndeclaredLibrary(loadConfig),
20
- gtkxSettings(),
21
- gtkxIcons(),
22
- gtkxAssetImports(),
23
- gtkxResources(loadConfig),
26
+ gtkxSettings(loadConfig, buildManifest),
27
+ gtkxIcons(loadConfig),
28
+ gtkxAssetImports(loadConfig),
29
+ gtkxBuiltUrl(),
30
+ gtkxResources(loadConfig, entryPath),
24
31
  gtkxCss(),
25
32
  gtkxReactCompiler(loadConfig),
26
33
  ];
@@ -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
  }
@@ -1,15 +1,33 @@
1
- import { BUNDLE_FILENAME, REFRESH_EXPORT } from "./resource-shared.js";
1
+ import { REFRESH_EXPORT, REGISTER_REFRESH_EXPORT } from "./resource-shared.js";
2
2
 
3
- const buildInitModuleSource = (): string =>
3
+ type InitModuleOptions = { isBuild: true; bundleReferenceId: string } |
4
+ { isBuild: false; devBundlePath: string };
5
+
6
+ const refreshCallbackSource = (): string[] => [
7
+ "const refreshCallbacks = new Map();",
8
+ "",
9
+ `export function ${REGISTER_REFRESH_EXPORT}(key, callback) {`,
10
+ " refreshCallbacks.set(key, callback);",
11
+ " callback();",
12
+ "}",
13
+ "",
14
+ "function runRefreshCallbacks() {",
15
+ " for (const callback of refreshCallbacks.values()) callback();",
16
+ "}",
17
+ ];
18
+
19
+ const buildInitModuleSource = (bundleReferenceId: string): string =>
4
20
  [
5
- "import { dirname, join } from \"node:path\";",
6
21
  "import { fileURLToPath } from \"node:url\";",
7
22
  "import { Resource, resourcesRegister } from \"@gtkx/gi/gio\";",
8
23
  "",
9
- "const bundleDir = dirname(fileURLToPath(import.meta.url));",
10
- `const resource = Resource.load(join(bundleDir, ${JSON.stringify(BUNDLE_FILENAME)}));`,
24
+ `const bundleLocation = import.meta.ROLLUP_FILE_URL_${bundleReferenceId};`,
25
+ "const bundlePath = bundleLocation.startsWith(\"file:\") ? fileURLToPath(bundleLocation) : bundleLocation;",
26
+ "const resource = Resource.load(bundlePath);",
11
27
  "resourcesRegister(resource);",
12
28
  "",
29
+ ...refreshCallbackSource(),
30
+ "",
13
31
  "export function ensureRegistered() {}",
14
32
  `export function ${REFRESH_EXPORT}() {}`,
15
33
  ].join("\n");
@@ -31,27 +49,38 @@ const devInitModuleSource = (bundlePath: string): string => {
31
49
  " current = next;",
32
50
  "}",
33
51
  "",
52
+ "function unregister() {",
53
+ " if (!current) return;",
54
+ " resourcesUnregister(current);",
55
+ " current = null;",
56
+ " lastSig = \"\";",
57
+ "}",
58
+ "",
59
+ ...refreshCallbackSource(),
60
+ "",
34
61
  "export function ensureRegistered() {",
35
- ` if (!existsSync(${bundlePathLiteral})) return;`,
62
+ ` if (!existsSync(${bundlePathLiteral})) {`,
63
+ " unregister();",
64
+ " return;",
65
+ " }",
36
66
  ` const { size, mtimeMs } = statSync(${bundlePathLiteral});`,
37
67
  " const sig = size + \":\" + mtimeMs;",
38
- " if (sig === lastSig) return;",
39
- " register();",
40
- " lastSig = sig;",
68
+ " if (sig !== lastSig) {",
69
+ " register();",
70
+ " lastSig = sig;",
71
+ " }",
41
72
  "}",
42
73
  "",
43
74
  "ensureRegistered();",
44
75
  "",
45
76
  `export function ${REFRESH_EXPORT}() {`,
46
- ` if (!existsSync(${bundlePathLiteral})) return;`,
47
- " register();",
48
- ` const { size, mtimeMs } = statSync(${bundlePathLiteral});`,
49
- " lastSig = size + \":\" + mtimeMs;",
77
+ " ensureRegistered();",
78
+ " runRefreshCallbacks();",
50
79
  "}",
51
80
  ].join("\n");
52
81
  };
53
82
 
54
- const renderInitModule = (options: { isBuild: boolean; devBundlePath: string }): string =>
55
- options.isBuild ? buildInitModuleSource() : devInitModuleSource(options.devBundlePath);
83
+ const renderInitModule = (options: InitModuleOptions): string =>
84
+ options.isBuild ? buildInitModuleSource(options.bundleReferenceId) : devInitModuleSource(options.devBundlePath);
56
85
 
57
86
  export { renderInitModule };
@@ -4,7 +4,9 @@ const VIRTUAL_PREFIX = "\0gtkx-resources:";
4
4
  const VIRTUAL_INIT = "\0gtkx-resources-init";
5
5
  const BUNDLE_FILENAME = "gtkx.gresource";
6
6
  const REL_SEPARATOR = "\0rel=";
7
+ const ICON_NAME_SEPARATOR = "\0icon=";
7
8
  const REFRESH_EXPORT = "__refresh";
9
+ const REGISTER_REFRESH_EXPORT = "__registerRefresh";
8
10
  const RESOURCE_PATH_EXPORT = "path";
9
11
  const { isVirtual, toVirtualId, fromVirtualId } = createVirtualNamespace(VIRTUAL_PREFIX);
10
12
 
@@ -33,7 +35,9 @@ export {
33
35
  VIRTUAL_INIT,
34
36
  BUNDLE_FILENAME,
35
37
  REL_SEPARATOR,
38
+ ICON_NAME_SEPARATOR,
36
39
  REFRESH_EXPORT,
40
+ REGISTER_REFRESH_EXPORT,
37
41
  RESOURCE_PATH_EXPORT,
38
42
  escapeXml,
39
43
  isVirtual,