Skip to content

Commit a923afb

Browse files
committed
feat: core
1 parent 7c0610b commit a923afb

File tree

16 files changed

+206
-45
lines changed

16 files changed

+206
-45
lines changed

packages/@vue/cli/lib/Creator.js

+13-8
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
const fs = require('fs')
22
const os = require('os')
3+
const ejs = require('ejs')
34
const path = require('path')
45
const inquirer = require('inquirer')
56
const { warn } = require('./util/log')
@@ -20,6 +21,7 @@ const defaultOptions = {
2021

2122
module.exports = class Creator {
2223
constructor (name, generators) {
24+
this.name = name
2325
const { modePrompt, featurePrompt } = this.resolveIntroPrompts()
2426
this.modePrompt = modePrompt
2527
this.featurePrompt = featurePrompt
@@ -28,6 +30,7 @@ module.exports = class Creator {
2830
this.promptCompleteCbs = []
2931
this.fileMiddlewares = []
3032

33+
this.options = {}
3134
this.pkg = {
3235
name,
3336
version: '0.1.0',
@@ -41,8 +44,8 @@ module.exports = class Creator {
4144
// virtual file tree
4245
this.files = {}
4346

44-
generators.forEach(generator => {
45-
generator.module(new GeneratorAPI(this, generator))
47+
generators.forEach(({ id, apply }) => {
48+
apply(new GeneratorAPI(id, this))
4649
})
4750
}
4851

@@ -56,10 +59,12 @@ module.exports = class Creator {
5659
} else if (options.mode === 'default') {
5760
options = defaultOptions
5861
}
62+
options.projectName = this.name
5963
options.features = options.features || []
6064

6165
// run cb registered by generators
6266
this.promptCompleteCbs.forEach(cb => cb(options))
67+
this.options = options
6368
debug('options')(options)
6469
// save options
6570
if (options.mode === 'manual' && options.save) {
@@ -82,18 +87,18 @@ module.exports = class Creator {
8287
message: `Pick a project creation mode:`,
8388
choices: [
8489
{
85-
name: 'Zero-configuration with defaults (Babel, ESLint, Unit Tests w/ Mocha)',
90+
name: 'Zero-configuration with defaults',
8691
value: 'default'
8792
},
8893
{
89-
name: 'Manually select features (advanced)',
94+
name: 'Manually select features',
9095
value: 'manual'
9196
}
9297
]
9398
}
9499
if (fs.existsSync(rcPath)) {
95100
modePrompt.choices.unshift({
96-
name: 'Using saved preferences',
101+
name: 'Use previously saved preferences',
97102
value: 'saved'
98103
})
99104
}
@@ -120,7 +125,7 @@ module.exports = class Creator {
120125
}
121126
]
122127
if (hasYarn()) {
123-
outroPrompts.push({
128+
outroPrompts.unshift({
124129
name: 'packageManager',
125130
when: isMode('manual'),
126131
type: 'list',
@@ -144,7 +149,7 @@ module.exports = class Creator {
144149
]
145150
})
146151
} else {
147-
outroPrompts.push({
152+
outroPrompts.unshift({
148153
name: 'packageManager',
149154
when: isMode('manual'),
150155
type: 'confirm',
@@ -209,7 +214,7 @@ module.exports = class Creator {
209214

210215
async resolveFiles () {
211216
for (const middleware of this.fileMiddlewares) {
212-
await middleware(this.files)
217+
await middleware(this.files, ejs.render)
213218
}
214219
debug('files')(this.files)
215220
}

packages/@vue/cli/lib/Generator.js

-9
This file was deleted.

packages/@vue/cli/lib/GeneratorAPI.js

+47-9
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,19 @@
1+
const fs = require('fs')
2+
const ejs = require('ejs')
3+
const path = require('path')
4+
const walk = require('klaw-sync')
15
const { error } = require('./util/log')
26
const mergeDeps = require('./util/mergeDeps')
3-
const isObject = val => val && typeof val === 'object'
7+
const errorParser = require('error-stack-parser')
8+
9+
const isString = val => typeof val === 'string'
410
const isFunction = val => typeof val === 'function'
11+
const isObject = val => val && typeof val === 'object'
512

613
module.exports = class GeneratorAPI {
7-
constructor (creator, generator) {
14+
constructor (id, creator) {
15+
this.id = id
816
this.creator = creator
9-
this.generator = generator
1017
}
1118

1219
injectFeature (feature) {
@@ -24,7 +31,7 @@ module.exports = class GeneratorAPI {
2431
if (!prompt) {
2532
error(
2633
`injectOptionForFeature error in generator "${
27-
this.generator.id
34+
this.id
2835
}": prompt "${name}" does not exist.`
2936
)
3037
}
@@ -58,7 +65,7 @@ module.exports = class GeneratorAPI {
5865
if (key === 'dependencies' || key === 'devDependencies') {
5966
// use special version resolution merge
6067
pkg[key] = mergeDeps(
61-
this.generator.id,
68+
this.id,
6269
existing,
6370
value,
6471
this.creator.depSources
@@ -73,9 +80,40 @@ module.exports = class GeneratorAPI {
7380
}
7481
}
7582

76-
renderFile (file, additionalData, ejsOptions) {
77-
// TODO render file based on generator path
78-
// render with ejs & options
79-
return file
83+
renderFiles (fileDir, additionalData = {}, ejsOptions = {}) {
84+
const baseDir = extractCallDir()
85+
if (isString(fileDir)) {
86+
fileDir = path.resolve(baseDir, fileDir)
87+
this.injectFileMiddleware(files => {
88+
const data = Object.assign({}, this.creator.options, additionalData)
89+
const _files = walk(fileDir, { nodir: true })
90+
for (const file of _files) {
91+
const relativePath = path.relative(fileDir, file.path)
92+
files[relativePath] = renderFile(file.path, data, ejsOptions)
93+
}
94+
})
95+
} else if (isObject(fileDir)) {
96+
this.injectFileMiddleware(files => {
97+
const data = Object.assign({}, this.creator.options, additionalData)
98+
for (const targetPath in fileDir) {
99+
const sourcePath = path.resolve(baseDir, fileDir[targetPath])
100+
files[targetPath] = renderFile(sourcePath, data, ejsOptions)
101+
}
102+
})
103+
} else if (isFunction(fileDir)) {
104+
this.injectFileMiddleware(fileDir)
105+
}
80106
}
81107
}
108+
109+
function extractCallDir () {
110+
// extract api.renderFiles() callsite file location using error stack
111+
const obj = {}
112+
Error.captureStackTrace(obj)
113+
const stack = errorParser.parse(obj)
114+
return path.dirname(stack[2].fileName)
115+
}
116+
117+
function renderFile (name, data, ejsOptions) {
118+
return ejs.render(fs.readFileSync(name, 'utf-8'), data, ejsOptions)
119+
}

packages/@vue/cli/lib/create.js

+7-3
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ const path = require('path')
33
const program = require('commander')
44
const Creator = require('./Creator')
55
const debug = require('debug')('create')
6-
const Generator = require('./Generator')
76
const { warn, error } = require('./util/log')
87
const resolveInstalledGenerators = require('./util/resolveInstalledGenerators')
98

@@ -18,15 +17,20 @@ if (!projectName) {
1817
process.exit(1)
1918
}
2019

20+
const createGenerator = (id, requirePath = id) => ({
21+
id,
22+
apply: require(requirePath)
23+
})
24+
2125
const builtInGenerators = fs
2226
.readdirSync(path.resolve(__dirname, './generators'))
2327
.filter(dir => dir.charAt(0) !== '.')
24-
.map(id => new Generator(id, `./generators/${id}`))
28+
.map(id => createGenerator(id, `./generators/${id}`))
2529

2630
debug(builtInGenerators)
2731

2832
const installedGenerators = resolveInstalledGenerators().map(id => {
29-
return new Generator(id)
33+
return createGenerator(id)
3034
})
3135

3236
const targetDir = path.resolve(process.cwd(), projectName)

packages/@vue/cli/lib/generators/babel/index.js

+1-3
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,7 @@ module.exports = api => {
77
'babel-preset-vue-app': '^2.0.0'
88
}
99
})
10-
api.injectFileMiddleware(files => {
11-
files['.babelrc'] = api.renderFile('.babelrc')
12-
})
10+
api.renderFiles('./files')
1311
}
1412
})
1513
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<!DOCTYPE html>
2+
<html>
3+
<head>
4+
<meta charset="utf-8">
5+
<meta name="viewport" content="width=device-width,initial-scale=1.0">
6+
<title><%= projectName %></title>
7+
</head>
8+
<body>
9+
<div id="app"></div>
10+
<!-- built files will be auto injected -->
11+
</body>
12+
</html>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
<template>
2+
<div id="app">
3+
<img src="./assets/logo.png">
4+
<HelloWorld msg="Welcome to Your Vue.js App"/>
5+
</div>
6+
</template>
7+
8+
<script>
9+
import HelloWorld from './components/HelloWorld.vue'
10+
11+
export default {
12+
name: 'app',
13+
components: {
14+
HelloWorld
15+
}
16+
}
17+
</script>
18+
19+
<style>
20+
#app {
21+
font-family: 'Avenir', Helvetica, Arial, sans-serif;
22+
-webkit-font-smoothing: antialiased;
23+
-moz-osx-font-smoothing: grayscale;
24+
text-align: center;
25+
color: #2c3e50;
26+
margin-top: 60px;
27+
}
28+
</style>
Loading
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
<template>
2+
<div class="hello">
3+
<h1>{{ msg }}</h1>
4+
<h2>Essential Links</h2>
5+
<ul>
6+
<li><a href="https://vuejs.org" target="_blank">Core Docs</a></li>
7+
<li><a href="https://forum.vuejs.org" target="_blank">Forum</a></li>
8+
<li><a href="https://chat.vuejs.org" target="_blank">Community Chat</a></li>
9+
<li><a href="https://twitter.com/vuejs" target="_blank">Twitter</a></li>
10+
<br>
11+
<li><a href="http://vuejs-templates.github.io/webpack/" target="_blank">Docs for This Template</a></li>
12+
</ul>
13+
<h2>Ecosystem</h2>
14+
<ul>
15+
<li><a href="http://router.vuejs.org/" target="_blank">vue-router</a></li>
16+
<li><a href="http://vuex.vuejs.org/" target="_blank">vuex</a></li>
17+
<li><a href="http://vue-loader.vuejs.org/" target="_blank">vue-loader</a></li>
18+
<li><a href="https://github.com/vuejs/awesome-vue" target="_blank">awesome-vue</a></li>
19+
</ul>
20+
</div>
21+
</template>
22+
23+
<script>
24+
export default {
25+
name: 'HelloWorld',
26+
props: {
27+
msg: String
28+
}
29+
}
30+
</script>
31+
32+
<!-- Add "scoped" attribute to limit CSS to this component only -->
33+
<style scoped>
34+
h1, h2 {
35+
font-weight: normal;
36+
}
37+
ul {
38+
list-style-type: none;
39+
padding: 0;
40+
}
41+
li {
42+
display: inline-block;
43+
margin: 0 10px;
44+
}
45+
a {
46+
color: #42b983;
47+
}
48+
</style>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import Vue from 'vue'
2+
import App from './App.vue'
3+
4+
new Vue({
5+
el: '#app',
6+
render: h => h(App)
7+
})
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,16 @@
11
module.exports = api => {
2-
2+
api.renderFiles('./files')
3+
api.extendPackage({
4+
scripts: {
5+
'dev': 'vue-cli-service serve',
6+
'build': 'vue-cli-service build',
7+
'start': 'vue-cli-service serve --prod'
8+
},
9+
dependencies: {
10+
'vue': '^2.5.13'
11+
},
12+
devDependencies: {
13+
'@vue/cli-service': '^1.0.0'
14+
}
15+
})
316
}

packages/@vue/cli/lib/generators/unit/files/Hello.spec.js packages/@vue/cli/lib/generators/unit/files/test/unit/HelloWorld.spec.js

+3-3
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,15 @@ import { expect } from 'expect'
55
<%_ if (assertionLibrary === 'chai') { _%>
66
import { expect } from 'chai'
77
<%_ } _%>
8-
import Hello from '@/components/Hello.vue'
8+
import HelloWorld from '@/components/HelloWorld.vue'
99

1010
describe('Hello.vue', () => {
1111
it('renders props.msg when passed', () => {
1212
const msg = 'new message'
13-
const wrapper = shallow(Hello, {
13+
const wrapper = shallow(HelloWorld, {
1414
context: { props: { msg } }
1515
})
16-
<%_ if (assertionLibrary === 'expect' || unit === 'jest') { _%>
16+
<%_ if (assertionLibrary === 'expect') { _%>
1717
expect(wrapper.text()).toBe(msg)
1818
<%_ } _%>
1919
<%_ if (assertionLibrary === 'chai') { _%>

packages/@vue/cli/lib/generators/unit/jest.js

+2-5
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
module.exports = (api, options) => {
2+
options.assertionLibrary = 'expect'
3+
api.renderFiles('./files')
24
api.extendPackage({
35
scripts: {
46
test: 'jest'
@@ -32,9 +34,4 @@ module.exports = (api, options) => {
3234
'mapCoverage': true
3335
}
3436
})
35-
36-
api.injectFileMiddleware(files => {
37-
// add dummy test
38-
files['test/unit/Hello.spec.js'] = api.renderFile('Hello.spec.js')
39-
})
4037
}

packages/@vue/cli/lib/generators/unit/mocha-webpack.js

+1-4
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,5 @@ module.exports = (api, options) => {
1616
}
1717
})
1818

19-
api.injectFileMiddleware(files => {
20-
// add dummy test
21-
files['test/unit/Hello.spec.js'] = api.renderFile('Hello.spec.js')
22-
})
19+
api.renderFiles('./files')
2320
}

0 commit comments

Comments
 (0)