Skip to content

Commit c46951f

Browse files
committed
Introduce a BaseAdapter class to remove all duplication
1 parent ff115a5 commit c46951f

13 files changed

Lines changed: 245 additions & 480 deletions

File tree

Lines changed: 177 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,177 @@
1+
'use strict';
2+
3+
const chalk = require('chalk');
4+
const debug = require('debug');
5+
const { get, set } = require('es-toolkit/compat');
6+
const fs = require('fs-extra');
7+
const path = require('node:path');
8+
const semver = require('semver');
9+
const Backup = require('../utils/backup');
10+
const { LOCKFILE, PACKAGE_JSON } = require('../utils/package-managers');
11+
12+
class BaseAdapter {
13+
configKey = 'npm';
14+
defaultInstallOptions = [];
15+
lockfile = '';
16+
name = '';
17+
overridesKey = '';
18+
19+
backup = null;
20+
debugFunction = null;
21+
22+
constructor(options) {
23+
this.backup = new Backup({ cwd: options.cwd });
24+
this.buildManagerOptions = options.buildManagerOptions;
25+
this.cwd = options.cwd;
26+
this.managerOptions = options.managerOptions;
27+
this.run = options.run ?? require('../utils/run');
28+
}
29+
30+
debug(...args) {
31+
if (this.debugFunction === null) {
32+
this.debugFunction = debug(`ember-try:dependency-manager-adapter:${this.name}`);
33+
}
34+
35+
this.debugFunction(...args);
36+
}
37+
38+
async setup(options = {}) {
39+
this._checkForDifferentLockfiles(options.ui);
40+
41+
await this.backup.addFiles([PACKAGE_JSON, this.lockfile]);
42+
}
43+
44+
async changeToDependencySet(dependencySet) {
45+
await this.applyDependencySet(dependencySet);
46+
await this._install(dependencySet);
47+
48+
const dependencies = {
49+
...dependencySet.dependencies,
50+
...dependencySet.devDependencies,
51+
};
52+
53+
const currentDependencies = Object.keys(dependencies).map((name) => ({
54+
name,
55+
packageManager: this.name,
56+
versionExpected: dependencies[name],
57+
versionSeen: this._findCurrentVersionOf(name),
58+
}));
59+
60+
this.debug('Switched to dependencies: \n', currentDependencies);
61+
62+
return currentDependencies;
63+
}
64+
65+
async applyDependencySet(dependencySet) {
66+
if (dependencySet === undefined) {
67+
return;
68+
}
69+
70+
this.debug('Changing to dependency set: %s', JSON.stringify(dependencySet));
71+
72+
const oldPackageJSON = JSON.parse(fs.readFileSync(this.backup.pathForFile(PACKAGE_JSON)));
73+
const newPackageJSON = this._packageJSONForDependencySet(oldPackageJSON, dependencySet);
74+
75+
this.debug('Write package.json with: \n', JSON.stringify(newPackageJSON));
76+
77+
fs.writeFileSync(path.join(this.cwd, PACKAGE_JSON), JSON.stringify(newPackageJSON, null, 2));
78+
}
79+
80+
async cleanup() {
81+
try {
82+
await this.backup.restoreFiles([PACKAGE_JSON, this.lockfile]);
83+
await this.backup.cleanUp();
84+
await this._install();
85+
} catch (error) {
86+
console.error('Error cleaning up scenario:', error);
87+
}
88+
}
89+
90+
_checkForDifferentLockfiles(ui) {
91+
for (const packageManager in LOCKFILE) {
92+
const lockfile = LOCKFILE[packageManager];
93+
94+
if (lockfile === this.lockfile) {
95+
continue;
96+
}
97+
98+
try {
99+
if (fs.statSync(path.join(this.cwd, lockfile)).isFile()) {
100+
ui.writeLine(
101+
chalk.yellow(
102+
`Detected a \`${lockfile}\` file. Add \`packageManager: '${packageManager}'\` to your \`config/ember-try.js\` configuration file if you want to use ${packageManager} to install dependencies.`,
103+
),
104+
);
105+
}
106+
} catch {
107+
// Move along.
108+
}
109+
}
110+
}
111+
112+
_findCurrentVersionOf(name) {
113+
const filename = path.join(this.cwd, 'node_modules', name, PACKAGE_JSON);
114+
115+
if (fs.existsSync(filename)) {
116+
return JSON.parse(fs.readFileSync(filename)).version;
117+
} else {
118+
return null;
119+
}
120+
}
121+
122+
async _install(dependencySet) {
123+
let managerOptions = this.managerOptions || [];
124+
125+
if (typeof this.buildManagerOptions === 'function') {
126+
managerOptions = this.buildManagerOptions(dependencySet);
127+
128+
if (Array.isArray(managerOptions) === false) {
129+
throw new Error('buildManagerOptions must return an array of options');
130+
}
131+
} else if (this.defaultInstallOptions.length) {
132+
for (const option of this.defaultInstallOptions) {
133+
if (managerOptions.includes(option) === false) {
134+
managerOptions.push(option);
135+
}
136+
}
137+
}
138+
139+
this.debug(`Running ${this.name} install with options %s`, managerOptions);
140+
141+
await this.run(this.name, ['install', ...managerOptions], { cwd: this.cwd });
142+
}
143+
144+
_packageJSONForDependencySet(packageJSON, dependencySet) {
145+
this._overridePackageJSONDependencies(packageJSON, dependencySet, 'dependencies');
146+
this._overridePackageJSONDependencies(packageJSON, dependencySet, 'devDependencies');
147+
this._overridePackageJSONDependencies(packageJSON, dependencySet, 'peerDependencies');
148+
this._overridePackageJSONDependencies(packageJSON, dependencySet, 'ember');
149+
this._overridePackageJSONDependencies(packageJSON, dependencySet, this.overridesKey);
150+
151+
return packageJSON;
152+
}
153+
154+
_overridePackageJSONDependencies(packageJSON, dependencySet, kindOfDependency) {
155+
if (get(dependencySet, kindOfDependency) === undefined) {
156+
return;
157+
}
158+
159+
let packageNames = Object.keys(get(dependencySet, kindOfDependency));
160+
161+
for (let packageName of packageNames) {
162+
let version = get(dependencySet, `${kindOfDependency}.${packageName}`);
163+
164+
if (version === null) {
165+
delete get(packageJSON, kindOfDependency)[packageName];
166+
} else {
167+
set(packageJSON, `${kindOfDependency}.${packageName}`, version);
168+
169+
if (semver.prerelease(version) || /^https*:\/\/.*\.tg*z/.test(version)) {
170+
set(packageJSON, `${this.overridesKey}.${packageName}`, `$${packageName}`);
171+
}
172+
}
173+
}
174+
}
175+
}
176+
177+
module.exports = { BaseAdapter };
Lines changed: 8 additions & 167 deletions
Original file line numberDiff line numberDiff line change
@@ -1,170 +1,11 @@
11
'use strict';
22

