Skip to content

Commit

Permalink
feat: lock minor versions when creating projects / adding plugins (#5134
Browse files Browse the repository at this point in the history
)

* 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
  • Loading branch information
sodatea committed Feb 4, 2020
1 parent c8cecff commit 773f8a4
Show file tree
Hide file tree
Showing 6 changed files with 58 additions and 43 deletions.
Expand Up @@ -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`,
Expand Down
19 changes: 4 additions & 15 deletions packages/@vue/cli/lib/Creator.js
Expand Up @@ -27,7 +27,6 @@ const {
const {
chalk,
execa,
semver,

log,
warn,
Expand Down Expand Up @@ -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`

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
}
// get latest CLI plugin version
const { latestMinor } = await getVersions()

// generate package.json with plugin dependencies
const pkg = {
name,
Expand All @@ -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`)
)
})

Expand Down
6 changes: 3 additions & 3 deletions packages/@vue/cli/lib/Upgrader.js
Expand Up @@ -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))
Expand All @@ -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) {
Expand Down
9 changes: 5 additions & 4 deletions packages/@vue/cli/lib/add.js
Expand Up @@ -7,6 +7,7 @@ const {
loadModule
} = require('@vue/cli-shared-utils')

const getVersions = require('./util/getVersions')
const PackageManager = require('./util/ProjectPackageManager')
const {
log,
Expand Down Expand Up @@ -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)}`)
Expand Down
47 changes: 28 additions & 19 deletions packages/@vue/cli/lib/util/ProjectPackageManager.js
Expand Up @@ -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) {
Expand All @@ -281,10 +293,7 @@ class PackageManager {
return
}

return this.runCommand([
...PACKAGE_MANAGER_CONFIG[this.bin].add,
packageName
])
return await this.runCommand('add', [packageName])
}
}

Expand Down
15 changes: 14 additions & 1 deletion packages/@vue/cli/lib/util/getVersions.js
Expand Up @@ -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
})
}

Expand Down Expand Up @@ -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
})
}
Expand Down

0 comments on commit 773f8a4

Please sign in to comment.