@gjsify/cli 0.4.28 → 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.
Files changed (62) hide show
  1. package/dist/cli.gjs.mjs +33 -33
  2. package/lib/actions/barrels-generate.js +1 -5
  3. package/lib/actions/build.d.ts +3 -3
  4. package/lib/actions/build.js +56 -64
  5. package/lib/bundler-pick.d.ts +3 -3
  6. package/lib/bundler-pick.js +5 -6
  7. package/lib/commands/build.js +37 -31
  8. package/lib/commands/check.js +3 -3
  9. package/lib/commands/fix.js +33 -23
  10. package/lib/commands/flatpak/build.js +6 -2
  11. package/lib/commands/flatpak/check.js +9 -3
  12. package/lib/commands/flatpak/ci.js +1 -2
  13. package/lib/commands/flatpak/deps.js +1 -2
  14. package/lib/commands/flatpak/diff.js +2 -6
  15. package/lib/commands/flatpak/init.js +19 -19
  16. package/lib/commands/flatpak/release.js +2 -2
  17. package/lib/commands/flatpak/scaffold.js +3 -11
  18. package/lib/commands/flatpak/sync-flathub.js +4 -8
  19. package/lib/commands/flatpak/utils.js +1 -6
  20. package/lib/commands/foreach.js +5 -14
  21. package/lib/commands/format.js +54 -41
  22. package/lib/commands/gettext.js +2 -10
  23. package/lib/commands/gresource.js +2 -8
  24. package/lib/commands/gsettings.js +1 -3
  25. package/lib/commands/install.js +13 -6
  26. package/lib/commands/lint.d.ts +1 -1
  27. package/lib/commands/lint.js +22 -22
  28. package/lib/commands/pack.js +29 -17
  29. package/lib/commands/publish.js +17 -18
  30. package/lib/commands/run.js +2 -6
  31. package/lib/commands/self-update.js +1 -3
  32. package/lib/commands/showcase.js +1 -1
  33. package/lib/commands/system-check.js +8 -11
  34. package/lib/commands/test.js +12 -8
  35. package/lib/commands/uninstall.js +1 -3
  36. package/lib/commands/upgrade.d.ts +1 -1
  37. package/lib/commands/upgrade.js +109 -120
  38. package/lib/commands/workspace.js +1 -3
  39. package/lib/config.js +18 -13
  40. package/lib/index.js +3 -1
  41. package/lib/templates/install.mjs.tmpl +20 -14
  42. package/lib/templates/oxfmtrc.tmpl +54 -0
  43. package/lib/templates/oxlintrc.json.tmpl +35 -0
  44. package/lib/types/config-data.d.ts +23 -13
  45. package/lib/utils/check-system-deps.js +10 -4
  46. package/lib/utils/detect-native-packages.js +1 -1
  47. package/lib/utils/dlx-cache.js +2 -7
  48. package/lib/utils/install-backend-native.d.ts +2 -2
  49. package/lib/utils/install-backend-native.js +65 -58
  50. package/lib/utils/install-backend.js +2 -1
  51. package/lib/utils/install-global.js +1 -3
  52. package/lib/utils/normalize-bundler-options.js +52 -17
  53. package/lib/utils/oxc-resolve.d.ts +63 -0
  54. package/lib/utils/oxc-resolve.js +264 -0
  55. package/lib/utils/pkg-json-edit.js +1 -6
  56. package/lib/utils/run-gjs.js +1 -4
  57. package/lib/utils/run-lifecycle-script.js +3 -7
  58. package/lib/utils/workspace-root.js +3 -1
  59. package/package.json +17 -17
  60. package/lib/templates/biome.json.tmpl +0 -79
  61. package/lib/utils/biome-resolve.d.ts +0 -47
  62. package/lib/utils/biome-resolve.js +0 -204
@@ -1,14 +1,16 @@
1
- // `gjsify lint` — wraps biome's `lint` mode.
1
+ // `gjsify lint` — wraps oxlint.
2
2
  //
3
- // Sibling of `gjsify format`. Spawns biome from node_modules directly
4
- // (no Node launcher). Default behaviour: report-only. Pass `--write`
5
- // for biome's safe-fix mode, or use `gjsify fix` for the combined
6
- // format + safe-lint-fix + organize-imports surface.
3
+ // Sibling of `gjsify format`. Spawns oxlint via its Node launcher
4
+ // (`node_modules/oxlint/bin/oxlint` `dist/cli.js`) NOT a bare binary —
5
+ // because oxlint's JS-plugin host (used by the internal
6
+ // `oxlint-plugin-gjsify` rule) lives in the JS launcher. Default behaviour:
7
+ // report-only. Pass `--fix` for oxlint's safe-fix mode, or use `gjsify fix`
8
+ // for the combined oxfmt + oxlint --fix surface.
7
9
  import { resolve } from 'node:path';
