@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.
- package/dist/cli.gjs.mjs +3 -3
- package/lib/bundler-pick.js +61 -87
- package/lib/cli-fail.d.ts +4 -7
- package/lib/cli-fail.js +29 -55
- package/lib/commands/affected.js +161 -286
- package/lib/commands/install.d.ts +63 -66
- package/lib/commands/install.js +372 -536
- package/lib/templates/install.mjs.tmpl +38 -68
- package/lib/types/cli-build-options.d.ts +33 -67
- package/lib/types/command.d.ts +9 -12
- package/lib/types/config-data.d.ts +125 -221
- package/lib/utils/auth-npmrc.d.ts +5 -10
- package/lib/utils/auth-npmrc.js +11 -21
- package/lib/utils/clear-targets.d.ts +8 -11
- package/lib/utils/clear-targets.js +22 -29
- package/lib/utils/copy-targets.d.ts +13 -19
- package/lib/utils/copy-targets.js +31 -44
- package/lib/utils/dep-aggregation.d.ts +7 -15
- package/lib/utils/dep-aggregation.js +9 -22
- package/lib/utils/detect-native-packages.d.ts +83 -116
- package/lib/utils/detect-native-packages.js +111 -149
- package/lib/utils/dir-link.d.ts +29 -48
- package/lib/utils/dir-link.js +33 -56
- package/lib/utils/discover-showcases.d.ts +6 -7
- package/lib/utils/discover-showcases.js +11 -15
- package/lib/utils/dlx-cache.d.ts +29 -38
- package/lib/utils/dlx-cache.js +53 -63
- package/lib/utils/force-exit.d.ts +0 -3
- package/lib/utils/force-exit.js +13 -24
- package/lib/utils/gi-search-path.d.ts +16 -24
- package/lib/utils/gi-search-path.js +54 -82
- package/lib/utils/gjs-bundle-guard.d.ts +5 -13
- package/lib/utils/gjs-bundle-guard.js +35 -57
- package/lib/utils/gjs-source-escape.d.ts +2 -2
- package/lib/utils/gjs-source-escape.js +21 -39
- package/lib/utils/gjsify-shim.d.ts +17 -20
- package/lib/utils/gjsify-shim.js +80 -120
- package/lib/utils/html-entry.d.ts +8 -12
- package/lib/utils/html-entry.js +19 -28
- package/lib/utils/install-backend-native.d.ts +123 -161
- package/lib/utils/install-backend-native.js +536 -737
- package/lib/utils/install-backend.d.ts +42 -57
- package/lib/utils/install-backend.js +16 -21
- package/lib/utils/install-cache-fs.d.ts +10 -15
- package/lib/utils/install-cache-fs.js +18 -26
- package/lib/utils/install-lock.d.ts +5 -6
- package/lib/utils/install-lock.js +46 -65
- package/lib/utils/install-packument-cache.d.ts +27 -33
- package/lib/utils/install-packument-cache.js +44 -57
- package/lib/utils/install-progress.d.ts +2 -9
- package/lib/utils/install-progress.js +14 -39
- package/lib/utils/install-tarball-cache.d.ts +14 -20
- package/lib/utils/install-tarball-cache.js +41 -59
- package/lib/utils/library-output.d.ts +9 -22
- package/lib/utils/library-output.js +19 -37
- package/lib/utils/load-npmrc.d.ts +5 -6
- package/lib/utils/load-npmrc.js +18 -27
- package/lib/utils/node-bundle-guard.d.ts +4 -8
- package/lib/utils/node-bundle-guard.js +21 -35
- package/lib/utils/node-script.js +33 -54
- package/lib/utils/node-version.d.ts +5 -14
- package/lib/utils/node-version.js +15 -24
- package/lib/utils/normalize-bundler-options.d.ts +7 -11
- package/lib/utils/normalize-bundler-options.js +24 -42
- package/lib/utils/npm-oidc.d.ts +23 -37
- package/lib/utils/npm-oidc.js +28 -53
- package/lib/utils/npm-otp-cache.d.ts +4 -4
- package/lib/utils/npm-otp-cache.js +20 -25
- package/lib/utils/npm-otp.d.ts +20 -30
- package/lib/utils/npm-otp.js +32 -51
- package/lib/utils/onboard-probe.d.ts +7 -13
- package/lib/utils/onboard-probe.js +16 -27
- package/lib/utils/parse-spec.d.ts +3 -3
- package/lib/utils/parse-spec.js +4 -5
- package/lib/utils/pkg-json-edit.d.ts +7 -12
- package/lib/utils/pkg-json-edit.js +13 -26
- package/lib/utils/pkg-json.d.ts +11 -18
- package/lib/utils/pkg-json.js +19 -28
- package/lib/utils/prefixed-output.d.ts +3 -4
- package/lib/utils/prefixed-output.js +13 -19
- package/lib/utils/prompt.d.ts +3 -4
- package/lib/utils/prompt.js +14 -18
- package/lib/utils/publish-diagnose.d.ts +11 -16
- package/lib/utils/publish-diagnose.js +15 -28
- package/lib/utils/publish-headers.d.ts +7 -11
- package/lib/utils/publish-headers.js +36 -65
- package/lib/utils/publishable-packages.d.ts +4 -6
- package/lib/utils/publishable-packages.js +14 -18
- package/lib/utils/resolve-gjs-entry.d.ts +5 -8
- package/lib/utils/resolve-gjs-entry.js +10 -27
- package/lib/utils/resolve-npm-package.d.ts +6 -11
- package/lib/utils/resolve-npm-package.js +29 -63
- package/lib/utils/resolve-plugin-by-name.d.ts +11 -19
- package/lib/utils/resolve-plugin-by-name.js +9 -14
- package/lib/utils/run-gjs.d.ts +16 -21
- package/lib/utils/run-gjs.js +31 -56
- package/lib/utils/run-lifecycle-script.d.ts +5 -9
- package/lib/utils/run-lifecycle-script.js +23 -42
- package/lib/utils/run-node.d.ts +21 -33
- package/lib/utils/run-node.js +43 -64
- package/lib/utils/runtimes.d.ts +25 -35
- package/lib/utils/runtimes.js +34 -46
- package/lib/utils/spawn.d.ts +32 -48
- package/lib/utils/spawn.js +51 -84
- package/lib/utils/stdin.d.ts +2 -2
- package/lib/utils/stdin.js +10 -25
- package/lib/utils/suggest.d.ts +6 -7
- package/lib/utils/suggest.js +8 -10
- package/lib/utils/system-gi.d.ts +18 -27
- package/lib/utils/system-gi.js +60 -114
- package/lib/utils/trust-registry.d.ts +4 -8
- package/lib/utils/trust-registry.js +7 -15
- package/lib/utils/win32-command.d.ts +8 -14
- package/lib/utils/win32-command.js +29 -43
- package/lib/utils/workspace-root.js +7 -12
- package/package.json +18 -18
package/lib/utils/auth-npmrc.js
CHANGED
|
@@ -1,11 +1,6 @@
|
|
|
1
|
-
//
|
|
2
|
-
// `
|
|
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
|
|
18
|
-
*
|
|
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
|
-
//
|
|
58
|
-
//
|
|
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
|
-
//
|
|
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
|
-
*
|
|
13
|
-
*
|
|
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
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
* A
|
|
25
|
-
*
|
|
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
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
//
|
|
7
|
-
//
|
|
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
|
-
*
|
|
17
|
-
*
|
|
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
|
-
*
|
|
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
|
|
51
|
-
*
|
|
52
|
-
*
|
|
53
|
-
* A
|
|
54
|
-
*
|
|
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
|
-
//
|
|
78
|
-
//
|
|
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`
|
|
85
|
-
//
|
|
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
|
-
//
|
|
96
|
-
//
|
|
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
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
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
|
-
*
|
|
24
|
-
*
|
|
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
|
|
29
|
-
*
|
|
30
|
-
*
|
|
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
|
-
*
|
|
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
|
-
//
|
|
5
|
-
//
|
|
6
|
-
//
|
|
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
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
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
|
-
*
|
|
22
|
-
*
|
|
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
|
|
27
|
-
*
|
|
28
|
-
*
|
|
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 —
|
|
42
|
-
*
|
|
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
|
|
47
|
-
*
|
|
48
|
-
*
|
|
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
|
-
*
|
|
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
|
|
71
|
-
*
|
|
72
|
-
*
|
|
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
|
-
*
|
|
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
|
-
//
|
|
124
|
-
//
|
|
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
|
|
137
|
-
//
|
|
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
|
-
/**
|
|
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
|
-
/**
|
|
20
|
+
/** Unique declared ranges — size > 1 IS the inconsistency. */
|
|
25
21
|
declaredRanges: Set<string>;
|
|
26
|
-
/** Range
|
|
22
|
+
/** Range declared by the most workspaces: the de-facto consensus. */
|
|
27
23
|
dominantRange: string;
|
|
28
|
-
/**
|
|
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
|
|
33
|
-
*
|
|
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
|
-
//
|
|
4
|
-
//
|
|
5
|
-
//
|
|
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
|
|
26
|
-
*
|
|
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
|
-
//
|
|
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
|
}
|