3-
const fs = require('fs-extra');
4-
const path = require('path');
5-
const debug = require('debug')('ember-try:dependency-manager-adapter:npm');
6-
const chalk = require('chalk');
7-
const semver = require('semver');
8-
const Backup = require('../utils/backup');
9-
10-
module.exports = class {
11-
configKey = 'npm';
12-
packageJSON = 'package.json';
13-
packageLock = 'package-lock.json';
14-
15-
constructor(options) {
16-
this.buildManagerOptions = options.buildManagerOptions;
17-
this.cwd = options.cwd;
18-
this.managerOptions = options.managerOptions;
19-
this.run = options.run || require('../utils/run');
20-
21-
this.backup = new Backup({ cwd: this.cwd });
22-
}
23-
24-
async setup(options) {
25-
if (!options) {
26-
options = {};
27-
}
28-
29-
this._runYarnCheck(options.ui);
30-
31-
return await this._backupOriginalDependencies();
32-
}
33-
34-
async changeToDependencySet(depSet) {
35-
this.applyDependencySet(depSet);
36-
37-
await this._install(depSet);
38-
39-
let deps = Object.assign({}, depSet.dependencies, depSet.devDependencies);
40-
let currentDeps = Object.keys(deps).map((dep) => {
41-
return {
42-
name: dep,
43-
versionExpected: deps[dep],
44-
versionSeen: this._findCurrentVersionOf(dep),
45-
packageManager: 'npm',
46-
};
47-
});
48-
49-
debug('Switched to dependencies: \n', currentDeps);
50-
51-
return currentDeps;
52-
}
53-
54-
async cleanup() {
55-
try {
56-
await this._restoreOriginalDependencies();
57-
} catch (e) {
58-
console.log('Error cleaning up npm scenario:', e); // eslint-disable-line no-console
59-
}
60-
}
61-
62-
_runYarnCheck(ui) {
63-
try {
64-
if (fs.statSync(path.join(this.cwd, 'yarn.lock')).isFile()) {
65-
ui.writeLine(
66-
chalk.yellow(
67-
"Detected a yarn.lock file. Add `packageManager: 'yarn'` to your `config/ember-try.js` configuration file if you want to use Yarn to install npm dependencies.",
68-
),
69-
);
70-
}
71-
} catch {
72-
// If no yarn.lock is found, no need to warn.
73-
}
74-
}
75-
76-
_findCurrentVersionOf(packageName) {
77-
let filename = path.join(this.cwd, 'node_modules', packageName, this.packageJSON);
78-
if (fs.existsSync(filename)) {
79-
return JSON.parse(fs.readFileSync(filename)).version;
80-
} else {
81-
return null;
82-
}
83-
}
84-
85-
async _install(depSet) {
86-
let mgrOptions = this.managerOptions || [];
87-
88-
// buildManagerOptions overrides all default
89-
if (typeof this.buildManagerOptions === 'function') {
90-
mgrOptions = this.buildManagerOptions(depSet);
91-
92-
if (!Array.isArray(mgrOptions)) {
93-
throw new Error('buildManagerOptions must return an array of options');
94-
}
95-
} else if (mgrOptions.indexOf('--no-package-lock') === -1) {
96-
mgrOptions = mgrOptions.concat(['--no-package-lock']);
97-
}
98-
99-
debug('Run npm install with options %s', mgrOptions);
100-
101-
await this.run('npm', [].concat(['install'], mgrOptions), { cwd: this.cwd });
102-
}
103-
104-
applyDependencySet(depSet) {
105-
debug('Changing to dependency set: %s', JSON.stringify(depSet));
106-
107-
if (!depSet) {
108-
return;
109-
}
110-
111-
let backupPackageJSON = this.backup.pathForFile(this.packageJSON);
112-
let packageJSONFile = path.join(this.cwd, this.packageJSON);
113-
let packageJSON = JSON.parse(fs.readFileSync(backupPackageJSON));
114-
let newPackageJSON = this._packageJSONForDependencySet(packageJSON, depSet);
115-
116-
debug('Write package.json with: \n', JSON.stringify(newPackageJSON));
117-
118-
fs.writeFileSync(packageJSONFile, JSON.stringify(newPackageJSON, null, 2));
119-
}
120-
121-
_packageJSONForDependencySet(packageJSON, depSet) {
122-
this._overridePackageJSONDependencies(packageJSON, depSet, 'dependencies');
123-
this._overridePackageJSONDependencies(packageJSON, depSet, 'devDependencies');
124-
this._overridePackageJSONDependencies(packageJSON, depSet, 'peerDependencies');
125-
this._overridePackageJSONDependencies(packageJSON, depSet, 'ember');
126-
this._overridePackageJSONDependencies(packageJSON, depSet, 'overrides');
127-
128-
return packageJSON;
129-
}
130-
131-
_overridePackageJSONDependencies(packageJSON, depSet, kindOfDependency) {
132-
if (!depSet[kindOfDependency]) {
133-
return;
134-
}
135-
136-
let packageNames = Object.keys(depSet[kindOfDependency]);
137-
138-
packageNames.forEach((packageName) => {
139-
if (!packageJSON[kindOfDependency]) {
140-
packageJSON[kindOfDependency] = {};
141-
}
142-
143-
let version = depSet[kindOfDependency][packageName];
144-
if (version === null) {
145-
delete packageJSON[kindOfDependency][packageName];
146-
} else {
147-
packageJSON[kindOfDependency][packageName] = version;
148-
149-
// in npm we need to always add an override if the version is a pre-release
150-
if (semver.prerelease(version) || /^https*:\/\/.*\.tg*z/.test(version)) {
151-
if (!packageJSON.overrides) {
152-
packageJSON.overrides = {};
153-
}
154-
155-
packageJSON.overrides[packageName] = `$${packageName}`;
156-
}
157-
}
158-
});
159-
}
160-
161-
async _restoreOriginalDependencies() {
162-
await this.backup.restoreFiles([this.packageJSON, this.packageLock]);
163-
await this.backup.cleanUp();
164-
await this._install();
165-
}
166-
167-
async _backupOriginalDependencies() {
168-
await this.backup.addFiles([this.packageJSON, this.packageLock]);
169-
}
3+
const { LOCKFILE } = require('../utils/package-managers');
4+
const { BaseAdapter } = require('./base');
5+
6+
module.exports = class NpmAdapter extends BaseAdapter {
7+
defaultInstallOptions = ['--no-package-lock'];
8+
lockfile = LOCKFILE.npm;
9+
name = 'npm';
10+
overridesKey = 'overrides';
17011
};

0 commit comments

Comments
 (0)