@gtkx/cli 1.1.0 → 1.2.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 (153) hide show
  1. package/dist/builder.d.ts.map +1 -1
  2. package/dist/builder.js +2 -0
  3. package/dist/builder.js.map +1 -1
  4. package/dist/codegen/report.d.ts.map +1 -1
  5. package/dist/codegen/report.js +3 -0
  6. package/dist/codegen/report.js.map +1 -1
  7. package/dist/codegen/run-codegen.d.ts +1 -0
  8. package/dist/codegen/run-codegen.d.ts.map +1 -1
  9. package/dist/codegen/run-codegen.js +7 -1
  10. package/dist/codegen/run-codegen.js.map +1 -1
  11. package/dist/commands/docs.d.ts.map +1 -1
  12. package/dist/commands/docs.js +1 -0
  13. package/dist/commands/docs.js.map +1 -1
  14. package/dist/deploy/freedesktop/copyright.d.ts +2 -2
  15. package/dist/deploy/freedesktop/copyright.d.ts.map +1 -1
  16. package/dist/deploy/freedesktop/copyright.js +105 -14
  17. package/dist/deploy/freedesktop/copyright.js.map +1 -1
  18. package/dist/deploy/install-hints.d.ts.map +1 -1
  19. package/dist/deploy/install-hints.js +7 -4
  20. package/dist/deploy/install-hints.js.map +1 -1
  21. package/dist/deploy/node-runtime/download.d.ts +5 -1
  22. package/dist/deploy/node-runtime/download.d.ts.map +1 -1
  23. package/dist/deploy/node-runtime/download.js +6 -3
  24. package/dist/deploy/node-runtime/download.js.map +1 -1
  25. package/dist/deploy/node-runtime/index.d.ts +2 -1
  26. package/dist/deploy/node-runtime/index.d.ts.map +1 -1
  27. package/dist/deploy/node-runtime/index.js +10 -9
  28. package/dist/deploy/node-runtime/index.js.map +1 -1
  29. package/dist/deploy/node-runtime/license.d.ts +4 -0
  30. package/dist/deploy/node-runtime/license.d.ts.map +1 -0
  31. package/dist/deploy/node-runtime/license.js +19 -0
  32. package/dist/deploy/node-runtime/license.js.map +1 -0
  33. package/dist/deploy/notices/collect.d.ts +8 -0
  34. package/dist/deploy/notices/collect.d.ts.map +1 -0
  35. package/dist/deploy/notices/collect.js +17 -0
  36. package/dist/deploy/notices/collect.js.map +1 -0
  37. package/dist/deploy/notices/dependencies.d.ts +5 -0
  38. package/dist/deploy/notices/dependencies.d.ts.map +1 -0
  39. package/dist/deploy/notices/dependencies.js +39 -0
  40. package/dist/deploy/notices/dependencies.js.map +1 -0
  41. package/dist/deploy/notices/gtkx.d.ts +6 -0
  42. package/dist/deploy/notices/gtkx.d.ts.map +1 -0
  43. package/dist/deploy/notices/gtkx.js +68 -0
  44. package/dist/deploy/notices/gtkx.js.map +1 -0
  45. package/dist/deploy/notices/libraries.d.ts +4 -0
  46. package/dist/deploy/notices/libraries.d.ts.map +1 -0
  47. package/dist/deploy/notices/libraries.js +60 -0
  48. package/dist/deploy/notices/libraries.js.map +1 -0
  49. package/dist/deploy/notices/node-runtime.d.ts +4 -0
  50. package/dist/deploy/notices/node-runtime.d.ts.map +1 -0
  51. package/dist/deploy/notices/node-runtime.js +78 -0
  52. package/dist/deploy/notices/node-runtime.js.map +1 -0
  53. package/dist/deploy/notices/packages.d.ts +13 -0
  54. package/dist/deploy/notices/packages.d.ts.map +1 -0
  55. package/dist/deploy/notices/packages.js +115 -0
  56. package/dist/deploy/notices/packages.js.map +1 -0
  57. package/dist/deploy/notices/render.d.ts +4 -0
  58. package/dist/deploy/notices/render.d.ts.map +1 -0
  59. package/dist/deploy/notices/render.js +41 -0
  60. package/dist/deploy/notices/render.js.map +1 -0
  61. package/dist/deploy/notices/text.d.ts +5 -0
  62. package/dist/deploy/notices/text.d.ts.map +1 -0
  63. package/dist/deploy/notices/text.js +34 -0
  64. package/dist/deploy/notices/text.js.map +1 -0
  65. package/dist/deploy/payload/copy-tree.d.ts +2 -1
  66. package/dist/deploy/payload/copy-tree.d.ts.map +1 -1
  67. package/dist/deploy/payload/copy-tree.js +3 -1
  68. package/dist/deploy/payload/copy-tree.js.map +1 -1
  69. package/dist/deploy/payload/stage.d.ts +7 -3
  70. package/dist/deploy/payload/stage.d.ts.map +1 -1
  71. package/dist/deploy/payload/stage.js +25 -14
  72. package/dist/deploy/payload/stage.js.map +1 -1
  73. package/dist/deploy/run-deploy.d.ts.map +1 -1
  74. package/dist/deploy/run-deploy.js +28 -7
  75. package/dist/deploy/run-deploy.js.map +1 -1
  76. package/dist/deploy/settings/extra-files.d.ts +4 -0
  77. package/dist/deploy/settings/extra-files.d.ts.map +1 -0
  78. package/dist/deploy/settings/extra-files.js +11 -0
  79. package/dist/deploy/settings/extra-files.js.map +1 -0
  80. package/dist/deploy/settings/index.d.ts.map +1 -1
  81. package/dist/deploy/settings/index.js +2 -1
  82. package/dist/deploy/settings/index.js.map +1 -1
  83. package/dist/deploy/settings/package-manifest.d.ts +2 -0
  84. package/dist/deploy/settings/package-manifest.d.ts.map +1 -1
  85. package/dist/deploy/settings/package-manifest.js +8 -0
  86. package/dist/deploy/settings/package-manifest.js.map +1 -1
  87. package/dist/deploy/settings/paths.d.ts +2 -1
  88. package/dist/deploy/settings/paths.d.ts.map +1 -1
  89. package/dist/deploy/settings/paths.js +1 -1
  90. package/dist/deploy/settings/paths.js.map +1 -1
  91. package/dist/deploy/targets/flatpak-manifest.d.ts +3 -1
  92. package/dist/deploy/targets/flatpak-manifest.d.ts.map +1 -1
  93. package/dist/deploy/targets/flatpak-manifest.js +50 -4
  94. package/dist/deploy/targets/flatpak-manifest.js.map +1 -1
  95. package/dist/deploy/targets/flatpak-pnpm.d.ts +15 -0
  96. package/dist/deploy/targets/flatpak-pnpm.d.ts.map +1 -0
  97. package/dist/deploy/targets/flatpak-pnpm.js +69 -0
  98. package/dist/deploy/targets/flatpak-pnpm.js.map +1 -0
  99. package/dist/deploy/targets/flatpak-source.d.ts.map +1 -1
  100. package/dist/deploy/targets/flatpak-source.js +70 -7
  101. package/dist/deploy/targets/flatpak-source.js.map +1 -1
  102. package/dist/deploy/targets/flatpak-sources.d.ts +5 -3
  103. package/dist/deploy/targets/flatpak-sources.d.ts.map +1 -1
  104. package/dist/deploy/targets/flatpak-sources.js +23 -9
  105. package/dist/deploy/targets/flatpak-sources.js.map +1 -1
  106. package/dist/deploy/targets/flatpak.d.ts.map +1 -1
  107. package/dist/deploy/targets/flatpak.js +3 -2
  108. package/dist/deploy/targets/flatpak.js.map +1 -1
  109. package/dist/deploy/tools.d.ts +3 -1
  110. package/dist/deploy/tools.d.ts.map +1 -1
  111. package/dist/deploy/tools.js +17 -3
  112. package/dist/deploy/tools.js.map +1 -1
  113. package/dist/deploy/types.d.ts +22 -2
  114. package/dist/deploy/types.d.ts.map +1 -1
  115. package/dist/deploy/types.js.map +1 -1
  116. package/dist/vite-plugins/bundled-packages.d.ts +5 -0
  117. package/dist/vite-plugins/bundled-packages.d.ts.map +1 -0
  118. package/dist/vite-plugins/bundled-packages.js +63 -0
  119. package/dist/vite-plugins/bundled-packages.js.map +1 -0
  120. package/package.json +9 -9
  121. package/src/builder.ts +2 -0
  122. package/src/codegen/report.ts +4 -0
  123. package/src/codegen/run-codegen.ts +11 -1
  124. package/src/commands/docs.ts +1 -0
  125. package/src/deploy/freedesktop/copyright.ts +153 -18
  126. package/src/deploy/install-hints.ts +9 -6
  127. package/src/deploy/node-runtime/download.ts +13 -4
  128. package/src/deploy/node-runtime/index.ts +17 -11
  129. package/src/deploy/node-runtime/license.ts +24 -0
  130. package/src/deploy/notices/collect.ts +26 -0
  131. package/src/deploy/notices/dependencies.ts +53 -0
  132. package/src/deploy/notices/gtkx.ts +92 -0
  133. package/src/deploy/notices/libraries.ts +81 -0
  134. package/src/deploy/notices/node-runtime.ts +101 -0
  135. package/src/deploy/notices/packages.ts +168 -0
  136. package/src/deploy/notices/render.ts +52 -0
  137. package/src/deploy/notices/text.ts +42 -0
  138. package/src/deploy/payload/copy-tree.ts +5 -1
  139. package/src/deploy/payload/stage.ts +40 -16
  140. package/src/deploy/run-deploy.ts +34 -6
  141. package/src/deploy/settings/extra-files.ts +21 -0
  142. package/src/deploy/settings/index.ts +2 -1
  143. package/src/deploy/settings/package-manifest.ts +12 -0
  144. package/src/deploy/settings/paths.ts +1 -1
  145. package/src/deploy/targets/flatpak-manifest.ts +69 -4
  146. package/src/deploy/targets/flatpak-pnpm.ts +115 -0
  147. package/src/deploy/targets/flatpak-source.ts +105 -6
  148. package/src/deploy/targets/flatpak-sources.ts +36 -9
  149. package/src/deploy/targets/flatpak.ts +3 -2
  150. package/src/deploy/tools.ts +23 -2
  151. package/src/deploy/types.ts +27 -1
  152. package/src/vite-plugins/bundled-packages.ts +95 -0
  153. package/LICENSE +0 -373
