Skip to content

Commit 02a0e8a

Browse files
authored
feat: vue upgrade monorepo support, --from option, and a new vue migrate --from command (#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
1 parent 592b305 commit 02a0e8a

File tree

10 files changed

+183
-106
lines changed

10 files changed

+183
-106
lines changed
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
test.todo('upgrade: should respect plugin resolveFrom')

packages/@vue/cli/__tests__/invoke.spec.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,3 +166,5 @@ test('should prompt if invoking in a git repository with uncommited changes', as
166166
])
167167
await invoke(`babel`, {}, project.dir)
168168
})
169+
170+
test.todo('invoke: should respect plugin resolveFrom')

packages/@vue/cli/bin/vue.js

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -177,14 +177,24 @@ program
177177
program
178178
.command('upgrade [plugin-name]')
179179
.description('(experimental) upgrade vue cli service / plugins')
180-
.option('-t, --to <version>', 'upgrade <package-name> to a version that is not latest')
180+
.option('-t, --to <version>', 'Upgrade <package-name> to a version that is not latest')
181+
.option('-f, --from <version>', 'Skip probing installed plugin, assuming it is upgraded from the designated version')
181182
.option('-r, --registry <url>', 'Use specified npm registry when installing dependencies')
182183
.option('--all', 'Upgrade all plugins')
183184
.option('--next', 'Also check for alpha / beta / rc versions when upgrading')
184185
.action((packageName, cmd) => {
185186
require('../lib/upgrade')(packageName, cleanArgs(cmd))
186187
})
187188

189+
program
190+
.command('migrate [plugin-name]')
191+
.description('(experimental) run migrator for an already-installed cli plugin')
192+
// TODO: use `requiredOption` after upgrading to commander 4.x
193+
.option('-f, --from <version>', 'The base version for the migrator to migrate from')
194+
.action((packageName, cmd) => {
195+
require('../lib/migrate')(packageName, cleanArgs(cmd))
196+
})
197+
188198
program
189199
.command('info')
190200
.description('print debugging information about your environment')

