mirror of
https://github.com/vuejs/vue-cli.git
synced 2026-03-09 17:09:05 -05:00
wip
This commit is contained in:
@@ -4,7 +4,6 @@ module.exports = service => {
|
||||
})
|
||||
|
||||
service.configureWebpack({ env: 'test' }, webpackConfig => {
|
||||
require('jsdom-global')()
|
||||
if (!webpackConfig.externals) {
|
||||
webpackConfig.externals = []
|
||||
}
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
require('jsdom-global')()
|
||||
|
||||
0
packages/@vue/cli/bin/vue
Normal file → Executable file
0
packages/@vue/cli/bin/vue
Normal file → Executable file
0
packages/@vue/cli/bin/vue-create
Normal file → Executable file
0
packages/@vue/cli/bin/vue-create
Normal file → Executable file
0
packages/@vue/cli/bin/vue-init
Normal file → Executable file
0
packages/@vue/cli/bin/vue-init
Normal file → Executable file
@@ -10,12 +10,19 @@ const writeFileTree = require('./util/writeFileTree')
|
||||
const rcPath = path.join(os.homedir(), '.vuerc')
|
||||
const isMode = _mode => ({ mode }) => _mode === mode
|
||||
|
||||
const defaultOptions = {
|
||||
features: ['eslint', 'unit'],
|
||||
eslint: 'eslint-only',
|
||||
unit: 'mocha',
|
||||
assertionLibrary: 'expect'
|
||||
}
|
||||
|
||||
module.exports = class Creator {
|
||||
constructor (name, generators) {
|
||||
this.name = name
|
||||
const { modePrompt, featuresPrompt } = this.resolveIntroPrompts()
|
||||
const { modePrompt, featurePrompt } = this.resolveIntroPrompts()
|
||||
this.modePrompt = modePrompt
|
||||
this.featuresPrompt = featuresPrompt
|
||||
this.featurePrompt = featurePrompt
|
||||
this.outroPrompts = this.resolveOutroPrompts()
|
||||
this.injectedPrompts = []
|
||||
this.deps = {}
|
||||
@@ -27,30 +34,38 @@ module.exports = class Creator {
|
||||
this.promptCompleteCbs = []
|
||||
this.fileMiddlewares = []
|
||||
|
||||
generators.forEach(({ module }) => {
|
||||
module(new GeneratorAPI(this, generator))
|
||||
generators.forEach(generator => {
|
||||
generator.module(new GeneratorAPI(this, generator))
|
||||
})
|
||||
}
|
||||
|
||||
async create (path) {
|
||||
// 1. prompt
|
||||
// prompt
|
||||
let options = await inquirer.prompt(this.resolveFinalPrompts())
|
||||
let needSave = false
|
||||
if (options.mode === 'saved') {
|
||||
options = this.loadSavedOptions()
|
||||
} else if (options.mode === 'default') {
|
||||
options = this.loadDefaultOptions()
|
||||
options = defaultOptions
|
||||
} else if (options.save) {
|
||||
this.saveOptions(options)
|
||||
needSave = true
|
||||
}
|
||||
options.features = options.features || []
|
||||
|
||||
// 2. run cbs (register generators)
|
||||
this.promptCompleteCbs.forEach((cb => cb(options))
|
||||
// 3. resolve deps, scripts and generate final package.json
|
||||
// run cbs (register generators)
|
||||
this.promptCompleteCbs.forEach(cb => cb(options))
|
||||
|
||||
// save after prompt complete cbs are run, since generators may modify
|
||||
// options in the callback
|
||||
if (needSave) {
|
||||
this.saveOptions(options)
|
||||
}
|
||||
|
||||
// resolve deps, scripts and generate final package.json
|
||||
this.resolvePackage()
|
||||
// 4. wait for file resolve
|
||||
// wait for file resolve
|
||||
await this.resolveFiles()
|
||||
// 5. write file tree to disk
|
||||
// write file tree to disk
|
||||
await writeFileTree(path, this.files)
|
||||
}
|
||||
|
||||
@@ -80,12 +95,12 @@ module.exports = class Creator {
|
||||
name: 'features',
|
||||
when: isMode('manual'),
|
||||
type: 'checkbox',
|
||||
message: 'Please check all features needed for your project.',
|
||||
message: 'Please check the features needed for your project.',
|
||||
choices: []
|
||||
}
|
||||
return {
|
||||
modePrompt,
|
||||
featuresPrompt
|
||||
featurePrompt
|
||||
}
|
||||
}
|
||||
|
||||
@@ -130,6 +145,7 @@ module.exports = class Creator {
|
||||
message: 'Automatically install NPM dependencies after project creation?'
|
||||
})
|
||||
}
|
||||
return outroPrompts
|
||||
}
|
||||
|
||||
resolveFinalPrompts () {
|
||||
@@ -140,12 +156,14 @@ module.exports = class Creator {
|
||||
return options.mode === 'manual' && originalWhen(options)
|
||||
}
|
||||
})
|
||||
return [].concat([
|
||||
const ret = [].concat(
|
||||
this.modePrompt,
|
||||
this.featuresPrompt,
|
||||
this.featurePrompt,
|
||||
this.injectedPrompts,
|
||||
this.outroPrompts
|
||||
])
|
||||
)
|
||||
console.log(ret)
|
||||
return ret
|
||||
}
|
||||
|
||||
loadSavedOptions () {
|
||||
@@ -208,9 +226,10 @@ module.exports = class Creator {
|
||||
return this.packageFields
|
||||
}
|
||||
|
||||
// TODO
|
||||
async resolveFiles () {
|
||||
|
||||
for (const middleware of this.fileMiddlewares) {
|
||||
await middleware(this.files)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
const { error } = require('./util/log')
|
||||
|
||||
module.exports = class GeneratorAPI {
|
||||
constructor (creator, generator) {
|
||||
this.creator = creator
|
||||
@@ -8,46 +10,50 @@ module.exports = class GeneratorAPI {
|
||||
this.creator.featurePrompt.choices.push(feature)
|
||||
}
|
||||
|
||||
injectOptionForFeature (featureName, option) {
|
||||
const feature = this.creator.featurePrompt.choices.find(f => {
|
||||
return f.name === featureName
|
||||
})
|
||||
if (!feature) {
|
||||
throw new Error(
|
||||
`injectOptionForFeature error in generator "${
|
||||
this.generator.id
|
||||
}": feature "${featureName}" does not exist.`
|
||||
)
|
||||
}
|
||||
feature.choices.push(option)
|
||||
}
|
||||
|
||||
injectPrompt (prompt) {
|
||||
this.creator.injectedPrompts.push(prompt)
|
||||
}
|
||||
|
||||
injectOptionForPrompt (name, option) {
|
||||
const prompt = this.creator.injectedPrompts.find(f => {
|
||||
return f.name === name
|
||||
})
|
||||
if (!prompt) {
|
||||
error(
|
||||
`injectOptionForFeature error in generator "${
|
||||
this.generator.id
|
||||
}": prompt "${name}" does not exist.`
|
||||
)
|
||||
}
|
||||
prompt.choices.push(option)
|
||||
}
|
||||
|
||||
onPromptComplete (cb) {
|
||||
this.creator.promptCompleteCbs.push(cb)
|
||||
}
|
||||
|
||||
injectDeps (deps) {
|
||||
Object.assign(this.creator.deps, deps)
|
||||
}
|
||||
|
||||
injectDevDeps(deps) {
|
||||
Object.assign(this.creator.devDeps, deps)
|
||||
}
|
||||
|
||||
injectScripts (scripts) {
|
||||
|
||||
Object.assign(this.creator.scripts, scripts)
|
||||
}
|
||||
|
||||
injectPackageFields (fields) {
|
||||
|
||||
Object.assign(this.creator.packageFields, fields)
|
||||
}
|
||||
|
||||
injectFilesMiddleware (middleware) {
|
||||
injectFileMiddleware (middleware) {
|
||||
this.creator.fileMiddlewares.push(middleware)
|
||||
}
|
||||
|
||||
renderFile (file) {
|
||||
|
||||
return file
|
||||
}
|
||||
|
||||
onCreateComplete (msg) {
|
||||
|
||||
@@ -1,36 +1,32 @@
|
||||
const fs = require('fs')
|
||||
const path = require('path')
|
||||
const program = require('commander')
|
||||
const Creator = require('./Creator')
|
||||
const Generator = require('./Generator')
|
||||
const { info, error } = require('./util/log')
|
||||
const { warn, error } = require('./util/log')
|
||||
const resolveInstalledGenerators = require('./util/resolveInstalledGenerators')
|
||||
|
||||
program.usage('<app-name>')
|
||||
program
|
||||
.usage('<app-name>')
|
||||
.parse(process.argv)
|
||||
|
||||
program.on('help', () => {
|
||||
console.log('TODO')
|
||||
})
|
||||
const projectName = program.args[0]
|
||||
if (!projectName) {
|
||||
warn(`\n Please provide an app name.`)
|
||||
program.outputHelp()
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
program.parse(process.argv)
|
||||
|
||||
const builtInGenerators = [
|
||||
'core',
|
||||
'babel',
|
||||
'typescript',
|
||||
'pwa',
|
||||
'eslint',
|
||||
'unit-jest',
|
||||
'unit-mocha-webpack',
|
||||
'e2e-nightwatch',
|
||||
'e2e-cypress'
|
||||
].map(id => new Generator(id, `./generators/${id}`))
|
||||
const builtInGenerators = fs
|
||||
.readdirSync(path.resolve(__dirname, './generators'))
|
||||
.map(id => new Generator(id, `./generators/${id}`))
|
||||
|
||||
const installedGenerators = resolveInstalledGenerators().map(id => {
|
||||
return new Generator(id)
|
||||
})
|
||||
|
||||
const creator = new Creator(builtInGenerators.concat(installedGenerators))
|
||||
const targetDir = path.resolve(process.cwd(), program.args[0])
|
||||
const targetDir = path.resolve(process.cwd(), projectName)
|
||||
const creator = new Creator(projectName, builtInGenerators.concat(installedGenerators))
|
||||
|
||||
creator
|
||||
.create(targetDir)
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
module.exports = api => {
|
||||
api.onPromptComplete(options => {
|
||||
if (!options.features.includes('ts')) {
|
||||
api.injectDeps({
|
||||
api.injectDevDeps({
|
||||
'@vue/cli-plugin-babel': '^1.0.0',
|
||||
'babel-preset-vue-app': '^2.0.0'
|
||||
})
|
||||
api.injectFileMiddleware(files => {
|
||||
files['.babelrc'] = api.renderFile('./files/.babelrc')
|
||||
files['.babelrc'] = api.renderFile('.babelrc')
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
module.exports = api => {
|
||||
|
||||
}
|
||||
|
||||
3
packages/@vue/cli/lib/generators/e2e/index.js
Normal file
3
packages/@vue/cli/lib/generators/e2e/index.js
Normal file
@@ -0,0 +1,3 @@
|
||||
module.exports = api => {
|
||||
|
||||
}
|
||||
3
packages/@vue/cli/lib/generators/eslint/index.js
Normal file
3
packages/@vue/cli/lib/generators/eslint/index.js
Normal file
@@ -0,0 +1,3 @@
|
||||
module.exports = api => {
|
||||
|
||||
}
|
||||
3
packages/@vue/cli/lib/generators/pwa/index.js
Normal file
3
packages/@vue/cli/lib/generators/pwa/index.js
Normal file
@@ -0,0 +1,3 @@
|
||||
module.exports = api => {
|
||||
|
||||
}
|
||||
3
packages/@vue/cli/lib/generators/router/index.js
Normal file
3
packages/@vue/cli/lib/generators/router/index.js
Normal file
@@ -0,0 +1,3 @@
|
||||
module.exports = api => {
|
||||
|
||||
}
|
||||
3
packages/@vue/cli/lib/generators/typescript/index.js
Normal file
3
packages/@vue/cli/lib/generators/typescript/index.js
Normal file
@@ -0,0 +1,3 @@
|
||||
module.exports = api => {
|
||||
|
||||
}
|
||||
@@ -1,55 +0,0 @@
|
||||
module.exports = api => {
|
||||
api.injectOptionForFeature('unit', {
|
||||
name: 'Mocha (with better webpack integration, https://mochajs.org/)',
|
||||
value: 'mocha',
|
||||
short: 'Mocha'
|
||||
})
|
||||
|
||||
api.injectPrompt({
|
||||
name: 'mochaAssertionLibrary',
|
||||
message: 'Pick an assertion library for unit tests:',
|
||||
when: options => options.unit === 'mocha',
|
||||
type: 'list',
|
||||
choices: [
|
||||
{
|
||||
name: 'Chai (http://chaijs.com/)',
|
||||
value: 'chai',
|
||||
short: 'Chai'
|
||||
},
|
||||
{
|
||||
name: 'Expect (https://facebook.github.io/jest/docs/en/expect.html)',
|
||||
value: 'expect',
|
||||
short: 'Expect'
|
||||
},
|
||||
{
|
||||
name: `I'll pick my own`,
|
||||
value: 'custom',
|
||||
short: 'Custom'
|
||||
}
|
||||
]
|
||||
})
|
||||
|
||||
api.onPromptComplete(options => {
|
||||
if (options.unit === 'mocha') {
|
||||
const dependencies = {
|
||||
'@vue/api-plugin-unit-mocha-webpack': '^1.0.0'
|
||||
'vue-test-utils': '^1.0.0'
|
||||
}
|
||||
if (options.mochaAssertionLibrary === 'chai') {
|
||||
dependencies.chai = '*'
|
||||
} else if (options.mochaAssertionLibrary === 'expect') {
|
||||
dependencies.expect = '*'
|
||||
}
|
||||
api.injectDependencies(dependencies)
|
||||
|
||||
api.injectScripts({
|
||||
test: 'vue-api-service test'
|
||||
})
|
||||
|
||||
api.injectFileMiddleware(files => {
|
||||
// add dummy test
|
||||
files['test/unit/Hello.spec.js'] = api.renderFile('./files/Hello.spec.js')
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -1,11 +1,11 @@
|
||||
import { shallow } from 'vue-test-utils'
|
||||
import Hello from '@/components/Hello.vue'
|
||||
{{#if_eq mochaAssertionLibrary 'expect'}}
|
||||
<% if (assertionLibrary === 'expect') { %>
|
||||
import { expect } from 'expect'
|
||||
{{/if_eq}}
|
||||
{{#if_eq mochaAssertionLibrary 'chai'}}
|
||||
<% } %>
|
||||
<% if (assertionLibrary === 'chai') { %>
|
||||
import { expect } from 'chai'
|
||||
{{/if_eq}}
|
||||
<% } %>
|
||||
|
||||
describe('Hello.vue', () => {
|
||||
it('renders props.msg when passed', () => {
|
||||
@@ -13,14 +13,14 @@ describe('Hello.vue', () => {
|
||||
const wrapper = shallow(Hello, {
|
||||
context: { props: { msg } }
|
||||
})
|
||||
{{#if_eq mochaAssertionLibrary 'expect'}}
|
||||
<% if (assertionLibrary === 'expect' || unit === 'jest') { %>
|
||||
expect(wrapper.text()).toBe(msg)
|
||||
{{/if_eq}}
|
||||
{{#if_eq mochaAssertionLibrary 'chai'}}
|
||||
<% } %>
|
||||
<% if (assertionLibrary === 'chai') { %>
|
||||
expect(wrapper.text()).to.equal(msg)
|
||||
{{/if_eq}}
|
||||
{{#if_eq mochaAssertionLibrary 'custom'}}
|
||||
<% } %>
|
||||
<% if (assertionLibrary === 'custom') { %>
|
||||
// assert wrapper.text() equals msg
|
||||
{{/if_eq}}
|
||||
<% } %>
|
||||
})
|
||||
})
|
||||
58
packages/@vue/cli/lib/generators/unit/index.js
Normal file
58
packages/@vue/cli/lib/generators/unit/index.js
Normal file
@@ -0,0 +1,58 @@
|
||||
module.exports = api => {
|
||||
api.injectFeature({
|
||||
name: 'Unit Testing',
|
||||
value: 'unit',
|
||||
short: 'Unit'
|
||||
})
|
||||
|
||||
api.injectPrompt({
|
||||
name: 'unit',
|
||||
when: options => options.features.includes('unit'),
|
||||
type: 'list',
|
||||
message: 'Pick a unit testing solution:',
|
||||
choices: [
|
||||
{
|
||||
name: 'Mocha (with better webpack integration, https://mochajs.org/)',
|
||||
value: 'mocha',
|
||||
short: 'Mocha'
|
||||
},
|
||||
{
|
||||
name: 'Jest (https://facebook.github.io/jest/)',
|
||||
value: 'jest',
|
||||
short: 'Jest'
|
||||
}
|
||||
]
|
||||
})
|
||||
|
||||
api.injectPrompt({
|
||||
name: 'assertionLibrary',
|
||||
message: 'Pick an assertion library for unit tests:',
|
||||
when: options => options.unit === 'mocha',
|
||||
type: 'list',
|
||||
choices: [
|
||||
{
|
||||
name: 'Chai (http://chaijs.com/)',
|
||||
value: 'chai',
|
||||
short: 'Chai'
|
||||
},
|
||||
{
|
||||
name: 'Expect (https://facebook.github.io/jest/docs/en/expect.html)',
|
||||
value: 'expect',
|
||||
short: 'Expect'
|
||||
},
|
||||
{
|
||||
name: `I'll pick my own`,
|
||||
value: 'custom',
|
||||
short: 'Custom'
|
||||
}
|
||||
]
|
||||
})
|
||||
|
||||
api.onPromptComplete(options => {
|
||||
if (options.unit === 'mocha') {
|
||||
require('./mocha-webpack')(api, options)
|
||||
} else if (options.unit === 'jest') {
|
||||
require('./jest')(api, options)
|
||||
}
|
||||
})
|
||||
}
|
||||
42
packages/@vue/cli/lib/generators/unit/jest.js
Normal file
42
packages/@vue/cli/lib/generators/unit/jest.js
Normal file
@@ -0,0 +1,42 @@
|
||||
module.exports = (api, options) => {
|
||||
api.injectDevDeps({
|
||||
'@vue/cli-plugin-unit-jest': '^1.0.0',
|
||||
"jest": "^22.0.4",
|
||||
'vue-test-utils': '^1.0.0'
|
||||
})
|
||||
|
||||
api.injectScripts({
|
||||
test: 'jest'
|
||||
})
|
||||
|
||||
api.injectPackageFields({
|
||||
"jest": {
|
||||
"moduleFileExtensions": [
|
||||
"js",
|
||||
"json",
|
||||
// tell Jest to handle *.vue files
|
||||
"vue"
|
||||
],
|
||||
"transform": {
|
||||
// process js with babel-jest
|
||||
"^.+\\.js$": "<rootDir>/node_modules/@vue/cli-plugin-unit-jest/node_modules/babel-jest",
|
||||
// process *.vue files with vue-jest
|
||||
".*\\.(vue)$": "<rootDir>/node_modules/@vue/cli-plugin-unit-jest/node_modules/vue-jest"
|
||||
},
|
||||
// support the same @ -> src alias mapping in source code
|
||||
"moduleNameMapper": {
|
||||
"^@/(.*)$": "<rootDir>/src/$1"
|
||||
},
|
||||
// serializer for snapshots
|
||||
"snapshotSerializers": [
|
||||
"<rootDir>/node_modules/@vue/cli-plugin-unit-jest/node_modules/jest-serializer-vue"
|
||||
],
|
||||
"mapCoverage": true
|
||||
}
|
||||
})
|
||||
|
||||
api.injectFileMiddleware(files => {
|
||||
// add dummy test
|
||||
files['test/unit/Hello.spec.js'] = api.renderFile('Hello.spec.js')
|
||||
})
|
||||
}
|
||||
21
packages/@vue/cli/lib/generators/unit/mocha-webpack.js
Normal file
21
packages/@vue/cli/lib/generators/unit/mocha-webpack.js
Normal file
@@ -0,0 +1,21 @@
|
||||
module.exports = (api, options) => {
|
||||
const dependencies = {
|
||||
'@vue/cli-plugin-unit-mocha-webpack': '^1.0.0',
|
||||
'vue-test-utils': '^1.0.0'
|
||||
}
|
||||
if (options.assertionLibrary === 'chai') {
|
||||
dependencies.chai = '^4.1.2'
|
||||
} else if (options.assertionLibrary === 'expect') {
|
||||
dependencies.expect = '^22.0.3'
|
||||
}
|
||||
api.injectDevDeps(dependencies)
|
||||
|
||||
api.injectScripts({
|
||||
test: 'vue-cli-service test'
|
||||
})
|
||||
|
||||
api.injectFileMiddleware(files => {
|
||||
// add dummy test
|
||||
files['test/unit/Hello.spec.js'] = api.renderFile('Hello.spec.js')
|
||||
})
|
||||
}
|
||||
3
packages/@vue/cli/lib/generators/vuex/index.js
Normal file
3
packages/@vue/cli/lib/generators/vuex/index.js
Normal file
@@ -0,0 +1,3 @@
|
||||
module.exports = api => {
|
||||
|
||||
}
|
||||
@@ -1,15 +1,21 @@
|
||||
const chalk = require('chalk')
|
||||
|
||||
exports.info = msg => {
|
||||
console.log(msg)
|
||||
console.log(chalk.gray(`\n ${msg}\n`))
|
||||
}
|
||||
|
||||
exports.success = msg => {
|
||||
console.log(msg)
|
||||
console.log(chalk.green(`\n ${msg}\n`))
|
||||
}
|
||||
|
||||
exports.warn = msg => {
|
||||
console.warn(msg)
|
||||
console.warn(chalk.yellow(`\n ${msg}\n`))
|
||||
}
|
||||
|
||||
exports.error = msg => {
|
||||
console.error(msg)
|
||||
console.error(`\n ${chalk.bgRed(' ERROR ')} ${chalk.red(msg)}\n`)
|
||||
if (msg instanceof Error) {
|
||||
console.log(msg.stack)
|
||||
}
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
@@ -45,7 +45,8 @@ inquirer.prompt([
|
||||
},
|
||||
{
|
||||
name: 'Linter',
|
||||
value: 'linter'
|
||||
value: 'linter',
|
||||
short: 'Linter'
|
||||
},
|
||||
{
|
||||
name: 'Unit Testing',
|
||||
@@ -66,7 +67,7 @@ inquirer.prompt([
|
||||
message: 'Pick a linter solution:',
|
||||
choices: [
|
||||
{
|
||||
name: 'ESLint w/ only error-prevention rules & configure the rest myself',
|
||||
name: 'ESLint w/ only error-prevention rules',
|
||||
value: 'eslint-only',
|
||||
short: 'ESLint only'
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user