@gjsify/cli 0.36.0 → 0.37.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 (116) hide show
  1. package/dist/cli.gjs.mjs +3 -3
  2. package/lib/bundler-pick.js +61 -87
  3. package/lib/cli-fail.d.ts +4 -7
  4. package/lib/cli-fail.js +29 -55
  5. package/lib/commands/affected.js +161 -286
  6. package/lib/commands/install.d.ts +63 -66
  7. package/lib/commands/install.js +372 -536
  8. package/lib/templates/install.mjs.tmpl +38 -68
  9. package/lib/types/cli-build-options.d.ts +33 -67
  10. package/lib/types/command.d.ts +9 -12
  11. package/lib/types/config-data.d.ts +125 -221
  12. package/lib/utils/auth-npmrc.d.ts +5 -10
  13. package/lib/utils/auth-npmrc.js +11 -21
  14. package/lib/utils/clear-targets.d.ts +8 -11
  15. package/lib/utils/clear-targets.js +22 -29
  16. package/lib/utils/copy-targets.d.ts +13 -19
  17. package/lib/utils/copy-targets.js +31 -44
  18. package/lib/utils/dep-aggregation.d.ts +7 -15
  19. package/lib/utils/dep-aggregation.js +9 -22
  20. package/lib/utils/detect-native-packages.d.ts +83 -116
  21. package/lib/utils/detect-native-packages.js +111 -149
  22. package/lib/utils/dir-link.d.ts +29 -48
  23. package/lib/utils/dir-link.js +33 -56
  24. package/lib/utils/discover-showcases.d.ts +6 -7
  25. package/lib/utils/discover-showcases.js +11 -15
  26. package/lib/utils/dlx-cache.d.ts +29 -38
  27. package/lib/utils/dlx-cache.js +53 -63
  28. package/lib/utils/force-exit.d.ts +0 -3
  29. package/lib/utils/force-exit.js +13 -24
  30. package/lib/utils/gi-search-path.d.ts +16 -24
  31. package/lib/utils/gi-search-path.js +54 -82
  32. package/lib/utils/gjs-bundle-guard.d.ts +5 -13
  33. package/lib/utils/gjs-bundle-guard.js +35 -57
  34. package/lib/utils/gjs-source-escape.d.ts +2 -2
  35. package/lib/utils/gjs-source-escape.js +21 -39
  36. package/lib/utils/gjsify-shim.d.ts +17 -20
  37. package/lib/utils/gjsify-shim.js +80 -120
  38. package/lib/utils/html-entry.d.ts +8 -12
  39. package/lib/utils/html-entry.js +19 -28
  40. package/lib/utils/install-backend-native.d.ts +123 -161
  41. package/lib/utils/install-backend-native.js +536 -737
  42. package/lib/utils/install-backend.d.ts +42 -57
  43. package/lib/utils/install-backend.js +16 -21
  44. package/lib/utils/install-cache-fs.d.ts +10 -15
  45. package/lib/utils/install-cache-fs.js +18 -26
  46. package/lib/utils/install-lock.d.ts +5 -6
  47. package/lib/utils/install-lock.js +46 -65
  48. package/lib/utils/install-packument-cache.d.ts +27 -33
  49. package/lib/utils/install-packument-cache.js +44 -57
  50. package/lib/utils/install-progress.d.ts +2 -9
  51. package/lib/utils/install-progress.js +14 -39
  52. package/lib/utils/install-tarball-cache.d.ts +14 -20
  53. package/lib/utils/install-tarball-cache.js +41 -59
  54. package/lib/utils/library-output.d.ts +9 -22
  55. package/lib/utils/library-output.js +19 -37
  56. package/lib/utils/load-npmrc.d.ts +5 -6
  57. package/lib/utils/load-npmrc.js +18 -27
  58. package/lib/utils/node-bundle-guard.d.ts +4 -8
  59. package/lib/utils/node-bundle-guard.js +21 -35
  60. package/lib/utils/node-script.js +33 -54
  61. package/lib/utils/node-version.d.ts +5 -14
  62. package/lib/utils/node-version.js +15 -24
  63. package/lib/utils/normalize-bundler-options.d.ts +7 -11
  64. package/lib/utils/normalize-bundler-options.js +24 -42
  65. package/lib/utils/npm-oidc.d.ts +23 -37
  66. package/lib/utils/npm-oidc.js +28 -53
  67. package/lib/utils/npm-otp-cache.d.ts +4 -4
  68. package/lib/utils/npm-otp-cache.js +20 -25
  69. package/lib/utils/npm-otp.d.ts +20 -30
  70. package/lib/utils/npm-otp.js +32 -51
  71. package/lib/utils/onboard-probe.d.ts +7 -13
  72. package/lib/utils/onboard-probe.js +16 -27
  73. package/lib/utils/parse-spec.d.ts +3 -3
  74. package/lib/utils/parse-spec.js +4 -5
  75. package/lib/utils/pkg-json-edit.d.ts +7 -12
  76. package/lib/utils/pkg-json-edit.js +13 -26
  77. package/lib/utils/pkg-json.d.ts +11 -18
  78. package/lib/utils/pkg-json.js +19 -28
  79. package/lib/utils/prefixed-output.d.ts +3 -4
  80. package/lib/utils/prefixed-output.js +13 -19
  81. package/lib/utils/prompt.d.ts +3 -4
  82. package/lib/utils/prompt.js +14 -18
  83. package/lib/utils/publish-diagnose.d.ts +11 -16
  84. package/lib/utils/publish-diagnose.js +15 -28
  85. package/lib/utils/publish-headers.d.ts +7 -11
  86. package/lib/utils/publish-headers.js +36 -65
  87. package/lib/utils/publishable-packages.d.ts +4 -6
  88. package/lib/utils/publishable-packages.js +14 -18
  89. package/lib/utils/resolve-gjs-entry.d.ts +5 -8
  90. package/lib/utils/resolve-gjs-entry.js +10 -27
  91. package/lib/utils/resolve-npm-package.d.ts +6 -11
  92. package/lib/utils/resolve-npm-package.js +29 -63
  93. package/lib/utils/resolve-plugin-by-name.d.ts +11 -19
  94. package/lib/utils/resolve-plugin-by-name.js +9 -14
  95. package/lib/utils/run-gjs.d.ts +16 -21
  96. package/lib/utils/run-gjs.js +31 -56
  97. package/lib/utils/run-lifecycle-script.d.ts +5 -9
  98. package/lib/utils/run-lifecycle-script.js +23 -42
  99. package/lib/utils/run-node.d.ts +21 -33
  100. package/lib/utils/run-node.js +43 -64
  101. package/lib/utils/runtimes.d.ts +25 -35
  102. package/lib/utils/runtimes.js +34 -46
  103. package/lib/utils/spawn.d.ts +32 -48
  104. package/lib/utils/spawn.js +51 -84
  105. package/lib/utils/stdin.d.ts +2 -2
  106. package/lib/utils/stdin.js +10 -25
  107. package/lib/utils/suggest.d.ts +6 -7
  108. package/lib/utils/suggest.js +8 -10
  109. package/lib/utils/system-gi.d.ts +18 -27
  110. package/lib/utils/system-gi.js +60 -114
  111. package/lib/utils/trust-registry.d.ts +4 -8
  112. package/lib/utils/trust-registry.js +7 -15
  113. package/lib/utils/win32-command.d.ts +8 -14
  114. package/lib/utils/win32-command.js +29 -43
  115. package/lib/utils/workspace-root.js +7 -12
  116. package/package.json +18 -18
