diff --git a/lib/dependency-manager-adapters/base.js b/lib/dependency-manager-adapters/base.js new file mode 100644 index 00000000..cb95f5fa --- /dev/null +++ b/lib/dependency-manager-adapters/base.js @@ -0,0 +1,177 @@ +'use strict'; + +const chalk = require('chalk'); +const debug = require('debug'); +const { get, set } = require('es-toolkit/compat'); +const fs = require('fs-extra'); +const path = require('node:path'); +const semver = require('semver'); +const Backup = require('../utils/backup'); +const { LOCKFILE, PACKAGE_JSON } = require('../utils/package-managers'); + +class BaseAdapter { + configKey = 'npm'; + defaultInstallOptions = []; + lockfile = ''; + name = ''; + overridesKey = ''; + + backup = null; + debugFunction = null; + + constructor(options) { + this.backup = new Backup({ cwd: options.cwd }); + this.buildManagerOptions = options.buildManagerOptions; + this.cwd = options.cwd; + this.managerOptions = options.managerOptions; + this.run = options.run ?? require('../utils/run'); + } + + debug(...args) { + if (this.debugFunction === null) { + this.debugFunction = debug(`ember-try:dependency-manager-adapter:${this.name}`); + } + + this.debugFunction(...args); + } + + async setup(options = {}) { + this._checkForDifferentLockfiles(options.ui); + + await this.backup.addFiles([PACKAGE_JSON, this.lockfile]); + } + + async changeToDependencySet(dependencySet) { + await this.applyDependencySet(dependencySet); + await this._install(dependencySet); + + const dependencies = { + ...dependencySet.dependencies, + ...dependencySet.devDependencies, + }; + + const currentDependencies = Object.keys(dependencies).map((name) => ({ + name, + packageManager: this.name, + versionExpected: dependencies[name], + versionSeen: this._findCurrentVersionOf(name), + })); + + this.debug('Switched to dependencies: \n', currentDependencies); + + return currentDependencies; + } + + async applyDependencySet(dependencySet) { + if (dependencySet === undefined) { + return; + } + + this.debug('Changing to dependency set: %s', JSON.stringify(dependencySet)); + + const oldPackageJSON = JSON.parse(fs.readFileSync(this.backup.pathForFile(PACKAGE_JSON))); + const newPackageJSON = this._packageJSONForDependencySet(oldPackageJSON, dependencySet); + + this.debug('Write package.json with: \n', JSON.stringify(newPackageJSON)); + + fs.writeFileSync(path.join(this.cwd, PACKAGE_JSON), JSON.stringify(newPackageJSON, null, 2)); + } + + async cleanup() { + try { + await this.backup.restoreFiles([PACKAGE_JSON, this.lockfile]); + await this.backup.cleanUp(); + await this._install(); + } catch (error) { + console.error('Error cleaning up scenario:', error); + } + } + + _checkForDifferentLockfiles(ui) { + for (const packageManager in LOCKFILE) { + const lockfile = LOCKFILE[packageManager]; + + if (lockfile === this.lockfile) { + continue; + } + + try { + if (fs.statSync(path.join(this.cwd, lockfile)).isFile()) { + ui.writeLine( + chalk.yellow( + `Detected a \`${lockfile}\` file. Add \`packageManager: '${packageManager}'\` to your \`config/ember-try.js\` configuration file if you want to use ${packageManager} to install dependencies.`, + ), + ); + } + } catch { + // Move along. + } + } + } + + _findCurrentVersionOf(name) { + const filename = path.join(this.cwd, 'node_modules', name, PACKAGE_JSON); + + if (fs.existsSync(filename)) { + return JSON.parse(fs.readFileSync(filename)).version; + } else { + return null; + } + } + + async _install(dependencySet) { + let managerOptions = this.managerOptions || []; + + if (typeof this.buildManagerOptions === 'function') { + managerOptions = this.buildManagerOptions(dependencySet); + + if (Array.isArray(managerOptions) === false) { + throw new Error('buildManagerOptions must return an array of options'); + } + } else if (this.defaultInstallOptions.length) { + for (const option of this.defaultInstallOptions) { + if (managerOptions.includes(option) === false) { + managerOptions.push(option); + } + } + } + + this.debug(`Running ${this.name} install with options %s`, managerOptions); + + await this.run(this.name, ['install', ...managerOptions], { cwd: this.cwd }); + } + + _packageJSONForDependencySet(packageJSON, dependencySet) { + this._overridePackageJSONDependencies(packageJSON, dependencySet, 'dependencies'); + this._overridePackageJSONDependencies(packageJSON, dependencySet, 'devDependencies'); + this._overridePackageJSONDependencies(packageJSON, dependencySet, 'peerDependencies'); + this._overridePackageJSONDependencies(packageJSON, dependencySet, 'ember'); + this._overridePackageJSONDependencies(packageJSON, dependencySet, this.overridesKey); + + return packageJSON; + } + + _overridePackageJSONDependencies(packageJSON, dependencySet, kindOfDependency) { + if (get(dependencySet, kindOfDependency) === undefined) { + return; + } + + let packageNames = Object.keys(get(dependencySet, kindOfDependency)); + + for (let packageName of packageNames) { + let version = get(dependencySet, `${kindOfDependency}.${packageName}`); + + if (version === null) { + delete get(packageJSON, kindOfDependency)[packageName]; + } else { + set(packageJSON, `${kindOfDependency}.${packageName}`, version); + + if (semver.prerelease(version) || /^https*:\/\/.*\.tg*z/.test(version)) { + set(packageJSON, `${this.overridesKey}.${packageName}`, `$${packageName}`); + } + } + } + } +} + +module.exports = { BaseAdapter }; diff --git a/lib/dependency-manager-adapters/npm.js b/lib/dependency-manager-adapters/npm.js index 14312d2a..47d5e8c1 100644 --- a/lib/dependency-manager-adapters/npm.js +++ b/lib/dependency-manager-adapters/npm.js @@ -1,195 +1,11 @@ 'use strict'; -const fs = require('fs-extra'); -const path = require('path'); -const debug = require('debug')('ember-try:dependency-manager-adapter:npm'); -const chalk = require('chalk'); -const semver = require('semver'); -const Backup = require('../utils/backup'); - -module.exports = class { - configKey = 'npm'; - packageJSON = 'package.json'; - packageLock = 'package-lock.json'; - useYarnCommand = false; - yarnLock = 'yarn.lock'; - - constructor(options) { - this.buildManagerOptions = options.buildManagerOptions; - this.cwd = options.cwd; - this.managerOptions = options.managerOptions; - this.run = options.run || require('../utils/run'); - this.useYarnCommand = options.useYarnCommand ?? false; - - this.backup = new Backup({ cwd: this.cwd }); - } - - async setup(options) { - if (!options) { - options = {}; - } - - this._runYarnCheck(options.ui); - - return await this._backupOriginalDependencies(); - } - - async changeToDependencySet(depSet) { - 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: this.useYarnCommand ? 'yarn' : 'npm', - }; - }); - - debug('Switched to dependencies: \n', currentDeps); - - return currentDeps; - } - - async cleanup() { - try { - await this._restoreOriginalDependencies(); - } catch (e) { - console.log('Error cleaning up npm scenario:', e); // eslint-disable-line no-console - } - } - - _runYarnCheck(ui) { - if (!this.useYarnCommand) { - try { - if (fs.statSync(path.join(this.cwd, this.yarnLock)).isFile()) { - ui.writeLine( - chalk.yellow( - "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.", - ), - ); - } - } catch (e) { - // If no yarn.lock is found, no need to warn. - } - } - } - - _findCurrentVersionOf(packageName) { - let filename = path.join(this.cwd, 'node_modules', packageName, this.packageJSON); - if (fs.existsSync(filename)) { - return JSON.parse(fs.readFileSync(filename)).version; - } else { - return null; - } - } - - async _install(depSet) { - let mgrOptions = this.managerOptions || []; - let cmd = this.useYarnCommand ? 'yarn' : 'npm'; - - // 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 (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-package-lock') === -1) { - mgrOptions = mgrOptions.concat(['--no-package-lock']); - } - } - - debug('Run npm/yarn install with options %s', mgrOptions); - - await this.run(cmd, [].concat(['install'], mgrOptions), { cwd: this.cwd }); - } - - applyDependencySet(depSet) { - debug('Changing to dependency set: %s', JSON.stringify(depSet)); - - if (!depSet) { - return; - } - - let backupPackageJSON = this.backup.pathForFile(this.packageJSON); - let packageJSONFile = path.join(this.cwd, this.packageJSON); - 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)); - } - - _packageJSONForDependencySet(packageJSON, depSet) { - this._overridePackageJSONDependencies(packageJSON, depSet, 'dependencies'); - this._overridePackageJSONDependencies(packageJSON, depSet, 'devDependencies'); - this._overridePackageJSONDependencies(packageJSON, depSet, 'peerDependencies'); - this._overridePackageJSONDependencies(packageJSON, depSet, 'ember'); - - if (this.useYarnCommand) { - this._overridePackageJSONDependencies(packageJSON, depSet, 'resolutions'); - } else { - this._overridePackageJSONDependencies(packageJSON, depSet, 'overrides'); - } - - return packageJSON; - } - - _overridePackageJSONDependencies(packageJSON, depSet, kindOfDependency) { - if (!depSet[kindOfDependency]) { - return; - } - - let packageNames = Object.keys(depSet[kindOfDependency]); - - packageNames.forEach((packageName) => { - if (!packageJSON[kindOfDependency]) { - packageJSON[kindOfDependency] = {}; - } - - let version = depSet[kindOfDependency][packageName]; - if (version === null) { - delete packageJSON[kindOfDependency][packageName]; - } else { - packageJSON[kindOfDependency][packageName] = version; - - // in npm we need to always add an override if the version is a pre-release - if ( - !this.useYarnCommand && - (semver.prerelease(version) || /^https*:\/\/.*\.tg*z/.test(version)) - ) { - if (!packageJSON.overrides) { - packageJSON.overrides = {}; - } - - packageJSON.overrides[packageName] = `$${packageName}`; - } - } - }); - } - - async _restoreOriginalDependencies() { - await this.backup.restoreFiles([this.packageJSON, this.packageLock, this.yarnLock]); - await this.backup.cleanUp(); - await this._install(); - } - - async _backupOriginalDependencies() { - await this.backup.addFiles([this.packageJSON, this.packageLock, this.yarnLock]); - } +const { LOCKFILE } = require('../utils/package-managers'); +const { BaseAdapter } = require('./base'); + +module.exports = class NpmAdapter extends BaseAdapter { + defaultInstallOptions = ['--no-package-lock']; + lockfile = LOCKFILE.npm; + name = 'npm'; + overridesKey = 'overrides'; }; diff --git a/lib/dependency-manager-adapters/pnpm.js b/lib/dependency-manager-adapters/pnpm.js index 81517150..891f0053 100644 --- a/lib/dependency-manager-adapters/pnpm.js +++ b/lib/dependency-manager-adapters/pnpm.js @@ -1,70 +1,18 @@ 'use strict'; -const fs = require('fs-extra'); -const path = require('path'); -const debug = require('debug')('ember-try:dependency-manager-adapter:pnpm'); -const Backup = require('../utils/backup'); -const semverLt = require('semver/functions/lt'); -const semverGte = require('semver/functions/gte'); +const semver = require('semver'); +const { LOCKFILE } = require('../utils/package-managers'); +const { BaseAdapter } = require('./base'); -const PACKAGE_JSON = 'package.json'; -const PNPM_LOCKFILE = 'pnpm-lock.yaml'; +module.exports = class PnpmAdapter extends BaseAdapter { + defaultInstallOptions = ['--no-lockfile', '--ignore-scripts']; + lockfile = LOCKFILE.pnpm; + name = 'pnpm'; + overridesKey = 'pnpm.overrides'; -module.exports = class { - // This still needs to be `npm` because we're still reading the dependencies - // from the `npm` key of the ember-try config. - configKey = 'npm'; - - constructor(options) { - this.buildManagerOptions = options.buildManagerOptions; - this.cwd = options.cwd; - this.managerOptions = options.managerOptions; - this.run = options.run || require('../utils/run'); - - this.backup = new Backup({ cwd: this.cwd }); - } - - async setup() { + async setup(options) { await this._throwOnResolutionMode(); - await this.backup.addFiles([PACKAGE_JSON, PNPM_LOCKFILE]); - } - - 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 { - await this.backup.restoreFiles([PACKAGE_JSON, PNPM_LOCKFILE]); - await this.backup.cleanUp(); - 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; - } + await super.setup(options); } /** @@ -103,92 +51,14 @@ module.exports = class { _isResolutionModeWrong(versionStr, resolutionMode) { // The `resolution-mode` is not set explicitly, and the current pnpm version makes it default // to `lowest-direct` - if (!resolutionMode.length && semverGte(versionStr, '8.0.0') && semverLt(versionStr, '8.7.0')) { + if ( + !resolutionMode.length && + semver.gte(versionStr, '8.0.0') && + semver.lt(versionStr, '8.7.0') + ) { return true; } return false; } - - 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'); - } - - if (!mgrOptions.includes('--ignore-scripts')) { - mgrOptions.push('--ignore-scripts'); - } - } - - // 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 = this.backup.pathForFile(PACKAGE_JSON); - 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. - - await this.backup.restoreFile(PNPM_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; - } - } - } }; diff --git a/lib/dependency-manager-adapters/workspace.js b/lib/dependency-manager-adapters/workspace.js index c9a3bb73..38c03457 100644 --- a/lib/dependency-manager-adapters/workspace.js +++ b/lib/dependency-manager-adapters/workspace.js @@ -4,25 +4,23 @@ const fs = require('fs-extra'); const path = require('path'); const debug = require('debug')('ember-try:dependency-manager-adapter:workspaces'); const walkSync = require('walk-sync'); - -const NpmAdapter = require('./npm'); +const { PACKAGE_JSON } = require('../utils/package-managers'); +const YarnAdapter = require('./yarn'); module.exports = class { - packageJSON = 'package.json'; - constructor(options) { this.buildManagerOptions = options.buildManagerOptions; this.cwd = options.cwd; this.managerOptions = options.managerOptions; this.run = options.run || require('../utils/run'); - this.useYarnCommand = options.useYarnCommand ?? false; - if (!this.useYarnCommand) { + if (options.packageManager !== 'yarn') { throw new Error( 'workspaces are currently only supported by Yarn, you must set `packageManager` to `yarn`', ); } - let packageJSON = JSON.parse(fs.readFileSync(this.packageJSON)); + + let packageJSON = JSON.parse(fs.readFileSync(PACKAGE_JSON)); let workspaceGlobs; if (Array.isArray(packageJSON.workspaces)) { @@ -47,21 +45,16 @@ module.exports = class { }); this._packageAdapters = workspacePaths.map((workspacePath) => { - return new NpmAdapter({ + return new YarnAdapter({ cwd: workspacePath, run: this.run, managerOptions: this.managerOptions, - useYarnCommand: true, buildManagerOptions: this.buildManagerOptions, }); }); } setup(options) { - if (!options) { - options = {}; - } - return Promise.all(this._packageAdapters.map((adapter) => adapter.setup(options))); } diff --git a/lib/dependency-manager-adapters/yarn.js b/lib/dependency-manager-adapters/yarn.js new file mode 100644 index 00000000..5e775e16 --- /dev/null +++ b/lib/dependency-manager-adapters/yarn.js @@ -0,0 +1,11 @@ +'use strict'; + +const { LOCKFILE } = require('../utils/package-managers'); +const { BaseAdapter } = require('./base'); + +module.exports = class YarnAdapter extends BaseAdapter { + defaultInstallOptions = ['--no-lockfile', '--ignore-engines']; + lockfile = LOCKFILE.yarn; + name = 'yarn'; + overridesKey = 'resolutions'; +}; diff --git a/lib/utils/dependency-manager-adapter-factory.js b/lib/utils/dependency-manager-adapter-factory.js index 340710b4..f4b57543 100644 --- a/lib/utils/dependency-manager-adapter-factory.js +++ b/lib/utils/dependency-manager-adapter-factory.js @@ -3,6 +3,7 @@ const NpmAdapter = require('../dependency-manager-adapters/npm'); const PnpmAdapter = require('../dependency-manager-adapters/pnpm'); const WorkspaceAdapter = require('../dependency-manager-adapters/workspace'); +const YarnAdapter = require('../dependency-manager-adapters/yarn'); module.exports = { generateFromConfig(config, root) { @@ -23,7 +24,7 @@ module.exports = { new WorkspaceAdapter({ cwd: root, managerOptions: config.npmOptions, - useYarnCommand: config.packageManager === 'yarn', + packageManager: config.packageManager, buildManagerOptions: config.buildManagerOptions, }), ); @@ -35,12 +36,19 @@ module.exports = { buildManagerOptions: config.buildManagerOptions, }), ); + } else if (config.packageManager === 'yarn') { + adapters.push( + new YarnAdapter({ + cwd: root, + managerOptions: config.npmOptions, + buildManagerOptions: config.buildManagerOptions, + }), + ); } else if (hasNpm) { adapters.push( new NpmAdapter({ cwd: root, managerOptions: config.npmOptions, - useYarnCommand: config.packageManager === 'yarn', buildManagerOptions: config.buildManagerOptions, }), ); diff --git a/lib/utils/package-managers.js b/lib/utils/package-managers.js new file mode 100644 index 00000000..ab17cf24 --- /dev/null +++ b/lib/utils/package-managers.js @@ -0,0 +1,9 @@ +const LOCKFILE = { + npm: 'package-lock.json', + pnpm: 'pnpm-lock.yaml', + yarn: 'yarn.lock', +}; + +const PACKAGE_JSON = 'package.json'; + +module.exports = { LOCKFILE, PACKAGE_JSON }; diff --git a/package.json b/package.json index 404f1dc6..5ec8c00a 100644 --- a/package.json +++ b/package.json @@ -38,6 +38,7 @@ "cli-table3": "^0.6.5", "debug": "^4.4.0", "ember-try-config": "^4.0.0", + "es-toolkit": "^1.29.0", "execa": "^4.1.0", "fs-extra": "^6.0.1", "resolve": "^1.22.8", diff --git a/test/dependency-manager-adapters/npm-adapter-test.js b/test/dependency-manager-adapters/npm-adapter-test.js index b124b717..f70c1123 100644 --- a/test/dependency-manager-adapters/npm-adapter-test.js +++ b/test/dependency-manager-adapters/npm-adapter-test.js @@ -14,7 +14,7 @@ let root = process.cwd(); let tmproot = path.join(root, 'tmp'); let tmpdir; -describe('npmAdapter', () => { +describe('npm Adapter', () => { beforeEach(() => { tmpdir = tmp.in(tmproot); process.chdir(tmpdir); @@ -26,10 +26,9 @@ describe('npmAdapter', () => { }); describe('#setup', () => { - it('backs up the `package.json`, `package-lock.json` and `yarn.lock` files if they exist', async () => { + it('backs up the `package.json` and `package-lock.json` files if they exist', async () => { writeJSONFile('package.json', { originalPackageJSON: true }); writeJSONFile('package-lock.json', { originalPackageLock: true }); - writeJSONFile('yarn.lock', { originalYarnLock: true }); let adapter = new NpmAdapter({ cwd: tmpdir }); @@ -41,225 +40,114 @@ describe('npmAdapter', () => { assertFileContainsJSON(adapter.backup.pathForFile('package-lock.json'), { originalPackageLock: true, }); - assertFileContainsJSON(adapter.backup.pathForFile('yarn.lock'), { - originalYarnLock: true, - }); }); }); describe('#_install', () => { - describe('without yarn', () => { - it('runs npm install', async () => { - writeJSONFile('package.json', fixturePackage); - let runCount = 0; - let stubbedRun = generateMockRun( - [ - { - command: 'npm install --no-package-lock', - callback(command, args, opts) { - runCount++; - expect(opts).to.have.property('cwd', tmpdir); - return Promise.resolve(); - }, + it('runs npm install', async () => { + writeJSONFile('package.json', fixturePackage); + let runCount = 0; + let stubbedRun = generateMockRun( + [ + { + command: 'npm install --no-package-lock', + callback(command, args, opts) { + runCount++; + expect(opts).to.have.property('cwd', tmpdir); + return Promise.resolve(); }, - ], - { allowPassthrough: false }, - ); - - let adapter = new NpmAdapter({ - cwd: tmpdir, - run: stubbedRun, - }); + }, + ], + { allowPassthrough: false }, + ); - await adapter._install(); - expect(runCount).to.equal(1); + let adapter = new NpmAdapter({ + cwd: tmpdir, + run: stubbedRun, }); - it('uses managerOptions for npm commands', async () => { - writeJSONFile('package.json', fixturePackage); - let runCount = 0; - let stubbedRun = generateMockRun( - [ - { - command: 'npm install --no-optional --no-package-lock', - callback() { - runCount++; - return Promise.resolve(); - }, - }, - ], - { allowPassthrough: false }, - ); - - let adapter = new NpmAdapter({ - cwd: tmpdir, - run: stubbedRun, - managerOptions: ['--no-optional'], - }); - - await adapter._install(); - expect(runCount).to.equal(1); - }); + await adapter._install(); + expect(runCount).to.equal(1); + }); - it('uses buildManagerOptions for npm commands', async () => { - writeJSONFile('package.json', fixturePackage); - let runCount = 0; - let stubbedRun = generateMockRun( - [ - { - command: 'npm install --flat', - callback() { - runCount++; - return Promise.resolve(); - }, + it('uses managerOptions for npm commands', async () => { + writeJSONFile('package.json', fixturePackage); + let runCount = 0; + let stubbedRun = generateMockRun( + [ + { + command: 'npm install --no-optional --no-package-lock', + callback() { + runCount++; + return Promise.resolve(); }, - ], - { allowPassthrough: false }, - ); - - let adapter = new NpmAdapter({ - cwd: tmpdir, - run: stubbedRun, - buildManagerOptions: function () { - return ['--flat']; }, - }); + ], + { allowPassthrough: false }, + ); - await adapter._install(); - expect(runCount).to.equal(1, 'npm install should run with buildManagerOptions'); + let adapter = new NpmAdapter({ + cwd: tmpdir, + run: stubbedRun, + managerOptions: ['--no-optional'], }); - it('throws an error if buildManagerOptions does not return an array', async () => { - let error; - try { - let adapter = new NpmAdapter({ - cwd: tmpdir, - run: () => {}, - buildManagerOptions: function () { - return 'string'; - }, - }); - - await adapter._install(); - } catch (e) { - error = e; - } - - expect(error.message).to.include('buildManagerOptions must return an array of options'); - }); + await adapter._install(); + expect(runCount).to.equal(1); }); - describe('with yarn', () => { - it('runs yarn install', async () => { - writeJSONFile('package.json', fixturePackage); - let runCount = 0; - let stubbedRun = generateMockRun( - [ - { - command: 'yarn install --no-lockfile --ignore-engines', - callback(command, args, opts) { - runCount++; - expect(opts).to.have.property('cwd', tmpdir); - return Promise.resolve(); - }, - }, - ], - { allowPassthrough: false }, - ); - - let adapter = new NpmAdapter({ - cwd: tmpdir, - run: stubbedRun, - useYarnCommand: true, - }); - - await adapter._install(); - expect(runCount).to.equal(1, 'Only yarn install should run'); - }); - - it('uses managerOptions for yarn commands', async () => { - writeJSONFile('package.json', fixturePackage); - let runCount = 0; - let stubbedRun = generateMockRun( - [ - { - command: 'yarn install --flat --no-lockfile --ignore-engines', - callback() { - runCount++; - return Promise.resolve(); - }, + it('uses buildManagerOptions for npm commands', async () => { + writeJSONFile('package.json', fixturePackage); + let runCount = 0; + let stubbedRun = generateMockRun( + [ + { + command: 'npm install --flat', + callback() { + runCount++; + return Promise.resolve(); }, - ], - { allowPassthrough: false }, - ); - - let adapter = new NpmAdapter({ - cwd: tmpdir, - run: stubbedRun, - useYarnCommand: true, - managerOptions: ['--flat'], - }); + }, + ], + { allowPassthrough: false }, + ); - await adapter._install(); - expect(runCount).to.equal(1, 'Only yarn install should run with manager options'); + let adapter = new NpmAdapter({ + cwd: tmpdir, + run: stubbedRun, + buildManagerOptions: function () { + return ['--flat']; + }, }); - it('uses buildManagerOptions for yarn commands', async () => { - writeJSONFile('package.json', fixturePackage); - let runCount = 0; - let stubbedRun = generateMockRun( - [ - { - command: 'yarn install --flat', - callback() { - runCount++; - return Promise.resolve(); - }, - }, - ], - { allowPassthrough: false }, - ); + await adapter._install(); + expect(runCount).to.equal(1, 'npm install should run with buildManagerOptions'); + }); + it('throws an error if buildManagerOptions does not return an array', async () => { + let error; + try { let adapter = new NpmAdapter({ cwd: tmpdir, - run: stubbedRun, - useYarnCommand: true, + run: () => {}, buildManagerOptions: function () { - return ['--flat']; + return 'string'; }, }); await adapter._install(); - expect(runCount).to.equal(1, 'Only yarn install should run with buildManagerOptions'); - }); + } catch (e) { + error = e; + } - it('throws an error if buildManagerOptions does not return an array', async () => { - let error; - try { - let adapter = new NpmAdapter({ - cwd: tmpdir, - run: () => {}, - useYarnCommand: true, - buildManagerOptions: function () { - return 'string'; - }, - }); - - await adapter._install(); - } catch (e) { - error = e; - } - - expect(error.message).to.include('buildManagerOptions must return an array of options'); - }); + expect(error.message).to.include('buildManagerOptions must return an array of options'); }); }); - describe('#_restoreOriginalDependencies', () => { - it('restores the `package.json`, `package-lock.json` and `yarn.lock` files if they exist', async () => { + describe('#cleanup', () => { + it('restores the `package.json` and `package-lock.json` files if they exist', async () => { writeJSONFile('package.json', { originalPackageJSON: true }); writeJSONFile('package-lock.json', { originalPackageLock: true }); - writeJSONFile('yarn.lock', { originalYarnLock: true }); let adapter = new NpmAdapter({ cwd: tmpdir }); @@ -268,9 +156,8 @@ describe('npmAdapter', () => { // Simulate modifying the files: writeJSONFile('package.json', { originalPackageJSON: false }); writeJSONFile('package-lock.json', { originalPackageLock: false }); - writeJSONFile('yarn.lock', { originalYarnLock: false }); - await adapter._restoreOriginalDependencies(); + await adapter.cleanup(); assertFileContainsJSON(path.join(tmpdir, 'package.json'), { originalPackageJSON: true, @@ -278,9 +165,6 @@ describe('npmAdapter', () => { assertFileContainsJSON(path.join(tmpdir, 'package-lock.json'), { originalPackageLock: true, }); - assertFileContainsJSON(path.join(tmpdir, 'yarn.lock'), { - originalYarnLock: true, - }); }); it('installs the original node modules again', async () => { @@ -303,7 +187,7 @@ describe('npmAdapter', () => { run: stubbedRun, }); - await adapter._restoreOriginalDependencies(); + await adapter.cleanup(); expect(runCount).to.equal(1); }); @@ -311,21 +195,21 @@ describe('npmAdapter', () => { describe('#_packageJSONForDependencySet', () => { it('changes specified dependency versions', () => { - let npmAdapter = new NpmAdapter({ cwd: tmpdir }); + let adapter = new NpmAdapter({ cwd: tmpdir }); let packageJSON = { devDependencies: { 'ember-feature-flags': '1.0.0' }, dependencies: { 'ember-cli-babel': '5.0.0' }, }; let depSet = { dependencies: { 'ember-cli-babel': '6.0.0' } }; - let resultJSON = npmAdapter._packageJSONForDependencySet(packageJSON, depSet); + let resultJSON = adapter._packageJSONForDependencySet(packageJSON, depSet); expect(resultJSON.dependencies['ember-cli-babel']).to.equal('6.0.0'); }); describe('overrides', () => { it('adds an override if you use a pre-release version for something', () => { - let npmAdapter = new NpmAdapter({ + let adapter = new NpmAdapter({ cwd: tmpdir, }); let packageJSON = { dependencies: { ember: '4.1.4' } }; @@ -333,7 +217,7 @@ describe('npmAdapter', () => { dependencies: { ember: '4.8.0-beta.1' }, }; - let resultJSON = npmAdapter._packageJSONForDependencySet(packageJSON, depSet); + let resultJSON = adapter._packageJSONForDependencySet(packageJSON, depSet); expect(resultJSON).to.deep.equal({ dependencies: { ember: '4.8.0-beta.1' }, @@ -341,25 +225,8 @@ describe('npmAdapter', () => { }); }); - it('does not add an override if you use a pre-release version with yarn', () => { - let npmAdapter = new NpmAdapter({ - cwd: tmpdir, - useYarnCommand: true, - }); - let packageJSON = { dependencies: { ember: '4.1.4' } }; - let depSet = { - dependencies: { ember: '4.8.0-beta.1' }, - }; - - let resultJSON = npmAdapter._packageJSONForDependencySet(packageJSON, depSet); - - expect(resultJSON).to.deep.equal({ - dependencies: { ember: '4.8.0-beta.1' }, - }); - }); - it('adds an override if you specify a version with a link to a .tgz file', () => { - let npmAdapter = new NpmAdapter({ + let adapter = new NpmAdapter({ cwd: tmpdir, }); let packageJSON = { dependencies: { ember: '4.1.4' } }; @@ -367,7 +234,7 @@ describe('npmAdapter', () => { dependencies: { ember: 'https://somesite.com/dependencies/funtime.tgz' }, }; - let resultJSON = npmAdapter._packageJSONForDependencySet(packageJSON, depSet); + let resultJSON = adapter._packageJSONForDependencySet(packageJSON, depSet); expect(resultJSON).to.deep.equal({ dependencies: { ember: 'https://somesite.com/dependencies/funtime.tgz' }, @@ -376,7 +243,7 @@ describe('npmAdapter', () => { }); it('does not add an override if you specify any other kind of link', () => { - let npmAdapter = new NpmAdapter({ + let adapter = new NpmAdapter({ cwd: tmpdir, }); let packageJSON = { dependencies: { ember: '4.1.4' } }; @@ -384,7 +251,7 @@ describe('npmAdapter', () => { dependencies: { ember: 'https://github.com/github/super-secret' }, }; - let resultJSON = npmAdapter._packageJSONForDependencySet(packageJSON, depSet); + let resultJSON = adapter._packageJSONForDependencySet(packageJSON, depSet); expect(resultJSON).to.deep.equal({ dependencies: { ember: 'https://github.com/github/super-secret' }, @@ -394,140 +261,64 @@ describe('npmAdapter', () => { describe('ember property', () => { it('adds the ember property to project package.json', () => { - let npmAdapter = new NpmAdapter({ + let adapter = new NpmAdapter({ cwd: tmpdir, - useYarnCommand: true, }); let packageJSON = {}; let depSet = { ember: { edition: 'octane' }, }; - let resultJSON = npmAdapter._packageJSONForDependencySet(packageJSON, depSet); + let resultJSON = adapter._packageJSONForDependencySet(packageJSON, depSet); expect(resultJSON).to.deep.equal({ ember: { edition: 'octane' } }); }); it('merges the ember property to project package.json', () => { - let npmAdapter = new NpmAdapter({ + let adapter = new NpmAdapter({ cwd: tmpdir, - useYarnCommand: true, }); let packageJSON = { ember: { foo: 'bar' } }; let depSet = { ember: { edition: 'octane' }, }; - let resultJSON = npmAdapter._packageJSONForDependencySet(packageJSON, depSet); + let resultJSON = adapter._packageJSONForDependencySet(packageJSON, depSet); expect(resultJSON).to.deep.equal({ ember: { foo: 'bar', edition: 'octane' } }); }); it('overrides existing fields inside the ember property to project package.json', () => { - let npmAdapter = new NpmAdapter({ + let adapter = new NpmAdapter({ cwd: tmpdir, - useYarnCommand: true, }); let packageJSON = { ember: { edition: 'classic' } }; let depSet = { ember: { edition: 'octane' }, }; - let resultJSON = npmAdapter._packageJSONForDependencySet(packageJSON, depSet); + let resultJSON = adapter._packageJSONForDependencySet(packageJSON, depSet); expect(resultJSON).to.deep.equal({ ember: { edition: 'octane' } }); }); it('removes any items with a null value', () => { - let npmAdapter = new NpmAdapter({ + let adapter = new NpmAdapter({ cwd: tmpdir, - useYarnCommand: true, }); let packageJSON = { ember: { edition: 'octane' } }; let depSet = { ember: { edition: null }, }; - let resultJSON = npmAdapter._packageJSONForDependencySet(packageJSON, depSet); + let resultJSON = adapter._packageJSONForDependencySet(packageJSON, depSet); expect(resultJSON).to.deep.equal({ ember: {} }); }); }); - it('adds a resolution for the specified dependency version', () => { - let npmAdapter = new NpmAdapter({ - cwd: tmpdir, - useYarnCommand: true, - }); - let packageJSON = { - dependencies: { 'ember-cli-babel': '5.0.0' }, - }; - let depSet = { - dependencies: { 'ember-cli-babel': '6.0.0' }, - resolutions: { 'ember-cli-babel': '6.0.0' }, - }; - - let resultJSON = npmAdapter._packageJSONForDependencySet(packageJSON, depSet); - - expect(resultJSON.resolutions['ember-cli-babel']).to.equal('6.0.0'); - }); - - it('removes a dependency from resolutions if its version is null', () => { - let npmAdapter = new NpmAdapter({ - cwd: tmpdir, - useYarnCommand: true, - }); - let packageJSON = { - dependencies: { 'ember-cli-babel': '5.0.0' }, - resolutions: { 'ember-cli-babel': '5.0.0' }, - }; - let depSet = { - dependencies: { 'ember-cli-babel': '6.0.0' }, - resolutions: { 'ember-cli-babel': null }, - }; - - let resultJSON = npmAdapter._packageJSONForDependencySet(packageJSON, depSet); - - expect(resultJSON.resolutions['ember-cli-babel']).to.be.undefined; - }); - - it('doesnt add resolutions if there are none specified', () => { - let npmAdapter = new NpmAdapter({ - cwd: tmpdir, - useYarnCommand: true, - }); - let packageJSON = { - dependencies: { 'ember-cli-babel': '5.0.0' }, - }; - let depSet = { - dependencies: { 'ember-cli-babel': '6.0.0' }, - }; - - let resultJSON = npmAdapter._packageJSONForDependencySet(packageJSON, depSet); - - expect(resultJSON.resolutions).to.be.undefined; - }); - - it('doesnt add resolutions when not using yarn', () => { - let npmAdapter = new NpmAdapter({ - cwd: tmpdir, - useYarnCommand: false, - }); - let packageJSON = { - dependencies: { 'ember-cli-babel': '5.0.0' }, - }; - let depSet = { - dependencies: { 'ember-cli-babel': '6.0.0' }, - resolutions: { 'ember-cli-babel': '6.0.0' }, - }; - - let resultJSON = npmAdapter._packageJSONForDependencySet(packageJSON, depSet); - - expect(resultJSON.resolutions).to.be.undefined; - }); - it('adds a override for the specified dependency version', () => { - let npmAdapter = new NpmAdapter({ + let adapter = new NpmAdapter({ cwd: tmpdir, }); let packageJSON = { @@ -538,13 +329,13 @@ describe('npmAdapter', () => { overrides: { 'ember-cli-babel': '6.0.0' }, }; - let resultJSON = npmAdapter._packageJSONForDependencySet(packageJSON, depSet); + let resultJSON = adapter._packageJSONForDependencySet(packageJSON, depSet); expect(resultJSON.overrides['ember-cli-babel']).to.equal('6.0.0'); }); it('removes a dependency from overrides if its version is null', () => { - let npmAdapter = new NpmAdapter({ + let adapter = new NpmAdapter({ cwd: tmpdir, }); let packageJSON = { @@ -556,13 +347,13 @@ describe('npmAdapter', () => { overrides: { 'ember-cli-babel': null }, }; - let resultJSON = npmAdapter._packageJSONForDependencySet(packageJSON, depSet); + let resultJSON = adapter._packageJSONForDependencySet(packageJSON, depSet); expect(resultJSON.overrides['ember-cli-babel']).to.be.undefined; }); it('doesnt add overrides if there are none specified', () => { - let npmAdapter = new NpmAdapter({ + let adapter = new NpmAdapter({ cwd: tmpdir, }); let packageJSON = { @@ -572,58 +363,40 @@ describe('npmAdapter', () => { dependencies: { 'ember-cli-babel': '6.0.0' }, }; - let resultJSON = npmAdapter._packageJSONForDependencySet(packageJSON, depSet); + let resultJSON = adapter._packageJSONForDependencySet(packageJSON, depSet); expect(resultJSON.resolutions).to.be.undefined; }); - it('doesnt add overrides when using yarn', () => { - let npmAdapter = new NpmAdapter({ - cwd: tmpdir, - useYarnCommand: true, - }); - let packageJSON = { - dependencies: { 'ember-cli-babel': '5.0.0' }, - }; - let depSet = { - dependencies: { 'ember-cli-babel': '6.0.0' }, - overrides: { 'ember-cli-babel': '6.0.0' }, - }; - - let resultJSON = npmAdapter._packageJSONForDependencySet(packageJSON, depSet); - - expect(resultJSON.overrides).to.be.undefined; - }); - it('changes specified npm dev dependency versions', () => { - let npmAdapter = new NpmAdapter({ cwd: tmpdir }); + let adapter = new NpmAdapter({ cwd: tmpdir }); let packageJSON = { devDependencies: { 'ember-feature-flags': '1.0.0' }, dependencies: { 'ember-cli-babel': '5.0.0' }, }; let depSet = { devDependencies: { 'ember-feature-flags': '2.0.1' } }; - let resultJSON = npmAdapter._packageJSONForDependencySet(packageJSON, depSet); + let resultJSON = adapter._packageJSONForDependencySet(packageJSON, depSet); expect(resultJSON.devDependencies['ember-feature-flags']).to.equal('2.0.1'); }); it('changes specified npm peer dependency versions', () => { - let npmAdapter = new NpmAdapter({ cwd: tmpdir }); + let adapter = new NpmAdapter({ cwd: tmpdir }); let packageJSON = { peerDependencies: { 'ember-cli-babel': '5.0.0' } }; let depSet = { peerDependencies: { 'ember-cli-babel': '4.0.0' } }; - let resultJSON = npmAdapter._packageJSONForDependencySet(packageJSON, depSet); + let resultJSON = adapter._packageJSONForDependencySet(packageJSON, depSet); expect(resultJSON.peerDependencies['ember-cli-babel']).to.equal('4.0.0'); }); it('can remove a package', () => { - let npmAdapter = new NpmAdapter({ cwd: tmpdir }); + let adapter = new NpmAdapter({ cwd: tmpdir }); let packageJSON = { devDependencies: { 'ember-feature-flags': '1.0.0' } }; let depSet = { devDependencies: { 'ember-feature-flags': null } }; - let resultJSON = npmAdapter._packageJSONForDependencySet(packageJSON, depSet); + let resultJSON = adapter._packageJSONForDependencySet(packageJSON, depSet); expect(resultJSON.devDependencies).to.not.have.property('ember-feature-flags'); }); diff --git a/test/dependency-manager-adapters/pnpm-adapter-test.js b/test/dependency-manager-adapters/pnpm-adapter-test.js index 569867e7..bd16a80e 100644 --- a/test/dependency-manager-adapters/pnpm-adapter-test.js +++ b/test/dependency-manager-adapters/pnpm-adapter-test.js @@ -270,181 +270,146 @@ describe('pnpm Adapter', () => { describe('#_packageJSONForDependencySet', () => { it('changes specified dependency versions', () => { - let npmAdapter = new PnpmAdapter({ cwd: tmpdir }); + let adapter = new PnpmAdapter({ cwd: tmpdir }); let packageJSON = { devDependencies: { 'ember-feature-flags': '1.0.0' }, dependencies: { 'ember-cli-babel': '5.0.0' }, }; let depSet = { dependencies: { 'ember-cli-babel': '6.0.0' } }; - let resultJSON = npmAdapter._packageJSONForDependencySet(packageJSON, depSet); + let resultJSON = adapter._packageJSONForDependencySet(packageJSON, depSet); expect(resultJSON.dependencies['ember-cli-babel']).to.equal('6.0.0'); }); describe('ember property', () => { it('adds the ember property to project package.json', () => { - let npmAdapter = new PnpmAdapter({ + let adapter = new PnpmAdapter({ cwd: tmpdir, - useYarnCommand: true, }); let packageJSON = {}; let depSet = { ember: { edition: 'octane' }, }; - let resultJSON = npmAdapter._packageJSONForDependencySet(packageJSON, depSet); + let resultJSON = adapter._packageJSONForDependencySet(packageJSON, depSet); expect(resultJSON).to.deep.equal({ ember: { edition: 'octane' } }); }); it('merges the ember property to project package.json', () => { - let npmAdapter = new PnpmAdapter({ + let adapter = new PnpmAdapter({ cwd: tmpdir, - useYarnCommand: true, }); let packageJSON = { ember: { foo: 'bar' } }; let depSet = { ember: { edition: 'octane' }, }; - let resultJSON = npmAdapter._packageJSONForDependencySet(packageJSON, depSet); + let resultJSON = adapter._packageJSONForDependencySet(packageJSON, depSet); expect(resultJSON).to.deep.equal({ ember: { foo: 'bar', edition: 'octane' } }); }); it('overrides existing fields inside the ember property to project package.json', () => { - let npmAdapter = new PnpmAdapter({ + let adapter = new PnpmAdapter({ cwd: tmpdir, - useYarnCommand: true, }); let packageJSON = { ember: { edition: 'classic' } }; let depSet = { ember: { edition: 'octane' }, }; - let resultJSON = npmAdapter._packageJSONForDependencySet(packageJSON, depSet); + let resultJSON = adapter._packageJSONForDependencySet(packageJSON, depSet); expect(resultJSON).to.deep.equal({ ember: { edition: 'octane' } }); }); it('removes any items with a null value', () => { - let npmAdapter = new PnpmAdapter({ + let adapter = new PnpmAdapter({ cwd: tmpdir, - useYarnCommand: true, }); let packageJSON = { ember: { edition: 'octane' } }; let depSet = { ember: { edition: null }, }; - let resultJSON = npmAdapter._packageJSONForDependencySet(packageJSON, depSet); + let resultJSON = adapter._packageJSONForDependencySet(packageJSON, depSet); expect(resultJSON).to.deep.equal({ ember: {} }); }); }); it('adds an override for the specified dependency version', () => { - let npmAdapter = new PnpmAdapter({ + let adapter = new PnpmAdapter({ cwd: tmpdir, - useYarnCommand: true, }); let packageJSON = { dependencies: { 'ember-cli-babel': '5.0.0' }, }; let depSet = { dependencies: { 'ember-cli-babel': '6.0.0' }, - overrides: { 'ember-cli-babel': '6.0.0' }, + pnpm: { + overrides: { 'ember-cli-babel': '6.0.0' }, + }, }; - let resultJSON = npmAdapter._packageJSONForDependencySet(packageJSON, depSet); + let resultJSON = adapter._packageJSONForDependencySet(packageJSON, depSet); - expect(resultJSON.overrides['ember-cli-babel']).to.equal('6.0.0'); + expect(resultJSON.pnpm.overrides['ember-cli-babel']).to.equal('6.0.0'); }); it('removes a dependency from overrides if its version is null', () => { - let npmAdapter = new PnpmAdapter({ - cwd: tmpdir, - useYarnCommand: true, - }); - let packageJSON = { - dependencies: { 'ember-cli-babel': '5.0.0' }, - overrides: { 'ember-cli-babel': '5.0.0' }, - }; - let depSet = { - dependencies: { 'ember-cli-babel': '6.0.0' }, - overrides: { 'ember-cli-babel': null }, - }; - - let resultJSON = npmAdapter._packageJSONForDependencySet(packageJSON, depSet); - - expect(resultJSON.overrides['ember-cli-babel']).to.be.undefined; - }); - - it('doesnt add resolutions if there are none specified', () => { - let npmAdapter = new PnpmAdapter({ - cwd: tmpdir, - useYarnCommand: true, - }); - let packageJSON = { - dependencies: { 'ember-cli-babel': '5.0.0' }, - }; - let depSet = { - dependencies: { 'ember-cli-babel': '6.0.0' }, - }; - - let resultJSON = npmAdapter._packageJSONForDependencySet(packageJSON, depSet); - - expect(resultJSON.resolutions).to.be.undefined; - }); - - it('doesnt add resolutions when not using yarn', () => { - let npmAdapter = new PnpmAdapter({ + let adapter = new PnpmAdapter({ cwd: tmpdir, - useYarnCommand: false, }); let packageJSON = { dependencies: { 'ember-cli-babel': '5.0.0' }, + pnpm: { + overrides: { 'ember-cli-babel': '5.0.0' }, + }, }; let depSet = { dependencies: { 'ember-cli-babel': '6.0.0' }, - resolutions: { 'ember-cli-babel': '6.0.0' }, + pnpm: { + overrides: { 'ember-cli-babel': null }, + }, }; - let resultJSON = npmAdapter._packageJSONForDependencySet(packageJSON, depSet); + let resultJSON = adapter._packageJSONForDependencySet(packageJSON, depSet); - expect(resultJSON.resolutions).to.be.undefined; + expect(resultJSON.pnpm.overrides['ember-cli-babel']).to.be.undefined; }); it('changes specified npm dev dependency versions', () => { - let npmAdapter = new PnpmAdapter({ cwd: tmpdir }); + let adapter = new PnpmAdapter({ cwd: tmpdir }); let packageJSON = { devDependencies: { 'ember-feature-flags': '1.0.0' }, dependencies: { 'ember-cli-babel': '5.0.0' }, }; let depSet = { devDependencies: { 'ember-feature-flags': '2.0.1' } }; - let resultJSON = npmAdapter._packageJSONForDependencySet(packageJSON, depSet); + let resultJSON = adapter._packageJSONForDependencySet(packageJSON, depSet); expect(resultJSON.devDependencies['ember-feature-flags']).to.equal('2.0.1'); }); it('changes specified npm peer dependency versions', () => { - let npmAdapter = new PnpmAdapter({ cwd: tmpdir }); + let adapter = new PnpmAdapter({ cwd: tmpdir }); let packageJSON = { peerDependencies: { 'ember-cli-babel': '5.0.0' } }; let depSet = { peerDependencies: { 'ember-cli-babel': '4.0.0' } }; - let resultJSON = npmAdapter._packageJSONForDependencySet(packageJSON, depSet); + let resultJSON = adapter._packageJSONForDependencySet(packageJSON, depSet); expect(resultJSON.peerDependencies['ember-cli-babel']).to.equal('4.0.0'); }); it('can remove a package', () => { - let npmAdapter = new PnpmAdapter({ cwd: tmpdir }); + let adapter = new PnpmAdapter({ cwd: tmpdir }); let packageJSON = { devDependencies: { 'ember-feature-flags': '1.0.0' } }; let depSet = { devDependencies: { 'ember-feature-flags': null } }; - let resultJSON = npmAdapter._packageJSONForDependencySet(packageJSON, depSet); + let resultJSON = adapter._packageJSONForDependencySet(packageJSON, depSet); expect(resultJSON.devDependencies).to.not.have.property('ember-feature-flags'); }); @@ -478,9 +443,9 @@ describe('pnpm Adapter', () => { { expected: false, pnpmVersion: '9.0.0', resolutionMode: 'lowest-direct' }, ].forEach(({ pnpmVersion, resolutionMode, expected }) => { it(`works with given version "${pnpmVersion}" and resolutionMode "${resolutionMode}"`, () => { - let npmAdapter = new PnpmAdapter({ cwd: tmpdir }); + let adapter = new PnpmAdapter({ cwd: tmpdir }); - let result = npmAdapter._isResolutionModeWrong(pnpmVersion, resolutionMode); + let result = adapter._isResolutionModeWrong(pnpmVersion, resolutionMode); expect(result).equal(expected); }); }); @@ -506,8 +471,8 @@ describe('pnpm Adapter', () => { { allowPassthrough: false } ); - let npmAdapter = new PnpmAdapter({ cwd: tmpdir, run: stubbedRun }); - let result = await npmAdapter._getPnpmVersion(); + let adapter = new PnpmAdapter({ cwd: tmpdir, run: stubbedRun }); + let result = await adapter._getPnpmVersion(); expect(result).equal(version); }); }); @@ -527,9 +492,9 @@ describe('pnpm Adapter', () => { { allowPassthrough: false }, ); - let npmAdapter = new PnpmAdapter({ cwd: tmpdir, run: stubbedRun }); + let adapter = new PnpmAdapter({ cwd: tmpdir, run: stubbedRun }); - let result = await npmAdapter._getResolutionMode(); + let result = await adapter._getResolutionMode(); expect(result).equal(''); }); @@ -546,11 +511,11 @@ describe('pnpm Adapter', () => { { allowPassthrough: false }, ); - let npmAdapter = new PnpmAdapter({ cwd: tmpdir, run: stubbedRun }); + let adapter = new PnpmAdapter({ cwd: tmpdir, run: stubbedRun }); setResolutionModeToHighest(tmpdir); - let result = await npmAdapter._getResolutionMode(); + let result = await adapter._getResolutionMode(); expect(result).equal('highest'); }); }); @@ -576,9 +541,9 @@ describe('pnpm Adapter', () => { { allowPassthrough: false }, ); - let npmAdapter = new PnpmAdapter({ cwd: tmpdir, run: stubbedRun }); + let adapter = new PnpmAdapter({ cwd: tmpdir, run: stubbedRun }); - return expect(npmAdapter._throwOnResolutionMode()).to.eventually.be.rejectedWith( + return expect(adapter._throwOnResolutionMode()).to.eventually.be.rejectedWith( 'You are using an old version of pnpm that uses wrong resolution mode that violates ember-try expectations. Please either upgrade pnpm or set `resolution-mode` to `highest` in `.npmrc`.', ); }); @@ -602,11 +567,11 @@ describe('pnpm Adapter', () => { { allowPassthrough: false }, ); - let npmAdapter = new PnpmAdapter({ cwd: tmpdir, run: stubbedRun }); + let adapter = new PnpmAdapter({ cwd: tmpdir, run: stubbedRun }); setResolutionModeToHighest(tmpdir); - await npmAdapter._throwOnResolutionMode('8.6.0'); + await adapter._throwOnResolutionMode('8.6.0'); }); }); @@ -630,9 +595,9 @@ describe('pnpm Adapter', () => { { allowPassthrough: false }, ); - let npmAdapter = new PnpmAdapter({ cwd: tmpdir, run: stubbedRun }); + let adapter = new PnpmAdapter({ cwd: tmpdir, run: stubbedRun }); - await npmAdapter._throwOnResolutionMode(); + await adapter._throwOnResolutionMode(); }); }); }); diff --git a/test/dependency-manager-adapters/workspace-adapter-test.js b/test/dependency-manager-adapters/workspace-adapter-test.js index 707f9e9a..be34e08f 100644 --- a/test/dependency-manager-adapters/workspace-adapter-test.js +++ b/test/dependency-manager-adapters/workspace-adapter-test.js @@ -14,7 +14,7 @@ let root = process.cwd(); let tmproot = path.join(root, 'tmp'); let tmpdir; -describe('workspaceAdapter', () => { +describe('workspace Adapter', () => { beforeEach(() => { tmpdir = tmp.in(tmproot); process.chdir(tmpdir); @@ -34,13 +34,13 @@ describe('workspaceAdapter', () => { let workspaceAdapter = new WorkspaceAdapter({ cwd: tmpdir, - useYarnCommand: true, + packageManager: 'yarn', }); return workspaceAdapter.setup().then(() => { - let npmAdapter = workspaceAdapter._packageAdapters[0]; + let adapter = workspaceAdapter._packageAdapters[0]; - assertFileContainsJSON(npmAdapter.backup.pathForFile('package.json'), { + assertFileContainsJSON(adapter.backup.pathForFile('package.json'), { originalPackageJSON: true, }); }); @@ -61,40 +61,36 @@ describe('workspaceAdapter', () => { let workspaceAdapter = new WorkspaceAdapter({ cwd: tmpdir, - useYarnCommand: true, + packageManager: 'yarn', }); return workspaceAdapter.setup().then(() => { - let npmAdapter = workspaceAdapter._packageAdapters[0]; + let adapter = workspaceAdapter._packageAdapters[0]; - assertFileContainsJSON(npmAdapter.backup.pathForFile('package.json'), { + assertFileContainsJSON(adapter.backup.pathForFile('package.json'), { originalPackageJSON: true, }); }); }); - it('backs up the `package.json`, `package-lock.json` and `yarn.lock` files if they exist', () => { + it('backs up the `package.json` and `yarn.lock` files if they exist', () => { fs.ensureDirSync('packages/test/node_modules'); writeJSONFile('packages/test/package.json', { originalPackageJSON: true }); - writeJSONFile('packages/test/package-lock.json', { originalPackageLock: true }); writeJSONFile('packages/test/yarn.lock', { originalYarnLock: true }); let workspaceAdapter = new WorkspaceAdapter({ cwd: tmpdir, - useYarnCommand: true, + packageManager: 'yarn', }); return workspaceAdapter.setup().then(() => { - let npmAdapter = workspaceAdapter._packageAdapters[0]; + let adapter = workspaceAdapter._packageAdapters[0]; - assertFileContainsJSON(npmAdapter.backup.pathForFile('package.json'), { + assertFileContainsJSON(adapter.backup.pathForFile('package.json'), { originalPackageJSON: true, }); - assertFileContainsJSON(npmAdapter.backup.pathForFile('package-lock.json'), { - originalPackageLock: true, - }); - assertFileContainsJSON(npmAdapter.backup.pathForFile('yarn.lock'), { + assertFileContainsJSON(adapter.backup.pathForFile('yarn.lock'), { originalYarnLock: true, }); }); @@ -108,7 +104,7 @@ describe('workspaceAdapter', () => { expect(() => { return new WorkspaceAdapter({ cwd: tmpdir, - useYarnCommand: true, + packageManager: 'yarn', }).setup(); }).to.throw( /you must define the `workspaces` property in package.json with at least one workspace to use workspaces with ember-try/, @@ -149,7 +145,7 @@ describe('workspaceAdapter', () => { return new WorkspaceAdapter({ cwd: tmpdir, run: stubbedRun, - useYarnCommand: true, + packageManager: 'yarn', }) ._install() .then(() => { @@ -175,7 +171,7 @@ describe('workspaceAdapter', () => { return new WorkspaceAdapter({ cwd: tmpdir, run: stubbedRun, - useYarnCommand: true, + packageManager: 'yarn', managerOptions: ['--flat'], }) ._install() @@ -202,7 +198,7 @@ describe('workspaceAdapter', () => { return new WorkspaceAdapter({ cwd: tmpdir, run: stubbedRun, - useYarnCommand: true, + packageManager: 'yarn', buildManagerOptions: function () { return ['--flat']; }, @@ -218,7 +214,7 @@ describe('workspaceAdapter', () => { new WorkspaceAdapter({ cwd: tmpdir, run: () => {}, - useYarnCommand: true, + packageManager: 'yarn', buildManagerOptions: function () { return 'string'; }, @@ -236,13 +232,13 @@ describe('workspaceAdapter', () => { let workspaceAdapter = new WorkspaceAdapter({ cwd: tmpdir, - useYarnCommand: true, + packageManager: 'yarn', run: () => Promise.resolve(), }); - let npmAdapter = workspaceAdapter._packageAdapters[0]; + let adapter = workspaceAdapter._packageAdapters[0]; - await npmAdapter._backupOriginalDependencies(); + await adapter.setup(); // Simulate modifying the file: writeJSONFile('packages/test/package.json', { originalPackageJSON: false }); @@ -261,7 +257,7 @@ describe('workspaceAdapter', () => { let workspaceAdapter = new WorkspaceAdapter({ cwd: tmpdir, - useYarnCommand: true, + packageManager: 'yarn', run: () => Promise.resolve(), }); @@ -279,16 +275,15 @@ describe('workspaceAdapter', () => { }); }); - it('replaces the yarn.lock and package-lock.json with the backed up version if they exist', () => { + it('replaces the yarn.lock with the backed up version if they exist', () => { fs.ensureDirSync('packages/test/node_modules'); writeJSONFile('packages/test/package.json', { originalPackageJSON: true }); writeJSONFile('packages/test/yarn.lock', { originalYarnLock: true }); - writeJSONFile('packages/test/package-lock.json', { originalPackageLock: true }); let workspaceAdapter = new WorkspaceAdapter({ cwd: tmpdir, - useYarnCommand: true, + packageManager: 'yarn', run: () => Promise.resolve(), }); @@ -297,7 +292,6 @@ describe('workspaceAdapter', () => { .then(() => { writeJSONFile('packages/test/package.json', { originalPackageJSON: false }); writeJSONFile('packages/test/yarn.lock', { originalYarnLock: false }); - writeJSONFile('packages/test/package-lock.json', { originalPackageLock: false }); return workspaceAdapter.cleanup(); }) @@ -308,9 +302,6 @@ describe('workspaceAdapter', () => { assertFileContainsJSON(path.join(tmpdir, 'packages/test/yarn.lock'), { originalYarnLock: true, }); - assertFileContainsJSON(path.join(tmpdir, 'packages/test/package-lock.json'), { - originalPackageLock: true, - }); }); }); }); @@ -329,7 +320,7 @@ describe('workspaceAdapter', () => { workspaceAdapter = new WorkspaceAdapter({ cwd: tmpdir, - useYarnCommand: true, + packageManager: 'yarn', run: () => Promise.resolve(), }); diff --git a/test/dependency-manager-adapters/yarn-adapter-test.js b/test/dependency-manager-adapters/yarn-adapter-test.js new file mode 100644 index 00000000..665a431a --- /dev/null +++ b/test/dependency-manager-adapters/yarn-adapter-test.js @@ -0,0 +1,352 @@ +'use strict'; + +let expect = require('chai').expect; +let fs = require('fs-extra'); +let path = require('path'); +let tmp = require('tmp-sync'); +let fixturePackage = require('../fixtures/package.json'); +let YarnAdapter = require('../../lib/dependency-manager-adapters/yarn'); +let writeJSONFile = require('../helpers/write-json-file'); +let assertFileContainsJSON = require('../helpers/assert-file-contains-json'); +let generateMockRun = require('../helpers/generate-mock-run'); + +let root = process.cwd(); +let tmproot = path.join(root, 'tmp'); +let tmpdir; + +describe('yarn Adapter', () => { + beforeEach(() => { + tmpdir = tmp.in(tmproot); + process.chdir(tmpdir); + }); + + afterEach(() => { + process.chdir(root); + return fs.remove(tmproot); + }); + + describe('#setup', () => { + it('backs up the `package.json` and `yarn.lock` files if they exist', async () => { + writeJSONFile('package.json', { originalPackageJSON: true }); + writeJSONFile('yarn.lock', { originalYarnLock: true }); + + let adapter = new YarnAdapter({ cwd: tmpdir }); + + await adapter.setup(); + + assertFileContainsJSON(adapter.backup.pathForFile('package.json'), { + originalPackageJSON: true, + }); + assertFileContainsJSON(adapter.backup.pathForFile('yarn.lock'), { + originalYarnLock: true, + }); + }); + }); + + describe('#_install', () => { + it('runs yarn install', async () => { + writeJSONFile('package.json', fixturePackage); + let runCount = 0; + let stubbedRun = generateMockRun( + [ + { + command: 'yarn install --no-lockfile --ignore-engines', + callback(command, args, opts) { + runCount++; + expect(opts).to.have.property('cwd', tmpdir); + return Promise.resolve(); + }, + }, + ], + { allowPassthrough: false }, + ); + + let adapter = new YarnAdapter({ + cwd: tmpdir, + run: stubbedRun, + }); + + await adapter._install(); + expect(runCount).to.equal(1, 'Only yarn install should run'); + }); + + it('uses managerOptions for yarn commands', async () => { + writeJSONFile('package.json', fixturePackage); + let runCount = 0; + let stubbedRun = generateMockRun( + [ + { + command: 'yarn install --flat --no-lockfile --ignore-engines', + callback() { + runCount++; + return Promise.resolve(); + }, + }, + ], + { allowPassthrough: false }, + ); + + let adapter = new YarnAdapter({ + cwd: tmpdir, + run: stubbedRun, + managerOptions: ['--flat'], + }); + + await adapter._install(); + expect(runCount).to.equal(1, 'Only yarn install should run with manager options'); + }); + + it('uses buildManagerOptions for yarn commands', async () => { + writeJSONFile('package.json', fixturePackage); + let runCount = 0; + let stubbedRun = generateMockRun( + [ + { + command: 'yarn install --flat', + callback() { + runCount++; + return Promise.resolve(); + }, + }, + ], + { allowPassthrough: false }, + ); + + let adapter = new YarnAdapter({ + cwd: tmpdir, + run: stubbedRun, + buildManagerOptions: function () { + return ['--flat']; + }, + }); + + await adapter._install(); + expect(runCount).to.equal(1, 'Only yarn install should run with buildManagerOptions'); + }); + + it('throws an error if buildManagerOptions does not return an array', async () => { + let error; + try { + let adapter = new YarnAdapter({ + cwd: tmpdir, + run: () => {}, + buildManagerOptions: function () { + return 'string'; + }, + }); + + await adapter._install(); + } catch (e) { + error = e; + } + + expect(error.message).to.include('buildManagerOptions must return an array of options'); + }); + }); + + describe('#cleanup', () => { + it('restores the `package.json` and `yarn.lock` files if they exist', async () => { + writeJSONFile('package.json', { originalPackageJSON: true }); + writeJSONFile('yarn.lock', { originalYarnLock: true }); + + let adapter = new YarnAdapter({ cwd: tmpdir }); + + await adapter.setup(); + + // Simulate modifying the files: + writeJSONFile('package.json', { originalPackageJSON: false }); + writeJSONFile('yarn.lock', { originalYarnLock: false }); + + await adapter.cleanup(); + + assertFileContainsJSON(path.join(tmpdir, 'package.json'), { + originalPackageJSON: true, + }); + assertFileContainsJSON(path.join(tmpdir, 'yarn.lock'), { + originalYarnLock: true, + }); + }); + + it('installs the original node modules again', async () => { + let runCount = 0; + let stubbedRun = generateMockRun( + [ + { + command: 'yarn install --no-lockfile --ignore-engines', + callback() { + runCount++; + return Promise.resolve(); + }, + }, + ], + { allowPassthrough: false }, + ); + + let adapter = new YarnAdapter({ + cwd: tmpdir, + run: stubbedRun, + }); + + await adapter.cleanup(); + + expect(runCount).to.equal(1); + }); + }); + + describe('#_packageJSONForDependencySet', () => { + it('changes specified dependency versions', () => { + let adapter = new YarnAdapter({ cwd: tmpdir }); + let packageJSON = { + devDependencies: { 'ember-feature-flags': '1.0.0' }, + dependencies: { 'ember-cli-babel': '5.0.0' }, + }; + let depSet = { dependencies: { 'ember-cli-babel': '6.0.0' } }; + + let resultJSON = adapter._packageJSONForDependencySet(packageJSON, depSet); + + expect(resultJSON.dependencies['ember-cli-babel']).to.equal('6.0.0'); + }); + + describe('ember property', () => { + it('adds the ember property to project package.json', () => { + let adapter = new YarnAdapter({ + cwd: tmpdir, + }); + let packageJSON = {}; + let depSet = { + ember: { edition: 'octane' }, + }; + + let resultJSON = adapter._packageJSONForDependencySet(packageJSON, depSet); + + expect(resultJSON).to.deep.equal({ ember: { edition: 'octane' } }); + }); + + it('merges the ember property to project package.json', () => { + let adapter = new YarnAdapter({ + cwd: tmpdir, + }); + let packageJSON = { ember: { foo: 'bar' } }; + let depSet = { + ember: { edition: 'octane' }, + }; + + let resultJSON = adapter._packageJSONForDependencySet(packageJSON, depSet); + + expect(resultJSON).to.deep.equal({ ember: { foo: 'bar', edition: 'octane' } }); + }); + + it('overrides existing fields inside the ember property to project package.json', () => { + let adapter = new YarnAdapter({ + cwd: tmpdir, + }); + let packageJSON = { ember: { edition: 'classic' } }; + let depSet = { + ember: { edition: 'octane' }, + }; + + let resultJSON = adapter._packageJSONForDependencySet(packageJSON, depSet); + + expect(resultJSON).to.deep.equal({ ember: { edition: 'octane' } }); + }); + + it('removes any items with a null value', () => { + let adapter = new YarnAdapter({ + cwd: tmpdir, + }); + let packageJSON = { ember: { edition: 'octane' } }; + let depSet = { + ember: { edition: null }, + }; + + let resultJSON = adapter._packageJSONForDependencySet(packageJSON, depSet); + + expect(resultJSON).to.deep.equal({ ember: {} }); + }); + }); + + it('adds a resolution for the specified dependency version', () => { + let adapter = new YarnAdapter({ + cwd: tmpdir, + }); + let packageJSON = { + dependencies: { 'ember-cli-babel': '5.0.0' }, + }; + let depSet = { + dependencies: { 'ember-cli-babel': '6.0.0' }, + resolutions: { 'ember-cli-babel': '6.0.0' }, + }; + + let resultJSON = adapter._packageJSONForDependencySet(packageJSON, depSet); + + expect(resultJSON.resolutions['ember-cli-babel']).to.equal('6.0.0'); + }); + + it('removes a dependency from resolutions if its version is null', () => { + let adapter = new YarnAdapter({ + cwd: tmpdir, + }); + let packageJSON = { + dependencies: { 'ember-cli-babel': '5.0.0' }, + resolutions: { 'ember-cli-babel': '5.0.0' }, + }; + let depSet = { + dependencies: { 'ember-cli-babel': '6.0.0' }, + resolutions: { 'ember-cli-babel': null }, + }; + + let resultJSON = adapter._packageJSONForDependencySet(packageJSON, depSet); + + expect(resultJSON.resolutions['ember-cli-babel']).to.be.undefined; + }); + + it('doesnt add resolutions if there are none specified', () => { + let adapter = new YarnAdapter({ + cwd: tmpdir, + }); + let packageJSON = { + dependencies: { 'ember-cli-babel': '5.0.0' }, + }; + let depSet = { + dependencies: { 'ember-cli-babel': '6.0.0' }, + }; + + let resultJSON = adapter._packageJSONForDependencySet(packageJSON, depSet); + + expect(resultJSON.resolutions).to.be.undefined; + }); + + it('changes specified npm dev dependency versions', () => { + let adapter = new YarnAdapter({ cwd: tmpdir }); + let packageJSON = { + devDependencies: { 'ember-feature-flags': '1.0.0' }, + dependencies: { 'ember-cli-babel': '5.0.0' }, + }; + let depSet = { devDependencies: { 'ember-feature-flags': '2.0.1' } }; + + let resultJSON = adapter._packageJSONForDependencySet(packageJSON, depSet); + + expect(resultJSON.devDependencies['ember-feature-flags']).to.equal('2.0.1'); + }); + + it('changes specified npm peer dependency versions', () => { + let adapter = new YarnAdapter({ cwd: tmpdir }); + let packageJSON = { peerDependencies: { 'ember-cli-babel': '5.0.0' } }; + let depSet = { peerDependencies: { 'ember-cli-babel': '4.0.0' } }; + + let resultJSON = adapter._packageJSONForDependencySet(packageJSON, depSet); + + expect(resultJSON.peerDependencies['ember-cli-babel']).to.equal('4.0.0'); + }); + + it('can remove a package', () => { + let adapter = new YarnAdapter({ cwd: tmpdir }); + let packageJSON = { devDependencies: { 'ember-feature-flags': '1.0.0' } }; + let depSet = { devDependencies: { 'ember-feature-flags': null } }; + + let resultJSON = adapter._packageJSONForDependencySet(packageJSON, depSet); + + expect(resultJSON.devDependencies).to.not.have.property('ember-feature-flags'); + }); + }); +}); diff --git a/test/tasks/try-each-test.js b/test/tasks/try-each-test.js index 21d0cc3a..11beb377 100644 --- a/test/tasks/try-each-test.js +++ b/test/tasks/try-each-test.js @@ -97,7 +97,7 @@ describe('tryEach', () => { return tryEachTask.run(config.scenarios, {}).then((exitCode) => { expect(exitCode).to.equal(0, 'exits 0 when all scenarios succeed'); expect(output).to.include( - "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.", + "Detected a `yarn.lock` file. Add `packageManager: 'yarn'` to your `config/ember-try.js` configuration file if you want to use yarn to install dependencies.", ); expect(output).to.include('Scenario first: SUCCESS'); expect(output).to.include('Scenario second: SUCCESS'); diff --git a/yarn.lock b/yarn.lock index c41bdee6..f26e2502 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3185,6 +3185,11 @@ es-errors@^1.3.0: resolved "https://registry.yarnpkg.com/es-errors/-/es-errors-1.3.0.tgz#05f75a25dab98e4fb1dcd5e1472c0546d5057c8f" integrity sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw== +es-toolkit@^1.29.0: + version "1.29.0" + resolved "https://registry.yarnpkg.com/es-toolkit/-/es-toolkit-1.29.0.tgz#a9c54e87880002e4186560fe0a567e76a61cb61a" + integrity sha512-GjTll+E6APcfAQA09D89HdT8Qn2Yb+TeDSDBTMcxAo+V+w1amAtCI15LJu4YPH/UCPoSo/F47Gr1LIM0TE0lZA== + es6-error@^4.0.1: version "4.1.1" resolved "https://registry.yarnpkg.com/es6-error/-/es6-error-4.1.1.tgz#9e3af407459deed47e9a91f9b885a84eb05c561d"