Skip to content

Commit

Permalink
Implement pnpm adapter
Browse files Browse the repository at this point in the history
  • Loading branch information
Turbo87 committed Nov 4, 2021
1 parent cb5f901 commit c2e80da
Show file tree
Hide file tree
Showing 3 changed files with 518 additions and 0 deletions.
170 changes: 170 additions & 0 deletions lib/dependency-manager-adapters/pnpm.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
'use strict';

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('--frozen-lockfile=false')) {
mgrOptions.push('--frozen-lockfile=false');
}

// 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;
}
}
},
});
13 changes: 13 additions & 0 deletions lib/utils/dependency-manager-adapter-factory.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
'use strict';

const NpmAdapter = require('../dependency-manager-adapters/npm');
const PnpmAdapter = require('../dependency-manager-adapters/pnpm');
const WorkspaceAdapter = require('../dependency-manager-adapters/workspace');

module.exports = {
Expand Down Expand Up @@ -34,6 +35,18 @@ module.exports = {
buildManagerOptions: config.buildManagerOptions,
})
);
} else if (config.usePnpm) {
console.warn(
'pnpm support is experimental for now. if you notice any problems please open an issue.'
);

adapters.push(
new PnpmAdapter({
cwd: root,
managerOptions: config.npmOptions,
buildManagerOptions: config.buildManagerOptions,
})
);
} else if (hasNpm) {
adapters.push(
new NpmAdapter({
Expand Down
Loading

0 comments on commit c2e80da

Please sign in to comment.