@ohos-ports/parcel-package-manager 2.16.4-beta.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/index.d.ts +40 -0
- package/lib/index.d.ts +10 -0
- package/lib/index.js +1968 -0
- package/lib/index.js.map +1 -0
- package/package.json +73 -0
- package/src/JSONParseStream.js +39 -0
- package/src/MockPackageInstaller.js +94 -0
- package/src/NodePackageManager.js +662 -0
- package/src/Npm.js +94 -0
- package/src/Pnpm.js +194 -0
- package/src/Yarn.js +157 -0
- package/src/getCurrentPackageManager.js +17 -0
- package/src/index.js +19 -0
- package/src/installPackage.js +284 -0
- package/src/promiseFromProcess.js +19 -0
- package/src/utils.js +93 -0
- package/src/validateModuleSpecifier.js +12 -0
- package/test/NodePackageManager.test.js +387 -0
- package/test/fixtures/empty/package.json +1 -0
- package/test/fixtures/has-a-not-yet-installed/package.json +5 -0
- package/test/fixtures/has-foo/node_modules/foo/index.js +1 -0
- package/test/fixtures/has-foo/node_modules/foo/package.json +3 -0
- package/test/fixtures/has-foo/node_modules/index.js +0 -0
- package/test/fixtures/has-foo/package.json +5 -0
- package/test/fixtures/has-foo/subpackage/package.json +1 -0
- package/test/fixtures/packages/a/index.js +1 -0
- package/test/fixtures/packages/a/package.json +3 -0
- package/test/fixtures/packages/foo-2.0/index.js +1 -0
- package/test/fixtures/packages/foo-2.0/package.json +4 -0
- package/test/fixtures/packages/peers/index.js +1 -0
- package/test/fixtures/packages/peers/package.json +7 -0
- package/test/fixtures/packages/peers-2.0/index.js +1 -0
- package/test/fixtures/packages/peers-2.0/package.json +7 -0
- package/test/getCurrentPackageManager.test.js +28 -0
- package/test/validateModuleSpecifiers.test.js +38 -0
|
@@ -0,0 +1,662 @@
|
|
|
1
|
+
// @flow
|
|
2
|
+
import type {FilePath, DependencySpecifier, SemverRange} from '@parcel/types';
|
|
3
|
+
import type {FileSystem} from '@parcel/fs';
|
|
4
|
+
import type {
|
|
5
|
+
ModuleRequest,
|
|
6
|
+
PackageManager,
|
|
7
|
+
PackageInstaller,
|
|
8
|
+
InstallOptions,
|
|
9
|
+
Invalidations,
|
|
10
|
+
PackageManagerResolveResult,
|
|
11
|
+
} from '@parcel/types';
|
|
12
|
+
|
|
13
|
+
import {registerSerializableClass} from '@parcel/core';
|
|
14
|
+
import ThrowableDiagnostic, {
|
|
15
|
+
encodeJSONKeyComponent,
|
|
16
|
+
escapeMarkdown,
|
|
17
|
+
generateJSONCodeHighlights,
|
|
18
|
+
md,
|
|
19
|
+
} from '@parcel/diagnostic';
|
|
20
|
+
import {NodeFS} from '@parcel/fs';
|
|
21
|
+
import nativeFS from 'fs';
|
|
22
|
+
import Module from 'module';
|
|
23
|
+
import path from 'path';
|
|
24
|
+
import semver from 'semver';
|
|
25
|
+
import logger from '@parcel/logger';
|
|
26
|
+
|
|
27
|
+
import {getModuleParts} from '@parcel/utils';
|
|
28
|
+
import {getConflictingLocalDependencies} from './utils';
|
|
29
|
+
import {installPackage} from './installPackage';
|
|
30
|
+
import pkg from '../package.json';
|
|
31
|
+
import {ResolverBase} from '@parcel/node-resolver-core';
|
|
32
|
+
import {pathToFileURL} from 'url';
|
|
33
|
+
import {transformSync} from '@swc/core';
|
|
34
|
+
|
|
35
|
+
// Package.json fields. Must match package_json.rs.
|
|
36
|
+
const MAIN = 1 << 0;
|
|
37
|
+
const SOURCE = 1 << 2;
|
|
38
|
+
const NODE_CONDITION = 1 << 3;
|
|
39
|
+
const SOURCE_CONDITION = 1 << 17;
|
|
40
|
+
const ENTRIES = MAIN | SOURCE;
|
|
41
|
+
const CONDITIONS = NODE_CONDITION | SOURCE_CONDITION;
|
|
42
|
+
|
|
43
|
+
const NODE_MODULES = `${path.sep}node_modules${path.sep}`;
|
|
44
|
+
|
|
45
|
+
const IS_FILE = 1 << 0;
|
|
46
|
+
const IS_DIR = 1 << 1;
|
|
47
|
+
const IS_SYMLINK = 1 << 2;
|
|
48
|
+
|
|
49
|
+
// There can be more than one instance of NodePackageManager, but node has only a single module cache.
|
|
50
|
+
// Therefore, the resolution cache and the map of parent to child modules should also be global.
|
|
51
|
+
const cache = new Map<DependencySpecifier, PackageManagerResolveResult>();
|
|
52
|
+
const children = new Map<FilePath, Set<DependencySpecifier>>();
|
|
53
|
+
const invalidationsCache = new Map<string, Invalidations>();
|
|
54
|
+
|
|
55
|
+
// This implements a package manager for Node by monkey patching the Node require
|
|
56
|
+
// algorithm so that it uses the specified FileSystem instead of the native one.
|
|
57
|
+
// It also handles installing packages when they are required if not already installed.
|
|
58
|
+
// See https://github.com/nodejs/node/blob/master/lib/internal/modules/cjs/loader.js
|
|
59
|
+
// for reference to Node internals.
|
|
60
|
+
export class NodePackageManager implements PackageManager {
|
|
61
|
+
fs: FileSystem;
|
|
62
|
+
projectRoot: FilePath;
|
|
63
|
+
installer: ?PackageInstaller;
|
|
64
|
+
resolver: ResolverBase;
|
|
65
|
+
currentExtensions: Array<string>;
|
|
66
|
+
|
|
67
|
+
constructor(
|
|
68
|
+
fs: FileSystem,
|
|
69
|
+
projectRoot: FilePath,
|
|
70
|
+
installer?: ?PackageInstaller,
|
|
71
|
+
) {
|
|
72
|
+
this.fs = fs;
|
|
73
|
+
this.projectRoot = projectRoot;
|
|
74
|
+
this.installer = installer;
|
|
75
|
+
|
|
76
|
+
// $FlowFixMe - no type for _extensions
|
|
77
|
+
this.currentExtensions = Object.keys(Module._extensions).map(e =>
|
|
78
|
+
e.substring(1),
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
_createResolver(): ResolverBase {
|
|
83
|
+
return new ResolverBase(this.projectRoot, {
|
|
84
|
+
fs:
|
|
85
|
+
this.fs instanceof NodeFS && process.versions.pnp == null
|
|
86
|
+
? undefined
|
|
87
|
+
: {
|
|
88
|
+
read: path => this.fs.readFileSync(path),
|
|
89
|
+
kind: path => {
|
|
90
|
+
let flags = 0;
|
|
91
|
+
try {
|
|
92
|
+
let stat = this.fs.lstatSync(path);
|
|
93
|
+
if (stat.isSymbolicLink()) {
|
|
94
|
+
flags |= IS_SYMLINK;
|
|
95
|
+
stat = this.fs.statSync(path);
|
|
96
|
+
}
|
|
97
|
+
if (stat.isFile()) {
|
|
98
|
+
flags |= IS_FILE;
|
|
99
|
+
} else if (stat.isDirectory()) {
|
|
100
|
+
flags |= IS_DIR;
|
|
101
|
+
}
|
|
102
|
+
} catch (err) {
|
|
103
|
+
// ignore
|
|
104
|
+
}
|
|
105
|
+
return flags;
|
|
106
|
+
},
|
|
107
|
+
readLink: path => this.fs.readlinkSync(path),
|
|
108
|
+
},
|
|
109
|
+
mode: 2,
|
|
110
|
+
entries: ENTRIES,
|
|
111
|
+
conditions: CONDITIONS,
|
|
112
|
+
packageExports: true,
|
|
113
|
+
moduleDirResolver:
|
|
114
|
+
process.versions.pnp != null
|
|
115
|
+
? (module, from) => {
|
|
116
|
+
// $FlowFixMe[prop-missing]
|
|
117
|
+
let pnp = Module.findPnpApi(path.dirname(from));
|
|
118
|
+
|
|
119
|
+
return pnp.resolveToUnqualified(
|
|
120
|
+
// append slash to force loading builtins from npm
|
|
121
|
+
module + '/',
|
|
122
|
+
from,
|
|
123
|
+
);
|
|
124
|
+
}
|
|
125
|
+
: undefined,
|
|
126
|
+
extensions: this.currentExtensions,
|
|
127
|
+
typescript: true,
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
static deserialize(opts: any): NodePackageManager {
|
|
132
|
+
return new NodePackageManager(opts.fs, opts.projectRoot, opts.installer);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
serialize(): {|
|
|
136
|
+
$$raw: boolean,
|
|
137
|
+
fs: FileSystem,
|
|
138
|
+
projectRoot: FilePath,
|
|
139
|
+
installer: ?PackageInstaller,
|
|
140
|
+
|} {
|
|
141
|
+
return {
|
|
142
|
+
$$raw: false,
|
|
143
|
+
fs: this.fs,
|
|
144
|
+
projectRoot: this.projectRoot,
|
|
145
|
+
installer: this.installer,
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
async require(
|
|
150
|
+
name: DependencySpecifier,
|
|
151
|
+
from: FilePath,
|
|
152
|
+
opts: ?{|
|
|
153
|
+
range?: ?SemverRange,
|
|
154
|
+
shouldAutoInstall?: boolean,
|
|
155
|
+
saveDev?: boolean,
|
|
156
|
+
|},
|
|
157
|
+
): Promise<any> {
|
|
158
|
+
let {resolved, type} = await this.resolve(name, from, opts);
|
|
159
|
+
if (type === 2) {
|
|
160
|
+
logger.warn({
|
|
161
|
+
message: 'ES module dependencies are experimental.',
|
|
162
|
+
origin: '@parcel/package-manager',
|
|
163
|
+
codeFrames: [
|
|
164
|
+
{
|
|
165
|
+
filePath: resolved,
|
|
166
|
+
codeHighlights: [],
|
|
167
|
+
},
|
|
168
|
+
],
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
// On Windows, Node requires absolute paths to be file URLs.
|
|
172
|
+
if (process.platform === 'win32' && path.isAbsolute(resolved)) {
|
|
173
|
+
resolved = pathToFileURL(resolved);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// $FlowFixMe
|
|
177
|
+
return import(resolved);
|
|
178
|
+
}
|
|
179
|
+
return this.load(resolved, from);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
requireSync(name: DependencySpecifier, from: FilePath): any {
|
|
183
|
+
let {resolved} = this.resolveSync(name, from);
|
|
184
|
+
return this.load(resolved, from);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
load(filePath: FilePath, from: FilePath): any {
|
|
188
|
+
if (!path.isAbsolute(filePath)) {
|
|
189
|
+
// Node builtin module
|
|
190
|
+
// $FlowFixMe
|
|
191
|
+
return require(filePath);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// $FlowFixMe[prop-missing]
|
|
195
|
+
const cachedModule = Module._cache[filePath];
|
|
196
|
+
if (cachedModule !== undefined) {
|
|
197
|
+
return cachedModule.exports;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// $FlowFixMe
|
|
201
|
+
let m = new Module(filePath, Module._cache[from] || module.parent);
|
|
202
|
+
|
|
203
|
+
// $FlowFixMe _extensions not in type
|
|
204
|
+
const extensions = Object.keys(Module._extensions);
|
|
205
|
+
// This handles supported extensions changing due to, for example, esbuild/register being used
|
|
206
|
+
// We assume that the extension list will change in size - as these tools usually add support for
|
|
207
|
+
// additional extensions.
|
|
208
|
+
if (extensions.length !== this.currentExtensions.length) {
|
|
209
|
+
this.currentExtensions = extensions.map(e => e.substring(1));
|
|
210
|
+
this.resolver = this._createResolver();
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
// $FlowFixMe[prop-missing]
|
|
214
|
+
Module._cache[filePath] = m;
|
|
215
|
+
|
|
216
|
+
// Patch require within this module so it goes through our require
|
|
217
|
+
m.require = id => {
|
|
218
|
+
return this.requireSync(id, filePath);
|
|
219
|
+
};
|
|
220
|
+
|
|
221
|
+
// Patch `fs.readFileSync` temporarily so that it goes through our file system
|
|
222
|
+
let {readFileSync, statSync} = nativeFS;
|
|
223
|
+
// $FlowFixMe
|
|
224
|
+
nativeFS.readFileSync = (filename, encoding) => {
|
|
225
|
+
return this.fs.readFileSync(filename, encoding);
|
|
226
|
+
};
|
|
227
|
+
|
|
228
|
+
// $FlowFixMe
|
|
229
|
+
nativeFS.statSync = filename => {
|
|
230
|
+
return this.fs.statSync(filename);
|
|
231
|
+
};
|
|
232
|
+
|
|
233
|
+
if (!filePath.includes(NODE_MODULES)) {
|
|
234
|
+
let extname = path.extname(filePath);
|
|
235
|
+
if (
|
|
236
|
+
(extname === '.ts' ||
|
|
237
|
+
extname === '.tsx' ||
|
|
238
|
+
extname === '.mts' ||
|
|
239
|
+
extname === '.cts') &&
|
|
240
|
+
// $FlowFixMe
|
|
241
|
+
!Module._extensions[extname]
|
|
242
|
+
) {
|
|
243
|
+
let compile = m._compile;
|
|
244
|
+
m._compile = (code, filename) => {
|
|
245
|
+
let out = transformSync(code, {
|
|
246
|
+
filename,
|
|
247
|
+
module: {type: 'commonjs', ignoreDynamic: true},
|
|
248
|
+
});
|
|
249
|
+
compile.call(m, out.code, filename);
|
|
250
|
+
};
|
|
251
|
+
|
|
252
|
+
// $FlowFixMe
|
|
253
|
+
Module._extensions[extname] = (m, filename) => {
|
|
254
|
+
// $FlowFixMe
|
|
255
|
+
delete Module._extensions[extname];
|
|
256
|
+
// $FlowFixMe
|
|
257
|
+
Module._extensions['.js'](m, filename);
|
|
258
|
+
};
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
try {
|
|
263
|
+
m.load(filePath);
|
|
264
|
+
} catch (err) {
|
|
265
|
+
// $FlowFixMe[prop-missing]
|
|
266
|
+
delete Module._cache[filePath];
|
|
267
|
+
throw err;
|
|
268
|
+
} finally {
|
|
269
|
+
// $FlowFixMe
|
|
270
|
+
nativeFS.readFileSync = readFileSync;
|
|
271
|
+
// $FlowFixMe
|
|
272
|
+
nativeFS.statSync = statSync;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
return m.exports;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
async resolve(
|
|
279
|
+
id: DependencySpecifier,
|
|
280
|
+
from: FilePath,
|
|
281
|
+
options?: ?{|
|
|
282
|
+
range?: ?SemverRange,
|
|
283
|
+
shouldAutoInstall?: boolean,
|
|
284
|
+
saveDev?: boolean,
|
|
285
|
+
|},
|
|
286
|
+
): Promise<PackageManagerResolveResult> {
|
|
287
|
+
let basedir = path.dirname(from);
|
|
288
|
+
let key = basedir + ':' + id;
|
|
289
|
+
let resolved = cache.get(key);
|
|
290
|
+
if (!resolved) {
|
|
291
|
+
let [name] = getModuleParts(id);
|
|
292
|
+
try {
|
|
293
|
+
resolved = this.resolveInternal(id, from);
|
|
294
|
+
} catch (e) {
|
|
295
|
+
if (
|
|
296
|
+
e.code !== 'MODULE_NOT_FOUND' ||
|
|
297
|
+
options?.shouldAutoInstall !== true ||
|
|
298
|
+
id.startsWith('.') // a local file, don't autoinstall
|
|
299
|
+
) {
|
|
300
|
+
if (
|
|
301
|
+
e.code === 'MODULE_NOT_FOUND' &&
|
|
302
|
+
options?.shouldAutoInstall !== true
|
|
303
|
+
) {
|
|
304
|
+
let err = new ThrowableDiagnostic({
|
|
305
|
+
diagnostic: {
|
|
306
|
+
message: escapeMarkdown(e.message),
|
|
307
|
+
hints: [
|
|
308
|
+
'Autoinstall is disabled, please install this package manually and restart Parcel.',
|
|
309
|
+
],
|
|
310
|
+
},
|
|
311
|
+
});
|
|
312
|
+
// $FlowFixMe - needed for loadParcelPlugin
|
|
313
|
+
err.code = 'MODULE_NOT_FOUND';
|
|
314
|
+
throw err;
|
|
315
|
+
} else {
|
|
316
|
+
throw e;
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
let conflicts = await getConflictingLocalDependencies(
|
|
321
|
+
this.fs,
|
|
322
|
+
name,
|
|
323
|
+
from,
|
|
324
|
+
this.projectRoot,
|
|
325
|
+
);
|
|
326
|
+
|
|
327
|
+
if (conflicts == null) {
|
|
328
|
+
this.invalidate(id, from);
|
|
329
|
+
await this.install([{name, range: options?.range}], from, {
|
|
330
|
+
saveDev: options?.saveDev ?? true,
|
|
331
|
+
});
|
|
332
|
+
|
|
333
|
+
return this.resolve(id, from, {
|
|
334
|
+
...options,
|
|
335
|
+
shouldAutoInstall: false,
|
|
336
|
+
});
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
throw new ThrowableDiagnostic({
|
|
340
|
+
diagnostic: conflicts.fields.map(field => ({
|
|
341
|
+
message: md`Could not find module "${name}", but it was listed in package.json. Run your package manager first.`,
|
|
342
|
+
origin: '@parcel/package-manager',
|
|
343
|
+
codeFrames: [
|
|
344
|
+
{
|
|
345
|
+
filePath: conflicts.filePath,
|
|
346
|
+
language: 'json',
|
|
347
|
+
code: conflicts.json,
|
|
348
|
+
codeHighlights: generateJSONCodeHighlights(conflicts.json, [
|
|
349
|
+
{
|
|
350
|
+
key: `/${field}/${encodeJSONKeyComponent(name)}`,
|
|
351
|
+
type: 'key',
|
|
352
|
+
message: 'Defined here, but not installed',
|
|
353
|
+
},
|
|
354
|
+
]),
|
|
355
|
+
},
|
|
356
|
+
],
|
|
357
|
+
})),
|
|
358
|
+
});
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
let range = options?.range;
|
|
362
|
+
if (range != null) {
|
|
363
|
+
let pkg = resolved.pkg;
|
|
364
|
+
if (pkg == null || !semver.satisfies(pkg.version, range)) {
|
|
365
|
+
let conflicts = await getConflictingLocalDependencies(
|
|
366
|
+
this.fs,
|
|
367
|
+
name,
|
|
368
|
+
from,
|
|
369
|
+
this.projectRoot,
|
|
370
|
+
);
|
|
371
|
+
|
|
372
|
+
if (conflicts == null && options?.shouldAutoInstall === true) {
|
|
373
|
+
this.invalidate(id, from);
|
|
374
|
+
await this.install([{name, range}], from);
|
|
375
|
+
return this.resolve(id, from, {
|
|
376
|
+
...options,
|
|
377
|
+
shouldAutoInstall: false,
|
|
378
|
+
});
|
|
379
|
+
} else if (conflicts != null) {
|
|
380
|
+
throw new ThrowableDiagnostic({
|
|
381
|
+
diagnostic: {
|
|
382
|
+
message: md`Could not find module "${name}" satisfying ${range}.`,
|
|
383
|
+
origin: '@parcel/package-manager',
|
|
384
|
+
codeFrames: [
|
|
385
|
+
{
|
|
386
|
+
filePath: conflicts.filePath,
|
|
387
|
+
language: 'json',
|
|
388
|
+
code: conflicts.json,
|
|
389
|
+
codeHighlights: generateJSONCodeHighlights(
|
|
390
|
+
conflicts.json,
|
|
391
|
+
conflicts.fields.map(field => ({
|
|
392
|
+
key: `/${field}/${encodeJSONKeyComponent(name)}`,
|
|
393
|
+
type: 'key',
|
|
394
|
+
message: 'Found this conflicting local requirement.',
|
|
395
|
+
})),
|
|
396
|
+
),
|
|
397
|
+
},
|
|
398
|
+
],
|
|
399
|
+
},
|
|
400
|
+
});
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
let version = pkg?.version;
|
|
404
|
+
let message = md`Could not resolve package "${name}" that satisfies ${range}.`;
|
|
405
|
+
if (version != null) {
|
|
406
|
+
message += md` Found ${version}.`;
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
throw new ThrowableDiagnostic({
|
|
410
|
+
diagnostic: {
|
|
411
|
+
message,
|
|
412
|
+
hints: [
|
|
413
|
+
'Looks like the incompatible version was installed transitively. Add this package as a direct dependency with a compatible version range.',
|
|
414
|
+
],
|
|
415
|
+
},
|
|
416
|
+
});
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
cache.set(key, resolved);
|
|
421
|
+
invalidationsCache.clear();
|
|
422
|
+
|
|
423
|
+
// Add the specifier as a child to the parent module.
|
|
424
|
+
// Don't do this if the specifier was an absolute path, as this was likely a dynamically resolved path
|
|
425
|
+
// (e.g. babel uses require() to load .babelrc.js configs and we don't want them to be added as children of babel itself).
|
|
426
|
+
if (!path.isAbsolute(name)) {
|
|
427
|
+
let moduleChildren = children.get(from);
|
|
428
|
+
if (!moduleChildren) {
|
|
429
|
+
moduleChildren = new Set();
|
|
430
|
+
children.set(from, moduleChildren);
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
moduleChildren.add(name);
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
return resolved;
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
resolveSync(
|
|
441
|
+
name: DependencySpecifier,
|
|
442
|
+
from: FilePath,
|
|
443
|
+
): PackageManagerResolveResult {
|
|
444
|
+
let basedir = path.dirname(from);
|
|
445
|
+
let key = basedir + ':' + name;
|
|
446
|
+
let resolved = cache.get(key);
|
|
447
|
+
if (!resolved) {
|
|
448
|
+
resolved = this.resolveInternal(name, from);
|
|
449
|
+
cache.set(key, resolved);
|
|
450
|
+
invalidationsCache.clear();
|
|
451
|
+
|
|
452
|
+
if (!path.isAbsolute(name)) {
|
|
453
|
+
let moduleChildren = children.get(from);
|
|
454
|
+
if (!moduleChildren) {
|
|
455
|
+
moduleChildren = new Set();
|
|
456
|
+
children.set(from, moduleChildren);
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
moduleChildren.add(name);
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
return resolved;
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
async install(
|
|
467
|
+
modules: Array<ModuleRequest>,
|
|
468
|
+
from: FilePath,
|
|
469
|
+
opts?: InstallOptions,
|
|
470
|
+
) {
|
|
471
|
+
await installPackage(this.fs, this, modules, from, this.projectRoot, {
|
|
472
|
+
packageInstaller: this.installer,
|
|
473
|
+
...opts,
|
|
474
|
+
});
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
getInvalidations(name: DependencySpecifier, from: FilePath): Invalidations {
|
|
478
|
+
let basedir = path.dirname(from);
|
|
479
|
+
let cacheKey = basedir + ':' + name;
|
|
480
|
+
let resolved = cache.get(cacheKey);
|
|
481
|
+
|
|
482
|
+
if (resolved && path.isAbsolute(resolved.resolved)) {
|
|
483
|
+
let cached = invalidationsCache.get(resolved.resolved);
|
|
484
|
+
if (cached != null) {
|
|
485
|
+
return cached;
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
let res = {
|
|
489
|
+
invalidateOnFileCreate: [],
|
|
490
|
+
invalidateOnFileChange: new Set(),
|
|
491
|
+
invalidateOnStartup: false,
|
|
492
|
+
};
|
|
493
|
+
|
|
494
|
+
let seen = new Set();
|
|
495
|
+
let addKey = (name, from) => {
|
|
496
|
+
let basedir = path.dirname(from);
|
|
497
|
+
let key = basedir + ':' + name;
|
|
498
|
+
if (seen.has(key)) {
|
|
499
|
+
return;
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
seen.add(key);
|
|
503
|
+
let resolved = cache.get(key);
|
|
504
|
+
if (!resolved || !path.isAbsolute(resolved.resolved)) {
|
|
505
|
+
return;
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
res.invalidateOnFileCreate.push(...resolved.invalidateOnFileCreate);
|
|
509
|
+
res.invalidateOnFileChange.add(resolved.resolved);
|
|
510
|
+
|
|
511
|
+
for (let file of resolved.invalidateOnFileChange) {
|
|
512
|
+
res.invalidateOnFileChange.add(file);
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
let moduleChildren = children.get(resolved.resolved);
|
|
516
|
+
if (moduleChildren) {
|
|
517
|
+
for (let specifier of moduleChildren) {
|
|
518
|
+
addKey(specifier, resolved.resolved);
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
};
|
|
522
|
+
|
|
523
|
+
addKey(name, from);
|
|
524
|
+
|
|
525
|
+
// If this is an ES module, we won't have any of the dependencies because import statements
|
|
526
|
+
// cannot be intercepted. Instead, ask the resolver to parse the file and recursively analyze the deps.
|
|
527
|
+
if (resolved.type === 2) {
|
|
528
|
+
let invalidations = this.resolver.getInvalidations(resolved.resolved);
|
|
529
|
+
invalidations.invalidateOnFileChange.forEach(i =>
|
|
530
|
+
res.invalidateOnFileChange.add(i),
|
|
531
|
+
);
|
|
532
|
+
invalidations.invalidateOnFileCreate.forEach(i =>
|
|
533
|
+
res.invalidateOnFileCreate.push(i),
|
|
534
|
+
);
|
|
535
|
+
res.invalidateOnStartup ||= invalidations.invalidateOnStartup;
|
|
536
|
+
if (res.invalidateOnStartup) {
|
|
537
|
+
logger.warn({
|
|
538
|
+
message: md`${path.relative(
|
|
539
|
+
this.projectRoot,
|
|
540
|
+
resolved.resolved,
|
|
541
|
+
)} contains non-statically analyzable dependencies in its module graph. This causes Parcel to invalidate the cache on startup.`,
|
|
542
|
+
origin: '@parcel/package-manager',
|
|
543
|
+
});
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
invalidationsCache.set(resolved.resolved, res);
|
|
548
|
+
return res;
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
return {
|
|
552
|
+
invalidateOnFileCreate: [],
|
|
553
|
+
invalidateOnFileChange: new Set(),
|
|
554
|
+
invalidateOnStartup: false,
|
|
555
|
+
};
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
invalidate(name: DependencySpecifier, from: FilePath) {
|
|
559
|
+
let seen = new Set();
|
|
560
|
+
|
|
561
|
+
let invalidate = (name, from) => {
|
|
562
|
+
let basedir = path.dirname(from);
|
|
563
|
+
let key = basedir + ':' + name;
|
|
564
|
+
if (seen.has(key)) {
|
|
565
|
+
return;
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
seen.add(key);
|
|
569
|
+
let resolved = cache.get(key);
|
|
570
|
+
if (!resolved || !path.isAbsolute(resolved.resolved)) {
|
|
571
|
+
return;
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
invalidationsCache.delete(resolved.resolved);
|
|
575
|
+
|
|
576
|
+
// $FlowFixMe
|
|
577
|
+
let module = Module._cache[resolved.resolved];
|
|
578
|
+
if (module) {
|
|
579
|
+
// $FlowFixMe
|
|
580
|
+
delete Module._cache[resolved.resolved];
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
let moduleChildren = children.get(resolved.resolved);
|
|
584
|
+
if (moduleChildren) {
|
|
585
|
+
for (let specifier of moduleChildren) {
|
|
586
|
+
invalidate(specifier, resolved.resolved);
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
children.delete(resolved.resolved);
|
|
591
|
+
cache.delete(key);
|
|
592
|
+
};
|
|
593
|
+
|
|
594
|
+
invalidate(name, from);
|
|
595
|
+
this.resolver = this._createResolver();
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
resolveInternal(name: string, from: string): PackageManagerResolveResult {
|
|
599
|
+
if (this.resolver == null) {
|
|
600
|
+
this.resolver = this._createResolver();
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
let res = this.resolver.resolve({
|
|
604
|
+
filename: name,
|
|
605
|
+
specifierType: 'commonjs',
|
|
606
|
+
parent: from,
|
|
607
|
+
});
|
|
608
|
+
|
|
609
|
+
// Invalidate whenever the .pnp.js file changes.
|
|
610
|
+
// TODO: only when we actually resolve a node_modules package?
|
|
611
|
+
if (process.versions.pnp != null && res.invalidateOnFileChange) {
|
|
612
|
+
// $FlowFixMe[prop-missing]
|
|
613
|
+
let pnp = Module.findPnpApi(path.dirname(from));
|
|
614
|
+
res.invalidateOnFileChange.push(pnp.resolveToUnqualified('pnpapi', null));
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
if (res.error) {
|
|
618
|
+
let e = new Error(`Could not resolve module "${name}" from "${from}"`);
|
|
619
|
+
// $FlowFixMe
|
|
620
|
+
e.code = 'MODULE_NOT_FOUND';
|
|
621
|
+
throw e;
|
|
622
|
+
}
|
|
623
|
+
switch (res.resolution.type) {
|
|
624
|
+
case 'Path': {
|
|
625
|
+
let self = this;
|
|
626
|
+
let resolved = res.resolution.value;
|
|
627
|
+
return {
|
|
628
|
+
resolved,
|
|
629
|
+
invalidateOnFileChange: new Set(res.invalidateOnFileChange),
|
|
630
|
+
invalidateOnFileCreate: res.invalidateOnFileCreate,
|
|
631
|
+
type: res.moduleType,
|
|
632
|
+
get pkg() {
|
|
633
|
+
let pkgPath = self.fs.findAncestorFile(
|
|
634
|
+
['package.json'],
|
|
635
|
+
resolved,
|
|
636
|
+
self.projectRoot,
|
|
637
|
+
);
|
|
638
|
+
return pkgPath
|
|
639
|
+
? JSON.parse(self.fs.readFileSync(pkgPath, 'utf8'))
|
|
640
|
+
: null;
|
|
641
|
+
},
|
|
642
|
+
};
|
|
643
|
+
}
|
|
644
|
+
case 'Builtin': {
|
|
645
|
+
let {scheme, module} = res.resolution.value;
|
|
646
|
+
return {
|
|
647
|
+
resolved: scheme ? `${scheme}:${module}` : module,
|
|
648
|
+
invalidateOnFileChange: new Set(res.invalidateOnFileChange),
|
|
649
|
+
invalidateOnFileCreate: res.invalidateOnFileCreate,
|
|
650
|
+
type: res.moduleType,
|
|
651
|
+
};
|
|
652
|
+
}
|
|
653
|
+
default:
|
|
654
|
+
throw new Error('Unknown resolution type');
|
|
655
|
+
}
|
|
656
|
+
}
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
registerSerializableClass(
|
|
660
|
+
`${pkg.version}:NodePackageManager`,
|
|
661
|
+
NodePackageManager,
|
|
662
|
+
);
|