@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.
Files changed (36) hide show
  1. package/LICENSE +21 -0
  2. package/index.d.ts +40 -0
  3. package/lib/index.d.ts +10 -0
  4. package/lib/index.js +1968 -0
  5. package/lib/index.js.map +1 -0
  6. package/package.json +73 -0
  7. package/src/JSONParseStream.js +39 -0
  8. package/src/MockPackageInstaller.js +94 -0
  9. package/src/NodePackageManager.js +662 -0
  10. package/src/Npm.js +94 -0
  11. package/src/Pnpm.js +194 -0
  12. package/src/Yarn.js +157 -0
  13. package/src/getCurrentPackageManager.js +17 -0
  14. package/src/index.js +19 -0
  15. package/src/installPackage.js +284 -0
  16. package/src/promiseFromProcess.js +19 -0
  17. package/src/utils.js +93 -0
  18. package/src/validateModuleSpecifier.js +12 -0
  19. package/test/NodePackageManager.test.js +387 -0
  20. package/test/fixtures/empty/package.json +1 -0
  21. package/test/fixtures/has-a-not-yet-installed/package.json +5 -0
  22. package/test/fixtures/has-foo/node_modules/foo/index.js +1 -0
  23. package/test/fixtures/has-foo/node_modules/foo/package.json +3 -0
  24. package/test/fixtures/has-foo/node_modules/index.js +0 -0
  25. package/test/fixtures/has-foo/package.json +5 -0
  26. package/test/fixtures/has-foo/subpackage/package.json +1 -0
  27. package/test/fixtures/packages/a/index.js +1 -0
  28. package/test/fixtures/packages/a/package.json +3 -0
  29. package/test/fixtures/packages/foo-2.0/index.js +1 -0
  30. package/test/fixtures/packages/foo-2.0/package.json +4 -0
  31. package/test/fixtures/packages/peers/index.js +1 -0
  32. package/test/fixtures/packages/peers/package.json +7 -0
  33. package/test/fixtures/packages/peers-2.0/index.js +1 -0
  34. package/test/fixtures/packages/peers-2.0/package.json +7 -0
  35. package/test/getCurrentPackageManager.test.js +28 -0
  36. package/test/validateModuleSpecifiers.test.js +38 -0
package/src/Npm.js ADDED
@@ -0,0 +1,94 @@
1
+ // @flow strict-local
2
+
3
+ import type {PackageInstaller, InstallerOptions} from '@parcel/types';
4
+
5
+ import path from 'path';
6
+ import spawn from 'cross-spawn';
7
+ import logger from '@parcel/logger';
8
+ import promiseFromProcess from './promiseFromProcess';
9
+ import {registerSerializableClass} from '@parcel/core';
10
+ import {npmSpecifierFromModuleRequest} from './utils';
11
+
12
+ // $FlowFixMe
13
+ import pkg from '../package.json';
14
+
15
+ const NPM_CMD = 'npm';
16
+
17
+ export class Npm implements PackageInstaller {
18
+ async install({
19
+ modules,
20
+ cwd,
21
+ fs,
22
+ packagePath,
23
+ saveDev = true,
24
+ }: InstallerOptions): Promise<void> {
25
+ // npm doesn't auto-create a package.json when installing,
26
+ // so create an empty one if needed.
27
+ if (packagePath == null) {
28
+ await fs.writeFile(path.join(cwd, 'package.json'), '{}');
29
+ }
30
+
31
+ let args = ['install', '--json', saveDev ? '--save-dev' : '--save'].concat(
32
+ modules.map(npmSpecifierFromModuleRequest),
33
+ );
34
+
35
+ // When Parcel is run by npm (e.g. via package.json scripts), several environment variables are
36
+ // added. When parcel in turn calls npm again, these can cause npm to behave stragely, so we
37
+ // filter them out when installing packages.
38
+ let env = {};
39
+ for (let key in process.env) {
40
+ if (!key.startsWith('npm_') && key !== 'INIT_CWD' && key !== 'NODE_ENV') {
41
+ env[key] = process.env[key];
42
+ }
43
+ }
44
+
45
+ let installProcess = spawn(NPM_CMD, args, {cwd, env});
46
+ let stdout = '';
47
+ installProcess.stdout.on('data', (buf: Buffer) => {
48
+ stdout += buf.toString();
49
+ });
50
+
51
+ let stderr = [];
52
+ installProcess.stderr.on('data', (buf: Buffer) => {
53
+ stderr.push(buf.toString().trim());
54
+ });
55
+
56
+ try {
57
+ await promiseFromProcess(installProcess);
58
+
59
+ let results: NPMResults = JSON.parse(stdout);
60
+ let addedCount = results.added.length;
61
+ if (addedCount > 0) {
62
+ logger.log({
63
+ origin: '@parcel/package-manager',
64
+ message: `Added ${addedCount} packages via npm`,
65
+ });
66
+ }
67
+
68
+ // Since we succeeded, stderr might have useful information not included
69
+ // in the json written to stdout. It's also not necessary to log these as
70
+ // errors as they often aren't.
71
+ for (let message of stderr) {
72
+ if (message.length > 0) {
73
+ logger.log({
74
+ origin: '@parcel/package-manager',
75
+ message,
76
+ });
77
+ }
78
+ }
79
+ } catch (e) {
80
+ throw new Error(
81
+ 'npm failed to install modules: ' +
82
+ e.message +
83
+ ' - ' +
84
+ stderr.join('\n'),
85
+ );
86
+ }
87
+ }
88
+ }
89
+
90
+ type NPMResults = {|
91
+ added: Array<{name: string, ...}>,
92
+ |};
93
+
94
+ registerSerializableClass(`${pkg.version}:Npm`, Npm);
package/src/Pnpm.js ADDED
@@ -0,0 +1,194 @@
1
+ // @flow strict-local
2
+
3
+ import type {PackageInstaller, InstallerOptions} from '@parcel/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 '@parcel/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);
@@ -0,0 +1,17 @@
1
+ // @flow
2
+
3
+ export default function getCurrentPackageManager(
4
+ userAgent: ?string = process.env.npm_config_user_agent,
5
+ ): ?{|name: string, version: string|} {
6
+ if (!userAgent) {
7
+ return undefined;
8
+ }
9
+
10
+ const pmSpec = userAgent.split(' ')[0];
11
+ const separatorPos = pmSpec.lastIndexOf('/');
12
+ const name = pmSpec.substring(0, separatorPos);
13
+ return {
14
+ name: name,
15
+ version: pmSpec.substring(separatorPos + 1),
16
+ };
17
+ }
package/src/index.js ADDED
@@ -0,0 +1,19 @@
1
+ // @flow
2
+
3
+ import type {PackageManagerResolveResult} from '@parcel/types';
4
+
5
+ export type {
6
+ PackageManager,
7
+ Invalidations,
8
+ PackageInstaller,
9
+ ModuleRequest,
10
+ } from '@parcel/types';
11
+ export * from './Npm';
12
+ export * from './Pnpm';
13
+ export * from './Yarn';
14
+ export * from './MockPackageInstaller';
15
+ export * from './NodePackageManager';
16
+ export {_addToInstallQueue} from './installPackage';
17
+
18
+ export type {PackageManagerResolveResult};
19
+ export type {PackageManagerResolveResult as ResolveResult};