Skip to content

Commit

Permalink
Add support for using yarn.lock file (#409)
Browse files Browse the repository at this point in the history
Add support for using yarn.lock file
  • Loading branch information
rwjblue committed Nov 11, 2019
2 parents 57d9ea2 + f7dee65 commit 0f887ba
Show file tree
Hide file tree
Showing 6 changed files with 328 additions and 26 deletions.
9 changes: 9 additions & 0 deletions README.md
Expand Up @@ -126,6 +126,15 @@ module.exports = function() {
dependencies will be restored to their prior state.
*/
useYarn: true,

/*
buildManagerOptions allows you to opt-out of the default options such as `--ignore-engines --no-lockfile`.
The buildManagerOptions function is aware of each scenario so you can customize your options.
*/
buildManagerOptions(scenario) {
return ['--ignore-engines'];
}

scenarios: [
{
name: 'Ember 1.10 with ember-data',
Expand Down
82 changes: 69 additions & 13 deletions lib/dependency-manager-adapters/npm.js
Expand Up @@ -16,11 +16,16 @@ module.exports = CoreObject.extend({
},
useYarnCommand: false,
yarnLock: 'yarn.lock',
yarnLockBackupFileName: 'yarn.lock.ember-try',
configKey: 'npm',
packageJSON: 'package.json',
packageJSONBackupFileName: 'package.json.ember-try',
nodeModules: 'node_modules',
nodeModulesBackupLocation: '.node_modules.ember-try',
npmShrinkWrap: 'npm-shrinkwrap.json',
npmShrinkWrapBackupFileName: 'npm-shrinkwrap.json.ember-try',
packageLock: 'package-lock.json',
packageLockBackupFileName: 'package-lock.json.ember-try',
setup(options) {
if (!options) {
options = {};
Expand All @@ -33,7 +38,7 @@ module.exports = CoreObject.extend({

adapter.applyDependencySet(depSet);

return adapter._install().then(() => {
return adapter._install(depSet).then(() => {
let deps = extend({}, depSet.dependencies || {}, depSet.devDependencies || {});
let currentDeps = Object.keys(deps).map((dep) => {
return {
Expand All @@ -58,6 +63,18 @@ module.exports = CoreObject.extend({
let cleanupTasks = [rimraf(path.join(adapter.cwd, adapter.packageJSONBackupFileName)),
rimraf(path.join(adapter.cwd, adapter.nodeModulesBackupLocation))];

if (fs.existsSync(path.join(this.cwd, this.yarnLockBackupFileName))) {
cleanupTasks.push(rimraf(path.join(adapter.cwd, adapter.yarnLockBackupFileName)));
}

if (fs.existsSync(path.join(this.cwd, this.npmShrinkWrapBackupFileName))) {
cleanupTasks.push(rimraf(path.join(adapter.cwd, adapter.npmShrinkWrapBackupFileName)));
}

if (fs.existsSync(path.join(this.cwd, this.packageLockBackupFileName))) {
cleanupTasks.push(rimraf(path.join(adapter.cwd, adapter.packageLockBackupFileName)));
}

return RSVP.all(cleanupTasks);
}).catch((e) => {
console.log('Error cleaning up npm scenario:', e); // eslint-disable-line no-console
Expand All @@ -82,26 +99,35 @@ module.exports = CoreObject.extend({
return null;
}
},
_install() {
_install(depSet) {
let adapter = this;
let mgrOptions = this.managerOptions || [];
let cmd = this.useYarnCommand ? 'yarn' : 'npm';

debug('Run npm install with options %s', mgrOptions);
// buildManagerOptions overrides all default
if (typeof this.buildManagerOptions === 'function') {
mgrOptions = this.buildManagerOptions(depSet);

let cmd = this.useYarnCommand ? 'yarn' : 'npm';
if (this.useYarnCommand) {
if (mgrOptions.indexOf('--no-lockfile') === -1) {
mgrOptions = mgrOptions.concat(['--no-lockfile']);
if (!Array.isArray(mgrOptions)) {
throw new Error('buildManagerOptions must return an array of options');
}
// npm warns on incompatible engines
// yarn errors, not a good experience
if (mgrOptions.indexOf('--ignore-engines') === -1) {
mgrOptions = mgrOptions.concat(['--ignore-engines']);
} else {
if (this.useYarnCommand) {
if (mgrOptions.indexOf('--no-lockfile') === -1) {
mgrOptions = mgrOptions.concat(['--no-lockfile']);
}
// npm warns on incompatible engines
// yarn errors, not a good experience
if (mgrOptions.indexOf('--ignore-engines') === -1) {
mgrOptions = mgrOptions.concat(['--ignore-engines']);
}
} else if (mgrOptions.indexOf('--no-shrinkwrap') === -1) {
mgrOptions = mgrOptions.concat(['--no-shrinkwrap']);
}
} else if (mgrOptions.indexOf('--no-shrinkwrap') === -1) {
mgrOptions = mgrOptions.concat(['--no-shrinkwrap']);
}

debug('Run npm/yarn install with options %s', mgrOptions);

return this.run(cmd, [].concat(['install'], mgrOptions), { cwd: this.cwd }).then(() => {
if (!adapter.useYarnCommand) {
return adapter.run('npm', ['--version'], { cwd: this.cwd, stdio: 'pipe' }).then((res) => {
Expand Down Expand Up @@ -171,6 +197,21 @@ module.exports = CoreObject.extend({
path.join(this.cwd, this.nodeModules), { clobber: true }),
];

if (fs.existsSync(path.join(this.cwd, this.yarnLockBackupFileName))) {
restoreTasks.push(copy(path.join(this.cwd, this.yarnLockBackupFileName),
path.join(this.cwd, this.yarnLock)));
}

if (fs.existsSync(path.join(this.cwd, this.npmShrinkWrapBackupFileName))) {
restoreTasks.push(copy(path.join(this.cwd, this.npmShrinkWrapBackupFileName),
path.join(this.cwd, this.npmShrinkWrap)));
}

if (fs.existsSync(path.join(this.cwd, this.packageLockBackupFileName))) {
restoreTasks.push(copy(path.join(this.cwd, this.packageLockBackupFileName),
path.join(this.cwd, this.packageLock)));
}

return RSVP.all(restoreTasks);
},
_backupOriginalDependencies() {
Expand All @@ -184,6 +225,21 @@ module.exports = CoreObject.extend({
copy(path.join(this.cwd, this.nodeModules),
path.join(this.cwd, this.nodeModulesBackupLocation), { clobber: true })];

if (fs.existsSync(path.join(this.cwd, this.yarnLock))) {
backupTasks.push(copy(path.join(this.cwd, this.yarnLock),
path.join(this.cwd, this.yarnLockBackupFileName)));
}

if (fs.existsSync(path.join(this.cwd, this.npmShrinkWrap))) {
backupTasks.push(copy(path.join(this.cwd, this.npmShrinkWrap),
path.join(this.cwd, this.npmShrinkWrapBackupFileName)));
}

if (fs.existsSync(path.join(this.cwd, this.packageLock))) {
backupTasks.push(copy(path.join(this.cwd, this.packageLock),
path.join(this.cwd, this.packageLockBackupFileName)));
}

return RSVP.all(backupTasks);
},
});
32 changes: 21 additions & 11 deletions lib/dependency-manager-adapters/workspace.js
Expand Up @@ -47,6 +47,7 @@ module.exports = CoreObject.extend({
run: this.run,
managerOptions: this.managerOptions,
useYarnCommand: true,
buildManagerOptions: this.buildManagerOptions,
});
});

Expand All @@ -59,7 +60,7 @@ module.exports = CoreObject.extend({
adapter.applyDependencySet(depSet);
});

return this._install().then(() => {
return this._install(depSet).then(() => {
let deps = extend({}, depSet.dependencies || {}, depSet.devDependencies || {});
let currentDeps = Object.keys(deps).map((dep) => {
return {
Expand All @@ -80,20 +81,29 @@ module.exports = CoreObject.extend({
return RSVP.all(this._packageAdapters.map(adapter => adapter.cleanup()));
},

_install() {
_install(depSet) {
let mgrOptions = this.managerOptions || [];

debug('Run yarn install with options %s', mgrOptions);

if (mgrOptions.indexOf('--no-lockfile') === -1) {
mgrOptions = mgrOptions.concat(['--no-lockfile']);
}
// npm warns on incompatible engines
// yarn errors, not a good experience
if (mgrOptions.indexOf('--ignore-engines') === -1) {
mgrOptions = mgrOptions.concat(['--ignore-engines']);
// 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.indexOf('--no-lockfile') === -1) {
mgrOptions = mgrOptions.concat(['--no-lockfile']);
}
// npm warns on incompatible engines
// yarn errors, not a good experience
if (mgrOptions.indexOf('--ignore-engines') === -1) {
mgrOptions = mgrOptions.concat(['--ignore-engines']);
}
}

debug('Run yarn install with options %s', mgrOptions);

return this.run('yarn', [].concat(['install'], mgrOptions), { cwd: this.cwd });
},

Expand Down
5 changes: 3 additions & 2 deletions lib/utils/dependency-manager-adapter-factory.js
Expand Up @@ -31,11 +31,12 @@ module.exports = {
new WorkspaceAdapter({
cwd: root,
managerOptions: config.npmOptions,
useYarnCommand: config.useYarn
useYarnCommand: config.useYarn,
buildManagerOptions: config.buildManagerOptions
})
);
} else if (hasNpm || hasBower) {
adapters.push(new NpmAdapter({ cwd: root, managerOptions: config.npmOptions, useYarnCommand: config.useYarn }));
adapters.push(new NpmAdapter({ cwd: root, managerOptions: config.npmOptions, useYarnCommand: config.useYarn, buildManagerOptions: config.buildManagerOptions }));
adapters.push(new BowerAdapter({ cwd: root, managerOptions: config.bowerOptions }));
}

Expand Down
114 changes: 114 additions & 0 deletions test/dependency-manager-adapters/npm-adapter-test.js
Expand Up @@ -39,6 +39,24 @@ describe('npmAdapter', () => {
assertFileContainsJSON(path.join(tmpdir, '.node_modules.ember-try/prove-it.json'), { originalNodeModules: true });
});
});

it('backs up the yarn.lock file, npm-shrinkwrap.json and package-lock.json if they exist', () => {
fs.mkdirSync('node_modules');
writeJSONFile('node_modules/prove-it.json', { originalNodeModules: true });
writeJSONFile('package.json', { originalPackageJSON: true });
writeJSONFile('yarn.lock', { originalYarnLock: true });
writeJSONFile('npm-shrinkwrap.json', { originalNpmShrinkWrap: true });
writeJSONFile('package-lock.json', { originalPackageLock: true });
return new NpmAdapter({
cwd: tmpdir,
}).setup().then(() => {
assertFileContainsJSON(path.join(tmpdir, 'package.json.ember-try'), { originalPackageJSON: true });
assertFileContainsJSON(path.join(tmpdir, '.node_modules.ember-try/prove-it.json'), { originalNodeModules: true });
assertFileContainsJSON(path.join(tmpdir, 'yarn.lock.ember-try'), { originalYarnLock: true });
assertFileContainsJSON(path.join(tmpdir, 'npm-shrinkwrap.json.ember-try'), { originalNpmShrinkWrap: true });
assertFileContainsJSON(path.join(tmpdir, 'package-lock.json.ember-try'), { originalPackageLock: true });
});
});
});

describe('#_install', () => {
Expand Down Expand Up @@ -127,6 +145,46 @@ describe('npmAdapter', () => {
expect(runCount).to.equal(2);
});
});

it('uses buildManagerOptions for npm commands', () => {
writeJSONFile('package.json', fixturePackage);
let runCount = 0;
let stubbedRun = generateMockRun([{
command: 'npm install --flat',
callback() {
runCount++;
return RSVP.resolve();
},
}, {
command: 'npm --version',
callback() {
runCount++;
return RSVP.resolve({stdout: '5.7.1'});
},
}], { allowPassthrough: false });

return new NpmAdapter({
cwd: tmpdir,
run: stubbedRun,
buildManagerOptions: function() {
return ['--flat'];
},
})._install().then(() => {
expect(runCount).to.equal(2, 'npm install should run with buildManagerOptions');
});
});

it('throws an error if buildManagerOptions does not return an array', () => {
expect(() => {
new NpmAdapter({
cwd: tmpdir,
run: () => {},
buildManagerOptions: function() {
return 'string';
},
})._install()
}).to.throw(/buildManagerOptions must return an array of options/);
});
});

describe('with yarn', () => {
Expand Down Expand Up @@ -171,6 +229,42 @@ describe('npmAdapter', () => {
expect(runCount).to.equal(1, 'Only yarn install should run with manager options');
});
});

it('uses buildManagerOptions for yarn commands', () => {
writeJSONFile('package.json', fixturePackage);
let runCount = 0;
let stubbedRun = generateMockRun([{
command: 'yarn install --flat',
callback() {
runCount++;
return RSVP.resolve();
},
}], { allowPassthrough: false });

return new NpmAdapter({
cwd: tmpdir,
run: stubbedRun,
useYarnCommand: true,
buildManagerOptions: function() {
return ['--flat'];
},
})._install().then(() => {
expect(runCount).to.equal(1, 'Only yarn install should run with buildManagerOptions');
});
});

it('throws an error if buildManagerOptions does not return an array', () => {
expect(() => {
new NpmAdapter({
cwd: tmpdir,
run: () => {},
useYarnCommand: true,
buildManagerOptions: function() {
return 'string';
},
})._install()
}).to.throw(/buildManagerOptions must return an array of options/);
});
});
});

Expand All @@ -185,6 +279,26 @@ describe('npmAdapter', () => {
assertFileContainsJSON(path.join(tmpdir, 'node_modules/prove-it.json'), { originalNodeModules: true });
});
});

it('replaces the yarn.lock, npm-shrinkwrap.json and package-lock.json with the backed up version if they exist', () => {
writeJSONFile('package.json.ember-try', { originalPackageJSON: true });
writeJSONFile('package.json', { originalPackageJSON: false });
fs.mkdirSync('.node_modules.ember-try');
writeJSONFile('.node_modules.ember-try/prove-it.json', { originalNodeModules: true });
writeJSONFile('yarn.lock.ember-try', { originalYarnLock: true });
writeJSONFile('yarn.lock', { originalYarnLock: false });
writeJSONFile('npm-shrinkwrap.json.ember-try', { originalNpmShrinkWrap: true });
writeJSONFile('npm-shrinkwrap.json', { originalNpmShrinkWrap: false });
writeJSONFile('package-lock.json.ember-try', { originalPackageLock: true });
writeJSONFile('package-lock.json', { originalPackageLock: false });
return new NpmAdapter({ cwd: tmpdir })._restoreOriginalDependencies().then(() => {
assertFileContainsJSON(path.join(tmpdir, 'package.json'), { originalPackageJSON: true });
assertFileContainsJSON(path.join(tmpdir, 'node_modules/prove-it.json'), { originalNodeModules: true });
assertFileContainsJSON(path.join(tmpdir, 'yarn.lock'), { originalYarnLock: true });
assertFileContainsJSON(path.join(tmpdir, 'npm-shrinkwrap.json'), { originalNpmShrinkWrap: true });
assertFileContainsJSON(path.join(tmpdir, 'package-lock.json'), { originalPackageLock: true });
});
});
});

describe('#_packageJSONForDependencySet', () => {
Expand Down

0 comments on commit 0f887ba

Please sign in to comment.