packages/@vue/cli/lib/Migrator.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ module.exports = class Migrator extends Generator {
2828
// apply migrators from plugins
2929
const api = new MigratorAPI(
3030
plugin.id,
31-
plugin.installed,
31+
plugin.baseVersion,
3232
this,
3333
plugin.options,
3434
this.rootOptions

packages/@vue/cli/lib/MigratorAPI.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,15 @@ class MigratorAPI extends GeneratorAPI {
88
* @param {object} options - options passed to this plugin
99
* @param {object} rootOptions - root options (the entire preset)
1010
*/
11-
constructor (id, installedVersion, migrator, options, rootOptions) {
11+
constructor (id, baseVersion, migrator, options, rootOptions) {
1212
super(id, migrator, options, rootOptions)
1313

14-
this.installedVersion = installedVersion
14+
this.baseVersion = baseVersion
1515
this.migrator = this.generator
1616
}
1717

1818
fromVersion (range) {
19-
return semver.satisfies(this.installedVersion, range)
19+
return semver.satisfies(this.baseVersion, range)
2020
}
2121
}
2222

packages/@vue/cli/lib/Upgrader.js

Lines changed: 26 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -11,23 +11,20 @@ const {
1111

1212
isPlugin,
1313
resolvePluginId,
14+
1415
loadModule
1516
} = require('@vue/cli-shared-utils')
1617

17-
const Migrator = require('./Migrator')
1818
const tryGetNewerRange = require('./util/tryGetNewerRange')
19-
const readFiles = require('./util/readFiles')
20-
const getChangedFiles = require('./util/getChangedFiles')
21-
22-
const getPackageJson = require('./util/getPackageJson')
19+
const getPkg = require('./util/getPkg')
2320
const PackageManager = require('./util/ProjectPackageManager')
2421

25-
const isTestOrDebug = process.env.VUE_CLI_TEST || process.env.VUE_CLI_DEBUG
22+
const { runMigrator } = require('./migrate')
2623

2724
module.exports = class Upgrader {
2825
constructor (context = process.cwd()) {
2926
this.context = context
30-
this.pkg = getPackageJson(this.context)
27+
this.pkg = getPkg(this.context)
3128
this.pm = new PackageManager({ context })
3229
}
3330

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

4542
for (const p of upgradable) {
46-
this.pkg = getPackageJson(this.context)
43+
// reread to avoid accidentally writing outdated package.json back
44+
this.pkg = getPkg(this.context)
4745
await this.upgrade(p.name, { to: p.latest })
4846
}
4947

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

66+
const installed = options.from || this.pm.getInstalledVersion(packageName)
67+
if (!installed) {
68+
throw new Error(
69+
`Can't find ${chalk.yellow(packageName)} in ${chalk.yellow('node_modules')}. Please install the dependencies first.\n` +
70+
`Or to force upgrade, you can specify your current plugin version with the ${chalk.cyan('--from')} option`
71+
)
72+
}
73+
6874
let targetVersion = options.to || 'latest'
6975
// if the targetVersion is not an exact version
7076
if (!/\d+\.\d+\.\d+/.test(targetVersion)) {
@@ -84,7 +90,6 @@ module.exports = class Upgrader {
8490
stopSpinner()
8591
}
8692

87-
const installed = this.pm.getInstalledVersion(packageName)
8893
if (targetVersion === installed) {
8994
log(`Already installed ${packageName}@${targetVersion}`)
9095

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

103108
// the cached `pkg` field won't automatically update after running `this.pm.upgrade`
104109
this.pkg[depEntry][packageName] = `^${targetVersion}`
105-
await this.runMigrator(packageName, { installed })
106-
}
107-
108-
async runMigrator (packageName, options) {
109110
const pluginMigrator = loadModule(`${packageName}/migrator`, this.context)
110-
if (!pluginMigrator) { return }
111-
112-
const plugin = {
113-
id: packageName,
114-
apply: pluginMigrator,
115-
installed: options.installed
116-
}
117-
118-
const afterInvokeCbs = []
119-
const migrator = new Migrator(this.context, {
120-
plugin: plugin,
121-
122-
pkg: this.pkg,
123-
files: await readFiles(this.context),
124-
afterInvokeCbs,
125-
invoking: true
126-
})
127111

128-
log(`🚀 Running migrator of ${packageName}`)
129-
await migrator.generate({
130-
extractConfigFiles: true,
131-
checkExisting: true
132-
})
133-
134-
const newDeps = migrator.pkg.dependencies
135-
const newDevDeps = migrator.pkg.devDependencies
136-
const depsChanged =
137-
JSON.stringify(newDeps) !== JSON.stringify(this.pkg.dependencies) ||
138-
JSON.stringify(newDevDeps) !== JSON.stringify(this.pkg.devDependencies)
139-
140-
if (!isTestOrDebug && depsChanged) {
141-
log(`📦 Installing additional dependencies...`)
142-
log()
143-
await this.pm.install()
144-
}
145-
146-
if (afterInvokeCbs.length) {
147-
logWithSpinner('⚓', `Running completion hooks...`)
148-
for (const cb of afterInvokeCbs) {
149-
await cb()
150-
}
151-
stopSpinner()
152-
log()
153-
}
154-
155-
log(
156-
`${chalk.green(
157-
'✔'
158-
)} Successfully invoked migrator for plugin: ${chalk.cyan(plugin.id)}`
159-
)
160-
161-
const changedFiles = getChangedFiles(this.context)
162-
if (changedFiles.length) {
163-
log(` The following files have been updated / added:\n`)
164-
log(chalk.red(changedFiles.map(line => ` ${line}`).join('\n')))
165-
log()
166-
log(
167-
` You should review these changes with ${chalk.cyan(
168-
'git diff'
169-
)} and commit them.`
112+
if (pluginMigrator) {
113+
await runMigrator(
114+
this.context,
115+
{
116+
id: packageName,
117+
apply: pluginMigrator,
118+
baseVersion: installed
119+
},
120+
this.pkg
170121
)
171-
log()
172122
}
173-
174-
migrator.printExitLogs()
175123
}
176124

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

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

195143
let latest = await this.pm.getRemoteVersion(name)
@@ -242,7 +190,7 @@ module.exports = class Upgrader {
242190
for (const p of upgradable) {
243191
const fields = [
244192
p.name,
245-
p.installed,
193+
p.installed || 'N/A',
246194
p.wanted,
247195
p.latest,
248196
`vue upgrade ${p.name}${includeNext ? ' --next' : ''}`

packages/@vue/cli/lib/invoke.js

Lines changed: 1 addition & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
const fs = require('fs-extra')
2-
const path = require('path')
31
const inquirer = require('inquirer')
42
const {
53
chalk,
@@ -18,21 +16,10 @@ const Generator = require('./Generator')
1816

1917
const confirmIfGitDirty = require('./util/confirmIfGitDirty')
2018
const readFiles = require('./util/readFiles')
19+
const getPkg = require('./util/getPkg')
2120
const getChangedFiles = require('./util/getChangedFiles')
2221
const PackageManager = require('./util/ProjectPackageManager')
2322

24-
function getPkg (context) {
25-
const pkgPath = path.resolve(context, 'package.json')
26-
if (!fs.existsSync(pkgPath)) {
27-
throw new Error(`package.json not found in ${chalk.yellow(context)}`)
28-
}
29-
const pkg = fs.readJsonSync(pkgPath)
30-
if (pkg.vuePlugins && pkg.vuePlugins.resolveFrom) {
31-
return getPkg(path.resolve(context, pkg.vuePlugins.resolveFrom))
32-
}
33-
return pkg
34-
}
35-
3623
async function invoke (pluginName, options = {}, context = process.cwd()) {
3724
if (!(await confirmIfGitDirty(context))) {
3825
return

packages/@vue/cli/lib/migrate.js

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
const {
2+
chalk,
3+
4+
log,
5+
error,
6+
logWithSpinner,
7+
stopSpinner,
8+
9+
loadModule,
10+
resolvePluginId
11+
} = require('@vue/cli-shared-utils')
12+
13+
const Migrator = require('./Migrator')
14+
const PackageManager = require('./util/ProjectPackageManager')
15+
16+
const readFiles = require('./util/readFiles')
17+
const getPkg = require('./util/getPkg')
18+
const getChangedFiles = require('./util/getChangedFiles')
19+
20+
const isTestOrDebug = process.env.VUE_CLI_TEST || process.env.VUE_CLI_DEBUG
21+
22+
async function runMigrator (context, plugin, pkg = getPkg(context)) {
23+
const afterInvokeCbs = []
24+
const migrator = new Migrator(context, {
25+
plugin,
26+
pkg,
27+
files: await readFiles(context),
28+
afterInvokeCbs
29+
})
30+
31+
log(`🚀 Running migrator of ${plugin.id}`)
32+
await migrator.generate({
33+
extractConfigFiles: true,
34+
checkExisting: true
35+
})
36+
37+
const newDeps = migrator.pkg.dependencies
38+
const newDevDeps = migrator.pkg.devDependencies
39+
const depsChanged =
40+
JSON.stringify(newDeps) !== JSON.stringify(pkg.dependencies) ||
41+
JSON.stringify(newDevDeps) !== JSON.stringify(pkg.devDependencies)
42+
if (!isTestOrDebug && depsChanged) {
43+
log(`📦 Installing additional dependencies...`)
44+
log()
45+
46+
const pm = new PackageManager({ context })
47+
await pm.install()
48+
}
49+
50+
if (afterInvokeCbs.length) {
51+
logWithSpinner('⚓', `Running completion hooks...`)
52+
for (const cb of afterInvokeCbs) {
53+
await cb()
54+
}
55+
stopSpinner()
56+
log()
57+
}
58+
59+
log(
60+
`${chalk.green(
61+
'✔'
62+
)} Successfully invoked migrator for plugin: ${chalk.cyan(plugin.id)}`
63+
)
64+
65+
const changedFiles = getChangedFiles(context)
66+
if (changedFiles.length) {
67+
log(` The following files have been updated / added:\n`)
68+
log(chalk.red(changedFiles.map(line => ` ${line}`).join('\n')))
69+
log()
70+
log(
71+
` You should review these changes with ${chalk.cyan(
72+
'git diff'
73+
)} and commit them.`
74+
)
75+
log()
76+
}
77+
78+
migrator.printExitLogs()
79+
}
80+
81+
async function migrate (pluginId, { from }, context = process.cwd()) {
82+
// TODO: remove this after upgrading to commander 4.x
83+
if (!from) {
84+
throw new Error(`Required option 'from' not specified`)
85+
}
86+
87+
const pluginName = resolvePluginId(pluginId)
88+
const pluginMigrator = loadModule(`${pluginName}/migrator`, context)
89+
if (!pluginMigrator) {
90+
log(`There's no migrator in ${pluginName}`)
91+
return
92+
}
93+
await runMigrator(context, {
94+
id: pluginName,
95+
apply: pluginMigrator,
96+
baseVersion: from
97+
})
98+
}
99+
100+
module.exports = (...args) => {
101+
return migrate(...args).catch(err => {
102+
error(err)
103+
if (!process.env.VUE_CLI_TEST) {
104+
process.exit(1)
105+
}
106+
})
107+
}
108+
109+
module.exports.runMigrator = runMigrator

0 commit comments

Comments
 (0)