1 | "use strict";
|
2 | Object.defineProperty(exports, "__esModule", { value: true });
|
3 |
|
4 |
|
5 |
|
6 |
|
7 |
|
8 |
|
9 |
|
10 | const core_1 = require("@angular-devkit/core");
|
11 | const tools_1 = require("@angular-devkit/schematics/tools");
|
12 | const path_1 = require("path");
|
13 | const semver_1 = require("semver");
|
14 | const schema_1 = require("../lib/config/schema");
|
15 | const analytics_1 = require("../models/analytics");
|
16 | const schematic_command_1 = require("../models/schematic-command");
|
17 | const install_package_1 = require("../tasks/install-package");
|
18 | const color_1 = require("../utilities/color");
|
19 | const package_manager_1 = require("../utilities/package-manager");
|
20 | const package_metadata_1 = require("../utilities/package-metadata");
|
21 | const npa = require('npm-package-arg');
|
22 | class AddCommand extends schematic_command_1.SchematicCommand {
|
23 | constructor() {
|
24 | super(...arguments);
|
25 | this.allowPrivateSchematics = true;
|
26 | }
|
27 | async initialize(options) {
|
28 | if (options.registry) {
|
29 | return super.initialize({ ...options, packageRegistry: options.registry });
|
30 | }
|
31 | else {
|
32 | return super.initialize(options);
|
33 | }
|
34 | }
|
35 | async run(options) {
|
36 | if (!options.collection) {
|
37 | this.logger.fatal(`The "ng add" command requires a name argument to be specified eg. ` +
|
38 | `${color_1.colors.yellow('ng add [name] ')}. For more details, use "ng help".`);
|
39 | return 1;
|
40 | }
|
41 | let packageIdentifier;
|
42 | try {
|
43 | packageIdentifier = npa(options.collection);
|
44 | }
|
45 | catch (e) {
|
46 | this.logger.error(e.message);
|
47 | return 1;
|
48 | }
|
49 | if (packageIdentifier.registry && this.isPackageInstalled(packageIdentifier.name)) {
|
50 | let validVersion = false;
|
51 | const installedVersion = await this.findProjectVersion(packageIdentifier.name);
|
52 | if (installedVersion) {
|
53 | if (packageIdentifier.type === 'range') {
|
54 | validVersion = semver_1.satisfies(installedVersion, packageIdentifier.fetchSpec);
|
55 | }
|
56 | else if (packageIdentifier.type === 'version') {
|
57 | const v1 = semver_1.valid(packageIdentifier.fetchSpec);
|
58 | const v2 = semver_1.valid(installedVersion);
|
59 | validVersion = v1 !== null && v1 === v2;
|
60 | }
|
61 | else if (!packageIdentifier.rawSpec) {
|
62 | validVersion = true;
|
63 | }
|
64 | }
|
65 | if (validVersion) {
|
66 |
|
67 | this.logger.info('Skipping installation: Package already installed');
|
68 | return this.executeSchematic(packageIdentifier.name, options['--']);
|
69 | }
|
70 | }
|
71 | const packageManager = await package_manager_1.getPackageManager(this.workspace.root);
|
72 | const usingYarn = packageManager === schema_1.PackageManager.Yarn;
|
73 | if (packageIdentifier.type === 'tag' && !packageIdentifier.rawSpec) {
|
74 |
|
75 |
|
76 | let packageMetadata;
|
77 | try {
|
78 | packageMetadata = await package_metadata_1.fetchPackageMetadata(packageIdentifier.name, this.logger, {
|
79 | registry: options.registry,
|
80 | usingYarn,
|
81 | verbose: options.verbose,
|
82 | });
|
83 | }
|
84 | catch (e) {
|
85 | this.logger.error('Unable to fetch package metadata: ' + e.message);
|
86 | return 1;
|
87 | }
|
88 | const latestManifest = packageMetadata.tags['latest'];
|
89 | if (latestManifest && Object.keys(latestManifest.peerDependencies).length === 0) {
|
90 | if (latestManifest.name === '@angular/pwa') {
|
91 | const version = await this.findProjectVersion('@angular/cli');
|
92 |
|
93 | const semverOptions = { includePrerelease: true };
|
94 | if (version &&
|
95 | ((semver_1.validRange(version) && semver_1.intersects(version, '7', semverOptions)) ||
|
96 | (semver_1.valid(version) && semver_1.satisfies(version, '7', semverOptions)))) {
|
97 | packageIdentifier = npa.resolve('@angular/pwa', '0.12');
|
98 | }
|
99 | }
|
100 | }
|
101 | else if (!latestManifest || (await this.hasMismatchedPeer(latestManifest))) {
|
102 |
|
103 | const versionManifests = Object.values(packageMetadata.versions).filter((value) => !semver_1.prerelease(value.version));
|
104 | versionManifests.sort((a, b) => semver_1.rcompare(a.version, b.version, true));
|
105 | let newIdentifier;
|
106 | for (const versionManifest of versionManifests) {
|
107 | if (!(await this.hasMismatchedPeer(versionManifest))) {
|
108 | newIdentifier = npa.resolve(packageIdentifier.name, versionManifest.version);
|
109 | break;
|
110 | }
|
111 | }
|
112 | if (!newIdentifier) {
|
113 | this.logger.warn("Unable to find compatible package. Using 'latest'.");
|
114 | }
|
115 | else {
|
116 | packageIdentifier = newIdentifier;
|
117 | }
|
118 | }
|
119 | }
|
120 | let collectionName = packageIdentifier.name;
|
121 | let savePackage;
|
122 | try {
|
123 | const manifest = await package_metadata_1.fetchPackageManifest(packageIdentifier, this.logger, {
|
124 | registry: options.registry,
|
125 | verbose: options.verbose,
|
126 | usingYarn,
|
127 | });
|
128 | savePackage = manifest['ng-add'] && manifest['ng-add'].save;
|
129 | collectionName = manifest.name;
|
130 | if (await this.hasMismatchedPeer(manifest)) {
|
131 | this.logger.warn('Package has unmet peer dependencies. Adding the package may not succeed.');
|
132 | }
|
133 | }
|
134 | catch (e) {
|
135 | this.logger.error('Unable to fetch package manifest: ' + e.message);
|
136 | return 1;
|
137 | }
|
138 | if (savePackage === false) {
|
139 |
|
140 |
|
141 | const tempPath = install_package_1.installTempPackage(packageIdentifier.raw, this.logger, packageManager, options.registry ? [`--registry="${options.registry}"`] : undefined);
|
142 | const resolvedCollectionPath = require.resolve(path_1.join(collectionName, 'package.json'), {
|
143 | paths: [tempPath],
|
144 | });
|
145 | collectionName = path_1.dirname(resolvedCollectionPath);
|
146 | }
|
147 | else {
|
148 | install_package_1.installPackage(packageIdentifier.raw, this.logger, packageManager, savePackage, options.registry ? [`--registry="${options.registry}"`] : undefined);
|
149 | }
|
150 | return this.executeSchematic(collectionName, options['--']);
|
151 | }
|
152 | async reportAnalytics(paths, options, dimensions = [], metrics = []) {
|
153 | const collection = options.collection;
|
154 |
|
155 | if (collection && analytics_1.isPackageNameSafeForAnalytics(collection)) {
|
156 | dimensions[core_1.analytics.NgCliAnalyticsDimensions.NgAddCollection] = collection;
|
157 | }
|
158 | else {
|
159 | delete dimensions[core_1.analytics.NgCliAnalyticsDimensions.NgAddCollection];
|
160 | }
|
161 | return super.reportAnalytics(paths, options, dimensions, metrics);
|
162 | }
|
163 | isPackageInstalled(name) {
|
164 | try {
|
165 | require.resolve(path_1.join(name, 'package.json'), { paths: [this.workspace.root] });
|
166 | return true;
|
167 | }
|
168 | catch (e) {
|
169 | if (e.code !== 'MODULE_NOT_FOUND') {
|
170 | throw e;
|
171 | }
|
172 | }
|
173 | return false;
|
174 | }
|
175 | async executeSchematic(collectionName, options = []) {
|
176 | const runOptions = {
|
177 | schematicOptions: options,
|
178 | collectionName,
|
179 | schematicName: 'ng-add',
|
180 | dryRun: false,
|
181 | force: false,
|
182 | };
|
183 | try {
|
184 | return await this.runSchematic(runOptions);
|
185 | }
|
186 | catch (e) {
|
187 | if (e instanceof tools_1.NodePackageDoesNotSupportSchematics) {
|
188 | this.logger.error(core_1.tags.oneLine `
|
189 | The package that you are trying to add does not support schematics. You can try using
|
190 | a different version of the package or contact the package author to add ng-add support.
|
191 | `);
|
192 | return 1;
|
193 | }
|
194 | throw e;
|
195 | }
|
196 | }
|
197 | async findProjectVersion(name) {
|
198 | let installedPackage;
|
199 | try {
|
200 | installedPackage = require.resolve(path_1.join(name, 'package.json'), {
|
201 | paths: [this.workspace.root],
|
202 | });
|
203 | }
|
204 | catch (_a) { }
|
205 | if (installedPackage) {
|
206 | try {
|
207 | const installed = await package_metadata_1.fetchPackageManifest(path_1.dirname(installedPackage), this.logger);
|
208 | return installed.version;
|
209 | }
|
210 | catch (_b) { }
|
211 | }
|
212 | let projectManifest;
|
213 | try {
|
214 | projectManifest = await package_metadata_1.fetchPackageManifest(this.workspace.root, this.logger);
|
215 | }
|
216 | catch (_c) { }
|
217 | if (projectManifest) {
|
218 | const version = projectManifest.dependencies[name] || projectManifest.devDependencies[name];
|
219 | if (version) {
|
220 | return version;
|
221 | }
|
222 | }
|
223 | return null;
|
224 | }
|
225 | async hasMismatchedPeer(manifest) {
|
226 | for (const peer in manifest.peerDependencies) {
|
227 | let peerIdentifier;
|
228 | try {
|
229 | peerIdentifier = npa.resolve(peer, manifest.peerDependencies[peer]);
|
230 | }
|
231 | catch (_a) {
|
232 | this.logger.warn(`Invalid peer dependency ${peer} found in package.`);
|
233 | continue;
|
234 | }
|
235 | if (peerIdentifier.type === 'version' || peerIdentifier.type === 'range') {
|
236 | try {
|
237 | const version = await this.findProjectVersion(peer);
|
238 | if (!version) {
|
239 | continue;
|
240 | }
|
241 |
|
242 | const options = { includePrerelease: true };
|
243 | if (!semver_1.intersects(version, peerIdentifier.rawSpec, options) &&
|
244 | !semver_1.satisfies(version, peerIdentifier.rawSpec, options)) {
|
245 | return true;
|
246 | }
|
247 | }
|
248 | catch (_b) {
|
249 |
|
250 | continue;
|
251 | }
|
252 | }
|
253 | else {
|
254 |
|
255 |
|
256 | }
|
257 | }
|
258 | return false;
|
259 | }
|
260 | }
|
261 | exports.AddCommand = AddCommand;
|