@gjsify/cli 0.3.21 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (67) hide show
  1. package/dist/cli.gjs.mjs +798 -0
  2. package/lib/actions/build.js +4 -17
  3. package/lib/bundler-pick.d.ts +79 -0
  4. package/lib/bundler-pick.js +428 -0
  5. package/lib/commands/foreach.d.ts +16 -0
  6. package/lib/commands/foreach.js +268 -0
  7. package/lib/commands/index.d.ts +2 -0
  8. package/lib/commands/index.js +2 -0
  9. package/lib/commands/install.d.ts +1 -0
  10. package/lib/commands/install.js +222 -26
  11. package/lib/commands/run.d.ts +1 -1
  12. package/lib/commands/run.js +133 -20
  13. package/lib/commands/workspace.d.ts +8 -0
  14. package/lib/commands/workspace.js +69 -0
  15. package/lib/config.js +12 -1
  16. package/lib/index.js +11 -3
  17. package/lib/types/config-data.d.ts +10 -1
  18. package/lib/utils/install-backend-native.d.ts +5 -1
  19. package/lib/utils/install-backend-native.js +88 -11
  20. package/lib/utils/install-backend.d.ts +11 -1
  21. package/lib/utils/install-backend.js +4 -2
  22. package/lib/utils/pkg-json-edit.d.ts +47 -0
  23. package/lib/utils/pkg-json-edit.js +108 -0
  24. package/package.json +36 -12
  25. package/src/actions/build.ts +0 -431
  26. package/src/actions/index.ts +0 -1
  27. package/src/commands/build.ts +0 -146
  28. package/src/commands/check.ts +0 -87
  29. package/src/commands/create.ts +0 -63
  30. package/src/commands/dlx.ts +0 -195
  31. package/src/commands/flatpak/build.ts +0 -225
  32. package/src/commands/flatpak/ci.ts +0 -173
  33. package/src/commands/flatpak/deps.ts +0 -120
  34. package/src/commands/flatpak/index.ts +0 -53
  35. package/src/commands/flatpak/init.ts +0 -191
  36. package/src/commands/flatpak/utils.ts +0 -76
  37. package/src/commands/gettext.ts +0 -258
  38. package/src/commands/gresource.ts +0 -97
  39. package/src/commands/gsettings.ts +0 -87
  40. package/src/commands/index.ts +0 -12
  41. package/src/commands/info.ts +0 -70
  42. package/src/commands/install.ts +0 -195
  43. package/src/commands/run.ts +0 -33
  44. package/src/commands/showcase.ts +0 -149
  45. package/src/config.ts +0 -304
  46. package/src/constants.ts +0 -1
  47. package/src/index.ts +0 -37
  48. package/src/types/cli-build-options.ts +0 -100
  49. package/src/types/command.ts +0 -10
  50. package/src/types/config-data-library.ts +0 -5
  51. package/src/types/config-data-typescript.ts +0 -6
  52. package/src/types/config-data.ts +0 -225
  53. package/src/types/cosmiconfig-result.ts +0 -5
  54. package/src/types/index.ts +0 -6
  55. package/src/utils/check-system-deps.ts +0 -480
  56. package/src/utils/detect-native-packages.ts +0 -153
  57. package/src/utils/discover-showcases.ts +0 -75
  58. package/src/utils/dlx-cache.ts +0 -135
  59. package/src/utils/install-backend-native.ts +0 -363
  60. package/src/utils/install-backend.ts +0 -88
  61. package/src/utils/install-global.ts +0 -182
  62. package/src/utils/normalize-bundler-options.ts +0 -129
  63. package/src/utils/parse-spec.ts +0 -48
  64. package/src/utils/resolve-gjs-entry.ts +0 -96
  65. package/src/utils/resolve-plugin-by-name.ts +0 -106
  66. package/src/utils/run-gjs.ts +0 -90
  67. package/tsconfig.json +0 -16
