mirror of
https://github.com/vuejs/vue-cli.git
synced 2026-03-13 20:51:41 -05:00
tweak registry check
This commit is contained in:
@@ -2,7 +2,7 @@ const chalk = require('chalk')
|
||||
const readline = require('readline')
|
||||
const { execSync } = require('child_process')
|
||||
const padStart = require('string.prototype.padstart')
|
||||
const { logWithSpinner, stopSpinner } = require('./spinner')
|
||||
const spinner = require('./spinner')
|
||||
|
||||
const format = (label, msg) => {
|
||||
return msg.split('\n').map((line, i) => {
|
||||
@@ -12,8 +12,7 @@ const format = (label, msg) => {
|
||||
}).join('\n')
|
||||
}
|
||||
|
||||
exports.logWithSpinner = logWithSpinner
|
||||
exports.stopSpinner = stopSpinner
|
||||
Object.assign(exports, spinner)
|
||||
|
||||
exports.log = msg => console.log(msg || '')
|
||||
|
||||
|
||||
@@ -34,3 +34,11 @@ exports.stopSpinner = (persist) => {
|
||||
}
|
||||
lastMsg = null
|
||||
}
|
||||
|
||||
exports.pauseSpinner = () => {
|
||||
spinner.stop()
|
||||
}
|
||||
|
||||
exports.resumeSpinner = () => {
|
||||
spinner.start()
|
||||
}
|
||||
|
||||
@@ -23,6 +23,10 @@ program
|
||||
program
|
||||
.command('create <app-name>')
|
||||
.description('create a new project powered by vue-cli-service')
|
||||
.option('-s, --saved', 'Skip prompts and use saved options')
|
||||
.option('-d, --default', 'Skip prompts and use default options')
|
||||
.option('-r, --registry <url>', 'Use specified NPM registry when installing dependencies')
|
||||
.option('-p, --package-manager <command>', 'Use specified NPM client when installing dependencies')
|
||||
.action(require('../lib/create'))
|
||||
|
||||
program
|
||||
@@ -46,23 +50,38 @@ program
|
||||
loadCommand('build', '@vue/cli-service-global').build(filename)
|
||||
})
|
||||
|
||||
// add some useful info on help
|
||||
program.on('--help', () => {
|
||||
console.log()
|
||||
console.log(` Run ${chalk.cyan(`vue <command> --help`)} for detailed usage of given command.`)
|
||||
console.log()
|
||||
})
|
||||
|
||||
// customize missing arg messages
|
||||
const formatArgs = ({ name, required }) => {
|
||||
return `${required ? '<' : '['}${name}${required ? '>' : ']'}`
|
||||
program.commands.forEach(c => c.on('--help', () => console.log()))
|
||||
|
||||
// enhance common error messages
|
||||
const enhanceErrorMessages = (methodName, log) => {
|
||||
program.Command.prototype[methodName] = function (...args) {
|
||||
this.outputHelp()
|
||||
console.log(` ` + chalk.red(log(...args)))
|
||||
console.log()
|
||||
process.exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
program.Command.prototype.missingArgument = function (argName) {
|
||||
console.log()
|
||||
console.log(` Missing required argument ${chalk.yellow(`<${argName}>`)}.`)
|
||||
console.log()
|
||||
console.log(` Usage: vue ${this._name} ${this._args.map(formatArgs).join(' ')}`)
|
||||
console.log()
|
||||
process.exit(1)
|
||||
}
|
||||
enhanceErrorMessages('missingArgument', argName => {
|
||||
return `Missing required argument ${chalk.yellow(`<${argName}>`)}.`
|
||||
})
|
||||
|
||||
enhanceErrorMessages('unknownOption', optionName => {
|
||||
return `Unknown option ${chalk.yellow(optionName)}.`
|
||||
})
|
||||
|
||||
enhanceErrorMessages('optionMissingArgument', (option, flag) => {
|
||||
return `Missing required argument for option ${chalk.yellow(option.flags)}` + (
|
||||
flag ? `, got ${chalk.yellow(flag)}` : ``
|
||||
)
|
||||
})
|
||||
|
||||
program.parse(process.argv)
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ const exec = require('util').promisify(require('child_process').exec)
|
||||
const {
|
||||
defaults,
|
||||
saveOptions,
|
||||
loadSavedOptions
|
||||
loadOptions
|
||||
} = require('./options')
|
||||
|
||||
const {
|
||||
@@ -44,9 +44,28 @@ module.exports = class Creator {
|
||||
promptModules.forEach(m => m(promptAPI))
|
||||
}
|
||||
|
||||
async create () {
|
||||
async create (cliOptions = {}) {
|
||||
const { name, context, createCompleteCbs } = this
|
||||
const options = await this.promptAndResolveOptions()
|
||||
|
||||
let options
|
||||
if (cliOptions.saved) {
|
||||
options = loadOptions()
|
||||
} else if (cliOptions.default) {
|
||||
options = defaults
|
||||
} else {
|
||||
options = await this.promptAndResolveOptions()
|
||||
}
|
||||
|
||||
// inject core service
|
||||
options.plugins['@vue/cli-service'] = {
|
||||
projectName: name
|
||||
}
|
||||
|
||||
const packageManager = (
|
||||
cliOptions.packageManager ||
|
||||
options.packageManager ||
|
||||
(hasYarn ? 'yarn' : 'npm')
|
||||
)
|
||||
|
||||
// write base package.json to disk
|
||||
clearConsole()
|
||||
@@ -72,7 +91,7 @@ module.exports = class Creator {
|
||||
// in development, avoid installation process
|
||||
setupDevProject(context, deps)
|
||||
} else {
|
||||
await installDeps(context, options.packageManager, deps)
|
||||
await installDeps(context, packageManager, deps, cliOptions.registry)
|
||||
}
|
||||
|
||||
// run generator
|
||||
@@ -90,7 +109,7 @@ module.exports = class Creator {
|
||||
// install additional deps (injected by generators)
|
||||
logWithSpinner('📦', `Installing additional dependencies...`)
|
||||
if (!process.env.VUE_CLI_TEST) {
|
||||
await installDeps(context, options.packageManager)
|
||||
await installDeps(context, packageManager, null, cliOptions.registry)
|
||||
}
|
||||
|
||||
// run complete cbs if any (injected by generators)
|
||||
@@ -125,7 +144,7 @@ module.exports = class Creator {
|
||||
|
||||
let options
|
||||
if (answers.mode === 'saved') {
|
||||
options = this.savedOptions // this is loaded when resolving prompts
|
||||
options = loadOptions()
|
||||
} else if (answers.mode === 'default') {
|
||||
options = defaults
|
||||
} else {
|
||||
@@ -143,11 +162,6 @@ module.exports = class Creator {
|
||||
saveOptions(options)
|
||||
}
|
||||
|
||||
// inject core service
|
||||
options.plugins['@vue/cli-service'] = {
|
||||
projectName: this.name
|
||||
}
|
||||
|
||||
debug('vue:cli-ptions')(options)
|
||||
return options
|
||||
}
|
||||
@@ -181,9 +195,8 @@ module.exports = class Creator {
|
||||
}
|
||||
]
|
||||
}
|
||||
const savedOptions = loadSavedOptions()
|
||||
const savedOptions = loadOptions()
|
||||
if (savedOptions.plugins) {
|
||||
this.savedOptions = savedOptions
|
||||
const savedFeatures = formatFeatures(savedOptions.plugins)
|
||||
modePrompt.choices.unshift({
|
||||
name: `Use previously saved preferences (${savedFeatures})`,
|
||||
|
||||
@@ -3,16 +3,16 @@ jest.mock('fs')
|
||||
const fs = require('fs')
|
||||
const {
|
||||
rcPath,
|
||||
saveOptions,
|
||||
loadSavedOptions
|
||||
loadOptions,
|
||||
saveOptions
|
||||
} = require('../options')
|
||||
|
||||
it('load options', () => {
|
||||
expect(loadSavedOptions()).toEqual({})
|
||||
expect(loadOptions()).toEqual({})
|
||||
fs.writeFileSync(rcPath, JSON.stringify({
|
||||
plugins: {}
|
||||
}, null, 2))
|
||||
expect(loadSavedOptions()).toEqual({
|
||||
expect(loadOptions()).toEqual({
|
||||
plugins: {}
|
||||
})
|
||||
})
|
||||
@@ -21,7 +21,7 @@ it('should not save unknown fields', () => {
|
||||
saveOptions({
|
||||
foo: 'bar'
|
||||
})
|
||||
expect(loadSavedOptions()).toEqual({
|
||||
expect(loadOptions()).toEqual({
|
||||
plugins: {}
|
||||
})
|
||||
})
|
||||
@@ -30,7 +30,7 @@ it('save options (merge)', () => {
|
||||
saveOptions({
|
||||
packageManager: 'yarn'
|
||||
})
|
||||
expect(loadSavedOptions()).toEqual({
|
||||
expect(loadOptions()).toEqual({
|
||||
packageManager: 'yarn',
|
||||
plugins: {}
|
||||
})
|
||||
@@ -40,7 +40,7 @@ it('save options (merge)', () => {
|
||||
foo: { a: 1 }
|
||||
}
|
||||
})
|
||||
expect(loadSavedOptions()).toEqual({
|
||||
expect(loadOptions()).toEqual({
|
||||
packageManager: 'yarn',
|
||||
plugins: {
|
||||
foo: { a: 1 }
|
||||
@@ -53,7 +53,7 @@ it('save options (merge)', () => {
|
||||
bar: { b: 2 }
|
||||
}
|
||||
})
|
||||
expect(loadSavedOptions()).toEqual({
|
||||
expect(loadOptions()).toEqual({
|
||||
packageManager: 'yarn',
|
||||
plugins: {
|
||||
bar: { b: 2 }
|
||||
@@ -67,7 +67,7 @@ it('save options (merge)', () => {
|
||||
bar: { d: 4 }
|
||||
}
|
||||
}, true)
|
||||
expect(loadSavedOptions()).toEqual({
|
||||
expect(loadOptions()).toEqual({
|
||||
packageManager: 'yarn',
|
||||
plugins: {
|
||||
foo: { a: 2, c: 3 },
|
||||
|
||||
@@ -7,7 +7,7 @@ const Creator = require('./Creator')
|
||||
const clearConsole = require('./util/clearConsole')
|
||||
const { error, stopSpinner } = require('@vue/cli-shared-utils')
|
||||
|
||||
async function create (projectName) {
|
||||
async function create (projectName, options) {
|
||||
const targetDir = path.resolve(process.cwd(), projectName)
|
||||
if (fs.existsSync(targetDir)) {
|
||||
clearConsole()
|
||||
@@ -36,11 +36,11 @@ async function create (projectName) {
|
||||
.map(file => require(`./promptModules/${file}`))
|
||||
|
||||
const creator = new Creator(projectName, targetDir, promptModules)
|
||||
await creator.create()
|
||||
await creator.create(options)
|
||||
}
|
||||
|
||||
module.exports = projectName => {
|
||||
create(projectName).catch(err => {
|
||||
module.exports = (...args) => {
|
||||
create(...args).catch(err => {
|
||||
stopSpinner(false) // do not persist
|
||||
error(err)
|
||||
process.exit(1)
|
||||
|
||||
@@ -9,6 +9,7 @@ const rcPath = exports.rcPath = (
|
||||
)
|
||||
|
||||
exports.defaults = {
|
||||
useTaobaoRegistry: null,
|
||||
packageManager: hasYarn ? 'yarn' : 'npm',
|
||||
plugins: {
|
||||
'@vue/cli-plugin-babel': {},
|
||||
@@ -17,10 +18,16 @@ exports.defaults = {
|
||||
}
|
||||
}
|
||||
|
||||
exports.loadSavedOptions = () => {
|
||||
let cachedOptions
|
||||
|
||||
exports.loadOptions = () => {
|
||||
if (cachedOptions) {
|
||||
return cachedOptions
|
||||
}
|
||||
if (fs.existsSync(rcPath)) {
|
||||
try {
|
||||
return JSON.parse(fs.readFileSync(rcPath, 'utf-8'))
|
||||
cachedOptions = JSON.parse(fs.readFileSync(rcPath, 'utf-8'))
|
||||
return cachedOptions
|
||||
} catch (e) {
|
||||
error(
|
||||
`Error loading saved preferences: ` +
|
||||
@@ -36,7 +43,7 @@ exports.loadSavedOptions = () => {
|
||||
}
|
||||
|
||||
exports.saveOptions = (toSave, deep) => {
|
||||
const options = exports.loadSavedOptions()
|
||||
const options = exports.loadOptions()
|
||||
if (deep) {
|
||||
deepMerge(options, toSave)
|
||||
} else {
|
||||
@@ -47,6 +54,7 @@ exports.saveOptions = (toSave, deep) => {
|
||||
delete options[key]
|
||||
}
|
||||
}
|
||||
cachedOptions = options
|
||||
try {
|
||||
fs.writeFileSync(rcPath, JSON.stringify(options, null, 2))
|
||||
} catch (e) {
|
||||
|
||||
@@ -1,6 +1,13 @@
|
||||
const { URL } = require('url')
|
||||
const https = require('https')
|
||||
const { spawn } = require('child_process')
|
||||
const chalk = require('chalk')
|
||||
const inquirer = require('inquirer')
|
||||
const { promisify } = require('util')
|
||||
const { spawn, exec } = require('child_process')
|
||||
const { loadOptions, saveOptions } = require('../options')
|
||||
const { pauseSpinner, resumeSpinner } = require('@vue/cli-shared-utils')
|
||||
|
||||
const debug = require('debug')('vue-cli:install')
|
||||
|
||||
const registries = {
|
||||
npm: 'https://registry.npmjs.org',
|
||||
@@ -20,42 +27,93 @@ const ping = url => new Promise((resolve, reject) => {
|
||||
req.end()
|
||||
})
|
||||
|
||||
const findFastestRegistry = () => {
|
||||
return Promise.race(Object.keys(registries).map(name => {
|
||||
return ping(registries[name])
|
||||
}))
|
||||
let checked
|
||||
let result
|
||||
const shouldUseTaobao = async (command) => {
|
||||
// ensure this only gets called once.
|
||||
if (checked) return result
|
||||
checked = true
|
||||
|
||||
// previously saved preference
|
||||
const saved = loadOptions().useTaobaoRegistry
|
||||
if (typeof saved === 'boolean') {
|
||||
return (result = saved)
|
||||
}
|
||||
|
||||
const save = val => {
|
||||
result = val
|
||||
saveOptions({ useTaobaoRegistry: val })
|
||||
return val
|
||||
}
|
||||
|
||||
const configValue = await promisify(exec)(`${command} config get registry`)
|
||||
const userCurrent = configValue.stdout.toString().trim()
|
||||
const defaultRegistry = registries[command]
|
||||
if (userCurrent !== defaultRegistry) {
|
||||
// user has configured custom regsitry, respect that
|
||||
return save(false)
|
||||
}
|
||||
const faster = await Promise.race([
|
||||
ping(defaultRegistry),
|
||||
ping(registries.taobao)
|
||||
])
|
||||
if (faster !== registries.taobao) {
|
||||
// default is already faster
|
||||
return save(false)
|
||||
}
|
||||
|
||||
// ask and save preference
|
||||
pauseSpinner()
|
||||
const { useTaobaoRegistry } = await inquirer.prompt([{
|
||||
name: 'useTaobaoRegistry',
|
||||
type: 'confirm',
|
||||
message: chalk.yellow(
|
||||
` Your connection to the the default ${command} registry seems to be slow.\n` +
|
||||
` Use ${chalk.cyan(registries.taobao)} for faster installation?`
|
||||
)
|
||||
}])
|
||||
resumeSpinner()
|
||||
return save(useTaobaoRegistry)
|
||||
}
|
||||
|
||||
let registry
|
||||
module.exports = async function installDeps (targetDir, command, deps) {
|
||||
registry = registry || await findFastestRegistry()
|
||||
module.exports = async function installDeps (targetDir, command, deps, cliRegistry) {
|
||||
const args = []
|
||||
if (command === 'npm') {
|
||||
args.push('install', '--loglevel', 'error')
|
||||
if (deps) {
|
||||
args.push('--save-dev')
|
||||
}
|
||||
} else if (command === 'yarn') {
|
||||
if (deps) {
|
||||
args.push('add', '--dev')
|
||||
}
|
||||
} else {
|
||||
throw new Error(`unknown package manager: ${command}`)
|
||||
}
|
||||
|
||||
const altRegistry = (
|
||||
cliRegistry || (
|
||||
(await shouldUseTaobao(command))
|
||||
? registries.taobao
|
||||
: null
|
||||
)
|
||||
)
|
||||
|
||||
if (altRegistry) {
|
||||
args.push(`--registry=${altRegistry}`)
|
||||
if (command === 'npm' && altRegistry === registries.taobao) {
|
||||
args.push(`--disturl=${taobaoDistURL}`)
|
||||
}
|
||||
}
|
||||
|
||||
if (deps) {
|
||||
args.push.apply(args, deps)
|
||||
}
|
||||
|
||||
debug(`command: `, command)
|
||||
debug(`args: `, args)
|
||||
|
||||
await new Promise((resolve, reject) => {
|
||||
const args = []
|
||||
if (command === 'npm') {
|
||||
args.push('install', '--loglevel', 'error')
|
||||
if (deps) {
|
||||
args.push('--save-dev')
|
||||
}
|
||||
} else if (command === 'yarn') {
|
||||
if (deps) {
|
||||
args.push('add', '--dev')
|
||||
}
|
||||
} else {
|
||||
throw new Error(`unknown package manager: ${command}`)
|
||||
}
|
||||
|
||||
if (registry !== registries[command]) {
|
||||
args.push(`--registry=${registry}`)
|
||||
if (registry === 'npm' && registry === registries.taobao) {
|
||||
args.push(`--disturl=${taobaoDistURL}`)
|
||||
}
|
||||
}
|
||||
|
||||
if (deps) {
|
||||
args.push.apply(args, deps)
|
||||
}
|
||||
|
||||
const child = spawn(command, args, {
|
||||
cwd: targetDir,
|
||||
stdio: 'pipe'
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
module.exports = function sortObject (obj, keyOrder) {
|
||||
if (!obj) return
|
||||
const res = {}
|
||||
const keys = Object.keys(obj)
|
||||
const getOrder = key => {
|
||||
|
||||
Reference in New Issue
Block a user