UNPKG

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