8
- import { BiomeNotFoundError, findBiomeConfig, printBiomeNotFound, runBiome, } from '../utils/biome-resolve.js';
10
+ import { OxcNotFoundError, findOxlintConfig, printOxcNotFound, runOxlint } from '../utils/oxc-resolve.js';
9
11
  export const lintCommand = {
10
12
  command: 'lint [paths..]',
11
- description: 'Run Biome lint diagnostics (native binary spawn no Node launcher).',
13
+ description: 'Run oxlint diagnostics (spawned via its Node launcher to support JS plugins).',
12
14
  builder: (yargs) => {
13
15
  return yargs
14
16
  .positional('paths', {
@@ -16,41 +18,39 @@ export const lintCommand = {
16
18
  type: 'string',
17
19
  array: true,
18
20
  })
19
- .option('write', {
21
+ .option('fix', {
20
22
  description: 'Apply safe lint fixes in place.',
21
23
  type: 'boolean',
22
24
  default: false,
23
25
  })
24
26
  .option('config-path', {
25
- description: 'Path to a biome.json. Default: walks up from cwd to find one.',
27
+ description: 'Path to an .oxlintrc.json. Default: walks up from cwd to find one.',
26
28
  type: 'string',
27
29
  normalize: true,
28
30
  })
29
31
  .option('verbose', {
30
- description: 'Echo the resolved biome binary + args before spawning.',
32
+ description: 'Echo the resolved oxlint launcher + args before spawning.',
31
33
  type: 'boolean',
32
34
  default: false,
33
35
  });
34
36
  },
35
37
  handler: async (args) => {
36
38
  const cwd = process.cwd();
37
- const paths = args.paths?.length
38
- ? args.paths
39
- : ['.'];
40
- const biomeArgs = ['lint'];
41
- if (args.write)
42
- biomeArgs.push('--write');
43
- const configPath = args.configPath ?? findBiomeConfig(cwd) ?? undefined;
39
+ const paths = args.paths?.length ? args.paths : ['.'];
40
+ const oxlintArgs = [];
41
+ if (args.fix)
42
+ oxlintArgs.push('--fix');
43
+ const configPath = args.configPath ?? findOxlintConfig(cwd) ?? undefined;
44
44
  if (configPath)
45
- biomeArgs.push(`--config-path=${resolve(configPath, '..')}`);
46
- biomeArgs.push(...paths);
45
+ oxlintArgs.push('--config', resolve(configPath));
46
+ oxlintArgs.push(...paths);
47
47
  try {
48
- const code = await runBiome(biomeArgs, { cwd, verbose: args.verbose });
48
+ const code = await runOxlint(oxlintArgs, { cwd, verbose: args.verbose });
49
49
  process.exitCode = code;
50
50
  }
51
51
  catch (err) {
52
- if (err instanceof BiomeNotFoundError) {
53
- printBiomeNotFound(err);
52
+ if (err instanceof OxcNotFoundError) {
53
+ printOxcNotFound(err);
54
54
  process.exitCode = 1;
55
55
  return;
56
56
  }
@@ -113,15 +113,11 @@ export async function packWorkspace(wsDir, opts = {}) {
113
113
  // mutated it (e.g. a `prepack` that injects build metadata into
114
114
  // package.json fields). Rare but legal — npm pack does the same.
115
115
  const sourceAfterScripts = readFileSync(pkgPath, 'utf-8');
116
- const pkgAfterScripts = sourceAfterScripts === originalSource
117
- ? pkg
118
- : JSON.parse(sourceAfterScripts);
116
+ const pkgAfterScripts = sourceAfterScripts === originalSource ? pkg : JSON.parse(sourceAfterScripts);
119
117
  // Rewrite workspace:^/~/* deps to resolved npm version ranges, mirroring
120
118
  // yarn's auto-rewrite at publish time. Done in-memory only — the source
121
119
  // package.json on disk is never mutated by `gjsify pack`.
122
- const rewrittenPkg = opts.skipWorkspaceRewrite
123
- ? pkgAfterScripts
124
- : rewriteWorkspaceDeps(pkgAfterScripts, wsDir);
120
+ const rewrittenPkg = opts.skipWorkspaceRewrite ? pkgAfterScripts : rewriteWorkspaceDeps(pkgAfterScripts, wsDir);
125
121
  const rewrittenSource = JSON.stringify(rewrittenPkg, null, indentOf(sourceAfterScripts)) + '\n';
126
122
  // Collect files according to the package.json `files` field (or npm's
127
123
  // default set). The package.json itself is always included with the
@@ -148,9 +144,7 @@ export async function packWorkspace(wsDir, opts = {}) {
148
144
  const tarBytes = createTarball(entries);
149
145
  const gzipBytes = await gzip(tarBytes);
150
146
  // npm filename: scope replaced with leading dash. "@gjsify/foo" → "gjsify-foo".
151
- const filenameBase = name.startsWith('@')
152
- ? name.slice(1).replace('/', '-')
153
- : name;
147
+ const filenameBase = name.startsWith('@') ? name.slice(1).replace('/', '-') : name;
154
148
  const filename = `${filenameBase}-${version}.tgz`;
155
149
  const sha1 = createHash('sha1').update(gzipBytes).digest('hex');
156
150
  const sha512 = createHash('sha512').update(gzipBytes).digest('base64');
@@ -188,7 +182,9 @@ export async function packWorkspace(wsDir, opts = {}) {
188
182
  */
189
183
  function collectFiles(wsDir, pkg) {
190
184
  const always = forceIncluded(pkg);
191
- const filesField = Array.isArray(pkg.files) ? pkg.files.filter((f) => typeof f === 'string') : null;
185
+ const filesField = Array.isArray(pkg.files)
186
+ ? pkg.files.filter((f) => typeof f === 'string')
187
+ : null;
192
188
  let candidates;
193
189
  if (filesField) {
194
190
  candidates = expandFilesPatterns(wsDir, filesField);
@@ -208,12 +204,25 @@ function collectFiles(wsDir, pkg) {
208
204
  }
209
205
  return [...out].sort();
210
206
  }
211
- const ALWAYS_INCLUDED_BASENAMES = new Set(['package.json', 'README', 'README.md', 'LICENSE', 'LICENSE.md', 'NOTICE', 'NOTICE.md']);
212
207
  const NEVER_INCLUDED_BASENAMES = new Set([
213
- '.git', '.svn', '.hg', '.gitignore', '.gitattributes', '.npmrc',
214
- 'CVS', '.DS_Store', 'node_modules', '.npmignore', 'package-lock.json',
215
- 'gjsify-lock.json', 'yarn.lock', 'yarn-error.log', '.yarn',
216
- '.pnp.cjs', '.pnp.loader.mjs', 'tsconfig.tsbuildinfo',
208
+ '.git',
209
+ '.svn',
210
+ '.hg',
211
+ '.gitignore',
212
+ '.gitattributes',
213
+ '.npmrc',
214
+ 'CVS',
215
+ '.DS_Store',
216
+ 'node_modules',
217
+ '.npmignore',
218
+ 'package-lock.json',
219
+ 'gjsify-lock.json',
220
+ 'yarn.lock',
221
+ 'yarn-error.log',
222
+ '.yarn',
223
+ '.pnp.cjs',
224
+ '.pnp.loader.mjs',
225
+ 'tsconfig.tsbuildinfo',
217
226
  ]);
218
227
  function forceIncluded(pkg) {
219
228
  const out = new Set();
@@ -292,7 +301,7 @@ function loadIgnore(wsDir) {
292
301
  const npmIgnorePath = join(wsDir, '.npmignore');
293
302
  const gitIgnorePath = join(wsDir, '.gitignore');
294
303
  const patterns = [];
295
- const sourcePath = existsSync(npmIgnorePath) ? npmIgnorePath : (existsSync(gitIgnorePath) ? gitIgnorePath : null);
304
+ const sourcePath = existsSync(npmIgnorePath) ? npmIgnorePath : existsSync(gitIgnorePath) ? gitIgnorePath : null;
296
305
  if (sourcePath) {
297
306
  const lines = readFileSync(sourcePath, 'utf-8').split('\n');
298
307
  for (const raw of lines) {
@@ -319,7 +328,10 @@ function globToRegex(glob) {
319
328
  // Escape regex metachars except *,?,/
320
329
  pat = pat.replace(/[.+^${}()|[\]\\]/g, '\\$&');
321
330
  // ** → .* * → [^/]* ? → [^/]
322
- pat = pat.replace(/\*\*/g, '__DOUBLESTAR__').replace(/\*/g, '[^/]*').replace(/__DOUBLESTAR__/g, '.*');
331
+ pat = pat
332
+ .replace(/\*\*/g, '__DOUBLESTAR__')
333
+ .replace(/\*/g, '[^/]*')
334
+ .replace(/__DOUBLESTAR__/g, '.*');
323
335
  pat = pat.replace(/\?/g, '[^/]');
324
336
  return new RegExp(`^${pat}($|/)`);
325
337
  }
@@ -36,9 +36,9 @@
36
36
  import { existsSync, readFileSync } from 'node:fs';
37
37
  import { homedir } from 'node:os';
38
38
  import { join, resolve } from 'node:path';
39
- import { DEFAULT_REGISTRY, parseNpmrc, registryFor, buildHeaders, } from '@gjsify/npm-registry';
39
+ import { DEFAULT_REGISTRY, parseNpmrc, registryFor, buildHeaders } from '@gjsify/npm-registry';
40
40
  import { packWorkspace } from './pack.js';
41
- import { getNpmTrustedToken, hasGithubOidcEnv, OidcExchangeError, OidcUnavailableError, } from '../utils/npm-oidc.js';
41
+ import { getNpmTrustedToken, hasGithubOidcEnv, OidcExchangeError, OidcUnavailableError } from '../utils/npm-oidc.js';
42
42
  export const publishCommand = {
43
43
  command: 'publish [path]',
44
44
  description: 'Pack + upload the workspace at <path> (default: cwd) to its npm registry. Drop-in for `npm publish` with workspace:^ rewrite handled automatically.',
@@ -68,7 +68,7 @@ export const publishCommand = {
68
68
  default: false,
69
69
  })
70
70
  .option('provenance', {
71
- description: 'Pass-through flag — recorded in the payload but no signing happens (gjsify doesn\'t ship a sigstore signer yet).',
71
+ description: "Pass-through flag — recorded in the payload but no signing happens (gjsify doesn't ship a sigstore signer yet).",
72
72
  type: 'boolean',
73
73
  default: false,
74
74
  })
@@ -227,9 +227,7 @@ export const publishCommand = {
227
227
  // libnpmpublish/lib/publish.js). The full scoped filename is what
228
228
  // `npm pack` writes to disk, but the registry stores tarballs at
229
229
  // the unscoped path.
230
- const unscopedName = packed.name.includes('/')
231
- ? packed.name.slice(packed.name.indexOf('/') + 1)
232
- : packed.name;
230
+ const unscopedName = packed.name.includes('/') ? packed.name.slice(packed.name.indexOf('/') + 1) : packed.name;
233
231
  const wireFilename = `${unscopedName}-${packed.version}.tgz`;
234
232
  const tarballUrl = `${registryClean}/${packed.name}/-/${wireFilename}`;
235
233
  // 4. Build payload + PUT
@@ -255,8 +253,7 @@ export const publishCommand = {
255
253
  // OIDC is used iff GitHub OIDC env vars are present AND no
256
254
  // `NODE_AUTH_TOKEN` is set. With `NODE_AUTH_TOKEN` set the user has
257
255
  // explicitly opted into token auth, so we don't shadow their choice.
258
- const wantTrusted = trustedFlag === true ||
259
- (trustedFlag === undefined && hasGithubOidcEnv() && !process.env.NODE_AUTH_TOKEN);
256
+ const wantTrusted = trustedFlag === true || (trustedFlag === undefined && hasGithubOidcEnv() && !process.env.NODE_AUTH_TOKEN);
260
257
  let authMode = 'token';
261
258
  if (wantTrusted) {
262
259
  try {
@@ -281,9 +278,7 @@ export const publishCommand = {
281
278
  // Trusted Publisher bootstrap"). Skip such a package when
282
279
  // --tolerate-untrusted-new is set so one un-bootstrapped
283
280
  // package doesn't break the entire serialized publish loop.
284
- const isUntrustedNewPackage = err instanceof OidcExchangeError &&
285
- err.status === 404 &&
286
- /package not found/i.test(err.body);
281
+ const isUntrustedNewPackage = err instanceof OidcExchangeError && err.status === 404 && /package not found/i.test(err.body);
287
282
  if (isUntrustedNewPackage && tolerateUntrustedNew) {
288
283
  const headerMsg = `${packed.name}@${packed.version} (skipped — no Trusted Publisher on npm, see AGENTS.md "New @gjsify/* package: first-publish + Trusted Publisher bootstrap")`;
289
284
  if (args.json) {
@@ -343,8 +338,7 @@ export const publishCommand = {
343
338
  if (!otp && res.status === 401) {
344
339
  const wwwAuth = res.headers.get('www-authenticate') ?? '';
345
340
  const body401 = await res.text().catch(() => '');
346
- const needsOtp = wwwAuth.toLowerCase().split(/,\s*/).includes('otp') ||
347
- /one-time pass/i.test(body401);
341
+ const needsOtp = wwwAuth.toLowerCase().split(/,\s*/).includes('otp') || /one-time pass/i.test(body401);
348
342
  if (needsOtp) {
349
343
  // Interactive path: if stdin is a TTY, prompt and retry once.
350
344
  if (process.stdin.isTTY && process.stdout.isTTY) {
@@ -394,8 +388,7 @@ export const publishCommand = {
394
388
  // Both are intentionally tolerated under --tolerate-republish so
395
389
  // that re-running a release workflow after a partial failure does
396
390
  // not error on the already-published packages.
397
- const isRepublishConflict = res.status === 409 ||
398
- (res.status === 403 && /previously published/i.test(text));
391
+ const isRepublishConflict = res.status === 409 || (res.status === 403 && /previously published/i.test(text));
399
392
  if (isRepublishConflict && tolerate) {
400
393
  const out = {
401
394
  ok: true,
@@ -433,11 +426,15 @@ async function packWorkspaceToBytes(wsDir) {
433
426
  try {
434
427
  (await import('node:fs')).rmSync(res.absolutePath);
435
428
  }
436
- catch { /* best effort */ }
429
+ catch {
430
+ /* best effort */
431
+ }
437
432
  try {
438
433
  (await import('node:fs')).rmdirSync(tmp);
439
434
  }
440
- catch { /* best effort */ }
435
+ catch {
436
+ /* best effort */
437
+ }
441
438
  return bytes;
442
439
  }
443
440
  async function loadRewrittenManifest(wsDir, pkg) {
@@ -490,7 +487,9 @@ async function loadNpmrc(cwd) {
490
487
  // The auth-token npmrc from actions/setup-node ships
491
488
  // `_authToken=${NODE_AUTH_TOKEN}` as a literal placeholder; the env var
492
489
  // is set on the publish step.
493
- const merged = sources.join('\n').replace(/\$\{([A-Z_][A-Z0-9_]*)\}/gi, (_, name) => process.env[name] ?? '');
490
+ const merged = sources
491
+ .join('\n')
492
+ .replace(/\$\{([A-Z_][A-Z0-9_]*)\}/gi, (_, name) => process.env[name] ?? '');
494
493
  return parseNpmrc(merged);
495
494
  }
496
495
  function buildPublishPayload(opts) {
@@ -108,9 +108,7 @@ async function runScript(script, extraArgs) {
108
108
  // (stdout is always a pipe there, but the GHA log viewer renders ANSI
109
109
  // fine). Respect user overrides: FORCE_COLOR=0 or NO_COLOR keeps
110
110
  // colors off.
111
- const colorEnv = process.env.FORCE_COLOR !== undefined || process.env.NO_COLOR !== undefined
112
- ? {}
113
- : { FORCE_COLOR: '1' };
111
+ const colorEnv = process.env.FORCE_COLOR !== undefined || process.env.NO_COLOR !== undefined ? {} : { FORCE_COLOR: '1' };
114
112
  const env = {
115
113
  ...process.env,
116
114
  ...colorEnv,
@@ -119,9 +117,7 @@ async function runScript(script, extraArgs) {
119
117
  npm_package_name: pkg.name ?? '',
120
118
  npm_package_version: pkg.version ?? '',
121
119
  };
122
- const fullCmd = extraArgs.length > 0
123
- ? `${literal} ${extraArgs.map(shellEscape).join(' ')}`
124
- : literal;
120
+ const fullCmd = extraArgs.length > 0 ? `${literal} ${extraArgs.map(shellEscape).join(' ')}` : literal;
125
121
  // ensureMainLoop() (called inside spawn) keeps GJS alive after the
126
122
  // child exits — without an explicit process.exit() the success path
127
123
  // would park the loop forever. The error path already exits.
@@ -76,9 +76,7 @@ export const selfUpdateCommand = {
76
76
  return;
77
77
  }
78
78
  if (args.check) {
79
- console.log(currentVersion
80
- ? `Update available: v${currentVersion} → v${target}`
81
- : `Install required: → v${target}`);
79
+ console.log(currentVersion ? `Update available: v${currentVersion} → v${target}` : `Install required: → v${target}`);
82
80
  process.exit(1);
83
81
  return;
84
82
  }
@@ -1,5 +1,5 @@
1
1
  import { discoverShowcases, findShowcase } from '../utils/discover-showcases.js';
2
- import { runMinimalChecks, detectPackageManager, buildInstallCommand, } from '../utils/check-system-deps.js';
2
+ import { runMinimalChecks, detectPackageManager, buildInstallCommand } from '../utils/check-system-deps.js';
3
3
  import { spawn } from 'node:child_process';
4
4
  import { fileURLToPath } from 'node:url';
5
5
  import { readFileSync } from 'node:fs';
@@ -3,8 +3,7 @@ export const systemCheckCommand = {
3
3
  command: 'system-check',
4
4
  description: 'Check that required system dependencies (GJS, GTK4, libsoup3, …) are installed. Optional dependencies are detected only when their @gjsify/* package is in your project. (Previously called `gjsify check`; the bare name now runs TypeScript checks across the workspace — see `gjsify check --help`.)',
5
5
  builder: (yargs) => {
6
- return yargs
7
- .option('json', {
6
+ return yargs.option('json', {
8
7
  description: 'Output results as JSON',
9
8
  type: 'boolean',
10
9
  default: false,
@@ -13,8 +12,8 @@ export const systemCheckCommand = {
13
12
  handler: async (args) => {
14
13
  const results = runAllChecks(process.cwd());
15
14
  const pm = detectPackageManager();
16
- const missingRequired = results.filter(r => !r.found && r.severity === 'required');
17
- const missingOptional = results.filter(r => !r.found && r.severity === 'optional');
15
+ const missingRequired = results.filter((r) => !r.found && r.severity === 'required');
16
+ const missingOptional = results.filter((r) => !r.found && r.severity === 'optional');
18
17
  const allMissing = [...missingRequired, ...missingOptional];
19
18
  if (args.json) {
20
19
  console.log(JSON.stringify({ packageManager: pm, deps: results }, null, 2));
@@ -23,8 +22,8 @@ export const systemCheckCommand = {
23
22
  return;
24
23
  }
25
24
  console.log('System dependency check\n');
26
- const required = results.filter(r => r.severity === 'required');
27
- const optional = results.filter(r => r.severity === 'optional');
25
+ const required = results.filter((r) => r.severity === 'required');
26
+ const optional = results.filter((r) => r.severity === 'optional');
28
27
  if (required.length > 0) {
29
28
  console.log('Required:');
30
29
  for (const dep of required) {
@@ -39,9 +38,7 @@ export const systemCheckCommand = {
39
38
  // ⚠ for missing-but-needed-by-installed-packages, ○ for missing-but-not-needed (shouldn't appear in conditional mode)
40
39
  const icon = dep.found ? '✓' : '⚠';
41
40
  const ver = dep.version ? ` (${dep.version})` : '';
42
- const requiredBy = dep.requiredBy && dep.requiredBy.length > 0
43
- ? ` — needed by ${dep.requiredBy.join(', ')}`
44
- : '';
41
+ const requiredBy = dep.requiredBy && dep.requiredBy.length > 0 ? ` — needed by ${dep.requiredBy.join(', ')}` : '';
45
42
  console.log(` ${icon} ${dep.name}${ver}${requiredBy}`);
46
43
  }
47
44
  }
@@ -51,10 +48,10 @@ export const systemCheckCommand = {
51
48
  return;
52
49
  }
53
50
  if (missingRequired.length > 0) {
54
- console.log(`\nMissing required: ${missingRequired.map(d => d.name).join(', ')}`);
51
+ console.log(`\nMissing required: ${missingRequired.map((d) => d.name).join(', ')}`);
55
52
  }
56
53
  if (missingOptional.length > 0) {
57
- console.log(`Missing optional: ${missingOptional.map(d => d.name).join(', ')}`);
54
+ console.log(`Missing optional: ${missingOptional.map((d) => d.name).join(', ')}`);
58
55
  }
59
56
  const cmd = buildInstallCommand(pm, allMissing);
60
57
  if (cmd) {
@@ -66,13 +66,15 @@ export const testCommand = {
66
66
  ? ['gjs']
67
67
  : args.runtime === 'node'
68
68
  ? ['node']
69
- : (testCfg.runtimes && testCfg.runtimes.length > 0 ? testCfg.runtimes : ['gjs', 'node']);
69
+ : testCfg.runtimes && testCfg.runtimes.length > 0
70
+ ? testCfg.runtimes
71
+ : ['gjs', 'node'];
70
72
  const results = [];
71
73
  for (const runtime of requested) {
72
74
  const outfile = join(outdir, `test.${runtime}.mjs`);
73
75
  // Build stage (skip if --no-build OR (not --rebuild AND outfile fresher than src)).
74
76
  if (args.build !== false) {
75
- const needsBuild = args.rebuild || !isFresh(outfile, entry, cwd);
77
+ const needsBuild = args.rebuild || !isFresh(outfile, entry);
76
78
  if (needsBuild) {
77
79
  const buildStart = Date.now();
78
80
  if (args.verbose) {
@@ -144,11 +146,11 @@ async function buildTestBundle(entry, outfile, runtime, verbose) {
144
146
  // the merged config; we set it explicitly here so package.json#main /
145
147
  // bundler.output.file from the surrounding project don't redirect the
146
148
  // bundle elsewhere.
147
- configData.library = { ...(configData.library ?? {}) };
149
+ configData.library = { ...configData.library };
148
150
  configData.bundler = {
149
- ...(configData.bundler ?? {}),
151
+ ...configData.bundler,
150
152
  input: [entry],
151
- output: { ...(configData.bundler?.output ?? {}), file: outfile },
153
+ output: { ...configData.bundler?.output, file: outfile },
152
154
  };
153
155
  const action = new BuildAction(configData);
154
156
  await action.start({ app: runtime, library: false });
@@ -171,7 +173,7 @@ async function runTestBundle(outfile, runtime) {
171
173
  });
172
174
  }
173
175
  /** True when `outfile` exists and is newer than every `.ts`/`.mts` file under the entry's directory tree. */
174
- function isFresh(outfile, entry, cwd) {
176
+ function isFresh(outfile, entry) {
175
177
  if (!existsSync(outfile))
176
178
  return false;
177
179
  const outMtime = statSync(outfile).mtimeMs;
@@ -186,7 +188,6 @@ function isFresh(outfile, entry, cwd) {
186
188
  // On any FS error, force rebuild to stay safe.
187
189
  return false;
188
190
  }
189
- void cwd;
190
191
  }
191
192
  function newestMtimeUnder(path) {
192
193
  const st = statSync(path);
@@ -194,7 +195,10 @@ function newestMtimeUnder(path) {
194
195
  return st.mtimeMs;
195
196
  let max = st.mtimeMs;
196
197
  for (const entry of readdirSync(path, { withFileTypes: true })) {
197
- if (entry.name === 'node_modules' || entry.name === 'dist' || entry.name === 'lib' || entry.name.startsWith('.')) {
198
+ if (entry.name === 'node_modules' ||
199
+ entry.name === 'dist' ||
200
+ entry.name === 'lib' ||
201
+ entry.name.startsWith('.')) {
198
202
  continue;
199
203
  }
200
204
  const child = join(path, entry.name);
@@ -134,9 +134,7 @@ function findBinShimsForPackage(binDir, pkgDir, verbose) {
134
134
  // Find the `exec [gjs -m] '<target>' "$@"` line; the path may
135
135
  // contain `:` from the optional prebuild preamble lines, which
136
136
  // is why we anchor to `exec ` rather than the first quoted run.
137
- const execLine = content
138
- .split('\n')
139
- .find((line) => /^exec (?:gjs -m )?'/.test(line));
137
+ const execLine = content.split('\n').find((line) => /^exec (?:gjs -m )?'/.test(line));
140
138
  if (!execLine)
141
139
  continue;
142
140
  const m = execLine.match(/'([^']+)'/);
@@ -1,4 +1,4 @@
1
- import type { Command } from "../types/index.js";
1
+ import type { Command } from '../types/index.js';
2
2
  interface UpgradeOptions {
3
3
  latest?: boolean;
4
4
  minor?: boolean;