mirror of
https://github.com/vuejs/vue-cli.git
synced 2026-01-14 03:11:19 -06:00
wip
This commit is contained in:
@@ -3,7 +3,8 @@
|
||||
"workspaces": [
|
||||
"packages/@vue/*"
|
||||
],
|
||||
"devDependencies": {
|
||||
"lerna": "^2.5.1"
|
||||
}
|
||||
"devDependencies": {
|
||||
"debug": "^3.1.0",
|
||||
"lerna": "^2.5.1"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ const { execSync } = require('child_process')
|
||||
const GeneratorAPI = require('./GeneratorAPI')
|
||||
const writeFileTree = require('./util/writeFileTree')
|
||||
|
||||
const debug = require('debug')
|
||||
const rcPath = path.join(os.homedir(), '.vuerc')
|
||||
const isMode = _mode => ({ mode }) => _mode === mode
|
||||
|
||||
@@ -19,21 +20,27 @@ const defaultOptions = {
|
||||
|
||||
module.exports = class Creator {
|
||||
constructor (name, generators) {
|
||||
this.name = name
|
||||
const { modePrompt, featurePrompt } = this.resolveIntroPrompts()
|
||||
this.modePrompt = modePrompt
|
||||
this.featurePrompt = featurePrompt
|
||||
this.outroPrompts = this.resolveOutroPrompts()
|
||||
this.injectedPrompts = []
|
||||
this.deps = {}
|
||||
this.devDeps = {}
|
||||
this.scripts = {}
|
||||
this.packageFields = {}
|
||||
this.files = {}
|
||||
this.postCreateMessages = []
|
||||
this.promptCompleteCbs = []
|
||||
this.fileMiddlewares = []
|
||||
|
||||
this.pkg = {
|
||||
name,
|
||||
version: '0.1.0',
|
||||
private: true,
|
||||
scripts: {},
|
||||
dependencies: {},
|
||||
devDependencies: {}
|
||||
}
|
||||
// for conflict resolution
|
||||
this.depSources = {}
|
||||
// virtual file tree
|
||||
this.files = {}
|
||||
|
||||
generators.forEach(generator => {
|
||||
generator.module(new GeneratorAPI(this, generator))
|
||||
})
|
||||
@@ -42,29 +49,28 @@ module.exports = class Creator {
|
||||
async create (path) {
|
||||
// prompt
|
||||
let options = await inquirer.prompt(this.resolveFinalPrompts())
|
||||
let needSave = false
|
||||
debug('rawOptions')(options)
|
||||
|
||||
if (options.mode === 'saved') {
|
||||
options = this.loadSavedOptions()
|
||||
} else if (options.mode === 'default') {
|
||||
options = defaultOptions
|
||||
} else if (options.save) {
|
||||
needSave = true
|
||||
}
|
||||
options.features = options.features || []
|
||||
|
||||
// run cbs (register generators)
|
||||
// run cb registered by generators
|
||||
this.promptCompleteCbs.forEach(cb => cb(options))
|
||||
|
||||
// save after prompt complete cbs are run, since generators may modify
|
||||
// options in the callback
|
||||
if (needSave) {
|
||||
debug('options')(options)
|
||||
// save options
|
||||
if (options.mode === 'manual' && options.save) {
|
||||
this.saveOptions(options)
|
||||
}
|
||||
|
||||
// resolve deps, scripts and generate final package.json
|
||||
this.resolvePackage()
|
||||
// wait for file resolve
|
||||
await this.resolveFiles()
|
||||
// set package.json
|
||||
this.resolvePkg()
|
||||
this.files['package.json'] = JSON.stringify(this.pkg, null ,2)
|
||||
// write file tree to disk
|
||||
await writeFileTree(path, this.files)
|
||||
}
|
||||
@@ -156,14 +162,14 @@ module.exports = class Creator {
|
||||
return options.mode === 'manual' && originalWhen(options)
|
||||
}
|
||||
})
|
||||
const ret = [].concat(
|
||||
const prompts = [].concat(
|
||||
this.modePrompt,
|
||||
this.featurePrompt,
|
||||
this.injectedPrompts,
|
||||
this.outroPrompts
|
||||
)
|
||||
console.log(ret)
|
||||
return ret
|
||||
debug('prompts')(prompts)
|
||||
return prompts
|
||||
}
|
||||
|
||||
loadSavedOptions () {
|
||||
@@ -191,45 +197,21 @@ module.exports = class Creator {
|
||||
}
|
||||
}
|
||||
|
||||
resolvePackage () {
|
||||
const { dependencies, devDependencies } = this.resolveDeps()
|
||||
const scripts = this.resolveScripts()
|
||||
const additionalFields = this.resolvePackageFields()
|
||||
const pkg = Object.assign({}, additionalFields, {
|
||||
name: this.name,
|
||||
version: '0.1.0',
|
||||
private: true,
|
||||
scripts,
|
||||
dependencies,
|
||||
devDependencies
|
||||
})
|
||||
this.files['package.json'] = JSON.stringify(pkg, null, 2)
|
||||
}
|
||||
|
||||
// TODO
|
||||
resolveDeps () {
|
||||
const dependencies = this.deps
|
||||
const devDependencies = this.devDeps
|
||||
return {
|
||||
dependencies,
|
||||
devDependencies
|
||||
}
|
||||
}
|
||||
|
||||
// TODO
|
||||
resolveScripts () {
|
||||
return this.scripts
|
||||
}
|
||||
|
||||
// TODO
|
||||
resolvePackageFields () {
|
||||
return this.packageFields
|
||||
resolvePkg () {
|
||||
const sortDeps = deps => Object.keys(deps).sort().reduce((res, name) => {
|
||||
res[name] = deps[name]
|
||||
return res
|
||||
}, {})
|
||||
this.pkg.dependencies = sortDeps(this.pkg.dependencies)
|
||||
this.pkg.devDependencies = sortDeps(this.pkg.devDependencies)
|
||||
debug('pkg')(this.pkg)
|
||||
}
|
||||
|
||||
async resolveFiles () {
|
||||
for (const middleware of this.fileMiddlewares) {
|
||||
await middleware(this.files)
|
||||
}
|
||||
debug('files')(this.files)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
const { error } = require('./util/log')
|
||||
const mergeDeps = require('./util/mergeDeps')
|
||||
const isObject = val => val && typeof val === 'object'
|
||||
const isFunction = val => typeof val === 'function'
|
||||
|
||||
module.exports = class GeneratorAPI {
|
||||
constructor (creator, generator) {
|
||||
@@ -32,31 +35,47 @@ module.exports = class GeneratorAPI {
|
||||
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)
|
||||
onCreateComplete (msg) {
|
||||
this.creator.onCreateCompleteCbs.push(cb)
|
||||
}
|
||||
|
||||
injectFileMiddleware (middleware) {
|
||||
this.creator.fileMiddlewares.push(middleware)
|
||||
}
|
||||
|
||||
renderFile (file) {
|
||||
return file
|
||||
extendPackage (fields, options = { merge: true }) {
|
||||
const pkg = this.creator.pkg
|
||||
const toMerge = isFunction(fields) ? fields(pkg) : fields
|
||||
for (const key in toMerge) {
|
||||
if (!options.merge || !(key in pkg)) {
|
||||
pkg[key] = toMerge[key]
|
||||
} else {
|
||||
const value = toMerge[key]
|
||||
const existing = pkg[key]
|
||||
if (Array.isArray(value) && Array.isArray(existing)) {
|
||||
pkg[key] = existing.concat(value)
|
||||
} else if (isObject(value) && isObject(existing)) {
|
||||
if (key === 'dependencies' || key === 'devDependencies') {
|
||||
// use special version resolution merge
|
||||
pkg[key] = mergeDeps(
|
||||
this.generator.id,
|
||||
existing,
|
||||
value,
|
||||
this.creator.depSources
|
||||
)
|
||||
} else {
|
||||
pkg[key] = Object.assign({}, existing, value)
|
||||
}
|
||||
} else {
|
||||
pkg[key] = value
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onCreateComplete (msg) {
|
||||
this.creator.onCreateCompleteCbs.push(cb)
|
||||
renderFile (file, additionalData, ejsOptions) {
|
||||
// TODO render file based on generator path
|
||||
// render with ejs & options
|
||||
return file
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ const fs = require('fs')
|
||||
const path = require('path')
|
||||
const program = require('commander')
|
||||
const Creator = require('./Creator')
|
||||
const debug = require('debug')('create')
|
||||
const Generator = require('./Generator')
|
||||
const { warn, error } = require('./util/log')
|
||||
const resolveInstalledGenerators = require('./util/resolveInstalledGenerators')
|
||||
@@ -21,6 +22,8 @@ const builtInGenerators = fs
|
||||
.readdirSync(path.resolve(__dirname, './generators'))
|
||||
.map(id => new Generator(id, `./generators/${id}`))
|
||||
|
||||
debug(builtInGenerators)
|
||||
|
||||
const installedGenerators = resolveInstalledGenerators().map(id => {
|
||||
return new Generator(id)
|
||||
})
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
module.exports = api => {
|
||||
api.onPromptComplete(options => {
|
||||
if (!options.features.includes('ts')) {
|
||||
api.injectDevDeps({
|
||||
'@vue/cli-plugin-babel': '^1.0.0',
|
||||
'babel-preset-vue-app': '^2.0.0'
|
||||
api.extendPackage({
|
||||
devDependencies: {
|
||||
'@vue/cli-plugin-babel': '^1.0.0',
|
||||
'babel-preset-vue-app': '^2.0.0'
|
||||
}
|
||||
})
|
||||
api.injectFileMiddleware(files => {
|
||||
files['.babelrc'] = api.renderFile('.babelrc')
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { shallow } from 'vue-test-utils'
|
||||
import Hello from '@/components/Hello.vue'
|
||||
<% if (assertionLibrary === 'expect') { %>
|
||||
<%_ if (assertionLibrary === 'expect') { _%>
|
||||
import { expect } from 'expect'
|
||||
<% } %>
|
||||
<% if (assertionLibrary === 'chai') { %>
|
||||
<%_ } _%>
|
||||
<%_ if (assertionLibrary === 'chai') { _%>
|
||||
import { expect } from 'chai'
|
||||
<% } %>
|
||||
<%_ } _%>
|
||||
import Hello from '@/components/Hello.vue'
|
||||
|
||||
describe('Hello.vue', () => {
|
||||
it('renders props.msg when passed', () => {
|
||||
@@ -13,14 +13,14 @@ describe('Hello.vue', () => {
|
||||
const wrapper = shallow(Hello, {
|
||||
context: { props: { msg } }
|
||||
})
|
||||
<% if (assertionLibrary === 'expect' || unit === 'jest') { %>
|
||||
<%_ if (assertionLibrary === 'expect' || unit === 'jest') { _%>
|
||||
expect(wrapper.text()).toBe(msg)
|
||||
<% } %>
|
||||
<% if (assertionLibrary === 'chai') { %>
|
||||
<%_ } _%>
|
||||
<%_ if (assertionLibrary === 'chai') { _%>
|
||||
expect(wrapper.text()).to.equal(msg)
|
||||
<% } %>
|
||||
<% if (assertionLibrary === 'custom') { %>
|
||||
<%_ } _%>
|
||||
<%_ if (assertionLibrary === 'custom') { _%>
|
||||
// assert wrapper.text() equals msg
|
||||
<% } %>
|
||||
<%_ } _%>
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,15 +1,13 @@
|
||||
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({
|
||||
api.extendPackage({
|
||||
scripts: {
|
||||
test: 'jest'
|
||||
},
|
||||
devDependencies: {
|
||||
'@vue/cli-plugin-unit-jest': '^1.0.0',
|
||||
"jest": "^22.0.4",
|
||||
'vue-test-utils': '^1.0.0'
|
||||
},
|
||||
"jest": {
|
||||
"moduleFileExtensions": [
|
||||
"js",
|
||||
|
||||
@@ -1,17 +1,19 @@
|
||||
module.exports = (api, options) => {
|
||||
const dependencies = {
|
||||
const devDependencies = {
|
||||
'@vue/cli-plugin-unit-mocha-webpack': '^1.0.0',
|
||||
'vue-test-utils': '^1.0.0'
|
||||
}
|
||||
if (options.assertionLibrary === 'chai') {
|
||||
dependencies.chai = '^4.1.2'
|
||||
devDependencies.chai = '^4.1.2'
|
||||
} else if (options.assertionLibrary === 'expect') {
|
||||
dependencies.expect = '^22.0.3'
|
||||
devDependencies.expect = '^22.0.3'
|
||||
}
|
||||
api.injectDevDeps(dependencies)
|
||||
|
||||
api.injectScripts({
|
||||
test: 'vue-cli-service test'
|
||||
api.extendPackage({
|
||||
devDependencies,
|
||||
scripts: {
|
||||
test: 'vue-cli-service test'
|
||||
}
|
||||
})
|
||||
|
||||
api.injectFileMiddleware(files => {
|
||||
|
||||
@@ -1,21 +1,25 @@
|
||||
const chalk = require('chalk')
|
||||
|
||||
exports.info = msg => {
|
||||
console.log(chalk.gray(`\n ${msg}\n`))
|
||||
const format = (label, msg) => {
|
||||
return msg.split('\n').map((line, i) => {
|
||||
return i === 0
|
||||
? `\n ${label} ${line}`
|
||||
: ` ${line}`
|
||||
}).join('\n') + '\n'
|
||||
}
|
||||
|
||||
exports.success = msg => {
|
||||
console.log(chalk.green(`\n ${msg}\n`))
|
||||
console.log(format(chalk.bgGreen(' OK '), chalk.green(msg)))
|
||||
}
|
||||
|
||||
exports.warn = msg => {
|
||||
console.warn(chalk.yellow(`\n ${msg}\n`))
|
||||
console.warn(format(chalk.bgYellow(chalk.black(' WARN ')), chalk.yellow(msg)))
|
||||
}
|
||||
|
||||
exports.error = msg => {
|
||||
console.error(`\n ${chalk.bgRed(' ERROR ')} ${chalk.red(msg)}\n`)
|
||||
console.error(format(chalk.bgRed(' ERROR '), chalk.red(msg)))
|
||||
if (msg instanceof Error) {
|
||||
console.log(msg.stack)
|
||||
console.error(msg.stack)
|
||||
}
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
54
packages/@vue/cli/lib/util/mergeDeps.js
Normal file
54
packages/@vue/cli/lib/util/mergeDeps.js
Normal file
@@ -0,0 +1,54 @@
|
||||
const semver = require('semver')
|
||||
const { warn } = require('./log')
|
||||
|
||||
module.exports = function resolveDeps (generatorId, to, from, sources) {
|
||||
const res = Object.assign({}, to)
|
||||
for (const name in from) {
|
||||
const r1 = to[name]
|
||||
const r2 = from[name]
|
||||
const sourceGeneratorId = sources[name]
|
||||
|
||||
if (!semver.validRange(r2)) {
|
||||
warn(
|
||||
`invalid version range for dependency "${name}":\n\n` +
|
||||
`- ${r2} injected by generator "${generatorId}"`
|
||||
)
|
||||
continue
|
||||
}
|
||||
|
||||
if (!r1) {
|
||||
res[name] = r2
|
||||
} else {
|
||||
const r = tryGetNewerRange(r1, r2)
|
||||
const didGetNewer = !!r
|
||||
// if failed to infer newer version, use existing one because it's likely
|
||||
// built-in
|
||||
res[name] = didGetNewer ? r : r1
|
||||
// if changed, update source
|
||||
if (res[name] === r2) {
|
||||
sources[name] = generatorId
|
||||
}
|
||||
// warn incompatible version requirements
|
||||
if (!semver.intersects(r1, r2)) {
|
||||
warn(
|
||||
`conflicting versions for project dependency "${name}":\n\n` +
|
||||
`- ${r1} injected by generator "${sourceGeneratorId}"\n` +
|
||||
`- ${r2} injected by generator "${generatorId}"\n\n` +
|
||||
`Using ${didGetNewer ? `newer ` : ``}version (${res[name]}), but this may cause build errors.`
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
const leadRE = /^(~|\^|>=?)/
|
||||
const rangeToVersion = r => r.replace(leadRE, '').replace(/x/g, '0')
|
||||
|
||||
function tryGetNewerRange (r1, r2) {
|
||||
const v1 = rangeToVersion(r1)
|
||||
const v2 = rangeToVersion(r2)
|
||||
if (semver.valid(v1) && semver.valid(v2)) {
|
||||
return semver.gt(v1, v2) ? r1 : r2
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,2 @@
|
||||
module.exports = function writeFileTree (dir, files) {
|
||||
console.log(files)
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
"dependencies": {
|
||||
"chalk": "^2.3.0",
|
||||
"commander": "^2.12.2",
|
||||
"inquirer": "^4.0.1"
|
||||
"inquirer": "^4.0.1",
|
||||
"semver": "^5.4.1"
|
||||
}
|
||||
}
|
||||
|
||||
10
yarn.lock
10
yarn.lock
@@ -412,6 +412,12 @@ dateformat@^1.0.11, dateformat@^1.0.12:
|
||||
get-stdin "^4.0.1"
|
||||
meow "^3.3.0"
|
||||
|
||||
debug@^3.1.0:
|
||||
version "3.1.0"
|
||||
resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261"
|
||||
dependencies:
|
||||
ms "2.0.0"
|
||||
|
||||
decamelize@^1.0.0, decamelize@^1.1.1, decamelize@^1.1.2:
|
||||
version "1.2.0"
|
||||
resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290"
|
||||
@@ -1049,6 +1055,10 @@ moment@^2.6.0:
|
||||
version "2.20.1"
|
||||
resolved "https://registry.yarnpkg.com/moment/-/moment-2.20.1.tgz#d6eb1a46cbcc14a2b2f9434112c1ff8907f313fd"
|
||||
|
||||
ms@2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8"
|
||||
|
||||
mute-stream@0.0.7:
|
||||
version "0.0.7"
|
||||
resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.7.tgz#3075ce93bc21b8fab43e1bc4da7e8115ed1e7bab"
|
||||
|
||||
Reference in New Issue
Block a user