@@ -1,11 +1,6 @@
1
- // Write/remove npm auth tokens in the user's `.npmrc` — the write side of
2
- // `load-npmrc.ts` (which only reads). Used by `gjsify login` / `gjsify logout`.
3
- //
4
- // npm stores the token under a "nerf-dart" key: the registry URL with the
5
- // protocol stripped, e.g. `//registry.npmjs.org/:_authToken=<token>`. We upsert
6
- // exactly that line in the userconfig file (`$NPM_CONFIG_USERCONFIG` or
7
- // `~/.npmrc`), preserving every other line, and chmod it to 0600 (it holds a
8
- // credential).
1
+ // The write side of `load-npmrc.ts`: `gjsify login`/`logout` upsert or drop one
2
+ // `_authToken` line in the userconfig `.npmrc` (`$NPM_CONFIG_USERCONFIG` or
3
+ // `~/.npmrc`), preserving every other line and chmod'ing the file to 0600.
9
4
  import { existsSync, readFileSync, writeFileSync, chmodSync, mkdirSync } from 'node:fs';
10
5
  import { homedir } from 'node:os';
11
6
  import { join, dirname } from 'node:path';
@@ -14,8 +9,9 @@ export function userconfigNpmrcPath() {
14
9
  return process.env.NPM_CONFIG_USERCONFIG || join(homedir(), '.npmrc');
15
10
  }
