1 | "use strict";
|
2 | Object.defineProperty(exports, "__esModule", { value: true });
|
3 | exports.AddCommand = void 0;
|
4 |
|
5 |
|
6 |
|
7 |
|
8 |
|
9 |
|
10 |
|
11 | const core_1 = require("@angular-devkit/core");
|
12 | const tools_1 = require("@angular-devkit/schematics/tools");
|
13 | const path_1 = require("path");
|
14 | const semver_1 = require("semver");
|
15 | const schema_1 = require("../lib/config/schema");
|
16 | const analytics_1 = require("../models/analytics");
|
17 | const schematic_command_1 = require("../models/schematic-command");
|
18 | const install_package_1 = require("../tasks/install-package");
|
19 | const color_1 = require("../utilities/color");
|
20 | const package_manager_1 = require("../utilities/package-manager");
|
21 | const package_metadata_1 = require("../utilities/package-metadata");
|
22 | const npa = require('npm-package-arg');
|
23 | class 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 |
|
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 |
|
77 |
|
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 |
|
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 |
|
141 |
|
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 |
|
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 |
|
250 | continue;
|
251 | }
|
252 | }
|
253 | else {
|
254 |
|
255 |
|
256 | }
|
257 | }
|
258 | return false;
|
259 | }
|
260 | }
|
261 | exports.AddCommand = AddCommand;
|