Skip to content

Commit

Permalink
feat: vue upgrade monorepo support, --from option, and a new `vue…
Browse files Browse the repository at this point in the history
… migrate --from` command (vuejs#5091)

* refactor(migrator): rename `installed` to `baseVersion`

* feat: `vue upgrade --from` option and a new `vue migrate` command

* fix: fix support for `vuePlugins.resolveFrom` option

* chore: add a fixme comment

* fix: use loadModule instead of manually calculating the package.json path

This also fixes support for monorepo.
(TODO: tests)

* fix: treat `resolveFrom` as `context`, fixing edge cases

* fix: use read-pkg instead of loadModule, avoid messing up require cache

* fix: getInstalledVersion still requires `loadModule` to support monorepo
  • Loading branch information
sodatea authored and mactanxin committed Feb 11, 2020
1 parent 5e1908d commit c508edb
Show file tree
Hide file tree
Showing 10 changed files with 183 additions and 106 deletions.
1 change: 1 addition & 0 deletions packages/@vue/cli/__tests__/Upgrader.spec.js
@@ -0,0 +1 @@
test.todo('upgrade: should respect plugin resolveFrom')
2 changes: 2 additions & 0 deletions packages/@vue/cli/__tests__/invoke.spec.js
Expand Up @@ -166,3 +166,5 @@ test('should prompt if invoking in a git repository with uncommited changes', as
])
await invoke(`babel`, {}, project.dir)
})

test.todo('invoke: should respect plugin resolveFrom')
12 changes: 11 additions & 1 deletion packages/@vue/cli/bin/vue.js
Expand Up @@ -177,14 +177,24 @@ program
program
.command('upgrade [plugin-name]')
.description('(experimental) upgrade vue cli service / plugins')
.option('-t, --to <version>', 'upgrade <package-name> to a version that is not latest')
.option('-t, --to <version>', 'Upgrade <package-name> to a version that is not latest')
.option('-f, --from <version>', 'Skip probing installed plugin, assuming it is upgraded from the designated version')
.option('-r, --registry <url>', 'Use specified npm registry when installing dependencies')
.option('--all', 'Upgrade all plugins')
.option('--next', 'Also check for alpha / beta / rc versions when upgrading')
.action((packageName, cmd) => {
require('../lib/upgrade')(packageName, cleanArgs(cmd))
})

program
.command('migrate [plugin-name]')
.description('(experimental) run migrator for an already-installed cli plugin')
// TODO: use `requiredOption` after upgrading to commander 4.x
.option('-f, --from <version>', 'The base version for the migrator to migrate from')
.action((packageName, cmd) => {
require('../lib/migrate')(packageName, cleanArgs(cmd))
})

