1 | 'use strict';
|
2 |
|
3 | const CoreObject = require('core-object');
|
4 | const fs = require('fs-extra');
|
5 | const util = require('util');
|
6 | const copy = util.promisify(fs.copy);
|
7 | const path = require('path');
|
8 | const debug = require('debug')('ember-try:dependency-manager-adapter:npm');
|
9 | const rimraf = util.promisify(require('rimraf'));
|
10 | const chalk = require('chalk');
|
11 | const semver = require('semver');
|
12 |
|
13 | module.exports = CoreObject.extend({
|
14 | init() {
|
15 | this._super.apply(this, arguments);
|
16 | this.run = this.run || require('../utils/run');
|
17 | },
|
18 | useYarnCommand: false,
|
19 | yarnLock: 'yarn.lock',
|
20 | yarnLockBackupFileName: 'yarn.lock.ember-try',
|
21 | configKey: 'npm',
|
22 | packageJSON: 'package.json',
|
23 | packageJSONBackupFileName: 'package.json.ember-try',
|
24 | nodeModules: 'node_modules',
|
25 | nodeModulesBackupLocation: '.node_modules.ember-try',
|
26 | npmShrinkWrap: 'npm-shrinkwrap.json',
|
27 | npmShrinkWrapBackupFileName: 'npm-shrinkwrap.json.ember-try',
|
28 | packageLock: 'package-lock.json',
|
29 | packageLockBackupFileName: 'package-lock.json.ember-try',
|
30 |
|
31 | async setup(options) {
|
32 | if (!options) {
|
33 | options = {};
|
34 | }
|
35 |
|
36 | this._runYarnCheck(options.ui);
|
37 |
|
38 | return await this._backupOriginalDependencies();
|
39 | },
|
40 |
|
41 | async changeToDependencySet(depSet) {
|
42 | this.applyDependencySet(depSet);
|
43 |
|
44 | await this._install(depSet);
|
45 |
|
46 | let deps = Object.assign({}, depSet.dependencies, depSet.devDependencies);
|
47 | let currentDeps = Object.keys(deps).map((dep) => {
|
48 | return {
|
49 | name: dep,
|
50 | versionExpected: deps[dep],
|
51 | versionSeen: this._findCurrentVersionOf(dep),
|
52 | packageManager: this.useYarnCommand ? 'yarn' : 'npm',
|
53 | };
|
54 | });
|
55 |
|
56 | debug('Switched to dependencies: \n', currentDeps);
|
57 |
|
58 | return currentDeps;
|
59 | },
|
60 |
|
61 | async cleanup() {
|
62 | try {
|
63 | await this._restoreOriginalDependencies();
|
64 |
|
65 | debug('Remove backup package.json and node_modules');
|
66 |
|
67 | let cleanupTasks = [
|
68 | rimraf(path.join(this.cwd, this.packageJSONBackupFileName)),
|
69 | rimraf(path.join(this.cwd, this.nodeModulesBackupLocation)),
|
70 | ];
|
71 |
|
72 | if (fs.existsSync(path.join(this.cwd, this.yarnLockBackupFileName))) {
|
73 | cleanupTasks.push(rimraf(path.join(this.cwd, this.yarnLockBackupFileName)));
|
74 | }
|
75 |
|
76 | if (fs.existsSync(path.join(this.cwd, this.npmShrinkWrapBackupFileName))) {
|
77 | cleanupTasks.push(rimraf(path.join(this.cwd, this.npmShrinkWrapBackupFileName)));
|
78 | }
|
79 |
|
80 | if (fs.existsSync(path.join(this.cwd, this.packageLockBackupFileName))) {
|
81 | cleanupTasks.push(rimraf(path.join(this.cwd, this.packageLockBackupFileName)));
|
82 | }
|
83 |
|
84 | return await Promise.all(cleanupTasks);
|
85 | } catch (e) {
|
86 | console.log('Error cleaning up npm scenario:', e);
|
87 | }
|
88 | },
|
89 |
|
90 | _runYarnCheck(ui) {
|
91 | if (!this.useYarnCommand) {
|
92 | try {
|
93 | if (fs.statSync(path.join(this.cwd, this.yarnLock)).isFile()) {
|
94 | ui.writeLine(
|
95 | chalk.yellow(
|
96 | 'Detected a yarn.lock file. Add `useYarn: true` to your `config/ember-try.js` configuration file if you want to use Yarn to install npm dependencies.'
|
97 | )
|
98 | );
|
99 | }
|
100 | } catch (e) {
|
101 |
|
102 | }
|
103 | }
|
104 | },
|
105 |
|
106 | _findCurrentVersionOf(packageName) {
|
107 | let filename = path.join(this.cwd, this.nodeModules, packageName, this.packageJSON);
|
108 | if (fs.existsSync(filename)) {
|
109 | return JSON.parse(fs.readFileSync(filename)).version;
|
110 | } else {
|
111 | return null;
|
112 | }
|
113 | },
|
114 |
|
115 | async _install(depSet) {
|
116 | let mgrOptions = this.managerOptions || [];
|
117 | let cmd = this.useYarnCommand ? 'yarn' : 'npm';
|
118 |
|
119 |
|
120 | if (typeof this.buildManagerOptions === 'function') {
|
121 | mgrOptions = this.buildManagerOptions(depSet);
|
122 |
|
123 | if (!Array.isArray(mgrOptions)) {
|
124 | throw new Error('buildManagerOptions must return an array of options');
|
125 | }
|
126 | } else {
|
127 | if (this.useYarnCommand) {
|
128 | if (mgrOptions.indexOf('--no-lockfile') === -1) {
|
129 | mgrOptions = mgrOptions.concat(['--no-lockfile']);
|
130 | }
|
131 |
|
132 |
|
133 | if (mgrOptions.indexOf('--ignore-engines') === -1) {
|
134 | mgrOptions = mgrOptions.concat(['--ignore-engines']);
|
135 | }
|
136 | } else if (mgrOptions.indexOf('--no-shrinkwrap') === -1) {
|
137 | mgrOptions = mgrOptions.concat(['--no-shrinkwrap']);
|
138 | }
|
139 | }
|
140 |
|
141 | debug('Run npm/yarn install with options %s', mgrOptions);
|
142 |
|
143 | await this.run(cmd, [].concat(['install'], mgrOptions), { cwd: this.cwd });
|
144 |
|
145 | if (!this.useYarnCommand) {
|
146 | let res = await this.run('npm', ['--version'], { cwd: this.cwd, stdio: 'pipe' });
|
147 | let version = res.stdout;
|
148 | if (version.match(/^4./)) {
|
149 | debug('Running npm prune because version is %s', version);
|
150 | return await this.run(this.configKey, ['prune'], { cwd: this.cwd });
|
151 | }
|
152 |
|
153 | debug('Not running npm prune because version is %s', version);
|
154 | }
|
155 | },
|
156 |
|
157 | applyDependencySet(depSet) {
|
158 | debug('Changing to dependency set: %s', JSON.stringify(depSet));
|
159 |
|
160 | if (!depSet) {
|
161 | return;
|
162 | }
|
163 |
|
164 | let backupPackageJSON = path.join(this.cwd, this.packageJSONBackupFileName);
|
165 | let packageJSONFile = path.join(this.cwd, this.packageJSON);
|
166 | let packageJSON = JSON.parse(fs.readFileSync(backupPackageJSON));
|
167 | let newPackageJSON = this._packageJSONForDependencySet(packageJSON, depSet);
|
168 |
|
169 | debug('Write package.json with: \n', JSON.stringify(newPackageJSON));
|
170 |
|
171 | fs.writeFileSync(packageJSONFile, JSON.stringify(newPackageJSON, null, 2));
|
172 | },
|
173 |
|
174 | _packageJSONForDependencySet(packageJSON, depSet) {
|
175 | this._overridePackageJSONDependencies(packageJSON, depSet, 'dependencies');
|
176 | this._overridePackageJSONDependencies(packageJSON, depSet, 'devDependencies');
|
177 | this._overridePackageJSONDependencies(packageJSON, depSet, 'peerDependencies');
|
178 | this._overridePackageJSONDependencies(packageJSON, depSet, 'ember');
|
179 |
|
180 | if (this.useYarnCommand) {
|
181 | this._overridePackageJSONDependencies(packageJSON, depSet, 'resolutions');
|
182 | } else {
|
183 | this._overridePackageJSONDependencies(packageJSON, depSet, 'overrides');
|
184 | }
|
185 |
|
186 | return packageJSON;
|
187 | },
|
188 |
|
189 | _overridePackageJSONDependencies(packageJSON, depSet, kindOfDependency) {
|
190 | if (!depSet[kindOfDependency]) {
|
191 | return;
|
192 | }
|
193 |
|
194 | let packageNames = Object.keys(depSet[kindOfDependency]);
|
195 |
|
196 | packageNames.forEach((packageName) => {
|
197 | if (!packageJSON[kindOfDependency]) {
|
198 | packageJSON[kindOfDependency] = {};
|
199 | }
|
200 |
|
201 | let version = depSet[kindOfDependency][packageName];
|
202 | if (version === null) {
|
203 | delete packageJSON[kindOfDependency][packageName];
|
204 | } else {
|
205 | packageJSON[kindOfDependency][packageName] = version;
|
206 |
|
207 |
|
208 | if (
|
209 | !this.useYarnCommand &&
|
210 | (semver.prerelease(version) || /^https*:\/\/.*\.tg*z/.test(version))
|
211 | ) {
|
212 | if (!packageJSON.overrides) {
|
213 | packageJSON.overrides = {};
|
214 | }
|
215 |
|
216 | packageJSON.overrides[packageName] = `$${packageName}`;
|
217 | }
|
218 | }
|
219 | });
|
220 | },
|
221 |
|
222 | _restoreOriginalDependencies() {
|
223 | debug('Restoring original package.json and node_modules');
|
224 |
|
225 | let restoreTasks = [
|
226 | copy(
|
227 | path.join(this.cwd, this.packageJSONBackupFileName),
|
228 | path.join(this.cwd, this.packageJSON)
|
229 | ),
|
230 | ];
|
231 |
|
232 | let nodeModulesBackupLocation = path.join(this.cwd, this.nodeModulesBackupLocation);
|
233 | if (fs.existsSync(nodeModulesBackupLocation)) {
|
234 | restoreTasks.push(
|
235 | copy(nodeModulesBackupLocation, path.join(this.cwd, this.nodeModules), { clobber: true })
|
236 | );
|
237 | }
|
238 |
|
239 | let yarnLockBackupFileName = path.join(this.cwd, this.yarnLockBackupFileName);
|
240 | if (fs.existsSync(yarnLockBackupFileName)) {
|
241 | restoreTasks.push(copy(yarnLockBackupFileName, path.join(this.cwd, this.yarnLock)));
|
242 | }
|
243 |
|
244 | let npmShrinkWrapBackupFileName = path.join(this.cwd, this.npmShrinkWrapBackupFileName);
|
245 | if (fs.existsSync(npmShrinkWrapBackupFileName)) {
|
246 | restoreTasks.push(copy(npmShrinkWrapBackupFileName, path.join(this.cwd, this.npmShrinkWrap)));
|
247 | }
|
248 |
|
249 | let packageLockBackupFileName = path.join(this.cwd, this.packageLockBackupFileName);
|
250 | if (fs.existsSync(packageLockBackupFileName)) {
|
251 | restoreTasks.push(copy(packageLockBackupFileName, path.join(this.cwd, this.packageLock)));
|
252 | }
|
253 |
|
254 | return Promise.all(restoreTasks);
|
255 | },
|
256 |
|
257 | _backupOriginalDependencies() {
|
258 | debug('Backing up package.json and node_modules');
|
259 |
|
260 | let backupTasks = [
|
261 | copy(
|
262 | path.join(this.cwd, this.packageJSON),
|
263 | path.join(this.cwd, this.packageJSONBackupFileName)
|
264 | ),
|
265 | ];
|
266 |
|
267 | let nodeModulesPath = path.join(this.cwd, this.nodeModules);
|
268 | if (fs.existsSync(nodeModulesPath)) {
|
269 | backupTasks.push(
|
270 | copy(nodeModulesPath, path.join(this.cwd, this.nodeModulesBackupLocation), {
|
271 | clobber: true,
|
272 | })
|
273 | );
|
274 | }
|
275 |
|
276 | let yarnLockPath = path.join(this.cwd, this.yarnLock);
|
277 | if (fs.existsSync(yarnLockPath)) {
|
278 | backupTasks.push(copy(yarnLockPath, path.join(this.cwd, this.yarnLockBackupFileName)));
|
279 | }
|
280 |
|
281 | let npmShrinkWrapPath = path.join(this.cwd, this.npmShrinkWrap);
|
282 | if (fs.existsSync(npmShrinkWrapPath)) {
|
283 | backupTasks.push(
|
284 | copy(npmShrinkWrapPath, path.join(this.cwd, this.npmShrinkWrapBackupFileName))
|
285 | );
|
286 | }
|
287 |
|
288 | let packageLockPath = path.join(this.cwd, this.packageLock);
|
289 | if (fs.existsSync(packageLockPath)) {
|
290 | backupTasks.push(copy(packageLockPath, path.join(this.cwd, this.packageLockBackupFileName)));
|
291 | }
|
292 |
|
293 | return Promise.all(backupTasks);
|
294 | },
|
295 | });
|