@ohos-ports/parcel-package-manager 2.9.3-beta.1
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 +22 -0
- package/lib/index.js +4967 -0
- package/lib/index.js.map +1 -0
- package/lib/types.d.ts +47 -0
- package/package.json +69 -0
- package/src/JSONParseStream.js +39 -0
- package/src/MockPackageInstaller.js +90 -0
- package/src/NodePackageManager.js +583 -0
- package/src/Npm.js +94 -0
- package/src/Pnpm.js +194 -0
- package/src/Yarn.js +157 -0
- package/src/index.js +8 -0
- package/src/installPackage.js +274 -0
- package/src/promiseFromProcess.js +19 -0
- package/src/types.js +63 -0
- package/src/utils.js +92 -0
- package/src/validateModuleSpecifier.js +12 -0
- package/test/NodePackageManager.test.js +310 -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/validateModuleSpecifiers.test.js +38 -0
package/src/Pnpm.js
ADDED
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
// @flow strict-local
|
|
2
|
+
|
|
3
|
+
import type {PackageInstaller, InstallerOptions} from './types';
|
|
4
|
+
|
|
5
|
+
import path from 'path';
|
|
6
|
+
import fs from 'fs';
|
|
7
|
+
import commandExists from 'command-exists';
|
|
8
|
+
import spawn from 'cross-spawn';
|
|
9
|
+
import logger from '@parcel/logger';
|
|
10
|
+
import split from 'split2';
|
|
11
|
+
import JSONParseStream from './JSONParseStream';
|
|
12
|
+
import promiseFromProcess from './promiseFromProcess';
|
|
13
|
+
import {registerSerializableClass} from '@parcel/core';
|
|
14
|
+
import {exec, npmSpecifierFromModuleRequest} from './utils';
|
|
15
|
+
|
|
16
|
+
// $FlowFixMe
|
|
17
|
+
import pkg from '../package.json';
|
|
18
|
+
|
|
19
|
+
const PNPM_CMD = 'pnpm';
|
|
20
|
+
|
|
21
|
+
type LogLevel = 'error' | 'warn' | 'info' | 'debug';
|
|
22
|
+
|
|
23
|
+
type ErrorLog = {|
|
|
24
|
+
err: {|
|
|
25
|
+
message: string,
|
|
26
|
+
code: string,
|
|
27
|
+
stack: string,
|
|
28
|
+
|},
|
|
29
|
+
|};
|
|
30
|
+
|
|
31
|
+
type PNPMLog =
|
|
32
|
+
| {|
|
|
33
|
+
+name: 'pnpm:progress',
|
|
34
|
+
packageId: string,
|
|
35
|
+
status: 'fetched' | 'found_in_store' | 'resolved',
|
|
36
|
+
|}
|
|
37
|
+
| {|
|
|
38
|
+
+name: 'pnpm:root',
|
|
39
|
+
added?: {|
|
|
40
|
+
id?: string,
|
|
41
|
+
name: string,
|
|
42
|
+
realName: string,
|
|
43
|
+
version?: string,
|
|
44
|
+
dependencyType?: 'prod' | 'dev' | 'optional',
|
|
45
|
+
latest?: string,
|
|
46
|
+
linkedFrom?: string,
|
|
47
|
+
|},
|
|
48
|
+
removed?: {|
|
|
49
|
+
name: string,
|
|
50
|
+
version?: string,
|
|
51
|
+
dependencyType?: 'prod' | 'dev' | 'optional',
|
|
52
|
+
|},
|
|
53
|
+
|}
|
|
54
|
+
| {|+name: 'pnpm:importing', from: string, method: string, to: string|}
|
|
55
|
+
| {|+name: 'pnpm:link', target: string, link: string|}
|
|
56
|
+
| {|+name: 'pnpm:stats', prefix: string, removed?: number, added?: number|};
|
|
57
|
+
|
|
58
|
+
type PNPMResults = {|
|
|
59
|
+
level: LogLevel,
|
|
60
|
+
prefix?: string,
|
|
61
|
+
message?: string,
|
|
62
|
+
...ErrorLog,
|
|
63
|
+
...PNPMLog,
|
|
64
|
+
|};
|
|
65
|
+
|
|
66
|
+
let hasPnpm: ?boolean;
|
|
67
|
+
let pnpmVersion: ?number;
|
|
68
|
+
|
|
69
|
+
export class Pnpm implements PackageInstaller {
|
|
70
|
+
static async exists(): Promise<boolean> {
|
|
71
|
+
if (hasPnpm != null) {
|
|
72
|
+
return hasPnpm;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
try {
|
|
76
|
+
hasPnpm = Boolean(await commandExists('pnpm'));
|
|
77
|
+
} catch (err) {
|
|
78
|
+
hasPnpm = false;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return hasPnpm;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
async install({
|
|
85
|
+
modules,
|
|
86
|
+
cwd,
|
|
87
|
+
saveDev = true,
|
|
88
|
+
}: InstallerOptions): Promise<void> {
|
|
89
|
+
if (pnpmVersion == null) {
|
|
90
|
+
let version = await exec('pnpm --version');
|
|
91
|
+
pnpmVersion = parseInt(version.stdout, 10);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
let args = ['add', '--reporter', 'ndjson'];
|
|
95
|
+
if (saveDev) {
|
|
96
|
+
args.push('-D');
|
|
97
|
+
}
|
|
98
|
+
if (pnpmVersion >= 7) {
|
|
99
|
+
if (fs.existsSync(path.join(cwd, 'pnpm-workspace.yaml'))) {
|
|
100
|
+
// installs in workspace root (regardless of cwd)
|
|
101
|
+
args.push('-w');
|
|
102
|
+
}
|
|
103
|
+
} else {
|
|
104
|
+
// ignores workspace root check
|
|
105
|
+
args.push('-W');
|
|
106
|
+
}
|
|
107
|
+
args = args.concat(modules.map(npmSpecifierFromModuleRequest));
|
|
108
|
+
|
|
109
|
+
let env = {};
|
|
110
|
+
for (let key in process.env) {
|
|
111
|
+
if (!key.startsWith('npm_') && key !== 'INIT_CWD' && key !== 'NODE_ENV') {
|
|
112
|
+
env[key] = process.env[key];
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
let addedCount = 0,
|
|
117
|
+
removedCount = 0;
|
|
118
|
+
|
|
119
|
+
let installProcess = spawn(PNPM_CMD, args, {
|
|
120
|
+
cwd,
|
|
121
|
+
env,
|
|
122
|
+
});
|
|
123
|
+
installProcess.stdout
|
|
124
|
+
.pipe(split())
|
|
125
|
+
.pipe(new JSONParseStream())
|
|
126
|
+
.on('error', e => {
|
|
127
|
+
logger.warn({
|
|
128
|
+
origin: '@parcel/package-manager',
|
|
129
|
+
message: e.chunk,
|
|
130
|
+
stack: e.stack,
|
|
131
|
+
});
|
|
132
|
+
})
|
|
133
|
+
.on('data', (json: PNPMResults) => {
|
|
134
|
+
if (json.level === 'error') {
|
|
135
|
+
logger.error({
|
|
136
|
+
origin: '@parcel/package-manager',
|
|
137
|
+
message: json.err.message,
|
|
138
|
+
stack: json.err.stack,
|
|
139
|
+
});
|
|
140
|
+
} else if (json.level === 'info' && typeof json.message === 'string') {
|
|
141
|
+
logger.info({
|
|
142
|
+
origin: '@parcel/package-manager',
|
|
143
|
+
message: prefix(json.message),
|
|
144
|
+
});
|
|
145
|
+
} else if (json.name === 'pnpm:stats') {
|
|
146
|
+
addedCount += json.added ?? 0;
|
|
147
|
+
removedCount += json.removed ?? 0;
|
|
148
|
+
}
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
let stderr = [];
|
|
152
|
+
installProcess.stderr
|
|
153
|
+
.on('data', str => {
|
|
154
|
+
stderr.push(str.toString());
|
|
155
|
+
})
|
|
156
|
+
.on('error', e => {
|
|
157
|
+
logger.warn({
|
|
158
|
+
origin: '@parcel/package-manager',
|
|
159
|
+
message: e.message,
|
|
160
|
+
});
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
try {
|
|
164
|
+
await promiseFromProcess(installProcess);
|
|
165
|
+
|
|
166
|
+
if (addedCount > 0 || removedCount > 0) {
|
|
167
|
+
logger.log({
|
|
168
|
+
origin: '@parcel/package-manager',
|
|
169
|
+
message: `Added ${addedCount} ${
|
|
170
|
+
removedCount > 0 ? `and removed ${removedCount} ` : ''
|
|
171
|
+
}packages via pnpm`,
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// Since we succeeded, stderr might have useful information not included
|
|
176
|
+
// in the json written to stdout. It's also not necessary to log these as
|
|
177
|
+
// errors as they often aren't.
|
|
178
|
+
for (let message of stderr) {
|
|
179
|
+
logger.log({
|
|
180
|
+
origin: '@parcel/package-manager',
|
|
181
|
+
message,
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
} catch (e) {
|
|
185
|
+
throw new Error('pnpm failed to install modules');
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
function prefix(message: string): string {
|
|
191
|
+
return 'pnpm: ' + message;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
registerSerializableClass(`${pkg.version}:Pnpm`, Pnpm);
|
package/src/Yarn.js
ADDED
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
// @flow strict-local
|
|
2
|
+
|
|
3
|
+
import type {PackageInstaller, InstallerOptions} from './types';
|
|
4
|
+
|
|
5
|
+
import commandExists from 'command-exists';
|
|
6
|
+
import spawn from 'cross-spawn';
|
|
7
|
+
import logger from '@parcel/logger';
|
|
8
|
+
import split from 'split2';
|
|
9
|
+
import JSONParseStream from './JSONParseStream';
|
|
10
|
+
import promiseFromProcess from './promiseFromProcess';
|
|
11
|
+
import {registerSerializableClass} from '@parcel/core';
|
|
12
|
+
import {exec, npmSpecifierFromModuleRequest} from './utils';
|
|
13
|
+
|
|
14
|
+
// $FlowFixMe
|
|
15
|
+
import pkg from '../package.json';
|
|
16
|
+
|
|
17
|
+
const YARN_CMD = 'yarn';
|
|
18
|
+
|
|
19
|
+
type YarnStdOutMessage =
|
|
20
|
+
| {|
|
|
21
|
+
+type: 'step',
|
|
22
|
+
data: {|
|
|
23
|
+
message: string,
|
|
24
|
+
current: number,
|
|
25
|
+
total: number,
|
|
26
|
+
|},
|
|
27
|
+
|}
|
|
28
|
+
| {|+type: 'success', data: string|}
|
|
29
|
+
| {|+type: 'info', data: string|}
|
|
30
|
+
| {|+type: 'tree' | 'progressStart' | 'progressTick'|};
|
|
31
|
+
|
|
32
|
+
type YarnStdErrMessage = {|
|
|
33
|
+
+type: 'error' | 'warning',
|
|
34
|
+
data: string,
|
|
35
|
+
|};
|
|
36
|
+
|
|
37
|
+
let hasYarn: ?boolean;
|
|
38
|
+
let yarnVersion: ?number;
|
|
39
|
+
|
|
40
|
+
export class Yarn implements PackageInstaller {
|
|
41
|
+
static async exists(): Promise<boolean> {
|
|
42
|
+
if (hasYarn != null) {
|
|
43
|
+
return hasYarn;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
try {
|
|
47
|
+
hasYarn = Boolean(await commandExists('yarn'));
|
|
48
|
+
} catch (err) {
|
|
49
|
+
hasYarn = false;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return hasYarn;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
async install({
|
|
56
|
+
modules,
|
|
57
|
+
cwd,
|
|
58
|
+
saveDev = true,
|
|
59
|
+
}: InstallerOptions): Promise<void> {
|
|
60
|
+
if (yarnVersion == null) {
|
|
61
|
+
let version = await exec('yarn --version');
|
|
62
|
+
yarnVersion = parseInt(version.stdout, 10);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
let args = ['add', '--json'].concat(
|
|
66
|
+
modules.map(npmSpecifierFromModuleRequest),
|
|
67
|
+
);
|
|
68
|
+
|
|
69
|
+
if (saveDev) {
|
|
70
|
+
args.push('-D');
|
|
71
|
+
if (yarnVersion < 2) {
|
|
72
|
+
args.push('-W');
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// When Parcel is run by Yarn (e.g. via package.json scripts), several environment variables are
|
|
77
|
+
// added. When parcel in turn calls Yarn again, these can cause Yarn to behave stragely, so we
|
|
78
|
+
// filter them out when installing packages.
|
|
79
|
+
let env = {};
|
|
80
|
+
for (let key in process.env) {
|
|
81
|
+
if (
|
|
82
|
+
!key.startsWith('npm_') &&
|
|
83
|
+
key !== 'YARN_WRAP_OUTPUT' &&
|
|
84
|
+
key !== 'INIT_CWD' &&
|
|
85
|
+
key !== 'NODE_ENV'
|
|
86
|
+
) {
|
|
87
|
+
env[key] = process.env[key];
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
let installProcess = spawn(YARN_CMD, args, {cwd, env});
|
|
92
|
+
installProcess.stdout
|
|
93
|
+
// Invoking yarn with --json provides streaming, newline-delimited JSON output.
|
|
94
|
+
.pipe(split())
|
|
95
|
+
.pipe(new JSONParseStream())
|
|
96
|
+
.on('error', e => {
|
|
97
|
+
logger.error(e, '@parcel/package-manager');
|
|
98
|
+
})
|
|
99
|
+
.on('data', (message: YarnStdOutMessage) => {
|
|
100
|
+
switch (message.type) {
|
|
101
|
+
case 'step':
|
|
102
|
+
logger.progress(
|
|
103
|
+
prefix(
|
|
104
|
+
`[${message.data.current}/${message.data.total}] ${message.data.message}`,
|
|
105
|
+
),
|
|
106
|
+
);
|
|
107
|
+
return;
|
|
108
|
+
case 'success':
|
|
109
|
+
case 'info':
|
|
110
|
+
logger.info({
|
|
111
|
+
origin: '@parcel/package-manager',
|
|
112
|
+
message: prefix(message.data),
|
|
113
|
+
});
|
|
114
|
+
return;
|
|
115
|
+
default:
|
|
116
|
+
// ignore
|
|
117
|
+
}
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
installProcess.stderr
|
|
121
|
+
.pipe(split())
|
|
122
|
+
.pipe(new JSONParseStream())
|
|
123
|
+
.on('error', e => {
|
|
124
|
+
logger.error(e, '@parcel/package-manager');
|
|
125
|
+
})
|
|
126
|
+
.on('data', (message: YarnStdErrMessage) => {
|
|
127
|
+
switch (message.type) {
|
|
128
|
+
case 'warning':
|
|
129
|
+
logger.warn({
|
|
130
|
+
origin: '@parcel/package-manager',
|
|
131
|
+
message: prefix(message.data),
|
|
132
|
+
});
|
|
133
|
+
return;
|
|
134
|
+
case 'error':
|
|
135
|
+
logger.error({
|
|
136
|
+
origin: '@parcel/package-manager',
|
|
137
|
+
message: prefix(message.data),
|
|
138
|
+
});
|
|
139
|
+
return;
|
|
140
|
+
default:
|
|
141
|
+
// ignore
|
|
142
|
+
}
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
try {
|
|
146
|
+
return await promiseFromProcess(installProcess);
|
|
147
|
+
} catch (e) {
|
|
148
|
+
throw new Error('Yarn failed to install modules:' + e.message);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
function prefix(message: string): string {
|
|
154
|
+
return 'yarn: ' + message;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
registerSerializableClass(`${pkg.version}:Yarn`, Yarn);
|
package/src/index.js
ADDED
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
// @flow
|
|
2
|
+
|
|
3
|
+
import type {FilePath, PackageJSON} from '@parcel/types';
|
|
4
|
+
import type {
|
|
5
|
+
ModuleRequest,
|
|
6
|
+
PackageManager,
|
|
7
|
+
PackageInstaller,
|
|
8
|
+
InstallOptions,
|
|
9
|
+
} from './types';
|
|
10
|
+
import type {FileSystem} from '@parcel/fs';
|
|
11
|
+
|
|
12
|
+
import invariant from 'assert';
|
|
13
|
+
import path from 'path';
|
|
14
|
+
import nullthrows from 'nullthrows';
|
|
15
|
+
import semver from 'semver';
|
|
16
|
+
import ThrowableDiagnostic, {
|
|
17
|
+
generateJSONCodeHighlights,
|
|
18
|
+
encodeJSONKeyComponent,
|
|
19
|
+
md,
|
|
20
|
+
} from '@parcel/diagnostic';
|
|
21
|
+
import logger from '@parcel/logger';
|
|
22
|
+
import {loadConfig, PromiseQueue, resolveConfig} from '@parcel/utils';
|
|
23
|
+
import WorkerFarm from '@parcel/workers';
|
|
24
|
+
|
|
25
|
+
import {Npm} from './Npm';
|
|
26
|
+
import {Yarn} from './Yarn';
|
|
27
|
+
import {Pnpm} from './Pnpm.js';
|
|
28
|
+
import {getConflictingLocalDependencies} from './utils';
|
|
29
|
+
import validateModuleSpecifier from './validateModuleSpecifier';
|
|
30
|
+
|
|
31
|
+
async function install(
|
|
32
|
+
fs: FileSystem,
|
|
33
|
+
packageManager: PackageManager,
|
|
34
|
+
modules: Array<ModuleRequest>,
|
|
35
|
+
from: FilePath,
|
|
36
|
+
projectRoot: FilePath,
|
|
37
|
+
options: InstallOptions = {},
|
|
38
|
+
): Promise<void> {
|
|
39
|
+
let {installPeers = true, saveDev = true, packageInstaller} = options;
|
|
40
|
+
let moduleNames = modules.map(m => m.name).join(', ');
|
|
41
|
+
|
|
42
|
+
logger.progress(`Installing ${moduleNames}...`);
|
|
43
|
+
|
|
44
|
+
let fromPkgPath = await resolveConfig(
|
|
45
|
+
fs,
|
|
46
|
+
from,
|
|
47
|
+
['package.json'],
|
|
48
|
+
projectRoot,
|
|
49
|
+
);
|
|
50
|
+
let cwd = fromPkgPath ? path.dirname(fromPkgPath) : fs.cwd();
|
|
51
|
+
|
|
52
|
+
if (!packageInstaller) {
|
|
53
|
+
packageInstaller = await determinePackageInstaller(fs, from, projectRoot);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
try {
|
|
57
|
+
await packageInstaller.install({
|
|
58
|
+
modules,
|
|
59
|
+
saveDev,
|
|
60
|
+
cwd,
|
|
61
|
+
packagePath: fromPkgPath,
|
|
62
|
+
fs,
|
|
63
|
+
});
|
|
64
|
+
} catch (err) {
|
|
65
|
+
throw new Error(`Failed to install ${moduleNames}: ${err.message}`);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
if (installPeers) {
|
|
69
|
+
await Promise.all(
|
|
70
|
+
modules.map(m =>
|
|
71
|
+
installPeerDependencies(
|
|
72
|
+
fs,
|
|
73
|
+
packageManager,
|
|
74
|
+
m,
|
|
75
|
+
from,
|
|
76
|
+
projectRoot,
|
|
77
|
+
options,
|
|
78
|
+
),
|
|
79
|
+
),
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
async function installPeerDependencies(
|
|
85
|
+
fs: FileSystem,
|
|
86
|
+
packageManager: PackageManager,
|
|
87
|
+
module: ModuleRequest,
|
|
88
|
+
from: FilePath,
|
|
89
|
+
projectRoot: FilePath,
|
|
90
|
+
options,
|
|
91
|
+
) {
|
|
92
|
+
const {resolved} = await packageManager.resolve(module.name, from);
|
|
93
|
+
const modulePkg: PackageJSON = nullthrows(
|
|
94
|
+
await loadConfig(fs, resolved, ['package.json'], projectRoot),
|
|
95
|
+
).config;
|
|
96
|
+
const peers = modulePkg.peerDependencies || {};
|
|
97
|
+
|
|
98
|
+
let modules: Array<ModuleRequest> = [];
|
|
99
|
+
for (let [name, range] of Object.entries(peers)) {
|
|
100
|
+
invariant(typeof range === 'string');
|
|
101
|
+
|
|
102
|
+
let conflicts = await getConflictingLocalDependencies(
|
|
103
|
+
fs,
|
|
104
|
+
name,
|
|
105
|
+
from,
|
|
106
|
+
projectRoot,
|
|
107
|
+
);
|
|
108
|
+
if (conflicts) {
|
|
109
|
+
let {pkg} = await packageManager.resolve(name, from);
|
|
110
|
+
invariant(pkg);
|
|
111
|
+
if (!semver.satisfies(pkg.version, range)) {
|
|
112
|
+
throw new ThrowableDiagnostic({
|
|
113
|
+
diagnostic: {
|
|
114
|
+
message: md`Could not install the peer dependency "${name}" for "${module.name}", installed version ${pkg.version} is incompatible with ${range}`,
|
|
115
|
+
origin: '@parcel/package-manager',
|
|
116
|
+
codeFrames: [
|
|
117
|
+
{
|
|
118
|
+
filePath: conflicts.filePath,
|
|
119
|
+
language: 'json',
|
|
120
|
+
code: conflicts.json,
|
|
121
|
+
codeHighlights: generateJSONCodeHighlights(
|
|
122
|
+
conflicts.json,
|
|
123
|
+
conflicts.fields.map(field => ({
|
|
124
|
+
key: `/${field}/${encodeJSONKeyComponent(name)}`,
|
|
125
|
+
type: 'key',
|
|
126
|
+
message: 'Found this conflicting local requirement.',
|
|
127
|
+
})),
|
|
128
|
+
),
|
|
129
|
+
},
|
|
130
|
+
],
|
|
131
|
+
},
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
continue;
|
|
136
|
+
}
|
|
137
|
+
modules.push({name, range});
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
if (modules.length) {
|
|
141
|
+
await install(
|
|
142
|
+
fs,
|
|
143
|
+
packageManager,
|
|
144
|
+
modules,
|
|
145
|
+
from,
|
|
146
|
+
projectRoot,
|
|
147
|
+
Object.assign({}, options, {installPeers: false}),
|
|
148
|
+
);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
async function determinePackageInstaller(
|
|
153
|
+
fs: FileSystem,
|
|
154
|
+
filepath: FilePath,
|
|
155
|
+
projectRoot: FilePath,
|
|
156
|
+
): Promise<PackageInstaller> {
|
|
157
|
+
let configFile = await resolveConfig(
|
|
158
|
+
fs,
|
|
159
|
+
filepath,
|
|
160
|
+
['package-lock.json', 'pnpm-lock.yaml', 'yarn.lock'],
|
|
161
|
+
projectRoot,
|
|
162
|
+
);
|
|
163
|
+
|
|
164
|
+
let configName = configFile && path.basename(configFile);
|
|
165
|
+
|
|
166
|
+
// Always use the package manager that seems to be used in the project,
|
|
167
|
+
// falling back to a different one wouldn't update the existing lockfile.
|
|
168
|
+
if (configName === 'package-lock.json') {
|
|
169
|
+
return new Npm();
|
|
170
|
+
} else if (configName === 'pnpm-lock.yaml') {
|
|
171
|
+
return new Pnpm();
|
|
172
|
+
} else if (configName === 'yarn.lock') {
|
|
173
|
+
return new Yarn();
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
if (await Yarn.exists()) {
|
|
177
|
+
return new Yarn();
|
|
178
|
+
} else if (await Pnpm.exists()) {
|
|
179
|
+
return new Pnpm();
|
|
180
|
+
} else {
|
|
181
|
+
return new Npm();
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
let queue = new PromiseQueue({maxConcurrent: 1});
|
|
186
|
+
let modulesInstalling: Set<string> = new Set();
|
|
187
|
+
|
|
188
|
+
// Exported so that it may be invoked from the worker api below.
|
|
189
|
+
// Do not call this directly! This can result in concurrent package installations
|
|
190
|
+
// across multiple instances of the package manager.
|
|
191
|
+
export function _addToInstallQueue(
|
|
192
|
+
fs: FileSystem,
|
|
193
|
+
packageManager: PackageManager,
|
|
194
|
+
modules: Array<ModuleRequest>,
|
|
195
|
+
filePath: FilePath,
|
|
196
|
+
projectRoot: FilePath,
|
|
197
|
+
options?: InstallOptions,
|
|
198
|
+
): Promise<mixed> {
|
|
199
|
+
modules = modules.map(request => ({
|
|
200
|
+
name: validateModuleSpecifier(request.name),
|
|
201
|
+
range: request.range,
|
|
202
|
+
}));
|
|
203
|
+
|
|
204
|
+
// Wrap PromiseQueue and track modules that are currently installing.
|
|
205
|
+
// If a request comes in for a module that is currently installing, don't bother
|
|
206
|
+
// enqueuing it.
|
|
207
|
+
let modulesToInstall = modules.filter(
|
|
208
|
+
m => !modulesInstalling.has(getModuleRequestKey(m)),
|
|
209
|
+
);
|
|
210
|
+
if (modulesToInstall.length) {
|
|
211
|
+
for (let m of modulesToInstall) {
|
|
212
|
+
modulesInstalling.add(getModuleRequestKey(m));
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
queue
|
|
216
|
+
.add(() =>
|
|
217
|
+
install(
|
|
218
|
+
fs,
|
|
219
|
+
packageManager,
|
|
220
|
+
modulesToInstall,
|
|
221
|
+
filePath,
|
|
222
|
+
projectRoot,
|
|
223
|
+
options,
|
|
224
|
+
).then(() => {
|
|
225
|
+
for (let m of modulesToInstall) {
|
|
226
|
+
modulesInstalling.delete(getModuleRequestKey(m));
|
|
227
|
+
}
|
|
228
|
+
}),
|
|
229
|
+
)
|
|
230
|
+
.then(
|
|
231
|
+
() => {},
|
|
232
|
+
() => {},
|
|
233
|
+
);
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
return queue.run();
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
export function installPackage(
|
|
240
|
+
fs: FileSystem,
|
|
241
|
+
packageManager: PackageManager,
|
|
242
|
+
modules: Array<ModuleRequest>,
|
|
243
|
+
filePath: FilePath,
|
|
244
|
+
projectRoot: FilePath,
|
|
245
|
+
options?: InstallOptions,
|
|
246
|
+
): Promise<mixed> {
|
|
247
|
+
if (WorkerFarm.isWorker()) {
|
|
248
|
+
let workerApi = WorkerFarm.getWorkerApi();
|
|
249
|
+
// TODO this should really be `__filename` but without the rewriting.
|
|
250
|
+
let bundlePath =
|
|
251
|
+
process.env.PARCEL_BUILD_ENV === 'production' &&
|
|
252
|
+
!process.env.PARCEL_SELF_BUILD
|
|
253
|
+
? path.join(__dirname, '..', 'lib/index.js')
|
|
254
|
+
: __filename;
|
|
255
|
+
return workerApi.callMaster({
|
|
256
|
+
location: bundlePath,
|
|
257
|
+
args: [fs, packageManager, modules, filePath, projectRoot, options],
|
|
258
|
+
method: '_addToInstallQueue',
|
|
259
|
+
});
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
return _addToInstallQueue(
|
|
263
|
+
fs,
|
|
264
|
+
packageManager,
|
|
265
|
+
modules,
|
|
266
|
+
filePath,
|
|
267
|
+
projectRoot,
|
|
268
|
+
options,
|
|
269
|
+
);
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
function getModuleRequestKey(moduleRequest: ModuleRequest): string {
|
|
273
|
+
return [moduleRequest.name, moduleRequest.range].join('@');
|
|
274
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
// @flow strict-local
|
|
2
|
+
|
|
3
|
+
import type {ChildProcess} from 'child_process';
|
|
4
|
+
|
|
5
|
+
export default function promiseFromProcess(
|
|
6
|
+
childProcess: ChildProcess,
|
|
7
|
+
): Promise<void> {
|
|
8
|
+
return new Promise((resolve, reject) => {
|
|
9
|
+
childProcess.on('error', reject);
|
|
10
|
+
childProcess.on('close', code => {
|
|
11
|
+
if (code !== 0) {
|
|
12
|
+
reject(new Error('Child process failed'));
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
resolve();
|
|
17
|
+
});
|
|
18
|
+
});
|
|
19
|
+
}
|
package/src/types.js
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
// @flow
|
|
2
|
+
|
|
3
|
+
import type {
|
|
4
|
+
FilePath,
|
|
5
|
+
FileCreateInvalidation,
|
|
6
|
+
SemverRange,
|
|
7
|
+
DependencySpecifier,
|
|
8
|
+
PackageJSON,
|
|
9
|
+
} from '@parcel/types';
|
|
10
|
+
import type {FileSystem} from '@parcel/fs';
|
|
11
|
+
|
|
12
|
+
export type ResolveResult = {|
|
|
13
|
+
resolved: FilePath | DependencySpecifier,
|
|
14
|
+
pkg?: ?PackageJSON,
|
|
15
|
+
invalidateOnFileCreate: Array<FileCreateInvalidation>,
|
|
16
|
+
invalidateOnFileChange: Set<FilePath>,
|
|
17
|
+
type: number,
|
|
18
|
+
|};
|
|
19
|
+
|
|
20
|
+
export type InstallOptions = {
|
|
21
|
+
installPeers?: boolean,
|
|
22
|
+
saveDev?: boolean,
|
|
23
|
+
packageInstaller?: ?PackageInstaller,
|
|
24
|
+
...
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
export type InstallerOptions = {|
|
|
28
|
+
modules: Array<ModuleRequest>,
|
|
29
|
+
fs: FileSystem,
|
|
30
|
+
cwd: FilePath,
|
|
31
|
+
packagePath?: ?FilePath,
|
|
32
|
+
saveDev?: boolean,
|
|
33
|
+
|};
|
|
34
|
+
|
|
35
|
+
export interface PackageInstaller {
|
|
36
|
+
install(opts: InstallerOptions): Promise<void>;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export type Invalidations = {|
|
|
40
|
+
invalidateOnFileCreate: Array<FileCreateInvalidation>,
|
|
41
|
+
invalidateOnFileChange: Set<FilePath>,
|
|
42
|
+
invalidateOnStartup: boolean,
|
|
43
|
+
|};
|
|
44
|
+
|
|
45
|
+
export interface PackageManager {
|
|
46
|
+
require(
|
|
47
|
+
id: DependencySpecifier,
|
|
48
|
+
from: FilePath,
|
|
49
|
+
?{|range?: ?SemverRange, shouldAutoInstall?: boolean, saveDev?: boolean|},
|
|
50
|
+
): Promise<any>;
|
|
51
|
+
resolve(
|
|
52
|
+
id: DependencySpecifier,
|
|
53
|
+
from: FilePath,
|
|
54
|
+
?{|range?: ?SemverRange, shouldAutoInstall?: boolean, saveDev?: boolean|},
|
|
55
|
+
): Promise<ResolveResult>;
|
|
56
|
+
getInvalidations(id: DependencySpecifier, from: FilePath): Invalidations;
|
|
57
|
+
invalidate(id: DependencySpecifier, from: FilePath): void;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export type ModuleRequest = {|
|
|
61
|
+
+name: string,
|
|
62
|
+
+range: ?SemverRange,
|
|
63
|
+
|};
|