program
.command('info')
.description('print debugging information about your environment')
Expand Down
2 changes: 1 addition & 1 deletion packages/@vue/cli/lib/Migrator.js
Expand Up @@ -28,7 +28,7 @@ module.exports = class Migrator extends Generator {
// apply migrators from plugins
const api = new MigratorAPI(
plugin.id,
plugin.installed,
plugin.baseVersion,
this,
plugin.options,
this.rootOptions
Expand Down
6 changes: 3 additions & 3 deletions packages/@vue/cli/lib/MigratorAPI.js
Expand Up @@ -8,15 +8,15 @@ class MigratorAPI extends GeneratorAPI {
* @param {object} options - options passed to this plugin
* @param {object} rootOptions - root options (the entire preset)
*/
constructor (id, installedVersion, migrator, options, rootOptions) {
constructor (id, baseVersion, migrator, options, rootOptions) {
super(id, migrator, options, rootOptions)

this.installedVersion = installedVersion
this.baseVersion = baseVersion
this.migrator = this.generator
}

fromVersion (range) {
return semver.satisfies(this.installedVersion, range)
return semver.satisfies(this.baseVersion, range)
}
}

Expand Down
104 changes: 26 additions & 78 deletions packages/@vue/cli/lib/Upgrader.js
Expand Up @@ -11,23 +11,20 @@ const {

isPlugin,
resolvePluginId,

loadModule
} = require('@vue/cli-shared-utils')

const Migrator = require('./Migrator')
const tryGetNewerRange = require('./util/tryGetNewerRange')
const readFiles = require('./util/readFiles')
const getChangedFiles = require('./util/getChangedFiles')

const getPackageJson = require('./util/getPackageJson')
const getPkg = require('./util/getPkg')
const PackageManager = require('./util/ProjectPackageManager')

const isTestOrDebug = process.env.VUE_CLI_TEST || process.env.VUE_CLI_DEBUG
const { runMigrator } = require('./migrate')

module.exports = class Upgrader {
constructor (context = process.cwd()) {
this.context = context
this.pkg = getPackageJson(this.context)
this.pkg = getPkg(this.context)
this.pm = new PackageManager({ context })
}

Expand All @@ -43,7 +40,8 @@ module.exports = class Upgrader {
}

for (const p of upgradable) {
this.pkg = getPackageJson(this.context)
// reread to avoid accidentally writing outdated package.json back
this.pkg = getPkg(this.context)
await this.upgrade(p.name, { to: p.latest })
}

Expand All @@ -65,6 +63,14 @@ module.exports = class Upgrader {
throw new Error(`Can't find ${chalk.yellow(packageName)} in ${chalk.yellow('package.json')}`)
}

const installed = options.from || this.pm.getInstalledVersion(packageName)
if (!installed) {
throw new Error(
`Can't find ${chalk.yellow(packageName)} in ${chalk.yellow('node_modules')}. Please install the dependencies first.\n` +
`Or to force upgrade, you can specify your current plugin version with the ${chalk.cyan('--from')} option`
)
}

let targetVersion = options.to || 'latest'
// if the targetVersion is not an exact version
if (!/\d+\.\d+\.\d+/.test(targetVersion)) {
Expand All @@ -84,7 +90,6 @@ module.exports = class Upgrader {
stopSpinner()
}

const installed = this.pm.getInstalledVersion(packageName)
if (targetVersion === installed) {
log(`Already installed ${packageName}@${targetVersion}`)

Expand All @@ -102,76 +107,19 @@ module.exports = class Upgrader {

// the cached `pkg` field won't automatically update after running `this.pm.upgrade`
this.pkg[depEntry][packageName] = `^${targetVersion}`
await this.runMigrator(packageName, { installed })
}

async runMigrator (packageName, options) {
const pluginMigrator = loadModule(`${packageName}/migrator`, this.context)
if (!pluginMigrator) { return }

const plugin = {
id: packageName,
apply: pluginMigrator,
installed: options.installed
}

const afterInvokeCbs = []
const migrator = new Migrator(this.context, {
plugin: plugin,

pkg: this.pkg,
files: await readFiles(this.context),
afterInvokeCbs,
invoking: true
})

log(`🚀 Running migrator of ${packageName}`)
await migrator.generate({
extractConfigFiles: true,
checkExisting: true
})

const newDeps = migrator.pkg.dependencies
const newDevDeps = migrator.pkg.devDependencies
const depsChanged =
JSON.stringify(newDeps) !== JSON.stringify(this.pkg.dependencies) ||
JSON.stringify(newDevDeps) !== JSON.stringify(this.pkg.devDependencies)

if (!isTestOrDebug && depsChanged) {
log(`📦 Installing additional dependencies...`)
log()
await this.pm.install()
}

if (afterInvokeCbs.length) {
logWithSpinner('⚓', `Running completion hooks...`)
for (const cb of afterInvokeCbs) {
await cb()
}
stopSpinner()
log()
}

log(
`${chalk.green(
'✔'
)} Successfully invoked migrator for plugin: ${chalk.cyan(plugin.id)}`
)

const changedFiles = getChangedFiles(this.context)
if (changedFiles.length) {
log(` The following files have been updated / added:\n`)
log(chalk.red(changedFiles.map(line => ` ${line}`).join('\n')))
log()
log(
` You should review these changes with ${chalk.cyan(
'git diff'
)} and commit them.`
if (pluginMigrator) {
await runMigrator(
this.context,
{
id: packageName,
apply: pluginMigrator,
baseVersion: installed
},
this.pkg
)
log()
}

migrator.printExitLogs()
}

async getUpgradable (includeNext) {
Expand All @@ -188,8 +136,8 @@ module.exports = class Upgrader {
const installed = await this.pm.getInstalledVersion(name)
const wanted = await this.pm.getRemoteVersion(name, range)

if (installed === 'N/A') {
throw new Error('At least one dependency is not installed. Please run npm install or yarn before trying to upgrade')
if (!installed) {
throw new Error(`At least one dependency can't be found. Please install the dependencies before trying to upgrade`)
}

let latest = await this.pm.getRemoteVersion(name)
Expand Down Expand Up @@ -242,7 +190,7 @@ module.exports = class Upgrader {
for (const p of upgradable) {
const fields = [
p.name,
p.installed,
p.installed || 'N/A',
p.wanted,
p.latest,
`vue upgrade ${p.name}${includeNext ? ' --next' : ''}`
Expand Down
15 changes: 1 addition & 14 deletions packages/@vue/cli/lib/invoke.js
@@ -1,5 +1,3 @@
const fs = require('fs-extra')
const path = require('path')
const inquirer = require('inquirer')
const {
chalk,
Expand All @@ -18,21 +16,10 @@ const Generator = require('./Generator')

const confirmIfGitDirty = require('./util/confirmIfGitDirty')
const readFiles = require('./util/readFiles')
const getPkg = require('./util/getPkg')
const getChangedFiles = require('./util/getChangedFiles')
const PackageManager = require('./util/ProjectPackageManager')

function getPkg (context) {
const pkgPath = path.resolve(context, 'package.json')
if (!fs.existsSync(pkgPath)) {
throw new Error(`package.json not found in ${chalk.yellow(context)}`)
}
const pkg = fs.readJsonSync(pkgPath)
if (pkg.vuePlugins && pkg.vuePlugins.resolveFrom) {
return getPkg(path.resolve(context, pkg.vuePlugins.resolveFrom))
}
return pkg
}

async function invoke (pluginName, options = {}, context = process.cwd()) {
if (!(await confirmIfGitDirty(context))) {
return
Expand Down
109 changes: 109 additions & 0 deletions packages/@vue/cli/lib/migrate.js
@@ -0,0 +1,109 @@
const {
chalk,

log,
error,
logWithSpinner,
stopSpinner,

loadModule,
resolvePluginId
} = require('@vue/cli-shared-utils')

const Migrator = require('./Migrator')
const PackageManager = require('./util/ProjectPackageManager')

const readFiles = require('./util/readFiles')
const getPkg = require('./util/getPkg')
const getChangedFiles = require('./util/getChangedFiles')

const isTestOrDebug = process.env.VUE_CLI_TEST || process.env.VUE_CLI_DEBUG

async function runMigrator (context, plugin, pkg = getPkg(context)) {
const afterInvokeCbs = []
const migrator = new Migrator(context, {
plugin,
pkg,
files: await readFiles(context),
afterInvokeCbs
})

log(`🚀 Running migrator of ${plugin.id}`)
await migrator.generate({
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)
if (!isTestOrDebug && depsChanged) {
log(`📦 Installing additional dependencies...`)
log()

const pm = new PackageManager({ context })
await pm.install()
}

if (afterInvokeCbs.length) {
logWithSpinner('⚓', `Running completion hooks...`)
for (const cb of afterInvokeCbs) {
await cb()
}
stopSpinner()
log()
}

log(
`${chalk.green(
'✔'
)} Successfully invoked migrator for plugin: ${chalk.cyan(plugin.id)}`
)

const changedFiles = getChangedFiles(context)
if (changedFiles.length) {
log(` The following files have been updated / added:\n`)
log(chalk.red(changedFiles.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 migrate (pluginId, { from }, context = process.cwd()) {
// TODO: remove this after upgrading to commander 4.x
if (!from) {
throw new Error(`Required option 'from' not specified`)
}

const pluginName = resolvePluginId(pluginId)
const pluginMigrator = loadModule(`${pluginName}/migrator`, context)
if (!pluginMigrator) {
log(`There's no migrator in ${pluginName}`)
return
}
await runMigrator(context, {
id: pluginName,
apply: pluginMigrator,
baseVersion: from
})
}

module.exports = (...args) => {
return migrate(...args).catch(err => {
error(err)
if (!process.env.VUE_CLI_TEST) {
process.exit(1)
}
})
}

module.exports.runMigrator = runMigrator

0 comments on commit c508edb

Please sign in to comment.