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