feat: lock minor versions when creating projects / adding plugins (#5134)

* feat: lock minor versions when creating projects / adding plugins

closes #5012

* refactor: also calculate latestMinor version

* feat: support add packages with tilde version range

* refactor: make the `runCommand` invocations more concise

* refactor: use the `getVersions` utility function to get latestMinor

* feat: when adding plugins, use tilde range by default

* fix: allow empty args
This commit is contained in:
Haoqun Jiang
2020-02-04 16:26:32 +08:00
committed by GitHub
parent c8cecffedb
commit 773f8a47e9
6 changed files with 57 additions and 42 deletions

View File

@@ -188,7 +188,10 @@ function install ({ id, type, range }, context) {
}
const pm = new PackageManager({ context: cwd.get() })
await pm.add(arg, type === 'devDependencies')
await pm.add(arg, {
tilde: !range && isPlugin(id),
dev: type === 'devDependencies'
})
logs.add({
message: `Dependency ${id} installed`,

View File

@@ -27,7 +27,6 @@ const {
const {
chalk,
execa,
semver,
log,
warn,
@@ -129,19 +128,9 @@ module.exports = class Creator extends EventEmitter {
logWithSpinner(``, `Creating project in ${chalk.yellow(context)}.`)
this.emit('creation', { event: 'creating' })
// get latest CLI version
const { current, latest } = await getVersions()
let latestMinor = `${semver.major(latest)}.${semver.minor(latest)}.0`
// get latest CLI plugin version
const { latestMinor } = await getVersions()
if (
// if the latest version contains breaking changes
/major/.test(semver.diff(current, latest)) ||
// or if using `next` branch of cli
(semver.gte(current, latest) && semver.prerelease(current))
) {
// fallback to the current cli version number
latestMinor = current
}
// generate package.json with plugin dependencies
const pkg = {
name,
@@ -161,7 +150,7 @@ module.exports = class Creator extends EventEmitter {
// Other `@vue/*` packages' version may not be in sync with the cli itself.
pkg.devDependencies[dep] = (
preset.plugins[dep].version ||
((/^@vue/.test(dep)) ? `^${latestMinor}` : `latest`)
((/^@vue/.test(dep)) ? `~${latestMinor}` : `latest`)
)
})

View File

@@ -93,7 +93,7 @@ module.exports = class Upgrader {
if (targetVersion === installed) {
log(`Already installed ${packageName}@${targetVersion}`)
const newRange = tryGetNewerRange(`^${targetVersion}`, required)
const newRange = tryGetNewerRange(`~${targetVersion}`, required)
if (newRange !== required) {
this.pkg[depEntry][packageName] = newRange
fs.writeFileSync(path.resolve(this.context, 'package.json'), JSON.stringify(this.pkg, null, 2))
@@ -103,10 +103,10 @@ module.exports = class Upgrader {
}
log(`Upgrading ${packageName} from ${installed} to ${targetVersion}`)
await this.pm.upgrade(`${packageName}@^${targetVersion}`)
await this.pm.upgrade(`${packageName}@~${targetVersion}`)
// the cached `pkg` field won't automatically update after running `this.pm.upgrade`
this.pkg[depEntry][packageName] = `^${targetVersion}`
this.pkg[depEntry][packageName] = `~${targetVersion}`
const pluginMigrator = loadModule(`${packageName}/migrator`, this.context)
if (pluginMigrator) {

View File

@@ -7,6 +7,7 @@ const {
loadModule
} = require('@vue/cli-shared-utils')
const getVersions = require('./util/getVersions')
const PackageManager = require('./util/ProjectPackageManager')
const {
log,
@@ -40,12 +41,12 @@ async function add (pluginName, options = {}, context = process.cwd()) {
log()
const pm = new PackageManager({ context })
const { latestMinor } = await getVersions()
const cliVersion = require('../package.json').version
if (isOfficialPlugin(packageName) && semver.prerelease(cliVersion)) {
await pm.add(`${packageName}@^${cliVersion}`)
if (isOfficialPlugin(packageName)) {
await pm.add(`${packageName}@~${latestMinor}`)
} else {
await pm.add(packageName)
await pm.add(packageName, { tilde: true })
}
log(`${chalk.green('✔')} Successfully installed plugin: ${chalk.cyan(packageName)}`)

View File

@@ -235,36 +235,48 @@ class PackageManager {
} catch (e) {}
}
async runCommand (args) {
async runCommand (command, args) {
await this.setRegistryEnvs()
await executeCommand(this.bin, args, this.context)
return await executeCommand(
this.bin,
[
...PACKAGE_MANAGER_CONFIG[this.bin][command],
...(args || [])
],
this.context
)
}
async install () {
if (process.env.VUE_CLI_TEST) {
try {
await this.runCommand([PACKAGE_MANAGER_CONFIG[this.bin].install, '--offline'])
await this.runCommand('install', ['--offline'])
} catch (e) {
await this.runCommand([PACKAGE_MANAGER_CONFIG[this.bin].install])
await this.runCommand('install')
}
}
return this.runCommand([PACKAGE_MANAGER_CONFIG[this.bin].install])
return await this.runCommand('install')
}
async add (packageName, isDev = true) {
return this.runCommand([
...PACKAGE_MANAGER_CONFIG[this.bin].add,
packageName,
...(isDev ? ['-D'] : [])
])
async add (packageName, {
tilde = false,
dev = true
} = {}) {
const args = dev ? ['-D'] : []
if (tilde) {
if (this.bin === 'yarn') {
args.push('--tilde')
} else {
process.env.npm_config_save_prefix = '~'
}
}
return await this.runCommand('add', [packageName, ...args])
}
async remove (packageName) {
return this.runCommand([
...PACKAGE_MANAGER_CONFIG[this.bin].remove,
packageName
])
return await this.runCommand('remove', [packageName])
}
async upgrade (packageName) {
@@ -281,10 +293,7 @@ class PackageManager {
return
}
return this.runCommand([
...PACKAGE_MANAGER_CONFIG[this.bin].add,
packageName
])
return await this.runCommand('add', [packageName])
}
}

View File

@@ -15,7 +15,8 @@ module.exports = async function getVersions () {
if (process.env.VUE_CLI_TEST || process.env.VUE_CLI_DEBUG) {
return (sessionCached = {
current: local,
latest: local
latest: local,
latestMinor: local
})
}
@@ -44,9 +45,21 @@ module.exports = async function getVersions () {
latest = cached
}
let latestMinor = `${semver.major(latest)}.${semver.minor(latest)}.0`
if (
// if the latest version contains breaking changes
/major/.test(semver.diff(local, latest)) ||
// or if using `next` branch of cli
(semver.gte(local, latest) && semver.prerelease(local))
) {
// fallback to the local cli version number
latestMinor = local
}
return (sessionCached = {
current: local,
latest,
latestMinor,
error
})
}