Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: vue upgrade monorepo support, --from option, and a new vue migrate --from command #5091

Merged
merged 11 commits into from Jan 28, 2020
Merged
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