forked from yarnpkg/yarn
-
Notifications
You must be signed in to change notification settings - Fork 0
/
add.js
180 lines (153 loc) 路 5.2 KB
/
add.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
/* @flow */
import type {Reporter} from '../../reporters/index.js';
import type {InstallCwdRequest, InstallPrepared} from './install.js';
import type {DependencyRequestPatterns} from '../../types.js';
import type Config from '../../config.js';
import Lockfile from '../../lockfile/wrapper.js';
import * as PackageReference from '../../package-reference.js';
import PackageRequest from '../../package-request.js';
import {buildTree} from './ls.js';
import {Install, _setFlags} from './install.js';
import {MessageError} from '../../errors.js';
const invariant = require('invariant');
export class Add extends Install {
constructor(
args: Array<string>,
flags: Object,
config: Config,
reporter: Reporter,
lockfile: Lockfile,
) {
super(flags, config, reporter, lockfile);
this.args = args;
this.flags.writeLockfile = true;
}
args: Array<string>;
/**
* TODO
*/
prepare(patterns: Array<string>, requests: DependencyRequestPatterns): Promise<InstallPrepared> {
const requestsWithArgs = requests.slice();
for (const pattern of this.args) {
requestsWithArgs.push({
pattern,
registry: 'npm',
visibility: PackageReference.USED,
optional: false,
});
}
return Promise.resolve({
patterns: patterns.concat(this.args),
requests: requestsWithArgs,
skip: false,
});
}
/**
* Description
*/
async init(): Promise<Array<string>> {
const patterns = await Install.prototype.init.call(this);
await this.maybeOutputSaveTree(patterns);
await this.savePackages();
return patterns;
}
/**
* Description
*/
fetchRequestFromCwd(): Promise<InstallCwdRequest> {
return Install.prototype.fetchRequestFromCwd.call(this, this.args);
}
/**
* Output a tree of any newly added dependencies.
*/
async maybeOutputSaveTree(patterns: Array<string>): Promise<void> {
const {trees, count} = await buildTree(this.resolver, this.linker, patterns, true, true);
this.reporter.success(
count === 1 ?
this.reporter.lang('savedNewDependency')
:
this.reporter.lang('savedNewDependencies', count),
);
this.reporter.tree('newDependencies', trees);
}
/**
* Save added packages to manifest if any of the --save flags were used.
*/
async savePackages(): Promise<void> {
const {dev, exact, tilde, optional, peer} = this.flags;
// get all the different registry manifests in this folder
const manifests = await this.config.getRootManifests();
// add new patterns to their appropriate registry manifest
for (const pattern of this.resolver.dedupePatterns(this.args)) {
const pkg = this.resolver.getResolvedPattern(pattern);
invariant(pkg, `missing package ${pattern}`);
const ref = pkg._reference;
invariant(ref, 'expected package reference');
const parts = PackageRequest.normalizePattern(pattern);
let version;
if (parts.hasVersion && parts.range) {
// if the user specified a range then use it verbatim
version = parts.range;
} else if (PackageRequest.getExoticResolver(pattern)) {
// wasn't a name/range tuple so this is just a raw exotic pattern
version = pattern;
} else if (tilde) { // --save-tilde
version = `~${pkg.version}`;
} else if (exact) { // --save-exact
version = pkg.version;
} else { // default to save prefix
version = `${String(this.config.getOption('save-prefix'))}${pkg.version}`;
}
// build up list of objects to put ourselves into from the cli args
const targetKeys: Array<string> = [];
if (dev) {
targetKeys.push('devDependencies');
}
if (peer) {
targetKeys.push('peerDependencies');
}
if (optional) {
targetKeys.push('optionalDependencies');
}
if (!targetKeys.length) {
targetKeys.push('dependencies');
}
// add it to manifest
const object = manifests[ref.registry].object;
for (const key of targetKeys) {
const target = object[key] = object[key] || {};
target[pkg.name] = version;
}
// add pattern so it's aliased in the lockfile
const newPattern = `${pkg.name}@${version}`;
if (newPattern === pattern) {
continue;
}
this.resolver.addPattern(newPattern, pkg);
this.resolver.removePattern(pattern);
}
await this.config.saveRootManifests(manifests);
}
}
export function setFlags(commander: Object) {
commander.usage('add [packages ...] [flags]');
_setFlags(commander);
commander.option('--dev', 'save package to your `devDependencies`');
commander.option('--peer', 'save package to your `peerDependencies`');
commander.option('--optional', 'save package to your `optionalDependencies`');
commander.option('--exact', '');
commander.option('--tilde', '');
}
export async function run(
config: Config,
reporter: Reporter,
flags: Object,
args: Array<string>,
): Promise<void> {
if (!args.length) {
throw new MessageError(reporter.lang('missingAddDependencies'));
}
const lockfile = await Lockfile.fromDirectory(config.cwd, reporter);
const install = new Add(args, flags, config, reporter, lockfile);
await install.init();
}