@vltpkg/cli-sdk 1.0.10 → 1.1.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/access.js +40 -4
- package/dist/commands/cache.js +7 -2
- package/dist/commands/deprecate.js +9 -9
- package/dist/commands/dist-tag.js +15 -19
- package/dist/commands/install/reporter.js +7 -0
- package/dist/commands/install.d.ts +6 -0
- package/dist/commands/install.js +20 -2
- package/dist/commands/list.js +25 -13
- package/dist/commands/profile.js +19 -1
- package/dist/commands/publish.js +25 -32
- package/dist/commands/query.js +20 -7
- package/dist/commands/setup.js +1 -2
- package/dist/commands/token.js +3 -1
- package/dist/commands/unpublish.js +35 -30
- package/dist/commands/update.d.ts +1 -0
- package/dist/commands/update.js +18 -1
- package/dist/commands/whoami.js +20 -2
- package/dist/config/definition.d.ts +4 -0
- package/dist/config/definition.js +21 -0
- package/dist/config/index.d.ts +14 -0
- package/dist/config/index.js +110 -5
- package/dist/is-vlt-installed.d.ts +18 -0
- package/dist/is-vlt-installed.js +46 -0
- package/dist/parse-add-remove-args.js +14 -1
- package/dist/persist-spec-config.d.ts +27 -0
- package/dist/persist-spec-config.js +212 -0
- package/dist/print-err.js +14 -2
- package/dist/query-host-contexts.js +2 -6
- package/dist/require-registry.d.ts +12 -0
- package/dist/require-registry.js +29 -0
- package/package.json +25 -25
- package/dist/registry-error-message.d.ts +0 -2
- package/dist/registry-error-message.js +0 -29
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { error } from '@vltpkg/error-cause';
|
|
2
|
-
import { RegistryClient } from '@vltpkg/registry-client';
|
|
2
|
+
import { RegistryClient, assertOk, requestError, } from '@vltpkg/registry-client';
|
|
3
3
|
import { Spec } from '@vltpkg/spec';
|
|
4
|
-
import { asError } from '@vltpkg/types';
|
|
5
4
|
import { commandUsage } from "../config/usage.js";
|
|
6
5
|
import { resolveRegistry } from "../require-registry.js";
|
|
7
6
|
export const needsRegistry = true;
|
|
@@ -87,16 +86,18 @@ export const command = async (conf) => {
|
|
|
87
86
|
});
|
|
88
87
|
}
|
|
89
88
|
catch (err) {
|
|
90
|
-
throw
|
|
91
|
-
|
|
92
|
-
});
|
|
93
|
-
}
|
|
94
|
-
if (packumentResponse.statusCode !== 200) {
|
|
95
|
-
throw error('Package not found on the registry', {
|
|
89
|
+
throw requestError(err, {
|
|
90
|
+
message: 'Failed to fetch package metadata',
|
|
96
91
|
url: packumentUrl,
|
|
97
|
-
response: packumentResponse,
|
|
98
92
|
});
|
|
99
93
|
}
|
|
94
|
+
assertOk(packumentResponse, {
|
|
95
|
+
message: 'Failed to fetch package metadata',
|
|
96
|
+
url: packumentUrl,
|
|
97
|
+
advice: statusCode => statusCode === 404 ?
|
|
98
|
+
`${name} was not found on this registry.`
|
|
99
|
+
: undefined,
|
|
100
|
+
});
|
|
100
101
|
const packument = packumentResponse.json();
|
|
101
102
|
const versions = packument.versions;
|
|
102
103
|
const distTags = packument['dist-tags'];
|
|
@@ -137,16 +138,17 @@ export const command = async (conf) => {
|
|
|
137
138
|
});
|
|
138
139
|
}
|
|
139
140
|
catch (err) {
|
|
140
|
-
throw
|
|
141
|
-
|
|
142
|
-
});
|
|
143
|
-
}
|
|
144
|
-
if (response.statusCode < 200 || response.statusCode >= 300) {
|
|
145
|
-
throw error('Failed to unpublish package version', {
|
|
141
|
+
throw requestError(err, {
|
|
142
|
+
message: 'Failed to unpublish package version',
|
|
146
143
|
url: putUrl,
|
|
147
|
-
|
|
144
|
+
method: 'PUT',
|
|
148
145
|
});
|
|
149
146
|
}
|
|
147
|
+
assertOk(response, {
|
|
148
|
+
message: 'Failed to unpublish package version',
|
|
149
|
+
url: putUrl,
|
|
150
|
+
method: 'PUT',
|
|
151
|
+
});
|
|
150
152
|
}
|
|
151
153
|
else {
|
|
152
154
|
// Unpublish entire package — DELETE the packument
|
|
@@ -159,16 +161,18 @@ export const command = async (conf) => {
|
|
|
159
161
|
});
|
|
160
162
|
}
|
|
161
163
|
catch (err) {
|
|
162
|
-
throw
|
|
163
|
-
|
|
164
|
-
});
|
|
165
|
-
}
|
|
166
|
-
if (packumentResponse.statusCode !== 200) {
|
|
167
|
-
throw error('Package not found on the registry', {
|
|
164
|
+
throw requestError(err, {
|
|
165
|
+
message: 'Failed to fetch package metadata',
|
|
168
166
|
url: packumentUrl,
|
|
169
|
-
response: packumentResponse,
|
|
170
167
|
});
|
|
171
168
|
}
|
|
169
|
+
assertOk(packumentResponse, {
|
|
170
|
+
message: 'Failed to fetch package metadata',
|
|
171
|
+
url: packumentUrl,
|
|
172
|
+
advice: statusCode => statusCode === 404 ?
|
|
173
|
+
`${name} was not found on this registry.`
|
|
174
|
+
: undefined,
|
|
175
|
+
});
|
|
172
176
|
const packument = packumentResponse.json();
|
|
173
177
|
const deleteUrl = new URL(`${encodedName}/-rev/${packument._rev}`, registryUrl);
|
|
174
178
|
let response;
|
|
@@ -184,16 +188,17 @@ export const command = async (conf) => {
|
|
|
184
188
|
});
|
|
185
189
|
}
|
|
186
190
|
catch (err) {
|
|
187
|
-
throw
|
|
188
|
-
|
|
189
|
-
});
|
|
190
|
-
}
|
|
191
|
-
if (response.statusCode < 200 || response.statusCode >= 300) {
|
|
192
|
-
throw error('Failed to unpublish package', {
|
|
191
|
+
throw requestError(err, {
|
|
192
|
+
message: 'Failed to unpublish package',
|
|
193
193
|
url: deleteUrl,
|
|
194
|
-
|
|
194
|
+
method: 'DELETE',
|
|
195
195
|
});
|
|
196
196
|
}
|
|
197
|
+
assertOk(response, {
|
|
198
|
+
message: 'Failed to unpublish package',
|
|
199
|
+
url: deleteUrl,
|
|
200
|
+
method: 'DELETE',
|
|
201
|
+
});
|
|
197
202
|
}
|
|
198
203
|
return {
|
|
199
204
|
name,
|
|
@@ -5,6 +5,7 @@ export declare const needsNpmRegistry = true;
|
|
|
5
5
|
export declare const usage: CommandUsage;
|
|
6
6
|
export declare const views: {
|
|
7
7
|
readonly json: (i: InstallResult) => {
|
|
8
|
+
persistedConfig?: import("../persist-spec-config.ts").SpecConfigPersistPlan | undefined;
|
|
8
9
|
graph: import("@vltpkg/graph").LockfileData;
|
|
9
10
|
buildQueue?: import("@vltpkg/dep-id").DepID[] | undefined;
|
|
10
11
|
message?: string | undefined;
|
package/dist/commands/update.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { update } from '@vltpkg/graph';
|
|
2
2
|
import { error } from '@vltpkg/error-cause';
|
|
3
3
|
import { commandUsage } from "../config/usage.js";
|
|
4
|
+
import { planSpecConfigPersist } from "../persist-spec-config.js";
|
|
5
|
+
import { asUnknownSpecPrefix } from "../require-registry.js";
|
|
4
6
|
import { lazyView } from "../view.js";
|
|
5
7
|
export const needsRegistry = true;
|
|
6
8
|
export const needsNpmRegistry = true;
|
|
@@ -10,6 +12,9 @@ export const usage = () => commandUsage({
|
|
|
10
12
|
description: `Update dependencies to their latest in-range versions.
|
|
11
13
|
Discards the lockfile and resolves dependencies from scratch.`,
|
|
12
14
|
options: {
|
|
15
|
+
'save-config': {
|
|
16
|
+
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.',
|
|
17
|
+
},
|
|
13
18
|
'allow-scripts': {
|
|
14
19
|
value: '<query>',
|
|
15
20
|
description: 'Filter which packages are allowed to run lifecycle scripts using DSS query syntax.',
|
|
@@ -25,6 +30,9 @@ export const views = {
|
|
|
25
30
|
}
|
|
26
31
|
: null),
|
|
27
32
|
graph: i.graph.toJSON(),
|
|
33
|
+
...(i.persistedConfig ?
|
|
34
|
+
{ persistedConfig: i.persistedConfig }
|
|
35
|
+
: null),
|
|
28
36
|
}),
|
|
29
37
|
human: lazyView(async () => (await import('./install/reporter.ts')).InstallReporter),
|
|
30
38
|
};
|
|
@@ -35,6 +43,7 @@ export const command = async (conf) => {
|
|
|
35
43
|
code: 'EUSAGE',
|
|
36
44
|
});
|
|
37
45
|
}
|
|
46
|
+
const persist = planSpecConfigPersist(conf);
|
|
38
47
|
/* c8 ignore start */
|
|
39
48
|
const allowScripts = conf.get('allow-scripts') ?
|
|
40
49
|
String(conf.get('allow-scripts'))
|
|
@@ -43,6 +52,14 @@ export const command = async (conf) => {
|
|
|
43
52
|
const { buildQueue, graph } = await update({
|
|
44
53
|
...conf.options,
|
|
45
54
|
allowScripts,
|
|
55
|
+
}).catch((er) => {
|
|
56
|
+
throw asUnknownSpecPrefix(er);
|
|
46
57
|
});
|
|
47
|
-
|
|
58
|
+
if (persist)
|
|
59
|
+
await conf.addConfigToFile(persist.which, persist.values);
|
|
60
|
+
return {
|
|
61
|
+
buildQueue,
|
|
62
|
+
graph,
|
|
63
|
+
...(persist ? { persistedConfig: persist } : null),
|
|
64
|
+
};
|
|
48
65
|
};
|
package/dist/commands/whoami.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { error } from '@vltpkg/error-cause';
|
|
2
|
+
import { RegistryClient, assertOk } from '@vltpkg/registry-client';
|
|
2
3
|
import { commandUsage } from "../config/usage.js";
|
|
3
4
|
import { resolveRegistry } from "../require-registry.js";
|
|
4
5
|
export const needsRegistry = true;
|
|
@@ -27,7 +28,24 @@ export const views = {
|
|
|
27
28
|
};
|
|
28
29
|
export const command = async (conf) => {
|
|
29
30
|
const rc = new RegistryClient(conf.options);
|
|
30
|
-
const
|
|
31
|
+
const url = new URL('-/whoami', await resolveRegistry(conf));
|
|
32
|
+
const response = await rc.request(url, { useCache: false });
|
|
33
|
+
assertOk(response, {
|
|
34
|
+
message: 'Failed to look up the current user',
|
|
35
|
+
url,
|
|
36
|
+
advice: statusCode => statusCode === 401 || statusCode === 403 ?
|
|
37
|
+
`Not logged in to ${url.origin}. Run \`vlt login\` and try again.`
|
|
38
|
+
: undefined,
|
|
39
|
+
});
|
|
31
40
|
const { username } = response.json();
|
|
41
|
+
if (typeof username !== 'string' || !username) {
|
|
42
|
+
throw error('Registry did not report a username', {
|
|
43
|
+
code: 'EREQUEST',
|
|
44
|
+
url,
|
|
45
|
+
wanted: 'a `username` string',
|
|
46
|
+
found: username,
|
|
47
|
+
response,
|
|
48
|
+
});
|
|
49
|
+
}
|
|
32
50
|
return { username };
|
|
33
51
|
};
|
|
@@ -355,12 +355,16 @@ export declare const definition: import("jackspeak").Jack<{
|
|
|
355
355
|
short: string;
|
|
356
356
|
description: string;
|
|
357
357
|
};
|
|
358
|
+
'save-config': {
|
|
359
|
+
description: string;
|
|
360
|
+
};
|
|
358
361
|
} & {
|
|
359
362
|
'save-dev': import("jackspeak").ConfigOption<"boolean", false, undefined>;
|
|
360
363
|
'save-exact': import("jackspeak").ConfigOption<"boolean", false, undefined>;
|
|
361
364
|
'save-optional': import("jackspeak").ConfigOption<"boolean", false, undefined>;
|
|
362
365
|
'save-peer': import("jackspeak").ConfigOption<"boolean", false, undefined>;
|
|
363
366
|
'save-prod': import("jackspeak").ConfigOption<"boolean", false, undefined>;
|
|
367
|
+
'save-config': import("jackspeak").ConfigOption<"boolean", false, undefined>;
|
|
364
368
|
} & {
|
|
365
369
|
'save-prefix': {
|
|
366
370
|
description: string;
|
|
@@ -151,6 +151,10 @@ export const definition = j
|
|
|
151
151
|
|
|
152
152
|
Command-specific fields may be set in a nested \`command\` object that
|
|
153
153
|
overrides any options defined at the top level.
|
|
154
|
+
|
|
155
|
+
Object type values given in the environment or on the command line
|
|
156
|
+
are merged key by key over the config files (command line wins over
|
|
157
|
+
environment); other values replace them.
|
|
154
158
|
`)
|
|
155
159
|
.flag({
|
|
156
160
|
color: {
|
|
@@ -216,6 +220,12 @@ export const definition = j
|
|
|
216
220
|
alias must be configured (e.g. via \`vlt setup\`) before it
|
|
217
221
|
can be used. The \`gh\` and \`jsr\` aliases have built-in
|
|
218
222
|
defaults that can be overridden here.
|
|
223
|
+
|
|
224
|
+
Aliases given on the command line or in
|
|
225
|
+
\`VLT_REGISTRIES\` (newline-delimited) merge with the
|
|
226
|
+
configured ones, later sources winning per key. Pass
|
|
227
|
+
\`--save-config\` to \`install\`, \`add\` or
|
|
228
|
+
\`update\` to save them to \`vlt.json\`.
|
|
219
229
|
`,
|
|
220
230
|
},
|
|
221
231
|
'scoped-registries': {
|
|
@@ -656,6 +666,17 @@ export const definition = j
|
|
|
656
666
|
devDependencies or optionalDependencies, but you want to
|
|
657
667
|
move it to be a non-optional production dependency.`,
|
|
658
668
|
},
|
|
669
|
+
'save-config': {
|
|
670
|
+
description: `Save the registry and git host options given on the
|
|
671
|
+
command line or via \`VLT_*\` env (\`--registry\`,
|
|
672
|
+
\`--registries\`, \`--default-registry-alias\`,
|
|
673
|
+
\`--scoped-registries\`, \`--jsr-registries\`,
|
|
674
|
+
\`--git-hosts\`, \`--git-host-archives\`) to the
|
|
675
|
+
project \`vlt.json\` (or the user one with
|
|
676
|
+
\`--config=user\`) after a successful \`install\`,
|
|
677
|
+
\`add\` or \`update\`. Values a config file already
|
|
678
|
+
has are left alone.`,
|
|
679
|
+
},
|
|
659
680
|
})
|
|
660
681
|
.opt({
|
|
661
682
|
'save-prefix': {
|
package/dist/config/index.d.ts
CHANGED
|
@@ -43,6 +43,11 @@ export type PairsAsRecords = ConfigOptionsNoExtras & {
|
|
|
43
43
|
* optional, since a config file only sets what it sets.
|
|
44
44
|
*/
|
|
45
45
|
export type ConfigFileLayer = Partial<PairsAsRecords>;
|
|
46
|
+
/**
|
|
47
|
+
* `registries` alias names are used as spec prefixes, where `~` is
|
|
48
|
+
* reserved, and an empty name has nothing to prefix with.
|
|
49
|
+
*/
|
|
50
|
+
export declare const assertRegistryKeys: (registries: unknown, file: string) => void;
|
|
46
51
|
export declare const pairsToRecords: (obj: NonNullable<ConfigFileData> | OptionsResults<ConfigDefinitions>) => PairsAsRecords;
|
|
47
52
|
export declare const recordsToPairs: (obj: RecordPairs) => RecordPairs;
|
|
48
53
|
export type ConfigDataNoCommand = {
|
|
@@ -145,6 +150,15 @@ export declare class Config {
|
|
|
145
150
|
* Parse the arguments and set configuration and positionals accordingly.
|
|
146
151
|
*/
|
|
147
152
|
parse(args?: string[]): this & ParsedConfig;
|
|
153
|
+
/**
|
|
154
|
+
* Values set on the command line or via `VLT_*` env for this run.
|
|
155
|
+
* Record fields hold only those `key=value` pairs, not the ones from
|
|
156
|
+
* config files.
|
|
157
|
+
*
|
|
158
|
+
* `VLT_*` env left as a parent vlt process wrote it only counts for
|
|
159
|
+
* what was explicit in that parent.
|
|
160
|
+
*/
|
|
161
|
+
explicit: ConfigData;
|
|
148
162
|
/**
|
|
149
163
|
* Get a `key=value` list option value as an object.
|
|
150
164
|
*
|
package/dist/config/index.js
CHANGED
|
@@ -25,7 +25,7 @@ import { PackageInfoClient } from '@vltpkg/package-info';
|
|
|
25
25
|
import { PackageJson } from '@vltpkg/package-json';
|
|
26
26
|
import { resetCaches } from '@vltpkg/dep-id';
|
|
27
27
|
import { getOptions } from '@vltpkg/spec';
|
|
28
|
-
import { assertRecordStringString, assertRecordStringT, isRecordStringString, } from '@vltpkg/types';
|
|
28
|
+
import { assertRecordStringString, assertRecordStringT, isObject, isRecordStringString, } from '@vltpkg/types';
|
|
29
29
|
import { find, load, reload, save } from '@vltpkg/vlt-json';
|
|
30
30
|
import { Monorepo } from '@vltpkg/workspaces';
|
|
31
31
|
import { readFile, rm, writeFile } from 'node:fs/promises';
|
|
@@ -58,7 +58,7 @@ const isRecordFieldValue = (k, v) => Array.isArray(v) &&
|
|
|
58
58
|
* `registries` alias names are used as spec prefixes, where `~` is
|
|
59
59
|
* reserved, and an empty name has nothing to prefix with.
|
|
60
60
|
*/
|
|
61
|
-
const assertRegistryKeys = (registries, file) => {
|
|
61
|
+
export const assertRegistryKeys = (registries, file) => {
|
|
62
62
|
if (!registries || typeof registries !== 'object')
|
|
63
63
|
return;
|
|
64
64
|
const obj = Array.isArray(registries) ?
|
|
@@ -67,6 +67,7 @@ const assertRegistryKeys = (registries, file) => {
|
|
|
67
67
|
for (const key of Object.keys(obj)) {
|
|
68
68
|
if (key === '' || key.includes('~')) {
|
|
69
69
|
throw error('Reserved character found in registries name', {
|
|
70
|
+
code: 'ECONFIG',
|
|
70
71
|
path: file,
|
|
71
72
|
found: key,
|
|
72
73
|
});
|
|
@@ -110,6 +111,35 @@ export const recordsToPairs = (obj) => {
|
|
|
110
111
|
]));
|
|
111
112
|
};
|
|
112
113
|
const kRecord = Symbol('parsed key=value record');
|
|
114
|
+
// jackspeak's env var name for a field
|
|
115
|
+
const envKey = (k) => `VLT_${k.replace(/[^a-zA-Z0-9]+/g, '_').toUpperCase()}`;
|
|
116
|
+
// `parse()` writes every resolved value to `VLT_*`, so a nested vlt
|
|
117
|
+
// can't tell inherited env from env set for it. this holds the
|
|
118
|
+
// parent's explicit values and the `VLT_*` env it wrote.
|
|
119
|
+
const kParentEnv = '__VLT_INTERNAL_EXPLICIT';
|
|
120
|
+
const parentEnv = () => {
|
|
121
|
+
try {
|
|
122
|
+
const p = JSON.parse(process.env[kParentEnv] ?? '');
|
|
123
|
+
return (isObject(p) &&
|
|
124
|
+
isObject(p.explicit) &&
|
|
125
|
+
Object.entries(p.explicit).every(([k, v]) => isRecordField(k) ?
|
|
126
|
+
Array.isArray(v) && v.every(x => typeof x === 'string')
|
|
127
|
+
: !isObject(v)) &&
|
|
128
|
+
isRecordStringString(p.env)) ?
|
|
129
|
+
p
|
|
130
|
+
: undefined;
|
|
131
|
+
}
|
|
132
|
+
catch {
|
|
133
|
+
return undefined;
|
|
134
|
+
}
|
|
135
|
+
};
|
|
136
|
+
// last pair per key wins, keeping the first one's position
|
|
137
|
+
const dedupePairs = (pairs) => [
|
|
138
|
+
...new Map(pairs.map(p => {
|
|
139
|
+
const eq = p.indexOf('=');
|
|
140
|
+
return [eq === -1 ? p : p.substring(0, eq), p];
|
|
141
|
+
})).values(),
|
|
142
|
+
];
|
|
113
143
|
/**
|
|
114
144
|
* Class that handles configuration for vlt.
|
|
115
145
|
*
|
|
@@ -220,18 +250,63 @@ export class Config {
|
|
|
220
250
|
return this;
|
|
221
251
|
// Store the original args for potential reload
|
|
222
252
|
this.#originalArgs = [...args];
|
|
253
|
+
const envKeys = Object.keys(defaultValues).filter(k => process.env[envKey(k)] !== undefined);
|
|
254
|
+
const parent = parentEnv();
|
|
255
|
+
// file layer values, before env replaces them
|
|
256
|
+
const fileValues = this.#defaults();
|
|
223
257
|
this.jack.loadEnvDefaults();
|
|
258
|
+
const envValues = this.#defaults();
|
|
224
259
|
const p = this.jack.parseRaw(args);
|
|
260
|
+
const cliKeys = Object.keys(p.values);
|
|
225
261
|
const fallback = getCommand(p.values['fallback-command']);
|
|
226
262
|
this.command = getCommand(p.positionals[0]);
|
|
227
263
|
const cmdOrFallback = this.command ?? fallback;
|
|
228
264
|
const cmdSpecific = cmdOrFallback && this.commandValues[cmdOrFallback];
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
265
|
+
const cmdPairs = cmdSpecific ? recordsToPairs(cmdSpecific) : {};
|
|
266
|
+
if (cmdSpecific)
|
|
267
|
+
this.jack.setConfigValues(cmdPairs);
|
|
232
268
|
// ok, applied cmd-specific defaults, do rest of the parse
|
|
233
269
|
this.jack.applyDefaults(p);
|
|
270
|
+
// what was set on the cli or env for this run. record fields hold
|
|
271
|
+
// only those pairs, env then cli, so later ones win per key.
|
|
272
|
+
const values = p.values;
|
|
273
|
+
const explicit = {};
|
|
274
|
+
for (const k of new Set([...envKeys, ...cliKeys])) {
|
|
275
|
+
const cli = cliKeys.includes(k) ? values[k] : undefined;
|
|
276
|
+
const env = this.#explicitEnv(k, envValues[k], parent);
|
|
277
|
+
if (isRecordField(k)) {
|
|
278
|
+
const pairs = [
|
|
279
|
+
...(env ?? []),
|
|
280
|
+
...(cli ?? []),
|
|
281
|
+
];
|
|
282
|
+
if (pairs.length)
|
|
283
|
+
explicit[k] = dedupePairs(pairs);
|
|
284
|
+
}
|
|
285
|
+
else if (cli !== undefined)
|
|
286
|
+
explicit[k] = cli;
|
|
287
|
+
// a command block beats env for scalars
|
|
288
|
+
else if (env !== undefined && !(k in cmdPairs))
|
|
289
|
+
explicit[k] = env;
|
|
290
|
+
}
|
|
291
|
+
this.explicit = explicit;
|
|
292
|
+
// record fields merge per key: file (or command block) -> env -> cli
|
|
293
|
+
for (const k of recordFields) {
|
|
294
|
+
const env = envKeys.includes(k) ? envValues[k] : [];
|
|
295
|
+
const cli = cliKeys.includes(k) ? values[k] : [];
|
|
296
|
+
const pairs = [...env, ...cli];
|
|
297
|
+
if (!pairs.length)
|
|
298
|
+
continue;
|
|
299
|
+
const base = (k in cmdPairs ? cmdPairs[k] : fileValues[k]) ?? [];
|
|
300
|
+
values[k] = dedupePairs([...base, ...pairs]);
|
|
301
|
+
}
|
|
234
302
|
this.jack.writeEnv(p);
|
|
303
|
+
process.env[kParentEnv] = JSON.stringify({
|
|
304
|
+
explicit,
|
|
305
|
+
env: Object.fromEntries(Object.keys(defaultValues).flatMap(k => {
|
|
306
|
+
const v = process.env[envKey(k)];
|
|
307
|
+
return v === undefined ? [] : [[envKey(k), v]];
|
|
308
|
+
})),
|
|
309
|
+
});
|
|
235
310
|
if (this.command)
|
|
236
311
|
p.positionals.shift();
|
|
237
312
|
else
|
|
@@ -249,6 +324,36 @@ export class Config {
|
|
|
249
324
|
/* c8 ignore stop */
|
|
250
325
|
return this;
|
|
251
326
|
}
|
|
327
|
+
/**
|
|
328
|
+
* Values set on the command line or via `VLT_*` env for this run.
|
|
329
|
+
* Record fields hold only those `key=value` pairs, not the ones from
|
|
330
|
+
* config files.
|
|
331
|
+
*
|
|
332
|
+
* `VLT_*` env left as a parent vlt process wrote it only counts for
|
|
333
|
+
* what was explicit in that parent.
|
|
334
|
+
*/
|
|
335
|
+
explicit = {};
|
|
336
|
+
// a field's explicit env value: all of it at the top level, the new
|
|
337
|
+
// pairs (or changed scalar) when inherited from a parent vlt. once a
|
|
338
|
+
// script changes a record var, the parent's explicit pairs are dropped.
|
|
339
|
+
#explicitEnv(k, value, parent) {
|
|
340
|
+
const cur = process.env[envKey(k)];
|
|
341
|
+
if (cur === undefined)
|
|
342
|
+
return undefined;
|
|
343
|
+
const written = parent?.env[envKey(k)];
|
|
344
|
+
if (cur === written)
|
|
345
|
+
return parent?.explicit[k];
|
|
346
|
+
if (!isRecordField(k) || written === undefined)
|
|
347
|
+
return value;
|
|
348
|
+
const old = new Set(written.split('\n'));
|
|
349
|
+
return value.filter(p => !old.has(p));
|
|
350
|
+
}
|
|
351
|
+
// the current jack defaults, ie config file (+ env, once loaded) values
|
|
352
|
+
#defaults() {
|
|
353
|
+
const d = { values: {}, positionals: [] };
|
|
354
|
+
this.jack.applyDefaults(d);
|
|
355
|
+
return d.values;
|
|
356
|
+
}
|
|
252
357
|
/**
|
|
253
358
|
* Get a `key=value` list option value as an object.
|
|
254
359
|
*
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Whether `node_modules` in `projectRoot` was built by `vlt install`.
|
|
3
|
+
*
|
|
4
|
+
* The store dir `node_modules/.vlt` is only ever created by reify, so it
|
|
5
|
+
* is the primary marker. A dependency-less install creates no store, so
|
|
6
|
+
* the hidden lockfile it writes is accepted as the fallback. Outside
|
|
7
|
+
* of reify, the `actual.load` fs walk and `vlt build` only write that
|
|
8
|
+
* file when the store exists, so a project installed by another client
|
|
9
|
+
* never gains either.
|
|
10
|
+
*/
|
|
11
|
+
export declare const isVltInstalled: (projectRoot: string) => boolean;
|
|
12
|
+
/**
|
|
13
|
+
* Throws when `node_modules` was not installed by vlt. Without a vlt
|
|
14
|
+
* install `actual.load` walks a foreign `node_modules`, recognizes
|
|
15
|
+
* nothing and reports every dependency as missing - an empty answer
|
|
16
|
+
* that looks like a real one.
|
|
17
|
+
*/
|
|
18
|
+
export declare const assertVltInstalled: (projectRoot: string, command: string) => void;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { lstatSync } from 'node:fs';
|
|
2
|
+
import { resolve } from 'node:path';
|
|
3
|
+
import { error } from '@vltpkg/error-cause';
|
|
4
|
+
// `throwIfNoEntry: false` only covers ENOENT: a `node_modules` that is a
|
|
5
|
+
// file (ENOTDIR) or unreadable (EACCES) still throws, and neither of
|
|
6
|
+
// those is a vlt install.
|
|
7
|
+
const lstat = (path) => {
|
|
8
|
+
try {
|
|
9
|
+
return lstatSync(path, { throwIfNoEntry: false });
|
|
10
|
+
}
|
|
11
|
+
catch {
|
|
12
|
+
return undefined;
|
|
13
|
+
}
|
|
14
|
+
};
|
|
15
|
+
const isDir = (path) => !!lstat(path)?.isDirectory();
|
|
16
|
+
const isFile = (path) => !!lstat(path)?.isFile();
|
|
17
|
+
/**
|
|
18
|
+
* Whether `node_modules` in `projectRoot` was built by `vlt install`.
|
|
19
|
+
*
|
|
20
|
+
* The store dir `node_modules/.vlt` is only ever created by reify, so it
|
|
21
|
+
* is the primary marker. A dependency-less install creates no store, so
|
|
22
|
+
* the hidden lockfile it writes is accepted as the fallback. Outside
|
|
23
|
+
* of reify, the `actual.load` fs walk and `vlt build` only write that
|
|
24
|
+
* file when the store exists, so a project installed by another client
|
|
25
|
+
* never gains either.
|
|
26
|
+
*/
|
|
27
|
+
export const isVltInstalled = (projectRoot) => isDir(resolve(projectRoot, 'node_modules/.vlt')) ||
|
|
28
|
+
isFile(resolve(projectRoot, 'node_modules/.vlt-lock.json'));
|
|
29
|
+
/**
|
|
30
|
+
* Throws when `node_modules` was not installed by vlt. Without a vlt
|
|
31
|
+
* install `actual.load` walks a foreign `node_modules`, recognizes
|
|
32
|
+
* nothing and reports every dependency as missing - an empty answer
|
|
33
|
+
* that looks like a real one.
|
|
34
|
+
*/
|
|
35
|
+
export const assertVltInstalled = (projectRoot, command) => {
|
|
36
|
+
if (isVltInstalled(projectRoot))
|
|
37
|
+
return;
|
|
38
|
+
const nodeModules = resolve(projectRoot, 'node_modules');
|
|
39
|
+
const hint = isDir(nodeModules) ?
|
|
40
|
+
`run \`vlt install\` to rebuild it before running \`vlt ${command}\``
|
|
41
|
+
: `run \`vlt install\` before running \`vlt ${command}\``;
|
|
42
|
+
throw error(`No vlt install found in ${nodeModules}\n\n ${hint}`, {
|
|
43
|
+
code: 'EQUERY',
|
|
44
|
+
path: nodeModules,
|
|
45
|
+
});
|
|
46
|
+
};
|
|
@@ -2,6 +2,7 @@ import { addKey, asDependency } from '@vltpkg/graph';
|
|
|
2
2
|
import { joinDepIDTuple, splitDepID } from '@vltpkg/dep-id';
|
|
3
3
|
import { Spec } from '@vltpkg/spec';
|
|
4
4
|
import { isAbsolute, relative, resolve } from 'node:path';
|
|
5
|
+
import { asUnknownSpecPrefix } from "./require-registry.js";
|
|
5
6
|
const rootDepID = joinDepIDTuple(['file', '.']);
|
|
6
7
|
/**
|
|
7
8
|
* Compute a DepID for the current working directory relative to the project
|
|
@@ -76,6 +77,18 @@ const rebaseFileSpec = (spec, importerPath, scurryCwd, specOptions, monorepo) =>
|
|
|
76
77
|
return spec;
|
|
77
78
|
return Spec.parseArgs(rebasedPath, specOptions);
|
|
78
79
|
};
|
|
80
|
+
/**
|
|
81
|
+
* Parse a positional arg, turning an unknown `name:` prefix into an
|
|
82
|
+
* `ECONFIG` that says how to define it.
|
|
83
|
+
*/
|
|
84
|
+
const parseArg = (item, options) => {
|
|
85
|
+
try {
|
|
86
|
+
return Spec.parseArgs(item, options);
|
|
87
|
+
}
|
|
88
|
+
catch (er) {
|
|
89
|
+
throw asUnknownSpecPrefix(er);
|
|
90
|
+
}
|
|
91
|
+
};
|
|
79
92
|
/**
|
|
80
93
|
* Parses the positional arguments into {@link AddImportersDependenciesMap}.
|
|
81
94
|
*/
|
|
@@ -90,7 +103,7 @@ export const parseAddArgs = (config, scurry, monorepo) => {
|
|
|
90
103
|
const parsedItems = [];
|
|
91
104
|
let hasFileSpecs = false;
|
|
92
105
|
for (const item of items) {
|
|
93
|
-
const spec =
|
|
106
|
+
const spec = parseArg(item, specOptions);
|
|
94
107
|
parsedItems.push({ spec, type });
|
|
95
108
|
if (spec.type === 'file')
|
|
96
109
|
hasFileSpecs = true;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* With `--save-config`, save registry and git host config given on the
|
|
3
|
+
* command line or via `VLT_*` env to `install` / `update`, so that the
|
|
4
|
+
* config files plus the lockfile can reproduce the install.
|
|
5
|
+
* @module
|
|
6
|
+
*/
|
|
7
|
+
import type { WhichConfig } from '@vltpkg/vlt-json';
|
|
8
|
+
import type { ConfigFileData, LoadedConfig } from './config/index.ts';
|
|
9
|
+
/** The config fields that change how specs are parsed. */
|
|
10
|
+
export declare const specConfigFields: readonly ["registry", "registries", "default-registry-alias", "scoped-registries", "jsr-registries", "git-hosts", "git-host-archives"];
|
|
11
|
+
export type SpecConfigPersistPlan = {
|
|
12
|
+
which: WhichConfig;
|
|
13
|
+
values: ConfigFileData;
|
|
14
|
+
};
|
|
15
|
+
/**
|
|
16
|
+
* The spec config set on the cli / env that the target config file (the
|
|
17
|
+
* project `vlt.json`, unless `--config=user`) should get. `undefined`
|
|
18
|
+
* unless `--save-config` is set. Values already in effect from a config
|
|
19
|
+
* file or builtin are left alone. Throws `ECONFIG` when the target file
|
|
20
|
+
* (or its `command.<cmd>` block) sets a value to something else, or a
|
|
21
|
+
* value is invalid.
|
|
22
|
+
*
|
|
23
|
+
* Call before installing, write the result after it succeeds.
|
|
24
|
+
*/
|
|
25
|
+
export declare const planSpecConfigPersist: (conf: LoadedConfig) => SpecConfigPersistPlan | undefined;
|
|
26
|
+
/** `field=value` / `field.key=value` lines for a plan's values. */
|
|
27
|
+
export declare const persistedEntries: (values: ConfigFileData) => string[];
|