From 3611ff6c92bf48fb03fbd1f27b325f4c8b861baa Mon Sep 17 00:00:00 2001 From: Haoqun Jiang Date: Wed, 22 May 2019 14:42:14 +0800 Subject: [PATCH 01/20] feat: [WIP] add MigratorAPI for `vue upgrade` usage --- .../@vue/cli-plugin-babel/migrator/index.js | 16 ++ packages/@vue/cli-shared-utils/index.js | 1 + packages/@vue/cli-shared-utils/lib/spinner.js | 4 + packages/@vue/cli-upgrade/index.js | 1 + packages/@vue/cli/bin/vue.js | 10 +- packages/@vue/cli/lib/Migrator.js | 8 + packages/@vue/cli/lib/MigratorAPI.js | 24 ++ packages/@vue/cli/lib/upgrade.js | 227 ++++++++++++++++++ .../@vue/cli/lib/util/getInstalledVersion.js | 14 ++ packages/@vue/cli/lib/util/getPackageJson.js | 21 ++ .../@vue/cli/lib/util/getPackageVersion.js | 10 +- packages/@vue/cli/lib/util/mergeDeps.js | 18 +- packages/@vue/cli/lib/util/packageManager.js | 56 +++++ .../@vue/cli/lib/util/tryGetNewerRange.js | 12 + 14 files changed, 401 insertions(+), 21 deletions(-) create mode 100644 packages/@vue/cli-plugin-babel/migrator/index.js create mode 100644 packages/@vue/cli/lib/Migrator.js create mode 100644 packages/@vue/cli/lib/MigratorAPI.js create mode 100644 packages/@vue/cli/lib/upgrade.js create mode 100644 packages/@vue/cli/lib/util/getInstalledVersion.js create mode 100644 packages/@vue/cli/lib/util/getPackageJson.js create mode 100644 packages/@vue/cli/lib/util/packageManager.js create mode 100644 packages/@vue/cli/lib/util/tryGetNewerRange.js diff --git a/packages/@vue/cli-plugin-babel/migrator/index.js b/packages/@vue/cli-plugin-babel/migrator/index.js new file mode 100644 index 0000000000..5c2b658bdd --- /dev/null +++ b/packages/@vue/cli-plugin-babel/migrator/index.js @@ -0,0 +1,16 @@ +module.exports = (api) => { + if (api.fromVersion('<=3.5.4')) { + // add core-js@2 as dependency + api.extendPackage({ + dependencies: { + 'core-js': '^2.6.5' + } + }) + } + + if (api.fromVersion('^3')) { + // TODO: update core-js@2 to core-js@3 + } + + api.exitLog('Full changelog: maybe-some-random-gist-url') +} diff --git a/packages/@vue/cli-shared-utils/index.js b/packages/@vue/cli-shared-utils/index.js index c5df8ba483..ab1213441d 100644 --- a/packages/@vue/cli-shared-utils/index.js +++ b/packages/@vue/cli-shared-utils/index.js @@ -17,3 +17,4 @@ exports.chalk = require('chalk') exports.execa = require('execa') +exports.semver = require('semver') diff --git a/packages/@vue/cli-shared-utils/lib/spinner.js b/packages/@vue/cli-shared-utils/lib/spinner.js index 7bc13d91fe..84f44d677e 100644 --- a/packages/@vue/cli-shared-utils/lib/spinner.js +++ b/packages/@vue/cli-shared-utils/lib/spinner.js @@ -43,6 +43,10 @@ exports.resumeSpinner = () => { spinner.start() } +exports.failSpinner = (text) => { + spinner.fail(text) +} + // silent all logs except errors during tests and keep record if (process.env.VUE_CLI_TEST) { require('./_silence')('spinner', exports) diff --git a/packages/@vue/cli-upgrade/index.js b/packages/@vue/cli-upgrade/index.js index 373941200b..d7c008c67e 100644 --- a/packages/@vue/cli-upgrade/index.js +++ b/packages/@vue/cli-upgrade/index.js @@ -120,6 +120,7 @@ module.exports = async function vueCliUpgrade (semverLevel = 'minor') { } console.log('These packages can be upgraded:\n') + // TODO: alternative: https://github.com/angular/angular-cli/blob/34a55c96b2ed38b226879913839b97c601387653/packages/schematics/update/update/index.ts#L490-L509 console.log(table.toString()) console.log( `\nView complete changelog at ${chalk.blue( diff --git a/packages/@vue/cli/bin/vue.js b/packages/@vue/cli/bin/vue.js index 9d88addbbf..2990d2fb23 100755 --- a/packages/@vue/cli/bin/vue.js +++ b/packages/@vue/cli/bin/vue.js @@ -166,10 +166,12 @@ program }) program - .command('upgrade [semverLevel]') - .description('upgrade vue cli service / plugins (default semverLevel: minor)') - .action((semverLevel, cmd) => { - loadCommand('upgrade', '@vue/cli-upgrade')(semverLevel, cleanArgs(cmd)) + .command('upgrade [package-name]') + .description('upgrade vue cli service / plugins') + .option('-t, --to ', 'upgrade to a version that is not latest') + .option('-r, --registry ', 'Use specified npm registry when installing dependencies') + .action((packageName, cmd) => { + require('../lib/upgrade')(packageName, cleanArgs(cmd)) }) program diff --git a/packages/@vue/cli/lib/Migrator.js b/packages/@vue/cli/lib/Migrator.js new file mode 100644 index 0000000000..efbfe55abb --- /dev/null +++ b/packages/@vue/cli/lib/Migrator.js @@ -0,0 +1,8 @@ +const Generator = require('./Generator') + +module.exports = class Migrator extends Generator { + constructor (context, options) { + super(context, options) + this.migrate = this.generate + } +} diff --git a/packages/@vue/cli/lib/MigratorAPI.js b/packages/@vue/cli/lib/MigratorAPI.js new file mode 100644 index 0000000000..80d3d3b955 --- /dev/null +++ b/packages/@vue/cli/lib/MigratorAPI.js @@ -0,0 +1,24 @@ +const semver = require('semver') +const GeneratorAPI = require('./GeneratorAPI') + +class MigratorAPI extends GeneratorAPI { + /** + * @param {string} id - Id of the owner plugin + * @param {Migrator} migrator - The invoking Migrator instance + * @param {object} options - options passed to this plugin + * @param {object} rootOptions - root options (the entire preset) + */ + constructor (id, installedVersion, migrator, options, rootOptions) { + super(id, migrator, options, rootOptions) + + this.installedVersion = installedVersion + this.migrator = this.generator + // this.installedVersion = getInstalledVersion(id) + } + + fromVersion (range) { + return semver.satisfies(this.installedVersion, range) + } +} + +module.exports = MigratorAPI diff --git a/packages/@vue/cli/lib/upgrade.js b/packages/@vue/cli/lib/upgrade.js new file mode 100644 index 0000000000..7a42531f2e --- /dev/null +++ b/packages/@vue/cli/lib/upgrade.js @@ -0,0 +1,227 @@ +const fs = require('fs') +const path = require('path') +const chalk = require('chalk') +const execa = require('execa') +const globby = require('globby') +const { isBinaryFileSync } = require('isbinaryfile') +const { + log, + error, + logWithSpinner, + stopSpinner, + failSpinner, + + isPlugin, + loadModule, + + hasProjectGit +} = require('@vue/cli-shared-utils') + +const Migrator = require('./Migrator') +const { getCommand, getVersion } = require('./util/packageManager') +const getPackageJson = require('./util/getPackageJson') +const getInstalledVersion = require('./util/getInstalledVersion') +const tryGetNewerRange = require('./util/tryGetNewerRange') +const { installDeps, installPackage } = require('./util/installDeps') +const normalizeFilePaths = require('./util/normalizeFilePaths') + +async function readFiles (context) { + const files = await globby(['**'], { + cwd: context, + onlyFiles: true, + gitignore: true, + ignore: ['**/node_modules/**', '**/.git/**'], + dot: true + }) + const res = {} + for (const file of files) { + const name = path.resolve(context, file) + res[file] = isBinaryFileSync(name) + ? fs.readFileSync(name) + : fs.readFileSync(name, 'utf-8') + } + return normalizeFilePaths(res) +} + +async function runMigrator (packageName, options, context) { + const isTestOrDebug = process.env.VUE_CLI_TEST || process.env.VUE_CLI_DEBUG + + const pluginMigrator = loadModule(`${packageName}/migrator`, context) + if (!pluginMigrator) { return } + + const { registry } = options + const plugin = { + packageName, + apply: migrator, + options: { + registry + } + } + + const pkg = getPackageJson(context) + const createCompleteCbs = [] + const migrator = new Migrator(context, { + pkg, + plugins: [plugin], + files: await readFiles(context), + completeCbs: createCompleteCbs, + invoking: true + }) + + log(`🚀 Running migrator of ${packageName}`) + await migrator.migrate({ + extractConfigFiles: true, + checkExisting: true + }) + + const newDeps = migrator.pkg.dependencies + const newDevDeps = migrator.pkg.devDependencies + const depsChanged = + JSON.stringify(newDeps) !== JSON.stringify(pkg.dependencies) || + JSON.stringify(newDevDeps) !== JSON.stringify(pkg.devDependencies) + + // TODO: + + if (!isTestOrDebug && depsChanged) { + log(`📦 Installing additional dependencies...`) + log() + const packageManager = getCommand(context) + await installDeps(context, packageManager, plugin.options && plugin.options.registry) + } + + if (createCompleteCbs.length) { + logWithSpinner('⚓', `Running completion hooks...`) + for (const cb of createCompleteCbs) { + await cb() + } + stopSpinner() + log() + } + + log(`${chalk.green('✔')} Successfully invoked migrator for plugin: ${chalk.cyan(plugin.id)}`) + if (!process.env.VUE_CLI_TEST && hasProjectGit(context)) { + const { stdout } = await execa('git', [ + 'ls-files', + '--exclude-standard', + '--modified', + '--others' + ], { + cwd: context + }) + if (stdout.trim()) { + log(` The following files have been updated / added:\n`) + log( + chalk.red( + stdout + .split(/\r?\n/g) + .map(line => ` ${line}`) + .join('\n') + ) + ) + log() + log( + ` You should review these changes with ${chalk.cyan( + `git diff` + )} and commit them.` + ) + log() + } + } + + migrator.printExitLogs() +} + +async function upgradeSinglePackage (packageName, options, context) { + // FIXME: properly implement this + const required = require(`${context}/package.json`).devDependencies[packageName] + const installed = getInstalledVersion(packageName) + + let targetVersion = options.to || 'latest' + + // if the targetVersion is not an exact version + if (!/\d+\.\d+\.\d+/.test(targetVersion)) { + if (targetVersion === 'latest') { + logWithSpinner(`Getting latest version of ${packageName}`) + } else { + logWithSpinner(`Getting max satisfying version of ${packageName}@${options.to}`) + } + + targetVersion = await getVersion(packageName, targetVersion, options.registry, context) + stopSpinner() + } + + if (targetVersion === installed) { + log(`Already installed ${packageName}@${targetVersion}`) + + const newRange = tryGetNewerRange(`^${targetVersion}`, required) + if (newRange !== required) { + // TODO: + // extendPackage({ devDependencies: { packageName: newRange } }) + log(`${chalk.green('✔')} Updated version range in ${chalk.yellow('package.json')}`) + } + return + } + + log(`Upgrading ${packageName} from ${installed} to ${targetVersion}`) + + // TODO: integrate getCommand into installPackage + const command = getCommand(context) + await installPackage(context, command, options.registry, packageName) + await runMigrator(packageName, { installed }, context) +} + +async function upgradeAll (context) { + // get current deps + // filter @vue/cli-service, @vue/cli-plugin-* & vue-cli-plugin-* + const pkg = getPackageJson(context) + const upgradable = { + dependencies: {}, + devDependencies: {}, + optionalDependencies: {} + } + + logWithSpinner('Gathering update information...') + for (const depType of Object.keys(upgradable)) { + for (const [packageName, range] of Object.entries(pkg[depType] || {})) { + if (packageName !== '@vue/cli-service' && !isPlugin(packageName)) { + continue + } + + const latest = await getVersion(packageName, 'latest', undefined, context) + const latestRange = `^${latest}` + + if (range !== latestRange) { + upgradable[depType][packageName] = latestRange + } + } + } + + // TODO: upgrade all (interactive) + log(upgradable) + + failSpinner() + + return Promise.reject() +} + +async function upgrade (packageName, options, context = process.cwd()) { + if (!packageName) { + if (options.to) { + error(`Must specify a package name to upgrade to ${options.to}`) + process.exit(1) + } + + return upgradeAll(context) + } + + return upgradeSinglePackage(packageName, options, context) +} + +module.exports = (...args) => { + return upgrade(...args).catch(err => { + error(err) + if (!process.env.VUE_CLI_TEST) { + process.exit(1) + } + }) +} diff --git a/packages/@vue/cli/lib/util/getInstalledVersion.js b/packages/@vue/cli/lib/util/getInstalledVersion.js new file mode 100644 index 0000000000..68741c00a4 --- /dev/null +++ b/packages/@vue/cli/lib/util/getInstalledVersion.js @@ -0,0 +1,14 @@ +const path = require('path') +const getPackageJson = require('./getPackageJson') + +module.exports = function getInstalledVersion (packageName) { + // for first level deps, read package.json directly is way faster than `npm list` + try { + const packageJson = getPackageJson( + path.resolve(process.cwd(), 'node_modules', packageName) + ) + return packageJson.version + } catch (e) { + return 'N/A' + } +} diff --git a/packages/@vue/cli/lib/util/getPackageJson.js b/packages/@vue/cli/lib/util/getPackageJson.js new file mode 100644 index 0000000000..7049cc39e5 --- /dev/null +++ b/packages/@vue/cli/lib/util/getPackageJson.js @@ -0,0 +1,21 @@ +const fs = require('fs') +const path = require('path') + +module.exports = function getPackageJson (projectPath) { + const packagePath = path.join(projectPath, 'package.json') + + let packageJson + try { + packageJson = fs.readFileSync(packagePath, 'utf-8') + } catch (err) { + throw new Error(`${packagePath} not exist`) + } + + try { + packageJson = JSON.parse(packageJson) + } catch (err) { + throw new Error('The package.json is malformed') + } + + return packageJson +} diff --git a/packages/@vue/cli/lib/util/getPackageVersion.js b/packages/@vue/cli/lib/util/getPackageVersion.js index f3767b6546..d4e9e5b2df 100644 --- a/packages/@vue/cli/lib/util/getPackageVersion.js +++ b/packages/@vue/cli/lib/util/getPackageVersion.js @@ -1,9 +1,11 @@ const { request } = require('@vue/cli-shared-utils') -module.exports = async function getPackageVersion (id, range = '') { - const registry = (await require('./shouldUseTaobao')()) - ? `https://registry.npm.taobao.org` - : `https://registry.npmjs.org` +module.exports = async function getPackageVersion (id, range = '', registry) { + if (!registry) { + registry = (await require('./shouldUseTaobao')()) + ? `https://registry.npm.taobao.org` + : `https://registry.npmjs.org` + } let result try { diff --git a/packages/@vue/cli/lib/util/mergeDeps.js b/packages/@vue/cli/lib/util/mergeDeps.js index ad99f7f3b1..958cc271c7 100644 --- a/packages/@vue/cli/lib/util/mergeDeps.js +++ b/packages/@vue/cli/lib/util/mergeDeps.js @@ -1,6 +1,11 @@ const semver = require('semver') const { warn } = require('@vue/cli-shared-utils') +const tryGetNewerRange = require('./tryGetNewerRange') + +const extractSemver = r => r.replace(/^.+#semver:/, '') +const injectSemver = (r, v) => semver.validRange(r) ? v : r.replace(/#semver:.+$/, `#semver:${v}`) + module.exports = function resolveDeps (generatorId, to, from, sources) { const res = Object.assign({}, to) for (const name in from) { @@ -49,16 +54,3 @@ module.exports = function resolveDeps (generatorId, to, from, sources) { } return res } - -const leadRE = /^(~|\^|>=?)/ -const rangeToVersion = r => r.replace(leadRE, '').replace(/x/g, '0') -const extractSemver = r => r.replace(/^.+#semver:/, '') -const injectSemver = (r, v) => semver.validRange(r) ? v : r.replace(/#semver:.+$/, `#semver:${v}`) - -function tryGetNewerRange (r1, r2) { - const v1 = rangeToVersion(r1) - const v2 = rangeToVersion(r2) - if (semver.valid(v1) && semver.valid(v2)) { - return semver.gt(v1, v2) ? r1 : r2 - } -} diff --git a/packages/@vue/cli/lib/util/packageManager.js b/packages/@vue/cli/lib/util/packageManager.js new file mode 100644 index 0000000000..de34597b06 --- /dev/null +++ b/packages/@vue/cli/lib/util/packageManager.js @@ -0,0 +1,56 @@ +const execa = require('execa') +const semver = require('semver') +const { + hasYarn, + hasProjectYarn, + hasPnpm3OrLater, + hasProjectPnpm +} = require('@vue/cli-shared-utils') +const { loadOptions } = require('../options') + +function getCommand (cwd = undefined) { + if (!cwd) { + return loadOptions().packageManager || (hasYarn() ? 'yarn' : hasPnpm3OrLater() ? 'pnpm' : 'npm') + } + return hasProjectYarn(cwd) ? 'yarn' : hasProjectPnpm(cwd) ? 'pnpm' : 'npm' +} + +// TODO: add cache +async function getMetadata (packageName, { field = '', packageManager, registry, cwd }) { + if (!packageManager) { + packageManager = getCommand(cwd) + } + + if (!registry) { + if ((await require('./shouldUseTaobao')())) { + registry = `https://registry.npm.taobao.org` + } else { + const { stdout } = await execa(packageManager, ['config', 'get', 'registry']) + registry = stdout + } + } + + const { stdout } = await execa(packageManager, ['info', packageName, field, '--json']) + const metadata = JSON.parse(stdout) + + if (packageManager === 'yarn') { + // `yarn info` outputs messages in the form of `{"type": "inspect", data: {}}` + return metadata.data + } + return metadata +} + +async function getVersion (packageName, versionRange, registry, cwd) { + const metadata = await getMetadata(packageName, { registry, cwd }) + if (Object.keys(metadata['dist-tags']).includes(versionRange)) { + return metadata['dist-tags'][versionRange] + } + const versions = Array.isArray(metadata.versions) ? metadata.versions : Object.keys(metadata.versions) + return semver.maxSatisfying(versions, versionRange) +} + +module.exports = { + getCommand, + getMetadata, + getVersion +} diff --git a/packages/@vue/cli/lib/util/tryGetNewerRange.js b/packages/@vue/cli/lib/util/tryGetNewerRange.js new file mode 100644 index 0000000000..529432bc1a --- /dev/null +++ b/packages/@vue/cli/lib/util/tryGetNewerRange.js @@ -0,0 +1,12 @@ +const semver = require('semver') + +const leadRE = /^(~|\^|>=?)/ +const rangeToVersion = r => r.replace(leadRE, '').replace(/x/g, '0') + +module.exports = function tryGetNewerRange (r1, r2) { + const v1 = rangeToVersion(r1) + const v2 = rangeToVersion(r2) + if (semver.valid(v1) && semver.valid(v2)) { + return semver.gt(v1, v2) ? r1 : r2 + } +} From 768c28033a4e7408504392880f434b133ae8c076 Mon Sep 17 00:00:00 2001 From: Haoqun Jiang Date: Fri, 14 Jun 2019 15:00:26 +0800 Subject: [PATCH 02/20] refactor: move readFiles to a separate file --- packages/@vue/cli/lib/invoke.js | 27 ++++--------------------- packages/@vue/cli/lib/upgrade.js | 4 ++++ packages/@vue/cli/lib/util/readFiles.js | 24 ++++++++++++++++++++++ 3 files changed, 32 insertions(+), 23 deletions(-) create mode 100644 packages/@vue/cli/lib/util/readFiles.js diff --git a/packages/@vue/cli/lib/invoke.js b/packages/@vue/cli/lib/invoke.js index b822f08dd1..e3e6ee817e 100644 --- a/packages/@vue/cli/lib/invoke.js +++ b/packages/@vue/cli/lib/invoke.js @@ -2,13 +2,7 @@ const fs = require('fs-extra') const path = require('path') const execa = require('execa') const chalk = require('chalk') -const globby = require('globby') const inquirer = require('inquirer') -const { isBinaryFileSync } = require('isbinaryfile') -const Generator = require('./Generator') -const { loadOptions } = require('./options') -const { installDeps } = require('./util/installDeps') -const normalizeFilePaths = require('./util/normalizeFilePaths') const { log, error, @@ -21,23 +15,10 @@ const { loadModule } = require('@vue/cli-shared-utils') -async function readFiles (context) { - const files = await globby(['**'], { - cwd: context, - onlyFiles: true, - gitignore: true, - ignore: ['**/node_modules/**', '**/.git/**'], - dot: true - }) - const res = {} - for (const file of files) { - const name = path.resolve(context, file) - res[file] = isBinaryFileSync(name) - ? fs.readFileSync(name) - : fs.readFileSync(name, 'utf-8') - } - return normalizeFilePaths(res) -} +const Generator = require('./Generator') +const { loadOptions } = require('./options') +const { installDeps } = require('./util/installDeps') +const readFiles = require('./util/readFiles') function getPkg (context) { const pkgPath = path.resolve(context, 'package.json') diff --git a/packages/@vue/cli/lib/upgrade.js b/packages/@vue/cli/lib/upgrade.js index 7a42531f2e..c6727a2f2e 100644 --- a/packages/@vue/cli/lib/upgrade.js +++ b/packages/@vue/cli/lib/upgrade.js @@ -197,6 +197,10 @@ async function upgradeAll (context) { } // TODO: upgrade all (interactive) + // for patch & minor versions, upgrade directly + // for major versions, prompt before upgrading + + // TODO: format the output https://github.com/angular/angular-cli/blob/34a55c96b2ed38b226879913839b97c601387653/packages/schematics/update/update/index.ts#L490-L509 log(upgradable) failSpinner() diff --git a/packages/@vue/cli/lib/util/readFiles.js b/packages/@vue/cli/lib/util/readFiles.js new file mode 100644 index 0000000000..c0b6f9abc6 --- /dev/null +++ b/packages/@vue/cli/lib/util/readFiles.js @@ -0,0 +1,24 @@ +const fs = require('fs-extra') +const path = require('path') + +const globby = require('globby') +const { isBinaryFileSync } = require('isbinaryfile') +const normalizeFilePaths = require('./normalizeFilePaths') + +module.exports = async function readFiles (context) { + const files = await globby(['**'], { + cwd: context, + onlyFiles: true, + gitignore: true, + ignore: ['**/node_modules/**', '**/.git/**'], + dot: true + }) + const res = {} + for (const file of files) { + const name = path.resolve(context, file) + res[file] = isBinaryFileSync(name) + ? fs.readFileSync(name) + : fs.readFileSync(name, 'utf-8') + } + return normalizeFilePaths(res) +} From b48c98c9d5c0bc4dd15cd7ea4cf8cd9e2547513f Mon Sep 17 00:00:00 2001 From: Haoqun Jiang Date: Fri, 14 Jun 2019 20:46:16 +0800 Subject: [PATCH 03/20] refactor: unify get registry logic --- .../cli-plugin-typescript/generator/index.js | 1 + .../apollo-server/connectors/dependencies.js | 8 +-- .../apollo-server/connectors/plugins.js | 8 +-- packages/@vue/cli/lib/Creator.js | 4 +- packages/@vue/cli/lib/add.js | 2 +- packages/@vue/cli/lib/invoke.js | 2 +- packages/@vue/cli/lib/upgrade.js | 12 ++-- .../@vue/cli/lib/util/getPackageVersion.js | 5 +- packages/@vue/cli/lib/util/installDeps.js | 28 ++++------ packages/@vue/cli/lib/util/packageManager.js | 56 ++++++++++++++----- 10 files changed, 73 insertions(+), 53 deletions(-) diff --git a/packages/@vue/cli-plugin-typescript/generator/index.js b/packages/@vue/cli-plugin-typescript/generator/index.js index 55f77531cc..c42b6e410d 100644 --- a/packages/@vue/cli-plugin-typescript/generator/index.js +++ b/packages/@vue/cli-plugin-typescript/generator/index.js @@ -3,6 +3,7 @@ module.exports = (api, { tsLint, lintOn = [] }, _, invoking) => { + debugger if (typeof lintOn === 'string') { lintOn = lintOn.split(',') } diff --git a/packages/@vue/cli-ui/apollo-server/connectors/dependencies.js b/packages/@vue/cli-ui/apollo-server/connectors/dependencies.js index e148cb0c76..d8190c1b10 100644 --- a/packages/@vue/cli-ui/apollo-server/connectors/dependencies.js +++ b/packages/@vue/cli-ui/apollo-server/connectors/dependencies.js @@ -211,7 +211,7 @@ function install ({ id, type, range }, context) { arg = id } - await installPackage(cwd.get(), getCommand(cwd.get()), null, arg, type === 'devDependencies') + await installPackage(cwd.get(), getCommand(cwd.get()), arg, type === 'devDependencies') logs.add({ message: `Dependency ${id} installed`, @@ -239,7 +239,7 @@ function uninstall ({ id }, context) { const dep = findOne(id, context) - await uninstallPackage(cwd.get(), getCommand(cwd.get()), null, id) + await uninstallPackage(cwd.get(), getCommand(cwd.get()), id) logs.add({ message: `Dependency ${id} uninstalled`, @@ -265,7 +265,7 @@ function update ({ id }, context) { const dep = findOne(id, context) const { current, wanted } = await getVersion(dep, context) - await updatePackage(cwd.get(), getCommand(cwd.get()), null, id) + await updatePackage(cwd.get(), getCommand(cwd.get()), id) logs.add({ message: `Dependency ${id} updated from ${current} to ${wanted}`, @@ -310,7 +310,7 @@ function updateAll (context) { args: [updatedDeps.length] }) - await updatePackage(cwd.get(), getCommand(cwd.get()), null, updatedDeps.map( + await updatePackage(cwd.get(), getCommand(cwd.get()), updatedDeps.map( p => p.id ).join(' ')) diff --git a/packages/@vue/cli-ui/apollo-server/connectors/plugins.js b/packages/@vue/cli-ui/apollo-server/connectors/plugins.js index b33085f8c3..c41a6991db 100644 --- a/packages/@vue/cli-ui/apollo-server/connectors/plugins.js +++ b/packages/@vue/cli-ui/apollo-server/connectors/plugins.js @@ -334,7 +334,7 @@ function install (id, context) { if (process.env.VUE_CLI_DEBUG && isOfficialPlugin(id)) { mockInstall(id, context) } else { - await installPackage(cwd.get(), getCommand(cwd.get()), null, id) + await installPackage(cwd.get(), getCommand(cwd.get()), id) } await initPrompts(id, context) installationStep = 'config' @@ -412,7 +412,7 @@ function uninstall (id, context) { if (process.env.VUE_CLI_DEBUG && isOfficialPlugin(id)) { mockUninstall(id, context) } else { - await uninstallPackage(cwd.get(), getCommand(cwd.get()), null, id) + await uninstallPackage(cwd.get(), getCommand(cwd.get()), id) } currentPluginId = null installationStep = null @@ -520,7 +520,7 @@ function update ({ id, full }, context) { if (localPath) { await updateLocalPackage({ cwd: cwd.get(), id, localPath, full }, context) } else { - await updatePackage(cwd.get(), getCommand(cwd.get()), null, id) + await updatePackage(cwd.get(), getCommand(cwd.get()), id) } logs.add({ @@ -583,7 +583,7 @@ async function updateAll (context) { args: [updatedPlugins.length] }) - await updatePackage(cwd.get(), getCommand(cwd.get()), null, updatedPlugins.map( + await updatePackage(cwd.get(), getCommand(cwd.get()), updatedPlugins.map( p => p.id ).join(' ')) diff --git a/packages/@vue/cli/lib/Creator.js b/packages/@vue/cli/lib/Creator.js index 250bfbd83c..13276b0588 100644 --- a/packages/@vue/cli/lib/Creator.js +++ b/packages/@vue/cli/lib/Creator.js @@ -159,7 +159,7 @@ module.exports = class Creator extends EventEmitter { // in development, avoid installation process await require('./util/setupDevProject')(context) } else { - await installDeps(context, packageManager, cliOptions.registry) + await installDeps(context, packageManager) } // run generator @@ -180,7 +180,7 @@ module.exports = class Creator extends EventEmitter { this.emit('creation', { event: 'deps-install' }) log() if (!isTestOrDebug) { - await installDeps(context, packageManager, cliOptions.registry) + await installDeps(context, packageManager) } // run complete cbs if any (injected by generators) diff --git a/packages/@vue/cli/lib/add.js b/packages/@vue/cli/lib/add.js index 07640da6c3..848a843cd2 100644 --- a/packages/@vue/cli/lib/add.js +++ b/packages/@vue/cli/lib/add.js @@ -28,7 +28,7 @@ async function add (pluginName, options = {}, context = process.cwd()) { log() const packageManager = loadOptions().packageManager || (hasProjectYarn(context) ? 'yarn' : hasProjectPnpm(context) ? 'pnpm' : 'npm') - await installPackage(context, packageManager, options.registry, packageName) + await installPackage(context, packageManager, packageName) log(`${chalk.green('✔')} Successfully installed plugin: ${chalk.cyan(packageName)}`) log() diff --git a/packages/@vue/cli/lib/invoke.js b/packages/@vue/cli/lib/invoke.js index e3e6ee817e..e9120e8ae1 100644 --- a/packages/@vue/cli/lib/invoke.js +++ b/packages/@vue/cli/lib/invoke.js @@ -127,7 +127,7 @@ async function runGenerator (context, plugin, pkg = getPkg(context)) { log() const packageManager = loadOptions().packageManager || (hasProjectYarn(context) ? 'yarn' : hasProjectPnpm(context) ? 'pnpm' : 'npm') - await installDeps(context, packageManager, plugin.options && plugin.options.registry) + await installDeps(context, packageManager) } if (createCompleteCbs.length) { diff --git a/packages/@vue/cli/lib/upgrade.js b/packages/@vue/cli/lib/upgrade.js index c6727a2f2e..59995f2958 100644 --- a/packages/@vue/cli/lib/upgrade.js +++ b/packages/@vue/cli/lib/upgrade.js @@ -49,13 +49,9 @@ async function runMigrator (packageName, options, context) { const pluginMigrator = loadModule(`${packageName}/migrator`, context) if (!pluginMigrator) { return } - const { registry } = options const plugin = { packageName, - apply: migrator, - options: { - registry - } + apply: migrator } const pkg = getPackageJson(context) @@ -86,7 +82,7 @@ async function runMigrator (packageName, options, context) { log(`📦 Installing additional dependencies...`) log() const packageManager = getCommand(context) - await installDeps(context, packageManager, plugin.options && plugin.options.registry) + await installDeps(context, packageManager) } if (createCompleteCbs.length) { @@ -146,7 +142,7 @@ async function upgradeSinglePackage (packageName, options, context) { logWithSpinner(`Getting max satisfying version of ${packageName}@${options.to}`) } - targetVersion = await getVersion(packageName, targetVersion, options.registry, context) + targetVersion = await getVersion(packageName, targetVersion, context) stopSpinner() } @@ -166,7 +162,7 @@ async function upgradeSinglePackage (packageName, options, context) { // TODO: integrate getCommand into installPackage const command = getCommand(context) - await installPackage(context, command, options.registry, packageName) + await installPackage(context, command, packageName) await runMigrator(packageName, { installed }, context) } diff --git a/packages/@vue/cli/lib/util/getPackageVersion.js b/packages/@vue/cli/lib/util/getPackageVersion.js index d4e9e5b2df..1a7c58c44e 100644 --- a/packages/@vue/cli/lib/util/getPackageVersion.js +++ b/packages/@vue/cli/lib/util/getPackageVersion.js @@ -1,10 +1,9 @@ const { request } = require('@vue/cli-shared-utils') +const { getRegistry } = require('./packageManager') module.exports = async function getPackageVersion (id, range = '', registry) { if (!registry) { - registry = (await require('./shouldUseTaobao')()) - ? `https://registry.npm.taobao.org` - : `https://registry.npmjs.org` + registry = await getRegistry() } let result diff --git a/packages/@vue/cli/lib/util/installDeps.js b/packages/@vue/cli/lib/util/installDeps.js index 8c5613453c..3c25cc75a5 100644 --- a/packages/@vue/cli/lib/util/installDeps.js +++ b/packages/@vue/cli/lib/util/installDeps.js @@ -3,7 +3,7 @@ const chalk = require('chalk') const execa = require('execa') const readline = require('readline') const registries = require('./registries') -const shouldUseTaobao = require('./shouldUseTaobao') +const { getRegistry } = require('./packageManager') const debug = require('debug')('vue-cli:install') @@ -68,14 +68,8 @@ function renderProgressBar (curr, total) { process.stderr.write(`[${complete}${incomplete}]${bar}`) } -async function addRegistryToArgs (command, args, cliRegistry) { - const altRegistry = ( - cliRegistry || ( - (await shouldUseTaobao(command)) - ? registries.taobao - : null - ) - ) +async function addRegistryToArgs (command, args) { + const altRegistry = await getRegistry({ packageManager: command }) if (altRegistry) { args.push(`--registry=${altRegistry}`) @@ -171,7 +165,7 @@ function executeCommand (command, args, targetDir) { }) } -exports.installDeps = async function installDeps (targetDir, command, cliRegistry) { +exports.installDeps = async function installDeps (targetDir, command) { checkPackageManagerIsSupported(command) const args = [] @@ -186,7 +180,7 @@ exports.installDeps = async function installDeps (targetDir, command, cliRegistr args.push('--shamefully-flatten') } - await addRegistryToArgs(command, args, cliRegistry) + await addRegistryToArgs(command, args) debug(`command: `, command) debug(`args: `, args) @@ -194,7 +188,7 @@ exports.installDeps = async function installDeps (targetDir, command, cliRegistr await executeCommand(command, args, targetDir) } -exports.installPackage = async function (targetDir, command, cliRegistry, packageName, dev = true) { +exports.installPackage = async function (targetDir, command, packageName, dev = true) { checkPackageManagerIsSupported(command) const args = [] @@ -207,7 +201,7 @@ exports.installPackage = async function (targetDir, command, cliRegistry, packag if (dev) args.push('-D') - await addRegistryToArgs(command, args, cliRegistry) + await addRegistryToArgs(command, args) args.push(packageName) @@ -217,7 +211,7 @@ exports.installPackage = async function (targetDir, command, cliRegistry, packag await executeCommand(command, args, targetDir) } -exports.uninstallPackage = async function (targetDir, command, cliRegistry, packageName) { +exports.uninstallPackage = async function (targetDir, command, packageName) { checkPackageManagerIsSupported(command) const args = [] @@ -228,7 +222,7 @@ exports.uninstallPackage = async function (targetDir, command, cliRegistry, pack args.push('remove') } - await addRegistryToArgs(command, args, cliRegistry) + await addRegistryToArgs(command, args) args.push(packageName) @@ -238,7 +232,7 @@ exports.uninstallPackage = async function (targetDir, command, cliRegistry, pack await executeCommand(command, args, targetDir) } -exports.updatePackage = async function (targetDir, command, cliRegistry, packageName) { +exports.updatePackage = async function (targetDir, command, packageName) { checkPackageManagerIsSupported(command) const args = [] @@ -249,7 +243,7 @@ exports.updatePackage = async function (targetDir, command, cliRegistry, package args.push('upgrade') } - await addRegistryToArgs(command, args, cliRegistry) + await addRegistryToArgs(command, args) packageName.split(' ').forEach(name => args.push(name)) diff --git a/packages/@vue/cli/lib/util/packageManager.js b/packages/@vue/cli/lib/util/packageManager.js index de34597b06..99ca49c1e3 100644 --- a/packages/@vue/cli/lib/util/packageManager.js +++ b/packages/@vue/cli/lib/util/packageManager.js @@ -1,4 +1,5 @@ const execa = require('execa') +const minimist = require('minimist') const semver = require('semver') const { hasYarn, @@ -6,31 +7,59 @@ const { hasPnpm3OrLater, hasProjectPnpm } = require('@vue/cli-shared-utils') + const { loadOptions } = require('../options') +const registries = require('./registries') +const shouldUseTaobao = require('./shouldUseTaobao') -function getCommand (cwd = undefined) { +function getCommand (cwd) { if (!cwd) { return loadOptions().packageManager || (hasYarn() ? 'yarn' : hasPnpm3OrLater() ? 'pnpm' : 'npm') } return hasProjectYarn(cwd) ? 'yarn' : hasProjectPnpm(cwd) ? 'pnpm' : 'npm' } -// TODO: add cache -async function getMetadata (packageName, { field = '', packageManager, registry, cwd }) { +// Any command that implemented registry-related feature should support +// `-r` / `--registry` option +async function getRegistry ({ cwd, packageManager } = {}) { + const args = minimist(process.argv, { + alias: { + r: 'registry' + } + }) + + if (args.registry) { + return args.registry + } + + if (await shouldUseTaobao()) { + return registries.taobao + } + if (!packageManager) { packageManager = getCommand(cwd) } + const { stdout } = await execa(packageManager, ['config', 'get', 'registry']) + return stdout +} - if (!registry) { - if ((await require('./shouldUseTaobao')())) { - registry = `https://registry.npm.taobao.org` - } else { - const { stdout } = await execa(packageManager, ['config', 'get', 'registry']) - registry = stdout - } +// TODO: add cache +async function getMetadata (packageName, { field = '', packageManager, cwd } = {}) { + if (!packageManager) { + packageManager = getCommand(cwd) } + const registry = await getRegistry({ cwd, packageManager }) - const { stdout } = await execa(packageManager, ['info', packageName, field, '--json']) + const { stdout } = await execa( + packageManager, + [ + 'info', + packageName, + field, + '--json', + '--registry', + registry + ]) const metadata = JSON.parse(stdout) if (packageManager === 'yarn') { @@ -40,8 +69,8 @@ async function getMetadata (packageName, { field = '', packageManager, registry, return metadata } -async function getVersion (packageName, versionRange, registry, cwd) { - const metadata = await getMetadata(packageName, { registry, cwd }) +async function getVersion (packageName, versionRange, cwd) { + const metadata = await getMetadata(packageName, { cwd }) if (Object.keys(metadata['dist-tags']).includes(versionRange)) { return metadata['dist-tags'][versionRange] } @@ -51,6 +80,7 @@ async function getVersion (packageName, versionRange, registry, cwd) { module.exports = { getCommand, + getRegistry, getMetadata, getVersion } From 369e9ca3c98e1e842990c6b73f2398484d8224d3 Mon Sep 17 00:00:00 2001 From: Haoqun Jiang Date: Tue, 18 Jun 2019 15:07:55 +0800 Subject: [PATCH 04/20] feat: implement update package.json only --- packages/@vue/cli/lib/upgrade.js | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/packages/@vue/cli/lib/upgrade.js b/packages/@vue/cli/lib/upgrade.js index 59995f2958..b12a574629 100644 --- a/packages/@vue/cli/lib/upgrade.js +++ b/packages/@vue/cli/lib/upgrade.js @@ -128,8 +128,18 @@ async function runMigrator (packageName, options, context) { } async function upgradeSinglePackage (packageName, options, context) { - // FIXME: properly implement this - const required = require(`${context}/package.json`).devDependencies[packageName] + const pkg = getPackageJson(context) + let depEntry, required + for (const depType of ['dependencies', 'devDependencies', 'optionalDependencies']) { + if (pkg[depType] && pkg[depType][packageName]) { + depEntry = depType + required = pkg[depType][packageName] + break + } + } + if (!required) { + throw new Error(`Can't find ${chalk.yellow(packageName)} in ${chalk.yellow('package.json')}`) + } const installed = getInstalledVersion(packageName) let targetVersion = options.to || 'latest' @@ -151,8 +161,8 @@ async function upgradeSinglePackage (packageName, options, context) { const newRange = tryGetNewerRange(`^${targetVersion}`, required) if (newRange !== required) { - // TODO: - // extendPackage({ devDependencies: { packageName: newRange } }) + pkg[depEntry][packageName] = newRange + fs.writeFileSync(path.resolve(context, 'package.json'), JSON.stringify(pkg, null, 2)) log(`${chalk.green('✔')} Updated version range in ${chalk.yellow('package.json')}`) } return From bc7be7e9b878d1ec9fb3e1d5264648e164d17ce0 Mon Sep 17 00:00:00 2001 From: Haoqun Jiang Date: Tue, 18 Jun 2019 17:19:52 +0800 Subject: [PATCH 05/20] refactor: make the code conciser --- packages/@vue/cli/lib/upgrade.js | 30 +++++------------------------- 1 file changed, 5 insertions(+), 25 deletions(-) diff --git a/packages/@vue/cli/lib/upgrade.js b/packages/@vue/cli/lib/upgrade.js index b12a574629..27e7b6ab53 100644 --- a/packages/@vue/cli/lib/upgrade.js +++ b/packages/@vue/cli/lib/upgrade.js @@ -2,8 +2,6 @@ const fs = require('fs') const path = require('path') const chalk = require('chalk') const execa = require('execa') -const globby = require('globby') -const { isBinaryFileSync } = require('isbinaryfile') const { log, error, @@ -18,30 +16,14 @@ const { } = require('@vue/cli-shared-utils') const Migrator = require('./Migrator') + const { getCommand, getVersion } = require('./util/packageManager') +const { installDeps, installPackage } = require('./util/installDeps') + const getPackageJson = require('./util/getPackageJson') const getInstalledVersion = require('./util/getInstalledVersion') const tryGetNewerRange = require('./util/tryGetNewerRange') -const { installDeps, installPackage } = require('./util/installDeps') -const normalizeFilePaths = require('./util/normalizeFilePaths') - -async function readFiles (context) { - const files = await globby(['**'], { - cwd: context, - onlyFiles: true, - gitignore: true, - ignore: ['**/node_modules/**', '**/.git/**'], - dot: true - }) - const res = {} - for (const file of files) { - const name = path.resolve(context, file) - res[file] = isBinaryFileSync(name) - ? fs.readFileSync(name) - : fs.readFileSync(name, 'utf-8') - } - return normalizeFilePaths(res) -} +const readFiles = require('./util/readFiles') async function runMigrator (packageName, options, context) { const isTestOrDebug = process.env.VUE_CLI_TEST || process.env.VUE_CLI_DEBUG @@ -170,9 +152,7 @@ async function upgradeSinglePackage (packageName, options, context) { log(`Upgrading ${packageName} from ${installed} to ${targetVersion}`) - // TODO: integrate getCommand into installPackage - const command = getCommand(context) - await installPackage(context, command, packageName) + await installPackage(context, getCommand(context), packageName) await runMigrator(packageName, { installed }, context) } From 30a4f8a9183a8cad7a16287507b187581e0fdd44 Mon Sep 17 00:00:00 2001 From: Haoqun Jiang Date: Thu, 20 Jun 2019 10:18:06 +0800 Subject: [PATCH 06/20] refactor: replace installPackage with updatePackage --- packages/@vue/cli/bin/vue.js | 2 +- packages/@vue/cli/lib/upgrade.js | 11 ++++++----- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/packages/@vue/cli/bin/vue.js b/packages/@vue/cli/bin/vue.js index 2990d2fb23..270a8ee553 100755 --- a/packages/@vue/cli/bin/vue.js +++ b/packages/@vue/cli/bin/vue.js @@ -168,7 +168,7 @@ program program .command('upgrade [package-name]') .description('upgrade vue cli service / plugins') - .option('-t, --to ', 'upgrade to a version that is not latest') + .option('-t, --to ', 'upgrade to a version that is not latest') .option('-r, --registry ', 'Use specified npm registry when installing dependencies') .action((packageName, cmd) => { require('../lib/upgrade')(packageName, cleanArgs(cmd)) diff --git a/packages/@vue/cli/lib/upgrade.js b/packages/@vue/cli/lib/upgrade.js index 27e7b6ab53..2f0d2fe90c 100644 --- a/packages/@vue/cli/lib/upgrade.js +++ b/packages/@vue/cli/lib/upgrade.js @@ -18,7 +18,7 @@ const { const Migrator = require('./Migrator') const { getCommand, getVersion } = require('./util/packageManager') -const { installDeps, installPackage } = require('./util/installDeps') +const { installDeps, updatePackage } = require('./util/installDeps') const getPackageJson = require('./util/getPackageJson') const getInstalledVersion = require('./util/getInstalledVersion') @@ -152,7 +152,7 @@ async function upgradeSinglePackage (packageName, options, context) { log(`Upgrading ${packageName} from ${installed} to ${targetVersion}`) - await installPackage(context, getCommand(context), packageName) + await updatePackage(context, getCommand(context), `${packageName}@^${targetVersion}`) await runMigrator(packageName, { installed }, context) } @@ -182,13 +182,14 @@ async function upgradeAll (context) { } } + // TODO: format the output + // https://github.com/angular/angular-cli/blob/34a55c96b2ed38b226879913839b97c601387653/packages/schematics/update/update/index.ts#L490-L509 + log(upgradable) + // TODO: upgrade all (interactive) // for patch & minor versions, upgrade directly // for major versions, prompt before upgrading - // TODO: format the output https://github.com/angular/angular-cli/blob/34a55c96b2ed38b226879913839b97c601387653/packages/schematics/update/update/index.ts#L490-L509 - log(upgradable) - failSpinner() return Promise.reject() From ff8bba43a6451ddf3a761dbd3470c5414bc987fd Mon Sep 17 00:00:00 2001 From: Haoqun Jiang Date: Thu, 20 Jun 2019 16:03:10 +0800 Subject: [PATCH 07/20] feat: add cache for metadata --- packages/@vue/cli/lib/util/packageManager.js | 24 ++++++++++++++++---- packages/@vue/cli/package.json | 1 + 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/packages/@vue/cli/lib/util/packageManager.js b/packages/@vue/cli/lib/util/packageManager.js index 99ca49c1e3..34eb1832a8 100644 --- a/packages/@vue/cli/lib/util/packageManager.js +++ b/packages/@vue/cli/lib/util/packageManager.js @@ -1,6 +1,8 @@ const execa = require('execa') const minimist = require('minimist') const semver = require('semver') +const LRU = require('lru-cache') + const { hasYarn, hasProjectYarn, @@ -43,13 +45,24 @@ async function getRegistry ({ cwd, packageManager } = {}) { return stdout } -// TODO: add cache +const metadataCache = new LRU({ + max: 200, + maxAge: 1000 * 60 * 30 // 30 min. +}) + async function getMetadata (packageName, { field = '', packageManager, cwd } = {}) { if (!packageManager) { packageManager = getCommand(cwd) } const registry = await getRegistry({ cwd, packageManager }) + const metadataKey = `${packageManager}-${registry}-${packageName}` + let metadata = metadataCache.get(metadataKey) + + if (metadata) { + return metadata + } + const { stdout } = await execa( packageManager, [ @@ -59,13 +72,16 @@ async function getMetadata (packageName, { field = '', packageManager, cwd } = { '--json', '--registry', registry - ]) - const metadata = JSON.parse(stdout) + ] + ) + metadata = JSON.parse(stdout) if (packageManager === 'yarn') { // `yarn info` outputs messages in the form of `{"type": "inspect", data: {}}` - return metadata.data + metadata = metadata.data } + + metadataCache.set(metadataKey, metadata) return metadata } diff --git a/packages/@vue/cli/package.json b/packages/@vue/cli/package.json index b609233d19..e976b83efd 100644 --- a/packages/@vue/cli/package.json +++ b/packages/@vue/cli/package.json @@ -47,6 +47,7 @@ "js-yaml": "^3.13.1", "jscodeshift": "^0.6.4", "lodash.clonedeep": "^4.5.0", + "lru-cache": "^5.1.1", "minimist": "^1.2.0", "recast": "^0.18.1", "request": "^2.87.0", From e805dd244234ae9ab3b8530777dcc05ffa4477eb Mon Sep 17 00:00:00 2001 From: Haoqun Jiang Date: Thu, 20 Jun 2019 16:46:53 +0800 Subject: [PATCH 08/20] feat: implement checkForUpdates --- packages/@vue/cli/lib/upgrade.js | 74 ++++++++++++++++++++++---------- 1 file changed, 52 insertions(+), 22 deletions(-) diff --git a/packages/@vue/cli/lib/upgrade.js b/packages/@vue/cli/lib/upgrade.js index 2f0d2fe90c..44b1bf90f8 100644 --- a/packages/@vue/cli/lib/upgrade.js +++ b/packages/@vue/cli/lib/upgrade.js @@ -5,9 +5,10 @@ const execa = require('execa') const { log, error, + done, + logWithSpinner, stopSpinner, - failSpinner, isPlugin, loadModule, @@ -156,43 +157,68 @@ async function upgradeSinglePackage (packageName, options, context) { await runMigrator(packageName, { installed }, context) } -async function upgradeAll (context) { +async function getUpgradable (context) { // get current deps // filter @vue/cli-service, @vue/cli-plugin-* & vue-cli-plugin-* const pkg = getPackageJson(context) - const upgradable = { - dependencies: {}, - devDependencies: {}, - optionalDependencies: {} - } + const upgradable = [] - logWithSpinner('Gathering update information...') - for (const depType of Object.keys(upgradable)) { - for (const [packageName, range] of Object.entries(pkg[depType] || {})) { - if (packageName !== '@vue/cli-service' && !isPlugin(packageName)) { + for (const depType of ['dependencies', 'devDependencies', 'optionalDependencies']) { + for (const [name, range] of Object.entries(pkg[depType] || {})) { + if (name !== '@vue/cli-service' && !isPlugin(name)) { continue } - const latest = await getVersion(packageName, 'latest', undefined, context) + const installed = await getInstalledVersion(name) + const wanted = await getVersion(name, range, context) + const latest = await getVersion(name, 'latest', context) const latestRange = `^${latest}` - if (range !== latestRange) { - upgradable[depType][packageName] = latestRange + if (installed !== latest || range !== latestRange) { + upgradable.push({ name, installed, wanted, latest }) } } } - // TODO: format the output - // https://github.com/angular/angular-cli/blob/34a55c96b2ed38b226879913839b97c601387653/packages/schematics/update/update/index.ts#L490-L509 - log(upgradable) + return upgradable +} + +async function checkForUpdates (context) { + logWithSpinner('Gathering pacakage information...') + const upgradable = await getUpgradable(context) + stopSpinner() + if (!upgradable.length) { + done('Seems all plugins are up to date. Good work!') + return + } + + // format the output + // adapted from @angular/cli + const names = upgradable.map(dep => dep.name) + let namePad = Math.max(...names.map(x => x.length)) + 2 + if (!Number.isFinite(namePad)) { + namePad = 30 + } + const pads = [namePad, 12, 12, 12, 0] + console.log( + ' ' + + ['Name', 'Installed', 'Wanted', 'Latest', 'Command to upgrade'].map((x, i) => x.padEnd(pads[i])).join('') + ) + // eslint-disable-next-line no-return-assign + console.log((' ' + '-'.repeat(pads.reduce((s, x) => s += x, 0) + 20))) + for (const p of upgradable) { + const fields = [p.name, p.installed, p.wanted, p.latest, `vue upgrade ${p.name}`] + console.log(' ' + fields.map((x, i) => x.padEnd(pads[i])).join('')) + } + + return upgradable +} + +async function upgradeAll (context) { // TODO: upgrade all (interactive) // for patch & minor versions, upgrade directly // for major versions, prompt before upgrading - - failSpinner() - - return Promise.reject() } async function upgrade (packageName, options, context = process.cwd()) { @@ -202,7 +228,11 @@ async function upgrade (packageName, options, context = process.cwd()) { process.exit(1) } - return upgradeAll(context) + if (options.all) { + return upgradeAll(context) + } + + return checkForUpdates(context) } return upgradeSinglePackage(packageName, options, context) From 0082d67a24678b73642242526975215882d56ef4 Mon Sep 17 00:00:00 2001 From: Haoqun Jiang Date: Sat, 22 Jun 2019 00:47:08 +0800 Subject: [PATCH 09/20] chore: use `chalk.underline` to draw the border line --- packages/@vue/cli/lib/upgrade.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/@vue/cli/lib/upgrade.js b/packages/@vue/cli/lib/upgrade.js index 44b1bf90f8..d9a375b5f4 100644 --- a/packages/@vue/cli/lib/upgrade.js +++ b/packages/@vue/cli/lib/upgrade.js @@ -203,10 +203,10 @@ async function checkForUpdates (context) { const pads = [namePad, 12, 12, 12, 0] console.log( ' ' + - ['Name', 'Installed', 'Wanted', 'Latest', 'Command to upgrade'].map((x, i) => x.padEnd(pads[i])).join('') + ['Name', 'Installed', 'Wanted', 'Latest', 'Command to upgrade'].map( + (x, i) => chalk.underline(x.padEnd(pads[i])) + ).join('') ) - // eslint-disable-next-line no-return-assign - console.log((' ' + '-'.repeat(pads.reduce((s, x) => s += x, 0) + 20))) for (const p of upgradable) { const fields = [p.name, p.installed, p.wanted, p.latest, `vue upgrade ${p.name}`] console.log(' ' + fields.map((x, i) => x.padEnd(pads[i])).join('')) From 582b1f1e462f76d77fecc4f5833e7e1fc1b416a6 Mon Sep 17 00:00:00 2001 From: Haoqun Jiang Date: Mon, 24 Jun 2019 21:04:38 +0800 Subject: [PATCH 10/20] fix: fix runtime bugs --- packages/@vue/cli/lib/Migrator.js | 27 ++++++++++++++++++++++++--- packages/@vue/cli/lib/MigratorAPI.js | 1 + packages/@vue/cli/lib/upgrade.js | 10 ++++++---- 3 files changed, 31 insertions(+), 7 deletions(-) diff --git a/packages/@vue/cli/lib/Migrator.js b/packages/@vue/cli/lib/Migrator.js index efbfe55abb..ccdf2918a2 100644 --- a/packages/@vue/cli/lib/Migrator.js +++ b/packages/@vue/cli/lib/Migrator.js @@ -1,8 +1,29 @@ const Generator = require('./Generator') +const MigratorAPI = require('./MigratorAPI') + +const inferRootOptions = require('./util/inferRootOptions') module.exports = class Migrator extends Generator { - constructor (context, options) { - super(context, options) - this.migrate = this.generate + constructor (context, { + plugin, + + pkg = {}, + completeCbs = [], + files = {}, + invoking = false + } = {}) { + super(context, { + pkg, + plugins: [], + completeCbs, + files, + invoking + }) + this.plugins = [plugin] + + const rootOptions = inferRootOptions(pkg) + // apply migrators from plugins + const api = new MigratorAPI(plugin.id, plugin.installed, this, plugin.options, rootOptions) + plugin.apply(api, plugin.options, rootOptions, invoking) } } diff --git a/packages/@vue/cli/lib/MigratorAPI.js b/packages/@vue/cli/lib/MigratorAPI.js index 80d3d3b955..492dea8153 100644 --- a/packages/@vue/cli/lib/MigratorAPI.js +++ b/packages/@vue/cli/lib/MigratorAPI.js @@ -17,6 +17,7 @@ class MigratorAPI extends GeneratorAPI { } fromVersion (range) { + console.log('fromVersion', range) return semver.satisfies(this.installedVersion, range) } } diff --git a/packages/@vue/cli/lib/upgrade.js b/packages/@vue/cli/lib/upgrade.js index d9a375b5f4..7b47d9ee19 100644 --- a/packages/@vue/cli/lib/upgrade.js +++ b/packages/@vue/cli/lib/upgrade.js @@ -33,22 +33,24 @@ async function runMigrator (packageName, options, context) { if (!pluginMigrator) { return } const plugin = { - packageName, - apply: migrator + id: packageName, + apply: pluginMigrator, + installed: options.installed } const pkg = getPackageJson(context) const createCompleteCbs = [] const migrator = new Migrator(context, { + plugin: plugin, + pkg, - plugins: [plugin], files: await readFiles(context), completeCbs: createCompleteCbs, invoking: true }) log(`🚀 Running migrator of ${packageName}`) - await migrator.migrate({ + await migrator.generate({ extractConfigFiles: true, checkExisting: true }) From 0dd33c0153955a227bcf9ebc936fdcf3b251010a Mon Sep 17 00:00:00 2001 From: Haoqun Jiang Date: Fri, 28 Jun 2019 09:47:54 +0800 Subject: [PATCH 11/20] chore: remove legacy @vue/cli-upgrade pacakage --- .../@vue/cli-upgrade/bin/vue-cli-upgrade.js | 3 - .../@vue/cli-upgrade/get-installed-version.js | 14 -- packages/@vue/cli-upgrade/get-package-json.js | 21 --- .../cli-upgrade/get-upgradable-version.js | 38 ----- packages/@vue/cli-upgrade/index.js | 154 ------------------ packages/@vue/cli-upgrade/package.json | 30 ---- 6 files changed, 260 deletions(-) delete mode 100644 packages/@vue/cli-upgrade/bin/vue-cli-upgrade.js delete mode 100644 packages/@vue/cli-upgrade/get-installed-version.js delete mode 100644 packages/@vue/cli-upgrade/get-package-json.js delete mode 100644 packages/@vue/cli-upgrade/get-upgradable-version.js delete mode 100644 packages/@vue/cli-upgrade/index.js delete mode 100644 packages/@vue/cli-upgrade/package.json diff --git a/packages/@vue/cli-upgrade/bin/vue-cli-upgrade.js b/packages/@vue/cli-upgrade/bin/vue-cli-upgrade.js deleted file mode 100644 index 796786c303..0000000000 --- a/packages/@vue/cli-upgrade/bin/vue-cli-upgrade.js +++ /dev/null @@ -1,3 +0,0 @@ -const vueCliUpgrade = require('../index') - -vueCliUpgrade() diff --git a/packages/@vue/cli-upgrade/get-installed-version.js b/packages/@vue/cli-upgrade/get-installed-version.js deleted file mode 100644 index ea6307f760..0000000000 --- a/packages/@vue/cli-upgrade/get-installed-version.js +++ /dev/null @@ -1,14 +0,0 @@ -const path = require('path') -const getPackageJson = require('./get-package-json') - -module.exports = function getInstalledVersion (packageName) { - // for first level deps, read package.json directly is way faster than `npm list` - try { - const packageJson = getPackageJson( - path.resolve(process.cwd(), 'node_modules', packageName) - ) - return packageJson.version - } catch (e) { - return 'N/A' - } -} diff --git a/packages/@vue/cli-upgrade/get-package-json.js b/packages/@vue/cli-upgrade/get-package-json.js deleted file mode 100644 index 7049cc39e5..0000000000 --- a/packages/@vue/cli-upgrade/get-package-json.js +++ /dev/null @@ -1,21 +0,0 @@ -const fs = require('fs') -const path = require('path') - -module.exports = function getPackageJson (projectPath) { - const packagePath = path.join(projectPath, 'package.json') - - let packageJson - try { - packageJson = fs.readFileSync(packagePath, 'utf-8') - } catch (err) { - throw new Error(`${packagePath} not exist`) - } - - try { - packageJson = JSON.parse(packageJson) - } catch (err) { - throw new Error('The package.json is malformed') - } - - return packageJson -} diff --git a/packages/@vue/cli-upgrade/get-upgradable-version.js b/packages/@vue/cli-upgrade/get-upgradable-version.js deleted file mode 100644 index dc1166a4c0..0000000000 --- a/packages/@vue/cli-upgrade/get-upgradable-version.js +++ /dev/null @@ -1,38 +0,0 @@ -const execa = require('execa') - -function getMaxSatisfying (packageName, range) { - let version = JSON.parse( - execa.shellSync(`npm view ${packageName}@${range} version --json`).stdout - ) - - if (typeof version !== 'string') { - version = version[0] - } - - return version -} - -module.exports = function getUpgradableVersion ( - packageName, - currRange, - semverLevel -) { - let newRange - if (semverLevel === 'patch') { - const currMaxVersion = getMaxSatisfying(packageName, currRange) - newRange = `~${currMaxVersion}` - const newMaxVersion = getMaxSatisfying(packageName, newRange) - newRange = `~${newMaxVersion}` - } else if (semverLevel === 'minor') { - const currMaxVersion = getMaxSatisfying(packageName, currRange) - newRange = `^${currMaxVersion}` - const newMaxVersion = getMaxSatisfying(packageName, newRange) - newRange = `^${newMaxVersion}` - } else if (semverLevel === 'major') { - newRange = `^${getMaxSatisfying(packageName, 'latest')}` - } else { - throw new Error('Release type must be one of patch | minor | major!') - } - - return newRange -} diff --git a/packages/@vue/cli-upgrade/index.js b/packages/@vue/cli-upgrade/index.js deleted file mode 100644 index d7c008c67e..0000000000 --- a/packages/@vue/cli-upgrade/index.js +++ /dev/null @@ -1,154 +0,0 @@ -const fs = require('fs') -const path = require('path') - -const chalk = require('chalk') -const Table = require('cli-table') -const inquirer = require('inquirer') - -/* eslint-disable node/no-extraneous-require */ -const { - hasYarn, - logWithSpinner, - stopSpinner -} = require('@vue/cli-shared-utils') -const { loadOptions } = require('@vue/cli/lib/options') -const { installDeps } = require('@vue/cli/lib/util/installDeps') -/* eslint-enable node/no-extraneous-require */ - -const getPackageJson = require('./get-package-json') -const getInstalledVersion = require('./get-installed-version') -const getUpgradableVersion = require('./get-upgradable-version') - -const projectPath = process.cwd() - -// - Resolve the version to upgrade to. -// - `vue upgrade [patch|minor|major]`: defaults to minor -// - If already latest, print message and exit -// - Otherwise, confirm via prompt - -function isCorePackage (packageName) { - return ( - packageName === '@vue/cli-service' || - packageName.startsWith('@vue/cli-plugin-') - ) -} - -function shouldUseYarn () { - // infer from lockfiles first - if (fs.existsSync(path.resolve(projectPath, 'package-lock.json'))) { - return false - } - - if (fs.existsSync(path.resolve(projectPath, 'yarn.lock')) && hasYarn()) { - return true - } - - // fallback to packageManager field in ~/.vuerc - const { packageManager } = loadOptions() - if (packageManager) { - return packageManager === 'yarn' - } - - return hasYarn() -} - -module.exports = async function vueCliUpgrade (semverLevel = 'minor') { - // get current deps - // filter @vue/cli-service & @vue/cli-plugin-* - const pkg = getPackageJson(projectPath) - const upgradableDepMaps = new Map([ - ['dependencies', new Map()], - ['devDependencies', new Map()], - ['optionalDependencies', new Map()] - ]) - - logWithSpinner('Gathering update information...') - for (const depType of upgradableDepMaps.keys()) { - for (const [packageName, currRange] of Object.entries(pkg[depType] || {})) { - if (!isCorePackage(packageName)) { - continue - } - - const upgradable = getUpgradableVersion( - packageName, - currRange, - semverLevel - ) - if (upgradable !== currRange) { - upgradableDepMaps.get(depType).set(packageName, upgradable) - } - } - } - - const table = new Table({ - head: ['package', 'installed', '', 'upgraded'], - colAligns: ['left', 'right', 'right', 'right'], - chars: { - top: '', - 'top-mid': '', - 'top-left': '', - 'top-right': '', - bottom: '', - 'bottom-mid': '', - 'bottom-left': '', - 'bottom-right': '', - left: '', - 'left-mid': '', - mid: '', - 'mid-mid': '', - right: '', - 'right-mid': '', - middle: '' - } - }) - - for (const [depType, depMap] of upgradableDepMaps.entries()) { - for (const packageName of depMap.keys()) { - const installedVersion = getInstalledVersion(packageName) - const upgradedVersion = depMap.get(packageName) - table.push([packageName, installedVersion, '→', upgradedVersion]) - - pkg[depType][packageName] = upgradedVersion - } - } - - stopSpinner() - - if ([...upgradableDepMaps.values()].every(depMap => depMap.size === 0)) { - console.log('Already up-to-date.') - return - } - - console.log('These packages can be upgraded:\n') - // TODO: alternative: https://github.com/angular/angular-cli/blob/34a55c96b2ed38b226879913839b97c601387653/packages/schematics/update/update/index.ts#L490-L509 - console.log(table.toString()) - console.log( - `\nView complete changelog at ${chalk.blue( - 'https://github.com/vuejs/vue-cli/blob/dev/CHANGELOG.md' - )}\n` - ) - - const useYarn = shouldUseYarn() - const { confirmed } = await inquirer.prompt([ - { - name: 'confirmed', - type: 'confirm', - message: `Upgrade ${chalk.yellow('package.json')} and run ${chalk.blue( - useYarn ? 'yarn install' : 'npm install' - )}?` - } - ]) - - if (!confirmed) { - return - } - - fs.writeFileSync(path.resolve(projectPath, 'package.json'), JSON.stringify(pkg, null, 2)) - console.log() - console.log(`${chalk.yellow('package.json')} saved`) - if (useYarn) { - await installDeps(projectPath, 'yarn') - } else { - await installDeps(projectPath, 'npm') - } -} diff --git a/packages/@vue/cli-upgrade/package.json b/packages/@vue/cli-upgrade/package.json deleted file mode 100644 index 813d5906a5..0000000000 --- a/packages/@vue/cli-upgrade/package.json +++ /dev/null @@ -1,30 +0,0 @@ -{ - "name": "@vue/cli-upgrade", - "version": "4.0.0-alpha.1", - "description": "utility to upgrade vue cli service / plugins in vue apps", - "main": "index.js", - "repository": { - "type": "git", - "url": "git+https://github.com/vuejs/vue-cli.git", - "directory": "packages/@vue/cli-upgrade" - }, - "keywords": [ - "vue", - "cli", - "upgrade", - "update" - ], - "author": "Haoqun Jiang ", - "license": "MIT", - "bugs": { - "url": "https://github.com/vuejs/vue-cli/issues" - }, - "homepage": "https://github.com/vuejs/vue-cli/tree/dev/packages/@vue/cli-upgrade#readme", - "dependencies": { - "@vue/cli-shared-utils": "^4.0.0-alpha.1", - "chalk": "^2.4.1", - "cli-table": "^0.3.1", - "execa": "^1.0.0", - "inquirer": "^6.3.1" - } -} From 185aec00e684ec8b2e879c59cca8ab5d117747f0 Mon Sep 17 00:00:00 2001 From: Haoqun Jiang Date: Fri, 28 Jun 2019 09:49:10 +0800 Subject: [PATCH 12/20] chore: mark `vue upgrade` as experimental --- packages/@vue/cli/bin/vue.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/@vue/cli/bin/vue.js b/packages/@vue/cli/bin/vue.js index 270a8ee553..8161aad511 100755 --- a/packages/@vue/cli/bin/vue.js +++ b/packages/@vue/cli/bin/vue.js @@ -167,7 +167,7 @@ program program .command('upgrade [package-name]') - .description('upgrade vue cli service / plugins') + .description('(experimental) upgrade vue cli service / plugins') .option('-t, --to ', 'upgrade to a version that is not latest') .option('-r, --registry ', 'Use specified npm registry when installing dependencies') .action((packageName, cmd) => { From 776a21a3b58792c10c18e4cadce51d6e157029c2 Mon Sep 17 00:00:00 2001 From: Haoqun Jiang Date: Fri, 28 Jun 2019 22:01:21 +0800 Subject: [PATCH 13/20] chore: setup test for upgrade command --- .../@vue/cli-plugin-babel/migrator/index.js | 2 +- packages/@vue/cli/__tests__/upgrade.spec.js | 32 +++++++++++++++++++ packages/@vue/cli/lib/Creator.js | 6 ++-- packages/@vue/cli/lib/MigratorAPI.js | 2 -- packages/@vue/cli/lib/upgrade.js | 7 ++-- 5 files changed, 40 insertions(+), 9 deletions(-) create mode 100644 packages/@vue/cli/__tests__/upgrade.spec.js diff --git a/packages/@vue/cli-plugin-babel/migrator/index.js b/packages/@vue/cli-plugin-babel/migrator/index.js index 5c2b658bdd..38bf85b5bf 100644 --- a/packages/@vue/cli-plugin-babel/migrator/index.js +++ b/packages/@vue/cli-plugin-babel/migrator/index.js @@ -1,5 +1,5 @@ module.exports = (api) => { - if (api.fromVersion('<=3.5.4')) { + if (api.fromVersion('<=3.5.3')) { // add core-js@2 as dependency api.extendPackage({ dependencies: { diff --git a/packages/@vue/cli/__tests__/upgrade.spec.js b/packages/@vue/cli/__tests__/upgrade.spec.js new file mode 100644 index 0000000000..f08d5d238d --- /dev/null +++ b/packages/@vue/cli/__tests__/upgrade.spec.js @@ -0,0 +1,32 @@ +const fs = require('fs') +const path = require('path') +const create = require('@vue/cli-test-utils/createTestProject') + +jest.setTimeout(200000) + +const outsideTestFolder = path.resolve(__dirname, '../../../../../tests') + +beforeAll(() => { + if (!fs.existsSync(outsideTestFolder)) { + fs.mkdirSync(outsideTestFolder) + } +}) + +test('upgrade: plugin-babel v3.5', async () => { + process.env.VUE_CLI_TEST_DO_INSTALL_PLUGIN = true + const project = await create('plugin-babel-old', { + plugins: { + '@vue/cli-plugin-babel': { + version: '3.5.3' + } + } + }, outsideTestFolder) + + const pkg = JSON.parse(await project.read('package.json')) + expect(pkg.dependencies).not.toHaveProperty('core-js') + + // await project.run(`${require.resolve('../bin/vue')} upgrade @vue/cli-plugin-babel`) + + // const updatedPkg = JSON.parse(await project.read('package.json')) + // expect(updatedPkg.dependencies).toHaveProperty('core-js') +}) diff --git a/packages/@vue/cli/lib/Creator.js b/packages/@vue/cli/lib/Creator.js index 13276b0588..7782b59cf6 100644 --- a/packages/@vue/cli/lib/Creator.js +++ b/packages/@vue/cli/lib/Creator.js @@ -112,7 +112,7 @@ module.exports = class Creator extends EventEmitter { let latestMinor = `${semver.major(latest)}.${semver.minor(latest)}.0` // if using `next` branch of cli - if (semver.gt(current, latest) && semver.prerelease(current)) { + if (semver.gte(current, latest) && semver.prerelease(current)) { latestMinor = current } // generate package.json with plugin dependencies @@ -136,6 +136,7 @@ module.exports = class Creator extends EventEmitter { ((/^@vue/.test(dep)) ? `^${latestMinor}` : `latest`) ) }) + // write package.json await writeFileTree(context, { 'package.json': JSON.stringify(pkg, null, 2) @@ -155,7 +156,8 @@ module.exports = class Creator extends EventEmitter { log(`⚙ Installing CLI plugins. This might take a while...`) log() this.emit('creation', { event: 'plugins-install' }) - if (isTestOrDebug) { + + if (isTestOrDebug && !process.env.VUE_CLI_TEST_DO_INSTALL_PLUGIN) { // in development, avoid installation process await require('./util/setupDevProject')(context) } else { diff --git a/packages/@vue/cli/lib/MigratorAPI.js b/packages/@vue/cli/lib/MigratorAPI.js index 492dea8153..1c6dd45226 100644 --- a/packages/@vue/cli/lib/MigratorAPI.js +++ b/packages/@vue/cli/lib/MigratorAPI.js @@ -13,11 +13,9 @@ class MigratorAPI extends GeneratorAPI { this.installedVersion = installedVersion this.migrator = this.generator - // this.installedVersion = getInstalledVersion(id) } fromVersion (range) { - console.log('fromVersion', range) return semver.satisfies(this.installedVersion, range) } } diff --git a/packages/@vue/cli/lib/upgrade.js b/packages/@vue/cli/lib/upgrade.js index 7b47d9ee19..1f040cf062 100644 --- a/packages/@vue/cli/lib/upgrade.js +++ b/packages/@vue/cli/lib/upgrade.js @@ -27,8 +27,6 @@ const tryGetNewerRange = require('./util/tryGetNewerRange') const readFiles = require('./util/readFiles') async function runMigrator (packageName, options, context) { - const isTestOrDebug = process.env.VUE_CLI_TEST || process.env.VUE_CLI_DEBUG - const pluginMigrator = loadModule(`${packageName}/migrator`, context) if (!pluginMigrator) { return } @@ -61,8 +59,7 @@ async function runMigrator (packageName, options, context) { JSON.stringify(newDeps) !== JSON.stringify(pkg.dependencies) || JSON.stringify(newDevDeps) !== JSON.stringify(pkg.devDependencies) - // TODO: - + const isTestOrDebug = process.env.VUE_CLI_TEST || process.env.VUE_CLI_DEBUG if (!isTestOrDebug && depsChanged) { log(`📦 Installing additional dependencies...`) log() @@ -114,6 +111,8 @@ async function runMigrator (packageName, options, context) { async function upgradeSinglePackage (packageName, options, context) { const pkg = getPackageJson(context) + + // TODO: allow packageName to be a shorthand let depEntry, required for (const depType of ['dependencies', 'devDependencies', 'optionalDependencies']) { if (pkg[depType] && pkg[depType][packageName]) { From 1336e7b51684c8ec6f1bcb16f50692e65bf3c373 Mon Sep 17 00:00:00 2001 From: Haoqun Jiang Date: Sun, 30 Jun 2019 23:59:36 +0800 Subject: [PATCH 14/20] test: fix migrator test setup --- packages/@vue/cli/__tests__/upgrade.spec.js | 13 +++++++++---- packages/@vue/cli/lib/upgrade.js | 15 +++++++++++---- packages/@vue/cli/lib/util/linkBin.js | 9 +++++++++ 3 files changed, 29 insertions(+), 8 deletions(-) diff --git a/packages/@vue/cli/__tests__/upgrade.spec.js b/packages/@vue/cli/__tests__/upgrade.spec.js index f08d5d238d..c085351d56 100644 --- a/packages/@vue/cli/__tests__/upgrade.spec.js +++ b/packages/@vue/cli/__tests__/upgrade.spec.js @@ -14,7 +14,7 @@ beforeAll(() => { test('upgrade: plugin-babel v3.5', async () => { process.env.VUE_CLI_TEST_DO_INSTALL_PLUGIN = true - const project = await create('plugin-babel-old', { + const project = await create('plugin-babel-legacy', { plugins: { '@vue/cli-plugin-babel': { version: '3.5.3' @@ -25,8 +25,13 @@ test('upgrade: plugin-babel v3.5', async () => { const pkg = JSON.parse(await project.read('package.json')) expect(pkg.dependencies).not.toHaveProperty('core-js') - // await project.run(`${require.resolve('../bin/vue')} upgrade @vue/cli-plugin-babel`) + // TODO: use short id + await project.run(`${require.resolve('../bin/vue')} upgrade @vue/cli-plugin-babel`) - // const updatedPkg = JSON.parse(await project.read('package.json')) - // expect(updatedPkg.dependencies).toHaveProperty('core-js') + const updatedPkg = JSON.parse(await project.read('package.json')) + expect(updatedPkg.dependencies).toHaveProperty('core-js') +}) + +test('upgrade: plugin-babel with core-js 2', async () => { + // TODO: }) diff --git a/packages/@vue/cli/lib/upgrade.js b/packages/@vue/cli/lib/upgrade.js index 1f040cf062..612d770d20 100644 --- a/packages/@vue/cli/lib/upgrade.js +++ b/packages/@vue/cli/lib/upgrade.js @@ -20,12 +20,15 @@ const Migrator = require('./Migrator') const { getCommand, getVersion } = require('./util/packageManager') const { installDeps, updatePackage } = require('./util/installDeps') +const { linkPackage } = require('./util/linkBin') const getPackageJson = require('./util/getPackageJson') const getInstalledVersion = require('./util/getInstalledVersion') const tryGetNewerRange = require('./util/tryGetNewerRange') const readFiles = require('./util/readFiles') +const isTestOrDebug = process.env.VUE_CLI_TEST || process.env.VUE_CLI_DEBUG + async function runMigrator (packageName, options, context) { const pluginMigrator = loadModule(`${packageName}/migrator`, context) if (!pluginMigrator) { return } @@ -59,7 +62,6 @@ async function runMigrator (packageName, options, context) { JSON.stringify(newDeps) !== JSON.stringify(pkg.dependencies) || JSON.stringify(newDevDeps) !== JSON.stringify(pkg.devDependencies) - const isTestOrDebug = process.env.VUE_CLI_TEST || process.env.VUE_CLI_DEBUG if (!isTestOrDebug && depsChanged) { log(`📦 Installing additional dependencies...`) log() @@ -154,7 +156,13 @@ async function upgradeSinglePackage (packageName, options, context) { log(`Upgrading ${packageName} from ${installed} to ${targetVersion}`) - await updatePackage(context, getCommand(context), `${packageName}@^${targetVersion}`) + if (isTestOrDebug) { + // link packages in current repo for test + await linkPackage(path.resolve(__dirname, `../../../${packageName}`), path.join(context, 'node_modules', packageName)) + } else { + await updatePackage(context, getCommand(context), `${packageName}@^${targetVersion}`) + } + await runMigrator(packageName, { installed }, context) } @@ -173,9 +181,8 @@ async function getUpgradable (context) { const installed = await getInstalledVersion(name) const wanted = await getVersion(name, range, context) const latest = await getVersion(name, 'latest', context) - const latestRange = `^${latest}` - if (installed !== latest || range !== latestRange) { + if (installed !== latest) { upgradable.push({ name, installed, wanted, latest }) } } diff --git a/packages/@vue/cli/lib/util/linkBin.js b/packages/@vue/cli/lib/util/linkBin.js index 31c7c2bab6..d038ac551c 100644 --- a/packages/@vue/cli/lib/util/linkBin.js +++ b/packages/@vue/cli/lib/util/linkBin.js @@ -19,3 +19,12 @@ exports.linkBin = async (src, dest) => { await fs.chmod(dest, '755') } } + +exports.linkPackage = async (src, dest) => { + if (!process.env.VUE_CLI_TEST && !process.env.VUE_CLI_DEBUG) { + throw new Error(`linkPackage should only be used during tests or debugging.`) + } + + await fs.remove(dest) + await fs.symlink(src, dest, 'dir') +} From 0d7bb2c87c3c4d648c879545dacaaae2ba920a11 Mon Sep 17 00:00:00 2001 From: Haoqun Jiang Date: Mon, 1 Jul 2019 15:12:02 +0800 Subject: [PATCH 15/20] feat: implement `vue upgrade --all` --- packages/@vue/cli/lib/upgrade.js | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/packages/@vue/cli/lib/upgrade.js b/packages/@vue/cli/lib/upgrade.js index 612d770d20..770a93eb22 100644 --- a/packages/@vue/cli/lib/upgrade.js +++ b/packages/@vue/cli/lib/upgrade.js @@ -220,13 +220,27 @@ async function checkForUpdates (context) { console.log(' ' + fields.map((x, i) => x.padEnd(pads[i])).join('')) } + console.log(`Run ${chalk.yellow('vue upgrade --all')} to upgrade all the above plugins`) + return upgradable } async function upgradeAll (context) { - // TODO: upgrade all (interactive) + // TODO: should confirm for major version upgrades // for patch & minor versions, upgrade directly // for major versions, prompt before upgrading + const upgradable = await getUpgradable(context) + + if (!upgradable.length) { + done('Seems all plugins are up to date. Good work!') + return + } + + for (const p of upgradable) { + await upgradeSinglePackage(p.name, { to: p.latest }, context) + } + + done('All plugins are up to date!') } async function upgrade (packageName, options, context = process.cwd()) { From b84950e42ea321cdae73cca0c74960eaecb1fa0e Mon Sep 17 00:00:00 2001 From: Haoqun Jiang Date: Mon, 1 Jul 2019 15:55:02 +0800 Subject: [PATCH 16/20] feat: allow short plugin id --- packages/@vue/cli/__tests__/upgrade.spec.js | 3 +-- packages/@vue/cli/lib/upgrade.js | 4 +++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/packages/@vue/cli/__tests__/upgrade.spec.js b/packages/@vue/cli/__tests__/upgrade.spec.js index c085351d56..e9031bf21c 100644 --- a/packages/@vue/cli/__tests__/upgrade.spec.js +++ b/packages/@vue/cli/__tests__/upgrade.spec.js @@ -25,8 +25,7 @@ test('upgrade: plugin-babel v3.5', async () => { const pkg = JSON.parse(await project.read('package.json')) expect(pkg.dependencies).not.toHaveProperty('core-js') - // TODO: use short id - await project.run(`${require.resolve('../bin/vue')} upgrade @vue/cli-plugin-babel`) + await project.run(`${require.resolve('../bin/vue')} upgrade @vue/babel`) const updatedPkg = JSON.parse(await project.read('package.json')) expect(updatedPkg.dependencies).toHaveProperty('core-js') diff --git a/packages/@vue/cli/lib/upgrade.js b/packages/@vue/cli/lib/upgrade.js index 770a93eb22..8495f6bc51 100644 --- a/packages/@vue/cli/lib/upgrade.js +++ b/packages/@vue/cli/lib/upgrade.js @@ -11,6 +11,7 @@ const { stopSpinner, isPlugin, + resolvePluginId, loadModule, hasProjectGit @@ -111,7 +112,8 @@ async function runMigrator (packageName, options, context) { migrator.printExitLogs() } -async function upgradeSinglePackage (packageName, options, context) { +async function upgradeSinglePackage (pluginId, options, context) { + const packageName = resolvePluginId(pluginId) const pkg = getPackageJson(context) // TODO: allow packageName to be a shorthand From 68630df8b8337c5dea40deb85073ff19d5188e83 Mon Sep 17 00:00:00 2001 From: Haoqun Jiang Date: Mon, 1 Jul 2019 16:20:37 +0800 Subject: [PATCH 17/20] chore: remove outdated comment --- packages/@vue/cli/lib/upgrade.js | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/@vue/cli/lib/upgrade.js b/packages/@vue/cli/lib/upgrade.js index 8495f6bc51..cc90bc9164 100644 --- a/packages/@vue/cli/lib/upgrade.js +++ b/packages/@vue/cli/lib/upgrade.js @@ -116,7 +116,6 @@ async function upgradeSinglePackage (pluginId, options, context) { const packageName = resolvePluginId(pluginId) const pkg = getPackageJson(context) - // TODO: allow packageName to be a shorthand let depEntry, required for (const depType of ['dependencies', 'devDependencies', 'optionalDependencies']) { if (pkg[depType] && pkg[depType][packageName]) { From 2ffad8918f9813ee4b1b2bd1a3b3fa1789fb6f91 Mon Sep 17 00:00:00 2001 From: Haoqun Jiang Date: Mon, 1 Jul 2019 20:42:35 +0800 Subject: [PATCH 18/20] feat: babel migrator --- .../@vue/cli-plugin-babel/migrator/index.js | 28 +++++++++++++------ packages/@vue/cli/__tests__/upgrade.spec.js | 21 +++++++++++++- packages/@vue/cli/lib/GeneratorAPI.js | 6 ++-- packages/@vue/cli/lib/util/mergeDeps.js | 4 +-- 4 files changed, 45 insertions(+), 14 deletions(-) diff --git a/packages/@vue/cli-plugin-babel/migrator/index.js b/packages/@vue/cli-plugin-babel/migrator/index.js index 38bf85b5bf..ea74209881 100644 --- a/packages/@vue/cli-plugin-babel/migrator/index.js +++ b/packages/@vue/cli-plugin-babel/migrator/index.js @@ -1,16 +1,26 @@ +const { chalk } = require('@vue/cli-shared-utils') + module.exports = (api) => { - if (api.fromVersion('<=3.5.3')) { - // add core-js@2 as dependency + // TODO: backport this part to v3 + // if (api.fromVersion('<=3.5.3')) { + // // add core-js@2 as dependency + // api.extendPackage({ + // dependencies: { + // 'core-js': '^2.6.5' + // } + // }) + // } + + if (api.fromVersion('^3')) { api.extendPackage({ dependencies: { - 'core-js': '^2.6.5' + 'core-js': '^3.1.2' } - }) - } + }, true) - if (api.fromVersion('^3')) { - // TODO: update core-js@2 to core-js@3 + // TODO: implement a codemod to migrate polyfills + api.exitLog(`core-js has been upgraded from v2 to v3. +If you have any custom polyfills defined in ${chalk.yellow('babael.config.js')}, please be aware their names may have been changed. +For more complete changelog, see https://github.com/zloirock/core-js/blob/master/CHANGELOG.md#300---20190319`) } - - api.exitLog('Full changelog: maybe-some-random-gist-url') } diff --git a/packages/@vue/cli/__tests__/upgrade.spec.js b/packages/@vue/cli/__tests__/upgrade.spec.js index e9031bf21c..70c6df53e3 100644 --- a/packages/@vue/cli/__tests__/upgrade.spec.js +++ b/packages/@vue/cli/__tests__/upgrade.spec.js @@ -1,6 +1,7 @@ const fs = require('fs') const path = require('path') const create = require('@vue/cli-test-utils/createTestProject') +// const { logs } = require('@vue/cli-shared-utils') jest.setTimeout(200000) @@ -29,8 +30,26 @@ test('upgrade: plugin-babel v3.5', async () => { const updatedPkg = JSON.parse(await project.read('package.json')) expect(updatedPkg.dependencies).toHaveProperty('core-js') + + // TODO: run upgrade in the same process so that we can access logs + // expect(logs.log.some(([msg]) => msg.match('core-js has been upgraded'))).toBe(true) }) test('upgrade: plugin-babel with core-js 2', async () => { - // TODO: + process.env.VUE_CLI_TEST_DO_INSTALL_PLUGIN = true + const project = await create('plugin-babel-v3', { + plugins: { + '@vue/cli-plugin-babel': { + version: '3.8.0' + } + } + }, outsideTestFolder) + + const pkg = JSON.parse(await project.read('package.json')) + expect(pkg.dependencies['core-js']).toMatch('^2') + + await project.run(`${require.resolve('../bin/vue')} upgrade @vue/babel --to next`) + + const updatedPkg = JSON.parse(await project.read('package.json')) + expect(updatedPkg.dependencies['core-js']).toMatch('^3') }) diff --git a/packages/@vue/cli/lib/GeneratorAPI.js b/packages/@vue/cli/lib/GeneratorAPI.js index cc0f87077e..ec7ffa566b 100644 --- a/packages/@vue/cli/lib/GeneratorAPI.js +++ b/packages/@vue/cli/lib/GeneratorAPI.js @@ -173,8 +173,9 @@ class GeneratorAPI { * files are written to disk. * * @param {object | () => object} fields - Fields to merge. + * @param {boolean} forceNewVersion - Ignore version conflicts when updating dependency version */ - extendPackage (fields) { + extendPackage (fields, forceNewVersion) { const pkg = this.generator.pkg const toMerge = isFunction(fields) ? fields(pkg) : fields for (const key in toMerge) { @@ -186,7 +187,8 @@ class GeneratorAPI { this.id, existing || {}, value, - this.generator.depSources + this.generator.depSources, + forceNewVersion ) } else if (!(key in pkg)) { pkg[key] = value diff --git a/packages/@vue/cli/lib/util/mergeDeps.js b/packages/@vue/cli/lib/util/mergeDeps.js index 958cc271c7..3815593548 100644 --- a/packages/@vue/cli/lib/util/mergeDeps.js +++ b/packages/@vue/cli/lib/util/mergeDeps.js @@ -6,7 +6,7 @@ const tryGetNewerRange = require('./tryGetNewerRange') const extractSemver = r => r.replace(/^.+#semver:/, '') const injectSemver = (r, v) => semver.validRange(r) ? v : r.replace(/#semver:.+$/, `#semver:${v}`) -module.exports = function resolveDeps (generatorId, to, from, sources) { +module.exports = function resolveDeps (generatorId, to, from, sources, forceNewVersion) { const res = Object.assign({}, to) for (const name in from) { const r1 = to[name] @@ -42,7 +42,7 @@ module.exports = function resolveDeps (generatorId, to, from, sources) { sources[name] = generatorId } // warn incompatible version requirements - if (!semver.validRange(r1semver) || !semver.validRange(r2semver) || !semver.intersects(r1semver, r2semver)) { + if (!forceNewVersion && !semver.validRange(r1semver) || !semver.validRange(r2semver) || !semver.intersects(r1semver, r2semver)) { warn( `conflicting versions for project dependency "${name}":\n\n` + `- ${r1} injected by generator "${sourceGeneratorId}"\n` + From 83c6f5ff767072f0f0a8acb569a72d7a20e8abc6 Mon Sep 17 00:00:00 2001 From: Haoqun Jiang Date: Mon, 1 Jul 2019 20:43:55 +0800 Subject: [PATCH 19/20] chore: rename test folder --- packages/@vue/cli/__tests__/upgrade.spec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/@vue/cli/__tests__/upgrade.spec.js b/packages/@vue/cli/__tests__/upgrade.spec.js index 70c6df53e3..26b3a576cf 100644 --- a/packages/@vue/cli/__tests__/upgrade.spec.js +++ b/packages/@vue/cli/__tests__/upgrade.spec.js @@ -5,7 +5,7 @@ const create = require('@vue/cli-test-utils/createTestProject') jest.setTimeout(200000) -const outsideTestFolder = path.resolve(__dirname, '../../../../../tests') +const outsideTestFolder = path.resolve(__dirname, '../../../../../vue-upgrade-tests') beforeAll(() => { if (!fs.existsSync(outsideTestFolder)) { From 82acd41561c685a90eb9ddae6a8f94c881da39a4 Mon Sep 17 00:00:00 2001 From: Haoqun Jiang Date: Tue, 2 Jul 2019 11:13:29 +0800 Subject: [PATCH 20/20] fix: fix forceNewVersion --- packages/@vue/cli/lib/util/mergeDeps.js | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/packages/@vue/cli/lib/util/mergeDeps.js b/packages/@vue/cli/lib/util/mergeDeps.js index 3815593548..61bd8c6d43 100644 --- a/packages/@vue/cli/lib/util/mergeDeps.js +++ b/packages/@vue/cli/lib/util/mergeDeps.js @@ -42,7 +42,14 @@ module.exports = function resolveDeps (generatorId, to, from, sources, forceNewV sources[name] = generatorId } // warn incompatible version requirements - if (!forceNewVersion && !semver.validRange(r1semver) || !semver.validRange(r2semver) || !semver.intersects(r1semver, r2semver)) { + if ( + !forceNewVersion && + ( + !semver.validRange(r1semver) || + !semver.validRange(r2semver) || + !semver.intersects(r1semver, r2semver) + ) + ) { warn( `conflicting versions for project dependency "${name}":\n\n` + `- ${r1} injected by generator "${sourceGeneratorId}"\n` +