@@ -1,87 +0,0 @@
1
- import type { Command } from '../types/index.js';
2
- import { runAllChecks, detectPackageManager, buildInstallCommand } from '../utils/check-system-deps.js';
3
-
4
- interface CheckOptions {
5
- json: boolean;
6
- }
7
-
8
- export const checkCommand: Command<any, CheckOptions> = {
9
- command: 'check',
10
- description: 'Check that required system dependencies (GJS, GTK4, libsoup3, …) are installed. Optional dependencies are detected only when their @gjsify/* package is in your project.',
11
- builder: (yargs) => {
12
- return yargs
13
- .option('json', {
14
- description: 'Output results as JSON',
15
- type: 'boolean',
16
- default: false,
17
- });
18
- },
19
- handler: async (args) => {
20
- const results = runAllChecks(process.cwd());
21
- const pm = detectPackageManager();
22
- const missingRequired = results.filter(r => !r.found && r.severity === 'required');
23
- const missingOptional = results.filter(r => !r.found && r.severity === 'optional');
24
- const allMissing = [...missingRequired, ...missingOptional];
25
-
26
- if (args.json) {
27
- console.log(JSON.stringify({ packageManager: pm, deps: results }, null, 2));
28
- // Only required deps influence the exit code.
29
- process.exit(missingRequired.length > 0 ? 1 : 0);
30
- return;
31
- }
32
-
33
- console.log('System dependency check\n');
34
-
35
- const required = results.filter(r => r.severity === 'required');
36
- const optional = results.filter(r => r.severity === 'optional');
37
-
38
- if (required.length > 0) {
39
- console.log('Required:');
40
- for (const dep of required) {
41
- const icon = dep.found ? '✓' : '✗';
42
- const ver = dep.version ? ` (${dep.version})` : '';
43
- console.log(` ${icon} ${dep.name}${ver}`);
44
- }
45
- }
46
-
47
- if (optional.length > 0) {
48
- console.log('\nOptional:');
49
- for (const dep of optional) {
50
- // ⚠ for missing-but-needed-by-installed-packages, ○ for missing-but-not-needed (shouldn't appear in conditional mode)
51
- const icon = dep.found ? '✓' : '⚠';
52
- const ver = dep.version ? ` (${dep.version})` : '';
53
- const requiredBy = dep.requiredBy && dep.requiredBy.length > 0
54
- ? ` — needed by ${dep.requiredBy.join(', ')}`
55
- : '';
56
- console.log(` ${icon} ${dep.name}${ver}${requiredBy}`);
57
- }
58
- }
59
-
60
- console.log(`\nPackage manager: ${pm}`);
61
-
62
- if (allMissing.length === 0) {
63
- console.log('\nAll dependencies found.');
64
- return;
65
- }
66
-
67
- if (missingRequired.length > 0) {
68
- console.log(`\nMissing required: ${missingRequired.map(d => d.name).join(', ')}`);
69
- }
70
- if (missingOptional.length > 0) {
71
- console.log(`Missing optional: ${missingOptional.map(d => d.name).join(', ')}`);
72
- }
73
-
74
- const cmd = buildInstallCommand(pm, allMissing);
75
- if (cmd) {
76
- console.log(`\nTo install:\n ${cmd}`);
77
- } else {
78
- console.log('\nNo install command available for your package manager. Install manually.');
79
- }
80
-
81
- // Exit non-zero ONLY if a required dependency is missing.
82
- // Optional deps that are missing but needed by an installed @gjsify/*
83
- // package generate a warning but keep exit code 0 — the user can still
84
- // build/run code paths that don't touch the optional library.
85
- process.exit(missingRequired.length > 0 ? 1 : 0);
86
- },
87
- };
@@ -1,63 +0,0 @@
1
- import { createProject, discoverTemplates } from '@gjsify/create-app';
2
- import { promptTemplate } from '@gjsify/create-app/prompt';
3
- import type { Command } from '../types/index.js';
4
-
5
- interface CreateOptions {
6
- 'project-name': string;
7
- template?: string;
8
- force: boolean;
9
- install: boolean;
10
- }
11
-
12
- export const createCommand: Command<any, CreateOptions> = {
13
- command: 'create [project-name]',
14
- description: 'Scaffold a new Gjsify project in a new directory.',
15
- builder: (yargs) => {
16
- const templates = discoverTemplates();
17
- const templateChoices = templates.map((t) => t.name);
18
- return yargs
19
- .positional('project-name', {
20
- describe: 'Name of the project directory to create',
21
- type: 'string',
22
- default: 'my-gjs-app',
23
- })
24
- .option('template', {
25
- alias: 't',
26
- describe: 'Template to scaffold from',
27
- type: 'string',
28
- choices: templateChoices.length > 0 ? templateChoices : undefined,
29
- })
30
- .option('force', {
31
- alias: 'f',
32
- describe: 'Scaffold into a non-empty directory',
33
- type: 'boolean',
34
- default: false,
35
- })
36
- .option('install', {
37
- describe: 'Run npm install after scaffolding',
38
- type: 'boolean',
39
- default: false,
40
- });
41
- },
42
- handler: async (args) => {
43
- let template = args.template;
44
- if (!template) {
45
- const templates = discoverTemplates();
46
- if (!process.stdin.isTTY) {
47
- const list = templates.map((t) => t.name).join(', ');
48
- console.error(
49
- `Error: --template is required in non-interactive mode. Available templates: ${list || '(none)'}`,
50
- );
51
- process.exit(1);
52
- }
53
- const picked = await promptTemplate(templates);
54
- template = picked.name;
55
- }
56
- await createProject({
57
- projectName: args['project-name'],
58
- template,
59
- force: args.force,
60
- install: args.install,
61
- });
62
- },
63
- };
@@ -1,195 +0,0 @@
1
- // `gjsify dlx <package> [bin] [-- args...]` — runs the GJS bundle of an
2
- // npm-published package without persisting it in the user's project.
3
- //
4
- // Cardinal rule: dlx is a **GJS-bundle runner**, not a generic bin runner.
5
- // It always invokes `gjs -m <bundle>` via the existing `runGjsBundle()` util.
6
- // Packages without a GJS entry (no `gjsify.main`/`gjsify.bin`, no fallback
7
- // `main`) fail loudly.
8
- //
9
- // Cache: $XDG_CACHE_HOME/gjsify/dlx/<sha256>/ with TTL (default 7d, override
10
- // via --cache-max-age=<minutes>). Cache hit on second run skips `npm install`
11
- // entirely. Layout + atomic-swap pattern adapted from pnpm's dlx implementation
12
- // (refs/pnpm/exec/commands/src/dlx.ts).
13
-
14
- import type { Command } from '../types/index.js';
15
- import { runGjsBundle } from '../utils/run-gjs.js';
16
- import { parseSpec, type ParsedSpec } from '../utils/parse-spec.js';
17
- import { resolveGjsEntry } from '../utils/resolve-gjs-entry.js';
18
- import {
19
- cacheDirFor,
20
- createCacheKey,
21
- getValidCachedPkg,
22
- makePrepareDir,
23
- resolveInstalledPkgDir,
24
- symlinkSwap,
25
- } from '../utils/dlx-cache.js';
26
- import { installPackages } from '../utils/install-backend.js';
27
-
28
- interface DlxOptions {
29
- spec: string;
30
- binOrArg?: string;
31
- extraArgs?: string[];
32
- 'cache-max-age': number;
33
- reinstall: boolean;
34
- frozen: boolean;
35
- verbose: boolean;
36
- registry?: string;
37
- }
38
-
39
- export const dlxCommand: Command<any, DlxOptions> = {
40
- command: 'dlx <spec> [binOrArg] [extraArgs..]',
41
- description:
42
- 'Run the GJS bundle of an npm-published package without installing it locally.',
43
- builder: (yargs) =>
44
- yargs
45
- .positional('spec', {
46
- description:
47
- 'Package spec (`name`, `name@version`, `@scope/name@spec`, or local path).',
48
- type: 'string',
49
- demandOption: true,
50
- })
51
- .positional('binOrArg', {
52
- description:
53
- 'Optional bin name when the package defines `gjsify.bin` with multiple entries; otherwise treated as the first argument forwarded to the bundle.',
54
- type: 'string',
55
- })
56
- .positional('extraArgs', {
57
- description: 'Extra args forwarded to `gjs -m <bundle>`.',
58
- type: 'string',
59
- array: true,
60
- })
61
- .option('cache-max-age', {
62
- description:
63
- 'Cache TTL in minutes. Defaults to 7 days. Use 0 to bypass cache.',
64
- type: 'number',
65
- default: 60 * 24 * 7,
66
- })
67
- .option('reinstall', {
68
- description:
69
- 'Bypass the cache for this run (alias for --cache-max-age=0).',
70
- type: 'boolean',
71
- default: false,
72
- })
73
- .option('frozen', {
74
- description:
75
- 'Use the project-local gjsify-lock.json verbatim — fail if missing or stale (no resolver pass).',
76
- type: 'boolean',
77
- default: false,
78
- })
79
- .option('verbose', {
80
- description: 'Verbose logging (passes --loglevel verbose to npm).',
81
- type: 'boolean',
82
- default: false,
83
- })
84
- .option('registry', {
85
- description: 'Registry URL override.',
86
- type: 'string',
87
- }),
88
- handler: async (args) => {
89
- const parsed = parseSpec(args.spec);
90
-
91
- const cacheMaxAge = args.reinstall ? 0 : args['cache-max-age'];
92
- const { pkgDir, cachedPkgName } = await ensurePkgDir(parsed, {
93
- verbose: args.verbose,
94
- registry: args.registry,
95
- cacheMaxAge,
96
- frozen: args.frozen,
97
- });
98
-
99
- // Bin / args disambiguation:
100
- // gjsify dlx <pkg> → no bin, no args
101
- // gjsify dlx <pkg> mybin → bin if package has gjsify.bin[mybin], else arg
102
- // gjsify dlx <pkg> mybin -- arg1 arg2 → bin + extra args
103
- // gjsify dlx <pkg> -- arg1 arg2 → no bin, extra args
104
- const { binName, extraArgs } = splitBinAndArgs(
105
- pkgDir,
106
- args.binOrArg,
107
- args.extraArgs ?? [],
108
- );
109
-
110
- const entry = resolveGjsEntry(pkgDir, binName);
111
- if (entry.fromFallback) {
112
- console.warn(
113
- `[gjsify dlx] package "${cachedPkgName ?? parsed.kind}" has no \`gjsify\` field — falling back to package.json#main. Add \`gjsify.main\` to silence.`,
114
- );
115
- }
116
-
117
- await runGjsBundle(entry.bundlePath, extraArgs);
118
- },
119
- };
120
-
121
- interface EnsureOpts {
122
- verbose: boolean;
123
- registry?: string;
124
- cacheMaxAge: number;
125
- frozen: boolean;
126
- }
127
-
128
- async function ensurePkgDir(
129
- parsed: ParsedSpec,
130
- opts: EnsureOpts,
131
- ): Promise<{ pkgDir: string; cachedPkgName: string | null }> {
132
- if (parsed.kind === 'local') {
133
- return { pkgDir: parsed.path, cachedPkgName: null };
134
- }
135
-
136
- const cacheKey = createCacheKey({ packages: [parsed.spec] });
137
- const cacheDir = cacheDirFor(cacheKey);
138
-
139
- const cached = opts.cacheMaxAge > 0 ? getValidCachedPkg(cacheDir, opts.cacheMaxAge) : undefined;
140
- if (cached) {
141
- return {
142
- pkgDir: resolveInstalledPkgDir(cached, parsed.name),
143
- cachedPkgName: parsed.name,
144
- };
145
- }
146
-
147
- const prepareDir = makePrepareDir(cacheDir);
148
- await installPackages({
149
- prefix: prepareDir,
150
- specs: [parsed.spec],
151
- verbose: opts.verbose,
152
- registry: opts.registry,
153
- // Cache-prepare dirs are scoped per cache key, so writing a lockfile
154
- // there gives us reproducibility for repeated `gjsify dlx <pkg>` calls
155
- // and lets `--frozen` short-circuit the resolver entirely.
156
- lockfile: true,
157
- frozen: opts.frozen,
158
- });
159
-
160
- const liveTarget = symlinkSwap(cacheDir, prepareDir);
161
- return {
162
- pkgDir: resolveInstalledPkgDir(liveTarget, parsed.name),
163
- cachedPkgName: parsed.name,
164
- };
165
- }
166
-
167
- import { existsSync, readFileSync } from 'node:fs';
168
- import { join } from 'node:path';
169
-
170
- function splitBinAndArgs(
171
- pkgDir: string,
172
- binOrArg: string | undefined,
173
- extraArgs: string[],
174
- ): { binName: string | null; extraArgs: string[] } {
175
- if (!binOrArg) {
176
- return { binName: null, extraArgs };
177
- }
178
-
179
- const pkgJsonPath = join(pkgDir, 'package.json');
180
- if (existsSync(pkgJsonPath)) {
181
- try {
182
- const pkg = JSON.parse(readFileSync(pkgJsonPath, 'utf-8')) as {
183
- gjsify?: { bin?: Record<string, string> };
184
- };
185
- const bins = pkg.gjsify?.bin;
186
- if (bins && Object.prototype.hasOwnProperty.call(bins, binOrArg)) {
187
- return { binName: binOrArg, extraArgs };
188
- }
189
- } catch {
190
- // Fall through to treating as an arg.
191
- }
192
- }
193
- // Not a known bin — treat the positional as the first argv to the bundle.
194
- return { binName: null, extraArgs: [binOrArg, ...extraArgs] };
195
- }
@@ -1,225 +0,0 @@
1
- // `gjsify flatpak build` — wrap `flatpak-builder` with sensible defaults.
2
- //
3
- // Replaces the project-local `build-flatpak.sh`-style scripts: same flag
4
- // shape (manifest, build-dir, install, repo, bundle), plus a `--tarball`
5
- // helper for Flathub submissions.
6
-
7
- import { spawn } from 'node:child_process';
8
- import { existsSync, mkdirSync, readdirSync, readFileSync, rmSync } from 'node:fs';
9
- import { dirname, resolve } from 'node:path';
10
- import type { Command } from '../../types/index.js';
11
-
12
- interface FlatpakBuildOptions {
13
- manifest?: string;
14
- buildDir?: string;
15
- install?: boolean;
16
- repo?: string;
17
- bundle?: string;
18
- tarball?: string;
19
- forceClean?: boolean;
20
- sandbox?: boolean;
21
- deleteBuildDirs?: boolean;
22
- installDepsFrom?: string;
23
- verbose?: boolean;
24
- }
25
-
26
- export const flatpakBuildCommand: Command<unknown, FlatpakBuildOptions> = {
27
- command: 'build [manifest]',
28
- description:
29
- 'Build the Flatpak via `flatpak-builder`. Wraps a typical install + export + bundle + tarball pipeline.',
30
- builder: (yargs) => {
31
- return yargs
32
- .positional('manifest', {
33
- description: 'Path to the Flatpak manifest (default: first *.json that looks like a manifest in cwd)',
34
- type: 'string',
35
- normalize: true,
36
- })
37
- .option('build-dir', {
38
- description: 'flatpak-builder working directory',
39
- type: 'string',
40
- default: 'flatpak-build',
41
- normalize: true,
42
- })
43
- .option('install', {
44
- description: 'After build, run `flatpak-builder --user --install` to install locally',
45
- type: 'boolean',
46
- default: false,
47
- })
48
- .option('repo', {
49
- description: 'Export the build into the given OSTree repo (passes `--repo=<dir>` to flatpak-builder)',
50
- type: 'string',
51
- normalize: true,
52
- })
53
- .option('bundle', {
54
- description: 'After --repo export, build a single-file bundle (`flatpak build-bundle`) at this path',
55
- type: 'string',
56
- normalize: true,
57
- })
58
- .option('tarball', {
59
- description: 'Create a tarball of the build dir (parity with the legacy build-flatpak.sh tarball step)',
60
- type: 'string',
61
- normalize: true,
62
- })
63
- .option('force-clean', {
64
- description: 'Pass --force-clean to flatpak-builder (default true)',
65
- type: 'boolean',
66
- default: true,
67
- })
68
- .option('sandbox', {
69
- description: 'Pass --sandbox to flatpak-builder (default true)',
70
- type: 'boolean',
71
- default: true,
72
- })
73
- .option('delete-build-dirs', {
74
- description: 'Pass --delete-build-dirs to flatpak-builder (default true)',
75
- type: 'boolean',
76
- default: true,
77
- })
78
- .option('install-deps-from', {
79
- description: 'Pass --install-deps-from to flatpak-builder (e.g. `flathub`)',
80
- type: 'string',
81
- })
82
- .option('verbose', {
83
- description: 'Print the underlying flatpak-builder invocations',
84
- type: 'boolean',
85
- default: false,
86
- });
87
- },
88
- handler: async (args) => {
89
- const cwd = process.cwd();
90
- const manifest = resolve(cwd, (args.manifest as string | undefined) ?? findDefaultManifest(cwd));
91
- if (!existsSync(manifest)) {
92
- throw new Error(`gjsify flatpak build: manifest ${manifest} not found`);
93
- }
94
-
95
- const buildDir = resolve(cwd, args.buildDir ?? 'flatpak-build');
96
- const sharedFlags: string[] = [];
97
- if (args.forceClean !== false) sharedFlags.push('--force-clean');
98
- if (args.sandbox !== false) sharedFlags.push('--sandbox');
99
- if (args.deleteBuildDirs !== false) sharedFlags.push('--delete-build-dirs');
100
- if (args.installDepsFrom) sharedFlags.push(`--install-deps-from=${args.installDepsFrom}`);
101
-
102
- // Reset the build dir so re-runs don't pick up half-stale state.
103
- if (existsSync(buildDir)) rmSync(buildDir, { recursive: true, force: true });
104
-
105
- await runFlatpakBuilder([...sharedFlags, buildDir, manifest], { verbose: args.verbose });
106
-
107
- if (args.install) {
108
- await runFlatpakBuilder(
109
- ['--user', '--install', '--force-clean', buildDir, manifest],
110
- { verbose: args.verbose },
111
- );
112
- }
113
-
114
- if (args.repo) {
115
- const repoPath = resolve(cwd, args.repo);
116
- mkdirSync(dirname(repoPath), { recursive: true });
117
- await runFlatpakBuilder(
118
- [`--repo=${repoPath}`, '--force-clean', buildDir, manifest],
119
- { verbose: args.verbose },
120
- );
121
- }
122
-
123
- if (args.bundle) {
124
- if (!args.repo) {
125
- throw new Error(
126
- 'gjsify flatpak build: --bundle requires --repo (the bundle is built from the OSTree repo).',
127
- );
128
- }
129
- const bundlePath = resolve(cwd, args.bundle);
130
- mkdirSync(dirname(bundlePath), { recursive: true });
131
- const repoPath = resolve(cwd, args.repo);
132
- const appId = readManifestAppId(manifest);
133
- await runFlatpak(
134
- ['build-bundle', repoPath, bundlePath, appId],
135
- { verbose: args.verbose },
136
- );
137
- }
138
-
139
- if (args.tarball) {
140
- const tarballPath = resolve(cwd, args.tarball);
141
- mkdirSync(dirname(tarballPath), { recursive: true });
142
- await runTar(['-czf', tarballPath, '-C', buildDir, '.'], { verbose: args.verbose });
143
- }
144
-
145
- console.log(`[gjsify flatpak build] done (${buildDir})`);
146
- },
147
- };
148
-
149
- /** Pick the first JSON file in cwd that looks like a Flatpak manifest. */
150
- function findDefaultManifest(cwd: string): string {
151
- return scanForManifest(cwd) ?? 'flatpak.json';
152
- }
153
-
154
- function scanForManifest(cwd: string): string | undefined {
155
- let entries: string[] = [];
156
- try {
157
- entries = readdirSync(cwd);
158
- } catch {
159
- return undefined;
160
- }
161
- for (const name of entries) {
162
- if (!name.endsWith('.json')) continue;
163
- if (name === 'package.json' || name === 'tsconfig.json' || name.startsWith('.')) continue;
164
- try {
165
- const json = JSON.parse(readFileSync(resolve(cwd, name), 'utf-8')) as Record<string, unknown>;
166
- if (typeof json.id === 'string' && typeof json.runtime === 'string' && Array.isArray(json.modules)) {
167
- return name;
168
- }
169
- } catch {
170
- // Not JSON or unreadable — skip.
171
- }
172
- }
173
- return undefined;
174
- }
175
-
176
- function readManifestAppId(manifest: string): string {
177
- const raw = readFileSync(manifest, 'utf-8');
178
- const json = JSON.parse(raw) as { id?: unknown };
179
- if (typeof json.id !== 'string') {
180
- throw new Error(`gjsify flatpak build: ${manifest} has no string "id" field`);
181
- }
182
- return json.id;
183
- }
184
-
185
- async function runFlatpakBuilder(args: string[], opts: { verbose?: boolean }) {
186
- return runProc('flatpak-builder', args, opts, {
187
- notFoundHint:
188
- 'flatpak-builder not found. Install via your distro (Fedora: `sudo dnf install flatpak-builder`).',
189
- });
190
- }
191
-
192
- async function runFlatpak(args: string[], opts: { verbose?: boolean }) {
193
- return runProc('flatpak', args, opts, {
194
- notFoundHint: 'flatpak not found. Install via your distro and add Flathub: see https://flathub.org/setup.',
195
- });
196
- }
197
-
198
- async function runTar(args: string[], opts: { verbose?: boolean }) {
199
- return runProc('tar', args, opts, { notFoundHint: 'tar not found.' });
200
- }
201
-
202
- function runProc(
203
- cmd: string,
204
- args: string[],
205
- opts: { verbose?: boolean },
206
- extra: { notFoundHint: string },
207
- ): Promise<void> {
208
- if (opts.verbose) {
209
- console.log(`[gjsify flatpak] ${cmd} ${args.join(' ')}`);
210
- }
211
- return new Promise((res, rej) => {
212
- const child = spawn(cmd, args, { stdio: 'inherit' });
213
- child.on('error', (err: NodeJS.ErrnoException) => {
214
- if (err.code === 'ENOENT') {
215
- rej(new Error(`gjsify flatpak: ${extra.notFoundHint}`));
216
- } else {
217
- rej(err);
218
- }
219
- });
220
- child.on('exit', (code) => {
221
- if (code === 0) res();
222
- else rej(new Error(`gjsify flatpak: ${cmd} exited with status ${code}`));
223
- });
224
- });
225
- }