package-management 0.0.16 → 0.2.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/LICENSE +21 -0
- package/dist/index.cjs +548 -359
- package/dist/index.d.cts +249 -73
- package/dist/index.d.mts +249 -73
- package/dist/index.d.ts +249 -73
- package/dist/index.mjs +538 -361
- package/package.json +11 -13
package/dist/index.mjs
CHANGED
|
@@ -1,25 +1,96 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
3
|
-
import process from 'node:process';
|
|
4
|
-
import path from 'node:path';
|
|
1
|
+
import path, { isAbsolute, dirname, normalize, join, extname } from 'pathe';
|
|
2
|
+
import util from 'node:util';
|
|
5
3
|
import { fileURLToPath } from 'node:url';
|
|
6
|
-
import {
|
|
4
|
+
import { existsSync, mkdirSync, writeFileSync, readFileSync, accessSync, constants, statSync } from 'node:fs';
|
|
7
5
|
import { asyncCacheFn } from 'async-cache-fn';
|
|
8
6
|
import { execaSync, execa } from 'execa';
|
|
9
7
|
import { findUp } from 'find-up';
|
|
10
|
-
import { readFile, rm } from 'node:fs/promises';
|
|
8
|
+
import { readFile as readFile$1, rm } from 'node:fs/promises';
|
|
11
9
|
import { resolvePathSync } from 'mlly';
|
|
10
|
+
import * as WST from 'workspace-tools';
|
|
11
|
+
import process from 'node:process';
|
|
12
|
+
import path$1 from 'node:path';
|
|
12
13
|
import { globbySync } from 'globby';
|
|
13
14
|
import gitignoreParser from 'parse-gitignore';
|
|
14
|
-
import * as WST from 'workspace-tools';
|
|
15
15
|
import { resolveAlias } from 'pathe/utils';
|
|
16
16
|
import os from 'node:os';
|
|
17
|
-
import {
|
|
18
|
-
import {
|
|
17
|
+
import { parse, applyEdits, modify } from 'jsonc-parser';
|
|
18
|
+
import { stringifyTOML, parseTOML, stringifyYAML, parseYAML, stringifyJSON5, parseJSON5, stringifyJSONC, parseJSONC } from 'confbox';
|
|
19
19
|
import { createStorage, restoreSnapshot, snapshot } from 'unstorage';
|
|
20
20
|
import defineMemoryDriver from 'unstorage/drivers/memory';
|
|
21
21
|
import defineFsLiteDriver from 'unstorage/drivers/fs-lite';
|
|
22
22
|
|
|
23
|
+
const getCallSites = "getCallSites" in util ? util.getCallSites : void 0;
|
|
24
|
+
const MAX_FRAMES = 200;
|
|
25
|
+
function resolveCallerFile(options) {
|
|
26
|
+
const { from, boundaryFunctionName, internalScripts = [] } = options ?? {};
|
|
27
|
+
if (from) return toCallerPath(String(from));
|
|
28
|
+
if (getCallSites === void 0) return void 0;
|
|
29
|
+
const sites = getCallSites(MAX_FRAMES, { sourceMap: true });
|
|
30
|
+
const scriptName = boundaryFunctionName && frameAfterFunction(sites, boundaryFunctionName) || firstForeignFrame(sites, [OWN_SCRIPT, ...internalScripts]);
|
|
31
|
+
return scriptName ? toFilePath(scriptName) : void 0;
|
|
32
|
+
}
|
|
33
|
+
const OWN_SCRIPT = import.meta.url;
|
|
34
|
+
function frameAfterFunction(sites, functionName) {
|
|
35
|
+
const boundary = sites.findIndex((site) => site.functionName === functionName);
|
|
36
|
+
return boundary === -1 ? void 0 : sites[boundary + 1]?.scriptName;
|
|
37
|
+
}
|
|
38
|
+
function firstForeignFrame(sites, internalScripts) {
|
|
39
|
+
const internal = new Set(internalScripts.map(toFilePath));
|
|
40
|
+
return sites.find(
|
|
41
|
+
(site) => (
|
|
42
|
+
// Frames from `node:` internals and evaluated code name no file, so they
|
|
43
|
+
// are never the caller's location however far out they appear.
|
|
44
|
+
isFileScript(site.scriptName) && !internal.has(toFilePath(site.scriptName))
|
|
45
|
+
)
|
|
46
|
+
)?.scriptName;
|
|
47
|
+
}
|
|
48
|
+
const isFileScript = (script) => Boolean(script) && (script.startsWith("file:") || isAbsolute(script));
|
|
49
|
+
const toFilePath = (script) => script.startsWith("file:") ? fileURLToPath(script) : script;
|
|
50
|
+
function toCallerPath(from) {
|
|
51
|
+
if (from.startsWith("file:")) return fileURLToPath(from);
|
|
52
|
+
if (isAbsolute(from)) return from;
|
|
53
|
+
throw new Error(
|
|
54
|
+
`\`from\` must be a file: URL or an absolute path, received ${JSON.stringify(from)}. Pass \`import.meta.url\`.`
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const _filename = (options) => resolveCallerFile(withOwnScript(options));
|
|
59
|
+
const _dirname = (options) => {
|
|
60
|
+
const filePath = resolveCallerFile(withOwnScript(options));
|
|
61
|
+
return filePath === void 0 ? void 0 : dirname(filePath);
|
|
62
|
+
};
|
|
63
|
+
const withOwnScript = (options) => ({
|
|
64
|
+
...options,
|
|
65
|
+
internalScripts: [...options?.internalScripts ?? [], import.meta.url]
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
function createFile(filePath, data, options) {
|
|
69
|
+
const { encoding = "utf-8", ...rest } = typeof options === "string" ? { encoding: options } : options ?? {};
|
|
70
|
+
const dir = path.dirname(filePath);
|
|
71
|
+
const writeFileOptions = { encoding, ...rest };
|
|
72
|
+
if (!existsSync(dir)) {
|
|
73
|
+
mkdirSync(dir, { recursive: true });
|
|
74
|
+
}
|
|
75
|
+
writeFileSync(filePath, data, writeFileOptions);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function readFile(filePath, options) {
|
|
79
|
+
return readFileSync(filePath, options?.encoding ?? "utf-8");
|
|
80
|
+
}
|
|
81
|
+
function readFileSafely(filePath, options) {
|
|
82
|
+
return existsSync(filePath) ? readFile(filePath, options) : void 0;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function isWritable(filename) {
|
|
86
|
+
try {
|
|
87
|
+
accessSync(filename, constants.W_OK);
|
|
88
|
+
return true;
|
|
89
|
+
} catch (e) {
|
|
90
|
+
return false;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
23
94
|
const toArray = (data) => Array.isArray(data) ? data : [data];
|
|
24
95
|
const entriesOf = (o) => Object.entries(o);
|
|
25
96
|
const fromEntries = (entries) => Object.fromEntries(entries);
|
|
@@ -33,7 +104,7 @@ const select = (obj, selection, mode) => {
|
|
|
33
104
|
};
|
|
34
105
|
function normalizePath(path, option) {
|
|
35
106
|
if (typeof option === "function") {
|
|
36
|
-
return
|
|
107
|
+
return option(path);
|
|
37
108
|
}
|
|
38
109
|
if (option === false) {
|
|
39
110
|
return path;
|
|
@@ -45,14 +116,6 @@ const invariant = (predicate, message) => {
|
|
|
45
116
|
throw new Error(message);
|
|
46
117
|
}
|
|
47
118
|
};
|
|
48
|
-
function getArrayItemAtOffset(arr, index, offset = 0) {
|
|
49
|
-
if (arr === void 0 || index === void 0) return void 0;
|
|
50
|
-
return arr[index + offset];
|
|
51
|
-
}
|
|
52
|
-
function isMatching(a, b) {
|
|
53
|
-
if (!a || !b) return false;
|
|
54
|
-
return a === b;
|
|
55
|
-
}
|
|
56
119
|
function checkResult(cb, options) {
|
|
57
120
|
try {
|
|
58
121
|
return buildSingleProp({
|
|
@@ -77,123 +140,16 @@ function buildSingleProp(entry) {
|
|
|
77
140
|
return { [key]: value };
|
|
78
141
|
}
|
|
79
142
|
|
|
80
|
-
function findCallerStackFrame(options) {
|
|
81
|
-
return findErrorStackFrame(options, (frame) => frame.isParentOfRootFunction);
|
|
82
|
-
}
|
|
83
|
-
function findErrorStackFrame(options, find) {
|
|
84
|
-
return getErrorStackFrames(options, find)[0];
|
|
85
|
-
}
|
|
86
|
-
function getErrorStackFrames(options, filter) {
|
|
87
|
-
const { error, rootFunctionName, startFrom = "top" } = options;
|
|
88
|
-
const frames = startFrom === "bottom" ? ErrorStackParser.parse(error).reverse() : ErrorStackParser.parse(error);
|
|
89
|
-
const { parsedFrames } = frames.reduce(
|
|
90
|
-
(acc, frame, index) => {
|
|
91
|
-
const parsed = parseFrame({
|
|
92
|
-
frame,
|
|
93
|
-
index,
|
|
94
|
-
rootFunctionName,
|
|
95
|
-
frames
|
|
96
|
-
});
|
|
97
|
-
const isValid = filter ? filter(parsed) : true;
|
|
98
|
-
if (isValid) {
|
|
99
|
-
acc.parsedFrames.push(parsed);
|
|
100
|
-
}
|
|
101
|
-
return acc;
|
|
102
|
-
},
|
|
103
|
-
{ parsedFrames: [] }
|
|
104
|
-
);
|
|
105
|
-
return parsedFrames ?? [];
|
|
106
|
-
}
|
|
107
|
-
function parseFrame(options) {
|
|
108
|
-
const { frame, frames, index, rootFunctionName, cwd, debug } = options ?? {};
|
|
109
|
-
const functionName = parseFunctionName(frame.functionName);
|
|
110
|
-
const isRootFunction = isMatching(functionName, rootFunctionName);
|
|
111
|
-
const beforeFrameFunctionName = parseFunctionName(
|
|
112
|
-
getArrayItemAtOffset(frames, index, -1)?.functionName
|
|
113
|
-
);
|
|
114
|
-
const isParentOfRootFunction = isMatching(
|
|
115
|
-
beforeFrameFunctionName,
|
|
116
|
-
rootFunctionName
|
|
117
|
-
);
|
|
118
|
-
const fileData = frame.fileName ? getFilePathData({ filepath: frame.fileName, cwd }) : void 0;
|
|
119
|
-
const { isFileInCwd: isFrameInScope, ...restFileData } = fileData ?? {};
|
|
120
|
-
const data = {
|
|
121
|
-
...restFileData,
|
|
122
|
-
functionName,
|
|
123
|
-
source: frame.source,
|
|
124
|
-
sourceFunctionName: frame.functionName,
|
|
125
|
-
isFrameInScope,
|
|
126
|
-
place: placeFormatter(index, frames?.length),
|
|
127
|
-
isRootFunction,
|
|
128
|
-
isParentOfRootFunction,
|
|
129
|
-
rootFunctionName
|
|
130
|
-
};
|
|
131
|
-
debug && console.log(data);
|
|
132
|
-
return data;
|
|
133
|
-
}
|
|
134
|
-
function getFilePathData({
|
|
135
|
-
filepath,
|
|
136
|
-
cwd
|
|
137
|
-
}) {
|
|
138
|
-
const workingDir = cwd ?? process.cwd();
|
|
139
|
-
const filePath = normalize(toFilePath(filepath));
|
|
140
|
-
const fileBasename = basename(filePath);
|
|
141
|
-
const dirPath = dirname(filePath);
|
|
142
|
-
const dirBasename = basename(dirPath);
|
|
143
|
-
const relativeFilePath = relative(workingDir, filePath);
|
|
144
|
-
const relativeDirPath = dirname(relativeFilePath);
|
|
145
|
-
const isFileInCwd = filePath.startsWith(workingDir);
|
|
146
|
-
return {
|
|
147
|
-
filePath,
|
|
148
|
-
dirPath,
|
|
149
|
-
relativeFilePath,
|
|
150
|
-
relativeDirPath,
|
|
151
|
-
fileBasename,
|
|
152
|
-
dirBasename,
|
|
153
|
-
isFileInCwd
|
|
154
|
-
};
|
|
155
|
-
}
|
|
156
|
-
function toFilePath(fileName) {
|
|
157
|
-
return fileName.startsWith("file:") ? fileURLToPath(fileName) : fileName;
|
|
158
|
-
}
|
|
159
|
-
function parseFunctionName(functionName) {
|
|
160
|
-
if (!functionName) return void 0;
|
|
161
|
-
return removeStart(functionName, "Module.");
|
|
162
|
-
}
|
|
163
|
-
function removeStart(input, value) {
|
|
164
|
-
return input.startsWith(value) ? input.slice(value.length) : input;
|
|
165
|
-
}
|
|
166
|
-
function placeFormatter(index, total) {
|
|
167
|
-
if (index === void 0 || !total) return void 0;
|
|
168
|
-
return [index, total].join("/");
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
const _filename = (options) => {
|
|
172
|
-
const { filePath } = findCallerStackFrame({ error: new Error(), ...options }) ?? {};
|
|
173
|
-
return filePath;
|
|
174
|
-
};
|
|
175
|
-
const _dirname = (options) => {
|
|
176
|
-
const { dirPath } = findCallerStackFrame({ error: new Error(), ...options }) ?? {};
|
|
177
|
-
return dirPath;
|
|
178
|
-
};
|
|
179
|
-
|
|
180
143
|
async function resolveModule(module) {
|
|
181
144
|
const resolved = await module;
|
|
182
|
-
|
|
145
|
+
const hasDefault = resolved !== null && typeof resolved === "object" && "default" in resolved;
|
|
146
|
+
return hasDefault ? resolved.default : resolved;
|
|
183
147
|
}
|
|
184
148
|
function isPackageModuleFound(name, options) {
|
|
185
149
|
return Boolean(resolvePackageModulePath(name, options));
|
|
186
150
|
}
|
|
187
151
|
function resolvePackageModulePath(name, options) {
|
|
188
|
-
|
|
189
|
-
[`${name}/package.json`, name],
|
|
190
|
-
options
|
|
191
|
-
);
|
|
192
|
-
if (resolvedPath === void 0) {
|
|
193
|
-
console.error(`Could not resolve package ${name}`);
|
|
194
|
-
return void 0;
|
|
195
|
-
}
|
|
196
|
-
return resolvedPath;
|
|
152
|
+
return findResolvedModulePath([`${name}/package.json`, name], options);
|
|
197
153
|
}
|
|
198
154
|
function findResolvedModulePath(paths, options) {
|
|
199
155
|
for (const path of paths) {
|
|
@@ -215,28 +171,68 @@ function resolveModulePath(modulePath, options) {
|
|
|
215
171
|
}
|
|
216
172
|
}
|
|
217
173
|
|
|
174
|
+
function getPackageFolder(options) {
|
|
175
|
+
return WST.findPackageRoot(options?.cwd ?? process.cwd());
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
function getPackageInfo(options) {
|
|
179
|
+
const packageDir = getPackageFolder(options);
|
|
180
|
+
if (!packageDir) {
|
|
181
|
+
throw new Error(
|
|
182
|
+
`No package.json found searching up from "${options?.cwd ?? process.cwd()}"`
|
|
183
|
+
);
|
|
184
|
+
}
|
|
185
|
+
return readPackageInfo({ packageDir });
|
|
186
|
+
}
|
|
187
|
+
function readPackageInfo({
|
|
188
|
+
packageDir
|
|
189
|
+
}) {
|
|
190
|
+
const packageJsonPath = path$1.join(packageDir, "package.json");
|
|
191
|
+
const packageJson = readPackageJson(packageJsonPath);
|
|
192
|
+
return {
|
|
193
|
+
name: packageJson.name,
|
|
194
|
+
path: packageJsonPath,
|
|
195
|
+
dirpath: packageDir,
|
|
196
|
+
packageJson
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
const packageJsonCache = /* @__PURE__ */ new Map();
|
|
200
|
+
function readPackageJson(packageJsonPath) {
|
|
201
|
+
const { mtimeMs } = statSync(packageJsonPath);
|
|
202
|
+
const cached = packageJsonCache.get(packageJsonPath);
|
|
203
|
+
if (cached?.mtimeMs === mtimeMs) return cached.packageJson;
|
|
204
|
+
const packageJson = JSON.parse(
|
|
205
|
+
readFileSync(packageJsonPath, "utf-8")
|
|
206
|
+
);
|
|
207
|
+
packageJsonCache.set(packageJsonPath, { mtimeMs, packageJson });
|
|
208
|
+
return packageJson;
|
|
209
|
+
}
|
|
210
|
+
|
|
218
211
|
const tsconfig = (tsconfigDir) => ({
|
|
219
212
|
get paths() {
|
|
213
|
+
if (!tsconfigDir) return [];
|
|
220
214
|
return globbySync(["tsconfig.json", "tsconfig.*.json"], {
|
|
221
|
-
cwd: tsconfigDir
|
|
222
|
-
|
|
215
|
+
cwd: tsconfigDir,
|
|
216
|
+
absolute: true
|
|
217
|
+
});
|
|
223
218
|
}
|
|
224
219
|
});
|
|
225
220
|
|
|
226
|
-
function
|
|
227
|
-
return gitignoreParser(
|
|
221
|
+
function parseGitignoreContent(gitignoreContent, options) {
|
|
222
|
+
return gitignoreParser(gitignoreContent, options);
|
|
228
223
|
}
|
|
229
|
-
function getGitignoreData(gitignorePath) {
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
224
|
+
function getGitignoreData(gitignorePath, options) {
|
|
225
|
+
if (!gitignorePath || !existsSync(gitignorePath)) {
|
|
226
|
+
return parseGitignoreContent("", options);
|
|
227
|
+
}
|
|
228
|
+
return parseGitignoreContent(readFileSync(gitignorePath, "utf-8"), options);
|
|
233
229
|
}
|
|
234
|
-
const gitignore = (gitignorePath) => ({
|
|
230
|
+
const gitignore = (gitignorePath, options) => ({
|
|
235
231
|
get data() {
|
|
236
|
-
return getGitignoreData(gitignorePath);
|
|
232
|
+
return getGitignoreData(gitignorePath, options);
|
|
237
233
|
},
|
|
238
234
|
get patterns() {
|
|
239
|
-
return getGitignoreData(gitignorePath).patterns;
|
|
235
|
+
return getGitignoreData(gitignorePath, options).patterns;
|
|
240
236
|
}
|
|
241
237
|
});
|
|
242
238
|
|
|
@@ -299,36 +295,13 @@ function getWorkspaceFolder(options) {
|
|
|
299
295
|
throwIfNotFound = true
|
|
300
296
|
} = options ?? {};
|
|
301
297
|
const getFolder = fallbackToGitRoot ? WST.findProjectRoot : WST.getWorkspaceManagerRoot;
|
|
302
|
-
const folder = getFolder(cwd);
|
|
298
|
+
const folder = checkResult(() => getFolder(cwd)).data;
|
|
303
299
|
if (folder === void 0 && throwIfNotFound) {
|
|
304
300
|
throw new Error(`Could not find workspace folder`);
|
|
305
301
|
}
|
|
306
302
|
return folder;
|
|
307
303
|
}
|
|
308
304
|
|
|
309
|
-
function getPackageFolder(options) {
|
|
310
|
-
return WST.findPackageRoot(options?.cwd ?? process.cwd());
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
function getPackageInfo(options) {
|
|
314
|
-
const packageDir = getPackageFolder(options);
|
|
315
|
-
return readPackageInfo({ packageDir });
|
|
316
|
-
}
|
|
317
|
-
function readPackageInfo({
|
|
318
|
-
packageDir
|
|
319
|
-
}) {
|
|
320
|
-
const packageJsonPath = path.join(packageDir, "package.json");
|
|
321
|
-
const packageJson = JSON.parse(
|
|
322
|
-
readFileSync(packageJsonPath, "utf-8")
|
|
323
|
-
);
|
|
324
|
-
return {
|
|
325
|
-
name: packageJson.name,
|
|
326
|
-
path: packageJsonPath,
|
|
327
|
-
dirpath: packageDir,
|
|
328
|
-
packageJson
|
|
329
|
-
};
|
|
330
|
-
}
|
|
331
|
-
|
|
332
305
|
function getWorkspaceProjectInfo(options) {
|
|
333
306
|
try {
|
|
334
307
|
const workspaceDir = getWorkspaceFolder(options);
|
|
@@ -344,13 +317,21 @@ function getWorkspacePackageInfoList(options) {
|
|
|
344
317
|
const wstList = WST.getWorkspaceInfos(workspaceDir);
|
|
345
318
|
return resolveWstList(wstList);
|
|
346
319
|
}
|
|
320
|
+
function toPackageInfo(info) {
|
|
321
|
+
return {
|
|
322
|
+
name: info.name,
|
|
323
|
+
dirpath: info.path,
|
|
324
|
+
path: join(info.path, "package.json"),
|
|
325
|
+
packageJson: info.packageJson
|
|
326
|
+
};
|
|
327
|
+
}
|
|
347
328
|
function common(options) {
|
|
348
329
|
const { cwd, includeRoot: includeWorkspace } = options ?? {};
|
|
349
330
|
const workspaceDir = getWorkspaceFolder({ cwd });
|
|
350
331
|
function resolveWstList(list) {
|
|
351
332
|
if (!workspaceDir || !list) return [];
|
|
352
|
-
|
|
353
|
-
return
|
|
333
|
+
const packages = list.map(toPackageInfo);
|
|
334
|
+
return includeWorkspace ? [getWorkspaceProjectInfo({ cwd }), ...packages] : packages;
|
|
354
335
|
}
|
|
355
336
|
return {
|
|
356
337
|
workspaceDir,
|
|
@@ -367,7 +348,7 @@ function getWorkspacePackageInfoMap(options) {
|
|
|
367
348
|
|
|
368
349
|
function getProjectInfoByName(name, options) {
|
|
369
350
|
const { cwd } = options ?? {};
|
|
370
|
-
return getWorkspacePackageInfoMap({ cwd })[name];
|
|
351
|
+
return getWorkspacePackageInfoMap({ cwd, includeRoot: true })[name];
|
|
371
352
|
}
|
|
372
353
|
|
|
373
354
|
function getGitRootFolder(options) {
|
|
@@ -382,7 +363,7 @@ function getGitRootFolder(options) {
|
|
|
382
363
|
}
|
|
383
364
|
return void 0;
|
|
384
365
|
}
|
|
385
|
-
return path
|
|
366
|
+
return path.normalize(stdout);
|
|
386
367
|
}
|
|
387
368
|
|
|
388
369
|
function getProjectInfo(folder, options) {
|
|
@@ -413,9 +394,14 @@ function parseWorkspaceFolderTypeOptions(option, defaultOptions) {
|
|
|
413
394
|
|
|
414
395
|
const project = (...args) => {
|
|
415
396
|
const [source, projectOptions] = args;
|
|
416
|
-
const { packageJson, packageJsonPath, packageName, projectDir } = info();
|
|
417
397
|
const {
|
|
418
|
-
|
|
398
|
+
packageJson,
|
|
399
|
+
name: packageName,
|
|
400
|
+
dirpath: projectDir,
|
|
401
|
+
path: packageJsonPath
|
|
402
|
+
} = getProjectInfo(source, projectOptions) ?? {};
|
|
403
|
+
const {
|
|
404
|
+
findPackageManager,
|
|
419
405
|
detectPackageManagers,
|
|
420
406
|
detectLockfilePackageManagers,
|
|
421
407
|
detectGlobalPackageManagers,
|
|
@@ -423,41 +409,29 @@ const project = (...args) => {
|
|
|
423
409
|
filterPackageManagers,
|
|
424
410
|
mapPackageManagers
|
|
425
411
|
} = definePackageManagerClient({ cwd: projectDir });
|
|
412
|
+
const getPackageJson = () => projectDir ? readPackageInfo({ packageDir: projectDir }).packageJson : packageJson;
|
|
426
413
|
return {
|
|
427
414
|
packageJson,
|
|
428
415
|
packageJsonPath,
|
|
429
416
|
packageName,
|
|
430
417
|
projectDir,
|
|
431
|
-
findPackageManager
|
|
418
|
+
findPackageManager,
|
|
432
419
|
detectPackageManagers,
|
|
433
420
|
detectGlobalPackageManagers,
|
|
434
421
|
detectLockfilePackageManagers,
|
|
435
422
|
globalVersions,
|
|
436
423
|
mapPackageManagers,
|
|
437
|
-
|
|
438
|
-
|
|
424
|
+
// Passed through as-is: substituting "" for an unresolved project made
|
|
425
|
+
// both of these read from the calling process's directory instead.
|
|
426
|
+
tsconfig: tsconfig(projectDir),
|
|
427
|
+
gitignore: gitignore(
|
|
428
|
+
projectDir ? path.join(projectDir, ".gitignore") : void 0
|
|
429
|
+
),
|
|
439
430
|
filterPackageManagers,
|
|
440
|
-
getPackageJson
|
|
441
|
-
findDependencyInPackageJson: (options) => findDependencyInPackageJson(options,
|
|
442
|
-
isDependencyInPackageJson: (options) => isDependencyInPackageJson(options,
|
|
431
|
+
getPackageJson,
|
|
432
|
+
findDependencyInPackageJson: (options) => findDependencyInPackageJson(options, getPackageJson()),
|
|
433
|
+
isDependencyInPackageJson: (options) => isDependencyInPackageJson(options, getPackageJson())
|
|
443
434
|
};
|
|
444
|
-
function info() {
|
|
445
|
-
const {
|
|
446
|
-
packageJson: packageJson2,
|
|
447
|
-
name: packageName2,
|
|
448
|
-
dirpath: projectDir2,
|
|
449
|
-
path: packageJsonPath2
|
|
450
|
-
} = getProjectInfo(source, projectOptions) ?? {};
|
|
451
|
-
return {
|
|
452
|
-
packageJson: packageJson2,
|
|
453
|
-
packageJsonPath: packageJsonPath2,
|
|
454
|
-
packageName: packageName2,
|
|
455
|
-
projectDir: projectDir2
|
|
456
|
-
};
|
|
457
|
-
}
|
|
458
|
-
function get(key) {
|
|
459
|
-
return info()[key];
|
|
460
|
-
}
|
|
461
435
|
};
|
|
462
436
|
|
|
463
437
|
function getWorkspacePackageNames(options) {
|
|
@@ -472,18 +446,24 @@ const workspace = {
|
|
|
472
446
|
getProject: project
|
|
473
447
|
};
|
|
474
448
|
|
|
475
|
-
function importer(imports, options) {
|
|
449
|
+
async function importer(imports, options) {
|
|
476
450
|
const { install: defaultInstall = true, installer } = options ?? {};
|
|
451
|
+
const resolved = imports.map((option) => resolveImportOption(option));
|
|
452
|
+
const missing = resolved.filter(
|
|
453
|
+
(option) => Boolean(option.name) && (option.install ?? defaultInstall) && !((option.checkExists ?? true) && isSatisfied(option.name))
|
|
454
|
+
);
|
|
455
|
+
await installMissing(
|
|
456
|
+
missing.filter(({ dev }) => !dev).map(({ name }) => name),
|
|
457
|
+
{ dev: false },
|
|
458
|
+
installer
|
|
459
|
+
);
|
|
460
|
+
await installMissing(
|
|
461
|
+
missing.filter(({ dev }) => dev).map(({ name }) => name),
|
|
462
|
+
{ dev: true },
|
|
463
|
+
installer
|
|
464
|
+
);
|
|
477
465
|
return Promise.all(
|
|
478
|
-
|
|
479
|
-
const importOpt = resolveImportOption(e);
|
|
480
|
-
const shouldInstall = importOpt.install ?? defaultInstall;
|
|
481
|
-
if (shouldInstall && importOpt.name) {
|
|
482
|
-
await installImport(importOpt.name, importOpt, installer);
|
|
483
|
-
}
|
|
484
|
-
const m = await importOpt.import();
|
|
485
|
-
return resolveModule(m);
|
|
486
|
-
})
|
|
466
|
+
resolved.map(async (option) => resolveModule(await option.import()))
|
|
487
467
|
);
|
|
488
468
|
}
|
|
489
469
|
const definePackage = (option) => {
|
|
@@ -507,12 +487,11 @@ function resolveImportOption(option) {
|
|
|
507
487
|
}
|
|
508
488
|
return option;
|
|
509
489
|
}
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
if (checkExists && isPackageDependency(packageName)) return;
|
|
490
|
+
const isSatisfied = (packageName) => isPackageDependency(packageName) && isPackageModuleFound(packageName);
|
|
491
|
+
async function installMissing(packageNames, options, installer) {
|
|
492
|
+
if (packageNames.length === 0) return;
|
|
514
493
|
const installerFn = installer ?? (await workspace.getProject("<package_folder>").findPackageManager()).installPackage;
|
|
515
|
-
await installerFn(
|
|
494
|
+
await installerFn(packageNames, options);
|
|
516
495
|
}
|
|
517
496
|
|
|
518
497
|
const importMap = async (importMap2, options) => {
|
|
@@ -534,20 +513,36 @@ function definePackageManager(config, options) {
|
|
|
534
513
|
const lockfiles = toArray(config.meta.lockfile);
|
|
535
514
|
return await findUp(lockfiles, { cwd });
|
|
536
515
|
});
|
|
516
|
+
const globalVersion = asyncCacheFn(
|
|
517
|
+
async (options2) => {
|
|
518
|
+
try {
|
|
519
|
+
const { stdout } = await $$({
|
|
520
|
+
command,
|
|
521
|
+
args: [agentOptions.version],
|
|
522
|
+
cwd: defaultCwd,
|
|
523
|
+
...options2
|
|
524
|
+
});
|
|
525
|
+
return `${stdout}`.trim();
|
|
526
|
+
} catch {
|
|
527
|
+
return void 0;
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
);
|
|
537
531
|
const installPackage = async (packageName, options2) => {
|
|
538
532
|
const install = agentArgs.install;
|
|
539
|
-
const { dev, preferOffline } =
|
|
540
|
-
|
|
541
|
-
{
|
|
542
|
-
preferOffline: true,
|
|
543
|
-
cwd: defaultCwd,
|
|
544
|
-
...options2
|
|
545
|
-
});
|
|
533
|
+
const { dev = false, preferOffline = true } = options2 ?? {};
|
|
534
|
+
const flags = select(install.options, { dev, preferOffline });
|
|
546
535
|
const packageNames = toArray(packageName);
|
|
536
|
+
assertInstallableNames(packageNames);
|
|
547
537
|
try {
|
|
548
538
|
await $$({
|
|
549
539
|
command,
|
|
550
|
-
args: [
|
|
540
|
+
args: [
|
|
541
|
+
install.command,
|
|
542
|
+
flags.dev,
|
|
543
|
+
flags.preferOffline,
|
|
544
|
+
...packageNames
|
|
545
|
+
],
|
|
551
546
|
cwd: defaultCwd,
|
|
552
547
|
...options2
|
|
553
548
|
});
|
|
@@ -589,21 +584,14 @@ function definePackageManager(config, options) {
|
|
|
589
584
|
readLockfile: asyncCacheFn(async (...args) => {
|
|
590
585
|
const lockfilePath = await findLockfilePath.noCache(...args);
|
|
591
586
|
if (!lockfilePath) return void 0;
|
|
592
|
-
return readFile(lockfilePath, "utf8");
|
|
587
|
+
return readFile$1(lockfilePath, "utf8");
|
|
593
588
|
}),
|
|
594
|
-
globalVersion
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
cwd: defaultCwd,
|
|
601
|
-
...options2
|
|
602
|
-
});
|
|
603
|
-
return `${stdout}`;
|
|
604
|
-
} catch (e) {
|
|
605
|
-
return void 0;
|
|
606
|
-
}
|
|
589
|
+
globalVersion,
|
|
590
|
+
matchesVersion: asyncCacheFn(async (...args) => {
|
|
591
|
+
const { matchesVersion } = config.meta;
|
|
592
|
+
if (!matchesVersion) return true;
|
|
593
|
+
const version = await globalVersion.noCache(...args);
|
|
594
|
+
return version ? matchesVersion(version) : false;
|
|
607
595
|
}),
|
|
608
596
|
definePackage,
|
|
609
597
|
installPackage,
|
|
@@ -617,6 +605,16 @@ function definePackageManager(config, options) {
|
|
|
617
605
|
}
|
|
618
606
|
};
|
|
619
607
|
}
|
|
608
|
+
function assertInstallableNames(packageNames) {
|
|
609
|
+
const rejected = packageNames.filter(
|
|
610
|
+
(name) => name.length === 0 || name.startsWith("-")
|
|
611
|
+
);
|
|
612
|
+
if (rejected.length > 0) {
|
|
613
|
+
throw new Error(
|
|
614
|
+
`Not a package name: ${rejected.map((name) => JSON.stringify(name)).join(", ")}. A package name cannot be empty or begin with "-".`
|
|
615
|
+
);
|
|
616
|
+
}
|
|
617
|
+
}
|
|
620
618
|
async function $$(options) {
|
|
621
619
|
const { command, args = [], silent = true, cwd, shellOptions } = options;
|
|
622
620
|
return execa(command, args.filter(notFalsy), {
|
|
@@ -639,7 +637,9 @@ const bun = definePackageManagerConfig({
|
|
|
639
637
|
command: "bun",
|
|
640
638
|
runner: "bunx",
|
|
641
639
|
meta: {
|
|
642
|
-
lockfile
|
|
640
|
+
// Bun wrote a binary lockfile originally and a text one from 1.2 onwards,
|
|
641
|
+
// so a project using either must still be detected.
|
|
642
|
+
lockfile: ["bun.lock", "bun.lockb"]
|
|
643
643
|
},
|
|
644
644
|
args: {
|
|
645
645
|
install: {
|
|
@@ -650,7 +650,7 @@ const bun = definePackageManagerConfig({
|
|
|
650
650
|
}
|
|
651
651
|
},
|
|
652
652
|
uninstall: {
|
|
653
|
-
command: "
|
|
653
|
+
command: "remove"
|
|
654
654
|
}
|
|
655
655
|
},
|
|
656
656
|
options: {
|
|
@@ -714,7 +714,9 @@ const yarn = definePackageManagerConfig({
|
|
|
714
714
|
command: "yarn",
|
|
715
715
|
runner: "yarn dlx",
|
|
716
716
|
meta: {
|
|
717
|
-
lockfile: "yarn.lock"
|
|
717
|
+
lockfile: "yarn.lock",
|
|
718
|
+
// Berry shares this command and lockfile, so the version decides.
|
|
719
|
+
matchesVersion: (version) => version.startsWith("1.")
|
|
718
720
|
},
|
|
719
721
|
args: {
|
|
720
722
|
install: {
|
|
@@ -733,6 +735,33 @@ const yarn = definePackageManagerConfig({
|
|
|
733
735
|
}
|
|
734
736
|
});
|
|
735
737
|
|
|
738
|
+
const yarnBerry = definePackageManagerConfig({
|
|
739
|
+
id: "yarn-berry",
|
|
740
|
+
name: "Yarn Berry",
|
|
741
|
+
command: "yarn",
|
|
742
|
+
runner: "yarn dlx",
|
|
743
|
+
meta: {
|
|
744
|
+
lockfile: "yarn.lock",
|
|
745
|
+
// Classic shares this command and lockfile; everything past 1.x is Berry.
|
|
746
|
+
matchesVersion: (version) => !version.startsWith("1.")
|
|
747
|
+
},
|
|
748
|
+
args: {
|
|
749
|
+
install: {
|
|
750
|
+
command: "add",
|
|
751
|
+
options: {
|
|
752
|
+
dev: "-D",
|
|
753
|
+
preferOffline: "--cached"
|
|
754
|
+
}
|
|
755
|
+
},
|
|
756
|
+
uninstall: {
|
|
757
|
+
command: "remove"
|
|
758
|
+
}
|
|
759
|
+
},
|
|
760
|
+
options: {
|
|
761
|
+
version: "--version"
|
|
762
|
+
}
|
|
763
|
+
});
|
|
764
|
+
|
|
736
765
|
async function findPackageManager(packageManagers, options) {
|
|
737
766
|
const packageManager = await findPackageManagerSafely(
|
|
738
767
|
packageManagers,
|
|
@@ -762,7 +791,9 @@ async function detectPackageManagers(packageManagers, options) {
|
|
|
762
791
|
async function detectLockfilePackageManagers(packageManagers, options) {
|
|
763
792
|
return filterPackageManagers(
|
|
764
793
|
packageManagers,
|
|
765
|
-
|
|
794
|
+
// Yarn Classic and Berry share a lockfile name, so a lockfile match alone
|
|
795
|
+
// would report both. The version check settles which one it is.
|
|
796
|
+
async (packageManager) => await packageManager.hasLockfile(options) && await packageManager.matchesVersion(options),
|
|
766
797
|
options
|
|
767
798
|
);
|
|
768
799
|
}
|
|
@@ -772,7 +803,7 @@ async function detectGlobalPackageManagers(packageManagers, options) {
|
|
|
772
803
|
// Without the `await`, `Boolean` receives a pending promise and is always
|
|
773
804
|
// `true`, so every package manager passes the filter regardless of whether
|
|
774
805
|
// it is actually installed.
|
|
775
|
-
async (packageManager) => Boolean(await packageManager.globalVersion(options)),
|
|
806
|
+
async (packageManager) => Boolean(await packageManager.globalVersion(options)) && await packageManager.matchesVersion(options),
|
|
776
807
|
options
|
|
777
808
|
);
|
|
778
809
|
}
|
|
@@ -805,7 +836,7 @@ function selectAllowedPackageManagers(packageManagers, options) {
|
|
|
805
836
|
);
|
|
806
837
|
}
|
|
807
838
|
|
|
808
|
-
const packageManagerConfigs = [pnpm, yarn, bun, npm];
|
|
839
|
+
const packageManagerConfigs = [pnpm, yarn, yarnBerry, bun, npm];
|
|
809
840
|
function definePackageManagerClient(options) {
|
|
810
841
|
const configs = packageManagerConfigs.map(
|
|
811
842
|
(config) => definePackageManager(config, options)
|
|
@@ -835,8 +866,11 @@ function definePackageManagerClient(options) {
|
|
|
835
866
|
};
|
|
836
867
|
}
|
|
837
868
|
|
|
838
|
-
function isPackageDependency(packageName) {
|
|
839
|
-
|
|
869
|
+
function isPackageDependency(packageName, options) {
|
|
870
|
+
const { packageJson } = getPackageInfo(options);
|
|
871
|
+
return toArray(packageName).every(
|
|
872
|
+
(name) => isDependencyInPackageJson(name, packageJson)
|
|
873
|
+
);
|
|
840
874
|
}
|
|
841
875
|
|
|
842
876
|
function definePathAliases(aliasDefinitions) {
|
|
@@ -854,42 +888,49 @@ function definePathAliases(aliasDefinitions) {
|
|
|
854
888
|
};
|
|
855
889
|
}
|
|
856
890
|
function getAliasedFilePath(aliasMap, options) {
|
|
857
|
-
const { to, startingFrom, cwd, checkExistence, glob } = typeof options === "string" || Array.isArray(options) ? { to: options } : options;
|
|
858
|
-
const resolveOptions = { cwd, checkExistence, glob, aliasMap };
|
|
891
|
+
const { to, startingFrom, cwd, from, checkExistence, glob } = typeof options === "string" || Array.isArray(options) ? { to: options } : options;
|
|
892
|
+
const resolveOptions = { cwd, from, checkExistence, glob, aliasMap };
|
|
859
893
|
try {
|
|
860
894
|
return startingFrom ? resolveRelativePathTo(to, startingFrom, resolveOptions) : resolvePathTo(to, resolveOptions);
|
|
861
895
|
} catch (error) {
|
|
862
|
-
if (
|
|
896
|
+
if (error instanceof PathNotFoundError) return void 0;
|
|
863
897
|
throw error;
|
|
864
898
|
}
|
|
865
899
|
}
|
|
900
|
+
class PathNotFoundError extends Error {
|
|
901
|
+
}
|
|
866
902
|
function resolveRelativePathTo(to, from, options) {
|
|
867
903
|
const pathFrom = resolvePathTo(from, options);
|
|
868
904
|
const pathTo = resolvePathTo(to, options);
|
|
869
|
-
return path
|
|
905
|
+
return path.relative(pathFrom, pathTo);
|
|
870
906
|
}
|
|
871
|
-
function resolvePathTo(pathTo, { cwd, checkExistence, glob, aliasMap }) {
|
|
872
|
-
const normalized = normalizePathTo(pathTo, { cwd, aliasMap });
|
|
907
|
+
function resolvePathTo(pathTo, { cwd, from, checkExistence, glob, aliasMap }) {
|
|
908
|
+
const normalized = normalizePathTo(pathTo, { cwd, from, aliasMap });
|
|
873
909
|
if (glob) {
|
|
874
910
|
const globPaths = globbySync(normalized, { cwd });
|
|
875
911
|
if (!globPaths[0])
|
|
876
|
-
throw new
|
|
912
|
+
throw new PathNotFoundError(`No paths found for glob: ${normalized}`);
|
|
877
913
|
return globPaths[0];
|
|
878
914
|
}
|
|
879
915
|
if (!glob && checkExistence && !existsSync(normalized))
|
|
880
|
-
throw new
|
|
916
|
+
throw new PathNotFoundError(`Path does not exist: ${normalized}`);
|
|
881
917
|
return normalized;
|
|
882
918
|
}
|
|
883
919
|
function normalizePathTo(pathTo, options) {
|
|
884
|
-
const { cwd, aliasMap } = options ?? {};
|
|
885
|
-
const
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
920
|
+
const { cwd, from, aliasMap } = options ?? {};
|
|
921
|
+
const baseDir = Array.isArray(pathTo) ? pathTo[0] : findAliasToken(pathTo, aliasMap);
|
|
922
|
+
if (baseDir === void 0) return pathTo;
|
|
923
|
+
const baseDirPath = assertResolved(
|
|
924
|
+
baseDir,
|
|
925
|
+
executeMapFn(aliasMap, baseDir, [{ cwd, from }])
|
|
926
|
+
);
|
|
927
|
+
return Array.isArray(pathTo) ? path.join(baseDirPath, ...pathTo.slice(1).filter(isNotNull)) : resolveAlias(pathTo, { [baseDir]: baseDirPath });
|
|
928
|
+
}
|
|
929
|
+
function assertResolved(alias, resolved) {
|
|
930
|
+
if (typeof resolved !== "string" || resolved.length === 0) {
|
|
931
|
+
throw new Error(`Path alias resolved to no location: ${alias}`);
|
|
891
932
|
}
|
|
892
|
-
return
|
|
933
|
+
return resolved;
|
|
893
934
|
}
|
|
894
935
|
function findAliasToken(pathTo, aliasMap) {
|
|
895
936
|
return Object.keys(aliasMap ?? {}).filter((alias) => pathTo === alias || pathTo.startsWith(`${alias}/`)).sort((a, b) => b.length - a.length)[0];
|
|
@@ -907,10 +948,17 @@ function getAliasMap(aliasDefs) {
|
|
|
907
948
|
return [
|
|
908
949
|
[alias, resolve],
|
|
909
950
|
...subpaths.map(({ to }) => {
|
|
910
|
-
const subpathAlias = path
|
|
951
|
+
const subpathAlias = path.join(alias, to);
|
|
911
952
|
return [
|
|
912
953
|
subpathAlias,
|
|
913
|
-
(opts) =>
|
|
954
|
+
(opts) => (
|
|
955
|
+
// The same assertion the bare alias gets. Without it an
|
|
956
|
+
// unresolvable parent produced `join(undefined, "/node_modules")`
|
|
957
|
+
// — a path at the filesystem root — instead of failing.
|
|
958
|
+
resolveAlias(subpathAlias, {
|
|
959
|
+
[alias]: assertResolved(alias, resolve(opts))
|
|
960
|
+
})
|
|
961
|
+
)
|
|
914
962
|
];
|
|
915
963
|
})
|
|
916
964
|
];
|
|
@@ -985,13 +1033,13 @@ const predefinedPathAliases = {
|
|
|
985
1033
|
subpaths: []
|
|
986
1034
|
},
|
|
987
1035
|
"<current_file>": {
|
|
988
|
-
//
|
|
989
|
-
|
|
1036
|
+
// `from` is the caller naming itself, which is exact. Without it the stack
|
|
1037
|
+
// is read, which is a best effort and Node-only.
|
|
1038
|
+
resolve: (opts) => _filename({ from: opts?.from, boundaryFunctionName: "getFilePath" }),
|
|
990
1039
|
subpaths: []
|
|
991
1040
|
},
|
|
992
1041
|
"<current_folder>": {
|
|
993
|
-
|
|
994
|
-
resolve: () => _dirname({ rootFunctionName: "getFilePath" }) ?? "",
|
|
1042
|
+
resolve: (opts) => _dirname({ from: opts?.from, boundaryFunctionName: "getFilePath" }),
|
|
995
1043
|
subpaths: []
|
|
996
1044
|
}
|
|
997
1045
|
};
|
|
@@ -1014,59 +1062,65 @@ const jsonSourceResolvers = {
|
|
|
1014
1062
|
text: (text) => {
|
|
1015
1063
|
return {
|
|
1016
1064
|
text: () => text,
|
|
1017
|
-
|
|
1065
|
+
// `jsonc-parser`'s parse, not `JSON.parse`: the whole point of this
|
|
1066
|
+
// module is editing files like tsconfig.json, which carry comments.
|
|
1067
|
+
data: () => parse(text)
|
|
1018
1068
|
};
|
|
1019
1069
|
},
|
|
1020
1070
|
filepath: (filepath) => {
|
|
1021
1071
|
const text = readFileSync(filepath, "utf-8");
|
|
1022
1072
|
return {
|
|
1023
1073
|
text: () => text,
|
|
1024
|
-
|
|
1074
|
+
// `jsonc-parser`'s parse, not `JSON.parse`: the whole point of this
|
|
1075
|
+
// module is editing files like tsconfig.json, which carry comments.
|
|
1076
|
+
data: () => parse(text)
|
|
1025
1077
|
};
|
|
1026
1078
|
}
|
|
1027
1079
|
};
|
|
1028
1080
|
function resolveJsonSource(source, as) {
|
|
1029
1081
|
const [sourceType, sourceData] = Object.entries(source).find(([_, selected]) => selected !== void 0) ?? [];
|
|
1030
|
-
if (!sourceType || !(sourceType in jsonSourceResolvers) ||
|
|
1082
|
+
if (!sourceType || !(sourceType in jsonSourceResolvers) || sourceData === void 0) {
|
|
1031
1083
|
throw new Error("Invalid source data");
|
|
1032
1084
|
}
|
|
1033
1085
|
const sourceTypeResolvers = jsonSourceResolvers[sourceType](
|
|
1034
1086
|
sourceData
|
|
1035
1087
|
);
|
|
1036
|
-
const resolved =
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1088
|
+
const resolved = fromEntries(
|
|
1089
|
+
entriesOf(sourceTypeResolvers).flatMap(([key, v]) => {
|
|
1090
|
+
if (as && key !== as) return [];
|
|
1091
|
+
try {
|
|
1092
|
+
return [[key, v()]];
|
|
1093
|
+
} catch (error) {
|
|
1094
|
+
throw new Error(`Failed to resolve ${key} from ${sourceType}`, {
|
|
1095
|
+
cause: error
|
|
1096
|
+
});
|
|
1097
|
+
}
|
|
1098
|
+
})
|
|
1099
|
+
);
|
|
1044
1100
|
if (as) {
|
|
1045
1101
|
return resolved[as];
|
|
1046
1102
|
}
|
|
1047
1103
|
return resolved;
|
|
1048
1104
|
}
|
|
1049
1105
|
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
const
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
}
|
|
1060
|
-
function parseJSONCEdits(edits) {
|
|
1106
|
+
const toPathSegment$1 = (segment) => /^\d+$/.test(segment) ? Number(segment) : segment;
|
|
1107
|
+
function resolveEditPath$1(path, options) {
|
|
1108
|
+
const { pathSeparator = "." } = options || {};
|
|
1109
|
+
if (Array.isArray(path)) return path;
|
|
1110
|
+
if (typeof path === "number" || pathSeparator === false) return [path];
|
|
1111
|
+
return path.split(pathSeparator).map(toPathSegment$1);
|
|
1112
|
+
}
|
|
1113
|
+
function parseJSONCEdits(edits, defaultEditOptions) {
|
|
1114
|
+
const resolve = (path, options) => resolveEditPath$1(path, { ...defaultEditOptions, ...options });
|
|
1061
1115
|
if (Array.isArray(edits)) {
|
|
1062
1116
|
return edits.map(({ path, ...edit }) => ({
|
|
1063
|
-
path:
|
|
1117
|
+
path: resolve(path, edit.options),
|
|
1064
1118
|
...edit
|
|
1065
1119
|
}));
|
|
1066
1120
|
}
|
|
1067
1121
|
return Object.entries(edits).map(
|
|
1068
1122
|
([path, value]) => ({
|
|
1069
|
-
path:
|
|
1123
|
+
path: resolve(path, value.options),
|
|
1070
1124
|
...value
|
|
1071
1125
|
})
|
|
1072
1126
|
);
|
|
@@ -1078,14 +1132,17 @@ function modifyJSON({
|
|
|
1078
1132
|
}) {
|
|
1079
1133
|
return checkResult(() => {
|
|
1080
1134
|
const text = resolveJsonSource(json, "text");
|
|
1081
|
-
const jsoncEdits = parseJSONCEdits(edits);
|
|
1082
|
-
const
|
|
1083
|
-
(edit) =>
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1135
|
+
const jsoncEdits = parseJSONCEdits(edits, defaultEditOptions);
|
|
1136
|
+
const updated = jsoncEdits.reduce(
|
|
1137
|
+
(current, edit) => applyEdits(
|
|
1138
|
+
current,
|
|
1139
|
+
modify(current, edit.path, edit.value, {
|
|
1140
|
+
...defaultEditOptions,
|
|
1141
|
+
...edit.options
|
|
1142
|
+
})
|
|
1143
|
+
),
|
|
1144
|
+
text
|
|
1087
1145
|
);
|
|
1088
|
-
const updated = applyEdits(text, editResult);
|
|
1089
1146
|
return resolveJsonSource({ text: updated });
|
|
1090
1147
|
});
|
|
1091
1148
|
}
|
|
@@ -1112,6 +1169,154 @@ function modifyJSONFile(filepath, edits, options) {
|
|
|
1112
1169
|
});
|
|
1113
1170
|
}
|
|
1114
1171
|
|
|
1172
|
+
const configLanguages = {
|
|
1173
|
+
json: { parse: parseJSONC, stringify: stringifyJSONC, surgical: true },
|
|
1174
|
+
jsonc: { parse: parseJSONC, stringify: stringifyJSONC, surgical: true },
|
|
1175
|
+
json5: { parse: parseJSON5, stringify: stringifyJSON5, surgical: false },
|
|
1176
|
+
yaml: { parse: parseYAML, stringify: stringifyYAML, surgical: false },
|
|
1177
|
+
toml: { parse: parseTOML, stringify: stringifyTOML, surgical: false }
|
|
1178
|
+
};
|
|
1179
|
+
const extensionFormats = {
|
|
1180
|
+
".json": "json",
|
|
1181
|
+
".jsonc": "jsonc",
|
|
1182
|
+
".json5": "json5",
|
|
1183
|
+
".yaml": "yaml",
|
|
1184
|
+
".yml": "yaml",
|
|
1185
|
+
".toml": "toml"
|
|
1186
|
+
};
|
|
1187
|
+
function isConfigFormat(value) {
|
|
1188
|
+
return value in configLanguages;
|
|
1189
|
+
}
|
|
1190
|
+
function getConfigFormat(filepath) {
|
|
1191
|
+
const extension = extname(filepath).toLowerCase();
|
|
1192
|
+
return extension in extensionFormats ? extensionFormats[extension] : void 0;
|
|
1193
|
+
}
|
|
1194
|
+
|
|
1195
|
+
function resolveFormat(source) {
|
|
1196
|
+
if (source.format) return source.format;
|
|
1197
|
+
const inferred = source.filepath ? getConfigFormat(source.filepath) : void 0;
|
|
1198
|
+
if (!inferred) {
|
|
1199
|
+
throw new Error(
|
|
1200
|
+
source.filepath ? `Unsupported config format: ${source.filepath}` : "A `format` is required for a text or data source"
|
|
1201
|
+
);
|
|
1202
|
+
}
|
|
1203
|
+
return inferred;
|
|
1204
|
+
}
|
|
1205
|
+
function resolveConfigSource(source, as) {
|
|
1206
|
+
const format = resolveFormat(source);
|
|
1207
|
+
const language = configLanguages[format];
|
|
1208
|
+
const { text: sourceText, data: sourceData, filepath } = source;
|
|
1209
|
+
const resolvers = {
|
|
1210
|
+
data: () => ({
|
|
1211
|
+
text: () => language.stringify(sourceData),
|
|
1212
|
+
data: () => sourceData
|
|
1213
|
+
}),
|
|
1214
|
+
text: () => ({
|
|
1215
|
+
text: () => sourceText,
|
|
1216
|
+
data: () => language.parse(sourceText)
|
|
1217
|
+
}),
|
|
1218
|
+
filepath: () => {
|
|
1219
|
+
const text = readFileSync(filepath, "utf-8");
|
|
1220
|
+
return {
|
|
1221
|
+
text: () => text,
|
|
1222
|
+
data: () => language.parse(text)
|
|
1223
|
+
};
|
|
1224
|
+
}
|
|
1225
|
+
};
|
|
1226
|
+
const selected = ["data", "text", "filepath"].find(
|
|
1227
|
+
(key) => source[key] !== void 0
|
|
1228
|
+
);
|
|
1229
|
+
if (!selected) {
|
|
1230
|
+
throw new Error("Invalid source data");
|
|
1231
|
+
}
|
|
1232
|
+
const sourceResolvers = resolvers[selected]();
|
|
1233
|
+
const resolved = fromEntries(
|
|
1234
|
+
entriesOf(sourceResolvers).flatMap(([key, value]) => {
|
|
1235
|
+
if (as && key !== as) return [];
|
|
1236
|
+
try {
|
|
1237
|
+
return [[key, value()]];
|
|
1238
|
+
} catch (error) {
|
|
1239
|
+
throw new Error(`Failed to resolve ${key} from ${selected}`, {
|
|
1240
|
+
cause: error
|
|
1241
|
+
});
|
|
1242
|
+
}
|
|
1243
|
+
})
|
|
1244
|
+
);
|
|
1245
|
+
if (as) {
|
|
1246
|
+
return resolved[as];
|
|
1247
|
+
}
|
|
1248
|
+
return resolved;
|
|
1249
|
+
}
|
|
1250
|
+
|
|
1251
|
+
const toPathSegment = (segment) => /^\d+$/.test(segment) ? Number(segment) : segment;
|
|
1252
|
+
function resolveEditPath(path, options) {
|
|
1253
|
+
const { pathSeparator = "." } = options || {};
|
|
1254
|
+
if (Array.isArray(path)) return path;
|
|
1255
|
+
if (typeof path === "number" || pathSeparator === false) return [path];
|
|
1256
|
+
return path.split(pathSeparator).map(toPathSegment);
|
|
1257
|
+
}
|
|
1258
|
+
function toEditList(edits, defaultEditOptions) {
|
|
1259
|
+
const entries = Array.isArray(edits) ? edits : Object.entries(edits).map(([path, edit]) => ({ path, ...edit }));
|
|
1260
|
+
return entries.map(({ path, value, options }) => ({
|
|
1261
|
+
path: resolveEditPath(path, { ...defaultEditOptions, ...options }),
|
|
1262
|
+
value
|
|
1263
|
+
}));
|
|
1264
|
+
}
|
|
1265
|
+
function setPath(target, path, value) {
|
|
1266
|
+
const [head, ...rest] = path;
|
|
1267
|
+
if (head === void 0) return value;
|
|
1268
|
+
const container = Array.isArray(target) ? [...target] : { ...target ?? {} };
|
|
1269
|
+
container[head] = setPath(container[head], rest, value);
|
|
1270
|
+
return container;
|
|
1271
|
+
}
|
|
1272
|
+
function modifyConfig({
|
|
1273
|
+
config,
|
|
1274
|
+
edits,
|
|
1275
|
+
defaultEditOptions
|
|
1276
|
+
}) {
|
|
1277
|
+
return checkResult(() => {
|
|
1278
|
+
const format = config.format ?? (config.filepath ? getConfigFormat(config.filepath) : void 0) ?? "json";
|
|
1279
|
+
const language = configLanguages[format];
|
|
1280
|
+
if (language.surgical) {
|
|
1281
|
+
const { data, error } = modifyJSON({
|
|
1282
|
+
json: config.filepath ? { filepath: config.filepath } : config.text !== void 0 ? { text: config.text } : { data: config.data },
|
|
1283
|
+
edits,
|
|
1284
|
+
defaultEditOptions
|
|
1285
|
+
});
|
|
1286
|
+
if (error) throw error;
|
|
1287
|
+
return data;
|
|
1288
|
+
}
|
|
1289
|
+
const current = resolveConfigSource(config, "data");
|
|
1290
|
+
const updated = toEditList(edits, defaultEditOptions).reduce(
|
|
1291
|
+
(result, edit) => setPath(result, edit.path, edit.value),
|
|
1292
|
+
current
|
|
1293
|
+
);
|
|
1294
|
+
return resolveConfigSource({ text: language.stringify(updated), format });
|
|
1295
|
+
});
|
|
1296
|
+
}
|
|
1297
|
+
function modifyConfigFile(filepath, edits, options) {
|
|
1298
|
+
return checkResult(() => {
|
|
1299
|
+
const { autoCommit = true, defaultEditOptions, format } = options || {};
|
|
1300
|
+
const { data: config, error } = modifyConfig({
|
|
1301
|
+
config: format ? { filepath, format } : { filepath },
|
|
1302
|
+
edits,
|
|
1303
|
+
defaultEditOptions
|
|
1304
|
+
});
|
|
1305
|
+
if (error) {
|
|
1306
|
+
throw error;
|
|
1307
|
+
}
|
|
1308
|
+
const commit = () => writeFileSync(filepath, config.text);
|
|
1309
|
+
if (autoCommit) {
|
|
1310
|
+
commit();
|
|
1311
|
+
return config;
|
|
1312
|
+
}
|
|
1313
|
+
return {
|
|
1314
|
+
config,
|
|
1315
|
+
commit
|
|
1316
|
+
};
|
|
1317
|
+
});
|
|
1318
|
+
}
|
|
1319
|
+
|
|
1115
1320
|
function isStorageValue(input) {
|
|
1116
1321
|
return input === null || typeof input === "string" || typeof input === "number" || typeof input === "boolean" || typeof input === "object";
|
|
1117
1322
|
}
|
|
@@ -1120,110 +1325,82 @@ const storage = createStorage({ driver: defineMemoryDriver() });
|
|
|
1120
1325
|
const tempFileSystem = defineFileSystemStorage({
|
|
1121
1326
|
base: join(os.tmpdir(), ".package-manager")
|
|
1122
1327
|
});
|
|
1328
|
+
const encodeEntry = (value) => typeof value === "string" ? value : JSON.stringify(value);
|
|
1123
1329
|
function defineFileSystemEntries(definition) {
|
|
1124
|
-
const fileSystemEntries = Object.entries(definition).map(([
|
|
1125
|
-
if (
|
|
1126
|
-
const {
|
|
1127
|
-
|
|
1128
|
-
options,
|
|
1129
|
-
serialize = (input) => JSON.stringify(input),
|
|
1130
|
-
deserialize = (input) => {
|
|
1131
|
-
try {
|
|
1132
|
-
return JSON.parse(input);
|
|
1133
|
-
} catch {
|
|
1134
|
-
return input;
|
|
1135
|
-
}
|
|
1136
|
-
}
|
|
1137
|
-
} = v();
|
|
1138
|
-
return {
|
|
1139
|
-
key: k,
|
|
1140
|
-
value: serialize(file),
|
|
1141
|
-
options,
|
|
1142
|
-
serialize,
|
|
1143
|
-
deserialize
|
|
1144
|
-
};
|
|
1330
|
+
const fileSystemEntries = Object.entries(definition).map(([key, value]) => {
|
|
1331
|
+
if (typeof value === "function") {
|
|
1332
|
+
const { file, options } = value();
|
|
1333
|
+
return { key, value: encodeEntry(file), options };
|
|
1145
1334
|
}
|
|
1146
|
-
if (isStorageValue(
|
|
1147
|
-
|
|
1148
|
-
key:
|
|
1149
|
-
|
|
1150
|
-
};
|
|
1335
|
+
if (!isStorageValue(value)) {
|
|
1336
|
+
throw new Error(
|
|
1337
|
+
`Cannot store a ${typeof value} at "${key}": file system entries must be a storage value or a function returning one.`
|
|
1338
|
+
);
|
|
1151
1339
|
}
|
|
1152
|
-
return
|
|
1153
|
-
})
|
|
1340
|
+
return { key, value: encodeEntry(value) };
|
|
1341
|
+
});
|
|
1154
1342
|
const resolved = {
|
|
1155
1343
|
definition,
|
|
1156
1344
|
fileSystemEntries
|
|
1157
1345
|
};
|
|
1158
1346
|
return resolved;
|
|
1159
1347
|
}
|
|
1348
|
+
const keyToFilePath = (root, key) => join(root, ...key.split(/[:/\\]+/).filter(Boolean));
|
|
1160
1349
|
function defineFileSystemStorage(options) {
|
|
1161
1350
|
const { base: root, initial, ...storageOptions } = options;
|
|
1162
|
-
let
|
|
1351
|
+
let entriesData = defineFileSystemEntries(initial ?? {});
|
|
1163
1352
|
const storage2 = createStorage({
|
|
1164
1353
|
driver: defineFsLiteDriver({ base: root, ...storageOptions })
|
|
1165
1354
|
});
|
|
1355
|
+
const getFilePath = (key) => keyToFilePath(root, key);
|
|
1356
|
+
const getFile = async (key) => ({
|
|
1357
|
+
key,
|
|
1358
|
+
filepath: getFilePath(key),
|
|
1359
|
+
data: await storage2.getItem(key),
|
|
1360
|
+
// `getItemRaw` resolves to null for a missing file rather than throwing.
|
|
1361
|
+
read: async () => (await storage2.getItemRaw(key))?.toString()
|
|
1362
|
+
});
|
|
1363
|
+
const removeAllFiles = async () => storage2.clear();
|
|
1166
1364
|
const fileStorage = {
|
|
1167
1365
|
createFile: async (key, data) => {
|
|
1168
1366
|
await storage2.setItem(key, data);
|
|
1169
1367
|
return {
|
|
1170
1368
|
key,
|
|
1171
|
-
filepath:
|
|
1369
|
+
filepath: getFilePath(key),
|
|
1172
1370
|
get: async () => storage2.getItem(key),
|
|
1173
1371
|
update: async (data2) => storage2.setItem(key, data2)
|
|
1174
1372
|
};
|
|
1175
1373
|
},
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
filepath: join(root, key),
|
|
1184
|
-
data: storageData,
|
|
1185
|
-
read: async () => (await storage2.getItemRaw(key))?.toString()
|
|
1186
|
-
};
|
|
1187
|
-
},
|
|
1188
|
-
async readFile(key) {
|
|
1189
|
-
const { read } = await this.getFile(key);
|
|
1190
|
-
const content = await read();
|
|
1191
|
-
return content;
|
|
1192
|
-
},
|
|
1193
|
-
snapshotFs: async (base = root) => {
|
|
1194
|
-
return snapshot(storage2, base);
|
|
1195
|
-
},
|
|
1196
|
-
restoreFs: async (snapshot2) => {
|
|
1197
|
-
return restoreSnapshot(storage2, snapshot2);
|
|
1198
|
-
},
|
|
1374
|
+
getFilePath,
|
|
1375
|
+
getFile,
|
|
1376
|
+
readFile: async (key) => (await getFile(key)).read(),
|
|
1377
|
+
// `base` is a key prefix. Defaulting it to the filesystem root would match
|
|
1378
|
+
// no key at all and silently report an empty filesystem.
|
|
1379
|
+
snapshotFs: async (base = "") => snapshot(storage2, base),
|
|
1380
|
+
restoreFs: async (snapshot2, base) => restoreSnapshot(storage2, snapshot2, base),
|
|
1199
1381
|
initializeFs: async (override) => {
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
await storage2.
|
|
1204
|
-
return storage2.setItems(initialFileEntriesData.fileSystemEntries);
|
|
1382
|
+
const previousKeys = entriesData.fileSystemEntries.map(({ key }) => key);
|
|
1383
|
+
if (override) entriesData = defineFileSystemEntries(override);
|
|
1384
|
+
await Promise.all(previousKeys.map((key) => storage2.removeItem(key)));
|
|
1385
|
+
await storage2.setItems(entriesData.fileSystemEntries);
|
|
1205
1386
|
},
|
|
1206
1387
|
defineFileSystemEntries,
|
|
1207
|
-
removeAllFiles
|
|
1388
|
+
removeAllFiles,
|
|
1208
1389
|
async deleteFileSystem() {
|
|
1209
|
-
await
|
|
1210
|
-
await storage2.
|
|
1390
|
+
await removeAllFiles();
|
|
1391
|
+
await storage2.dispose();
|
|
1211
1392
|
await rm(root, { recursive: true, force: true });
|
|
1212
1393
|
},
|
|
1213
1394
|
storage: storage2,
|
|
1214
1395
|
meta: {
|
|
1215
|
-
|
|
1396
|
+
// A getter, so that `initializeFs(override)` is reflected here rather
|
|
1397
|
+
// than this reporting whatever the definition was at construction.
|
|
1398
|
+
get fileEntriesData() {
|
|
1399
|
+
return entriesData;
|
|
1400
|
+
}
|
|
1216
1401
|
}
|
|
1217
1402
|
};
|
|
1218
|
-
if (initial) {
|
|
1219
|
-
return {
|
|
1220
|
-
initialize: async () => {
|
|
1221
|
-
await fileStorage.initializeFs(initial);
|
|
1222
|
-
return fileStorage;
|
|
1223
|
-
}
|
|
1224
|
-
};
|
|
1225
|
-
}
|
|
1226
1403
|
return fileStorage;
|
|
1227
1404
|
}
|
|
1228
1405
|
|
|
1229
|
-
export { _dirname, _filename, defineFileSystemEntries, defineFileSystemStorage, definePackage, definePackageManagerClient, definePathAliases, detectGlobalPackageManagers, detectLockfilePackageManagers, detectPackageManagers, filterPackageManagers, findDependencyInPackageJson, findPackageManager, findPackageManagerSafely, findResolvedModulePath, getAliasMap, getFolderByPackageName, getGitRootFolder, getGlobalVersions, getPackageFolder, getPath, getWorkspaceFolder, importMap, importer, isDependencyInPackageJson, isPackageDependency, isPackageModuleFound, mapPackageManagers, modifyJSON, modifyJSONFile, packageManagerConfigs, predefinedPathAliases, project, resolveModule, resolveModulePath, resolvePackageModulePath, storage, tempFileSystem, workspace };
|
|
1406
|
+
export { _dirname, _filename, configLanguages, createFile, defineFileSystemEntries, defineFileSystemStorage, definePackage, definePackageManager, definePackageManagerClient, definePathAliases, detectGlobalPackageManagers, detectLockfilePackageManagers, detectPackageManagers, filterPackageManagers, findDependencyInPackageJson, findPackageManager, findPackageManagerSafely, findResolvedModulePath, getAliasMap, getConfigFormat, getFolderByPackageName, getGitRootFolder, getGlobalVersions, getPackageFolder, getPath, getWorkspaceFolder, importMap, importer, isConfigFormat, isDependencyInPackageJson, isPackageDependency, isPackageModuleFound, isWritable, mapPackageManagers, modifyConfig, modifyConfigFile, modifyJSON, modifyJSONFile, packageManagerConfigs, predefinedPathAliases, project, readFile, readFileSafely, resolveConfigSource, resolveModule, resolveModulePath, resolvePackageModulePath, storage, tempFileSystem, workspace };
|