ember-try
Version:
An ember-cli addon to test against multiple dependencies, such as ember and ember-data.
173 lines (137 loc) • 5.7 kB
JavaScript
const CoreObject = require('core-object');
const fs = require('fs-extra');
const path = require('path');
const debug = require('debug')('ember-try:dependency-manager-adapter:pnpm');
const PACKAGE_JSON = 'package.json';
const PACKAGE_JSON_BACKUP = 'package.json.ember-try';
const PNPM_LOCKFILE = 'pnpm-lock.yaml';
// Note: the upstream convention is to append `.ember-try` _after_ the file
// extension, however this breaks syntax highlighting, so I've chosen to
// insert it right before the file extension.
const PNPM_LOCKFILE_BACKUP = 'pnpm-lock.ember-try.yaml';
module.exports = CoreObject.extend({
// This still needs to be `npm` because we're still reading the dependencies
// from the `npm` key of the ember-try config.
configKey: 'npm',
init() {
this._super.apply(this, arguments);
this.run = this.run || require('../utils/run');
},
async setup() {
let pkg = path.join(this.cwd, PACKAGE_JSON);
let pkgBackup = path.join(this.cwd, PACKAGE_JSON_BACKUP);
debug(`Copying ${PACKAGE_JSON}`);
await fs.copy(pkg, pkgBackup);
let lockFile = path.join(this.cwd, PNPM_LOCKFILE);
let lockFileBackup = path.join(this.cwd, PNPM_LOCKFILE_BACKUP);
if (fs.existsSync(lockFile)) {
debug(`Copying ${PNPM_LOCKFILE}`);
await fs.copy(lockFile, lockFileBackup);
}
},
async changeToDependencySet(depSet) {
await this.applyDependencySet(depSet);
await this._install(depSet);
let deps = Object.assign({}, depSet.dependencies, depSet.devDependencies);
let currentDeps = Object.keys(deps).map((dep) => {
return {
name: dep,
versionExpected: deps[dep],
versionSeen: this._findCurrentVersionOf(dep),
packageManager: 'pnpm',
};
});
debug('Switched to dependencies: \n', currentDeps);
return currentDeps;
},
async cleanup() {
try {
debug(`Restoring original ${PACKAGE_JSON}`);
let pkg = path.join(this.cwd, PACKAGE_JSON);
let pkgBackup = path.join(this.cwd, PACKAGE_JSON_BACKUP);
await fs.copy(pkgBackup, pkg);
await fs.remove(pkgBackup);
debug(`Restoring original ${PNPM_LOCKFILE}`);
let lockFile = path.join(this.cwd, PNPM_LOCKFILE);
let lockFileBackup = path.join(this.cwd, PNPM_LOCKFILE_BACKUP);
await fs.copy(lockFileBackup, lockFile);
await fs.remove(lockFileBackup);
await this._install();
} catch (e) {
console.log('Error cleaning up scenario:', e); // eslint-disable-line no-console
}
},
_findCurrentVersionOf(packageName) {
let filename = path.join(this.cwd, 'node_modules', packageName, PACKAGE_JSON);
if (fs.existsSync(filename)) {
return JSON.parse(fs.readFileSync(filename)).version;
} else {
return null;
}
},
async _install(depSet) {
let mgrOptions = this.managerOptions || [];
// buildManagerOptions overrides all default
if (typeof this.buildManagerOptions === 'function') {
mgrOptions = this.buildManagerOptions(depSet);
if (!Array.isArray(mgrOptions)) {
throw new Error('buildManagerOptions must return an array of options');
}
} else {
if (!mgrOptions.includes('--no-lockfile')) {
mgrOptions.push('--no-lockfile');
}
}
// Note: We are explicitly *not* using `--no-lockfile` here, so that we
// only have to resolve the dependencies that have actually changed.
debug('Run pnpm install with options %s', mgrOptions);
await this.run('pnpm', [].concat(['install'], mgrOptions), { cwd: this.cwd });
},
async applyDependencySet(depSet) {
debug('Changing to dependency set: %s', JSON.stringify(depSet));
if (!depSet) {
return;
}
let backupPackageJSON = path.join(this.cwd, PACKAGE_JSON_BACKUP);
let packageJSONFile = path.join(this.cwd, PACKAGE_JSON);
let packageJSON = JSON.parse(fs.readFileSync(backupPackageJSON));
let newPackageJSON = this._packageJSONForDependencySet(packageJSON, depSet);
debug('Write package.json with: \n', JSON.stringify(newPackageJSON));
fs.writeFileSync(packageJSONFile, JSON.stringify(newPackageJSON, null, 2));
// We restore the original lockfile here, so that we always create a minimal
// diff compared to the original locked dependency set.
let lockFile = path.join(this.cwd, PNPM_LOCKFILE);
let lockFileBackup = path.join(this.cwd, PNPM_LOCKFILE_BACKUP);
if (fs.existsSync(lockFileBackup)) {
debug(`Restoring original ${PNPM_LOCKFILE}`);
await fs.copy(lockFileBackup, lockFile);
}
},
_packageJSONForDependencySet(packageJSON, depSet) {
this._overridePackageJSONDependencies(packageJSON, depSet, 'dependencies');
this._overridePackageJSONDependencies(packageJSON, depSet, 'devDependencies');
this._overridePackageJSONDependencies(packageJSON, depSet, 'peerDependencies');
this._overridePackageJSONDependencies(packageJSON, depSet, 'ember');
// see https://pnpm.io/package_json#pnpmoverrides
this._overridePackageJSONDependencies(packageJSON, depSet, 'overrides');
return packageJSON;
},
_overridePackageJSONDependencies(packageJSON, depSet, kindOfDependency) {
if (!depSet[kindOfDependency]) {
return;
}
let packageNames = Object.keys(depSet[kindOfDependency]);
for (let packageName of packageNames) {
if (!packageJSON[kindOfDependency]) {
packageJSON[kindOfDependency] = {};
}
let version = depSet[kindOfDependency][packageName];
if (version === null) {
delete packageJSON[kindOfDependency][packageName];
} else {
packageJSON[kindOfDependency][packageName] = version;
}
}
},
});
;