UNPKG

5.7 kBJavaScriptView Raw
1'use strict';
2
3const CoreObject = require('core-object');
4const fs = require('fs-extra');
5const path = require('path');
6const debug = require('debug')('ember-try:dependency-manager-adapter:pnpm');
7
8const PACKAGE_JSON = 'package.json';
9const PACKAGE_JSON_BACKUP = 'package.json.ember-try';
10const PNPM_LOCKFILE = 'pnpm-lock.yaml';
11
12// Note: the upstream convention is to append `.ember-try` _after_ the file
13// extension, however this breaks syntax highlighting, so I've chosen to
14// insert it right before the file extension.
15const PNPM_LOCKFILE_BACKUP = 'pnpm-lock.ember-try.yaml';
16
17module.exports = CoreObject.extend({
18 // This still needs to be `npm` because we're still reading the dependencies
19 // from the `npm` key of the ember-try config.
20 configKey: 'npm',
21
22 init() {
23 this._super.apply(this, arguments);
24 this.run = this.run || require('../utils/run');
25 },
26
27 async setup() {
28 let pkg = path.join(this.cwd, PACKAGE_JSON);
29 let pkgBackup = path.join(this.cwd, PACKAGE_JSON_BACKUP);
30 debug(`Copying ${PACKAGE_JSON}`);
31 await fs.copy(pkg, pkgBackup);
32
33 let lockFile = path.join(this.cwd, PNPM_LOCKFILE);
34 let lockFileBackup = path.join(this.cwd, PNPM_LOCKFILE_BACKUP);
35 if (fs.existsSync(lockFile)) {
36 debug(`Copying ${PNPM_LOCKFILE}`);
37 await fs.copy(lockFile, lockFileBackup);
38 }
39 },
40
41 async changeToDependencySet(depSet) {
42 await 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: 'pnpm',
53 };
54 });
55
56 debug('Switched to dependencies: \n', currentDeps);
57
58 return currentDeps;
59 },
60
61 async cleanup() {
62 try {
63 debug(`Restoring original ${PACKAGE_JSON}`);
64 let pkg = path.join(this.cwd, PACKAGE_JSON);
65 let pkgBackup = path.join(this.cwd, PACKAGE_JSON_BACKUP);
66 await fs.copy(pkgBackup, pkg);
67 await fs.remove(pkgBackup);
68
69 debug(`Restoring original ${PNPM_LOCKFILE}`);
70 let lockFile = path.join(this.cwd, PNPM_LOCKFILE);
71 let lockFileBackup = path.join(this.cwd, PNPM_LOCKFILE_BACKUP);
72 await fs.copy(lockFileBackup, lockFile);
73 await fs.remove(lockFileBackup);
74
75 await this._install();
76 } catch (e) {
77 console.log('Error cleaning up scenario:', e); // eslint-disable-line no-console
78 }
79 },
80
81 _findCurrentVersionOf(packageName) {
82 let filename = path.join(this.cwd, 'node_modules', packageName, PACKAGE_JSON);
83 if (fs.existsSync(filename)) {
84 return JSON.parse(fs.readFileSync(filename)).version;
85 } else {
86 return null;
87 }
88 },
89
90 async _install(depSet) {
91 let mgrOptions = this.managerOptions || [];
92
93 // buildManagerOptions overrides all default
94 if (typeof this.buildManagerOptions === 'function') {
95 mgrOptions = this.buildManagerOptions(depSet);
96
97 if (!Array.isArray(mgrOptions)) {
98 throw new Error('buildManagerOptions must return an array of options');
99 }
100 } else {
101 if (!mgrOptions.includes('--no-lockfile')) {
102 mgrOptions.push('--no-lockfile');
103 }
104 }
105
106 // Note: We are explicitly *not* using `--no-lockfile` here, so that we
107 // only have to resolve the dependencies that have actually changed.
108
109 debug('Run pnpm install with options %s', mgrOptions);
110
111 await this.run('pnpm', [].concat(['install'], mgrOptions), { cwd: this.cwd });
112 },
113
114 async applyDependencySet(depSet) {
115 debug('Changing to dependency set: %s', JSON.stringify(depSet));
116
117 if (!depSet) {
118 return;
119 }
120
121 let backupPackageJSON = path.join(this.cwd, PACKAGE_JSON_BACKUP);
122 let packageJSONFile = path.join(this.cwd, PACKAGE_JSON);
123 let packageJSON = JSON.parse(fs.readFileSync(backupPackageJSON));
124 let newPackageJSON = this._packageJSONForDependencySet(packageJSON, depSet);
125
126 debug('Write package.json with: \n', JSON.stringify(newPackageJSON));
127 fs.writeFileSync(packageJSONFile, JSON.stringify(newPackageJSON, null, 2));
128
129 // We restore the original lockfile here, so that we always create a minimal
130 // diff compared to the original locked dependency set.
131
132 let lockFile = path.join(this.cwd, PNPM_LOCKFILE);
133 let lockFileBackup = path.join(this.cwd, PNPM_LOCKFILE_BACKUP);
134 if (fs.existsSync(lockFileBackup)) {
135 debug(`Restoring original ${PNPM_LOCKFILE}`);
136 await fs.copy(lockFileBackup, lockFile);
137 }
138 },
139
140 _packageJSONForDependencySet(packageJSON, depSet) {
141 this._overridePackageJSONDependencies(packageJSON, depSet, 'dependencies');
142 this._overridePackageJSONDependencies(packageJSON, depSet, 'devDependencies');
143 this._overridePackageJSONDependencies(packageJSON, depSet, 'peerDependencies');
144 this._overridePackageJSONDependencies(packageJSON, depSet, 'ember');
145
146 // see https://pnpm.io/package_json#pnpmoverrides
147 this._overridePackageJSONDependencies(packageJSON, depSet, 'overrides');
148
149 return packageJSON;
150 },
151
152 _overridePackageJSONDependencies(packageJSON, depSet, kindOfDependency) {
153 if (!depSet[kindOfDependency]) {
154 return;
155 }
156
157 let packageNames = Object.keys(depSet[kindOfDependency]);
158
159 for (let packageName of packageNames) {
160 if (!packageJSON[kindOfDependency]) {
161 packageJSON[kindOfDependency] = {};
162 }
163
164 let version = depSet[kindOfDependency][packageName];
165 if (version === null) {
166 delete packageJSON[kindOfDependency][packageName];
167 } else {
168 packageJSON[kindOfDependency][packageName] = version;
169 }
170 }
171 },
172});