@@ -1,13 +1,20 @@
1
- import { posix, relative } from "node:path";
1
+ import { existsSync, realpathSync } from "node:fs";
2
+ import { posix, relative, resolve } from "node:path";
2
3
  import type { DeployPayload, DeploySettings } from "../types.js";
3
4
  import { listFilesRecursive } from "../../internal/list-files.js";
4
5
  import { BUNDLE_FILENAME } from "../../vite-plugins/esm-extension.js";
5
6
  import { renderDbusService } from "../freedesktop/dbus-service.js";
6
7
  import { renderDesktopEntry } from "../freedesktop/desktop-entry.js";
7
8
  import { renderMetainfo } from "../freedesktop/metainfo.js";
9
+ import { renderMimePackage } from "../freedesktop/mime-package.js";
8
10
  import { optional } from "../nfpm/optional.js";
11
+ import { renderNotices } from "../notices/render.js";
12
+ import { executableModeFor } from "../payload/copy-tree.js";
9
13
  import { iconPathFor } from "../payload/icons.js";
10
14
  import { renderLauncher } from "../payload/launcher.js";
15
+ import { licenseDestination, nodeLicenseDestination, NOTICES_FILENAME, noticesDestination } from "../payload/stage.js";
16
+ import { isInside } from "../settings/paths.js";
17
+ import { pnpmPathFor, type PnpmPin, pnpmSources } from "./flatpak-pnpm.js";
11
18
  import { DESTINATION, type FlatpakModule, postInstallCommands, validationCommands } from "./flatpak-prebuilt.js";
