@vltpkg/cli-sdk 1.3.8 → 1.4.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/commands/cache.d.ts +2 -2
- package/dist/commands/cache.js +22 -7
- package/dist/commands/init.js +4 -6
- package/dist/commands/install.js +6 -0
- package/dist/commands/pack.js +2 -1
- package/dist/commands/pkg.js +4 -1
- package/dist/commands/publish.js +4 -1
- package/dist/commands/version.js +4 -1
- package/dist/config/definition.d.ts +6 -2
- package/dist/config/definition.js +26 -10
- package/dist/config/index.d.ts +4 -0
- package/dist/config/index.js +22 -6
- package/dist/config/spec-aliases.d.ts +22 -0
- package/dist/config/spec-aliases.js +47 -0
- package/dist/exec-command.js +12 -6
- package/dist/index.js +12 -8
- package/dist/output.js +2 -1
- package/dist/save-types.d.ts +20 -0
- package/dist/save-types.js +105 -0
- package/dist/sort-scope-locations.d.ts +11 -0
- package/dist/sort-scope-locations.js +57 -0
- package/package.json +28 -28
package/dist/commands/cache.d.ts
CHANGED
|
@@ -56,8 +56,8 @@ declare const usageDef: {
|
|
|
56
56
|
readonly description: "Check global store entries against their cached\n tarballs (file list, contents, exec bits) and remove\n any that differ or have no cached tarball, e.g. after\n a file in `node_modules` was edited in place.";
|
|
57
57
|
};
|
|
58
58
|
readonly 'prune-store': {
|
|
59
|
-
readonly usage: "";
|
|
60
|
-
readonly description: "Remove global store entries that no `node_modules`\n folder links to.
|
|
59
|
+
readonly usage: "[<date>]";
|
|
60
|
+
readonly description: "Remove global store entries that no `node_modules`\n folder links to. Copies do not show use: an entry\n copied or cloned from (install scripts,\n `store-linker=copy`, macOS, a cache on another\n drive) stays if that last happened after\n `<date>`, or in the last 30 days\n without one.";
|
|
61
61
|
};
|
|
62
62
|
};
|
|
63
63
|
readonly examples: {
|
package/dist/commands/cache.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { error } from '@vltpkg/error-cause';
|
|
2
2
|
import { CacheEntry, assertOk } from '@vltpkg/registry-client';
|
|
3
3
|
import { Spec } from '@vltpkg/spec';
|
|
4
|
-
import { removeStoreEntry, storeEntryCopied, storeEntryLinked, storeEntryNames, storeEntryTime, verifyStoreEntry, } from '@vltpkg/tar/store-entry';
|
|
4
|
+
import { removeStoreEntry, storeEntryCopied, storeEntryLastCopied, storeEntryLinked, storeEntryNames, storeEntryTime, verifyStoreEntry, } from '@vltpkg/tar/store-entry';
|
|
5
5
|
import { readStoreIndex } from '@vltpkg/tar/store-index';
|
|
6
6
|
import { integrityHex } from '@vltpkg/types';
|
|
7
7
|
import { readFileSync } from 'node:fs';
|
|
@@ -24,6 +24,8 @@ export class CacheView extends ViewClass {
|
|
|
24
24
|
export const views = {
|
|
25
25
|
human: CacheView,
|
|
26
26
|
};
|
|
27
|
+
/** `prune-store`: days a copied entry stays, by default */
|
|
28
|
+
const pruneDays = 30;
|
|
27
29
|
const usageDef = {
|
|
28
30
|
command: 'cache',
|
|
29
31
|
usage: '<command> [flags]',
|
|
@@ -76,11 +78,14 @@ const usageDef = {
|
|
|
76
78
|
a file in \`node_modules\` was edited in place.`,
|
|
77
79
|
},
|
|
78
80
|
'prune-store': {
|
|
79
|
-
usage: '',
|
|
81
|
+
usage: '[<date>]',
|
|
80
82
|
description: `Remove global store entries that no \`node_modules\`
|
|
81
|
-
folder links to.
|
|
82
|
-
|
|
83
|
-
|
|
83
|
+
folder links to. Copies do not show use: an entry
|
|
84
|
+
copied or cloned from (install scripts,
|
|
85
|
+
\`store-linker=copy\`, macOS, a cache on another
|
|
86
|
+
drive) stays if that last happened after
|
|
87
|
+
\`<date>\`, or in the last ${pruneDays} days
|
|
88
|
+
without one.`,
|
|
84
89
|
},
|
|
85
90
|
},
|
|
86
91
|
examples: {
|
|
@@ -335,14 +340,24 @@ const verify = async (conf, specs, view) => {
|
|
|
335
340
|
...(!all && { missing }),
|
|
336
341
|
};
|
|
337
342
|
};
|
|
338
|
-
const pruneStore = async (conf,
|
|
343
|
+
const pruneStore = async (conf, args, view) => {
|
|
344
|
+
const since = args.length ?
|
|
345
|
+
new Date(args.join(' ')).getTime()
|
|
346
|
+
: Date.now() - pruneDays * 24 * 60 * 60 * 1000;
|
|
347
|
+
if (Number.isNaN(since)) {
|
|
348
|
+
throw error('Invalid date', {
|
|
349
|
+
code: 'EUSAGE',
|
|
350
|
+
found: args.join(' '),
|
|
351
|
+
});
|
|
352
|
+
}
|
|
339
353
|
const { storeRoot } = conf.options;
|
|
340
354
|
const names = storeEntryNames(storeRoot);
|
|
341
355
|
const removed = {};
|
|
342
356
|
for (const hex of names) {
|
|
343
357
|
const entry = resolve(storeRoot, hex);
|
|
344
358
|
const index = readStoreIndex(entry);
|
|
345
|
-
if (storeEntryCopied(entry, index)
|
|
359
|
+
if ((storeEntryCopied(entry, index) &&
|
|
360
|
+
storeEntryLastCopied(entry) >= since) ||
|
|
346
361
|
storeEntryLinked(entry, index)) {
|
|
347
362
|
continue;
|
|
348
363
|
}
|
package/dist/commands/init.js
CHANGED
|
@@ -60,13 +60,11 @@ export const command = async (conf) => {
|
|
|
60
60
|
const { config: parsedWSConfig, source } = resolveWSConfig(conf.options.projectRoot, conf.options.packageJson);
|
|
61
61
|
// what gets written back to vlt.json. The raw vlt.json value is
|
|
62
62
|
// reused as-is so its existing shape is preserved. When the
|
|
63
|
-
// definitions live in package.json
|
|
64
|
-
// first -- otherwise adding a `workspaces`
|
|
65
|
-
// every workspace
|
|
63
|
+
// definitions live in package.json or pnpm-workspace.yaml instead,
|
|
64
|
+
// seed vlt.json with them first -- otherwise adding a `workspaces`
|
|
65
|
+
// field here would shadow every workspace already declared there.
|
|
66
66
|
const workspacesConfig = load('workspaces', assertWSConfig) ??
|
|
67
|
-
(source
|
|
68
|
-
Object.values(parsedWSConfig).flat()
|
|
69
|
-
: undefined);
|
|
67
|
+
(source ? Object.values(parsedWSConfig).flat() : undefined);
|
|
70
68
|
const results = [];
|
|
71
69
|
const addToConfig = [];
|
|
72
70
|
// create a new package.json file for every workspace
|
package/dist/commands/install.js
CHANGED
|
@@ -3,6 +3,7 @@ import { install } from '@vltpkg/graph';
|
|
|
3
3
|
import { parseAddArgs } from "../parse-add-remove-args.js";
|
|
4
4
|
import { planSpecConfigPersist } from "../persist-spec-config.js";
|
|
5
5
|
import { asUnknownSpecPrefix } from "../require-registry.js";
|
|
6
|
+
import { addTypesDeps } from "../save-types.js";
|
|
6
7
|
import { trackInstall } from "../telemetry.js";
|
|
7
8
|
import { lazyView } from "../view.js";
|
|
8
9
|
export const needsRegistry = true;
|
|
@@ -32,6 +33,9 @@ export const usage = () => commandUsage({
|
|
|
32
33
|
'save-prod': {
|
|
33
34
|
description: 'Save installed packages to package.json as dependencies.',
|
|
34
35
|
},
|
|
36
|
+
'save-types': {
|
|
37
|
+
description: 'Also save matching @types/* packages to devDependencies for added packages that ship no types.',
|
|
38
|
+
},
|
|
35
39
|
'save-config': {
|
|
36
40
|
description: 'Save registry and git host options given on the command line or via env (e.g. `--registries name=url`) to the project vlt.json, or the user vlt.json with --config=user.',
|
|
37
41
|
},
|
|
@@ -130,6 +134,8 @@ export const command = async (conf) => {
|
|
|
130
134
|
const scurry = conf.options.scurry;
|
|
131
135
|
const { add } = parseAddArgs(conf, scurry, monorepo);
|
|
132
136
|
const persist = planSpecConfigPersist(conf);
|
|
137
|
+
if (conf.values['save-types'])
|
|
138
|
+
await addTypesDeps(conf, add);
|
|
133
139
|
const frozenLockfile = conf.options['frozen-lockfile'];
|
|
134
140
|
const expectLockfile = conf.options['expect-lockfile'];
|
|
135
141
|
const lockfileOnly = conf.options['lockfile-only'];
|
package/dist/commands/pack.js
CHANGED
|
@@ -9,6 +9,7 @@ import { actual } from '@vltpkg/graph';
|
|
|
9
9
|
import { Query } from '@vltpkg/query';
|
|
10
10
|
import { error } from '@vltpkg/error-cause';
|
|
11
11
|
import { createHostContextsMap } from "../query-host-contexts.js";
|
|
12
|
+
import { sortScopeLocations } from "../sort-scope-locations.js";
|
|
12
13
|
import { minimatch } from 'minimatch';
|
|
13
14
|
export const needsRegistry = true;
|
|
14
15
|
export const usage = () => commandUsage({
|
|
@@ -97,7 +98,7 @@ const scopeLocations = async (queryString, conf) => {
|
|
|
97
98
|
}));
|
|
98
99
|
locations.push(resolve(projectRoot, location));
|
|
99
100
|
}
|
|
100
|
-
return locations;
|
|
101
|
+
return sortScopeLocations(locations, options.monorepo);
|
|
101
102
|
};
|
|
102
103
|
export const command = async (conf) => {
|
|
103
104
|
const { options, projectRoot } = conf;
|
package/dist/commands/pkg.js
CHANGED
|
@@ -9,6 +9,7 @@ import { SecurityArchive } from '@vltpkg/security-archive';
|
|
|
9
9
|
import { views as initViews } from "./init.js";
|
|
10
10
|
import { commandUsage } from "../config/usage.js";
|
|
11
11
|
import { createHostContextsMap } from "../query-host-contexts.js";
|
|
12
|
+
import { sortScopeLocations } from "../sort-scope-locations.js";
|
|
12
13
|
import { minimatch } from 'minimatch';
|
|
13
14
|
export const views = {
|
|
14
15
|
human: (results, _, config) => {
|
|
@@ -129,15 +130,17 @@ export const command = async (conf) => {
|
|
|
129
130
|
const { nodes: resultNodes } = await query.search(queryString, {
|
|
130
131
|
signal: new AbortController().signal,
|
|
131
132
|
});
|
|
133
|
+
const found = [];
|
|
132
134
|
for (const node of resultNodes) {
|
|
133
135
|
const location = node.location;
|
|
134
136
|
assert(location, error(`node ${node.id} has no location`, {
|
|
135
137
|
found: node,
|
|
136
138
|
}));
|
|
137
139
|
if (node.id !== VIRTUAL_ROOT_ID) {
|
|
138
|
-
|
|
140
|
+
found.push(resolve(node.projectRoot, location));
|
|
139
141
|
}
|
|
140
142
|
}
|
|
143
|
+
locations.push(...sortScopeLocations(found, options.monorepo));
|
|
141
144
|
}
|
|
142
145
|
else if (paths?.length || groups?.length || recursive) {
|
|
143
146
|
for (const workspace of options.monorepo ?? []) {
|
package/dist/commands/publish.js
CHANGED
|
@@ -10,6 +10,7 @@ import prettyBytes from 'pretty-bytes';
|
|
|
10
10
|
import { actual } from '@vltpkg/graph';
|
|
11
11
|
import { Query } from '@vltpkg/query';
|
|
12
12
|
import { createHostContextsMap } from "../query-host-contexts.js";
|
|
13
|
+
import { sortScopeLocations } from "../sort-scope-locations.js";
|
|
13
14
|
import { minimatch } from 'minimatch';
|
|
14
15
|
import { resolveRegistry } from "../require-registry.js";
|
|
15
16
|
import { stderr } from "../output.js";
|
|
@@ -114,13 +115,15 @@ export const command = async (conf) => {
|
|
|
114
115
|
const { nodes } = await query.search(queryString, {
|
|
115
116
|
signal: new AbortController().signal,
|
|
116
117
|
});
|
|
118
|
+
const found = [];
|
|
117
119
|
for (const node of nodes) {
|
|
118
120
|
const { location } = node.toJSON();
|
|
119
121
|
assert(location, error(`node ${node.id} has no location`, {
|
|
120
122
|
found: node,
|
|
121
123
|
}));
|
|
122
|
-
|
|
124
|
+
found.push(resolve(projectRoot, location));
|
|
123
125
|
}
|
|
126
|
+
locations.push(...sortScopeLocations(found, options.monorepo));
|
|
124
127
|
}
|
|
125
128
|
else if (paths?.length || groups?.length || recursive) {
|
|
126
129
|
for (const workspace of options.monorepo ?? []) {
|
package/dist/commands/version.js
CHANGED
|
@@ -8,6 +8,7 @@ import assert from 'node:assert';
|
|
|
8
8
|
import { actual } from '@vltpkg/graph';
|
|
9
9
|
import { Query } from '@vltpkg/query';
|
|
10
10
|
import { createHostContextsMap } from "../query-host-contexts.js";
|
|
11
|
+
import { sortScopeLocations } from "../sort-scope-locations.js";
|
|
11
12
|
import { minimatch } from 'minimatch';
|
|
12
13
|
/**
|
|
13
14
|
* `--no-git-tag-version` skips the whole git dance, matching npm.
|
|
@@ -223,13 +224,15 @@ export const command = async (conf) => {
|
|
|
223
224
|
const { nodes } = await query.search(queryString, {
|
|
224
225
|
signal: new AbortController().signal,
|
|
225
226
|
});
|
|
227
|
+
const found = [];
|
|
226
228
|
for (const node of nodes) {
|
|
227
229
|
const location = node.location;
|
|
228
230
|
assert(location, error(`node ${node.id} has no location`, {
|
|
229
231
|
found: node,
|
|
230
232
|
}));
|
|
231
|
-
|
|
233
|
+
found.push(resolve(node.projectRoot, location));
|
|
232
234
|
}
|
|
235
|
+
locations.push(...sortScopeLocations(found, options.monorepo));
|
|
233
236
|
}
|
|
234
237
|
else if (paths?.length || groups?.length || recursive) {
|
|
235
238
|
for (const workspace of options.monorepo ?? []) {
|
|
@@ -75,7 +75,7 @@ export declare const recordFields: readonly ["git-hosts", "registries", "git-hos
|
|
|
75
75
|
export type RecordField = (typeof recordFields)[number];
|
|
76
76
|
export declare const isRecordField: (s: string) => s is RecordField;
|
|
77
77
|
/** Valid `store-linker` values. */
|
|
78
|
-
export declare const storeLinkers: readonly ["auto", "hardlink", "copy", "unpack"];
|
|
78
|
+
export declare const storeLinkers: readonly ["auto", "hardlink", "clone", "copy", "unpack"];
|
|
79
79
|
export declare const definition: import("jackspeak").Jack<{
|
|
80
80
|
color: {
|
|
81
81
|
short: string;
|
|
@@ -139,7 +139,7 @@ export declare const definition: import("jackspeak").Jack<{
|
|
|
139
139
|
'store-linker': {
|
|
140
140
|
hint: string;
|
|
141
141
|
description: string;
|
|
142
|
-
validOptions: readonly ["auto", "hardlink", "copy", "unpack"];
|
|
142
|
+
validOptions: readonly ["auto", "hardlink", "clone", "copy", "unpack"];
|
|
143
143
|
default: string;
|
|
144
144
|
};
|
|
145
145
|
tag: {
|
|
@@ -365,6 +365,9 @@ export declare const definition: import("jackspeak").Jack<{
|
|
|
365
365
|
short: string;
|
|
366
366
|
description: string;
|
|
367
367
|
};
|
|
368
|
+
'save-types': {
|
|
369
|
+
description: string;
|
|
370
|
+
};
|
|
368
371
|
'save-config': {
|
|
369
372
|
description: string;
|
|
370
373
|
};
|
|
@@ -374,6 +377,7 @@ export declare const definition: import("jackspeak").Jack<{
|
|
|
374
377
|
'save-optional': import("jackspeak").ConfigOption<"boolean", false, undefined>;
|
|
375
378
|
'save-peer': import("jackspeak").ConfigOption<"boolean", false, undefined>;
|
|
376
379
|
'save-prod': import("jackspeak").ConfigOption<"boolean", false, undefined>;
|
|
380
|
+
'save-types': import("jackspeak").ConfigOption<"boolean", false, undefined>;
|
|
377
381
|
'save-config': import("jackspeak").ConfigOption<"boolean", false, undefined>;
|
|
378
382
|
} & {
|
|
379
383
|
'save-prefix': {
|
|
@@ -114,6 +114,7 @@ export const isRecordField = (s) => recordFields.includes(s);
|
|
|
114
114
|
export const storeLinkers = [
|
|
115
115
|
'auto',
|
|
116
116
|
'hardlink',
|
|
117
|
+
'clone',
|
|
117
118
|
'copy',
|
|
118
119
|
'unpack',
|
|
119
120
|
];
|
|
@@ -226,8 +227,11 @@ export const definition = j
|
|
|
226
227
|
|
|
227
228
|
There is **no** built-in \`npm\` registry URL; the \`npm\`
|
|
228
229
|
alias must be configured (e.g. via \`vlt setup\`) before it
|
|
229
|
-
can be used. The \`gh\`
|
|
230
|
-
|
|
230
|
+
can be used. The \`gh\` alias has a built-in default
|
|
231
|
+
that can be overridden here. A name used by
|
|
232
|
+
\`--jsr-registries\` or \`--git-hosts\` (e.g. \`jsr\`,
|
|
233
|
+
\`github\`), or a built-in protocol (e.g. \`file\`), is
|
|
234
|
+
an error.
|
|
231
235
|
|
|
232
236
|
Aliases given on the command line or in
|
|
233
237
|
\`VLT_REGISTRIES\` (newline-delimited) merge with the
|
|
@@ -317,28 +321,35 @@ export const definition = j
|
|
|
317
321
|
default: cacheDir,
|
|
318
322
|
},
|
|
319
323
|
'store-linker': {
|
|
320
|
-
hint: 'auto | hardlink | copy | unpack',
|
|
324
|
+
hint: 'auto | hardlink | clone | copy | unpack',
|
|
321
325
|
description: `How packages are placed in \`node_modules\`. Every
|
|
322
326
|
linker but \`unpack\` fills a global store under
|
|
323
327
|
\`cache\` in the background, then places packages
|
|
324
328
|
from it.
|
|
325
329
|
|
|
326
330
|
- auto: Default. On Linux, hardlink from the global
|
|
327
|
-
store, copying any file that cannot be linked.
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
+
store, copying any file that cannot be linked. On
|
|
332
|
+
macOS, clone from it, on a Node with \`node:ffi\`
|
|
333
|
+
(26.1 and later, and \`--allow-ffi\` under
|
|
334
|
+
\`--permission\`); a hardlink is the slowest way
|
|
335
|
+
to place a file on APFS, so macOS without it, and
|
|
336
|
+
Windows, unpack by default.
|
|
331
337
|
- hardlink: Hardlink from the global store on every
|
|
332
338
|
platform.
|
|
339
|
+
- clone: Clone each package directory from the
|
|
340
|
+
global store copy-on-write (\`clonefile\`, macOS
|
|
341
|
+
on APFS). Files share storage with the store
|
|
342
|
+
until edited, so editing in place is safe.
|
|
343
|
+
Unpacks where cloning fails.
|
|
333
344
|
- copy: Copy from the global store. Use this when
|
|
334
345
|
editing files in \`node_modules\` in place, since
|
|
335
346
|
a hardlink shares its content with every project.
|
|
336
347
|
- unpack: Unpack each package tarball, skipping the
|
|
337
348
|
global store.
|
|
338
349
|
|
|
339
|
-
A package is copied right before its
|
|
340
|
-
scripts run, so they don't change its files
|
|
341
|
-
store.
|
|
350
|
+
A hardlinked package is copied right before its
|
|
351
|
+
install scripts run, so they don't change its files
|
|
352
|
+
in the store; a cloned one needs no copy.
|
|
342
353
|
An invalid \`VLT_STORE_LINKER\` warns and uses
|
|
343
354
|
\`unpack\`.`,
|
|
344
355
|
validOptions: storeLinkers,
|
|
@@ -702,6 +713,11 @@ export const definition = j
|
|
|
702
713
|
devDependencies or optionalDependencies, but you want to
|
|
703
714
|
move it to be a non-optional production dependency.`,
|
|
704
715
|
},
|
|
716
|
+
'save-types': {
|
|
717
|
+
description: `Also save the matching \`@types/*\` package as a
|
|
718
|
+
devDependency for each added registry package that
|
|
719
|
+
ships no TypeScript types of its own.`,
|
|
720
|
+
},
|
|
705
721
|
'save-config': {
|
|
706
722
|
description: `Save the registry and git host options given on the
|
|
707
723
|
command line or via \`VLT_*\` env (\`--registry\`,
|
package/dist/config/index.d.ts
CHANGED
|
@@ -85,6 +85,8 @@ export type ConfigOptions = ConfigOptionsNoExtras & Pick<SpecOptions, 'catalog'
|
|
|
85
85
|
storeRoot: string;
|
|
86
86
|
monorepo?: Monorepo;
|
|
87
87
|
packageInfo: PackageInfoClient;
|
|
88
|
+
/** aborted when the command fails */
|
|
89
|
+
signal: AbortSignal;
|
|
88
90
|
};
|
|
89
91
|
/**
|
|
90
92
|
* The base config definition set as a type
|
|
@@ -125,6 +127,8 @@ export declare class Config {
|
|
|
125
127
|
* to recalculate the options.
|
|
126
128
|
*/
|
|
127
129
|
resetOptions(projectRoot?: string): void;
|
|
130
|
+
/** Abort requests still in flight */
|
|
131
|
+
abort(): void;
|
|
128
132
|
/**
|
|
129
133
|
* positional arguments to the vlt process
|
|
130
134
|
*/
|
package/dist/config/index.js
CHANGED
|
@@ -24,6 +24,7 @@ import { error } from '@vltpkg/error-cause';
|
|
|
24
24
|
import { PackageInfoClient } from '@vltpkg/package-info';
|
|
25
25
|
import { PackageJson } from '@vltpkg/package-json';
|
|
26
26
|
import { storeRoot } from '@vltpkg/registry-client/store-root';
|
|
27
|
+
import { cloneAvailable } from '@vltpkg/tar/clonefile';
|
|
27
28
|
import { resetCaches } from '@vltpkg/dep-id';
|
|
28
29
|
import { getOptions } from '@vltpkg/spec';
|
|
29
30
|
import { assertRecordStringString, assertRecordStringT, isObject, isRecordStringString, } from '@vltpkg/types';
|
|
@@ -35,6 +36,7 @@ import { PathScurry } from 'path-scurry';
|
|
|
35
36
|
import { commands, definition, defaultValues, getCommand, isRecordField, recordFields, storeLinkers, } from "./definition.js";
|
|
36
37
|
import { merge } from "./merge.js";
|
|
37
38
|
import { cloneLayer, mergeLayers } from "./merge-layers.js";
|
|
39
|
+
import { assertSpecAliases } from "./spec-aliases.js";
|
|
38
40
|
export { commands, definition, isRecordField, recordFields, };
|
|
39
41
|
export const kCustomInspect = Symbol.for('nodejs.util.inspect.custom');
|
|
40
42
|
// turn a set of pairs into a Record object.
|
|
@@ -172,6 +174,7 @@ export class Config {
|
|
|
172
174
|
const asRecords = pairsToRecords(this.parse().values);
|
|
173
175
|
const extras = {
|
|
174
176
|
projectRoot: this.projectRoot,
|
|
177
|
+
signal: this.#abort.signal,
|
|
175
178
|
storeRoot: storeRoot(asRecords.cache),
|
|
176
179
|
scurry,
|
|
177
180
|
packageJson,
|
|
@@ -197,7 +200,8 @@ export class Config {
|
|
|
197
200
|
return Object.fromEntries(Object.entries(options).filter(([k]) => k !== 'monorepo' &&
|
|
198
201
|
k !== 'scurry' &&
|
|
199
202
|
k !== 'packageJson' &&
|
|
200
|
-
k !== 'packageInfo'
|
|
203
|
+
k !== 'packageInfo' &&
|
|
204
|
+
k !== 'signal'));
|
|
201
205
|
},
|
|
202
206
|
});
|
|
203
207
|
return this.#options;
|
|
@@ -213,6 +217,11 @@ export class Config {
|
|
|
213
217
|
}
|
|
214
218
|
// memoized options() getter value
|
|
215
219
|
#options;
|
|
220
|
+
#abort = new AbortController();
|
|
221
|
+
/** Abort requests still in flight */
|
|
222
|
+
abort() {
|
|
223
|
+
this.#abort.abort();
|
|
224
|
+
}
|
|
216
225
|
/**
|
|
217
226
|
* positional arguments to the vlt process
|
|
218
227
|
*/
|
|
@@ -277,13 +286,16 @@ export class Config {
|
|
|
277
286
|
// ok, applied cmd-specific defaults, do rest of the parse
|
|
278
287
|
this.jack.applyDefaults(p);
|
|
279
288
|
// `auto` only hardlinks from the global store on linux: on APFS a
|
|
280
|
-
// hardlink is the slowest way to place a file, so darwin
|
|
281
|
-
//
|
|
282
|
-
//
|
|
289
|
+
// hardlink is the slowest way to place a file, so darwin clones
|
|
290
|
+
// instead, when its node can load `node:ffi` to call clonefile(2)
|
|
291
|
+
// with, and win32 (and darwin without it) unpack by default. the
|
|
292
|
+
// env layer is rewritten too, so `explicit` records the resolved
|
|
293
|
+
// value.
|
|
283
294
|
if (p.values['store-linker'] === 'auto' &&
|
|
284
295
|
process.platform !== 'linux') {
|
|
285
|
-
|
|
286
|
-
|
|
296
|
+
const linker = cloneAvailable() ? 'clone' : 'unpack';
|
|
297
|
+
p.values['store-linker'] = linker;
|
|
298
|
+
envValues['store-linker'] = linker;
|
|
287
299
|
}
|
|
288
300
|
// what was set on the cli or env for this run. record fields hold
|
|
289
301
|
// only those pairs, env then cli, so later ones win per key.
|
|
@@ -317,6 +329,10 @@ export class Config {
|
|
|
317
329
|
const base = (k in cmdPairs ? cmdPairs[k] : fileValues[k]) ?? [];
|
|
318
330
|
values[k] = dedupePairs([...base, ...pairs]);
|
|
319
331
|
}
|
|
332
|
+
// skipped for `vlt config`, so a bad alias can be removed
|
|
333
|
+
if (cmdOrFallback !== 'config') {
|
|
334
|
+
assertSpecAliases(pairsToRecords(p.values));
|
|
335
|
+
}
|
|
320
336
|
this.jack.writeEnv(p);
|
|
321
337
|
process.env[kParentEnv] = JSON.stringify({
|
|
322
338
|
explicit,
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
declare const builtins: {
|
|
2
|
+
registries: {
|
|
3
|
+
gh: string;
|
|
4
|
+
};
|
|
5
|
+
'jsr-registries': {
|
|
6
|
+
jsr: string;
|
|
7
|
+
};
|
|
8
|
+
'git-hosts': {
|
|
9
|
+
github: string;
|
|
10
|
+
bitbucket: string;
|
|
11
|
+
gitlab: string;
|
|
12
|
+
gist: string;
|
|
13
|
+
};
|
|
14
|
+
};
|
|
15
|
+
type SpecAliasField = keyof typeof builtins;
|
|
16
|
+
/**
|
|
17
|
+
* Throw `ECONFIG` if a spec prefix alias is in more than one of
|
|
18
|
+
* `registries` / `jsr-registries` / `git-hosts` (built-ins included),
|
|
19
|
+
* or is a built-in protocol. Overriding a built-in in its own field is ok.
|
|
20
|
+
*/
|
|
21
|
+
export declare const assertSpecAliases: (values: Partial<Record<SpecAliasField, Record<string, string>>>) => void;
|
|
22
|
+
export {};
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { error } from '@vltpkg/error-cause';
|
|
2
|
+
import { builtinProtocols, defaultGitHosts, defaultJsrRegistries, defaultRegistries, } from '@vltpkg/spec';
|
|
3
|
+
const builtins = {
|
|
4
|
+
registries: defaultRegistries,
|
|
5
|
+
'jsr-registries': defaultJsrRegistries,
|
|
6
|
+
'git-hosts': defaultGitHosts,
|
|
7
|
+
};
|
|
8
|
+
const fields = Object.keys(builtins);
|
|
9
|
+
/**
|
|
10
|
+
* Throw `ECONFIG` if a spec prefix alias is in more than one of
|
|
11
|
+
* `registries` / `jsr-registries` / `git-hosts` (built-ins included),
|
|
12
|
+
* or is a built-in protocol. Overriding a built-in in its own field is ok.
|
|
13
|
+
*/
|
|
14
|
+
export const assertSpecAliases = (values) => {
|
|
15
|
+
const owners = new Map();
|
|
16
|
+
for (const field of fields) {
|
|
17
|
+
for (const key of Object.keys(builtins[field])) {
|
|
18
|
+
if (values[field]?.[key] === undefined) {
|
|
19
|
+
owners.set(key, { field, builtin: true });
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
for (const field of fields) {
|
|
24
|
+
for (const key of Object.keys(values[field] ?? {})) {
|
|
25
|
+
const name = `${field}.${key}`;
|
|
26
|
+
if (builtinProtocols.has(key)) {
|
|
27
|
+
throw error(`${name} shadows the built-in \`${key}:\` protocol`, {
|
|
28
|
+
code: 'ECONFIG',
|
|
29
|
+
name,
|
|
30
|
+
found: key,
|
|
31
|
+
wanted: 'a name that is not a built-in protocol',
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
const owner = owners.get(key);
|
|
35
|
+
if (owner) {
|
|
36
|
+
const where = `${owner.field}.${key}${owner.builtin ? ' (built-in)' : ''}`;
|
|
37
|
+
throw error(`\`${key}:\` spec prefix defined in both ${where} and ${name}`, {
|
|
38
|
+
code: 'ECONFIG',
|
|
39
|
+
name,
|
|
40
|
+
found: key,
|
|
41
|
+
wanted: `a ${field} name not used by ${owner.field}`,
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
owners.set(key, { field, builtin: false });
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
};
|
package/dist/exec-command.js
CHANGED
|
@@ -14,6 +14,7 @@ import { stderr, stdout, styleTextStdout } from "./output.js";
|
|
|
14
14
|
import assert from 'node:assert';
|
|
15
15
|
import { resolve } from 'node:path';
|
|
16
16
|
import { createHostContextsMap } from "./query-host-contexts.js";
|
|
17
|
+
import { sortScopeLocations } from "./sort-scope-locations.js";
|
|
17
18
|
const isScriptSet = (o) => {
|
|
18
19
|
if (!o || typeof o !== 'object')
|
|
19
20
|
return false;
|
|
@@ -98,11 +99,13 @@ export class ExecCommand {
|
|
|
98
99
|
this.#defaultIgnoreMissing = true;
|
|
99
100
|
const mainManifest = conf.options.packageJson.maybeRead(this.projectRoot);
|
|
100
101
|
let graph;
|
|
102
|
+
let monorepo;
|
|
101
103
|
if (mainManifest) {
|
|
104
|
+
monorepo = Monorepo.load(this.projectRoot);
|
|
102
105
|
graph = actual.load({
|
|
103
106
|
...conf.options,
|
|
104
107
|
mainManifest,
|
|
105
|
-
monorepo
|
|
108
|
+
monorepo,
|
|
106
109
|
loadManifests: false,
|
|
107
110
|
});
|
|
108
111
|
}
|
|
@@ -120,14 +123,15 @@ export class ExecCommand {
|
|
|
120
123
|
const { nodes } = await query.search(queryString, {
|
|
121
124
|
signal: new AbortController().signal,
|
|
122
125
|
});
|
|
123
|
-
|
|
126
|
+
const locations = [];
|
|
124
127
|
for (const node of nodes) {
|
|
125
128
|
const { location } = node.toJSON();
|
|
126
129
|
assert(location, error(`node ${node.id} has no location`, {
|
|
127
130
|
found: node,
|
|
128
131
|
}));
|
|
129
|
-
|
|
132
|
+
locations.push(location);
|
|
130
133
|
}
|
|
134
|
+
this.#nodes = sortScopeLocations(locations, monorepo);
|
|
131
135
|
}
|
|
132
136
|
else if (paths?.length || groups?.length || recursive) {
|
|
133
137
|
this.#defaultIgnoreMissing = true;
|
|
@@ -163,8 +167,9 @@ export class ExecCommand {
|
|
|
163
167
|
}
|
|
164
168
|
// run across workspaces
|
|
165
169
|
let failed = false;
|
|
166
|
-
const runInDir = async (cwd, label) => {
|
|
167
|
-
const
|
|
170
|
+
const runInDir = async (cwd, label, signal) => {
|
|
171
|
+
const arg = { ...this.bgArg(cwd), signal };
|
|
172
|
+
const result = await this.bg(arg).catch((er) => {
|
|
168
173
|
if (isErrorWithCause(er) && isRunResult(er.cause)) {
|
|
169
174
|
this.printResult(label, er.cause);
|
|
170
175
|
}
|
|
@@ -189,7 +194,8 @@ export class ExecCommand {
|
|
|
189
194
|
}
|
|
190
195
|
}
|
|
191
196
|
else if (this.#monorepo) {
|
|
192
|
-
|
|
197
|
+
// aborted on the first failure, killing the rest
|
|
198
|
+
const wsResultMap = await this.#monorepo.run((ws, signal) => runInDir(ws.fullpath, ws.path, signal));
|
|
193
199
|
for (const [ws, result] of wsResultMap) {
|
|
194
200
|
if (result)
|
|
195
201
|
resultMap.set(ws.path, result);
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { format } from 'node:util';
|
|
2
|
-
import { asRootError } from '@vltpkg/output/error';
|
|
2
|
+
import { asRootError, findRootError } from '@vltpkg/output/error';
|
|
3
3
|
import { loadPackageJson } from 'package-json-from-dist';
|
|
4
4
|
import { getSortedCliOptions, getSortedKeys, } from "./config/definition.js";
|
|
5
5
|
import { Config } from "./config/index.js";
|
|
@@ -12,13 +12,14 @@ const loadVlt = async (cwd, argv) => {
|
|
|
12
12
|
return await Config.load(cwd, argv);
|
|
13
13
|
}
|
|
14
14
|
catch (e) {
|
|
15
|
-
const
|
|
15
|
+
const code = findRootError(e, { code: 'ECONFIG' }) ? 'ECONFIG' : 'JACKSPEAK';
|
|
16
|
+
const err = asRootError(e, { code });
|
|
16
17
|
const { found, path, wanted, name } = err.cause;
|
|
17
18
|
const isConfigFile = typeof path === 'string';
|
|
18
|
-
const msg = isConfigFile ?
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
const validOptions = wanted ? undefined
|
|
19
|
+
const msg = isConfigFile ? `Problem in Config File ${path}`
|
|
20
|
+
: code === 'ECONFIG' ? 'Config Error'
|
|
21
|
+
: 'Invalid Option Flag';
|
|
22
|
+
const validOptions = wanted || code === 'ECONFIG' ? undefined
|
|
22
23
|
: isConfigFile ? getSortedKeys()
|
|
23
24
|
: getSortedCliOptions();
|
|
24
25
|
stderr(msg);
|
|
@@ -34,7 +35,10 @@ const loadVlt = async (cwd, argv) => {
|
|
|
34
35
|
stderr(indent('Valid Options:'));
|
|
35
36
|
stderr(indent(validOptions.join('\n'), 4));
|
|
36
37
|
}
|
|
37
|
-
|
|
38
|
+
// `vlt help` would fail the same way
|
|
39
|
+
if (code !== 'ECONFIG') {
|
|
40
|
+
stderr(indent(`Run 'vlt help' for more information about available options.`));
|
|
41
|
+
}
|
|
38
42
|
return flushAndExit(process.exitCode || 1);
|
|
39
43
|
}
|
|
40
44
|
};
|
|
@@ -57,7 +61,7 @@ const run = async () => {
|
|
|
57
61
|
if (vlt.command !== 'init' &&
|
|
58
62
|
(vlt.get('workspace') || vlt.get('workspace-group')) &&
|
|
59
63
|
![...(monorepo?.values() ?? [])].length) {
|
|
60
|
-
stderr(`Error: No matching workspaces found. Make sure the vlt.json config,
|
|
64
|
+
stderr(`Error: No matching workspaces found. Make sure the vlt.json config, the "workspaces" field of the root package.json, or the "packages" list of pnpm-workspace.yaml contains the correct workspaces.`);
|
|
61
65
|
if (vlt.get('workspace')) {
|
|
62
66
|
stderr(indent(`Workspace: ${format(vlt.get('workspace'))}`));
|
|
63
67
|
}
|
package/dist/output.js
CHANGED
|
@@ -227,6 +227,7 @@ export const outputCommand = async (cliCommand, conf, { start, vltVersion } = {
|
|
|
227
227
|
...formatOptions,
|
|
228
228
|
colors: stderrColor,
|
|
229
229
|
});
|
|
230
|
-
|
|
230
|
+
// stop pending requests, so the process can exit on its own
|
|
231
|
+
conf.abort();
|
|
231
232
|
}
|
|
232
233
|
};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { AddImportersDependenciesMap } from '@vltpkg/graph';
|
|
2
|
+
import type { Manifest } from '@vltpkg/types';
|
|
3
|
+
import type { LoadedConfig } from './config/index.ts';
|
|
4
|
+
type TypedManifest = Pick<Manifest, 'exports'> & {
|
|
5
|
+
types?: unknown;
|
|
6
|
+
typings?: unknown;
|
|
7
|
+
typesVersions?: unknown;
|
|
8
|
+
};
|
|
9
|
+
/** `foo` → `@types/foo`, `@scope/foo` → `@types/scope__foo` */
|
|
10
|
+
export declare const typesName: (name: string) => string;
|
|
11
|
+
/** Does the manifest declare its own TypeScript types? */
|
|
12
|
+
export declare const hasOwnTypes: (m: TypedManifest) => boolean;
|
|
13
|
+
/**
|
|
14
|
+
* For each added registry package that ships no types, add the newest
|
|
15
|
+
* non-deprecated `@types/*` package in the same major (major.minor for
|
|
16
|
+
* 0.x), else its older-major `latest`, to that importer's
|
|
17
|
+
* devDependencies.
|
|
18
|
+
*/
|
|
19
|
+
export declare const addTypesDeps: (conf: LoadedConfig, add: AddImportersDependenciesMap) => Promise<void>;
|
|
20
|
+
export {};
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { splitDepID } from '@vltpkg/dep-id';
|
|
2
|
+
import { addKey, asDependency } from '@vltpkg/graph';
|
|
3
|
+
import { parse } from '@vltpkg/semver';
|
|
4
|
+
import { Spec } from '@vltpkg/spec';
|
|
5
|
+
import { isErrorWithCause, isObject, longDependencyTypes, } from '@vltpkg/types';
|
|
6
|
+
import { resolve } from 'node:path';
|
|
7
|
+
/** `foo` → `@types/foo`, `@scope/foo` → `@types/scope__foo` */
|
|
8
|
+
export const typesName = (name) => `@types/${name.startsWith('@') ? name.slice(1).replace('/', '__') : name}`;
|
|
9
|
+
const exportsHaveTypes = (e) => !!e &&
|
|
10
|
+
typeof e === 'object' &&
|
|
11
|
+
Object.entries(e).some(([k, v]) => k === 'types' || k.startsWith('types@') || exportsHaveTypes(v));
|
|
12
|
+
// root entry only: typed subpaths don't type `import 'pkg'`
|
|
13
|
+
const rootExport = (e) => isObject(e) && Object.keys(e).some(k => k.startsWith('.')) ?
|
|
14
|
+
e['.']
|
|
15
|
+
: e;
|
|
16
|
+
/** Does the manifest declare its own TypeScript types? */
|
|
17
|
+
export const hasOwnTypes = (m) => !!(m.types || m.typings || m.typesVersions) ||
|
|
18
|
+
exportsHaveTypes(rootExport(m.exports));
|
|
19
|
+
const isResolveError = (er) => isErrorWithCause(er) &&
|
|
20
|
+
isObject(er.cause) &&
|
|
21
|
+
er.cause.code === 'ERESOLVE';
|
|
22
|
+
/**
|
|
23
|
+
* For each added registry package that ships no types, add the newest
|
|
24
|
+
* non-deprecated `@types/*` package in the same major (major.minor for
|
|
25
|
+
* 0.x), else its older-major `latest`, to that importer's
|
|
26
|
+
* devDependencies.
|
|
27
|
+
*/
|
|
28
|
+
export const addTypesDeps = async (conf, add) => {
|
|
29
|
+
const { options } = conf;
|
|
30
|
+
const { packageInfo, packageJson, projectRoot } = options;
|
|
31
|
+
const exact = conf.values['save-exact'];
|
|
32
|
+
const prefix = conf.values['save-prefix'];
|
|
33
|
+
const lookups = new Map();
|
|
34
|
+
const lookup = async (spec, name) => {
|
|
35
|
+
const types = (range) => packageInfo.manifest(Spec.parse(name, range, options));
|
|
36
|
+
try {
|
|
37
|
+
const m = await packageInfo.manifest(spec);
|
|
38
|
+
const { version = '' } = m;
|
|
39
|
+
const v = parse(version);
|
|
40
|
+
if (!v || hasOwnTypes(m))
|
|
41
|
+
return;
|
|
42
|
+
let tm;
|
|
43
|
+
try {
|
|
44
|
+
// @types track lib major (major.minor for 0.x); x-range, `^0`
|
|
45
|
+
// would only match 0.0.0
|
|
46
|
+
tm = await types(v.major ? String(v.major) : `0.${v.minor}`);
|
|
47
|
+
}
|
|
48
|
+
catch (er) {
|
|
49
|
+
if (!isResolveError(er))
|
|
50
|
+
throw er;
|
|
51
|
+
// @types often lag lib majors: take `latest` if older
|
|
52
|
+
tm = await types('latest');
|
|
53
|
+
if (!parse(tm.version ?? '')?.lessThan(v))
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
if (!tm.version || tm.deprecated)
|
|
57
|
+
return;
|
|
58
|
+
// abbreviated packuments omit types fields: recheck on full one
|
|
59
|
+
const { versions } = await packageInfo.packument(spec, {
|
|
60
|
+
full: true,
|
|
61
|
+
});
|
|
62
|
+
if (hasOwnTypes({ ...versions[version] }))
|
|
63
|
+
return;
|
|
64
|
+
return Spec.parse(name, exact ? tm.version : `${prefix}${tm.version}`, options);
|
|
65
|
+
}
|
|
66
|
+
catch (er) {
|
|
67
|
+
if (isResolveError(er))
|
|
68
|
+
return;
|
|
69
|
+
throw er;
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
await Promise.all([...add].map(async ([importer, deps]) => {
|
|
73
|
+
const mani = packageJson.maybeRead(resolve(projectRoot, splitDepID(importer)[1])) ?? {};
|
|
74
|
+
const found = await Promise.all([...deps.values()].map(async ({ spec }) => {
|
|
75
|
+
const { final } = spec;
|
|
76
|
+
const name = typesName(spec.name);
|
|
77
|
+
if (final.type !== 'registry' ||
|
|
78
|
+
final.name !== spec.name ||
|
|
79
|
+
spec.name.startsWith('@types/') ||
|
|
80
|
+
deps.has(name) ||
|
|
81
|
+
[...longDependencyTypes].some(t => mani[t]?.[name] !== undefined) ||
|
|
82
|
+
// other registry: its @types/<name> is a different pkg
|
|
83
|
+
final.registry !==
|
|
84
|
+
Spec.parse(name, '', options).final.registry) {
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
const key = String(spec);
|
|
88
|
+
let p = lookups.get(key);
|
|
89
|
+
if (!p)
|
|
90
|
+
lookups.set(key, (p = lookup(spec, name)));
|
|
91
|
+
return p;
|
|
92
|
+
}));
|
|
93
|
+
const types = found.filter(s => s !== undefined);
|
|
94
|
+
if (!types.length)
|
|
95
|
+
return;
|
|
96
|
+
// fresh Map: importers may share one deps Map
|
|
97
|
+
add.set(importer, new Map([
|
|
98
|
+
...deps,
|
|
99
|
+
...types.map(spec => [
|
|
100
|
+
addKey(spec),
|
|
101
|
+
asDependency({ spec, type: 'dev' }),
|
|
102
|
+
]),
|
|
103
|
+
]));
|
|
104
|
+
}));
|
|
105
|
+
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { Monorepo } from '@vltpkg/workspaces';
|
|
2
|
+
/**
|
|
3
|
+
* Reorder `--scope` target locations so each workspace follows the
|
|
4
|
+
* workspaces it depends on (directly or via unmatched workspaces),
|
|
5
|
+
* matching --workspace/--recursive. Ties broken by query order (a dep
|
|
6
|
+
* may move ahead of unrelated matches); cycles broken
|
|
7
|
+
* deterministically. Non-workspace
|
|
8
|
+
* locations (root, installed deps) keep their index. Accepts absolute
|
|
9
|
+
* or projectRoot-relative locations; returns the original strings.
|
|
10
|
+
*/
|
|
11
|
+
export declare const sortScopeLocations: (locations: string[], monorepo?: Monorepo) => string[];
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { resolve } from 'node:path';
|
|
2
|
+
import { graphRunSync } from '@vltpkg/graph-run';
|
|
3
|
+
/**
|
|
4
|
+
* Reorder `--scope` target locations so each workspace follows the
|
|
5
|
+
* workspaces it depends on (directly or via unmatched workspaces),
|
|
6
|
+
* matching --workspace/--recursive. Ties broken by query order (a dep
|
|
7
|
+
* may move ahead of unrelated matches); cycles broken
|
|
8
|
+
* deterministically. Non-workspace
|
|
9
|
+
* locations (root, installed deps) keep their index. Accepts absolute
|
|
10
|
+
* or projectRoot-relative locations; returns the original strings.
|
|
11
|
+
*/
|
|
12
|
+
export const sortScopeLocations = (locations, monorepo) => {
|
|
13
|
+
if (!monorepo || locations.length < 2)
|
|
14
|
+
return locations;
|
|
15
|
+
// resolve first: workspaces are also keyed by name and rel path
|
|
16
|
+
const wsAt = (loc) => monorepo.get(resolve(monorepo.projectRoot, loc));
|
|
17
|
+
const slots = locations.map(loc => [loc, wsAt(loc)]);
|
|
18
|
+
const locOf = new Map();
|
|
19
|
+
for (const [loc, ws] of slots) {
|
|
20
|
+
if (ws && !locOf.has(ws))
|
|
21
|
+
locOf.set(ws, loc);
|
|
22
|
+
}
|
|
23
|
+
const [first, ...rest] = locOf.keys();
|
|
24
|
+
if (!first || !rest.length)
|
|
25
|
+
return locations;
|
|
26
|
+
// matched deps, walking through unmatched workspaces
|
|
27
|
+
const getDeps = (ws) => {
|
|
28
|
+
const found = [];
|
|
29
|
+
const seen = new Set([ws]);
|
|
30
|
+
const queue = monorepo.getDeps(ws);
|
|
31
|
+
for (const dep of queue) {
|
|
32
|
+
if (seen.has(dep))
|
|
33
|
+
continue;
|
|
34
|
+
seen.add(dep);
|
|
35
|
+
if (locOf.has(dep))
|
|
36
|
+
found.push(dep);
|
|
37
|
+
else
|
|
38
|
+
queue.push(...monorepo.getDeps(dep));
|
|
39
|
+
}
|
|
40
|
+
return found;
|
|
41
|
+
};
|
|
42
|
+
// DFS post-order: deps first, ties in query order
|
|
43
|
+
const sorted = graphRunSync({
|
|
44
|
+
graph: [first, ...rest],
|
|
45
|
+
getDeps,
|
|
46
|
+
visit: () => { },
|
|
47
|
+
}).keys();
|
|
48
|
+
const placed = new Set();
|
|
49
|
+
return slots.map(([loc, ws]) => {
|
|
50
|
+
if (!ws || placed.has(ws))
|
|
51
|
+
return loc;
|
|
52
|
+
placed.add(ws);
|
|
53
|
+
const next = sorted.next().value;
|
|
54
|
+
/* c8 ignore next - same count as placed slots */
|
|
55
|
+
return (next && locOf.get(next)) ?? loc;
|
|
56
|
+
});
|
|
57
|
+
};
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vltpkg/cli-sdk",
|
|
3
3
|
"description": "The source for the vlt CLI",
|
|
4
|
-
"version": "1.
|
|
4
|
+
"version": "1.4.0",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
7
7
|
"url": "git+https://github.com/vltpkg/vltpkg.git",
|
|
@@ -14,33 +14,33 @@
|
|
|
14
14
|
},
|
|
15
15
|
"dependencies": {
|
|
16
16
|
"@resvg/resvg-wasm": "^2.6.2",
|
|
17
|
-
"@vltpkg/config": "1.
|
|
18
|
-
"@vltpkg/dep-id": "1.
|
|
19
|
-
"@vltpkg/dot-prop": "1.
|
|
20
|
-
"@vltpkg/error-cause": "1.
|
|
21
|
-
"@vltpkg/git": "1.
|
|
22
|
-
"@vltpkg/graph": "1.
|
|
23
|
-
"@vltpkg/graph-run": "1.
|
|
24
|
-
"@vltpkg/init": "1.
|
|
25
|
-
"@vltpkg/output": "1.
|
|
26
|
-
"@vltpkg/package-info": "1.
|
|
27
|
-
"@vltpkg/package-json": "1.
|
|
28
|
-
"@vltpkg/pick-manifest": "1.
|
|
29
|
-
"@vltpkg/promise-spawn": "1.
|
|
30
|
-
"@vltpkg/query": "1.
|
|
31
|
-
"@vltpkg/registry-client": "1.
|
|
32
|
-
"@vltpkg/rollback-remove": "1.
|
|
33
|
-
"@vltpkg/run": "1.
|
|
34
|
-
"@vltpkg/security-archive": "1.
|
|
35
|
-
"@vltpkg/semver": "1.
|
|
36
|
-
"@vltpkg/spec": "1.
|
|
37
|
-
"@vltpkg/tar": "1.
|
|
38
|
-
"@vltpkg/types": "1.
|
|
39
|
-
"@vltpkg/url-open": "1.
|
|
40
|
-
"@vltpkg/vlt-json": "1.
|
|
41
|
-
"@vltpkg/vlx": "1.
|
|
42
|
-
"@vltpkg/workspaces": "1.
|
|
43
|
-
"@vltpkg/xdg": "1.
|
|
17
|
+
"@vltpkg/config": "1.4.0",
|
|
18
|
+
"@vltpkg/dep-id": "1.4.0",
|
|
19
|
+
"@vltpkg/dot-prop": "1.4.0",
|
|
20
|
+
"@vltpkg/error-cause": "1.4.0",
|
|
21
|
+
"@vltpkg/git": "1.4.0",
|
|
22
|
+
"@vltpkg/graph": "1.4.0",
|
|
23
|
+
"@vltpkg/graph-run": "1.4.0",
|
|
24
|
+
"@vltpkg/init": "1.4.0",
|
|
25
|
+
"@vltpkg/output": "1.4.0",
|
|
26
|
+
"@vltpkg/package-info": "1.4.0",
|
|
27
|
+
"@vltpkg/package-json": "1.4.0",
|
|
28
|
+
"@vltpkg/pick-manifest": "1.4.0",
|
|
29
|
+
"@vltpkg/promise-spawn": "1.4.0",
|
|
30
|
+
"@vltpkg/query": "1.4.0",
|
|
31
|
+
"@vltpkg/registry-client": "1.4.0",
|
|
32
|
+
"@vltpkg/rollback-remove": "1.4.0",
|
|
33
|
+
"@vltpkg/run": "1.4.0",
|
|
34
|
+
"@vltpkg/security-archive": "1.4.0",
|
|
35
|
+
"@vltpkg/semver": "1.4.0",
|
|
36
|
+
"@vltpkg/spec": "1.4.0",
|
|
37
|
+
"@vltpkg/tar": "1.4.0",
|
|
38
|
+
"@vltpkg/types": "1.4.0",
|
|
39
|
+
"@vltpkg/url-open": "1.4.0",
|
|
40
|
+
"@vltpkg/vlt-json": "1.4.0",
|
|
41
|
+
"@vltpkg/vlx": "1.4.0",
|
|
42
|
+
"@vltpkg/workspaces": "1.4.0",
|
|
43
|
+
"@vltpkg/xdg": "1.4.0",
|
|
44
44
|
"ansi-to-pre": "^1.0.6",
|
|
45
45
|
"beautiful-mermaid": "^1.1.3",
|
|
46
46
|
"chalk": "^5.6.2",
|