package-management 0.0.16 → 0.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/LICENSE +21 -0
- package/dist/index.cjs +382 -357
- package/dist/index.d.cts +148 -74
- package/dist/index.d.mts +148 -74
- package/dist/index.d.ts +148 -74
- package/dist/index.mjs +378 -357
- package/package.json +10 -13
package/dist/index.mjs
CHANGED
|
@@ -1,25 +1,88 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
3
|
-
import process from 'node:process';
|
|
4
|
-
import path from 'node:path';
|
|
1
|
+
import path, { isAbsolute, dirname, normalize, join } from 'pathe';
|
|
2
|
+
import util from 'node:util';
|
|
5
3
|
import { fileURLToPath } from 'node:url';
|
|
6
|
-
import {
|
|
4
|
+
import { existsSync, mkdirSync, writeFileSync, accessSync, constants, statSync, readFileSync } 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
8
|
import { readFile, 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 { morph } from '@arktype/util';
|
|
17
|
+
import { parse, applyEdits, modify } from 'jsonc-parser';
|
|
19
18
|
import { createStorage, restoreSnapshot, snapshot } from 'unstorage';
|
|
20
19
|
import defineMemoryDriver from 'unstorage/drivers/memory';
|
|
21
20
|
import defineFsLiteDriver from 'unstorage/drivers/fs-lite';
|
|
22
21
|
|
|
22
|
+
const getCallSites = "getCallSites" in util ? util.getCallSites : void 0;
|
|
23
|
+
const MAX_FRAMES = 200;
|
|
24
|
+
function resolveCallerFile(options) {
|
|
25
|
+
const { from, boundaryFunctionName, internalScripts = [] } = options ?? {};
|
|
26
|
+
if (from) return toCallerPath(String(from));
|
|
27
|
+
if (getCallSites === void 0) return void 0;
|
|
28
|
+
const sites = getCallSites(MAX_FRAMES, { sourceMap: true });
|
|
29
|
+
const scriptName = boundaryFunctionName && frameAfterFunction(sites, boundaryFunctionName) || firstForeignFrame(sites, [OWN_SCRIPT, ...internalScripts]);
|
|
30
|
+
return scriptName ? toFilePath(scriptName) : void 0;
|
|
31
|
+
}
|
|
32
|
+
const OWN_SCRIPT = import.meta.url;
|
|
33
|
+
function frameAfterFunction(sites, functionName) {
|
|
34
|
+
const boundary = sites.findIndex((site) => site.functionName === functionName);
|
|
35
|
+
return boundary === -1 ? void 0 : sites[boundary + 1]?.scriptName;
|
|
36
|
+
}
|
|
37
|
+
function firstForeignFrame(sites, internalScripts) {
|
|
38
|
+
const internal = new Set(internalScripts.map(toFilePath));
|
|
39
|
+
return sites.find(
|
|
40
|
+
(site) => (
|
|
41
|
+
// Frames from `node:` internals and evaluated code name no file, so they
|
|
42
|
+
// are never the caller's location however far out they appear.
|
|
43
|
+
isFileScript(site.scriptName) && !internal.has(toFilePath(site.scriptName))
|
|
44
|
+
)
|
|
45
|
+
)?.scriptName;
|
|
46
|
+
}
|
|
47
|
+
const isFileScript = (script) => Boolean(script) && (script.startsWith("file:") || isAbsolute(script));
|
|
48
|
+
const toFilePath = (script) => script.startsWith("file:") ? fileURLToPath(script) : script;
|
|
49
|
+
function toCallerPath(from) {
|
|
50
|
+
if (from.startsWith("file:")) return fileURLToPath(from);
|
|
51
|
+
if (isAbsolute(from)) return from;
|
|
52
|
+
throw new Error(
|
|
53
|
+
`\`from\` must be a file: URL or an absolute path, received ${JSON.stringify(from)}. Pass \`import.meta.url\`.`
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const _filename = (options) => resolveCallerFile(withOwnScript(options));
|
|
58
|
+
const _dirname = (options) => {
|
|
59
|
+
const filePath = resolveCallerFile(withOwnScript(options));
|
|
60
|
+
return filePath === void 0 ? void 0 : dirname(filePath);
|
|
61
|
+
};
|
|
62
|
+
const withOwnScript = (options) => ({
|
|
63
|
+
...options,
|
|
64
|
+
internalScripts: [...options?.internalScripts ?? [], import.meta.url]
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
function createFile(filePath, data, options) {
|
|
68
|
+
const { encoding = "utf-8", ...rest } = typeof options === "string" ? { encoding: options } : options ?? {};
|
|
69
|
+
const dir = path.dirname(filePath);
|
|
70
|
+
const writeFileOptions = { encoding, ...rest };
|
|
71
|
+
if (!existsSync(dir)) {
|
|
72
|
+
mkdirSync(dir, { recursive: true });
|
|
73
|
+
}
|
|
74
|
+
writeFileSync(filePath, data, writeFileOptions);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function isWritable(filename) {
|
|
78
|
+
try {
|
|
79
|
+
accessSync(filename, constants.W_OK);
|
|
80
|
+
return true;
|
|
81
|
+
} catch (e) {
|
|
82
|
+
return false;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
23
86
|
const toArray = (data) => Array.isArray(data) ? data : [data];
|
|
24
87
|
const entriesOf = (o) => Object.entries(o);
|
|
25
88
|
const fromEntries = (entries) => Object.fromEntries(entries);
|
|
@@ -33,7 +96,7 @@ const select = (obj, selection, mode) => {
|
|
|
33
96
|
};
|
|
34
97
|
function normalizePath(path, option) {
|
|
35
98
|
if (typeof option === "function") {
|
|
36
|
-
return
|
|
99
|
+
return option(path);
|
|
37
100
|
}
|
|
38
101
|
if (option === false) {
|
|
39
102
|
return path;
|
|
@@ -45,14 +108,6 @@ const invariant = (predicate, message) => {
|
|
|
45
108
|
throw new Error(message);
|
|
46
109
|
}
|
|
47
110
|
};
|
|
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
111
|
function checkResult(cb, options) {
|
|
57
112
|
try {
|
|
58
113
|
return buildSingleProp({
|
|
@@ -77,123 +132,16 @@ function buildSingleProp(entry) {
|
|
|
77
132
|
return { [key]: value };
|
|
78
133
|
}
|
|
79
134
|
|
|
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
135
|
async function resolveModule(module) {
|
|
181
136
|
const resolved = await module;
|
|
182
|
-
|
|
137
|
+
const hasDefault = resolved !== null && typeof resolved === "object" && "default" in resolved;
|
|
138
|
+
return hasDefault ? resolved.default : resolved;
|
|
183
139
|
}
|
|
184
140
|
function isPackageModuleFound(name, options) {
|
|
185
141
|
return Boolean(resolvePackageModulePath(name, options));
|
|
186
142
|
}
|
|
187
143
|
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;
|
|
144
|
+
return findResolvedModulePath([`${name}/package.json`, name], options);
|
|
197
145
|
}
|
|
198
146
|
function findResolvedModulePath(paths, options) {
|
|
199
147
|
for (const path of paths) {
|
|
@@ -215,28 +163,68 @@ function resolveModulePath(modulePath, options) {
|
|
|
215
163
|
}
|
|
216
164
|
}
|
|
217
165
|
|
|
166
|
+
function getPackageFolder(options) {
|
|
167
|
+
return WST.findPackageRoot(options?.cwd ?? process.cwd());
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
function getPackageInfo(options) {
|
|
171
|
+
const packageDir = getPackageFolder(options);
|
|
172
|
+
if (!packageDir) {
|
|
173
|
+
throw new Error(
|
|
174
|
+
`No package.json found searching up from "${options?.cwd ?? process.cwd()}"`
|
|
175
|
+
);
|
|
176
|
+
}
|
|
177
|
+
return readPackageInfo({ packageDir });
|
|
178
|
+
}
|
|
179
|
+
function readPackageInfo({
|
|
180
|
+
packageDir
|
|
181
|
+
}) {
|
|
182
|
+
const packageJsonPath = path$1.join(packageDir, "package.json");
|
|
183
|
+
const packageJson = readPackageJson(packageJsonPath);
|
|
184
|
+
return {
|
|
185
|
+
name: packageJson.name,
|
|
186
|
+
path: packageJsonPath,
|
|
187
|
+
dirpath: packageDir,
|
|
188
|
+
packageJson
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
const packageJsonCache = /* @__PURE__ */ new Map();
|
|
192
|
+
function readPackageJson(packageJsonPath) {
|
|
193
|
+
const { mtimeMs } = statSync(packageJsonPath);
|
|
194
|
+
const cached = packageJsonCache.get(packageJsonPath);
|
|
195
|
+
if (cached?.mtimeMs === mtimeMs) return cached.packageJson;
|
|
196
|
+
const packageJson = JSON.parse(
|
|
197
|
+
readFileSync(packageJsonPath, "utf-8")
|
|
198
|
+
);
|
|
199
|
+
packageJsonCache.set(packageJsonPath, { mtimeMs, packageJson });
|
|
200
|
+
return packageJson;
|
|
201
|
+
}
|
|
202
|
+
|
|
218
203
|
const tsconfig = (tsconfigDir) => ({
|
|
219
204
|
get paths() {
|
|
205
|
+
if (!tsconfigDir) return [];
|
|
220
206
|
return globbySync(["tsconfig.json", "tsconfig.*.json"], {
|
|
221
|
-
cwd: tsconfigDir
|
|
222
|
-
|
|
207
|
+
cwd: tsconfigDir,
|
|
208
|
+
absolute: true
|
|
209
|
+
});
|
|
223
210
|
}
|
|
224
211
|
});
|
|
225
212
|
|
|
226
|
-
function
|
|
227
|
-
return gitignoreParser(
|
|
213
|
+
function parseGitignoreContent(gitignoreContent, options) {
|
|
214
|
+
return gitignoreParser(gitignoreContent, options);
|
|
228
215
|
}
|
|
229
|
-
function getGitignoreData(gitignorePath) {
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
216
|
+
function getGitignoreData(gitignorePath, options) {
|
|
217
|
+
if (!gitignorePath || !existsSync(gitignorePath)) {
|
|
218
|
+
return parseGitignoreContent("", options);
|
|
219
|
+
}
|
|
220
|
+
return parseGitignoreContent(readFileSync(gitignorePath, "utf-8"), options);
|
|
233
221
|
}
|
|
234
|
-
const gitignore = (gitignorePath) => ({
|
|
222
|
+
const gitignore = (gitignorePath, options) => ({
|
|
235
223
|
get data() {
|
|
236
|
-
return getGitignoreData(gitignorePath);
|
|
224
|
+
return getGitignoreData(gitignorePath, options);
|
|
237
225
|
},
|
|
238
226
|
get patterns() {
|
|
239
|
-
return getGitignoreData(gitignorePath).patterns;
|
|
227
|
+
return getGitignoreData(gitignorePath, options).patterns;
|
|
240
228
|
}
|
|
241
229
|
});
|
|
242
230
|
|
|
@@ -299,36 +287,13 @@ function getWorkspaceFolder(options) {
|
|
|
299
287
|
throwIfNotFound = true
|
|
300
288
|
} = options ?? {};
|
|
301
289
|
const getFolder = fallbackToGitRoot ? WST.findProjectRoot : WST.getWorkspaceManagerRoot;
|
|
302
|
-
const folder = getFolder(cwd);
|
|
290
|
+
const folder = checkResult(() => getFolder(cwd)).data;
|
|
303
291
|
if (folder === void 0 && throwIfNotFound) {
|
|
304
292
|
throw new Error(`Could not find workspace folder`);
|
|
305
293
|
}
|
|
306
294
|
return folder;
|
|
307
295
|
}
|
|
308
296
|
|
|
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
297
|
function getWorkspaceProjectInfo(options) {
|
|
333
298
|
try {
|
|
334
299
|
const workspaceDir = getWorkspaceFolder(options);
|
|
@@ -344,13 +309,21 @@ function getWorkspacePackageInfoList(options) {
|
|
|
344
309
|
const wstList = WST.getWorkspaceInfos(workspaceDir);
|
|
345
310
|
return resolveWstList(wstList);
|
|
346
311
|
}
|
|
312
|
+
function toPackageInfo(info) {
|
|
313
|
+
return {
|
|
314
|
+
name: info.name,
|
|
315
|
+
dirpath: info.path,
|
|
316
|
+
path: join(info.path, "package.json"),
|
|
317
|
+
packageJson: info.packageJson
|
|
318
|
+
};
|
|
319
|
+
}
|
|
347
320
|
function common(options) {
|
|
348
321
|
const { cwd, includeRoot: includeWorkspace } = options ?? {};
|
|
349
322
|
const workspaceDir = getWorkspaceFolder({ cwd });
|
|
350
323
|
function resolveWstList(list) {
|
|
351
324
|
if (!workspaceDir || !list) return [];
|
|
352
|
-
|
|
353
|
-
return
|
|
325
|
+
const packages = list.map(toPackageInfo);
|
|
326
|
+
return includeWorkspace ? [getWorkspaceProjectInfo({ cwd }), ...packages] : packages;
|
|
354
327
|
}
|
|
355
328
|
return {
|
|
356
329
|
workspaceDir,
|
|
@@ -367,7 +340,7 @@ function getWorkspacePackageInfoMap(options) {
|
|
|
367
340
|
|
|
368
341
|
function getProjectInfoByName(name, options) {
|
|
369
342
|
const { cwd } = options ?? {};
|
|
370
|
-
return getWorkspacePackageInfoMap({ cwd })[name];
|
|
343
|
+
return getWorkspacePackageInfoMap({ cwd, includeRoot: true })[name];
|
|
371
344
|
}
|
|
372
345
|
|
|
373
346
|
function getGitRootFolder(options) {
|
|
@@ -382,7 +355,7 @@ function getGitRootFolder(options) {
|
|
|
382
355
|
}
|
|
383
356
|
return void 0;
|
|
384
357
|
}
|
|
385
|
-
return path
|
|
358
|
+
return path.normalize(stdout);
|
|
386
359
|
}
|
|
387
360
|
|
|
388
361
|
function getProjectInfo(folder, options) {
|
|
@@ -413,9 +386,14 @@ function parseWorkspaceFolderTypeOptions(option, defaultOptions) {
|
|
|
413
386
|
|
|
414
387
|
const project = (...args) => {
|
|
415
388
|
const [source, projectOptions] = args;
|
|
416
|
-
const { packageJson, packageJsonPath, packageName, projectDir } = info();
|
|
417
389
|
const {
|
|
418
|
-
|
|
390
|
+
packageJson,
|
|
391
|
+
name: packageName,
|
|
392
|
+
dirpath: projectDir,
|
|
393
|
+
path: packageJsonPath
|
|
394
|
+
} = getProjectInfo(source, projectOptions) ?? {};
|
|
395
|
+
const {
|
|
396
|
+
findPackageManager,
|
|
419
397
|
detectPackageManagers,
|
|
420
398
|
detectLockfilePackageManagers,
|
|
421
399
|
detectGlobalPackageManagers,
|
|
@@ -423,41 +401,29 @@ const project = (...args) => {
|
|
|
423
401
|
filterPackageManagers,
|
|
424
402
|
mapPackageManagers
|
|
425
403
|
} = definePackageManagerClient({ cwd: projectDir });
|
|
404
|
+
const getPackageJson = () => projectDir ? readPackageInfo({ packageDir: projectDir }).packageJson : packageJson;
|
|
426
405
|
return {
|
|
427
406
|
packageJson,
|
|
428
407
|
packageJsonPath,
|
|
429
408
|
packageName,
|
|
430
409
|
projectDir,
|
|
431
|
-
findPackageManager
|
|
410
|
+
findPackageManager,
|
|
432
411
|
detectPackageManagers,
|
|
433
412
|
detectGlobalPackageManagers,
|
|
434
413
|
detectLockfilePackageManagers,
|
|
435
414
|
globalVersions,
|
|
436
415
|
mapPackageManagers,
|
|
437
|
-
|
|
438
|
-
|
|
416
|
+
// Passed through as-is: substituting "" for an unresolved project made
|
|
417
|
+
// both of these read from the calling process's directory instead.
|
|
418
|
+
tsconfig: tsconfig(projectDir),
|
|
419
|
+
gitignore: gitignore(
|
|
420
|
+
projectDir ? path.join(projectDir, ".gitignore") : void 0
|
|
421
|
+
),
|
|
439
422
|
filterPackageManagers,
|
|
440
|
-
getPackageJson
|
|
441
|
-
findDependencyInPackageJson: (options) => findDependencyInPackageJson(options,
|
|
442
|
-
isDependencyInPackageJson: (options) => isDependencyInPackageJson(options,
|
|
423
|
+
getPackageJson,
|
|
424
|
+
findDependencyInPackageJson: (options) => findDependencyInPackageJson(options, getPackageJson()),
|
|
425
|
+
isDependencyInPackageJson: (options) => isDependencyInPackageJson(options, getPackageJson())
|
|
443
426
|
};
|
|
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
427
|
};
|
|
462
428
|
|
|
463
429
|
function getWorkspacePackageNames(options) {
|
|
@@ -472,18 +438,24 @@ const workspace = {
|
|
|
472
438
|
getProject: project
|
|
473
439
|
};
|
|
474
440
|
|
|
475
|
-
function importer(imports, options) {
|
|
441
|
+
async function importer(imports, options) {
|
|
476
442
|
const { install: defaultInstall = true, installer } = options ?? {};
|
|
443
|
+
const resolved = imports.map((option) => resolveImportOption(option));
|
|
444
|
+
const missing = resolved.filter(
|
|
445
|
+
(option) => Boolean(option.name) && (option.install ?? defaultInstall) && !((option.checkExists ?? true) && isSatisfied(option.name))
|
|
446
|
+
);
|
|
447
|
+
await installMissing(
|
|
448
|
+
missing.filter(({ dev }) => !dev).map(({ name }) => name),
|
|
449
|
+
{ dev: false },
|
|
450
|
+
installer
|
|
451
|
+
);
|
|
452
|
+
await installMissing(
|
|
453
|
+
missing.filter(({ dev }) => dev).map(({ name }) => name),
|
|
454
|
+
{ dev: true },
|
|
455
|
+
installer
|
|
456
|
+
);
|
|
477
457
|
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
|
-
})
|
|
458
|
+
resolved.map(async (option) => resolveModule(await option.import()))
|
|
487
459
|
);
|
|
488
460
|
}
|
|
489
461
|
const definePackage = (option) => {
|
|
@@ -507,12 +479,11 @@ function resolveImportOption(option) {
|
|
|
507
479
|
}
|
|
508
480
|
return option;
|
|
509
481
|
}
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
if (checkExists && isPackageDependency(packageName)) return;
|
|
482
|
+
const isSatisfied = (packageName) => isPackageDependency(packageName) && isPackageModuleFound(packageName);
|
|
483
|
+
async function installMissing(packageNames, options, installer) {
|
|
484
|
+
if (packageNames.length === 0) return;
|
|
514
485
|
const installerFn = installer ?? (await workspace.getProject("<package_folder>").findPackageManager()).installPackage;
|
|
515
|
-
await installerFn(
|
|
486
|
+
await installerFn(packageNames, options);
|
|
516
487
|
}
|
|
517
488
|
|
|
518
489
|
const importMap = async (importMap2, options) => {
|
|
@@ -534,20 +505,36 @@ function definePackageManager(config, options) {
|
|
|
534
505
|
const lockfiles = toArray(config.meta.lockfile);
|
|
535
506
|
return await findUp(lockfiles, { cwd });
|
|
536
507
|
});
|
|
508
|
+
const globalVersion = asyncCacheFn(
|
|
509
|
+
async (options2) => {
|
|
510
|
+
try {
|
|
511
|
+
const { stdout } = await $$({
|
|
512
|
+
command,
|
|
513
|
+
args: [agentOptions.version],
|
|
514
|
+
cwd: defaultCwd,
|
|
515
|
+
...options2
|
|
516
|
+
});
|
|
517
|
+
return `${stdout}`.trim();
|
|
518
|
+
} catch {
|
|
519
|
+
return void 0;
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
);
|
|
537
523
|
const installPackage = async (packageName, options2) => {
|
|
538
524
|
const install = agentArgs.install;
|
|
539
|
-
const { dev, preferOffline } =
|
|
540
|
-
|
|
541
|
-
{
|
|
542
|
-
preferOffline: true,
|
|
543
|
-
cwd: defaultCwd,
|
|
544
|
-
...options2
|
|
545
|
-
});
|
|
525
|
+
const { dev = false, preferOffline = true } = options2 ?? {};
|
|
526
|
+
const flags = select(install.options, { dev, preferOffline });
|
|
546
527
|
const packageNames = toArray(packageName);
|
|
528
|
+
assertInstallableNames(packageNames);
|
|
547
529
|
try {
|
|
548
530
|
await $$({
|
|
549
531
|
command,
|
|
550
|
-
args: [
|
|
532
|
+
args: [
|
|
533
|
+
install.command,
|
|
534
|
+
flags.dev,
|
|
535
|
+
flags.preferOffline,
|
|
536
|
+
...packageNames
|
|
537
|
+
],
|
|
551
538
|
cwd: defaultCwd,
|
|
552
539
|
...options2
|
|
553
540
|
});
|
|
@@ -591,19 +578,12 @@ function definePackageManager(config, options) {
|
|
|
591
578
|
if (!lockfilePath) return void 0;
|
|
592
579
|
return readFile(lockfilePath, "utf8");
|
|
593
580
|
}),
|
|
594
|
-
globalVersion
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
cwd: defaultCwd,
|
|
601
|
-
...options2
|
|
602
|
-
});
|
|
603
|
-
return `${stdout}`;
|
|
604
|
-
} catch (e) {
|
|
605
|
-
return void 0;
|
|
606
|
-
}
|
|
581
|
+
globalVersion,
|
|
582
|
+
matchesVersion: asyncCacheFn(async (...args) => {
|
|
583
|
+
const { matchesVersion } = config.meta;
|
|
584
|
+
if (!matchesVersion) return true;
|
|
585
|
+
const version = await globalVersion.noCache(...args);
|
|
586
|
+
return version ? matchesVersion(version) : false;
|
|
607
587
|
}),
|
|
608
588
|
definePackage,
|
|
609
589
|
installPackage,
|
|
@@ -617,6 +597,16 @@ function definePackageManager(config, options) {
|
|
|
617
597
|
}
|
|
618
598
|
};
|
|
619
599
|
}
|
|
600
|
+
function assertInstallableNames(packageNames) {
|
|
601
|
+
const rejected = packageNames.filter(
|
|
602
|
+
(name) => name.length === 0 || name.startsWith("-")
|
|
603
|
+
);
|
|
604
|
+
if (rejected.length > 0) {
|
|
605
|
+
throw new Error(
|
|
606
|
+
`Not a package name: ${rejected.map((name) => JSON.stringify(name)).join(", ")}. A package name cannot be empty or begin with "-".`
|
|
607
|
+
);
|
|
608
|
+
}
|
|
609
|
+
}
|
|
620
610
|
async function $$(options) {
|
|
621
611
|
const { command, args = [], silent = true, cwd, shellOptions } = options;
|
|
622
612
|
return execa(command, args.filter(notFalsy), {
|
|
@@ -639,7 +629,9 @@ const bun = definePackageManagerConfig({
|
|
|
639
629
|
command: "bun",
|
|
640
630
|
runner: "bunx",
|
|
641
631
|
meta: {
|
|
642
|
-
lockfile
|
|
632
|
+
// Bun wrote a binary lockfile originally and a text one from 1.2 onwards,
|
|
633
|
+
// so a project using either must still be detected.
|
|
634
|
+
lockfile: ["bun.lock", "bun.lockb"]
|
|
643
635
|
},
|
|
644
636
|
args: {
|
|
645
637
|
install: {
|
|
@@ -650,7 +642,7 @@ const bun = definePackageManagerConfig({
|
|
|
650
642
|
}
|
|
651
643
|
},
|
|
652
644
|
uninstall: {
|
|
653
|
-
command: "
|
|
645
|
+
command: "remove"
|
|
654
646
|
}
|
|
655
647
|
},
|
|
656
648
|
options: {
|
|
@@ -714,7 +706,9 @@ const yarn = definePackageManagerConfig({
|
|
|
714
706
|
command: "yarn",
|
|
715
707
|
runner: "yarn dlx",
|
|
716
708
|
meta: {
|
|
717
|
-
lockfile: "yarn.lock"
|
|
709
|
+
lockfile: "yarn.lock",
|
|
710
|
+
// Berry shares this command and lockfile, so the version decides.
|
|
711
|
+
matchesVersion: (version) => version.startsWith("1.")
|
|
718
712
|
},
|
|
719
713
|
args: {
|
|
720
714
|
install: {
|
|
@@ -733,6 +727,33 @@ const yarn = definePackageManagerConfig({
|
|
|
733
727
|
}
|
|
734
728
|
});
|
|
735
729
|
|
|
730
|
+
const yarnBerry = definePackageManagerConfig({
|
|
731
|
+
id: "yarn-berry",
|
|
732
|
+
name: "Yarn Berry",
|
|
733
|
+
command: "yarn",
|
|
734
|
+
runner: "yarn dlx",
|
|
735
|
+
meta: {
|
|
736
|
+
lockfile: "yarn.lock",
|
|
737
|
+
// Classic shares this command and lockfile; everything past 1.x is Berry.
|
|
738
|
+
matchesVersion: (version) => !version.startsWith("1.")
|
|
739
|
+
},
|
|
740
|
+
args: {
|
|
741
|
+
install: {
|
|
742
|
+
command: "add",
|
|
743
|
+
options: {
|
|
744
|
+
dev: "-D",
|
|
745
|
+
preferOffline: "--cached"
|
|
746
|
+
}
|
|
747
|
+
},
|
|
748
|
+
uninstall: {
|
|
749
|
+
command: "remove"
|
|
750
|
+
}
|
|
751
|
+
},
|
|
752
|
+
options: {
|
|
753
|
+
version: "--version"
|
|
754
|
+
}
|
|
755
|
+
});
|
|
756
|
+
|
|
736
757
|
async function findPackageManager(packageManagers, options) {
|
|
737
758
|
const packageManager = await findPackageManagerSafely(
|
|
738
759
|
packageManagers,
|
|
@@ -762,7 +783,9 @@ async function detectPackageManagers(packageManagers, options) {
|
|
|
762
783
|
async function detectLockfilePackageManagers(packageManagers, options) {
|
|
763
784
|
return filterPackageManagers(
|
|
764
785
|
packageManagers,
|
|
765
|
-
|
|
786
|
+
// Yarn Classic and Berry share a lockfile name, so a lockfile match alone
|
|
787
|
+
// would report both. The version check settles which one it is.
|
|
788
|
+
async (packageManager) => await packageManager.hasLockfile(options) && await packageManager.matchesVersion(options),
|
|
766
789
|
options
|
|
767
790
|
);
|
|
768
791
|
}
|
|
@@ -772,7 +795,7 @@ async function detectGlobalPackageManagers(packageManagers, options) {
|
|
|
772
795
|
// Without the `await`, `Boolean` receives a pending promise and is always
|
|
773
796
|
// `true`, so every package manager passes the filter regardless of whether
|
|
774
797
|
// it is actually installed.
|
|
775
|
-
async (packageManager) => Boolean(await packageManager.globalVersion(options)),
|
|
798
|
+
async (packageManager) => Boolean(await packageManager.globalVersion(options)) && await packageManager.matchesVersion(options),
|
|
776
799
|
options
|
|
777
800
|
);
|
|
778
801
|
}
|
|
@@ -805,7 +828,7 @@ function selectAllowedPackageManagers(packageManagers, options) {
|
|
|
805
828
|
);
|
|
806
829
|
}
|
|
807
830
|
|
|
808
|
-
const packageManagerConfigs = [pnpm, yarn, bun, npm];
|
|
831
|
+
const packageManagerConfigs = [pnpm, yarn, yarnBerry, bun, npm];
|
|
809
832
|
function definePackageManagerClient(options) {
|
|
810
833
|
const configs = packageManagerConfigs.map(
|
|
811
834
|
(config) => definePackageManager(config, options)
|
|
@@ -835,8 +858,11 @@ function definePackageManagerClient(options) {
|
|
|
835
858
|
};
|
|
836
859
|
}
|
|
837
860
|
|
|
838
|
-
function isPackageDependency(packageName) {
|
|
839
|
-
|
|
861
|
+
function isPackageDependency(packageName, options) {
|
|
862
|
+
const { packageJson } = getPackageInfo(options);
|
|
863
|
+
return toArray(packageName).every(
|
|
864
|
+
(name) => isDependencyInPackageJson(name, packageJson)
|
|
865
|
+
);
|
|
840
866
|
}
|
|
841
867
|
|
|
842
868
|
function definePathAliases(aliasDefinitions) {
|
|
@@ -854,42 +880,49 @@ function definePathAliases(aliasDefinitions) {
|
|
|
854
880
|
};
|
|
855
881
|
}
|
|
856
882
|
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 };
|
|
883
|
+
const { to, startingFrom, cwd, from, checkExistence, glob } = typeof options === "string" || Array.isArray(options) ? { to: options } : options;
|
|
884
|
+
const resolveOptions = { cwd, from, checkExistence, glob, aliasMap };
|
|
859
885
|
try {
|
|
860
886
|
return startingFrom ? resolveRelativePathTo(to, startingFrom, resolveOptions) : resolvePathTo(to, resolveOptions);
|
|
861
887
|
} catch (error) {
|
|
862
|
-
if (
|
|
888
|
+
if (error instanceof PathNotFoundError) return void 0;
|
|
863
889
|
throw error;
|
|
864
890
|
}
|
|
865
891
|
}
|
|
892
|
+
class PathNotFoundError extends Error {
|
|
893
|
+
}
|
|
866
894
|
function resolveRelativePathTo(to, from, options) {
|
|
867
895
|
const pathFrom = resolvePathTo(from, options);
|
|
868
896
|
const pathTo = resolvePathTo(to, options);
|
|
869
|
-
return path
|
|
897
|
+
return path.relative(pathFrom, pathTo);
|
|
870
898
|
}
|
|
871
|
-
function resolvePathTo(pathTo, { cwd, checkExistence, glob, aliasMap }) {
|
|
872
|
-
const normalized = normalizePathTo(pathTo, { cwd, aliasMap });
|
|
899
|
+
function resolvePathTo(pathTo, { cwd, from, checkExistence, glob, aliasMap }) {
|
|
900
|
+
const normalized = normalizePathTo(pathTo, { cwd, from, aliasMap });
|
|
873
901
|
if (glob) {
|
|
874
902
|
const globPaths = globbySync(normalized, { cwd });
|
|
875
903
|
if (!globPaths[0])
|
|
876
|
-
throw new
|
|
904
|
+
throw new PathNotFoundError(`No paths found for glob: ${normalized}`);
|
|
877
905
|
return globPaths[0];
|
|
878
906
|
}
|
|
879
907
|
if (!glob && checkExistence && !existsSync(normalized))
|
|
880
|
-
throw new
|
|
908
|
+
throw new PathNotFoundError(`Path does not exist: ${normalized}`);
|
|
881
909
|
return normalized;
|
|
882
910
|
}
|
|
883
911
|
function normalizePathTo(pathTo, options) {
|
|
884
|
-
const { cwd, aliasMap } = options ?? {};
|
|
885
|
-
const
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
912
|
+
const { cwd, from, aliasMap } = options ?? {};
|
|
913
|
+
const baseDir = Array.isArray(pathTo) ? pathTo[0] : findAliasToken(pathTo, aliasMap);
|
|
914
|
+
if (baseDir === void 0) return pathTo;
|
|
915
|
+
const baseDirPath = assertResolved(
|
|
916
|
+
baseDir,
|
|
917
|
+
executeMapFn(aliasMap, baseDir, [{ cwd, from }])
|
|
918
|
+
);
|
|
919
|
+
return Array.isArray(pathTo) ? path.join(baseDirPath, ...pathTo.slice(1).filter(isNotNull)) : resolveAlias(pathTo, { [baseDir]: baseDirPath });
|
|
920
|
+
}
|
|
921
|
+
function assertResolved(alias, resolved) {
|
|
922
|
+
if (typeof resolved !== "string" || resolved.length === 0) {
|
|
923
|
+
throw new Error(`Path alias resolved to no location: ${alias}`);
|
|
891
924
|
}
|
|
892
|
-
return
|
|
925
|
+
return resolved;
|
|
893
926
|
}
|
|
894
927
|
function findAliasToken(pathTo, aliasMap) {
|
|
895
928
|
return Object.keys(aliasMap ?? {}).filter((alias) => pathTo === alias || pathTo.startsWith(`${alias}/`)).sort((a, b) => b.length - a.length)[0];
|
|
@@ -907,10 +940,17 @@ function getAliasMap(aliasDefs) {
|
|
|
907
940
|
return [
|
|
908
941
|
[alias, resolve],
|
|
909
942
|
...subpaths.map(({ to }) => {
|
|
910
|
-
const subpathAlias = path
|
|
943
|
+
const subpathAlias = path.join(alias, to);
|
|
911
944
|
return [
|
|
912
945
|
subpathAlias,
|
|
913
|
-
(opts) =>
|
|
946
|
+
(opts) => (
|
|
947
|
+
// The same assertion the bare alias gets. Without it an
|
|
948
|
+
// unresolvable parent produced `join(undefined, "/node_modules")`
|
|
949
|
+
// — a path at the filesystem root — instead of failing.
|
|
950
|
+
resolveAlias(subpathAlias, {
|
|
951
|
+
[alias]: assertResolved(alias, resolve(opts))
|
|
952
|
+
})
|
|
953
|
+
)
|
|
914
954
|
];
|
|
915
955
|
})
|
|
916
956
|
];
|
|
@@ -985,13 +1025,13 @@ const predefinedPathAliases = {
|
|
|
985
1025
|
subpaths: []
|
|
986
1026
|
},
|
|
987
1027
|
"<current_file>": {
|
|
988
|
-
//
|
|
989
|
-
|
|
1028
|
+
// `from` is the caller naming itself, which is exact. Without it the stack
|
|
1029
|
+
// is read, which is a best effort and Node-only.
|
|
1030
|
+
resolve: (opts) => _filename({ from: opts?.from, boundaryFunctionName: "getFilePath" }),
|
|
990
1031
|
subpaths: []
|
|
991
1032
|
},
|
|
992
1033
|
"<current_folder>": {
|
|
993
|
-
|
|
994
|
-
resolve: () => _dirname({ rootFunctionName: "getFilePath" }) ?? "",
|
|
1034
|
+
resolve: (opts) => _dirname({ from: opts?.from, boundaryFunctionName: "getFilePath" }),
|
|
995
1035
|
subpaths: []
|
|
996
1036
|
}
|
|
997
1037
|
};
|
|
@@ -1014,59 +1054,65 @@ const jsonSourceResolvers = {
|
|
|
1014
1054
|
text: (text) => {
|
|
1015
1055
|
return {
|
|
1016
1056
|
text: () => text,
|
|
1017
|
-
|
|
1057
|
+
// `jsonc-parser`'s parse, not `JSON.parse`: the whole point of this
|
|
1058
|
+
// module is editing files like tsconfig.json, which carry comments.
|
|
1059
|
+
data: () => parse(text)
|
|
1018
1060
|
};
|
|
1019
1061
|
},
|
|
1020
1062
|
filepath: (filepath) => {
|
|
1021
1063
|
const text = readFileSync(filepath, "utf-8");
|
|
1022
1064
|
return {
|
|
1023
1065
|
text: () => text,
|
|
1024
|
-
|
|
1066
|
+
// `jsonc-parser`'s parse, not `JSON.parse`: the whole point of this
|
|
1067
|
+
// module is editing files like tsconfig.json, which carry comments.
|
|
1068
|
+
data: () => parse(text)
|
|
1025
1069
|
};
|
|
1026
1070
|
}
|
|
1027
1071
|
};
|
|
1028
1072
|
function resolveJsonSource(source, as) {
|
|
1029
1073
|
const [sourceType, sourceData] = Object.entries(source).find(([_, selected]) => selected !== void 0) ?? [];
|
|
1030
|
-
if (!sourceType || !(sourceType in jsonSourceResolvers) ||
|
|
1074
|
+
if (!sourceType || !(sourceType in jsonSourceResolvers) || sourceData === void 0) {
|
|
1031
1075
|
throw new Error("Invalid source data");
|
|
1032
1076
|
}
|
|
1033
1077
|
const sourceTypeResolvers = jsonSourceResolvers[sourceType](
|
|
1034
1078
|
sourceData
|
|
1035
1079
|
);
|
|
1036
|
-
const resolved =
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1080
|
+
const resolved = fromEntries(
|
|
1081
|
+
entriesOf(sourceTypeResolvers).flatMap(([key, v]) => {
|
|
1082
|
+
if (as && key !== as) return [];
|
|
1083
|
+
try {
|
|
1084
|
+
return [[key, v()]];
|
|
1085
|
+
} catch (error) {
|
|
1086
|
+
throw new Error(`Failed to resolve ${key} from ${sourceType}`, {
|
|
1087
|
+
cause: error
|
|
1088
|
+
});
|
|
1089
|
+
}
|
|
1090
|
+
})
|
|
1091
|
+
);
|
|
1044
1092
|
if (as) {
|
|
1045
1093
|
return resolved[as];
|
|
1046
1094
|
}
|
|
1047
1095
|
return resolved;
|
|
1048
1096
|
}
|
|
1049
1097
|
|
|
1098
|
+
const toPathSegment = (segment) => /^\d+$/.test(segment) ? Number(segment) : segment;
|
|
1050
1099
|
function resolveEditPath(path, options) {
|
|
1051
|
-
const { pathSeparator = "." } = {};
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
return pathSeparator === false ? path2 : path2.split(pathSeparator);
|
|
1056
|
-
}
|
|
1057
|
-
return path2;
|
|
1058
|
-
}).flat();
|
|
1100
|
+
const { pathSeparator = "." } = options || {};
|
|
1101
|
+
if (Array.isArray(path)) return path;
|
|
1102
|
+
if (typeof path === "number" || pathSeparator === false) return [path];
|
|
1103
|
+
return path.split(pathSeparator).map(toPathSegment);
|
|
1059
1104
|
}
|
|
1060
|
-
function parseJSONCEdits(edits) {
|
|
1105
|
+
function parseJSONCEdits(edits, defaultEditOptions) {
|
|
1106
|
+
const resolve = (path, options) => resolveEditPath(path, { ...defaultEditOptions, ...options });
|
|
1061
1107
|
if (Array.isArray(edits)) {
|
|
1062
1108
|
return edits.map(({ path, ...edit }) => ({
|
|
1063
|
-
path:
|
|
1109
|
+
path: resolve(path, edit.options),
|
|
1064
1110
|
...edit
|
|
1065
1111
|
}));
|
|
1066
1112
|
}
|
|
1067
1113
|
return Object.entries(edits).map(
|
|
1068
1114
|
([path, value]) => ({
|
|
1069
|
-
path:
|
|
1115
|
+
path: resolve(path, value.options),
|
|
1070
1116
|
...value
|
|
1071
1117
|
})
|
|
1072
1118
|
);
|
|
@@ -1078,14 +1124,17 @@ function modifyJSON({
|
|
|
1078
1124
|
}) {
|
|
1079
1125
|
return checkResult(() => {
|
|
1080
1126
|
const text = resolveJsonSource(json, "text");
|
|
1081
|
-
const jsoncEdits = parseJSONCEdits(edits);
|
|
1082
|
-
const
|
|
1083
|
-
(edit) =>
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1127
|
+
const jsoncEdits = parseJSONCEdits(edits, defaultEditOptions);
|
|
1128
|
+
const updated = jsoncEdits.reduce(
|
|
1129
|
+
(current, edit) => applyEdits(
|
|
1130
|
+
current,
|
|
1131
|
+
modify(current, edit.path, edit.value, {
|
|
1132
|
+
...defaultEditOptions,
|
|
1133
|
+
...edit.options
|
|
1134
|
+
})
|
|
1135
|
+
),
|
|
1136
|
+
text
|
|
1087
1137
|
);
|
|
1088
|
-
const updated = applyEdits(text, editResult);
|
|
1089
1138
|
return resolveJsonSource({ text: updated });
|
|
1090
1139
|
});
|
|
1091
1140
|
}
|
|
@@ -1120,110 +1169,82 @@ const storage = createStorage({ driver: defineMemoryDriver() });
|
|
|
1120
1169
|
const tempFileSystem = defineFileSystemStorage({
|
|
1121
1170
|
base: join(os.tmpdir(), ".package-manager")
|
|
1122
1171
|
});
|
|
1172
|
+
const encodeEntry = (value) => typeof value === "string" ? value : JSON.stringify(value);
|
|
1123
1173
|
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
|
-
};
|
|
1174
|
+
const fileSystemEntries = Object.entries(definition).map(([key, value]) => {
|
|
1175
|
+
if (typeof value === "function") {
|
|
1176
|
+
const { file, options } = value();
|
|
1177
|
+
return { key, value: encodeEntry(file), options };
|
|
1145
1178
|
}
|
|
1146
|
-
if (isStorageValue(
|
|
1147
|
-
|
|
1148
|
-
key:
|
|
1149
|
-
|
|
1150
|
-
};
|
|
1179
|
+
if (!isStorageValue(value)) {
|
|
1180
|
+
throw new Error(
|
|
1181
|
+
`Cannot store a ${typeof value} at "${key}": file system entries must be a storage value or a function returning one.`
|
|
1182
|
+
);
|
|
1151
1183
|
}
|
|
1152
|
-
return
|
|
1153
|
-
})
|
|
1184
|
+
return { key, value: encodeEntry(value) };
|
|
1185
|
+
});
|
|
1154
1186
|
const resolved = {
|
|
1155
1187
|
definition,
|
|
1156
1188
|
fileSystemEntries
|
|
1157
1189
|
};
|
|
1158
1190
|
return resolved;
|
|
1159
1191
|
}
|
|
1192
|
+
const keyToFilePath = (root, key) => join(root, ...key.split(/[:/\\]+/).filter(Boolean));
|
|
1160
1193
|
function defineFileSystemStorage(options) {
|
|
1161
1194
|
const { base: root, initial, ...storageOptions } = options;
|
|
1162
|
-
let
|
|
1195
|
+
let entriesData = defineFileSystemEntries(initial ?? {});
|
|
1163
1196
|
const storage2 = createStorage({
|
|
1164
1197
|
driver: defineFsLiteDriver({ base: root, ...storageOptions })
|
|
1165
1198
|
});
|
|
1199
|
+
const getFilePath = (key) => keyToFilePath(root, key);
|
|
1200
|
+
const getFile = async (key) => ({
|
|
1201
|
+
key,
|
|
1202
|
+
filepath: getFilePath(key),
|
|
1203
|
+
data: await storage2.getItem(key),
|
|
1204
|
+
// `getItemRaw` resolves to null for a missing file rather than throwing.
|
|
1205
|
+
read: async () => (await storage2.getItemRaw(key))?.toString()
|
|
1206
|
+
});
|
|
1207
|
+
const removeAllFiles = async () => storage2.clear();
|
|
1166
1208
|
const fileStorage = {
|
|
1167
1209
|
createFile: async (key, data) => {
|
|
1168
1210
|
await storage2.setItem(key, data);
|
|
1169
1211
|
return {
|
|
1170
1212
|
key,
|
|
1171
|
-
filepath:
|
|
1213
|
+
filepath: getFilePath(key),
|
|
1172
1214
|
get: async () => storage2.getItem(key),
|
|
1173
1215
|
update: async (data2) => storage2.setItem(key, data2)
|
|
1174
1216
|
};
|
|
1175
1217
|
},
|
|
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
|
-
},
|
|
1218
|
+
getFilePath,
|
|
1219
|
+
getFile,
|
|
1220
|
+
readFile: async (key) => (await getFile(key)).read(),
|
|
1221
|
+
// `base` is a key prefix. Defaulting it to the filesystem root would match
|
|
1222
|
+
// no key at all and silently report an empty filesystem.
|
|
1223
|
+
snapshotFs: async (base = "") => snapshot(storage2, base),
|
|
1224
|
+
restoreFs: async (snapshot2, base) => restoreSnapshot(storage2, snapshot2, base),
|
|
1199
1225
|
initializeFs: async (override) => {
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
await storage2.
|
|
1204
|
-
return storage2.setItems(initialFileEntriesData.fileSystemEntries);
|
|
1226
|
+
const previousKeys = entriesData.fileSystemEntries.map(({ key }) => key);
|
|
1227
|
+
if (override) entriesData = defineFileSystemEntries(override);
|
|
1228
|
+
await Promise.all(previousKeys.map((key) => storage2.removeItem(key)));
|
|
1229
|
+
await storage2.setItems(entriesData.fileSystemEntries);
|
|
1205
1230
|
},
|
|
1206
1231
|
defineFileSystemEntries,
|
|
1207
|
-
removeAllFiles
|
|
1232
|
+
removeAllFiles,
|
|
1208
1233
|
async deleteFileSystem() {
|
|
1209
|
-
await
|
|
1210
|
-
await storage2.
|
|
1234
|
+
await removeAllFiles();
|
|
1235
|
+
await storage2.dispose();
|
|
1211
1236
|
await rm(root, { recursive: true, force: true });
|
|
1212
1237
|
},
|
|
1213
1238
|
storage: storage2,
|
|
1214
1239
|
meta: {
|
|
1215
|
-
|
|
1240
|
+
// A getter, so that `initializeFs(override)` is reflected here rather
|
|
1241
|
+
// than this reporting whatever the definition was at construction.
|
|
1242
|
+
get fileEntriesData() {
|
|
1243
|
+
return entriesData;
|
|
1244
|
+
}
|
|
1216
1245
|
}
|
|
1217
1246
|
};
|
|
1218
|
-
if (initial) {
|
|
1219
|
-
return {
|
|
1220
|
-
initialize: async () => {
|
|
1221
|
-
await fileStorage.initializeFs(initial);
|
|
1222
|
-
return fileStorage;
|
|
1223
|
-
}
|
|
1224
|
-
};
|
|
1225
|
-
}
|
|
1226
1247
|
return fileStorage;
|
|
1227
1248
|
}
|
|
1228
1249
|
|
|
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 };
|
|
1250
|
+
export { _dirname, _filename, createFile, defineFileSystemEntries, defineFileSystemStorage, definePackage, definePackageManager, definePackageManagerClient, definePathAliases, detectGlobalPackageManagers, detectLockfilePackageManagers, detectPackageManagers, filterPackageManagers, findDependencyInPackageJson, findPackageManager, findPackageManagerSafely, findResolvedModulePath, getAliasMap, getFolderByPackageName, getGitRootFolder, getGlobalVersions, getPackageFolder, getPath, getWorkspaceFolder, importMap, importer, isDependencyInPackageJson, isPackageDependency, isPackageModuleFound, isWritable, mapPackageManagers, modifyJSON, modifyJSONFile, packageManagerConfigs, predefinedPathAliases, project, resolveModule, resolveModulePath, resolvePackageModulePath, storage, tempFileSystem, workspace };
|