@gtkx/cli 1.1.0 → 1.2.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 (50) hide show
  1. package/dist/codegen/report.d.ts.map +1 -1
  2. package/dist/codegen/report.js +3 -0
  3. package/dist/codegen/report.js.map +1 -1
  4. package/dist/codegen/run-codegen.d.ts +1 -0
  5. package/dist/codegen/run-codegen.d.ts.map +1 -1
  6. package/dist/codegen/run-codegen.js +7 -1
  7. package/dist/codegen/run-codegen.js.map +1 -1
  8. package/dist/commands/docs.d.ts.map +1 -1
  9. package/dist/commands/docs.js +1 -0
  10. package/dist/commands/docs.js.map +1 -1
  11. package/dist/deploy/install-hints.d.ts.map +1 -1
  12. package/dist/deploy/install-hints.js +7 -4
  13. package/dist/deploy/install-hints.js.map +1 -1
  14. package/dist/deploy/run-deploy.d.ts.map +1 -1
  15. package/dist/deploy/run-deploy.js +6 -2
  16. package/dist/deploy/run-deploy.js.map +1 -1
  17. package/dist/deploy/settings/package-manifest.d.ts +1 -0
  18. package/dist/deploy/settings/package-manifest.d.ts.map +1 -1
  19. package/dist/deploy/settings/package-manifest.js +1 -0
  20. package/dist/deploy/settings/package-manifest.js.map +1 -1
  21. package/dist/deploy/targets/flatpak-pnpm.d.ts +15 -0
  22. package/dist/deploy/targets/flatpak-pnpm.d.ts.map +1 -0
  23. package/dist/deploy/targets/flatpak-pnpm.js +69 -0
  24. package/dist/deploy/targets/flatpak-pnpm.js.map +1 -0
  25. package/dist/deploy/targets/flatpak-source.d.ts.map +1 -1
  26. package/dist/deploy/targets/flatpak-source.js +15 -6
  27. package/dist/deploy/targets/flatpak-source.js.map +1 -1
  28. package/dist/deploy/targets/flatpak-sources.d.ts +5 -3
  29. package/dist/deploy/targets/flatpak-sources.d.ts.map +1 -1
  30. package/dist/deploy/targets/flatpak-sources.js +23 -9
  31. package/dist/deploy/targets/flatpak-sources.js.map +1 -1
  32. package/dist/deploy/targets/flatpak.d.ts.map +1 -1
  33. package/dist/deploy/targets/flatpak.js +3 -2
  34. package/dist/deploy/targets/flatpak.js.map +1 -1
  35. package/dist/deploy/tools.d.ts +3 -1
  36. package/dist/deploy/tools.d.ts.map +1 -1
  37. package/dist/deploy/tools.js +17 -3
  38. package/dist/deploy/tools.js.map +1 -1
  39. package/package.json +9 -9
  40. package/src/codegen/report.ts +4 -0
  41. package/src/codegen/run-codegen.ts +11 -1
  42. package/src/commands/docs.ts +1 -0
  43. package/src/deploy/install-hints.ts +9 -6
  44. package/src/deploy/run-deploy.ts +7 -1
  45. package/src/deploy/settings/package-manifest.ts +2 -0
  46. package/src/deploy/targets/flatpak-pnpm.ts +115 -0
  47. package/src/deploy/targets/flatpak-source.ts +18 -5
  48. package/src/deploy/targets/flatpak-sources.ts +36 -9
  49. package/src/deploy/targets/flatpak.ts +3 -2
  50. package/src/deploy/tools.ts +23 -2
@@ -47,10 +47,14 @@ const PACKAGE_FOR_TOOL: Record<string, FamilyPackages> = {
47
47
  tar: { arch: "tar", debian: "tar", fedora: "tar", suse: "tar" },
48
48
  };
49
49
 