16
11
  /**
17
- * The npm "nerf-dart" key for a registry: the URL with the protocol removed,
18
- * keeping the host + path with a trailing slash — `//registry.npmjs.org/`.
12
+ * The npm "nerf-dart" key for a registry — the URL with the protocol removed,
13
+ * host + path, trailing slash: `//registry.npmjs.org/`. npm keys credentials by
14
+ * this, so the exact shape is what makes the token findable.
19
15
  */
20
16
  export function nerfDart(registry) {
21
17
  const u = new URL(registry);
@@ -42,10 +38,7 @@ function writeUserconfig(path, content) {
42
38
  /* non-POSIX filesystem */
43
39
  }
44
40
  }
45
- /**
46
- * Upsert `<nerf-dart>:_authToken=<token>` in the userconfig `.npmrc`, preserving
47
- * all other lines. Returns the file path written.
48
- */
41
+ /** Upsert `<nerf-dart>:_authToken=<token>`; returns the file path written. */
49
42
  export function writeAuthToken(registry, token) {
50
43
  const path = userconfigNpmrcPath();
51
44
  const key = authTokenKey(registry);
@@ -54,15 +47,15 @@ export function writeAuthToken(registry, token) {
54
47
  const lines = existing.length ? existing.split('\n') : [];
55
48
  let replaced = false;
56
49
  const next = lines.map((l) => {
57
- // Match the key at the start of the line (ignore `${...}` placeholder
58
- // values too — we always overwrite with the concrete token).
50
+ // Matches on the KEY only, so a `${VAR}` placeholder value is overwritten
51
+ // with the concrete token like any other.
59
52
  if (l.replace(/\s+/g, '').startsWith(`${key}=`)) {
60
53
  replaced = true;
61
54
  return line;
62
55
  }
63
56
  return l;
64
57
  });
65
- // Strip trailing blank lines so we don't accumulate them across rewrites.
58
+ // Or they accumulate across rewrites.
66
59
  while (next.length && next[next.length - 1].trim() === '')
67
60
  next.pop();
68
61
  if (!replaced)
@@ -70,10 +63,7 @@ export function writeAuthToken(registry, token) {
70
63
  writeUserconfig(path, next.join('\n') + '\n');
71
64
  return path;
72
65
  }
73
- /**
74
- * Remove the `<nerf-dart>:_authToken=` line for a registry from the userconfig
75
- * `.npmrc`. Returns `{ path, removed }` — `removed` is false if no line matched.
76
- */
66
+ /** Drop the registry's `_authToken` line; `removed` is false when none matched. */
77
67
  export function removeAuthToken(registry) {
78
68
  const path = userconfigNpmrcPath();
79
69
  const key = authTokenKey(registry);
@@ -8,9 +8,9 @@ export interface ClearContext {
8
8
  /**
9
9
  * Does this path segment carry a shell wildcard?
10
10
  *
11
- * `*` and `?` only — what `rm -rf *.log` used. Deliberately NOT a glob engine:
12
- * a `clear` script names build output, and `**` or character classes there would
13
- * let a line delete more than its author can see at a glance.
11
+ * `*` and `?` only — what `rm -rf *.log` used. Deliberately NOT a glob engine: a `clear` script
12
+ * names build output, and `**` or character classes would let a line delete more than its
13
+ * author can see at a glance.
14
14
  */
15
15
  export declare function hasWildcard(segment: string): boolean;
16
16
  /** Compile one wildcard segment to an anchored regex, escaping every other metachar. */
@@ -18,13 +18,10 @@ export declare function segmentToRegExp(segment: string): RegExp;
18
18
  /**
19
19
  * Expand and validate every target, resolving wildcards against `readdir`.
20
20
  *
21
- * ALL targets are resolved before the caller deletes any of them — a typo in the
22
- * fourth path must not be discovered after the first three are gone.
23
- *
24
- * A wildcard is honoured in the LAST segment only, matched against a single
25
- * directory read: the shell semantics the rewritten scripts need, with none of
26
- * the recursive traversal. A pattern matching nothing yields no targets, which
27
- * is `rm -rf`'s behaviour under `--force` and why a package that was never built
28
- * clears cleanly.
21
+ * ALL targets are resolved before the caller deletes any — a typo in the fourth path must not
22
+ * be discovered after the first three are gone. A wildcard is honoured in the LAST segment
23
+ * only, matched against a single directory read: shell semantics without recursive traversal.
24
+ * A pattern matching nothing yields no targets, which is `rm -rf --force`'s behaviour and why a
25
+ * package that was never built clears cleanly.
29
26
  */
30
27
  export declare function resolveClearTargets(paths: readonly string[], ctx: ClearContext): string[];
@@ -1,20 +1,19 @@
1
1
  // Target resolution for `gjsify clear` — the safety-critical half, kept pure.
2
2
  //
3
- // A recursive delete driven by strings from 224 package.json files is a footgun,
4
- // so the "which paths does this actually delete" decision lives here, with the
5
- // directory read INJECTED. That makes every rule below unit-testable without a
6
- // real filesystem, which is the point: the tests that matter are the ones for
7
- // paths that must be REFUSED, and creating a fixture outside the cwd to prove
8
- // that is exactly what a test must not do.
3
+ // A recursive delete driven by strings from every workspace's package.json is a footgun, so
4
+ // the "which paths does this actually delete" decision lives here with the directory read
5
+ // INJECTED. That makes every rule below unit-testable without a real filesystem, which is the
6
+ // point: the tests that matter are the ones for paths that must be REFUSED, and creating a
7
+ // fixture outside the cwd to prove that is exactly what a test must not do.
9
8
  //
10
9
  // See `commands/clear.ts` for why the command exists at all.
11
10
  import { basename, dirname, isAbsolute, join, relative, resolve, sep } from 'node:path';
12
11
  /**
13
12
  * Does this path segment carry a shell wildcard?
14
13
  *
15
- * `*` and `?` only — what `rm -rf *.log` used. Deliberately NOT a glob engine:
16
- * a `clear` script names build output, and `**` or character classes there would
17
- * let a line delete more than its author can see at a glance.
14
+ * `*` and `?` only — what `rm -rf *.log` used. Deliberately NOT a glob engine: a `clear` script
15
+ * names build output, and `**` or character classes would let a line delete more than its
16
+ * author can see at a glance.
18
17
  */
19
18
  export function hasWildcard(segment) {
20
19
  return segment.includes('*') || segment.includes('?');
@@ -30,9 +29,8 @@ export function segmentToRegExp(segment) {
30
29
  /**
31
30
  * Reject a target that is not strictly inside `cwd`.
32
31
  *
33
- * A hard error, never a skip: a typo that would otherwise delete a sibling
34
- * package — or `$HOME` — must not depend on anyone reading a warning in the
35
- * middle of a 224-package `foreach` run.
32
+ * A hard error, never a skip: a typo that would otherwise delete a sibling package — or
33
+ * `$HOME` — must not depend on anyone reading a warning mid-`foreach` across every workspace.
36
34
  */
37
35
  function assertInsideCwd(absolute, original, cwd) {
38
36
  const rel = relative(cwd, absolute);
@@ -47,14 +45,11 @@ function assertInsideCwd(absolute, original, cwd) {
47
45
  /**
48
46
  * Expand and validate every target, resolving wildcards against `readdir`.
49
47
  *
50
- * ALL targets are resolved before the caller deletes any of them — a typo in the
51
- * fourth path must not be discovered after the first three are gone.
52
- *
53
- * A wildcard is honoured in the LAST segment only, matched against a single
54
- * directory read: the shell semantics the rewritten scripts need, with none of
55
- * the recursive traversal. A pattern matching nothing yields no targets, which
56
- * is `rm -rf`'s behaviour under `--force` and why a package that was never built
57
- * clears cleanly.
48
+ * ALL targets are resolved before the caller deletes any — a typo in the fourth path must not
49
+ * be discovered after the first three are gone. A wildcard is honoured in the LAST segment
50
+ * only, matched against a single directory read: shell semantics without recursive traversal.
51
+ * A pattern matching nothing yields no targets, which is `rm -rf --force`'s behaviour and why a
52
+ * package that was never built clears cleanly.
58
53
  */
59
54
  export function resolveClearTargets(paths, ctx) {
60
55
  const out = [];
@@ -73,17 +68,16 @@ export function resolveClearTargets(paths, ctx) {
73
68
  function expandTarget(target, ctx) {
74
69
  const leaf = basename(target);
75
70
  const parent = dirname(target);
76
- // Checked BEFORE the no-wildcard shortcut: `*/dist` has a wildcard-free
77
- // basename, and treating it as a literal path would silently delete nothing
78
- // instead of telling the author the shape is unsupported.
71
+ // Checked BEFORE the no-wildcard shortcut: `*/dist` has a wildcard-free basename, and
72
+ // treating it as a literal path would silently delete nothing instead of telling the
73
+ // author the shape is unsupported.
79
74
  if (hasWildcard(parent)) {
80
75
  throw new Error(`gjsify clear: a wildcard is only supported in the last path segment — got "${target}".`);
81
76
  }
82
77
  if (!hasWildcard(leaf))
83
78
  return [resolve(ctx.cwd, target)];
84
- // The parent is validated too: `../*.log` must be refused, and it is only
85
- // refused if the containing DIRECTORY is checked against cwd before the
86
- // expansion — the matches themselves are all inside it.
79
+ // The parent is validated too: `../*.log` is only refused if the containing DIRECTORY is
80
+ // checked against cwd before expansion — the matches themselves are all inside it.
87
81
  const dir = resolve(ctx.cwd, parent);
88
82
  assertInsideCwdOrSelf(dir, target, ctx.cwd);
89
83
  const re = segmentToRegExp(leaf);
@@ -92,9 +86,8 @@ function expandTarget(target, ctx) {
92
86
  names = ctx.readdir(dir);
93
87
  }
94
88
  catch {
95
- // The containing directory is absent or unreadable — nothing to clear,
96
- // like `rm -rf` on a missing path. A permission problem on an EXISTING
97
- // target still surfaces from the delete itself.
89
+ // Absent or unreadable containing directory — nothing to clear, like `rm -rf` on a
90
+ // missing path. A permission problem on an EXISTING target still surfaces from the delete.
98
91
  return [];
99
92
  }
100
93
  return names
@@ -13,29 +13,23 @@ export interface CopyOperation {
13
13
  /**
14
14
  * Is the destination a DIRECTORY to copy into, rather than the target path?
15
15
  *
16
- * Decided from the ARGUMENTS ALONE — never from what is on disk. `cp` decides it
17
- * by stat'ing the destination, which is why `cp -r src/assets dist/res` copies
18
- * the directory to `dist/res` the first time and to `dist/res/assets` the
19
- * second: the same script means two different things depending on whether it has
20
- * run before. Four of the scripts this command replaces carry exactly that shape,
21
- * so they were already non-idempotent on Linux. A build step must not be.
16
+ * Decided from the ARGUMENTS ALONE — never from what is on disk. `cp` decides it by stat'ing the
17
+ * destination, which is why `cp -r src/assets dist/res` copies the directory to `dist/res` the
18
+ * first time and to `dist/res/assets` the second: the same script means two things depending on
19
+ * whether it has run before. Several scripts this command replaces carried that shape and were
20
+ * already non-idempotent on Linux. A build step must not be.
22
21
  *
23
- * The rule, in full:
24
- * - a trailing `/` (or `\`) on the destination — the explicit spelling;
25
- * - more than one source;
26
- * - any source carrying a wildcard.
22
+ * Directory mode iff: a trailing `/` (or `\`) on the destination — the explicit spelling; more
23
+ * than one source; or any source carrying a wildcard.
27
24
  *
28
- * The last two are what keep the two platforms in agreement. A POSIX shell
29
- * expands `src/public/*` before gjsify is started (many sources); `cmd.exe` does
30
- * not expand anything and passes the pattern through (one source, a wildcard).
31
- * Both land in directory mode, so the same script means the same thing on both.
25
+ * The last two keep the platforms in agreement: a POSIX shell expands `src/public/*` before
26
+ * gjsify starts (many sources), while `cmd.exe` passes the pattern through (one source, a
27
+ * wildcard). Both land in directory mode, so the same script means the same thing on both.
32
28
  */
33
29
  export declare function isDirectoryDestination(sources: readonly string[], dest: string): boolean;
34
30
  /**
35
- * Resolve `gjsify copy <sources…> <destination>` into the exact list of copies.
36
- *
37
- * ALL operations are planned before the caller performs any of them, so a bad
38
- * argument in position four cannot be discovered after three files have already
39
- * been written.
31
+ * Resolve `gjsify copy <sources…> <destination>` into the exact list of copies. ALL operations are
32
+ * planned before the caller performs any, so a bad argument in position four cannot be discovered
33
+ * after three files have been written.
40
34
  */
41
35
  export declare function planCopy(args: readonly string[], ctx: CopyContext): CopyOperation[];
@@ -1,9 +1,9 @@
1
1
  // Target resolution for `gjsify copy` — the deciding half, kept pure.
2
2
  //
3
- // Same split as `clear-targets.ts`, for the same reason: the paths come from
4
- // package.json files rather than from a human at a prompt, so "which file lands
5
- // where" must be testable without a filesystem — including the cases that must
6
- // be REFUSED, which cannot be proven by writing outside the working directory.
3
+ // Same split as `clear-targets.ts`, for the same reason: the paths come from package.json files
4
+ // rather than from a human at a prompt, so "which file lands where" must be testable without a
5
+ // filesystem — including the cases that must be REFUSED, which cannot be proven by writing
6
+ // outside the working directory.
7
7
  //
8
8
  // See `commands/copy.ts` for why the command exists at all.
9
9
  import { basename, dirname, isAbsolute, join, relative, resolve, sep } from 'node:path';
@@ -11,22 +11,18 @@ import { hasWildcard, segmentToRegExp } from './clear-targets.js';
11
11
  /**
12
12
  * Is the destination a DIRECTORY to copy into, rather than the target path?
13
13
  *
14
- * Decided from the ARGUMENTS ALONE — never from what is on disk. `cp` decides it
15
- * by stat'ing the destination, which is why `cp -r src/assets dist/res` copies
16
- * the directory to `dist/res` the first time and to `dist/res/assets` the
17
- * second: the same script means two different things depending on whether it has
18
- * run before. Four of the scripts this command replaces carry exactly that shape,
19
- * so they were already non-idempotent on Linux. A build step must not be.
14
+ * Decided from the ARGUMENTS ALONE — never from what is on disk. `cp` decides it by stat'ing the
15
+ * destination, which is why `cp -r src/assets dist/res` copies the directory to `dist/res` the
16
+ * first time and to `dist/res/assets` the second: the same script means two things depending on
17
+ * whether it has run before. Several scripts this command replaces carried that shape and were
18
+ * already non-idempotent on Linux. A build step must not be.
20
19
  *
21
- * The rule, in full:
22
- * - a trailing `/` (or `\`) on the destination — the explicit spelling;
23
- * - more than one source;
24
- * - any source carrying a wildcard.
20
+ * Directory mode iff: a trailing `/` (or `\`) on the destination — the explicit spelling; more
21
+ * than one source; or any source carrying a wildcard.
25
22
  *
26
- * The last two are what keep the two platforms in agreement. A POSIX shell
27
- * expands `src/public/*` before gjsify is started (many sources); `cmd.exe` does
28
- * not expand anything and passes the pattern through (one source, a wildcard).
29
- * Both land in directory mode, so the same script means the same thing on both.
23
+ * The last two keep the platforms in agreement: a POSIX shell expands `src/public/*` before
24
+ * gjsify starts (many sources), while `cmd.exe` passes the pattern through (one source, a
25
+ * wildcard). Both land in directory mode, so the same script means the same thing on both.
30
26
  */
31
27
  export function isDirectoryDestination(sources, dest) {
32
28
  if (dest.endsWith('/') || dest.endsWith('\\'))
@@ -38,15 +34,13 @@ export function isDirectoryDestination(sources, dest) {
38
34
  /**
39
35
  * Reject a destination that is not inside `cwd`.
40
36
  *
41
- * A hard error, never a skip — the same posture as `clear`, for the mirrored
42
- * reason: `clear` must not DELETE outside the package, `copy` must not WRITE
43
- * outside it. `allowSelf` covers `gjsify copy ../shared/x.wasm ./`, where the
37
+ * A hard error, never a skip — mirroring `clear`: it must not DELETE outside the package, `copy`
38
+ * must not WRITE outside it. `allowSelf` covers `gjsify copy ../shared/x.wasm ./`, where the
44
39
  * package root legitimately IS the destination directory.
45
40
  *
46
- * SOURCES are deliberately NOT checked. Three of the scripts this replaces read
47
- * from a sibling package (`../../../packages/infra/lightningcss-wasm/…`), which
48
- * is the normal shape for staging a workspace artifact, and reading is not the
49
- * dangerous direction.
41
+ * SOURCES are deliberately NOT checked: reading from a sibling package
42
+ * (`../../../packages/infra/lightningcss-wasm/…`) is the normal shape for staging a workspace
43
+ * artifact, and reading is not the dangerous direction.
50
44
  */
51
45
  function assertWritableDestination(absolute, original, cwd, allowSelf) {
52
46
  const rel = relative(cwd, absolute);
@@ -62,15 +56,12 @@ function assertWritableDestination(absolute, original, cwd, allowSelf) {
62
56
  }
63
57
  }
64
58
  /**
65
- * Expand one source argument. A wildcard is honoured in the LAST segment only,
66
- * against a single directory read — the same restricted grammar `clear` uses,
67
- * and for the same reason: a build script names files, and `**` there would copy
68
- * more than its author can see at a glance.
59
+ * Expand one source argument. A wildcard is honoured in the LAST segment only, against a single
60
+ * directory read — the same restricted grammar `clear` uses, and for the same reason.
69
61
  *
70
- * A pattern matching nothing yields NO operations. A literal path that does not
71
- * exist yields one operation, which then fails loudly in the copy itself — the
72
- * asymmetry is deliberate and mirrors the shell: `cp missing.html dist/` is an
73
- * error, `cp *.html dist/` on an empty directory is not.
62
+ * A pattern matching nothing yields NO operations; a literal path that does not exist yields one,
63
+ * which then fails loudly in the copy itself. The asymmetry mirrors the shell: `cp missing.html
64
+ * dist/` is an error, `cp *.html dist/` on an empty directory is not.
74
65
  */
75
66
  function expandSource(source, ctx) {
76
67
  const leaf = basename(source);
@@ -95,11 +86,9 @@ function expandSource(source, ctx) {
95
86
  .map((name) => join(dir, name));
96
87
  }
97
88
  /**
98
- * Resolve `gjsify copy <sources…> <destination>` into the exact list of copies.
99
- *
100
- * ALL operations are planned before the caller performs any of them, so a bad
101
- * argument in position four cannot be discovered after three files have already
102
- * been written.
89
+ * Resolve `gjsify copy <sources…> <destination>` into the exact list of copies. ALL operations are
90
+ * planned before the caller performs any, so a bad argument in position four cannot be discovered
91
+ * after three files have been written.
103
92
  */
104
93
  export function planCopy(args, ctx) {
105
94
  if (args.length < 2) {
@@ -120,9 +109,8 @@ export function planCopy(args, ctx) {
120
109
  expanded.push(...expandSource(source, ctx));
121
110
  }
122
111
  if (!intoDirectory && expanded.length !== 1) {
123
- // Only reachable when a single wildcard-free source expanded to something
124
- // other than one path, which cannot happen — but leaving it unstated would
125
- // make a future change to `expandSource` silently copy the wrong file.
112
+ // Unreachable today (a single wildcard-free source always expands to one path), but
113
+ // leaving it unstated would let a future `expandSource` change silently copy the wrong file.
126
114
  throw new Error(`gjsify copy: "${dest}" names a single destination, but ${expanded.length} sources resolved.`);
127
115
  }
128
116
  const ops = [];
@@ -133,9 +121,8 @@ export function planCopy(args, ctx) {
133
121
  throw new Error(`gjsify copy: "${from}" is its own destination.`);
134
122
  }
135
123
  if (seenDest.has(to)) {
136
- // Two sources with the same basename copied into one directory: the
137
- // second silently overwrites the first, and which one wins depends on
138
- // argument order. Name it instead.
124
+ // Two sources with the same basename into one directory: the second would silently
125
+ // overwrite the first, and which wins depends on argument order.
139
126
  throw new Error(`gjsify copy: two sources would both be written to ${to}.`);
140
127
  }
141
128
  seenDest.add(to);
@@ -4,40 +4,32 @@ export interface DepDeclaration {
4
4
  workspace: string;
5
5
  /** Absolute path to the workspace's `package.json`. */
6
6
  workspaceLocation: string;
7
- /** Dependency name (e.g. `rolldown`). */
8
7
  name: string;
9
- /** Which manifest field this declaration lives in. */
10
8
  field: 'dependencies' | 'devDependencies' | 'optionalDependencies' | 'peerDependencies';
11
9
  /** The original range string (e.g. `^1.0.4`). */
12
10
  currentRange: string;
13
- /** The parsed version (max-satisfying numeric) inside the range, or `null`. */
11
+ /** Max-satisfying numeric version inside the range, or `null` if unparseable. */
14
12
  currentVersion: string | null;
15
13
  /** Range prefix preserved on write-back (`^`, `~`, `>=`, …; `""` for literal). */
16
14
  prefix: string;
17
15
  }
18
16
  /** Aggregated view of one external dep across all workspaces that declare it. */
19
17
  export interface DependencyGroup {
20
- /** Dependency name. */
21
18
  name: string;
22
- /** Every declaration of this dep across the workspace. */
23
19
  occurrences: DepDeclaration[];
24
- /** Set of all unique declared ranges (size > 1 → inconsistency). */
20
+ /** Unique declared ranges — size > 1 IS the inconsistency. */
25
21
  declaredRanges: Set<string>;
26
- /** Range string declared by the most workspaces (the de-facto consensus). */
22
+ /** Range declared by the most workspaces: the de-facto consensus. */
27
23
  dominantRange: string;
28
- /** When ranges disagree, the highest declared semver version across them. */
24
+ /** Highest declared semver across the group. */
29
25
  highestVersion: string | null;
30
26
  }
31
27
  /**
32
- * Group a flat list of `DepDeclaration`s by dep name. Each group has the full
33
- * occurrence list plus pre-computed aggregates (consensus range, highest
34
- * declared version) so callers don't have to re-scan.
28
+ * Group a flat list of `DepDeclaration`s by dep name, pre-computing the aggregates
29
+ * so no caller has to re-scan the occurrences.
35
30
  */
36
31
  export declare function groupByDependency(decls: readonly DepDeclaration[]): DependencyGroup[];
37
32
  /** `true` when the group's workspaces declare the dep at >1 distinct range. */
38
33
  export declare function isInconsistent(group: DependencyGroup): boolean;
39
- /**
40
- * Filter to just the inconsistent groups (>1 distinct range across workspaces).
41
- * Convenience wrapper for `--align` and `--check` modes.
42
- */
34
+ /** The inconsistent groups only — what `--align` and `--check` operate on. */
43
35
  export declare function findInconsistencies(groups: readonly DependencyGroup[]): DependencyGroup[];
@@ -1,16 +1,8 @@
1
- // Workspace-aware dependency aggregation for `gjsify upgrade`.
2
- //
3
- // Given a flat list of per-workspace dependency declarations, group them by
4
- // package name and surface inconsistencies (the same package declared at
5
- // different version ranges across workspaces). Used by:
6
- //
7
- // - `gjsify upgrade` interactive table — shows fan-out (count of workspaces
8
- // declaring the dep) and flags inconsistent declarations with `⚠`.
9
- // - `gjsify upgrade --align` — offline mode that detects inconsistencies
10
- // and proposes a single range (the highest semver-satisfying one) without
11
- // hitting the registry.
12
- // - `gjsify upgrade --check` — CI gate that exits non-zero when any
13
- // inconsistency exists.
1
+ // Workspace-aware dependency aggregation for `gjsify upgrade`: group per-workspace
2
+ // declarations by package name and surface inconsistencies — the same package
3
+ // declared at different ranges across workspaces. Drives the interactive table's
4
+ // fan-out column, `--align` (propose one range offline, no registry hit) and
5
+ // `--check` (CI gate, non-zero on any inconsistency).
14
6
  import { compare, parse } from '@gjsify/semver';
15
7
  /** Local `gt` shim: `@gjsify/semver` exports `compare` returning -1|0|1. */
16
8
  function semverGt(a, b) {
@@ -22,9 +14,8 @@ function semverGt(a, b) {
22
14
  }
23
15
  }
24
16
  /**
25
- * Group a flat list of `DepDeclaration`s by dep name. Each group has the full
26
- * occurrence list plus pre-computed aggregates (consensus range, highest
27
- * declared version) so callers don't have to re-scan.
17
+ * Group a flat list of `DepDeclaration`s by dep name, pre-computing the aggregates
18
+ * so no caller has to re-scan the occurrences.
28
19
  */
29
20
  export function groupByDependency(decls) {
30
21
  const map = new Map();
@@ -48,8 +39,7 @@ export function groupByDependency(decls) {
48
39
  }
49
40
  }
50
41
  }
51
- // Second pass: dominantRange = the range with the most occurrences;
52
- // ties resolve by highest semver, then by lexicographic order.
42
+ // dominantRange = most occurrences; ties break on highest semver, then lexically.
53
43
  for (const g of map.values()) {
54
44
  const counts = new Map();
55
45
  for (const occ of g.occurrences) {
@@ -77,10 +67,7 @@ export function groupByDependency(decls) {
77
67
  export function isInconsistent(group) {
78
68
  return group.declaredRanges.size > 1;
79
69
  }
80
- /**
81
- * Filter to just the inconsistent groups (>1 distinct range across workspaces).
82
- * Convenience wrapper for `--align` and `--check` modes.
83
- */
70
+ /** The inconsistent groups only — what `--align` and `--check` operate on. */
84
71
  export function findInconsistencies(groups) {
85
72
  return groups.filter(isInconsistent);
86
73
  }