@gjsify/cli 0.4.27 → 0.4.29
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 +34 -32
- package/lib/actions/barrels-generate.js +1 -5
- package/lib/actions/build.d.ts +3 -3
- package/lib/actions/build.js +56 -64
- package/lib/bundler-pick.d.ts +3 -3
- package/lib/bundler-pick.js +5 -6
- package/lib/commands/build.js +37 -31
- package/lib/commands/check.js +3 -3
- package/lib/commands/fix.js +33 -23
- package/lib/commands/flatpak/build.js +6 -2
- package/lib/commands/flatpak/check.js +9 -3
- package/lib/commands/flatpak/ci.js +1 -2
- package/lib/commands/flatpak/deps.js +1 -2
- package/lib/commands/flatpak/diff.js +2 -6
- package/lib/commands/flatpak/init.js +19 -19
- package/lib/commands/flatpak/release.js +2 -2
- package/lib/commands/flatpak/scaffold.js +3 -11
- package/lib/commands/flatpak/sync-flathub.js +4 -8
- package/lib/commands/flatpak/utils.js +1 -6
- package/lib/commands/foreach.js +5 -14
- package/lib/commands/format.js +54 -41
- package/lib/commands/gettext.js +2 -10
- package/lib/commands/gresource.js +2 -8
- package/lib/commands/gsettings.js +1 -3
- package/lib/commands/install.js +13 -6
- package/lib/commands/lint.d.ts +1 -1
- package/lib/commands/lint.js +22 -22
- package/lib/commands/pack.js +29 -17
- package/lib/commands/publish.d.ts +1 -0
- package/lib/commands/publish.js +113 -21
- package/lib/commands/run.js +2 -6
- package/lib/commands/self-update.js +36 -8
- package/lib/commands/showcase.js +1 -1
- package/lib/commands/system-check.js +8 -11
- package/lib/commands/test.js +12 -8
- package/lib/commands/uninstall.js +1 -3
- package/lib/commands/upgrade.d.ts +1 -1
- package/lib/commands/upgrade.js +109 -120
- package/lib/commands/workspace.js +1 -3
- package/lib/config.js +18 -13
- package/lib/index.js +21 -0
- package/lib/templates/install.mjs.tmpl +20 -14
- package/lib/templates/oxfmtrc.tmpl +54 -0
- package/lib/templates/oxlintrc.json.tmpl +35 -0
- package/lib/types/config-data.d.ts +23 -13
- package/lib/utils/check-system-deps.js +10 -4
- package/lib/utils/detect-native-packages.js +1 -1
- package/lib/utils/dlx-cache.js +2 -7
- package/lib/utils/install-backend-native.d.ts +2 -2
- package/lib/utils/install-backend-native.js +72 -63
- package/lib/utils/install-backend.d.ts +13 -0
- package/lib/utils/install-backend.js +2 -1
- package/lib/utils/install-global.js +1 -3
- package/lib/utils/normalize-bundler-options.js +52 -17
- package/lib/utils/oxc-resolve.d.ts +63 -0
- package/lib/utils/oxc-resolve.js +264 -0
- package/lib/utils/pkg-json-edit.js +1 -6
- package/lib/utils/run-gjs.js +1 -4
- package/lib/utils/run-lifecycle-script.js +3 -7
- package/lib/utils/workspace-root.js +3 -1
- package/package.json +17 -17
- package/lib/templates/biome.json.tmpl +0 -79
- package/lib/utils/biome-resolve.d.ts +0 -47
- package/lib/utils/biome-resolve.js +0 -204
package/lib/commands/upgrade.js
CHANGED
|
@@ -12,104 +12,104 @@
|
|
|
12
12
|
// are skipped — those are the gjsify monorepo internal links and `gjsify
|
|
13
13
|
// install` resolves them locally. Only external npm specs get checked
|
|
14
14
|
// against the registry.
|
|
15
|
-
import { readFileSync, writeFileSync, existsSync } from
|
|
16
|
-
import { join, resolve } from
|
|
17
|
-
import { homedir } from
|
|
18
|
-
import { createInterface } from
|
|
19
|
-
import { parse } from
|
|
20
|
-
import { DEFAULT_REGISTRY, fetchPackument, parseNpmrc
|
|
15
|
+
import { readFileSync, writeFileSync, existsSync } from 'node:fs';
|
|
16
|
+
import { join, resolve } from 'node:path';
|
|
17
|
+
import { homedir } from 'node:os';
|
|
18
|
+
import { createInterface } from 'node:readline/promises';
|
|
19
|
+
import { parse } from '@gjsify/semver';
|
|
20
|
+
import { DEFAULT_REGISTRY, fetchPackument, parseNpmrc } from '@gjsify/npm-registry';
|
|
21
21
|
export const upgradeCommand = {
|
|
22
|
-
command:
|
|
23
|
-
description:
|
|
22
|
+
command: 'upgrade',
|
|
23
|
+
description: 'Check the npm registry for newer versions of declared dependencies and update package.json. Interactive by default; `--latest` / `--minor` / `--patch` switch to non-interactive bulk-update mode.',
|
|
24
24
|
builder: (yargs) => {
|
|
25
25
|
return yargs
|
|
26
|
-
.option(
|
|
27
|
-
description:
|
|
28
|
-
type:
|
|
26
|
+
.option('latest', {
|
|
27
|
+
description: 'Non-interactive: bump every dependency to its latest version (allows major).',
|
|
28
|
+
type: 'boolean',
|
|
29
29
|
default: false,
|
|
30
30
|
})
|
|
31
|
-
.option(
|
|
32
|
-
description:
|
|
33
|
-
type:
|
|
31
|
+
.option('minor', {
|
|
32
|
+
description: 'Non-interactive: bump every dependency to the latest within the same major (semver-minor + semver-patch).',
|
|
33
|
+
type: 'boolean',
|
|
34
34
|
default: false,
|
|
35
35
|
})
|
|
36
|
-
.option(
|
|
37
|
-
description:
|
|
38
|
-
type:
|
|
36
|
+
.option('patch', {
|
|
37
|
+
description: 'Non-interactive: bump every dependency to the latest within the same minor (semver-patch only).',
|
|
38
|
+
type: 'boolean',
|
|
39
39
|
default: false,
|
|
40
40
|
})
|
|
41
|
-
.option(
|
|
42
|
-
description:
|
|
43
|
-
type:
|
|
41
|
+
.option('filter', {
|
|
42
|
+
description: 'Only consider packages whose name matches this substring (case-insensitive). Repeatable; comma-separated values are split.',
|
|
43
|
+
type: 'string',
|
|
44
44
|
})
|
|
45
|
-
.option(
|
|
46
|
-
description:
|
|
47
|
-
type:
|
|
45
|
+
.option('dry-run', {
|
|
46
|
+
description: 'Print the upgrade plan without writing package.json.',
|
|
47
|
+
type: 'boolean',
|
|
48
48
|
default: false,
|
|
49
49
|
})
|
|
50
|
-
.option(
|
|
51
|
-
description:
|
|
52
|
-
type:
|
|
50
|
+
.option('cwd', {
|
|
51
|
+
description: 'Project directory. Default: process.cwd().',
|
|
52
|
+
type: 'string',
|
|
53
53
|
})
|
|
54
|
-
.option(
|
|
55
|
-
alias:
|
|
56
|
-
description:
|
|
57
|
-
type:
|
|
54
|
+
.option('yes', {
|
|
55
|
+
alias: 'y',
|
|
56
|
+
description: 'Interactive mode: select all without prompting.',
|
|
57
|
+
type: 'boolean',
|
|
58
58
|
default: false,
|
|
59
59
|
})
|
|
60
|
-
.option(
|
|
61
|
-
description:
|
|
62
|
-
type:
|
|
60
|
+
.option('verbose', {
|
|
61
|
+
description: 'Print extra resolution details.',
|
|
62
|
+
type: 'boolean',
|
|
63
63
|
default: false,
|
|
64
64
|
});
|
|
65
65
|
},
|
|
66
66
|
handler: async (args) => {
|
|
67
67
|
const cwd = resolve(args.cwd ?? process.cwd());
|
|
68
|
-
const pkgJsonPath = join(cwd,
|
|
68
|
+
const pkgJsonPath = join(cwd, 'package.json');
|
|
69
69
|
if (!existsSync(pkgJsonPath)) {
|
|
70
70
|
throw new Error(`[gjsify upgrade] no package.json at ${pkgJsonPath}`);
|
|
71
71
|
}
|
|
72
|
-
const rawPkg = readFileSync(pkgJsonPath,
|
|
72
|
+
const rawPkg = readFileSync(pkgJsonPath, 'utf-8');
|
|
73
73
|
const pkg = JSON.parse(rawPkg);
|
|
74
74
|
const filters = args.filter
|
|
75
75
|
? args.filter
|
|
76
|
-
.split(
|
|
76
|
+
.split(',')
|
|
77
77
|
.map((s) => s.trim().toLowerCase())
|
|
78
78
|
.filter(Boolean)
|
|
79
79
|
: [];
|
|
80
80
|
const entries = collectExternalDeps(pkg, filters);
|
|
81
81
|
if (entries.length === 0) {
|
|
82
|
-
console.log(
|
|
82
|
+
console.log('[gjsify upgrade] no external npm dependencies to check.');
|
|
83
83
|
return;
|
|
84
84
|
}
|
|
85
85
|
const npmrc = await loadNpmrcLight(cwd);
|
|
86
86
|
const mode = args.latest
|
|
87
|
-
?
|
|
87
|
+
? 'latest'
|
|
88
88
|
: args.minor
|
|
89
|
-
?
|
|
89
|
+
? 'minor'
|
|
90
90
|
: args.patch
|
|
91
|
-
?
|
|
92
|
-
:
|
|
91
|
+
? 'patch'
|
|
92
|
+
: 'interactive';
|
|
93
93
|
console.log(`[gjsify upgrade] checking ${entries.length} dependencies against ${npmrc.registry}…`);
|
|
94
94
|
const candidates = await resolveCandidates(entries, npmrc, args.verbose ?? false, mode);
|
|
95
95
|
if (candidates.length === 0) {
|
|
96
|
-
console.log(
|
|
96
|
+
console.log('✅ all dependencies are up to date');
|
|
97
97
|
return;
|
|
98
98
|
}
|
|
99
99
|
printTable(candidates);
|
|
100
100
|
let selected;
|
|
101
|
-
if (mode ===
|
|
101
|
+
if (mode === 'interactive' && !args.yes) {
|
|
102
102
|
selected = await promptSelection(candidates);
|
|
103
103
|
}
|
|
104
|
-
else if (args.yes && mode ===
|
|
105
|
-
console.log(
|
|
104
|
+
else if (args.yes && mode === 'interactive') {
|
|
105
|
+
console.log('[gjsify upgrade] -y / --yes: selecting all');
|
|
106
106
|
selected = candidates;
|
|
107
107
|
}
|
|
108
108
|
else {
|
|
109
109
|
selected = candidates;
|
|
110
110
|
}
|
|
111
111
|
if (selected.length === 0) {
|
|
112
|
-
console.log(
|
|
112
|
+
console.log('[gjsify upgrade] nothing selected; package.json unchanged.');
|
|
113
113
|
return;
|
|
114
114
|
}
|
|
115
115
|
if (args.dryRun) {
|
|
@@ -121,34 +121,29 @@ export const upgradeCommand = {
|
|
|
121
121
|
},
|
|
122
122
|
};
|
|
123
123
|
// ─── Resolution ─────────────────────────────────────────────────────────
|
|
124
|
-
const DEP_FIELDS = [
|
|
125
|
-
"dependencies",
|
|
126
|
-
"devDependencies",
|
|
127
|
-
"optionalDependencies",
|
|
128
|
-
"peerDependencies",
|
|
129
|
-
];
|
|
124
|
+
const DEP_FIELDS = ['dependencies', 'devDependencies', 'optionalDependencies', 'peerDependencies'];
|
|
130
125
|
function collectExternalDeps(pkg, filters) {
|
|
131
126
|
const out = [];
|
|
132
127
|
for (const field of DEP_FIELDS) {
|
|
133
128
|
const map = pkg[field];
|
|
134
|
-
if (!map || typeof map !==
|
|
129
|
+
if (!map || typeof map !== 'object')
|
|
135
130
|
continue;
|
|
136
131
|
for (const [name, raw] of Object.entries(map)) {
|
|
137
|
-
if (typeof raw !==
|
|
132
|
+
if (typeof raw !== 'string')
|
|
138
133
|
continue;
|
|
139
134
|
if (filters.length && !filters.some((f) => name.toLowerCase().includes(f))) {
|
|
140
135
|
continue;
|
|
141
136
|
}
|
|
142
137
|
// Skip workspace-protocol + file: + link: + git: + http(s): specs.
|
|
143
|
-
if (raw.startsWith(
|
|
144
|
-
raw.startsWith(
|
|
145
|
-
raw.startsWith(
|
|
146
|
-
raw.startsWith(
|
|
147
|
-
raw.startsWith(
|
|
148
|
-
raw.startsWith(
|
|
149
|
-
raw.startsWith(
|
|
150
|
-
raw ===
|
|
151
|
-
raw ===
|
|
138
|
+
if (raw.startsWith('workspace:') ||
|
|
139
|
+
raw.startsWith('file:') ||
|
|
140
|
+
raw.startsWith('link:') ||
|
|
141
|
+
raw.startsWith('git+') ||
|
|
142
|
+
raw.startsWith('git:') ||
|
|
143
|
+
raw.startsWith('http') ||
|
|
144
|
+
raw.startsWith('npm:') || // e.g. `foo: npm:@scope/foo@^1`
|
|
145
|
+
raw === '*' ||
|
|
146
|
+
raw === 'latest') {
|
|
152
147
|
continue;
|
|
153
148
|
}
|
|
154
149
|
const { prefix, version } = splitRange(raw);
|
|
@@ -171,8 +166,8 @@ function collectExternalDeps(pkg, filters) {
|
|
|
171
166
|
function splitRange(range) {
|
|
172
167
|
const m = range.match(/^(\^|~|>=|<=|>|<|=)?\s*([0-9].*)$/);
|
|
173
168
|
if (!m)
|
|
174
|
-
return { prefix:
|
|
175
|
-
const prefix = m[1] ??
|
|
169
|
+
return { prefix: '', version: null };
|
|
170
|
+
const prefix = m[1] ?? '';
|
|
176
171
|
const version = m[2]?.split(/\s|[|&,]/)[0] ?? null; // strip range modifiers (`||`, ` - `, etc.)
|
|
177
172
|
const parsed = version ? parse(version) : null;
|
|
178
173
|
return { prefix, version: parsed?.version ?? null };
|
|
@@ -190,7 +185,7 @@ async function resolveCandidates(entries, npmrc, verbose, mode) {
|
|
|
190
185
|
const entry = entries[i];
|
|
191
186
|
try {
|
|
192
187
|
const packument = await fetchPackument(entry.name, { npmrc });
|
|
193
|
-
const latest = packument[
|
|
188
|
+
const latest = packument['dist-tags']?.latest;
|
|
194
189
|
if (!latest) {
|
|
195
190
|
if (verbose)
|
|
196
191
|
console.warn(` ${entry.name}: no dist-tags.latest, skipping`);
|
|
@@ -202,11 +197,11 @@ async function resolveCandidates(entries, npmrc, verbose, mode) {
|
|
|
202
197
|
continue;
|
|
203
198
|
}
|
|
204
199
|
const diff = classifyDiff(entry.currentVersion, latest);
|
|
205
|
-
if (diff ===
|
|
200
|
+
if (diff === 'none')
|
|
206
201
|
continue;
|
|
207
|
-
if (mode ===
|
|
202
|
+
if (mode === 'minor' && diff === 'major')
|
|
208
203
|
continue;
|
|
209
|
-
if (mode ===
|
|
204
|
+
if (mode === 'patch' && (diff === 'major' || diff === 'minor'))
|
|
210
205
|
continue;
|
|
211
206
|
results.push({
|
|
212
207
|
...entry,
|
|
@@ -219,7 +214,6 @@ async function resolveCandidates(entries, npmrc, verbose, mode) {
|
|
|
219
214
|
console.warn(` ${entry.name}: fetch failed (${err.message})`);
|
|
220
215
|
}
|
|
221
216
|
}
|
|
222
|
-
void packumentToString;
|
|
223
217
|
}
|
|
224
218
|
await Promise.all(Array.from({ length: cap }, () => worker()));
|
|
225
219
|
results.sort((a, b) => a.name.localeCompare(b.name));
|
|
@@ -229,39 +223,39 @@ function classifyDiff(current, latest) {
|
|
|
229
223
|
const c = parse(current);
|
|
230
224
|
const l = parse(latest);
|
|
231
225
|
if (!c || !l)
|
|
232
|
-
return
|
|
226
|
+
return 'none';
|
|
233
227
|
if (c.major !== l.major)
|
|
234
|
-
return l.major > c.major ?
|
|
228
|
+
return l.major > c.major ? 'major' : 'none';
|
|
235
229
|
if (c.minor !== l.minor)
|
|
236
|
-
return l.minor > c.minor ?
|
|
230
|
+
return l.minor > c.minor ? 'minor' : 'none';
|
|
237
231
|
if (c.patch !== l.patch)
|
|
238
|
-
return l.patch > c.patch ?
|
|
239
|
-
if ((c.prerelease ?? []).join(
|
|
240
|
-
return
|
|
241
|
-
return
|
|
232
|
+
return l.patch > c.patch ? 'patch' : 'none';
|
|
233
|
+
if ((c.prerelease ?? []).join('.') !== (l.prerelease ?? []).join('.'))
|
|
234
|
+
return 'prerelease';
|
|
235
|
+
return 'none';
|
|
242
236
|
}
|
|
243
237
|
// ─── Output / Interaction ──────────────────────────────────────────────
|
|
244
238
|
const ANSI = {
|
|
245
|
-
reset:
|
|
246
|
-
bold:
|
|
247
|
-
dim:
|
|
248
|
-
red:
|
|
249
|
-
yellow:
|
|
250
|
-
green:
|
|
251
|
-
cyan:
|
|
239
|
+
reset: '\x1b[0m',
|
|
240
|
+
bold: '\x1b[1m',
|
|
241
|
+
dim: '\x1b[2m',
|
|
242
|
+
red: '\x1b[31m',
|
|
243
|
+
yellow: '\x1b[33m',
|
|
244
|
+
green: '\x1b[32m',
|
|
245
|
+
cyan: '\x1b[36m',
|
|
252
246
|
};
|
|
253
247
|
function colorForDiff(diff) {
|
|
254
248
|
switch (diff) {
|
|
255
|
-
case
|
|
249
|
+
case 'major':
|
|
256
250
|
return ANSI.red;
|
|
257
|
-
case
|
|
251
|
+
case 'minor':
|
|
258
252
|
return ANSI.yellow;
|
|
259
|
-
case
|
|
253
|
+
case 'patch':
|
|
260
254
|
return ANSI.green;
|
|
261
|
-
case
|
|
255
|
+
case 'prerelease':
|
|
262
256
|
return ANSI.cyan;
|
|
263
257
|
default:
|
|
264
|
-
return
|
|
258
|
+
return '';
|
|
265
259
|
}
|
|
266
260
|
}
|
|
267
261
|
function printTable(candidates) {
|
|
@@ -269,33 +263,33 @@ function printTable(candidates) {
|
|
|
269
263
|
const curW = Math.max(...candidates.map((c) => c.currentRange.length), 7);
|
|
270
264
|
const newW = Math.max(...candidates.map((c) => c.latestVersion.length), 6);
|
|
271
265
|
const idxW = String(candidates.length).length + 2;
|
|
272
|
-
const head =
|
|
266
|
+
const head = ' '.repeat(idxW) +
|
|
273
267
|
ANSI.bold +
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
268
|
+
'name'.padEnd(nameW) +
|
|
269
|
+
' ' +
|
|
270
|
+
'current'.padEnd(curW) +
|
|
271
|
+
' ' +
|
|
272
|
+
'latest'.padEnd(newW) +
|
|
273
|
+
' ' +
|
|
274
|
+
'kind' +
|
|
281
275
|
ANSI.reset;
|
|
282
276
|
console.log(head);
|
|
283
|
-
console.log(
|
|
277
|
+
console.log(' '.repeat(idxW) + ANSI.dim + '─'.repeat(nameW + curW + newW + 12) + ANSI.reset);
|
|
284
278
|
for (let i = 0; i < candidates.length; i++) {
|
|
285
279
|
const c = candidates[i];
|
|
286
280
|
const idx = `${i + 1}.`.padEnd(idxW);
|
|
287
281
|
const color = colorForDiff(c.diff);
|
|
288
282
|
console.log(idx +
|
|
289
283
|
c.name.padEnd(nameW) +
|
|
290
|
-
|
|
284
|
+
' ' +
|
|
291
285
|
ANSI.dim +
|
|
292
286
|
c.currentRange.padEnd(curW) +
|
|
293
287
|
ANSI.reset +
|
|
294
|
-
|
|
288
|
+
' ' +
|
|
295
289
|
color +
|
|
296
290
|
c.latestVersion.padEnd(newW) +
|
|
297
291
|
ANSI.reset +
|
|
298
|
-
|
|
292
|
+
' ' +
|
|
299
293
|
color +
|
|
300
294
|
c.diff +
|
|
301
295
|
ANSI.reset);
|
|
@@ -303,28 +297,28 @@ function printTable(candidates) {
|
|
|
303
297
|
}
|
|
304
298
|
async function promptSelection(candidates) {
|
|
305
299
|
if (!process.stdin.isTTY) {
|
|
306
|
-
console.log(
|
|
300
|
+
console.log('[gjsify upgrade] non-TTY stdin: pass --latest / --minor / --patch (or --yes for interactive-all) to upgrade non-interactively.');
|
|
307
301
|
return [];
|
|
308
302
|
}
|
|
309
303
|
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
310
304
|
try {
|
|
311
|
-
console.log(
|
|
305
|
+
console.log('\nSelect upgrades: comma- or space-separated indices, ' +
|
|
312
306
|
ANSI.bold +
|
|
313
|
-
|
|
307
|
+
'a' +
|
|
314
308
|
ANSI.reset +
|
|
315
|
-
|
|
309
|
+
' for all, ranges like ' +
|
|
316
310
|
ANSI.bold +
|
|
317
|
-
|
|
311
|
+
'1-3' +
|
|
318
312
|
ANSI.reset +
|
|
319
|
-
|
|
313
|
+
', or ' +
|
|
320
314
|
ANSI.bold +
|
|
321
|
-
|
|
315
|
+
'ENTER' +
|
|
322
316
|
ANSI.reset +
|
|
323
|
-
|
|
324
|
-
const answer = (await rl.question(
|
|
317
|
+
' to skip:');
|
|
318
|
+
const answer = (await rl.question('> ')).trim();
|
|
325
319
|
if (!answer)
|
|
326
320
|
return [];
|
|
327
|
-
if (answer.toLowerCase() ===
|
|
321
|
+
if (answer.toLowerCase() === 'a' || answer.toLowerCase() === 'all')
|
|
328
322
|
return candidates;
|
|
329
323
|
const picked = new Set();
|
|
330
324
|
for (const token of answer.split(/[\s,]+/).filter(Boolean)) {
|
|
@@ -339,9 +333,7 @@ async function promptSelection(candidates) {
|
|
|
339
333
|
picked.add(Number(token) - 1);
|
|
340
334
|
}
|
|
341
335
|
}
|
|
342
|
-
return [...picked]
|
|
343
|
-
.filter((i) => i >= 0 && i < candidates.length)
|
|
344
|
-
.map((i) => candidates[i]);
|
|
336
|
+
return [...picked].filter((i) => i >= 0 && i < candidates.length).map((i) => candidates[i]);
|
|
345
337
|
}
|
|
346
338
|
finally {
|
|
347
339
|
rl.close();
|
|
@@ -357,7 +349,7 @@ function writePackageJson(path, rawText, parsed, selected) {
|
|
|
357
349
|
map[c.name] = c.prefix + c.latestVersion;
|
|
358
350
|
}
|
|
359
351
|
const indent = detectIndent(rawText);
|
|
360
|
-
writeFileSync(path, JSON.stringify(parsed, null, indent) + (rawText.endsWith(
|
|
352
|
+
writeFileSync(path, JSON.stringify(parsed, null, indent) + (rawText.endsWith('\n') ? '\n' : ''), 'utf-8');
|
|
361
353
|
}
|
|
362
354
|
function detectIndent(json) {
|
|
363
355
|
const m = json.match(/^\{\n( +)/);
|
|
@@ -377,11 +369,11 @@ async function loadNpmrcLight(cwd) {
|
|
|
377
369
|
// as install-backend-native, except env-var `npm_config_registry` wins
|
|
378
370
|
// over file values (matches npm's real semantics, lets the test harness
|
|
379
371
|
// point at a mock registry without touching `~/.npmrc`).
|
|
380
|
-
for (const candidate of [join(homedir(),
|
|
372
|
+
for (const candidate of [join(homedir(), '.npmrc'), join(cwd, '.npmrc')]) {
|
|
381
373
|
if (!existsSync(candidate))
|
|
382
374
|
continue;
|
|
383
375
|
try {
|
|
384
|
-
const proj = parseNpmrc(readFileSync(candidate,
|
|
376
|
+
const proj = parseNpmrc(readFileSync(candidate, 'utf-8'));
|
|
385
377
|
parsed = {
|
|
386
378
|
...parsed,
|
|
387
379
|
...proj,
|
|
@@ -397,6 +389,3 @@ async function loadNpmrcLight(cwd) {
|
|
|
397
389
|
}
|
|
398
390
|
return parsed;
|
|
399
391
|
}
|
|
400
|
-
function packumentToString(p) {
|
|
401
|
-
return `${p.name}@${p["dist-tags"]?.latest ?? "?"}`;
|
|
402
|
-
}
|
|
@@ -51,9 +51,7 @@ export const workspaceCommand = {
|
|
|
51
51
|
// on isTTY (chalk, picocolors, biome) drop colors when stdout is a
|
|
52
52
|
// pipe, including GitHub Actions where the log viewer renders ANSI
|
|
53
53
|
// fine.
|
|
54
|
-
const colorEnv = process.env.FORCE_COLOR !== undefined || process.env.NO_COLOR !== undefined
|
|
55
|
-
? {}
|
|
56
|
-
: { FORCE_COLOR: '1' };
|
|
54
|
+
const colorEnv = process.env.FORCE_COLOR !== undefined || process.env.NO_COLOR !== undefined ? {} : { FORCE_COLOR: '1' };
|
|
57
55
|
await new Promise((resolve, reject) => {
|
|
58
56
|
const child = spawn(runner, argv, {
|
|
59
57
|
cwd: target.location,
|
package/lib/config.js
CHANGED
|
@@ -41,7 +41,10 @@ function merge(target, ...sources) {
|
|
|
41
41
|
return target;
|
|
42
42
|
}
|
|
43
43
|
function isPlainObject(val) {
|
|
44
|
-
return typeof val === 'object' &&
|
|
44
|
+
return (typeof val === 'object' &&
|
|
45
|
+
val !== null &&
|
|
46
|
+
!Array.isArray(val) &&
|
|
47
|
+
Object.getPrototypeOf(val) === Object.prototype);
|
|
45
48
|
}
|
|
46
49
|
/**
|
|
47
50
|
* Read a dotted path (`a.b.c`) from a plain object. Returns `undefined` for
|
|
@@ -85,13 +88,12 @@ export class Config {
|
|
|
85
88
|
// package.json#gjsify exists.
|
|
86
89
|
const fileExplorer = cosmiconfig(APP_NAME, {
|
|
87
90
|
...this.loadOptions,
|
|
88
|
-
searchPlaces: (this.loadOptions.searchPlaces ?? defaultSearchPlaces(APP_NAME))
|
|
89
|
-
.filter((p) => p !== 'package.json'),
|
|
91
|
+
searchPlaces: (this.loadOptions.searchPlaces ?? defaultSearchPlaces(APP_NAME)).filter((p) => p !== 'package.json'),
|
|
90
92
|
});
|
|
91
|
-
const fileResult = await fileExplorer.search(searchFrom);
|
|
93
|
+
const fileResult = (await fileExplorer.search(searchFrom));
|
|
92
94
|
const merged = {};
|
|
93
95
|
try {
|
|
94
|
-
const pkg = await this.readPackageJSON(searchFrom);
|
|
96
|
+
const pkg = (await this.readPackageJSON(searchFrom));
|
|
95
97
|
if (isPlainObject(pkg?.gjsify))
|
|
96
98
|
merge(merged, pkg.gjsify);
|
|
97
99
|
}
|
|
@@ -125,8 +127,8 @@ export class Config {
|
|
|
125
127
|
const configFile = await this.load(process.cwd());
|
|
126
128
|
const configData = { ...configFile.config };
|
|
127
129
|
const configFilePath = configFile.filepath || process.cwd();
|
|
128
|
-
const pkg = await this.readPackageJSON(configFilePath);
|
|
129
|
-
const tsConfig = await this.readTSConfig(configFilePath);
|
|
130
|
+
const pkg = (await this.readPackageJSON(configFilePath));
|
|
131
|
+
const tsConfig = (await this.readTSConfig(configFilePath));
|
|
130
132
|
tsConfig.reflection ||= cliArgs.reflection;
|
|
131
133
|
// TODO replace with `cliArgs.logLevel`
|
|
132
134
|
configData.verbose = cliArgs.verbose || false;
|
|
@@ -141,12 +143,15 @@ export class Config {
|
|
|
141
143
|
const raw = Array.isArray(cliArgs.excludeGlobals)
|
|
142
144
|
? cliArgs.excludeGlobals.join(',')
|
|
143
145
|
: String(cliArgs.excludeGlobals);
|
|
144
|
-
const ids = raw
|
|
146
|
+
const ids = raw
|
|
147
|
+
.split(',')
|
|
148
|
+
.map((s) => s.trim())
|
|
149
|
+
.filter(Boolean);
|
|
145
150
|
if (ids.length)
|
|
146
151
|
configData.excludeGlobals = [...(configData.excludeGlobals ?? []), ...ids];
|
|
147
152
|
}
|
|
148
|
-
merge(configData.library ??= {}, pkg, configData.library);
|
|
149
|
-
merge(configData.typescript ??= {}, tsConfig, configData.typescript);
|
|
153
|
+
merge((configData.library ??= {}), pkg, configData.library);
|
|
154
|
+
merge((configData.typescript ??= {}), tsConfig, configData.typescript);
|
|
150
155
|
// Parse `KEY=VALUE` style flags into Record<string, string>.
|
|
151
156
|
// - `--define`: VALUE is a JS expression (string literals must be
|
|
152
157
|
// pre-quoted by the caller, e.g. `'"1.2.3"'`).
|
|
@@ -170,7 +175,7 @@ export class Config {
|
|
|
170
175
|
const defineMap = parseKvPairs(cliArgs.define ?? [], 'define');
|
|
171
176
|
const aliasMap = parseKvPairs(cliArgs.alias ?? [], 'alias');
|
|
172
177
|
if (Object.keys(aliasMap).length) {
|
|
173
|
-
configData.aliases = { ...
|
|
178
|
+
configData.aliases = { ...configData.aliases, ...aliasMap };
|
|
174
179
|
}
|
|
175
180
|
// Resolve `defineFromPackageJson` / `defineFromEnv` into raw
|
|
176
181
|
// KEY=<JSON-stringified value> entries that get merged into the
|
|
@@ -289,14 +294,14 @@ export class Config {
|
|
|
289
294
|
// CLI --define wins over package.json/env (manual overrides during
|
|
290
295
|
// debugging beat declarative config).
|
|
291
296
|
transform.define = {
|
|
292
|
-
...
|
|
297
|
+
...transform.define,
|
|
293
298
|
...fromPkgDefines,
|
|
294
299
|
...fromEnvDefines,
|
|
295
300
|
...defineMap,
|
|
296
301
|
};
|
|
297
302
|
}
|
|
298
303
|
if (configData.verbose)
|
|
299
|
-
console.debug(
|
|
304
|
+
console.debug('configData', configData);
|
|
300
305
|
return configData;
|
|
301
306
|
}
|
|
302
307
|
}
|
package/lib/index.js
CHANGED
|
@@ -6,6 +6,26 @@ import yargs from 'yargs';
|
|
|
6
6
|
import { hideBin } from 'yargs/helpers';
|
|
7
7
|
import { buildCommand as build, testCommand as test, runCommand as run, infoCommand as info, systemCheckCommand as systemCheck, 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, formatCommand as format, lintCommand as lint, fixCommand as fix, upgradeCommand as upgrade, barrelsCommand as barrels, } from './commands/index.js';
|
|
8
8
|
import { APP_NAME } from './constants.js';
|
|
9
|
+
// Detect which runtime is executing the CLI (GJS or Node.js).
|
|
10
|
+
// GJS MUST be checked first because @gjsify/process sets
|
|
11
|
+
// `process.versions.node = '20.0.0'` for compatibility — a plain
|
|
12
|
+
// `process.versions.node` check would be a false Node positive under GJS.
|
|
13
|
+
function runtimeLabel() {
|
|
14
|
+
try {
|
|
15
|
+
const sys = globalThis.imports?.system;
|
|
16
|
+
if (sys?.version !== undefined) {
|
|
17
|
+
const v = Number(sys.version);
|
|
18
|
+
return `GJS ${Math.floor(v / 10000)}.${Math.floor((v % 10000) / 100)}.${v % 100} (SpiderMonkey)`;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
catch {
|
|
22
|
+
/* not GJS */
|
|
23
|
+
}
|
|
24
|
+
if (typeof process !== 'undefined' && typeof process.versions?.node === 'string') {
|
|
25
|
+
return `Node.js ${process.version}`;
|
|
26
|
+
}
|
|
27
|
+
return 'unknown runtime';
|
|
28
|
+
}
|
|
9
29
|
// Read the version from package.json adjacent to the bundle. yargs's
|
|
10
30
|
// auto-version-discovery (its `pkg-up`-driven default) doesn't reach
|
|
11
31
|
// through the bundled `dist/cli.gjs.mjs` path on GJS — falls back to
|
|
@@ -68,5 +88,6 @@ await cli
|
|
|
68
88
|
.command(fix.command, fix.description, fix.builder, fix.handler)
|
|
69
89
|
.command(barrels.command, barrels.description, barrels.builder, barrels.handler)
|
|
70
90
|
.demandCommand(1)
|
|
91
|
+
.epilogue(`Running on ${runtimeLabel()}`)
|
|
71
92
|
.help()
|
|
72
93
|
.parseAsync();
|
|
@@ -45,14 +45,17 @@ Gio._promisify(Gio.Subprocess.prototype, 'wait_check_async');
|
|
|
45
45
|
// Substituted by `gjsify generate-installer` for end-user apps.
|
|
46
46
|
const DEFAULT_TARGET = '@gjsify/cli';
|
|
47
47
|
const DEFAULT_BIN_NAME = 'gjsify';
|
|
48
|
-
const DEFAULT_BOOTSTRAP_URL =
|
|
49
|
-
'https://github.com/gjsify/gjsify/releases/latest/download/cli.gjs.mjs';
|
|
48
|
+
const DEFAULT_BOOTSTRAP_URL = 'https://github.com/gjsify/gjsify/releases/latest/download/cli.gjs.mjs';
|
|
50
49
|
const DEFAULT_BOOTSTRAP_SHA256_URL = `${DEFAULT_BOOTSTRAP_URL}.sha256`;
|
|
51
50
|
|
|
52
51
|
const USER_AGENT = 'gjsify-installer/1.0';
|
|
53
52
|
|
|
54
|
-
function info(msg) {
|
|
55
|
-
|
|
53
|
+
function info(msg) {
|
|
54
|
+
print(`[gjsify] ${msg}`);
|
|
55
|
+
}
|
|
56
|
+
function error(msg) {
|
|
57
|
+
printerr(`[gjsify] ERROR: ${msg}`);
|
|
58
|
+
}
|
|
56
59
|
|
|
57
60
|
function parseArgs() {
|
|
58
61
|
const argv = system?.programArgs ?? [];
|
|
@@ -63,9 +66,8 @@ function parseArgs() {
|
|
|
63
66
|
let bootstrapUrl = GLib.getenv('GJSIFY_INSTALL_BOOTSTRAP_URL') || DEFAULT_BOOTSTRAP_URL;
|
|
64
67
|
let bootstrapSha256Url = GLib.getenv('GJSIFY_INSTALL_BOOTSTRAP_SHA256_URL');
|
|
65
68
|
if (bootstrapSha256Url === null || bootstrapSha256Url === undefined) {
|
|
66
|
-
bootstrapSha256Url =
|
|
67
|
-
? DEFAULT_BOOTSTRAP_SHA256_URL
|
|
68
|
-
: `${bootstrapUrl}.sha256`;
|
|
69
|
+
bootstrapSha256Url =
|
|
70
|
+
bootstrapUrl === DEFAULT_BOOTSTRAP_URL ? DEFAULT_BOOTSTRAP_SHA256_URL : `${bootstrapUrl}.sha256`;
|
|
69
71
|
}
|
|
70
72
|
for (let i = 0; i < argv.length; i++) {
|
|
71
73
|
const a = argv[i];
|
|
@@ -154,8 +156,7 @@ function sha256Hex(bytes) {
|
|
|
154
156
|
function cacheDir() {
|
|
155
157
|
const override = GLib.getenv('GJSIFY_INSTALL_BOOTSTRAP_CACHE');
|
|
156
158
|
if (override) return override;
|
|
157
|
-
const xdg = GLib.getenv('XDG_CACHE_HOME') ||
|
|
158
|
-
GLib.build_filenamev([GLib.get_home_dir(), '.cache']);
|
|
159
|
+
const xdg = GLib.getenv('XDG_CACHE_HOME') || GLib.build_filenamev([GLib.get_home_dir(), '.cache']);
|
|
159
160
|
return GLib.build_filenamev([xdg, 'gjsify', 'bootstrap']);
|
|
160
161
|
}
|
|
161
162
|
|
|
@@ -164,9 +165,7 @@ function ensureDir(dir) {
|
|
|
164
165
|
}
|
|
165
166
|
|
|
166
167
|
function writeBytes(path, bytes) {
|
|
167
|
-
Gio.File.new_for_path(path).replace_contents(
|
|
168
|
-
bytes, null, false, Gio.FileCreateFlags.REPLACE_DESTINATION, null,
|
|
169
|
-
);
|
|
168
|
+
Gio.File.new_for_path(path).replace_contents(bytes, null, false, Gio.FileCreateFlags.REPLACE_DESTINATION, null);
|
|
170
169
|
}
|
|
171
170
|
|
|
172
171
|
async function downloadBootstrap(session, bootstrapUrl, sha256Url) {
|
|
@@ -190,7 +189,11 @@ async function downloadBootstrap(session, bootstrapUrl, sha256Url) {
|
|
|
190
189
|
}
|
|
191
190
|
}
|
|
192
191
|
const dir = cacheDir();
|
|
193
|
-
try {
|
|
192
|
+
try {
|
|
193
|
+
ensureDir(dir);
|
|
194
|
+
} catch {
|
|
195
|
+
/* exists */
|
|
196
|
+
}
|
|
194
197
|
const bundlePath = GLib.build_filenamev([dir, 'cli.gjs.mjs']);
|
|
195
198
|
writeBytes(bundlePath, bundleBytes);
|
|
196
199
|
info(`Bootstrap cached at ${bundlePath} (${bundleBytes.length} bytes)`);
|
|
@@ -220,7 +223,10 @@ async function runInstall(bundlePath, spec) {
|
|
|
220
223
|
|
|
221
224
|
async function main() {
|
|
222
225
|
const opts = parseArgs();
|
|
223
|
-
if (opts.help) {
|
|
226
|
+
if (opts.help) {
|
|
227
|
+
printUsage();
|
|
228
|
+
exit(0);
|
|
229
|
+
}
|
|
224
230
|
checkGjsVersion();
|
|
225
231
|
|
|
226
232
|
const session = new Soup.Session();
|