mirror of
https://github.com/vuejs/vue-cli.git
synced 2026-04-22 12:28:44 -05:00
feat: allow saving multiple presets
This commit is contained in:
@@ -28,6 +28,8 @@ exports.prompt = prompts => {
|
||||
const a = pendingAssertions[i - skipped]
|
||||
if (!a) {
|
||||
console.error(`no matching assertion for prompt:`, prompt)
|
||||
console.log(prompts)
|
||||
console.log(pendingAssertions)
|
||||
}
|
||||
|
||||
if (a.message) {
|
||||
|
||||
@@ -3,15 +3,15 @@ jest.setTimeout(30000)
|
||||
const path = require('path')
|
||||
const portfinder = require('portfinder')
|
||||
const { createServer } = require('http-server')
|
||||
const { defaults } = require('@vue/cli/lib/options')
|
||||
const { defaultPreset } = require('@vue/cli/lib/options')
|
||||
const create = require('@vue/cli-test-utils/createTestProject')
|
||||
const launchPuppeteer = require('@vue/cli-test-utils/launchPuppeteer')
|
||||
|
||||
let server, browser
|
||||
test('pwa', async () => {
|
||||
// it's ok to mutate here since jest loads each test in a separate vm
|
||||
defaults.plugins['@vue/cli-plugin-pwa'] = {}
|
||||
const project = await create('pwa-build', defaults)
|
||||
defaultPreset.plugins['@vue/cli-plugin-pwa'] = {}
|
||||
const project = await create('pwa-build', defaultPreset)
|
||||
expect(project.has('src/registerServiceWorker.js')).toBe(true)
|
||||
|
||||
const { stdout } = await project.run('vue-cli-service build')
|
||||
|
||||
@@ -3,13 +3,13 @@ jest.setTimeout(30000)
|
||||
const path = require('path')
|
||||
const portfinder = require('portfinder')
|
||||
const { createServer } = require('http-server')
|
||||
const { defaults } = require('@vue/cli/lib/options')
|
||||
const { defaultPreset } = require('@vue/cli/lib/options')
|
||||
const create = require('@vue/cli-test-utils/createTestProject')
|
||||
const launchPuppeteer = require('@vue/cli-test-utils/launchPuppeteer')
|
||||
|
||||
let server, browser, page
|
||||
test('build', async () => {
|
||||
const project = await create('e2e-build', defaults)
|
||||
const project = await create('e2e-build', defaultPreset)
|
||||
|
||||
// test public copy
|
||||
project.write('public/foo.js', '1')
|
||||
|
||||
@@ -3,13 +3,13 @@ jest.setTimeout(30000)
|
||||
const path = require('path')
|
||||
const portfinder = require('portfinder')
|
||||
const { createServer } = require('http-server')
|
||||
const { defaults } = require('@vue/cli/lib/options')
|
||||
const { defaultPreset } = require('@vue/cli/lib/options')
|
||||
const create = require('@vue/cli-test-utils/createTestProject')
|
||||
const launchPuppeteer = require('@vue/cli-test-utils/launchPuppeteer')
|
||||
|
||||
let server, browser, page
|
||||
test('build with DLL', async () => {
|
||||
const project = await create('e2e-build-dll', Object.assign({}, defaults, {
|
||||
const project = await create('e2e-build-dll', Object.assign({}, defaultPreset, {
|
||||
router: true,
|
||||
vuex: true
|
||||
}))
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
jest.setTimeout(30000)
|
||||
|
||||
const { defaults } = require('@vue/cli/lib/options')
|
||||
const { defaultPreset } = require('@vue/cli/lib/options')
|
||||
const create = require('@vue/cli-test-utils/createTestProject')
|
||||
const serve = require('@vue/cli-test-utils/serveWithPuppeteer')
|
||||
|
||||
const sleep = n => new Promise(resolve => setTimeout(resolve, n))
|
||||
|
||||
test('serve', async () => {
|
||||
const project = await create('e2e-serve', defaults)
|
||||
const project = await create('e2e-serve', defaultPreset)
|
||||
|
||||
await serve(
|
||||
() => project.run('vue-cli-service serve'),
|
||||
@@ -26,7 +26,7 @@ test('serve', async () => {
|
||||
})
|
||||
|
||||
test('serve with router', async () => {
|
||||
const project = await create('e2e-serve-router', Object.assign({}, defaults, {
|
||||
const project = await create('e2e-serve-router', Object.assign({}, defaultPreset, {
|
||||
router: true
|
||||
}))
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
module.exports = function silence (exports) {
|
||||
module.exports = function silence (logName, exports) {
|
||||
const logs = {}
|
||||
Object.keys(exports).forEach(key => {
|
||||
if (key !== 'error') {
|
||||
@@ -8,5 +8,5 @@ module.exports = function silence (exports) {
|
||||
}
|
||||
}
|
||||
})
|
||||
exports.logs = logs
|
||||
exports[logName] = logs
|
||||
}
|
||||
|
||||
@@ -45,5 +45,5 @@ exports.clearConsole = title => {
|
||||
|
||||
// silent all logs except errors during tests and keep record
|
||||
if (process.env.VUE_CLI_TEST) {
|
||||
require('./_silence')(exports)
|
||||
require('./_silence')('logs', exports)
|
||||
}
|
||||
|
||||
@@ -45,5 +45,5 @@ exports.resumeSpinner = () => {
|
||||
|
||||
// silent all logs except errors during tests and keep record
|
||||
if (process.env.VUE_CLI_TEST) {
|
||||
require('./_silence')(exports)
|
||||
require('./_silence')('spinner', exports)
|
||||
}
|
||||
|
||||
@@ -4,11 +4,15 @@ const { error } = require('./logger')
|
||||
// proxy to joi for option validation
|
||||
exports.createSchema = fn => fn(joi)
|
||||
|
||||
exports.validate = (obj, schema, options = {}) => {
|
||||
exports.validate = (obj, schema, options = {}, noExit) => {
|
||||
joi.validate(obj, schema, options, err => {
|
||||
if (err) {
|
||||
error(`vue-cli options validation failed:\n` + err.message)
|
||||
process.exit(1)
|
||||
if (!noExit) {
|
||||
process.exit(1)
|
||||
} else {
|
||||
throw err
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// using this requires mocking fs & inquirer
|
||||
|
||||
const Creator = require('@vue/cli/lib/Creator')
|
||||
const { loadOptions } = require('@vue/cli/lib/options')
|
||||
const { expectPrompts } = require('inquirer') // from mock
|
||||
|
||||
module.exports = async function assertPromptModule (
|
||||
@@ -13,7 +14,7 @@ module.exports = async function assertPromptModule (
|
||||
if (opts.plguinsOnly) {
|
||||
expectedPrompts.unshift(
|
||||
{
|
||||
message: 'project creation mode',
|
||||
message: 'Please pick a preset',
|
||||
choose: 1
|
||||
}
|
||||
)
|
||||
@@ -23,23 +24,24 @@ module.exports = async function assertPromptModule (
|
||||
choose: 1 // package.json
|
||||
},
|
||||
{
|
||||
message: 'package manager',
|
||||
choose: 0 // yarn
|
||||
},
|
||||
{
|
||||
message: 'Save the preferences',
|
||||
message: 'Save this as a preset',
|
||||
confirm: false
|
||||
}
|
||||
)
|
||||
if (!loadOptions().packageManager) {
|
||||
expectedPrompts.push({
|
||||
message: 'package manager',
|
||||
choose: 0 // yarn
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
expectPrompts(expectedPrompts)
|
||||
const creator = new Creator('test', '/', [].concat(module))
|
||||
const options = await creator.promptAndResolveOptions()
|
||||
const preset = await creator.promptAndResolvePreset()
|
||||
|
||||
if (opts.plguinsOnly) {
|
||||
delete options.packageManager
|
||||
delete options.useConfigFiles
|
||||
delete preset.useConfigFiles
|
||||
}
|
||||
expect(options).toEqual(expectedOptions)
|
||||
expect(preset).toEqual(expectedOptions)
|
||||
}
|
||||
|
||||
@@ -6,15 +6,9 @@ const readFile = promisify(fs.readFile)
|
||||
const writeFile = promisify(fs.writeFile)
|
||||
const mkdirp = promisify(require('mkdirp'))
|
||||
|
||||
module.exports = function createTestProject (name, config, cwd) {
|
||||
module.exports = function createTestProject (name, preset, cwd) {
|
||||
cwd = cwd || path.resolve(__dirname, '../../test')
|
||||
|
||||
config = Object.assign({
|
||||
packageManager: 'yarn',
|
||||
useTaobaoRegistry: false,
|
||||
plugins: {}
|
||||
}, config)
|
||||
|
||||
const projectRoot = path.resolve(cwd, name)
|
||||
|
||||
const read = file => {
|
||||
@@ -46,8 +40,8 @@ module.exports = function createTestProject (name, config, cwd) {
|
||||
'create',
|
||||
name,
|
||||
'--force',
|
||||
'--config',
|
||||
JSON.stringify(config)
|
||||
'--inlinePreset',
|
||||
JSON.stringify(preset)
|
||||
]
|
||||
|
||||
const options = {
|
||||
|
||||
@@ -7,15 +7,20 @@ const assertPromptModule = require('@vue/cli-test-utils/assertPromptModule')
|
||||
test('default', async () => {
|
||||
const epxectedPrompts = [
|
||||
{
|
||||
message: 'project creation mode',
|
||||
message: 'pick a preset',
|
||||
choices: [
|
||||
'Zero-config',
|
||||
'default',
|
||||
'Manually select'
|
||||
],
|
||||
choose: 0
|
||||
},
|
||||
{
|
||||
message: 'package manager',
|
||||
choices: ['Yarn', 'NPM'],
|
||||
choose: 0
|
||||
}
|
||||
]
|
||||
assertPromptModule([], epxectedPrompts, defaults)
|
||||
await assertPromptModule([], epxectedPrompts, defaults.presets.default)
|
||||
})
|
||||
|
||||
test('manual + PromptModuleAPI', async () => {
|
||||
@@ -73,18 +78,16 @@ test('manual + PromptModuleAPI', async () => {
|
||||
choose: 0
|
||||
},
|
||||
{
|
||||
message: 'package manager',
|
||||
choices: ['Yarn', 'NPM'],
|
||||
choose: 0
|
||||
message: 'Save this as a preset',
|
||||
confirm: true
|
||||
},
|
||||
{
|
||||
message: 'Save the preferences',
|
||||
confirm: true
|
||||
message: 'Save preset as',
|
||||
input: 'test'
|
||||
}
|
||||
]
|
||||
|
||||
const expectedOptions = {
|
||||
packageManager: 'yarn',
|
||||
useConfigFiles: true,
|
||||
plugins: {
|
||||
bar: {},
|
||||
@@ -98,8 +101,8 @@ test('manual + PromptModuleAPI', async () => {
|
||||
const expectedPromptsForSaved = [
|
||||
{
|
||||
choices: [
|
||||
'Use previously saved',
|
||||
'Zero-config',
|
||||
'test',
|
||||
'default',
|
||||
'Manually'
|
||||
],
|
||||
choose: 0
|
||||
|
||||
@@ -323,10 +323,7 @@ test('extract config files', async () => {
|
||||
},
|
||||
jest: {
|
||||
foo: 'bar'
|
||||
},
|
||||
browserslist: [
|
||||
'>=ie9'
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
const generator = new Generator('/', {}, [
|
||||
@@ -345,6 +342,5 @@ test('extract config files', async () => {
|
||||
expect(fs.readFileSync('/.babelrc', 'utf-8')).toMatch(json(configs.babel))
|
||||
expect(fs.readFileSync('/.postcssrc', 'utf-8')).toMatch(json(configs.postcss))
|
||||
expect(fs.readFileSync('/.eslintrc', 'utf-8')).toMatch(json(configs.eslintConfig))
|
||||
expect(fs.readFileSync('/.browserslistrc', 'utf-8')).toMatch(configs.browserslist.join('\n'))
|
||||
expect(fs.readFileSync('/jest.config.js', 'utf-8')).toMatch(`module.exports = {\n foo: 'bar'\n}`)
|
||||
})
|
||||
|
||||
@@ -4,16 +4,17 @@ const fs = require('fs')
|
||||
const {
|
||||
rcPath,
|
||||
loadOptions,
|
||||
saveOptions
|
||||
saveOptions,
|
||||
savePreset
|
||||
} = require('../lib/options')
|
||||
|
||||
test('load options', () => {
|
||||
expect(loadOptions()).toEqual({})
|
||||
fs.writeFileSync(rcPath, JSON.stringify({
|
||||
plugins: {}
|
||||
presets: {}
|
||||
}, null, 2))
|
||||
expect(loadOptions()).toEqual({
|
||||
plugins: {}
|
||||
presets: {}
|
||||
})
|
||||
})
|
||||
|
||||
@@ -22,49 +23,52 @@ test('should not save unknown fields', () => {
|
||||
foo: 'bar'
|
||||
})
|
||||
expect(loadOptions()).toEqual({
|
||||
plugins: {}
|
||||
presets: {}
|
||||
})
|
||||
})
|
||||
|
||||
test('save options (merge)', () => {
|
||||
test('save options', () => {
|
||||
// partial
|
||||
saveOptions({
|
||||
packageManager: 'yarn'
|
||||
})
|
||||
expect(loadOptions()).toEqual({
|
||||
packageManager: 'yarn',
|
||||
plugins: {}
|
||||
presets: {}
|
||||
})
|
||||
|
||||
// replace
|
||||
saveOptions({
|
||||
plugins: {
|
||||
presets: {
|
||||
foo: { a: 1 }
|
||||
}
|
||||
})
|
||||
expect(loadOptions()).toEqual({
|
||||
packageManager: 'yarn',
|
||||
plugins: {
|
||||
presets: {
|
||||
foo: { a: 1 }
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
// shallow save should replace fields
|
||||
saveOptions({
|
||||
plugins: {
|
||||
bar: { b: 2 }
|
||||
}
|
||||
})
|
||||
test('save preset', () => {
|
||||
savePreset('bar', { a: 2 })
|
||||
expect(loadOptions()).toEqual({
|
||||
packageManager: 'yarn',
|
||||
plugins: {
|
||||
bar: { b: 2 }
|
||||
presets: {
|
||||
foo: { a: 1 },
|
||||
bar: { a: 2 }
|
||||
}
|
||||
})
|
||||
|
||||
// should entirely replace presets
|
||||
savePreset('foo', { c: 3 })
|
||||
savePreset('bar', { d: 4 })
|
||||
expect(loadOptions()).toEqual({
|
||||
packageManager: 'yarn',
|
||||
presets: {
|
||||
foo: { c: 3 },
|
||||
bar: { d: 4 }
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
test('save options (replace)', () => {
|
||||
const toSave = {
|
||||
foo: 'bar'
|
||||
}
|
||||
saveOptions(toSave, true)
|
||||
expect(loadOptions()).toEqual(toSave)
|
||||
})
|
||||
|
||||
@@ -37,9 +37,9 @@ program
|
||||
program
|
||||
.command('create <app-name>')
|
||||
.description('create a new project powered by vue-cli-service')
|
||||
.option('-s, --saved', 'Skip prompts and use saved config')
|
||||
.option('-p, --preset <presetName>', 'Skip prompts and use saved preset')
|
||||
.option('-d, --default', 'Skip prompts and use default config')
|
||||
.option('-c, --config <json>', 'Skip prompts and use inline JSON string as config')
|
||||
.option('-i, --inlinePreset <json>', 'Skip prompts and use inline JSON string as preset')
|
||||
.option('-r, --registry <url>', 'Use specified NPM registry when installing dependencies')
|
||||
.option('-m, --packageManager <command>', 'Use specified NPM client when installing dependencies')
|
||||
.option('-f, --force', 'Overwrite target directory if it exists')
|
||||
|
||||
@@ -16,9 +16,10 @@ const setupDevProject = require('./util/setupDevProject')
|
||||
|
||||
const {
|
||||
defaults,
|
||||
validate,
|
||||
saveOptions,
|
||||
loadOptions
|
||||
loadOptions,
|
||||
savePreset,
|
||||
validatePreset
|
||||
} = require('./options')
|
||||
|
||||
const {
|
||||
@@ -30,14 +31,14 @@ const {
|
||||
stopSpinner
|
||||
} = require('@vue/cli-shared-utils')
|
||||
|
||||
const isMode = _mode => ({ mode }) => _mode === mode
|
||||
const isManualMode = answers => answers.preset === '__manual__'
|
||||
|
||||
module.exports = class Creator {
|
||||
constructor (name, context, promptModules) {
|
||||
this.name = name
|
||||
this.context = process.env.VUE_CLI_CONTEXT = context
|
||||
const { modePrompt, featurePrompt } = this.resolveIntroPrompts()
|
||||
this.modePrompt = modePrompt
|
||||
const { presetPrompt, featurePrompt } = this.resolveIntroPrompts()
|
||||
this.presetPrompt = presetPrompt
|
||||
this.featurePrompt = featurePrompt
|
||||
this.outroPrompts = this.resolveOutroPrompts()
|
||||
this.injectedPrompts = []
|
||||
@@ -56,32 +57,35 @@ module.exports = class Creator {
|
||||
return execa(command, args, { cwd: context })
|
||||
}
|
||||
|
||||
let options
|
||||
if (cliOptions.saved) {
|
||||
options = loadOptions()
|
||||
let preset
|
||||
if (cliOptions.preset) {
|
||||
// vue create foo --preset bar
|
||||
preset = this.resolvePreset(cliOptions.preset)
|
||||
} else if (cliOptions.default) {
|
||||
options = defaults
|
||||
} else if (cliOptions.config) {
|
||||
// vue create foo --default
|
||||
preset = defaults.presets.default
|
||||
} else if (cliOptions.inlinePreset) {
|
||||
// vue create foo --inlinePreset {...}
|
||||
try {
|
||||
options = JSON.parse(cliOptions.config)
|
||||
preset = JSON.parse(cliOptions.inlinePreset)
|
||||
} catch (e) {
|
||||
error(`CLI inline config is not valid JSON: ${cliOptions.config}`)
|
||||
error(`CLI inline preset is not valid JSON: ${cliOptions.inlinePreset}`)
|
||||
process.exit(1)
|
||||
}
|
||||
} else {
|
||||
options = await this.promptAndResolveOptions()
|
||||
preset = await this.promptAndResolvePreset()
|
||||
}
|
||||
|
||||
// clone before mutating
|
||||
options = cloneDeep(options)
|
||||
preset = cloneDeep(preset)
|
||||
// inject core service
|
||||
options.plugins['@vue/cli-service'] = Object.assign({
|
||||
preset.plugins['@vue/cli-service'] = Object.assign({
|
||||
projectName: name
|
||||
}, options)
|
||||
}, preset)
|
||||
|
||||
const packageManager = (
|
||||
cliOptions.packageManager ||
|
||||
options.packageManager ||
|
||||
loadOptions().packageManager ||
|
||||
(hasYarn ? 'yarn' : 'npm')
|
||||
)
|
||||
|
||||
@@ -103,7 +107,7 @@ module.exports = class Creator {
|
||||
private: true,
|
||||
devDependencies: {}
|
||||
}
|
||||
const deps = Object.keys(options.plugins)
|
||||
const deps = Object.keys(preset.plugins)
|
||||
deps.forEach(dep => {
|
||||
pkg.devDependencies[dep] = `^${latestCLIVersion}`
|
||||
})
|
||||
@@ -133,12 +137,12 @@ module.exports = class Creator {
|
||||
// run generator
|
||||
log()
|
||||
log(`🚀 Invoking generators...`)
|
||||
const plugins = this.resolvePlugins(options.plugins)
|
||||
const plugins = this.resolvePlugins(preset.plugins)
|
||||
const generator = new Generator(
|
||||
context,
|
||||
pkg,
|
||||
plugins,
|
||||
options.useConfigFiles,
|
||||
preset.useConfigFiles,
|
||||
createCompleteCbs
|
||||
)
|
||||
await generator.generate()
|
||||
@@ -174,43 +178,69 @@ module.exports = class Creator {
|
||||
log(
|
||||
`👉 Get started with the following commands:\n\n` +
|
||||
chalk.cyan(` ${chalk.gray('$')} cd ${name}\n`) +
|
||||
chalk.cyan(` ${chalk.gray('$')} ${options.packageManager === 'yarn' ? 'yarn serve' : 'npm run serve'}`)
|
||||
chalk.cyan(` ${chalk.gray('$')} ${packageManager === 'yarn' ? 'yarn serve' : 'npm run serve'}`)
|
||||
)
|
||||
log()
|
||||
}
|
||||
|
||||
async promptAndResolveOptions () {
|
||||
async promptAndResolvePreset () {
|
||||
// prompt
|
||||
clearConsole()
|
||||
const answers = await inquirer.prompt(this.resolveFinalPrompts())
|
||||
debug('vue:cli-answers')(answers)
|
||||
debug('vue-cli:answers')(answers)
|
||||
|
||||
let options
|
||||
if (answers.mode === 'saved') {
|
||||
options = loadOptions()
|
||||
} else if (answers.mode === 'default') {
|
||||
options = defaults
|
||||
if (answers.packageManager) {
|
||||
saveOptions({
|
||||
packageManager: answers.packageManager
|
||||
})
|
||||
}
|
||||
|
||||
let preset
|
||||
if (answers.preset && answers.preset !== '__manual__') {
|
||||
preset = this.resolvePreset(answers.preset)
|
||||
} else {
|
||||
// manual
|
||||
options = {
|
||||
packageManager: answers.packageManager || loadOptions().packageManager,
|
||||
preset = {
|
||||
useConfigFiles: answers.useConfigFiles === 'files',
|
||||
plugins: {}
|
||||
}
|
||||
// run cb registered by prompt modules to finalize the options
|
||||
this.promptCompleteCbs.forEach(cb => cb(answers, options))
|
||||
answers.features = answers.features || []
|
||||
// run cb registered by prompt modules to finalize the preset
|
||||
this.promptCompleteCbs.forEach(cb => cb(answers, preset))
|
||||
}
|
||||
|
||||
// validate
|
||||
validate(options)
|
||||
validatePreset(preset)
|
||||
|
||||
// save options
|
||||
if (answers.mode === 'manual' && answers.save) {
|
||||
saveOptions(options, true /* replace */)
|
||||
// save preset
|
||||
if (answers.save && answers.saveName) {
|
||||
savePreset(answers.saveName, preset)
|
||||
}
|
||||
|
||||
debug('vue:cli-options')(options)
|
||||
return options
|
||||
debug('vue-cli:preset')(preset)
|
||||
return preset
|
||||
}
|
||||
|
||||
resolvePreset (name) {
|
||||
const savedPresets = loadOptions().presets || {}
|
||||
let preset = savedPresets[name]
|
||||
// use default preset if user has not overwritten it
|
||||
if (name === 'default' && !preset) {
|
||||
preset = defaults.presets.default
|
||||
}
|
||||
if (!preset) {
|
||||
error(`preset "${name}" not found.`)
|
||||
const presets = Object.keys(savedPresets)
|
||||
if (presets.length) {
|
||||
log()
|
||||
log(`available presets:\n${presets.join(`\n`)}`)
|
||||
} else {
|
||||
log(`you don't seem to have any saved preset.`)
|
||||
log(`run vue-cli in manual mode to create a preset.`)
|
||||
}
|
||||
process.exit(1)
|
||||
}
|
||||
return preset
|
||||
}
|
||||
|
||||
// { id: options } => [{ id, apply, options }]
|
||||
@@ -228,51 +258,45 @@ module.exports = class Creator {
|
||||
}
|
||||
|
||||
resolveIntroPrompts () {
|
||||
const defualtFeatures = formatFeatures(defaults)
|
||||
const modePrompt = {
|
||||
name: 'mode',
|
||||
const savedOptions = loadOptions()
|
||||
const presets = Object.assign({}, savedOptions.presets, defaults.presets)
|
||||
const presetChoices = Object.keys(presets).map(name => {
|
||||
return {
|
||||
name: `${name} (${formatFeatures(presets[name])})`,
|
||||
value: name
|
||||
}
|
||||
})
|
||||
const presetPrompt = {
|
||||
name: 'preset',
|
||||
type: 'list',
|
||||
message: `Please pick a project creation mode:`,
|
||||
message: `Please pick a preset:`,
|
||||
choices: [
|
||||
{
|
||||
name: `Zero-config with defaults (${defualtFeatures})`,
|
||||
value: 'default'
|
||||
},
|
||||
...presetChoices,
|
||||
{
|
||||
name: 'Manually select features',
|
||||
value: 'manual'
|
||||
value: '__manual__'
|
||||
}
|
||||
]
|
||||
}
|
||||
const savedOptions = loadOptions()
|
||||
if (savedOptions.plugins) {
|
||||
const savedFeatures = formatFeatures(savedOptions)
|
||||
modePrompt.choices.unshift({
|
||||
name: `Use previously saved config (${savedFeatures})`,
|
||||
value: 'saved'
|
||||
})
|
||||
}
|
||||
const featurePrompt = {
|
||||
name: 'features',
|
||||
when: isMode('manual'),
|
||||
when: isManualMode,
|
||||
type: 'checkbox',
|
||||
message: 'Check the features needed for your project:',
|
||||
choices: [],
|
||||
pageSize: 8
|
||||
}
|
||||
return {
|
||||
modePrompt,
|
||||
presetPrompt,
|
||||
featurePrompt
|
||||
}
|
||||
}
|
||||
|
||||
resolveOutroPrompts () {
|
||||
const outroPrompts = []
|
||||
const savedOptions = loadOptions()
|
||||
if (savedOptions.useConfigFiles == null) {
|
||||
outroPrompts.push({
|
||||
const outroPrompts = [
|
||||
{
|
||||
name: 'useConfigFiles',
|
||||
when: isMode('manual'),
|
||||
when: isManualMode,
|
||||
type: 'list',
|
||||
message: 'Where do you prefer placing config for Babel, PostCSS, ESLint, etc.?',
|
||||
choices: [
|
||||
@@ -285,12 +309,26 @@ module.exports = class Creator {
|
||||
value: 'pkg'
|
||||
}
|
||||
]
|
||||
})
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'save',
|
||||
when: isManualMode,
|
||||
type: 'confirm',
|
||||
message: 'Save this as a preset for future projects?'
|
||||
},
|
||||
{
|
||||
name: 'saveName',
|
||||
when: answers => answers.save,
|
||||
type: 'input',
|
||||
message: 'Save preset as:'
|
||||
}
|
||||
]
|
||||
|
||||
// ask for packageManager once
|
||||
const savedOptions = loadOptions()
|
||||
if (hasYarn && !savedOptions.packageManager) {
|
||||
outroPrompts.push({
|
||||
name: 'packageManager',
|
||||
when: isMode('manual'),
|
||||
type: 'list',
|
||||
message: 'Pick the package manager to use when installing dependencies:',
|
||||
choices: [
|
||||
@@ -307,30 +345,25 @@ module.exports = class Creator {
|
||||
]
|
||||
})
|
||||
}
|
||||
outroPrompts.push({
|
||||
name: 'save',
|
||||
when: isMode('manual'),
|
||||
type: 'confirm',
|
||||
message: 'Save the preferences for future projects? (You can always manually edit ~/.vuerc)'
|
||||
})
|
||||
|
||||
return outroPrompts
|
||||
}
|
||||
|
||||
resolveFinalPrompts () {
|
||||
// patch generator-injected prompts to only show when mode === 'manual'
|
||||
// patch generator-injected prompts to only show in manual mode
|
||||
this.injectedPrompts.forEach(prompt => {
|
||||
const originalWhen = prompt.when || (() => true)
|
||||
prompt.when = options => {
|
||||
return options.mode === 'manual' && originalWhen(options)
|
||||
prompt.when = answers => {
|
||||
return isManualMode(answers) && originalWhen(answers)
|
||||
}
|
||||
})
|
||||
const prompts = [].concat(
|
||||
this.modePrompt,
|
||||
const prompts = [
|
||||
this.presetPrompt,
|
||||
this.featurePrompt,
|
||||
this.injectedPrompts,
|
||||
this.outroPrompts
|
||||
)
|
||||
debug('vue:cli-prompts')(prompts)
|
||||
...this.injectedPrompts,
|
||||
...this.outroPrompts
|
||||
]
|
||||
debug('vue-cli:prompts')(prompts)
|
||||
return prompts
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,44 +1,49 @@
|
||||
const fs = require('fs')
|
||||
const os = require('os')
|
||||
const path = require('path')
|
||||
const {
|
||||
error,
|
||||
hasYarn,
|
||||
createSchema,
|
||||
validate
|
||||
} = require('@vue/cli-shared-utils')
|
||||
const cloneDeep = require('lodash.clonedeep')
|
||||
const { error, log, createSchema, validate } = require('@vue/cli-shared-utils')
|
||||
|
||||
const rcPath = exports.rcPath = (
|
||||
process.env.VUE_CLI_CONFIG_PATH ||
|
||||
path.join(os.homedir(), '.vuerc')
|
||||
)
|
||||
|
||||
const schema = createSchema(joi => joi.object().keys({
|
||||
const presetSchema = createSchema(joi => joi.object().keys({
|
||||
useConfigFiles: joi.boolean(),
|
||||
router: joi.boolean(),
|
||||
vuex: joi.boolean(),
|
||||
cssPreprocessor: joi.string().only(['sass', 'less', 'stylus']),
|
||||
useTaobaoRegistry: joi.boolean(),
|
||||
packageManager: joi.string().only(['yarn', 'npm']),
|
||||
useConfigFiles: joi.boolean(),
|
||||
plugins: joi.object().required()
|
||||
}))
|
||||
|
||||
exports.validate = options => validate(options, schema)
|
||||
const schema = createSchema(joi => joi.object().keys({
|
||||
packageManager: joi.string().only(['yarn', 'npm']),
|
||||
useTaobaoRegistry: joi.boolean(),
|
||||
presets: joi.object().pattern(/^/, presetSchema)
|
||||
}))
|
||||
|
||||
exports.defaults = {
|
||||
exports.validatePreset = preset => validate(preset, presetSchema)
|
||||
|
||||
exports.defaultPreset = {
|
||||
router: false,
|
||||
vuex: false,
|
||||
useConfigFiles: false,
|
||||
cssPreprocessor: undefined,
|
||||
useConfigFiles: undefined,
|
||||
useTaobaoRegistry: undefined,
|
||||
packageManager: hasYarn ? 'yarn' : 'npm',
|
||||
plugins: {
|
||||
'@vue/cli-plugin-babel': {},
|
||||
'@vue/cli-plugin-eslint': {
|
||||
config: 'base',
|
||||
lintOn: ['save', 'commit']
|
||||
},
|
||||
'@vue/cli-plugin-unit-mocha': {}
|
||||
lintOn: ['save']
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
exports.defaults = {
|
||||
packageManager: undefined,
|
||||
useTaobaoRegistry: undefined,
|
||||
presets: {
|
||||
'default': exports.defaultPreset
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,28 +56,32 @@ exports.loadOptions = () => {
|
||||
if (fs.existsSync(rcPath)) {
|
||||
try {
|
||||
cachedOptions = JSON.parse(fs.readFileSync(rcPath, 'utf-8'))
|
||||
return cachedOptions
|
||||
} catch (e) {
|
||||
error(
|
||||
`Error loading saved preferences: ` +
|
||||
`~/.vuerc may be corrupted or have syntax errors. ` +
|
||||
`You may need to delete it and re-run vue-cli in manual mode.\n` +
|
||||
`Please fix/delete it and re-run vue-cli in manual mode.\n` +
|
||||
`(${e.message})`,
|
||||
)
|
||||
process.exit(1)
|
||||
// process.exit(1)
|
||||
}
|
||||
try {
|
||||
validate(cachedOptions, schema, undefined, true /* noExit */)
|
||||
} catch (e) {
|
||||
log(
|
||||
`~/.vuerc may be outdated. ` +
|
||||
`Please delete it and re-run vue-cli in manual mode.`
|
||||
)
|
||||
// process.exit(1)
|
||||
}
|
||||
return cachedOptions
|
||||
} else {
|
||||
return {}
|
||||
}
|
||||
}
|
||||
|
||||
exports.saveOptions = (toSave, replace) => {
|
||||
let options
|
||||
if (replace) {
|
||||
options = toSave
|
||||
} else {
|
||||
options = Object.assign(exports.loadOptions(), toSave)
|
||||
}
|
||||
exports.saveOptions = toSave => {
|
||||
const options = Object.assign(cloneDeep(exports.loadOptions()), toSave)
|
||||
for (const key in options) {
|
||||
if (!(key in exports.defaults)) {
|
||||
delete options[key]
|
||||
@@ -89,3 +98,9 @@ exports.saveOptions = (toSave, replace) => {
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
exports.savePreset = (name, preset) => {
|
||||
const presets = cloneDeep(exports.loadOptions().presets || {})
|
||||
presets[name] = preset
|
||||
exports.saveOptions({ presets })
|
||||
}
|
||||
|
||||
@@ -22,11 +22,14 @@ module.exports = {
|
||||
jest: {
|
||||
filename: 'jest.config.js',
|
||||
transform: js
|
||||
},
|
||||
browserslist: {
|
||||
filename: '.browserslistrc',
|
||||
transform: value => value.join('\n')
|
||||
}
|
||||
|
||||
// these are less likely to be edited frequently
|
||||
|
||||
// browserslist: {
|
||||
// filename: '.browserslistrc',
|
||||
// transform: value => value.join('\n')
|
||||
// },
|
||||
// 'lint-staged': {
|
||||
// filename: '.lintstagedrc',
|
||||
// transform: json
|
||||
|
||||
@@ -1,14 +1,17 @@
|
||||
const chalk = require('chalk')
|
||||
|
||||
module.exports = function formatFeatures (options, lead, joiner) {
|
||||
module.exports = function formatFeatures (preset, lead, joiner) {
|
||||
const features = []
|
||||
if (options.router) {
|
||||
if (preset.router) {
|
||||
features.push('vue-router')
|
||||
}
|
||||
if (options.vuex) {
|
||||
if (preset.vuex) {
|
||||
features.push('vuex')
|
||||
}
|
||||
const plugins = Object.keys(options.plugins).filter(dep => {
|
||||
if (preset.cssPreprocessor) {
|
||||
features.push(preset.cssPreprocessor)
|
||||
}
|
||||
const plugins = Object.keys(preset.plugins).filter(dep => {
|
||||
return dep !== '@vue/cli-service'
|
||||
})
|
||||
features.push.apply(features, plugins)
|
||||
|
||||
Reference in New Issue
Block a user