@gtkx/cli 1.5.0 → 2.0.0-beta.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 (211) hide show
  1. package/README.md +5 -5
  2. package/dist/cli.js +4 -0
  3. package/dist/cli.js.map +1 -1
  4. package/dist/codegen/freshness.d.ts.map +1 -1
  5. package/dist/codegen/freshness.js +45 -15
  6. package/dist/codegen/freshness.js.map +1 -1
  7. package/dist/codegen/reference.d.ts +17 -0
  8. package/dist/codegen/reference.d.ts.map +1 -0
  9. package/dist/codegen/reference.js +31 -0
  10. package/dist/codegen/reference.js.map +1 -0
  11. package/dist/codegen/report.d.ts.map +1 -1
  12. package/dist/codegen/report.js +18 -15
  13. package/dist/codegen/report.js.map +1 -1
  14. package/dist/codegen/run-codegen.d.ts +5 -5
  15. package/dist/codegen/run-codegen.d.ts.map +1 -1
  16. package/dist/codegen/run-codegen.js +24 -59
  17. package/dist/codegen/run-codegen.js.map +1 -1
  18. package/dist/codegen/store-resolver.d.ts +0 -1
  19. package/dist/codegen/store-resolver.d.ts.map +1 -1
  20. package/dist/codegen/store-resolver.js +8 -17
  21. package/dist/codegen/store-resolver.js.map +1 -1
  22. package/dist/commands/docs.d.ts.map +1 -1
  23. package/dist/commands/docs.js +6 -22
  24. package/dist/commands/docs.js.map +1 -1
  25. package/dist/commands/mcp.d.ts +13 -0
  26. package/dist/commands/mcp.d.ts.map +1 -0
  27. package/dist/commands/mcp.js +68 -0
  28. package/dist/commands/mcp.js.map +1 -0
  29. package/dist/deploy/freedesktop/copyright.d.ts.map +1 -1
  30. package/dist/deploy/freedesktop/copyright.js +2 -13
  31. package/dist/deploy/freedesktop/copyright.js.map +1 -1
  32. package/dist/deploy/freedesktop/xml.d.ts.map +1 -1
  33. package/dist/deploy/freedesktop/xml.js +21 -25
  34. package/dist/deploy/freedesktop/xml.js.map +1 -1
  35. package/dist/deploy/node-runtime/license.d.ts.map +1 -1
  36. package/dist/deploy/node-runtime/license.js +2 -9
  37. package/dist/deploy/node-runtime/license.js.map +1 -1
  38. package/dist/deploy/notices/gtkx.js +1 -1
  39. package/dist/deploy/notices/gtkx.js.map +1 -1
  40. package/dist/deploy/notices/libraries.d.ts.map +1 -1
  41. package/dist/deploy/notices/libraries.js +2 -1
  42. package/dist/deploy/notices/libraries.js.map +1 -1
  43. package/dist/deploy/notices/packages.d.ts.map +1 -1
  44. package/dist/deploy/notices/packages.js +3 -10
  45. package/dist/deploy/notices/packages.js.map +1 -1
  46. package/dist/deploy/payload/copy-tree.d.ts.map +1 -1
  47. package/dist/deploy/payload/copy-tree.js +3 -3
  48. package/dist/deploy/payload/copy-tree.js.map +1 -1
  49. package/dist/deploy/payload/stage.d.ts.map +1 -1
  50. package/dist/deploy/payload/stage.js +1 -3
  51. package/dist/deploy/payload/stage.js.map +1 -1
  52. package/dist/deploy/settings/build-manifest.d.ts.map +1 -1
  53. package/dist/deploy/settings/build-manifest.js +4 -15
  54. package/dist/deploy/settings/build-manifest.js.map +1 -1
  55. package/dist/deploy/settings/libraries.d.ts.map +1 -1
  56. package/dist/deploy/settings/libraries.js +2 -21
  57. package/dist/deploy/settings/libraries.js.map +1 -1
  58. package/dist/deploy/settings/paths.d.ts +1 -2
  59. package/dist/deploy/settings/paths.d.ts.map +1 -1
  60. package/dist/deploy/settings/paths.js +5 -15
  61. package/dist/deploy/settings/paths.js.map +1 -1
  62. package/dist/deploy/settings/screenshots.d.ts.map +1 -1
  63. package/dist/deploy/settings/screenshots.js +6 -31
  64. package/dist/deploy/settings/screenshots.js.map +1 -1
  65. package/dist/deploy/targets/flatpak-pnpm.js +4 -4
  66. package/dist/deploy/targets/flatpak-pnpm.js.map +1 -1
  67. package/dist/deploy/targets/flatpak-source.d.ts.map +1 -1
  68. package/dist/deploy/targets/flatpak-source.js +4 -4
  69. package/dist/deploy/targets/flatpak-source.js.map +1 -1
  70. package/dist/deploy/targets/flatpak-sources.js +1 -1
  71. package/dist/deploy/targets/flatpak-sources.js.map +1 -1
  72. package/dist/deploy/tools.d.ts.map +1 -1
  73. package/dist/deploy/tools.js +1 -3
  74. package/dist/deploy/tools.js.map +1 -1
  75. package/dist/dev/runner-main.js +1 -1
  76. package/dist/dev/runner-main.js.map +1 -1
  77. package/dist/dev/schema-dir.d.ts +1 -1
  78. package/dist/dev/schema-dir.d.ts.map +1 -1
  79. package/dist/dev/schema-dir.js +2 -4
  80. package/dist/dev/schema-dir.js.map +1 -1
  81. package/dist/i18n/catalogs.d.ts.map +1 -1
  82. package/dist/i18n/catalogs.js +2 -1
  83. package/dist/i18n/catalogs.js.map +1 -1
  84. package/dist/i18n/metadata-templates.d.ts.map +1 -1
  85. package/dist/i18n/metadata-templates.js +2 -1
  86. package/dist/i18n/metadata-templates.js.map +1 -1
  87. package/dist/i18n/source-messages.d.ts +3 -21
  88. package/dist/i18n/source-messages.d.ts.map +1 -1
  89. package/dist/i18n/source-messages.js +66 -85
  90. package/dist/i18n/source-messages.js.map +1 -1
  91. package/dist/i18n/types.d.ts +6 -7
  92. package/dist/i18n/types.d.ts.map +1 -1
  93. package/dist/i18n/types.js +83 -96
  94. package/dist/i18n/types.js.map +1 -1
  95. package/dist/internal/agent-rules.d.ts +7 -0
  96. package/dist/internal/agent-rules.d.ts.map +1 -0
  97. package/dist/internal/agent-rules.js +106 -0
  98. package/dist/internal/agent-rules.js.map +1 -0
  99. package/dist/internal/docs-elements.d.ts +8 -0
  100. package/dist/internal/docs-elements.d.ts.map +1 -0
  101. package/dist/internal/docs-elements.js +12 -0
  102. package/dist/internal/docs-elements.js.map +1 -0
  103. package/dist/internal/icon-path.d.ts.map +1 -1
  104. package/dist/internal/icon-path.js +5 -16
  105. package/dist/internal/icon-path.js.map +1 -1
  106. package/dist/internal/source-imports.d.ts.map +1 -1
  107. package/dist/internal/source-imports.js +10 -42
  108. package/dist/internal/source-imports.js.map +1 -1
  109. package/dist/mcp/client-config.d.ts +22 -0
  110. package/dist/mcp/client-config.d.ts.map +1 -0
  111. package/dist/mcp/client-config.js +94 -0
  112. package/dist/mcp/client-config.js.map +1 -0
  113. package/dist/settings/render.d.ts +0 -1
  114. package/dist/settings/render.d.ts.map +1 -1
  115. package/dist/settings/render.js +1 -11
  116. package/dist/settings/render.js.map +1 -1
  117. package/dist/settings/schema.d.ts +2 -2
  118. package/dist/settings/schema.d.ts.map +1 -1
  119. package/dist/settings/schema.js +21 -62
  120. package/dist/settings/schema.js.map +1 -1
  121. package/dist/vite-plugins/asset-imports.d.ts +1 -2
  122. package/dist/vite-plugins/asset-imports.d.ts.map +1 -1
  123. package/dist/vite-plugins/asset-imports.js +19 -32
  124. package/dist/vite-plugins/asset-imports.js.map +1 -1
  125. package/dist/vite-plugins/asset-specifier.d.ts +1 -3
  126. package/dist/vite-plugins/asset-specifier.d.ts.map +1 -1
  127. package/dist/vite-plugins/asset-specifier.js +1 -4
  128. package/dist/vite-plugins/asset-specifier.js.map +1 -1
  129. package/dist/vite-plugins/i18n.d.ts.map +1 -1
  130. package/dist/vite-plugins/i18n.js +2 -1
  131. package/dist/vite-plugins/i18n.js.map +1 -1
  132. package/dist/vite-plugins/index.js +2 -2
  133. package/dist/vite-plugins/index.js.map +1 -1
  134. package/dist/vite-plugins/resources.d.ts.map +1 -1
  135. package/dist/vite-plugins/resources.js +31 -96
  136. package/dist/vite-plugins/resources.js.map +1 -1
  137. package/dist/vite-plugins/settings-worker-env.d.ts.map +1 -1
  138. package/dist/vite-plugins/settings-worker-env.js +1 -3
  139. package/dist/vite-plugins/settings-worker-env.js.map +1 -1
  140. package/dist/vite-plugins/settings.d.ts +1 -2
  141. package/dist/vite-plugins/settings.d.ts.map +1 -1
  142. package/dist/vite-plugins/settings.js +8 -13
  143. package/dist/vite-plugins/settings.js.map +1 -1
  144. package/dist/vite-plugins/undeclared-library.d.ts.map +1 -1
  145. package/dist/vite-plugins/undeclared-library.js +10 -13
  146. package/dist/vite-plugins/undeclared-library.js.map +1 -1
  147. package/dist/vite-plugins/worker.d.ts.map +1 -1
  148. package/dist/vite-plugins/worker.js +0 -1
  149. package/dist/vite-plugins/worker.js.map +1 -1
  150. package/package.json +21 -14
  151. package/src/cli.ts +5 -0
  152. package/src/codegen/freshness.ts +66 -16
  153. package/src/codegen/reference.ts +52 -0
  154. package/src/codegen/report.ts +16 -17
  155. package/src/codegen/run-codegen.ts +31 -91
  156. package/src/codegen/store-resolver.ts +8 -24
  157. package/src/commands/docs.ts +6 -36
  158. package/src/commands/mcp.ts +81 -0
  159. package/src/deploy/freedesktop/copyright.ts +2 -14
  160. package/src/deploy/freedesktop/xml.ts +24 -32
  161. package/src/deploy/node-runtime/license.ts +2 -8
  162. package/src/deploy/notices/gtkx.ts +1 -1
  163. package/src/deploy/notices/libraries.ts +2 -1
  164. package/src/deploy/notices/packages.ts +3 -11
  165. package/src/deploy/payload/copy-tree.ts +3 -4
  166. package/src/deploy/payload/stage.ts +2 -6
  167. package/src/deploy/settings/build-manifest.ts +4 -16
  168. package/src/deploy/settings/libraries.ts +1 -25
  169. package/src/deploy/settings/paths.ts +6 -16
  170. package/src/deploy/settings/screenshots.ts +8 -46
  171. package/src/deploy/targets/flatpak-pnpm.ts +4 -4
  172. package/src/deploy/targets/flatpak-source.ts +4 -4
  173. package/src/deploy/targets/flatpak-sources.ts +1 -1
  174. package/src/deploy/tools.ts +1 -3
  175. package/src/dev/runner-main.ts +1 -1
  176. package/src/dev/schema-dir.ts +2 -4
  177. package/src/i18n/catalogs.ts +2 -1
  178. package/src/i18n/metadata-templates.ts +2 -1
  179. package/src/i18n/source-messages.ts +77 -115
  180. package/src/i18n/types.ts +97 -131
  181. package/src/internal/agent-rules.ts +135 -0
  182. package/src/internal/docs-elements.ts +21 -0
  183. package/src/internal/icon-path.ts +6 -19
  184. package/src/internal/source-imports.ts +14 -50
  185. package/src/mcp/client-config.ts +130 -0
  186. package/src/settings/render.ts +1 -13
  187. package/src/settings/schema.ts +19 -81
  188. package/src/vite-plugins/asset-imports.ts +20 -44
  189. package/src/vite-plugins/asset-specifier.ts +0 -5
  190. package/src/vite-plugins/i18n.ts +2 -1
  191. package/src/vite-plugins/index.ts +2 -2
  192. package/src/vite-plugins/resources.ts +36 -139
  193. package/src/vite-plugins/settings-worker-env.ts +1 -3
  194. package/src/vite-plugins/settings.ts +11 -19
  195. package/src/vite-plugins/undeclared-library.ts +14 -20
  196. package/src/vite-plugins/worker.ts +0 -1
  197. package/dist/i18n/binding-extractor.d.ts +0 -30
  198. package/dist/i18n/binding-extractor.d.ts.map +0 -1
  199. package/dist/i18n/binding-extractor.js +0 -1324
  200. package/dist/i18n/binding-extractor.js.map +0 -1
  201. package/dist/i18n/extractor-plugin.d.ts +0 -8
  202. package/dist/i18n/extractor-plugin.d.ts.map +0 -1
  203. package/dist/i18n/extractor-plugin.js +0 -247
  204. package/dist/i18n/extractor-plugin.js.map +0 -1
  205. package/dist/internal/data-dir.d.ts +0 -4
  206. package/dist/internal/data-dir.d.ts.map +0 -1
  207. package/dist/internal/data-dir.js +0 -52
  208. package/dist/internal/data-dir.js.map +0 -1
  209. package/src/i18n/binding-extractor.ts +0 -1969
  210. package/src/i18n/extractor-plugin.ts +0 -413
  211. package/src/internal/data-dir.ts +0 -70
