@gjsify/cli 0.4.9 → 0.4.11
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.
- package/dist/cli.gjs.mjs +136 -134
- package/lib/commands/generate-installer.d.ts +10 -0
- package/lib/commands/generate-installer.js +113 -0
- package/lib/commands/index.d.ts +3 -0
- package/lib/commands/index.js +3 -0
- package/lib/commands/self-update.d.ts +8 -0
- package/lib/commands/self-update.js +138 -0
- package/lib/commands/uninstall.d.ts +9 -0
- package/lib/commands/uninstall.js +145 -0
- package/lib/index.js +4 -1
- package/lib/templates/install.mjs.tmpl +248 -0
- package/lib/utils/install-global.js +13 -1
- package/package.json +17 -17
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { Command } from '../types/index.js';
|
|
2
|
+
interface GenerateInstallerOptions {
|
|
3
|
+
target?: string;
|
|
4
|
+
'bin-name'?: string;
|
|
5
|
+
'bootstrap-url'?: string;
|
|
6
|
+
output: string;
|
|
7
|
+
force: boolean;
|
|
8
|
+
}
|
|
9
|
+
export declare const generateInstallerCommand: Command<any, GenerateInstallerOptions>;
|
|
10
|
+
export {};
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
// `gjsify generate-installer [target]` — scaffold an `install.mjs` for any
|
|
2
|
+
// GJS-runnable npm package, modeled on gjsify's own installer.
|
|
3
|
+
//
|
|
4
|
+
// The generated `install.mjs` is a verbatim copy of gjsify's root `install.mjs`
|
|
5
|
+
// with three constants substituted:
|
|
6
|
+
//
|
|
7
|
+
// DEFAULT_TARGET → the consumer's npm package name
|
|
8
|
+
// DEFAULT_BIN_NAME → the consumer's bin name (key of `gjsify.bin` or `bin`)
|
|
9
|
+
// DEFAULT_BOOTSTRAP_URL → URL of a gjsify `cli.gjs.mjs` bootstrap bundle
|
|
10
|
+
//
|
|
11
|
+
// End-user workflow:
|
|
12
|
+
// cd my-gjs-app
|
|
13
|
+
// gjsify generate-installer
|
|
14
|
+
// git add install.mjs && git commit
|
|
15
|
+
// # README:
|
|
16
|
+
// # curl -fsSL https://github.com/me/my-gjs-app/raw/main/install.mjs \
|
|
17
|
+
// # -o /tmp/i.mjs && gjs -m /tmp/i.mjs && rm /tmp/i.mjs
|
|
18
|
+
//
|
|
19
|
+
// The template is read at build time (static-read-inliner inlines the file
|
|
20
|
+
// contents into the bundled CLI), so the runtime cost is just a string
|
|
21
|
+
// replace + write.
|
|
22
|
+
import { existsSync, readFileSync, writeFileSync } from 'node:fs';
|
|
23
|
+
import { resolve } from 'node:path';
|
|
24
|
+
// Lazy load. Reading at the top level breaks `gjsify run copy-templates`
|
|
25
|
+
// (the bootstrap step that ships the template into `lib/templates/` after
|
|
26
|
+
// `tsc`): the run script must first import this module to dispatch into
|
|
27
|
+
// itself, which would then ENOENT on the not-yet-copied template file.
|
|
28
|
+
// The static-read-inliner can still detect this shape inside the handler.
|
|
29
|
+
function loadInstallerTemplate() {
|
|
30
|
+
return readFileSync(new URL('../templates/install.mjs.tmpl', import.meta.url), 'utf-8');
|
|
31
|
+
}
|
|
32
|
+
const DEFAULT_BOOTSTRAP_URL = 'https://github.com/gjsify/gjsify/releases/latest/download/cli.gjs.mjs';
|
|
33
|
+
export const generateInstallerCommand = {
|
|
34
|
+
command: 'generate-installer [target]',
|
|
35
|
+
description: 'Scaffold an install.mjs in the current directory for a GJS-runnable npm package.',
|
|
36
|
+
builder: (yargs) => yargs
|
|
37
|
+
.positional('target', {
|
|
38
|
+
description: 'Npm package name to install (default: current package.json name).',
|
|
39
|
+
type: 'string',
|
|
40
|
+
})
|
|
41
|
+
.option('bin-name', {
|
|
42
|
+
description: 'Bin name produced by the installer (default: first key of `gjsify.bin` or `bin`).',
|
|
43
|
+
type: 'string',
|
|
44
|
+
})
|
|
45
|
+
.option('bootstrap-url', {
|
|
46
|
+
description: 'Override the cli.gjs.mjs bootstrap bundle URL (default: gjsify GitHub releases/latest).',
|
|
47
|
+
type: 'string',
|
|
48
|
+
})
|
|
49
|
+
.option('output', {
|
|
50
|
+
description: 'Where to write the generated installer.',
|
|
51
|
+
type: 'string',
|
|
52
|
+
default: 'install.mjs',
|
|
53
|
+
})
|
|
54
|
+
.option('force', {
|
|
55
|
+
description: 'Overwrite an existing output file.',
|
|
56
|
+
type: 'boolean',
|
|
57
|
+
default: false,
|
|
58
|
+
}),
|
|
59
|
+
handler: (args) => {
|
|
60
|
+
const outputPath = resolve(process.cwd(), args.output);
|
|
61
|
+
if (existsSync(outputPath) && !args.force) {
|
|
62
|
+
console.error(`${args.output} already exists. Re-run with --force to overwrite.`);
|
|
63
|
+
process.exit(1);
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
const pkgJsonPath = resolve(process.cwd(), 'package.json');
|
|
67
|
+
let pkgJson = null;
|
|
68
|
+
if (existsSync(pkgJsonPath)) {
|
|
69
|
+
try {
|
|
70
|
+
pkgJson = JSON.parse(readFileSync(pkgJsonPath, 'utf-8'));
|
|
71
|
+
}
|
|
72
|
+
catch {
|
|
73
|
+
/* no pkg.json or unparsable — fall back to flags */
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
const target = args.target ?? pkgJson?.name;
|
|
77
|
+
if (!target) {
|
|
78
|
+
console.error('No target package: pass `gjsify generate-installer <pkg>` or run inside a directory with a package.json.');
|
|
79
|
+
process.exit(1);
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
const binName = args['bin-name'] ?? pickDefaultBinName(pkgJson, target);
|
|
83
|
+
const bootstrapUrl = args['bootstrap-url'] ?? DEFAULT_BOOTSTRAP_URL;
|
|
84
|
+
const rendered = loadInstallerTemplate()
|
|
85
|
+
.replace(/const DEFAULT_TARGET = '[^']+';/, `const DEFAULT_TARGET = ${JSON.stringify(target)};`)
|
|
86
|
+
.replace(/const DEFAULT_BIN_NAME = '[^']+';/, `const DEFAULT_BIN_NAME = ${JSON.stringify(binName)};`)
|
|
87
|
+
.replace(/const DEFAULT_BOOTSTRAP_URL =\s*'[^']+';/, `const DEFAULT_BOOTSTRAP_URL = ${JSON.stringify(bootstrapUrl)};`);
|
|
88
|
+
writeFileSync(outputPath, rendered, { mode: 0o755 });
|
|
89
|
+
console.log(`Wrote ${args.output} (target=${target}, bin=${binName}).`);
|
|
90
|
+
console.log('');
|
|
91
|
+
console.log('Install one-liner for your README:');
|
|
92
|
+
console.log(` curl -fsSL https://github.com/<you>/<repo>/raw/main/${args.output} -o /tmp/i.mjs \\`);
|
|
93
|
+
console.log(' && gjs -m /tmp/i.mjs && rm /tmp/i.mjs');
|
|
94
|
+
},
|
|
95
|
+
};
|
|
96
|
+
function pickDefaultBinName(pkgJson, target) {
|
|
97
|
+
const gjsifyBin = pkgJson?.gjsify?.bin;
|
|
98
|
+
if (gjsifyBin && typeof gjsifyBin === 'object') {
|
|
99
|
+
const first = Object.keys(gjsifyBin)[0];
|
|
100
|
+
if (first)
|
|
101
|
+
return first;
|
|
102
|
+
}
|
|
103
|
+
const npmBin = pkgJson?.bin;
|
|
104
|
+
if (npmBin && typeof npmBin === 'object') {
|
|
105
|
+
const first = Object.keys(npmBin)[0];
|
|
106
|
+
if (first)
|
|
107
|
+
return first;
|
|
108
|
+
}
|
|
109
|
+
if (typeof npmBin === 'string') {
|
|
110
|
+
return target.startsWith('@') ? target.slice(target.indexOf('/') + 1) : target;
|
|
111
|
+
}
|
|
112
|
+
return target.startsWith('@') ? target.slice(target.indexOf('/') + 1) : target;
|
|
113
|
+
}
|
package/lib/commands/index.d.ts
CHANGED
package/lib/commands/index.js
CHANGED
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
// `gjsify self-update` — refresh the installed @gjsify/cli to a newer release.
|
|
2
|
+
//
|
|
3
|
+
// Walks `import.meta.url` to find this CLI's own package.json (works whether
|
|
4
|
+
// running from `lib/index.js` under Node or the published `dist/cli.gjs.mjs`
|
|
5
|
+
// bundle under GJS). Compares against the latest version on the npm registry
|
|
6
|
+
// (or the requested `--tag`); when an upgrade is needed, re-uses the existing
|
|
7
|
+
// `installPackages` + `linkGlobalBins` pipeline to lay down the new tree at
|
|
8
|
+
// the user-global XDG location.
|
|
9
|
+
//
|
|
10
|
+
// Limitation: only works when the current CLI is installed under
|
|
11
|
+
// `defaultGlobalLayout().prefix` (i.e. via `gjsify install -g` or via the
|
|
12
|
+
// `install.mjs` bootstrap). Installs from `npm install -g @gjsify/cli` land
|
|
13
|
+
// elsewhere and we don't try to chase them — we print a warning and exit.
|
|
14
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
15
|
+
import { dirname, join, resolve } from 'node:path';
|
|
16
|
+
import { fileURLToPath } from 'node:url';
|
|
17
|
+
import { fetchPackument } from '@gjsify/npm-registry';
|
|
18
|
+
import { installPackages } from '../utils/install-backend.js';
|
|
19
|
+
import { defaultGlobalLayout, linkGlobalBins } from '../utils/install-global.js';
|
|
20
|
+
const PACKAGE_NAME = '@gjsify/cli';
|
|
21
|
+
export const selfUpdateCommand = {
|
|
22
|
+
command: 'self-update',
|
|
23
|
+
description: `Update the installed ${PACKAGE_NAME} to the latest release (or pinned --tag).`,
|
|
24
|
+
builder: (yargs) => yargs
|
|
25
|
+
.option('check', {
|
|
26
|
+
description: 'Only check whether a newer version is available; do not install.',
|
|
27
|
+
type: 'boolean',
|
|
28
|
+
default: false,
|
|
29
|
+
})
|
|
30
|
+
.option('force', {
|
|
31
|
+
description: 'Reinstall even when the current version already matches the target tag.',
|
|
32
|
+
type: 'boolean',
|
|
33
|
+
default: false,
|
|
34
|
+
})
|
|
35
|
+
.option('tag', {
|
|
36
|
+
description: 'npm dist-tag or pinned version to install (e.g. `latest`, `next`, `0.5.0`).',
|
|
37
|
+
type: 'string',
|
|
38
|
+
default: 'latest',
|
|
39
|
+
}),
|
|
40
|
+
handler: async (args) => {
|
|
41
|
+
const layout = defaultGlobalLayout();
|
|
42
|
+
const installedPkgDir = join(layout.prefix, 'node_modules', PACKAGE_NAME);
|
|
43
|
+
const installedPkgJson = join(installedPkgDir, 'package.json');
|
|
44
|
+
const currentVersion = readCurrentVersion();
|
|
45
|
+
const installedAtPrefix = existsSync(installedPkgJson);
|
|
46
|
+
console.log(`Current ${PACKAGE_NAME}: v${currentVersion ?? '(unknown)'}`);
|
|
47
|
+
if (!installedAtPrefix) {
|
|
48
|
+
console.warn(`\nWarning: no @gjsify/cli install found under ${layout.prefix}.\n` +
|
|
49
|
+
`self-update only manages installs created by install.mjs or \`gjsify install -g\`.\n` +
|
|
50
|
+
`If you installed via \`npm install -g\`, remove that and use:\n` +
|
|
51
|
+
` curl -fsSL https://github.com/gjsify/gjsify/releases/latest/download/install.mjs -o /tmp/g.mjs && gjs -m /tmp/g.mjs && rm /tmp/g.mjs`);
|
|
52
|
+
}
|
|
53
|
+
console.log(`Fetching dist-tags for ${PACKAGE_NAME}@${args.tag} ...`);
|
|
54
|
+
let packument;
|
|
55
|
+
try {
|
|
56
|
+
packument = await fetchPackument(PACKAGE_NAME);
|
|
57
|
+
}
|
|
58
|
+
catch (err) {
|
|
59
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
60
|
+
console.error(`Failed to fetch packument: ${msg}`);
|
|
61
|
+
process.exit(1);
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
const target = resolveTag(packument, args.tag);
|
|
65
|
+
if (!target) {
|
|
66
|
+
console.error(`Unknown dist-tag '${args.tag}' on ${PACKAGE_NAME}. ` +
|
|
67
|
+
`Known tags: ${Object.keys(packument['dist-tags'] ?? {}).join(', ') || '(none)'}`);
|
|
68
|
+
process.exit(1);
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
console.log(`Latest matching --tag ${args.tag}: v${target}`);
|
|
72
|
+
if (currentVersion === target && !args.force) {
|
|
73
|
+
console.log(`Already up to date (v${target}).`);
|
|
74
|
+
if (!args.check)
|
|
75
|
+
console.log(`Run with --force to reinstall anyway.`);
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
if (args.check) {
|
|
79
|
+
console.log(currentVersion
|
|
80
|
+
? `Update available: v${currentVersion} → v${target}`
|
|
81
|
+
: `Install required: → v${target}`);
|
|
82
|
+
process.exit(1);
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
console.log(`Installing ${PACKAGE_NAME}@${target} ...`);
|
|
86
|
+
await installPackages({
|
|
87
|
+
prefix: layout.prefix,
|
|
88
|
+
specs: [`${PACKAGE_NAME}@${target}`],
|
|
89
|
+
verbose: false,
|
|
90
|
+
});
|
|
91
|
+
const linked = linkGlobalBins([PACKAGE_NAME], layout);
|
|
92
|
+
if (linked.length === 0) {
|
|
93
|
+
console.warn('self-update: install completed but no bins were linked — package.json may be missing a `bin` field.');
|
|
94
|
+
}
|
|
95
|
+
else {
|
|
96
|
+
for (const bin of linked) {
|
|
97
|
+
console.log(` • ${bin.link} → ${bin.target}`);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
console.log(`\nUpdated ${PACKAGE_NAME} to v${target}.`);
|
|
101
|
+
},
|
|
102
|
+
};
|
|
103
|
+
/**
|
|
104
|
+
* Resolve the CLI's own `package.json#version`. Walks up from
|
|
105
|
+
* `import.meta.url` (the bundle file under GJS, or `lib/index.js` under
|
|
106
|
+
* Node) until it finds a package.json with `name === '@gjsify/cli'`.
|
|
107
|
+
*/
|
|
108
|
+
function readCurrentVersion() {
|
|
109
|
+
try {
|
|
110
|
+
const here = fileURLToPath(import.meta.url);
|
|
111
|
+
let dir = dirname(resolve(here));
|
|
112
|
+
for (let i = 0; i < 8 && dir !== dirname(dir); i++) {
|
|
113
|
+
const candidate = join(dir, 'package.json');
|
|
114
|
+
if (existsSync(candidate)) {
|
|
115
|
+
const pkg = JSON.parse(readFileSync(candidate, 'utf-8'));
|
|
116
|
+
if (pkg.name === PACKAGE_NAME && typeof pkg.version === 'string') {
|
|
117
|
+
return pkg.version;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
dir = dirname(dir);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
catch {
|
|
124
|
+
/* not in a recognizable layout */
|
|
125
|
+
}
|
|
126
|
+
return null;
|
|
127
|
+
}
|
|
128
|
+
function resolveTag(packument, tag) {
|
|
129
|
+
const distTags = (packument['dist-tags'] ?? {});
|
|
130
|
+
if (distTags[tag])
|
|
131
|
+
return distTags[tag];
|
|
132
|
+
// Allow pinned versions via `--tag 0.5.0`
|
|
133
|
+
if (packument.versions && typeof packument.versions === 'object') {
|
|
134
|
+
if (packument.versions[tag])
|
|
135
|
+
return tag;
|
|
136
|
+
}
|
|
137
|
+
return null;
|
|
138
|
+
}
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
// `gjsify uninstall -g <pkg>` — symmetric inverse of `install -g`.
|
|
2
|
+
//
|
|
3
|
+
// Removes the installed package tree from the user-global XDG location
|
|
4
|
+
// and any bin shims under `~/.local/bin/` that point into it. Mirrors
|
|
5
|
+
// the layout decisions in install-global.ts:
|
|
6
|
+
//
|
|
7
|
+
// ~/.local/share/gjsify/global/node_modules/<pkg>/ ← deleted
|
|
8
|
+
// ~/.local/bin/<bin> ← deleted iff it
|
|
9
|
+
// execs a path
|
|
10
|
+
// inside the
|
|
11
|
+
// removed tree
|
|
12
|
+
//
|
|
13
|
+
// Scope: --global only. Project-local uninstall (mirror of `npm uninstall
|
|
14
|
+
// <pkg>` without -g) is a separate workstream — it needs to rewrite
|
|
15
|
+
// package.json + refresh the lockfile, which install -g doesn't touch.
|
|
16
|
+
import { existsSync, readFileSync, readdirSync, rmSync, statSync, unlinkSync } from 'node:fs';
|
|
17
|
+
import { join } from 'node:path';
|
|
18
|
+
import { defaultGlobalLayout, specToPackageName } from '../utils/install-global.js';
|
|
19
|
+
export const uninstallCommand = {
|
|
20
|
+
command: 'uninstall <packages..>',
|
|
21
|
+
description: 'Uninstall a previously installed package. Currently only `--global` mode is supported.',
|
|
22
|
+
builder: (yargs) => yargs
|
|
23
|
+
.positional('packages', {
|
|
24
|
+
description: 'Package(s) to uninstall (npm names, optionally with version).',
|
|
25
|
+
type: 'string',
|
|
26
|
+
array: true,
|
|
27
|
+
demandOption: true,
|
|
28
|
+
})
|
|
29
|
+
.option('global', {
|
|
30
|
+
description: 'Uninstall from the user-global XDG location (the install -g target).',
|
|
31
|
+
type: 'boolean',
|
|
32
|
+
alias: 'g',
|
|
33
|
+
default: false,
|
|
34
|
+
})
|
|
35
|
+
.option('dry-run', {
|
|
36
|
+
description: 'Show what would be removed without touching the filesystem.',
|
|
37
|
+
type: 'boolean',
|
|
38
|
+
default: false,
|
|
39
|
+
})
|
|
40
|
+
.option('verbose', {
|
|
41
|
+
description: 'Verbose logging.',
|
|
42
|
+
type: 'boolean',
|
|
43
|
+
default: false,
|
|
44
|
+
}),
|
|
45
|
+
handler: (args) => {
|
|
46
|
+
if (!args.global) {
|
|
47
|
+
console.error('gjsify uninstall currently only supports --global. ' +
|
|
48
|
+
'For project-local removal, edit package.json + re-run `gjsify install`.');
|
|
49
|
+
process.exit(1);
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
const layout = defaultGlobalLayout();
|
|
53
|
+
const dryRun = args['dry-run'] ?? false;
|
|
54
|
+
const verbose = args.verbose ?? false;
|
|
55
|
+
const prefix = `gjsify uninstall${dryRun ? ' (dry-run)' : ''} --global`;
|
|
56
|
+
console.log(`${prefix} ← ${layout.prefix}`);
|
|
57
|
+
console.log(`${' '.repeat(prefix.length)} bins ← ${layout.binDir}`);
|
|
58
|
+
let removedAny = false;
|
|
59
|
+
for (const spec of args.packages) {
|
|
60
|
+
const pkgName = specToPackageName(spec);
|
|
61
|
+
const pkgDir = join(layout.prefix, 'node_modules', pkgName);
|
|
62
|
+
if (!existsSync(pkgDir)) {
|
|
63
|
+
console.warn(` ✗ ${pkgName} — not installed at ${pkgDir}`);
|
|
64
|
+
continue;
|
|
65
|
+
}
|
|
66
|
+
// Find bin shims that exec into this package's tree. The shims
|
|
67
|
+
// are POSIX sh launchers written by linkGlobalBins; we identify
|
|
68
|
+
// candidates by reading the launcher script and matching the
|
|
69
|
+
// absolute path.
|
|
70
|
+
const binsToRemove = findBinShimsForPackage(layout.binDir, pkgDir, verbose);
|
|
71
|
+
if (dryRun) {
|
|
72
|
+
console.log(` • would remove ${pkgDir}`);
|
|
73
|
+
for (const bin of binsToRemove) {
|
|
74
|
+
console.log(` • would remove ${bin}`);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
else {
|
|
78
|
+
rmSync(pkgDir, { recursive: true, force: true });
|
|
79
|
+
console.log(` • removed ${pkgDir}`);
|
|
80
|
+
for (const bin of binsToRemove) {
|
|
81
|
+
unlinkSync(bin);
|
|
82
|
+
console.log(` • removed ${bin}`);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
removedAny = true;
|
|
86
|
+
}
|
|
87
|
+
if (!removedAny) {
|
|
88
|
+
console.error('\nNo packages removed.');
|
|
89
|
+
process.exit(1);
|
|
90
|
+
}
|
|
91
|
+
},
|
|
92
|
+
};
|
|
93
|
+
/**
|
|
94
|
+
* Scan `binDir` for POSIX `sh` launchers whose `exec` target points into
|
|
95
|
+
* `pkgDir`. The launcher shape is fixed by `linkGlobalBins` — either:
|
|
96
|
+
*
|
|
97
|
+
* #!/bin/sh
|
|
98
|
+
* exec '<absolute-path>' "$@"
|
|
99
|
+
*
|
|
100
|
+
* or (for `.gjs.mjs` / `.mjs` targets):
|
|
101
|
+
*
|
|
102
|
+
* #!/bin/sh
|
|
103
|
+
* exec gjs -m '<absolute-path>' "$@"
|
|
104
|
+
*
|
|
105
|
+
* We parse the absolute path out of the single-quoted segment and check
|
|
106
|
+
* whether it's under `pkgDir`. Non-shim files (e.g. unrelated binaries
|
|
107
|
+
* the user installed via `npm install -g`) are skipped silently.
|
|
108
|
+
*/
|
|
109
|
+
function findBinShimsForPackage(binDir, pkgDir, verbose) {
|
|
110
|
+
if (!existsSync(binDir))
|
|
111
|
+
return [];
|
|
112
|
+
const matches = [];
|
|
113
|
+
let entries;
|
|
114
|
+
try {
|
|
115
|
+
entries = readdirSync(binDir);
|
|
116
|
+
}
|
|
117
|
+
catch {
|
|
118
|
+
return [];
|
|
119
|
+
}
|
|
120
|
+
for (const name of entries) {
|
|
121
|
+
const fullPath = join(binDir, name);
|
|
122
|
+
try {
|
|
123
|
+
const st = statSync(fullPath);
|
|
124
|
+
if (!st.isFile())
|
|
125
|
+
continue;
|
|
126
|
+
const content = readFileSync(fullPath, 'utf-8');
|
|
127
|
+
if (!content.startsWith('#!/bin/sh'))
|
|
128
|
+
continue;
|
|
129
|
+
// Match the first single-quoted absolute path.
|
|
130
|
+
const m = content.match(/'([^']+)'/);
|
|
131
|
+
if (!m)
|
|
132
|
+
continue;
|
|
133
|
+
const target = m[1];
|
|
134
|
+
if (target.startsWith(pkgDir + '/') || target === pkgDir) {
|
|
135
|
+
matches.push(fullPath);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
catch (err) {
|
|
139
|
+
if (verbose) {
|
|
140
|
+
console.warn(` ? could not inspect ${fullPath}: ${err.message}`);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
return matches;
|
|
145
|
+
}
|
package/lib/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import yargs from 'yargs';
|
|
3
3
|
import { hideBin } from 'yargs/helpers';
|
|
4
|
-
import { buildCommand as build, runCommand as run, infoCommand as info, checkCommand as check, showcaseCommand as showcase, createCommand as create, gresourceCommand as gresource, gettextCommand as gettext, gsettingsCommand as gsettings, flatpakCommand as flatpak, dlxCommand as dlx, installCommand as install, foreachCommand as foreach, workspaceCommand as workspace, packCommand as pack, publishCommand as publish, } from './commands/index.js';
|
|
4
|
+
import { buildCommand as build, runCommand as run, infoCommand as info, checkCommand as check, showcaseCommand as showcase, createCommand as create, gresourceCommand as gresource, gettextCommand as gettext, gsettingsCommand as gsettings, flatpakCommand as flatpak, dlxCommand as dlx, installCommand as install, foreachCommand as foreach, workspaceCommand as workspace, packCommand as pack, publishCommand as publish, selfUpdateCommand as selfUpdate, generateInstallerCommand as generateInstaller, uninstallCommand as uninstall, } from './commands/index.js';
|
|
5
5
|
import { APP_NAME } from './constants.js';
|
|
6
6
|
// `parseAsync()` instead of `.argv` so the top-level await keeps the
|
|
7
7
|
// process alive until command handlers complete. Under Node this is
|
|
@@ -27,6 +27,9 @@ await yargs(hideBin(process.argv))
|
|
|
27
27
|
.command(workspace.command, workspace.description, workspace.builder, workspace.handler)
|
|
28
28
|
.command(pack.command, pack.description, pack.builder, pack.handler)
|
|
29
29
|
.command(publish.command, publish.description, publish.builder, publish.handler)
|
|
30
|
+
.command(selfUpdate.command, selfUpdate.description, selfUpdate.builder, selfUpdate.handler)
|
|
31
|
+
.command(generateInstaller.command, generateInstaller.description, generateInstaller.builder, generateInstaller.handler)
|
|
32
|
+
.command(uninstall.command, uninstall.description, uninstall.builder, uninstall.handler)
|
|
30
33
|
.demandCommand(1)
|
|
31
34
|
.help()
|
|
32
35
|
.parseAsync();
|