12
19
  import {
13
20
  detectPackageManager,
@@ -15,9 +22,16 @@ import {
15
22
  installCommandFor,
16
23
  nodeExtensionPathFor,
17
24
  type PackageManager,
25
+ pnpmPinFor,
18
26
  resolveGitSource,
19
27
  } from "./flatpak-sources.js";
20
28
 
29
+ type InstalledFile = {
30
+ destination: string;
31
+ source: string;
32
+ resolved: string;
33
+ };
34
+
21
35
  const LAUNCHER_FILENAME = "launcher.sh";
22
36
  const MODULE_BUILD_ROOT = "/run/build";
23
37
  const RUNTIME_PREFIX = "/app";
@@ -25,6 +39,7 @@ const NPM_CACHE_DIR = "flatpak-node/npm-cache";
25
39
  const YARN_MIRROR_DIR = "flatpak-node/yarn-mirror";
26
40
  const PLAIN_ARGUMENT = /^[\w./-]+$/;
27
41
  const QUOTE_ESCAPE = String.raw`'\''`;
42
+ const OCTAL_RADIX = 8;
28
43
 
29
44
  const inlineSource = (fileName: string, contents: string): FlatpakModule => ({
30
45
  type: "inline",
@@ -72,6 +87,21 @@ const activationInstallCommands = (settings: DeploySettings): string[] =>
72
87
  )]
73
88
  : [];
74
89
 
90
+ const mimeSources = (settings: DeploySettings): FlatpakModule[] => {
91
+ const contents = renderMimePackage(settings);
92
+
93
+ return contents === null ? [] : [inlineSource(`${settings.applicationId}.xml`, contents)];
94
+ };
95
+
96
+ const mimeInstallCommands = (settings: DeploySettings): string[] =>
97
+ settings.fileAssociations.length === 0
98
+ ? []
99
+ : [installCommand(
100
+ `${settings.applicationId}.xml`,
101
+ `${DESTINATION}/share/mime/packages/${settings.applicationId}.xml`,
102
+ "m644",
103
+ )];
104
+
75
105
  const metadataInstallCommands = (settings: DeploySettings): string[] => [
76
106
  installCommand(
77
107
  `${settings.applicationId}.desktop`,
@@ -84,6 +114,7 @@ const metadataInstallCommands = (settings: DeploySettings): string[] => [
84
114
  "m644",
85
115
  ),
86
116
  ...activationInstallCommands(settings),
117
+ ...mimeInstallCommands(settings),
87
118
  ];
88
119
 
89
120
  const schemaInstallCommands = (settings: DeploySettings): string[] =>
@@ -118,19 +149,81 @@ const iconInstallCommands = (settings: DeploySettings): string[] => {
118
149
  return listFilesRecursive(iconsDir).map((icon) => iconInstallCommand(settings, icon.absPath, icon.rel));
119
150
  };
120
151
 
152
+ const assertInsideProject = (settings: DeploySettings, installed: InstalledFile): void => {
153
+ const { resolved } = installed;
154
+ const target = existsSync(resolved) ? realpathSync(resolved) : resolved;
155
+
156
+ if (isInside(settings.paths.root, target)) {
157
+ return;
158
+ }
159
+
160
+ throw new Error(
161
+ `Cannot install "${installed.destination}" from "${installed.source}": a source-mode manifest builds from ` +
162
+ `your git checkout, so every file it installs has to live inside ${settings.paths.root} and be committed. ` +
163
+ "Move it into the project, or drop it from the source build.",
164
+ );
165
+ };
166
+
167
+ const licenseInstallCommands = (settings: DeploySettings): string[] => {
168
+ const licenseFile = settings.paths.licenseFile;
169
+
170
+ if (licenseFile === null) {
171
+ return [];
172
+ }
173
+
174
+ const destination = licenseDestination(settings);
175
+ assertInsideProject(settings, { destination, source: licenseFile, resolved: licenseFile });
176
+ const target = `${DESTINATION}/${shellArgument(destination)}`;
177
+
178
+ return [installCommand(projectRelative(settings, licenseFile), target, "m644")];
179
+ };
180
+
181
+ const noticesInstallCommands = (settings: DeploySettings): string[] => {
182
+ const extensionLicense = `${nodeExtensionPathFor(settings)}/LICENSE`;
183
+ const nodeTarget = `${DESTINATION}/${shellArgument(nodeLicenseDestination(settings))}`;
184
+
185
+ return [
186
+ installCommand(NOTICES_FILENAME, `${DESTINATION}/${shellArgument(noticesDestination(settings))}`, "m644"),
187
+ `test ! -f ${extensionLicense} || ${installCommand(extensionLicense, nodeTarget, "m644")}`,
188
+ ];
189
+ };
190
+
191
+ const extraFileInstallCommands = (settings: DeploySettings): string[] =>
192
+ settings.extraFiles.map((file) => {
193
+ const resolved = resolve(settings.paths.root, file.source);
194
+ assertInsideProject(settings, { destination: file.destination, source: file.source, resolved });
195
+ const mode = file.mode ?? executableModeFor(resolved);
196
+
197
+ return installCommand(
198
+ projectRelative(settings, resolved),
199
+ `${DESTINATION}/${shellArgument(file.destination)}`,
200
+ `m${mode.toString(OCTAL_RADIX)}`,
201
+ );
202
+ });
203
+
204
+ const moduleDirFor = (settings: DeploySettings): string => `${MODULE_BUILD_ROOT}/${settings.binaryName}`;
205
+
121
206
  const offlineEnvFor = (manager: PackageManager, settings: DeploySettings): Record<string, string> => {
122
- const moduleDir = `${MODULE_BUILD_ROOT}/${settings.binaryName}`;
207
+ if (manager === "pnpm") {
208
+ return {};
209
+ }
123
210
 
124
211
  if (manager === "yarn") {
125
- return { YARN_OFFLINE_MIRROR: `${moduleDir}/${YARN_MIRROR_DIR}` };
212
+ return { YARN_OFFLINE_MIRROR: `${moduleDirFor(settings)}/${YARN_MIRROR_DIR}` };
126
213
  }
127
214
 
128
- return { npm_config_cache: `${moduleDir}/${NPM_CACHE_DIR}`, npm_config_offline: "true" };
215
+ return { npm_config_cache: `${moduleDirFor(settings)}/${NPM_CACHE_DIR}`, npm_config_offline: "true" };
129
216
  };
130
217
 
218
+ const appendPathFor = (settings: DeploySettings, pin: PnpmPin | null, nodeExtensionPath: string): string =>
219
+ pin === null
220
+ ? `${nodeExtensionPath}/bin`
221
+ : `${pnpmPathFor(moduleDirFor(settings))}:${nodeExtensionPath}/bin`;
222
+
131
223
  const flatpakSourceModule = (payload: DeployPayload): FlatpakModule => {
132
224
  const settings = payload.settings;
133
225
  const manager = detectPackageManager(settings);
226
+ const pin = pnpmPinFor(settings, manager);
134
227
  const postInstall = postInstallCommands(payload);
135
228
  const nodeExtensionPath = nodeExtensionPathFor(settings);
136
229
 
@@ -138,26 +231,32 @@ const flatpakSourceModule = (payload: DeployPayload): FlatpakModule => {
138
231
  name: settings.binaryName,
139
232
  buildsystem: "simple",
140
233
  "build-options": {
141
- "append-path": `${nodeExtensionPath}/bin`,
234
+ "append-path": appendPathFor(settings, pin, nodeExtensionPath),
142
235
  env: { npm_config_nodedir: nodeExtensionPath, ...offlineEnvFor(manager, settings) },
143
236
  strip: false,
144
237
  "no-debuginfo": true,
145
238
  },
146
239
  sources: [
147
240
  resolveGitSource(settings),
241
+ ...(pin === null ? [] : pnpmSources(pin, moduleDirFor(settings))),
148
242
  GENERATED_SOURCES,
149
243
  inlineSource(`${settings.applicationId}.desktop`, renderDesktopEntry(settings)),
150
244
  inlineSource(`${settings.applicationId}.metainfo.xml`, renderMetainfo(settings)),
245
+ ...mimeSources(settings),
151
246
  inlineSource(LAUNCHER_FILENAME, renderLauncher(settings)),
247
+ inlineSource(NOTICES_FILENAME, renderNotices(settings, payload.notices)),
152
248
  ...activationSource(settings),
153
249
  ],
154
250
  "build-commands": [
155
- installCommandFor(manager),
251
+ installCommandFor(settings, manager),
156
252
  "npx gtkx build",
157
253
  ...runtimeInstallCommands(settings, nodeExtensionPath),
158
254
  ...metadataInstallCommands(settings),
159
255
  ...schemaInstallCommands(settings),
160
256
  ...iconInstallCommands(settings),
257
+ ...licenseInstallCommands(settings),
258
+ ...noticesInstallCommands(settings),
259
+ ...extraFileInstallCommands(settings),
161
260
  ...(settings.deploy.flatpak?.buildCommands ?? []),
162
261
  ...validationCommands(settings),
163
262
  ],
@@ -5,8 +5,11 @@ import type { DeploySettings } from "../types.js";
5
5
  import { runCliTool } from "../../internal/run-cli-tool.js";
6
6
  import { gitRemoteUrl, runGit } from "../git.js";
7
7
  import { optional } from "../nfpm/optional.js";
8
+ import { FLATPAK_NODE_GENERATOR, GENERATOR_PNPM_OPTION } from "../tools.js";
9
+ import { pnpmInstallCommand, type PnpmPin, pnpmStoreVersionFor, resolvePnpmPin } from "./flatpak-pnpm.js";
8
10
 
9
11
  type PackageManager = "npm" | "pnpm" | "yarn";
12
+ type VendoredManager = Exclude<PackageManager, "pnpm">;
10
13
 
11
14
  type GitSource = {
12
15
  type: "git";
@@ -21,7 +24,7 @@ type GitRevision = {
21
24
  };
22
25
 
23
26
  const GENERATED_SOURCES = "generated-sources.json";
24
- const GENERATOR = "flatpak-node-generator";
27
+ const COMMIT_PEEL = "^{commit}";
25
28
  const FETCHABLE_URL = /^https?:\/\//;
26
29
  const DEFAULT_NODE_EXTENSION = "org.freedesktop.Sdk.Extension.node24";
27
30
  const NODE_EXTENSION_PREFIX = "org.freedesktop.Sdk.Extension.";
@@ -33,9 +36,8 @@ const LOCKFILE_BY_MANAGER: Record<PackageManager, string> = {
33
36
  yarn: "yarn.lock",
34
37
  };
35
38
 
36
- const INSTALL_COMMAND: Record<PackageManager, string> = {
39
+ const INSTALL_COMMAND: Record<VendoredManager, string> = {
37
40
  npm: "npm ci --offline",
38
- pnpm: "pnpm install --offline --frozen-lockfile",
39
41
  yarn: "yarn install --offline",
40
42
  };
41
43
 
@@ -59,7 +61,11 @@ const detectPackageManager = (settings: DeploySettings): PackageManager => {
59
61
  return found;
60
62
  };
61
63
 
62
- const installCommandFor = (manager: PackageManager): string => INSTALL_COMMAND[manager];
64
+ const pnpmPinFor = (settings: DeploySettings, manager: PackageManager): PnpmPin | null =>
65
+ manager === "pnpm" ? resolvePnpmPin(settings) : null;
66
+
67
+ const installCommandFor = (settings: DeploySettings, manager: PackageManager): string =>
68
+ manager === "pnpm" ? pnpmInstallCommand(resolvePnpmPin(settings)) : INSTALL_COMMAND[manager];
63
69
 
64
70
  const nodeExtensionFor = (settings: DeploySettings): string =>
65
71
  settings.deploy.flatpak?.nodeExtension ?? DEFAULT_NODE_EXTENSION;
@@ -98,6 +104,19 @@ const resolveSourceUrl = (settings: DeploySettings): string => {
98
104
  return url;
99
105
  };
100
106
 
107
+ const commitForTag = (root: string, tag: string): string => {
108
+ const commit = runGit(root, ["rev-parse", `${tag}${COMMIT_PEEL}`]);
109
+
110
+ if (commit === null) {
111
+ throw new Error(
112
+ `Cannot pin the Flathub source to "${tag}": Flathub builds a fixed tree, not a movable tag, and that ` +
113
+ `tag does not resolve in ${root}. Create or fetch it, or set \`deploy.flatpak.source.commit\`.`,
114
+ );
115
+ }
116
+
117
+ return commit;
118
+ };
119
+
101
120
  const configuredRevision = (settings: DeploySettings): GitRevision | null => {
102
121
  const source = settings.deploy.flatpak?.source ?? {};
103
122
 
@@ -105,7 +124,11 @@ const configuredRevision = (settings: DeploySettings): GitRevision | null => {
105
124
  return { ...optional("tag", source.tag), commit: source.commit };
106
125
  }
107
126
 
108
- return source.tag === undefined ? null : { tag: source.tag };
127
+ if (source.tag === undefined) {
128
+ return null;
129
+ }
130
+
131
+ return { tag: source.tag, commit: commitForTag(settings.paths.root, source.tag) };
109
132
  };
110
133
 
111
134
  const workingTreeRevision = (settings: DeploySettings): GitRevision => {
@@ -141,7 +164,10 @@ const isolateLockfile = (root: string, lockfile: string): string => {
141
164
  return dir;
142
165
  };
143
166
 
144
- const generateNodeSources = (settings: DeploySettings, manager: PackageManager): void => {
167
+ const storeVersionArgs = (pin: PnpmPin | null): string[] =>
168
+ pin === null ? [] : [GENERATOR_PNPM_OPTION, pnpmStoreVersionFor(pin)];
169
+
170
+ const generateNodeSources = (settings: DeploySettings, manager: PackageManager, pin: PnpmPin | null): void => {
145
171
  const lockfile = settings.deploy.flatpak?.lockfile ?? LOCKFILE_BY_MANAGER[manager];
146
172
  const output = generatedSourcesPath(settings);
147
173
  mkdirSync(dirname(output), { recursive: true });
@@ -149,9 +175,9 @@ const generateNodeSources = (settings: DeploySettings, manager: PackageManager):
149
175
 
150
176
  try {
151
177
  runCliTool({
152
- tool: GENERATOR,
153
- args: [manager, join(isolated, basename(lockfile)), "-o", output],
154
- target: "the offline npm sources",
178
+ tool: FLATPAK_NODE_GENERATOR.command,
179
+ args: [manager, join(isolated, basename(lockfile)), ...storeVersionArgs(pin), "-o", output],
180
+ target: "the offline dependency sources",
155
181
  shouldStream: true,
156
182
  });
157
183
  } finally {
@@ -167,5 +193,6 @@ export {
167
193
  nodeExtensionFor,
168
194
  nodeExtensionPathFor,
169
195
  type PackageManager,
196
+ pnpmPinFor,
170
197
  resolveGitSource,
171
198
  };
@@ -6,7 +6,7 @@ import type { DeployArtifact, DeployManifest, DeployPayload, DeploySettings, Dep
6
6
  import { runCliTool } from "../../internal/run-cli-tool.js";
7
7
  import { APPSTREAMCLI, DESKTOP_FILE_VALIDATE, FLATPAK, FLATPAK_BUILDER } from "../tools.js";
8
8
  import { branchFor, renderFlatpakManifest } from "./flatpak-manifest.js";
9
- import { detectPackageManager, generateNodeSources } from "./flatpak-sources.js";
9
+ import { detectPackageManager, generateNodeSources, pnpmPinFor } from "./flatpak-sources.js";
10
10
 
11
11
  type BuilderInvocation = {
12
12
  tool: string;
@@ -45,7 +45,8 @@ const renderManifests = (payload: DeployPayload): DeployManifest[] => {
45
45
  const settings = payload.settings;
46
46
 
47
47
  if (isSourceMode(settings)) {
48
- generateNodeSources(settings, detectPackageManager(settings));
48
+ const manager = detectPackageManager(settings);
49
+ generateNodeSources(settings, manager, pnpmPinFor(settings, manager));
49
50
  }
50
51
 
51
52
  return [{
@@ -10,6 +10,8 @@ type ToolReport = {
10
10
 
11
11
  const DETAIL_COLUMN = 26;
12
12
  const FLATPAK_BUILDER_REF = "org.flatpak.Builder";
13
+ const FLATPAK_NODE_GENERATOR_COMMAND = "flatpak-node-generator";
14
+ const GENERATOR_PNPM_OPTION = "--pnpm-store-version";
13
15
 
14
16
  const APPSTREAMCLI: DeployTool = {
15
17
  command: "appstreamcli",
@@ -44,11 +46,16 @@ const FLATPAK_BUILDER: DeployTool = {
44
46
  };
45
47
 
46
48
  const FLATPAK_NODE_GENERATOR: DeployTool = {
47
- command: "flatpak-node-generator",
48
- purpose: "vendors the npm dependencies a Flathub build installs offline",
49
+ command: FLATPAK_NODE_GENERATOR_COMMAND,
50
+ purpose: "vendors the dependencies a Flathub build installs offline",
49
51
  isOptional: false,
50
52
  };
51
53
 
54
+ const FLATPAK_NODE_GENERATOR_PNPM: DeployTool = {
55
+ ...FLATPAK_NODE_GENERATOR,
56
+ isPresent: () => hasGeneratorPnpmSupport(),
57
+ };
58
+
52
59
  const STRIP: DeployTool = {
53
60
  command: "strip",
54
61
  purpose: "would shrink the bundled Node.js by removing its symbols",
@@ -71,6 +78,18 @@ const isFlatpakRefInstalled = (ref: string): boolean => {
71
78
  return spawnSync(flatpak, ["info", ref], { stdio: "ignore" }).status === 0;
72
79
  };
73
80
 
81
+ const hasGeneratorPnpmSupport = (): boolean => {
82
+ const generator = tryResolveExecutable(FLATPAK_NODE_GENERATOR_COMMAND);
83
+
84
+ if (generator === undefined) {
85
+ return false;
86
+ }
87
+
88
+ const help = spawnSync(generator, ["--help"], { encoding: "utf8" });
89
+
90
+ return help.error === undefined && help.output.join("").includes(GENERATOR_PNPM_OPTION);
91
+ };
92
+
74
93
  const isToolPresent = (tool: DeployTool): boolean =>
75
94
  tool.isPresent === undefined ? tryResolveExecutable(tool.command) !== undefined : tool.isPresent();
76
95
 
@@ -123,6 +142,8 @@ const warnMissingOptional = (report: ToolReport): void => {
123
142
  export {
124
143
  APPSTREAMCLI,
125
144
  FLATPAK_NODE_GENERATOR,
145
+ FLATPAK_NODE_GENERATOR_PNPM,
146
+ GENERATOR_PNPM_OPTION,
126
147
  assertTools,
127
148
  DESKTOP_FILE_VALIDATE,
128
149
  FILE_TOOL,
@@ -19,11 +19,27 @@ type StagedFile = {
19
19
 
20
20
  type NodeRuntime = {
21
21
  path: string;
22
+ licenseFile: string | null;
22
23
  version: string;
23
24
  glibcFloor: string | null;
24
25
  isStripped: boolean;
25
26
  };
26
27
 
28
+ type Notice = {
29
+ subject: string;
30
+ license: string;
31
+ source: string | null;
32
+ copyright: string[];
33
+ text: string | null;
34
+ };
35
+
36
+ type NoticeSection = {
37
+ title: string;
38
+ files: string[];
39
+ summary: string[];
40
+ notices: Notice[];
41
+ };
42
+
27
43
  type DeployDeveloper = {
28
44
  id: string | null;
29
45
  name: string;
@@ -51,6 +67,12 @@ type DeployFileAssociation = {
51
67
  description: string | null;
52
68
  };
53
69
 
70
+ type DeployExtraFile = {
71
+ destination: string;
72
+ source: string;
73
+ mode: number | null;
74
+ };
75
+
54
76
  type DeployDesktopAction = {
55
77
  id: string;
56
78
  name: string;
@@ -118,7 +140,7 @@ type DeploySettings = {
118
140
  desktopActions: DeployDesktopAction[];
119
141
  desktopEntry: Record<string, string>;
120
142
  isDbusActivatable: boolean;
121
- extraFiles: Record<string, string>;
143
+ extraFiles: DeployExtraFile[];
122
144
  versions: DeployVersions;
123
145
  arch: DeployArch;
124
146
  paths: DeployPaths;
@@ -130,6 +152,7 @@ type DeployPayload = {
130
152
  settings: DeploySettings;
131
153
  node: NodeRuntime | null;
132
154
  stage: StagedFile[];
155
+ notices: NoticeSection[];
133
156
  overlays: Record<DeployTargetName, StagedFile[]>;
134
157
  };
135
158
 
@@ -157,6 +180,7 @@ export type {
157
180
  DeployConfig,
158
181
  DeployDesktopAction,
159
182
  DeployDeveloper,
183
+ DeployExtraFile,
160
184
  DeployFileAssociation,
161
185
  DeployManifest,
162
186
  DeployPaths,
@@ -169,6 +193,8 @@ export type {
169
193
  DeployTool,
170
194
  DeployVersions,
171
195
  NodeRuntime,
196
+ Notice,
197
+ NoticeSection,
172
198
  PackageFamily,
173
199
  StagedFile,
174
200
  };
@@ -0,0 +1,95 @@
1
+ import type { Plugin } from "vite";
2
+ import { isRecord, sortStringsBy } from "@gtkx/utils";
3
+ import { readFileSync } from "node:fs";
4
+ import { dirname, join, relative, resolve } from "node:path";
5
+ import { stripQuery } from "./strip-query.js";
6
+
7
+ type PackageIdentity = {
8
+ name: string;
9
+ version: string | null;
10
+ };
11
+
12
+ type RecordedPackage = PackageIdentity & {
13
+ dir: string;
14
+ };
15
+
16
+ type PackagesState = {
17
+ outDir: string;
18
+ };
19
+
20
+ const BUNDLED_PACKAGES_FILENAME = "gtkx-packages.json";
21
+ const MANIFEST_FILENAME = "package.json";
22
+ const DEFAULT_OUT_DIR = "dist";
23
+ const JSON_INDENT = 4;
24
+
25
+ const identityIn = (dir: string): PackageIdentity | null => {
26
+ try {
27
+ const parsed: unknown = JSON.parse(readFileSync(join(dir, MANIFEST_FILENAME), "utf8"));
28
+
29
+ if (!isRecord(parsed) || typeof parsed.name !== "string") {
30
+ return null;
31
+ }
32
+
33
+ return { name: parsed.name, version: typeof parsed.version === "string" ? parsed.version : null };
34
+ } catch {
35
+ return null;
36
+ }
37
+ };
38
+
39
+ const packageIn = (dir: string): RecordedPackage | null => {
40
+ const identity = identityIn(dir);
41
+
42
+ if (identity !== null) {
43
+ return { ...identity, dir };
44
+ }
45
+
46
+ const parent = dirname(dir);
47
+
48
+ return parent === dir ? null : packageIn(parent);
49
+ };
50
+
51
+ const packageForModule = (id: string): RecordedPackage | null => {
52
+ const path = stripQuery(id);
53
+
54
+ return path.startsWith("/") ? packageIn(dirname(path)) : null;
55
+ };
56
+
57
+ const keyFor = (entry: RecordedPackage): string => `${entry.name}@${entry.version ?? ""}`;
58
+
59
+ const relativeTo = (outDir: string, entry: RecordedPackage): RecordedPackage => ({
60
+ ...entry,
61
+ dir: relative(outDir, entry.dir),
62
+ });
63
+
64
+ const renderPackages = (outDir: string, ids: string[]): string => {
65
+ 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));
68
+
69
+ return `${JSON.stringify({ packages }, null, JSON_INDENT)}\n`;
70
+ };
71
+
72
+ function gtkxBundledPackages(root: string): Plugin {
73
+ const state: PackagesState = { outDir: join(root, DEFAULT_OUT_DIR) };
74
+
75
+ return {
76
+ name: "gtkx:bundled-packages",
77
+ apply: "build",
78
+
79
+ configResolved(config) {
80
+ state.outDir = resolve(config.root, config.build.outDir);
81
+ },
82
+
83
+ generateBundle(_options, bundle) {
84
+ const ids = Object.values(bundle).flatMap((output) => (output.type === "chunk" ? output.moduleIds : []));
85
+
86
+ this.emitFile({
87
+ type: "asset",
88
+ fileName: BUNDLED_PACKAGES_FILENAME,
89
+ source: renderPackages(state.outDir, ids),
90
+ });
91
+ },
92
+ };
93
+ }
94
+
95
+ export { BUNDLED_PACKAGES_FILENAME, gtkxBundledPackages };