@@ -1,13 +1,9 @@
1
- import type { ConfigLoader } from "@gtkx/config";
2
- import type { ParseResult, Plugin, UserConfig } from "vite";
3
- import { createConfigLoader } from "@gtkx/config/internal";
1
+ import type { ParseResult, Plugin } from "vite";
4
2
  import { parseSync } from "vite";
5
3
  import { sourceLanguage } from "../internal/source-imports.js";
6
4
  import { ASSET_MENTION_RE } from "./asset-extensions.js";
7
5
  import {
8
- DATA_PREFIX,
9
6
  isAssetSpecifier,
10
- isDataAsset,
11
7
  isUrlSpecifier,
12
8
  parseIconSpecifier,
13
9
  parseResourceSpecifier,
@@ -21,18 +17,11 @@ type ExportStatement = ParsedModule["staticExports"][number];
21
17
  type ExportEntry = ExportStatement["entries"][number];
22
18
  type BindingEntry = { importName: { name: string | null }; isType: boolean };
23
19
  type NamedBinding = { name: string; source: string };
24
- type PluginState = { isV2: boolean };
25
20
 
26
21
  const NODE_MODULES = /(?:^|\/)node_modules\//;
27
22
  const VIRTUAL_PREFIX = "\0";
28
23
  const DEFAULT_BINDING = "default";
29
24
  const RESOURCE_BINDING = JSON.stringify(RESOURCE_PATH_EXPORT);
30
- const DATA_IMPORT_EXAMPLE = `"${DATA_PREFIX}<path>"`;
31
- const URL_FALLBACK = ", or import the default for the asset URL.";
32
-
33
- const BUNDLE_ADVICE =
34
- `An asset staged into the GResource bundle exports ${RESOURCE_BINDING} and a "resource://" default, and ` +
35
- "nothing else.";
36
25
 
37
26
  const QUERY_ADVICE =
38
27
  `A URL asset exports only its default filesystem path; import the default instead of ${RESOURCE_BINDING}.`;
@@ -43,9 +32,7 @@ const RESOURCE_ADVICE =
43
32
  const ICON_ADVICE = "An icon asset exports only its default icon-theme name, and nothing else.";
44
33
 
45
34
  const RELATIVE_ADVICE =
46
- `Only assets imported through "${DATA_PREFIX}" are staged into the GResource bundle and export ` +
47
- `${RESOURCE_BINDING}; move the file under the data directory and import it as ` +
48
- `${DATA_IMPORT_EXAMPLE}${URL_FALLBACK}`;
35
+ "Add ?resource for a GResource, ?icon for a themed icon, or ?url for an emitted file.";
49
36
 
50
37
  const boundName = (entry: BindingEntry): string | null => {
51
38
  const { name } = entry.importName;
@@ -78,50 +65,46 @@ const bindings = (parsed: ParsedModule): NamedBinding[] => [
78
65
  ...parsed.staticExports.flatMap((statement) => exportBindings(statement)),
79
66
  ];
80
67
 
81
- const isBacked = ({ name, source }: NamedBinding, isV2: boolean): boolean => {
68
+ const isBacked = ({ name, source }: NamedBinding): boolean => {
82
69
  if (name !== RESOURCE_PATH_EXPORT) {
83
70
  return false;
84
71
  }
85
72
 
86
- return isV2 ? parseResourceSpecifier(source) !== null : isDataAsset(source);
73
+ return parseResourceSpecifier(source) !== null;
87
74
  };
88
75
 
89
76
  const isCheckedSpecifier = (source: string): boolean => isAssetSpecifier(source) || isUrlSpecifier(source);
90
77
 
91
- const unbackedBinding = (parsed: ParsedModule, isV2: boolean): NamedBinding | undefined =>
92
- bindings(parsed).find((binding) => isCheckedSpecifier(binding.source) && !isBacked(binding, isV2));
93
-
94
- const isBundledResource = (source: string, isV2: boolean): boolean =>
95
- isV2
96
- ? parseResourceSpecifier(source) !== null || parseIconSpecifier(source) !== null
97
- : isDataAsset(source);
78
+ const unbackedBinding = (parsed: ParsedModule): NamedBinding | undefined =>
79
+ bindings(parsed).find((binding) => isCheckedSpecifier(binding.source) && !isBacked(binding));
98
80
 
99
- const v2BundleAdvice = (source: string): string =>
100
- parseIconSpecifier(source) === null ? RESOURCE_ADVICE : ICON_ADVICE;
81
+ const adviceFor = (source: string): string => {
82
+ if (parseResourceSpecifier(source) !== null) {
83
+ return RESOURCE_ADVICE;
84
+ }
101
85
 
102
- const bundleAdvice = (source: string, isV2: boolean): string => {
103
- if (isBundledResource(source, isV2)) {
104
- return isV2 ? v2BundleAdvice(source) : BUNDLE_ADVICE;
86
+ if (parseIconSpecifier(source) !== null) {
87
+ return ICON_ADVICE;
105
88
  }
106
89
 
107
90
  if (isUrlSpecifier(source)) {
108
91
  return QUERY_ADVICE;
109
92
  }
110
93
 
111
- return source.startsWith(DATA_PREFIX) ? QUERY_ADVICE : RELATIVE_ADVICE;
94
+ return RELATIVE_ADVICE;
112
95
  };
113
96
 
114
- const unbackedBindingError = (path: string, binding: NamedBinding, isV2: boolean): Error =>
97
+ const unbackedBindingError = (path: string, binding: NamedBinding): Error =>
115
98
  new Error(
116
99
  `${path}: ${JSON.stringify(binding.source)} does not export ${JSON.stringify(binding.name)}, which ` +
117
100
  "gtkx build rejects and gtkx dev would bind as undefined. " +
118
- bundleAdvice(binding.source, isV2),
101
+ adviceFor(binding.source),
119
102
  );
120
103
 
121
104
  const isCheckedSource = (path: string): boolean => !path.startsWith(VIRTUAL_PREFIX) && !NODE_MODULES.test(path);
122
105
  const hasCheckedAssetMention = (code: string): boolean => ASSET_MENTION_RE.test(code) || code.includes("?url");
123
106
 
124
- const checkAssetImports = (code: string, id: string, isV2: boolean): void => {
107
+ const checkAssetImports = (code: string, id: string): void => {
125
108
  const path = stripQuery(id);
126
109
  const lang = sourceLanguage(path);
127
110
 
@@ -135,27 +118,20 @@ const checkAssetImports = (code: string, id: string, isV2: boolean): void => {
135
118
  return;
136
119
  }
137
120
 
138
- const binding = unbackedBinding(parsed.module, isV2);
121
+ const binding = unbackedBinding(parsed.module);
139
122
 
140
123
  if (binding !== undefined) {
141
- throw unbackedBindingError(path, binding, isV2);
124
+ throw unbackedBindingError(path, binding);
142
125
  }
143
126
  };
144
127
 
145
- function gtkxAssetImports(loadConfig: ConfigLoader = createConfigLoader()): Plugin {
146
- const state: PluginState = { isV2: false };
147
-
128
+ function gtkxAssetImports(): Plugin {
148
129
  return {
149
130
  name: "gtkx:asset-imports",
150
131
  enforce: "pre",
151
132
 
152
- async config(config: UserConfig) {
153
- const loaded = await loadConfig.load(config.root ?? process.cwd());
154
- state.isV2 = loaded.config.future?.v2ResourceImports === true;
155
- },
156
-
157
133
  transform(code, id) {
158
- checkAssetImports(code, id, state.isV2);
134
+ checkAssetImports(code, id);
159
135
  },
160
136
  };
161
137
  }
@@ -1,4 +1,3 @@
1
- import { DATA_IMPORT_PREFIX } from "../internal/data-dir.js";
2
1
  import { ASSET_PATH_RE, ASSET_RE } from "./asset-extensions.js";
3
2
  import { stripQuery } from "./strip-query.js";
4
3
 
@@ -12,7 +11,6 @@ type IconSpecifier = {
12
11
  iconName: string | null;
13
12
  };
14
13
 
15
- const DATA_PREFIX = `${DATA_IMPORT_PREFIX}/`;
16
14
  const RELATIVE_PREFIX_RE = /^\.\.?(?:\/|$)/;
17
15
  const RESOURCE_QUERY_RE = /^resource(?:=([^&#]+))?$/;
18
16
  const ICON_QUERY_RE = /^icon(?:=([^&#]+))?$/;
@@ -25,7 +23,6 @@ const getQuery = (source: string): string | null => {
25
23
  };
26
24
 
27
25
  const isAssetSpecifier = (source: string): boolean => ASSET_PATH_RE.test(source);
28
- const isDataAsset = (source: string): boolean => source.startsWith(DATA_PREFIX) && ASSET_RE.test(source);
29
26
 
30
27
  const isBareRelativeAsset = (source: string): boolean =>
31
28
  getQuery(source) === null && RELATIVE_PREFIX_RE.test(source) && ASSET_RE.test(source);
@@ -71,10 +68,8 @@ const isUrlSpecifier = (source: string): boolean => {
71
68
  };
72
69
 
73
70
  export {
74
- DATA_PREFIX,
75
71
  isAssetSpecifier,
76
72
  isBareRelativeAsset,
77
- isDataAsset,
78
73
  isUrlSpecifier,
79
74
  parseIconSpecifier,
80
75
  parseResourceSpecifier,
@@ -1,6 +1,7 @@
1
1
  import type { ConfigLoader } from "@gtkx/config";
2
2
  import type { Plugin, ResolvedConfig } from "vite";
3
3
  import { createConfigLoader } from "@gtkx/config/internal";
4
+ import { toPosixPath } from "@gtkx/utils";
4
5
  import { existsSync, mkdirSync } from "node:fs";
5
6
  import { createRequire } from "node:module";
6
7
  import { dirname, join, posix, relative, resolve } from "node:path";
@@ -32,7 +33,7 @@ const isI18nLocaleModule = (state: I18nState, id: string): boolean => {
32
33
  return false;
33
34
  }
34
35
 
35
- const path = relative(state.i18nRoot, stripQuery(id)).replaceAll("\\", "/");
36
+ const path = toPosixPath(relative(state.i18nRoot, stripQuery(id)));
36
37
 
37
38
  return LOCALE_MODULE_PATHS.has(path);
38
39
  };
@@ -26,9 +26,9 @@ const gtkxVitePlugins = (
26
26
  ...(entryPath === undefined ? [] : [gtkxI18n(entryPath, loadConfig, shouldPreserveI18nMetadata)]),
27
27
  gtkxStoreLinks(),
28
28
  gtkxUndeclaredLibrary(loadConfig),
29
- gtkxSettings(loadConfig, buildManifest),
29
+ gtkxSettings(buildManifest),
30
30
  gtkxIcons(loadConfig),
31
- gtkxAssetImports(loadConfig),
31
+ gtkxAssetImports(),
32
32
  gtkxBuiltUrl(),
33
33
  gtkxResources(loadConfig, entryPath),
34
34
  gtkxCss(),
@@ -1,20 +1,17 @@
1
1
  import type { ConfigLoader } from "@gtkx/config";
2
2
  import type { ModuleNode, Plugin, ResolvedConfig, Rolldown, UserConfig, ViteDevServer } from "vite";
3
3
  import { createConfigLoader, resourceBasePath } from "@gtkx/config/internal";
4
- import { error, info, isRecord, sortStrings } from "@gtkx/utils";
4
+ import { error, info, isPathInside, isRecord, sortStrings, toPosixPath } from "@gtkx/utils";
5
5
  import { copyFileSync, existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
6
6
  import { basename, dirname, extname, isAbsolute, join, posix, relative, sep } from "node:path";
7
7
  import { parseSync } from "vite";
8
- import { resolveDataDir } from "../internal/data-dir.js";
9
- import { type ListedFile, listFilesRecursive } from "../internal/list-files.js";
8
+ import { withExclusiveLoad } from "../internal/module-loads.js";
10
9
  import { runCliTool } from "../internal/run-cli-tool.js";
11
10
  import { parseRuntimeImportsIn, type SourceImport, sourceLanguage } from "../internal/source-imports.js";
12
11
  import { createRetainedStagingDir, type RetainedStagingDir, withStagingDir } from "../internal/staging-dir.js";
13
12
  import { ASSET_RE } from "./asset-extensions.js";
14
13
  import {
15
- DATA_PREFIX,
16
14
  isBareRelativeAsset,
17
- isDataAsset,
18
15
  parseIconSpecifier,
19
16
  parseResourceSpecifier,
20
17
  } from "./asset-specifier.js";
@@ -44,7 +41,6 @@ type PluginState = {
44
41
  prefix: string;
45
42
  root: string;
46
43
  isBuild: boolean;
47
- isV2: boolean;
48
44
  entries: Map<string, ResourceEntry>;
49
45
  bundledSpecifiers: Map<string, Set<string>>;
50
46
  moduleDependencies: Map<string, Set<string>>;
@@ -58,7 +54,6 @@ type PluginState = {
58
54
  devBundlePath: string;
59
55
  server: ViteDevServer | null;
60
56
  compiledSignature: string;
61
- dataDir: string | null;
62
57
  bundleReferenceId: string | null;
63
58
  iconOwners: Map<string, ResourceEntry>;
64
59
  };
@@ -118,7 +113,7 @@ type DerivedIconEntry = {
118
113
  resourcePath: string;
119
114
  };
120
115
 
121
- type LegacyResolveRequest = {
116
+ type ResolveRequest = {
122
117
  source: string;
123
118
  importer: string | undefined;
124
119
  options: Record<string, unknown> | undefined;
@@ -128,6 +123,7 @@ const RESOURCE_COMPILER = "glib-compile-resources";
128
123
  const MANIFEST_PREFIX = "/";
129
124
  const DEV_STAGING_PREFIX = "resources-dev";
130
125
  const MANIFEST_FILENAME = "package.json";
126
+ const DATA_PREFIX = "#data/";
131
127
  const BUNDLED_QUERY_MENTION_RE = /[?&](?:icon|resource)(?:[=&]|$)/;
132
128
  const ICON_EXTENSIONS: Set<string> = new Set([".png", ".svg", ".xpm"]);
133
129
 
@@ -164,16 +160,8 @@ const STOCK_ICON_CONTEXTS: Set<string> = new Set([
164
160
  const ICON_SIZE_RE = /^([1-9]\d*)x\1(?:@[1-9]\d*)?$/;
165
161
  const RESERVED_ICON_NAMES: Set<string> = new Set(["", ".", ".."]);
166
162
 
167
- const toForwardSlashes = (value: string): string => value.replaceAll(/[/\\]/g, "/");
168
-
169
- const isWithin = (root: string, path: string): boolean => {
170
- const rel = relative(root, path);
171
-
172
- return rel.length > 0 && !isAbsolute(rel) && rel !== ".." && !rel.startsWith(`..${sep}`);
173
- };
174
-
175
163
  const isDependencyFile = (root: string, path: string): boolean =>
176
- isWithin(root, path) && relative(root, path).split(sep).includes("node_modules");
164
+ isPathInside(root, path) && relative(root, path).split(sep).includes("node_modules");
177
165
 
178
166
  const packageIdentityIn = (dir: string): string | null => {
179
167
  try {
@@ -216,17 +204,17 @@ const validateResourcePath = (resourcePath: string): string => {
216
204
  };
217
205
 
218
206
  const derivedResourcePath = (state: PluginState, sourcePath: string): string => {
219
- if (isWithin(state.root, sourcePath) && !isDependencyFile(state.root, sourcePath)) {
220
- return validateResourcePath(`${state.prefix}/${toForwardSlashes(relative(state.root, sourcePath))}`);
207
+ if (isPathInside(state.root, sourcePath) && !isDependencyFile(state.root, sourcePath)) {
208
+ return validateResourcePath(`${state.prefix}/${toPosixPath(relative(state.root, sourcePath))}`);
221
209
  }
222
210
 
223
211
  const owner = packageForFile(sourcePath);
224
212
 
225
- if (owner === null || !isWithin(owner.dir, sourcePath)) {
213
+ if (owner === null || !isPathInside(owner.dir, sourcePath)) {
226
214
  throw new Error(`${sourcePath} is outside the application and does not belong to a named package`);
227
215
  }
228
216
 
229
- const packagePath = toForwardSlashes(relative(owner.dir, sourcePath));
217
+ const packagePath = toPosixPath(relative(owner.dir, sourcePath));
230
218
 
231
219
  return validateResourcePath(`${state.prefix}/${owner.name}/${packagePath}`);
232
220
  };
@@ -292,7 +280,7 @@ const iconLayoutInParts = (parts: string[], beforeIndex: number): string | null
292
280
  };
293
281
 
294
282
  const iconLayoutFromTree = (sourcePath: string): string | null => {
295
- const parts = toForwardSlashes(sourcePath).split("/");
283
+ const parts = toPosixPath(sourcePath).split("/");
296
284
 
297
285
  return iconLayoutInParts(parts, parts.length - 2);
298
286
  };
@@ -335,11 +323,10 @@ const hasSideEffectIconImport = (code: string, id: string): boolean => {
335
323
  };
336
324
 
337
325
  const retainSideEffectIconImport = (
338
- state: PluginState,
339
326
  code: string,
340
327
  id: string,
341
328
  ): { code: string; moduleSideEffects: true } | null =>
342
- state.isV2 && hasSideEffectIconImport(code, id)
329
+ hasSideEffectIconImport(code, id)
343
330
  ? { code, moduleSideEffects: true }
344
331
  : null;
345
332
 
@@ -430,20 +417,6 @@ const refreshRegisteredDevBundle = (state: PluginState): void => {
430
417
  }
431
418
  };
432
419
 
433
- const scanDataAssets = (dataDir: string): ListedFile[] => listFilesRecursive(dataDir, (name) => ASSET_RE.test(name));
434
-
435
- const primeLegacyDevBundle = (state: PluginState): void => {
436
- if (state.dataDir === null) {
437
- return;
438
- }
439
-
440
- for (const { absPath, rel } of scanDataAssets(state.dataDir)) {
441
- registerEntry(state, absPath, rel);
442
- }
443
-
444
- compileDevBundle(state);
445
- };
446
-
447
420
  const registerDevAsset = (state: PluginState): void => {
448
421
  if (entriesSignature(state) !== state.compiledSignature) {
449
422
  compileDevBundle(state);
@@ -490,7 +463,7 @@ const activateEntry = (state: PluginState, entry: ResourceEntry): ResourceEntry
490
463
  };
491
464
 
492
465
  const isDeclarativeImporter = (state: PluginState, importer: string | undefined): importer is string =>
493
- !state.isBuild && state.isV2 && importer !== undefined;
466
+ !state.isBuild && importer !== undefined;
494
467
 
495
468
  const setSourceAvailability = (state: PluginState, file: string, isAvailable: boolean): void => {
496
469
  if (isAvailable) {
@@ -713,26 +686,6 @@ const resolveIconEntry = async (
713
686
  };
714
687
  };
715
688
 
716
- const resolveLegacyAssetEntry = async (
717
- ctx: ResolveContext,
718
- state: PluginState,
719
- request: LegacyResolveRequest,
720
- ): Promise<ResourceEntry | undefined> => {
721
- const resolved = await ctx.resolve(request.source, request.importer, { ...request.options, skipSelf: true });
722
-
723
- if (!resolved || resolved.external) {
724
- return undefined;
725
- }
726
-
727
- const sourcePath = stripQuery(resolved.id);
728
-
729
- if (!isAbsolute(sourcePath)) {
730
- return undefined;
731
- }
732
-
733
- return registerEntry(state, sourcePath, request.source.slice(DATA_PREFIX.length));
734
- };
735
-
736
689
  const loadInitModule = (state: PluginState): string => {
737
690
  if (state.isBuild) {
738
691
  if (state.bundleReferenceId === null) {
@@ -831,8 +784,7 @@ const loadAssetModule = (state: PluginState, virtualId: string): string => {
831
784
  const { absPath, iconName, rel } = virtualAssetMetadata(virtualId);
832
785
  const resourcePath = validateResourcePath(rel);
833
786
  const { entry, isRegistered } = loadedResourceEntry(state, absPath, resourcePath);
834
- const uri = `resource://${entry.resourcePath}`;
835
- const defaultValue = iconName ?? (state.isV2 ? entry.resourcePath : uri);
787
+ const defaultValue = iconName ?? entry.resourcePath;
836
788
  compileLoadedDevAsset(state, isRegistered);
837
789
 
838
790
  return [
@@ -899,12 +851,12 @@ const isSourceModule = (file: string): boolean => sourceLanguage(file) !== undef
899
851
  const isBundledSource = (source: string): boolean =>
900
852
  parseResourceSpecifier(source) !== null || parseIconSpecifier(source) !== null;
901
853
 
902
- const validateUnbundledV2Source = (source: string): void => {
854
+ const validateUnbundledSource = (source: string): void => {
903
855
  if (isLegacyDataSpecifier(source)) {
904
856
  throw new Error(`${JSON.stringify(source)} uses the legacy #data asset form`);
905
857
  }
906
858
 
907
- rejectInvalidV2Specifier(source);
859
+ rejectInvalidSpecifier(source);
908
860
  };
909
861
 
910
862
  const resolvedModuleDependency = async (
@@ -916,7 +868,7 @@ const resolvedModuleDependency = async (
916
868
  return null;
917
869
  }
918
870
 
919
- validateUnbundledV2Source(source);
871
+ validateUnbundledSource(source);
920
872
  const resolved = await ctx.resolve(source, importer, { skipSelf: true });
921
873
 
922
874
  if (resolved === null) {
@@ -952,7 +904,7 @@ const importerReconciliation = async (
952
904
  const file = stripQuery(id);
953
905
  const key = importerKey(file);
954
906
 
955
- if (!state.isV2 || state.isBuild || !isSourceModule(file)) {
907
+ if (state.isBuild || !isSourceModule(file)) {
956
908
  return null;
957
909
  }
958
910
 
@@ -1171,35 +1123,16 @@ const forgetImporterDeclarations = (state: PluginState, importer: string): void
1171
1123
  state.declaredIcons.delete(importer);
1172
1124
  };
1173
1125
 
1174
- const cloneStringSetMap = (source: Map<string, Set<string>>): Map<string, Set<string>> => {
1175
- const clone: Map<string, Set<string>> = new Map();
1176
-
1177
- for (const [key, values] of source) {
1178
- clone.set(key, new Set(values));
1179
- }
1180
-
1181
- return clone;
1182
- };
1183
-
1184
- const cloneDeclarationMap = (
1185
- source: Map<string, Map<string, ResourceEntry>>,
1186
- ): Map<string, Map<string, ResourceEntry>> => {
1187
- const clone: Map<string, Map<string, ResourceEntry>> = new Map();
1188
-
1189
- for (const [key, declarations] of source) {
1190
- clone.set(key, new Map(declarations));
1191
- }
1192
-
1193
- return clone;
1194
- };
1126
+ const cloneMapValues = <Value>(source: Map<string, Value>, clone: (value: Value) => Value): Map<string, Value> =>
1127
+ new Map([...source].map(([key, value]) => [key, clone(value)]));
1195
1128
 
1196
1129
  const reconciliationCandidate = (state: PluginState): PluginState => ({
1197
1130
  ...state,
1198
- bundledSpecifiers: cloneStringSetMap(state.bundledSpecifiers),
1199
- moduleDependencies: cloneStringSetMap(state.moduleDependencies),
1200
- prunedDependencies: cloneStringSetMap(state.prunedDependencies),
1201
- declaredEntries: cloneDeclarationMap(state.declaredEntries),
1202
- declaredIcons: cloneDeclarationMap(state.declaredIcons),
1131
+ bundledSpecifiers: cloneMapValues(state.bundledSpecifiers, (values) => new Set(values)),
1132
+ moduleDependencies: cloneMapValues(state.moduleDependencies, (values) => new Set(values)),
1133
+ prunedDependencies: cloneMapValues(state.prunedDependencies, (values) => new Set(values)),
1134
+ declaredEntries: cloneMapValues(state.declaredEntries, (entries) => new Map(entries)),
1135
+ declaredIcons: cloneMapValues(state.declaredIcons, (entries) => new Map(entries)),
1203
1136
  missingSourcePaths: new Set(state.missingSourcePaths),
1204
1137
  });
1205
1138
 
@@ -1333,7 +1266,7 @@ const commitReconciliation = (state: PluginState, candidate: PluginState): void
1333
1266
  state.iconOwners = candidate.iconOwners;
1334
1267
  };
1335
1268
 
1336
- const reconcileV2Importer = async (ctx: ResolveContext, state: PluginState, id: string): Promise<void> => {
1269
+ const reconcileImporter = async (ctx: ResolveContext, state: PluginState, id: string): Promise<void> => {
1337
1270
  const reconciliation = await importerReconciliation(ctx, state, id);
1338
1271
 
1339
1272
  if (reconciliation === null) {
@@ -1353,7 +1286,6 @@ const resolveResourceConfig = async (state: PluginState, config: UserConfig, loa
1353
1286
  const loaded = await loadConfig.load(config.root ?? process.cwd());
1354
1287
  state.prefix = resourceBasePath(loaded.config.applicationId);
1355
1288
  state.root = loaded.root;
1356
- state.isV2 = loaded.config.future?.v2ResourceImports === true;
1357
1289
 
1358
1290
  return {
1359
1291
  assetsInclude: [ASSET_RE],
@@ -1423,14 +1355,6 @@ const attachResourceWatcher = (state: PluginState, server: ViteDevServer): void
1423
1355
  const applyResolvedConfig = (state: PluginState, config: ResolvedConfig): void => {
1424
1356
  state.isBuild = config.command === "build";
1425
1357
  state.root = config.root;
1426
- state.dataDir = null;
1427
-
1428
- if (state.isBuild || state.isV2) {
1429
- return;
1430
- }
1431
-
1432
- const relativeDataDir = resolveDataDir(config.root);
1433
- state.dataDir = relativeDataDir === null ? null : join(config.root, relativeDataDir);
1434
1358
  };
1435
1359
 
1436
1360
  const loadResourceModule = (state: PluginState, id: string): string | undefined => {
@@ -1448,7 +1372,7 @@ const loadResourceModule = (state: PluginState, id: string): string | undefined
1448
1372
  const isLegacyDataSpecifier = (source: string): boolean =>
1449
1373
  source.startsWith(DATA_PREFIX) && ASSET_RE.test(stripQuery(source));
1450
1374
 
1451
- const rejectInvalidV2Specifier = (source: string): void => {
1375
+ const rejectInvalidSpecifier = (source: string): void => {
1452
1376
  if (BUNDLED_QUERY_MENTION_RE.test(source)) {
1453
1377
  throw new Error(
1454
1378
  `${JSON.stringify(source)} is not a valid bundled asset import; use ?resource, ` +
@@ -1467,7 +1391,7 @@ const rejectInvalidV2Specifier = (source: string): void => {
1467
1391
  const resolveExplicitResourceId = async (
1468
1392
  ctx: ResolveContext,
1469
1393
  state: PluginState,
1470
- request: LegacyResolveRequest,
1394
+ request: ResolveRequest,
1471
1395
  ): Promise<string | undefined> => {
1472
1396
  const specifier = parseResourceSpecifier(request.source);
1473
1397
 
@@ -1488,7 +1412,7 @@ const resolveExplicitResourceId = async (
1488
1412
  const resolveIconResourceId = async (
1489
1413
  ctx: ResolveContext,
1490
1414
  state: PluginState,
1491
- request: LegacyResolveRequest,
1415
+ request: ResolveRequest,
1492
1416
  ): Promise<RetainedIconModuleId | undefined> => {
1493
1417
  const specifier = parseIconSpecifier(request.source);
1494
1418
 
@@ -1508,10 +1432,10 @@ const resolveIconResourceId = async (
1508
1432
  : { id: virtualIconId(resolved), moduleSideEffects: true };
1509
1433
  };
1510
1434
 
1511
- const resolveV2ResourceId = async (
1435
+ const resolveBundledResourceId = async (
1512
1436
  ctx: ResolveContext,
1513
1437
  state: PluginState,
1514
- request: LegacyResolveRequest,
1438
+ request: ResolveRequest,
1515
1439
  ): Promise<ResourceModuleId | undefined> => {
1516
1440
  if (isLegacyDataSpecifier(request.source)) {
1517
1441
  throw new Error(
@@ -1534,46 +1458,21 @@ const resolveV2ResourceId = async (
1534
1458
  return resolved;
1535
1459
  }
1536
1460
 
1537
- rejectInvalidV2Specifier(request.source);
1461
+ rejectInvalidSpecifier(request.source);
1538
1462
 
1539
1463
  return undefined;
1540
1464
  };
1541
1465
 
1542
- const resolveLegacyResourceId = async (
1543
- ctx: ResolveContext,
1544
- state: PluginState,
1545
- request: LegacyResolveRequest,
1546
- ): Promise<string | undefined> => {
1547
- if (BUNDLED_QUERY_MENTION_RE.test(request.source)) {
1548
- throw new Error(`${JSON.stringify(request.source)} requires future.v2ResourceImports`);
1549
- }
1550
-
1551
- if (!isDataAsset(request.source)) {
1552
- return undefined;
1553
- }
1554
-
1555
- const entry = await resolveLegacyAssetEntry(ctx, state, request);
1556
-
1557
- return entry === undefined ? undefined : virtualAssetId(entry);
1558
- };
1559
-
1560
1466
  const resolveResourceId = (
1561
1467
  ctx: ResolveContext,
1562
1468
  state: PluginState,
1563
- request: LegacyResolveRequest,
1469
+ request: ResolveRequest,
1564
1470
  ): Promise<ResourceModuleId | undefined> => {
1565
1471
  if (request.source === VIRTUAL_INIT) {
1566
1472
  return Promise.resolve(VIRTUAL_INIT);
1567
1473
  }
1568
1474
 
1569
- return state.isV2
1570
- ? resolveV2ResourceId(ctx, state, request)
1571
- : resolveLegacyResourceId(ctx, state, request);
1572
- };
1573
-
1574
- const configureResourceServer = (state: PluginState, server: ViteDevServer): void => {
1575
- attachResourceWatcher(state, server);
1576
- primeLegacyDevBundle(state);
1475
+ return resolveBundledResourceId(ctx, state, request);
1577
1476
  };
1578
1477
 
1579
1478
  const createResourcesPlugin = (state: PluginState, loadConfig: ConfigLoader): Plugin => ({
@@ -1588,7 +1487,7 @@ const createResourcesPlugin = (state: PluginState, loadConfig: ConfigLoader): Pl
1588
1487
  },
1589
1488
 
1590
1489
  configureServer: (server) => {
1591
- configureResourceServer(state, server);
1490
+ attachResourceWatcher(state, server);
1592
1491
  },
1593
1492
 
1594
1493
  buildStart() {
@@ -1610,9 +1509,9 @@ const createResourcesPlugin = (state: PluginState, loadConfig: ConfigLoader): Pl
1610
1509
  },
1611
1510
 
1612
1511
  async transform(code, id) {
1613
- await reconcileV2Importer(this, state, id);
1512
+ await withExclusiveLoad(state, () => reconcileImporter(this, state, id));
1614
1513
 
1615
- return retainSideEffectIconImport(state, code, id);
1514
+ return retainSideEffectIconImport(code, id);
1616
1515
  },
1617
1516
 
1618
1517
  generateBundle(_options, bundle) {
@@ -1629,7 +1528,6 @@ function gtkxResources(loadConfig: ConfigLoader = createConfigLoader(), entryPat
1629
1528
  prefix: "",
1630
1529
  root: "",
1631
1530
  isBuild: false,
1632
- isV2: false,
1633
1531
  entries: new Map(),
1634
1532
  bundledSpecifiers: new Map(),
1635
1533
  moduleDependencies: new Map(),
@@ -1643,7 +1541,6 @@ function gtkxResources(loadConfig: ConfigLoader = createConfigLoader(), entryPat
1643
1541
  devBundlePath: "",
1644
1542
  server: null,
1645
1543
  compiledSignature: "",
1646
- dataDir: null,
1647
1544
  bundleReferenceId: null,
1648
1545
  iconOwners: new Map(),
1649
1546
  };
@@ -1,7 +1,6 @@
1
1
  import type { UserConfig } from "vite";
2
2
  import type { Plugin } from "vitest/config";
3
3
  import { createConfigLoader } from "@gtkx/config/internal";
4
- import { resolveDataDir } from "../internal/data-dir.js";
5
4
  import { prependSchemaDir, stageAndCompileProjectSchemas } from "../settings/schema.js";
6
5
 
7
6
  function gtkxSettingsWorkerEnv(): Plugin {
@@ -13,8 +12,7 @@ function gtkxSettingsWorkerEnv(): Plugin {
13
12
 
14
13
  async config(config: UserConfig) {
15
14
  const loaded = await loadConfig.load(config.root ?? process.cwd());
16
- const dataDir = loaded.config.future?.v2ResourceImports === true ? null : resolveDataDir(loaded.root);
17
- const dir = stageAndCompileProjectSchemas(loaded.root, dataDir);
15
+ const dir = stageAndCompileProjectSchemas(loaded.root);
18
16
 
19
17
  if (dir === null) {
20
18
  return;