50
- const EXTRA_HINTS: Record<string, string> = {
51
- "flatpak-builder": "or: flatpak install --user -y flathub org.flatpak.Builder",
52
- "flatpak-node-generator":
53
- "pipx install git+https://github.com/flatpak/flatpak-builder-tools.git#subdirectory=node",
50
+ const GENERATOR_SOURCE = "git+https://github.com/flatpak/flatpak-builder-tools.git#subdirectory=node";
51
+
52
+ const EXTRA_HINTS: Record<string, string[]> = {
53
+ "flatpak-builder": ["or: flatpak install --user -y flathub org.flatpak.Builder"],
54
+ "flatpak-node-generator": [
55
+ `pipx install ${GENERATOR_SOURCE}`,
56
+ `or, when the installed copy is too old to vendor pnpm: pipx install --force ${GENERATOR_SOURCE}`,
57
+ ],
54
58
  };
55
59
 
56
60
  const unquote = (value: string): string => QUOTED.exec(value)?.[1] ?? value;
@@ -99,10 +103,9 @@ const detectPackageFamily = (): PackageFamily => {
99
103
  const installHints = (tool: string, family: PackageFamily): string[] => {
100
104
  const packageName = PACKAGE_FOR_TOOL[tool]?.[family];
101
105
  const command = INSTALL_COMMAND[family];
102
- const extra = EXTRA_HINTS[tool];
103
106
  const install = packageName !== undefined && command !== undefined ? [`${command} ${packageName}`] : [];
104
107
 
105
- return extra === undefined ? install : [...install, extra];
108
+ return [...install, ...(EXTRA_HINTS[tool] ?? [])];
106
109
  };
107
110
 
108
111
  export { detectPackageFamily, installHints };
@@ -22,11 +22,13 @@ import { DEFAULT_TARGETS, parseTargetList, targetsFor } from "./registry.js";
22
22
  import { resolveDeploySettings } from "./settings/index.js";
23
23
  import { readPackageManifest } from "./settings/package-manifest.js";
24
24
  import { missingDeployError } from "./settings/starter.js";
25
+ import { detectPackageManager } from "./targets/flatpak-sources.js";
25
26
  import {
26
27
  APPSTREAMCLI,
27
28
  assertTools,
28
29
  DESKTOP_FILE_VALIDATE,
29
30
  FLATPAK_NODE_GENERATOR,
31
+ FLATPAK_NODE_GENERATOR_PNPM,
30
32
  probeTools,
31
33
  STRIP,
32
34
  TAR,
@@ -93,7 +95,11 @@ const warnMissingNetwork = (settings: DeploySettings): void => {
93
95
  const sourceModeTools = (targets: DeployTarget[], settings: DeploySettings): DeployTool[] => {
94
96
  const isFlatpakSource = settings.deploy.flatpak?.mode === "source";
95
97
 
96
- return isFlatpakSource && targets.some((target) => target.name === "flatpak") ? [FLATPAK_NODE_GENERATOR] : [];
98
+ if (!isFlatpakSource || targets.every((target) => target.name !== "flatpak")) {
99
+ return [];
100
+ }
101
+
102
+ return detectPackageManager(settings) === "pnpm" ? [FLATPAK_NODE_GENERATOR_PNPM] : [FLATPAK_NODE_GENERATOR];
97
103
  };
98
104
 
99
105
  const isNodeRequired = (targets: DeployTarget[], settings: DeploySettings): boolean =>
@@ -13,6 +13,7 @@ type PackageManifest = {
13
13
  description: string | null;
14
14
  license: string | null;
15
15
  homepage: string | null;
16
+ packageManager: string | null;
16
17
  author: ManifestAuthor;
17
18
  };
18
19
 
@@ -59,6 +60,7 @@ const readPackageManifest = (root: string): PackageManifest => {
59
60
  description: optionalString(manifest.description),
60
61
  license: optionalString(manifest.license),
61
62
  homepage: optionalString(manifest.homepage),
63
+ packageManager: optionalString(manifest.packageManager),
62
64
  author: parseAuthor(manifest.author),
63
65
  };
64
66
  };
@@ -0,0 +1,115 @@
1
+ import type { DeploySettings } from "../types.js";
2
+ import type { FlatpakModule } from "./flatpak-prebuilt.js";
3
+ import { readPackageManifest } from "../settings/package-manifest.js";
4
+
5
+ type PnpmPin = {
6
+ version: string;
7
+ major: number;
8
+ minor: number;
9
+ sha512: string;
10
+ };
11
+
12
+ const PNPM_VERSION = "11.21.0";
13
+ const PNPM_MAJOR = 11;
14
+ const PNPM_MINOR = 21;
15
+
16
+ const PNPM_SHA512 = "521705bce689924eac72f5a3587122f362689ef6571e55ba80076fd637c11132ecffada26" +
17
+ "fad4ea79c485bfddbfd3d5a2a5b05805a77e893de71ec8a6cca3bb1";
18
+
19
+ const PNPM_DIR = "flatpak-pnpm";
20
+ const PNPM_COMMAND = "pnpm";
21
+ const PNPM_PREFIX = "pnpm@";
22
+ const PNPM_TARBALL_URL = "https://registry.npmjs.org/pnpm/-/pnpm-";
23
+ const PNPM_INSTALL = "pnpm install --offline --frozen-lockfile";
24
+ const TRUST_LOCKFILE = "--trust-lockfile";
25
+ const STORE_V10 = "v10";
26
+ const STORE_V11 = "v11";
27
+ const STORE_V10_MAJOR = 10;
28
+ const STORE_V11_MAJOR = 11;
29
+ const TRUST_LOCKFILE_MINOR = 3;
30
+
31
+ const DEFAULT_PIN: PnpmPin = {
32
+ version: PNPM_VERSION,
33
+ major: PNPM_MAJOR,
34
+ minor: PNPM_MINOR,
35
+ sha512: PNPM_SHA512,
36
+ };
37
+
38
+ const PNPM_PIN_FIELD =
39
+ /^pnpm@(?<version>(?<major>\d+)\.(?<minor>\d+)\.\d+)\+sha512\.(?<sha512>[\da-f]{128})$/;
40
+
41
+ const pinFromGroups = (groups: Record<string, string | undefined>): PnpmPin | null => {
42
+ const { major, minor, sha512, version } = groups;
43
+
44
+ if (major === undefined || minor === undefined || sha512 === undefined || version === undefined) {
45
+ return null;
46
+ }
47
+
48
+ return { version, major: Number(major), minor: Number(minor), sha512 };
49
+ };
50
+
51
+ const isSupportedPin = (pin: PnpmPin): boolean =>
52
+ pin.major === STORE_V10_MAJOR || (pin.major === STORE_V11_MAJOR && pin.minor >= TRUST_LOCKFILE_MINOR);
53
+
54
+ const resolvePnpmPin = (settings: DeploySettings): PnpmPin => {
55
+ const field = readPackageManifest(settings.paths.root).packageManager;
56
+
57
+ if (field === null) {
58
+ return DEFAULT_PIN;
59
+ }
60
+
61
+ if (!field.startsWith(PNPM_PREFIX)) {
62
+ throw new Error(
63
+ `Cannot vendor pnpm for the Flathub sandbox: "packageManager" is "${field}" but the offline install ` +
64
+ "resolves from a pnpm lockfile. Run `corepack use pnpm@<version>` to pin it to pnpm, or point " +
65
+ "`deploy.flatpak.packageManager` at the manager whose lockfile the build should install from.",
66
+ );
67
+ }
68
+
69
+ const pin = pinFromGroups(PNPM_PIN_FIELD.exec(field)?.groups ?? {});
70
+
71
+ if (pin === null) {
72
+ throw new Error(
73
+ `Cannot vendor pnpm for the Flathub sandbox: "packageManager" is "${field}", which carries no sha512 ` +
74
+ "integrity digest, and every source in a Flathub build has to be hash-pinned. Run " +
75
+ "`corepack use pnpm@<version>` to rewrite it with its digest.",
76
+ );
77
+ }
78
+
79
+ if (!isSupportedPin(pin)) {
80
+ throw new Error(
81
+ `Cannot vendor pnpm ${pin.version} for the Flathub sandbox: the offline install runs on pnpm 10, or on ` +
82
+ "pnpm 11 from 11.3.0 on, where `--trust-lockfile` exists. Earlier pnpm 11 releases reach the registry " +
83
+ 'during the supply-chain check and the sandbox has no network. Pin "packageManager" to one of those.',
84
+ );
85
+ }
86
+
87
+ return pin;
88
+ };
89
+
90
+ const pnpmPathFor = (moduleDir: string): string => `${moduleDir}/${PNPM_DIR}`;
91
+
92
+ const pnpmSources = (pin: PnpmPin, moduleDir: string): FlatpakModule[] => [
93
+ { type: "archive", url: `${PNPM_TARBALL_URL}${pin.version}.tgz`, sha512: pin.sha512, dest: PNPM_DIR },
94
+ {
95
+ type: "script",
96
+ dest: PNPM_DIR,
97
+ "dest-filename": PNPM_COMMAND,
98
+ commands: [`exec node ${pnpmPathFor(moduleDir)}/bin/pnpm.cjs "$@"`],
99
+ },
100
+ ];
101
+
102
+ const pnpmInstallCommand = (pin: PnpmPin): string =>
103
+ pin.major === STORE_V11_MAJOR ? `${PNPM_INSTALL} ${TRUST_LOCKFILE}` : PNPM_INSTALL;
104
+
105
+ const pnpmStoreVersionFor = (pin: PnpmPin): string =>
106
+ pin.major === STORE_V10_MAJOR ? STORE_V10 : STORE_V11;
107
+
108
+ export {
109
+ pnpmInstallCommand,
110
+ pnpmPathFor,
111
+ type PnpmPin,
112
+ pnpmSources,
113
+ pnpmStoreVersionFor,
114
+ resolvePnpmPin,
115
+ };
@@ -8,6 +8,7 @@ import { renderMetainfo } from "../freedesktop/metainfo.js";
8
8
  import { optional } from "../nfpm/optional.js";
9
9
  import { iconPathFor } from "../payload/icons.js";
10
10
  import { renderLauncher } from "../payload/launcher.js";
11
+ import { pnpmPathFor, type PnpmPin, pnpmSources } from "./flatpak-pnpm.js";
11
12
  import { DESTINATION, type FlatpakModule, postInstallCommands, validationCommands } from "./flatpak-prebuilt.js";
12
13
  import {
13
14
  detectPackageManager,
@@ -15,6 +16,7 @@ import {
15
16
  installCommandFor,
16
17
  nodeExtensionPathFor,
17
18
  type PackageManager,
19
+ pnpmPinFor,
18
20
  resolveGitSource,
19
21
  } from "./flatpak-sources.js";
20
22
 
@@ -118,19 +120,29 @@ const iconInstallCommands = (settings: DeploySettings): string[] => {
118
120
  return listFilesRecursive(iconsDir).map((icon) => iconInstallCommand(settings, icon.absPath, icon.rel));
119
121
  };
120
122
 
123
+ const moduleDirFor = (settings: DeploySettings): string => `${MODULE_BUILD_ROOT}/${settings.binaryName}`;
124
+
121
125
  const offlineEnvFor = (manager: PackageManager, settings: DeploySettings): Record<string, string> => {
122
- const moduleDir = `${MODULE_BUILD_ROOT}/${settings.binaryName}`;
126
+ if (manager === "pnpm") {
127
+ return {};
128
+ }
123
129
 
124
130
  if (manager === "yarn") {
125
- return { YARN_OFFLINE_MIRROR: `${moduleDir}/${YARN_MIRROR_DIR}` };
131
+ return { YARN_OFFLINE_MIRROR: `${moduleDirFor(settings)}/${YARN_MIRROR_DIR}` };
126
132
  }
127
133
 
128
- return { npm_config_cache: `${moduleDir}/${NPM_CACHE_DIR}`, npm_config_offline: "true" };
134
+ return { npm_config_cache: `${moduleDirFor(settings)}/${NPM_CACHE_DIR}`, npm_config_offline: "true" };
129
135
  };
130
136
 
137
+ const appendPathFor = (settings: DeploySettings, pin: PnpmPin | null, nodeExtensionPath: string): string =>
138
+ pin === null
139
+ ? `${nodeExtensionPath}/bin`
140
+ : `${pnpmPathFor(moduleDirFor(settings))}:${nodeExtensionPath}/bin`;
141
+
131
142
  const flatpakSourceModule = (payload: DeployPayload): FlatpakModule => {
132
143
  const settings = payload.settings;
133
144
  const manager = detectPackageManager(settings);
145
+ const pin = pnpmPinFor(settings, manager);
134
146
  const postInstall = postInstallCommands(payload);
135
147
  const nodeExtensionPath = nodeExtensionPathFor(settings);
136
148
 
@@ -138,13 +150,14 @@ const flatpakSourceModule = (payload: DeployPayload): FlatpakModule => {
138
150
  name: settings.binaryName,
139
151
  buildsystem: "simple",
140
152
  "build-options": {
141
- "append-path": `${nodeExtensionPath}/bin`,
153
+ "append-path": appendPathFor(settings, pin, nodeExtensionPath),
142
154
  env: { npm_config_nodedir: nodeExtensionPath, ...offlineEnvFor(manager, settings) },
143
155
  strip: false,
144
156
  "no-debuginfo": true,
145
157
  },
146
158
  sources: [
147
159
  resolveGitSource(settings),
160
+ ...(pin === null ? [] : pnpmSources(pin, moduleDirFor(settings))),
148
161
  GENERATED_SOURCES,
149
162
  inlineSource(`${settings.applicationId}.desktop`, renderDesktopEntry(settings)),
150
163
  inlineSource(`${settings.applicationId}.metainfo.xml`, renderMetainfo(settings)),
@@ -152,7 +165,7 @@ const flatpakSourceModule = (payload: DeployPayload): FlatpakModule => {
152
165
  ...activationSource(settings),
153
166
  ],
154
167
  "build-commands": [
155
- installCommandFor(manager),
168
+ installCommandFor(settings, manager),
156
169
  "npx gtkx build",
157
170
  ...runtimeInstallCommands(settings, nodeExtensionPath),
158
171
  ...metadataInstallCommands(settings),
@@ -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,