Skip to content

Commit 74f629e

Browse files
xierenyuanhaoqunjiang
authored andcommitted
feat(cli): Generator support async (#3897)
1 parent 42ecd08 commit 74f629e

File tree

7 files changed

+97
-21
lines changed

7 files changed

+97
-21
lines changed

packages/@vue/cli/__tests__/Generator.spec.js

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -256,7 +256,7 @@ test('api: extendPackage merge dependencies', async () => {
256256
})
257257

258258
test('api: warn invalid dep range', async () => {
259-
new Generator('/', { plugins: [
259+
const generator = new Generator('/', { plugins: [
260260
{
261261
id: 'test1',
262262
apply: api => {
@@ -269,6 +269,8 @@ test('api: warn invalid dep range', async () => {
269269
}
270270
] })
271271

272+
await generator.generate()
273+
272274
expect(logs.warn.some(([msg]) => {
273275
return (
274276
msg.match(/invalid version range for dependency "foo"/) &&
@@ -278,7 +280,7 @@ test('api: warn invalid dep range', async () => {
278280
})
279281

280282
test('api: extendPackage dependencies conflict', async () => {
281-
new Generator('/', { plugins: [
283+
const generator = new Generator('/', { plugins: [
282284
{
283285
id: 'test1',
284286
apply: api => {
@@ -301,6 +303,8 @@ test('api: extendPackage dependencies conflict', async () => {
301303
}
302304
] })
303305

306+
await generator.generate()
307+
304308
expect(logs.warn.some(([msg]) => {
305309
return (
306310
msg.match(/conflicting versions for project dependency "foo"/) &&
@@ -312,7 +316,7 @@ test('api: extendPackage dependencies conflict', async () => {
312316
})
313317

314318
test('api: extendPackage merge warn nonstrictly semver deps', async () => {
315-
new Generator('/', { plugins: [
319+
const generator = new Generator('/', { plugins: [
316320
{
317321
id: 'test3',
318322
apply: api => {
@@ -335,6 +339,8 @@ test('api: extendPackage merge warn nonstrictly semver deps', async () => {
335339
}
336340
] })
337341

342+
await generator.generate()
343+
338344
expect(logs.warn.some(([msg]) => {
339345
return (
340346
msg.match(/conflicting versions for project dependency "bar"/) &&
@@ -436,10 +442,10 @@ test('api: hasPlugin', () => {
436442
] })
437443
})
438444

439-
test('api: onCreateComplete', () => {
445+
test('api: onCreateComplete', async () => {
440446
const fn = () => {}
441447
const cbs = []
442-
new Generator('/', {
448+
const generator = new Generator('/', {
443449
plugins: [
444450
{
445451
id: 'test',
@@ -467,6 +473,9 @@ test('api: afterInvoke', () => {
467473
],
468474
afterInvokeCbs: cbs
469475
})
476+
477+
await generator.generate()
478+
470479
expect(cbs).toContain(fn)
471480
})
472481

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
const sleep = n => new Promise(resolve => setTimeout(resolve, n))
2+
3+
module.exports = async (api, options) => {
4+
api.render('./template', options)
5+
6+
// add asynchronous code test
7+
await sleep(1000)
8+
9+
api.extendPackage({
10+
scripts: {
11+
testasync: 'this is the test'
12+
},
13+
devDependencies: {
14+
'vue-cli-plugin-async-generator': 'v0.0.1'
15+
}
16+
})
17+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
<%= ok %>
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"plugins": {
3+
"@vue/cli-plugin-babel": {}
4+
}
5+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
module.exports = [{
2+
type: 'confirm',
3+
name: 'ok',
4+
message: 'Are you ok?'
5+
}]

packages/@vue/cli/__tests__/preset.spec.js

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,3 +56,32 @@ test('should recognize generator/index.js in a local preset directory', async ()
5656
const pkg = require(path.resolve(cwd, name, 'package.json'))
5757
expect(pkg.devDependencies).toHaveProperty('@vue/cli-plugin-babel')
5858
})
59+
60+
test('should recognize generator/index.js in a local preset directory by async generatory', async () => {
61+
const cwd = path.resolve(__dirname, '../../../test')
62+
const name = 'test-preset-template-async-generator'
63+
64+
expectPrompts([{
65+
message: 'Are you ok',
66+
confirm: true
67+
}])
68+
69+
await create(
70+
name,
71+
{
72+
force: true,
73+
git: false,
74+
cwd,
75+
preset: path.resolve(__dirname, './mock-preset-with-async-generator')
76+
}
77+
)
78+
79+
const testFile = await fs.readFile(path.resolve(cwd, name, 'test.js'), 'utf-8')
80+
expect(testFile).toBe('true\n')
81+
82+
const pkg = require(path.resolve(cwd, name, 'package.json'))
83+
expect(pkg.devDependencies).toHaveProperty('@vue/cli-plugin-babel')
84+
expect(pkg.devDependencies).toHaveProperty('vue-cli-plugin-async-generator')
85+
expect(pkg.scripts).toHaveProperty('testasync')
86+
})
87+

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

Lines changed: 26 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ module.exports = class Generator {
8383
this.pm = new PackageManager({ context })
8484
this.imports = {}
8585
this.rootOptions = {}
86-
this.afterInvokeCbs = []
86+
this.afterInvokeCbs = afterInvokeCbs
8787
this.afterAnyInvokeCbs = afterAnyInvokeCbs
8888
this.configTransforms = {}
8989
this.defaultConfigTransforms = defaultConfigTransforms
@@ -98,10 +98,8 @@ module.exports = class Generator {
9898
// exit messages
9999
this.exitLogs = []
100100

101-
const pluginIds = plugins.map(p => p.id)
102-
103101
// load all the other plugins
104-
this.allPlugins = Object.keys(this.pkg.dependencies || {})
102+
this.allPluginIds = Object.keys(this.pkg.dependencies || {})
105103
.concat(Object.keys(this.pkg.devDependencies || {}))
106104
.filter(isPlugin)
107105

@@ -110,43 +108,55 @@ module.exports = class Generator {
110108
? cliService.options
111109
: inferRootOptions(pkg)
112110

111+
this.rootOptions = rootOptions
112+
}
113+
114+
async initPlugins () {
115+
const { rootOptions, invoking } = this
116+
const pluginIds = this.plugins.map(p => p.id)
117+
113118
// apply hooks from all plugins
114-
this.allPlugins.forEach(id => {
119+
for (const id of this.allPluginIds) {
115120
const api = new GeneratorAPI(id, this, {}, rootOptions)
116-
const pluginGenerator = loadModule(`${id}/generator`, context)
121+
const pluginGenerator = loadModule(`${id}/generator`, this.context)
117122

118123
if (pluginGenerator && pluginGenerator.hooks) {
119-
pluginGenerator.hooks(api, {}, rootOptions, pluginIds)
124+
await pluginGenerator.hooks(api, {}, rootOptions, pluginIds)
120125
}
121-
})
126+
}
122127

123128
// We are doing save/load to make the hook order deterministic
124129
// save "any" hooks
125130
const afterAnyInvokeCbsFromPlugins = this.afterAnyInvokeCbs
126131

127132
// reset hooks
128-
this.afterInvokeCbs = afterInvokeCbs
129133
this.afterAnyInvokeCbs = []
130134
this.postProcessFilesCbs = []
131135

132136
// apply generators from plugins
133-
plugins.forEach(({ id, apply, options }) => {
137+
for (const plugin of this.plugins) {
138+
const { id, apply, options } = plugin
134139
const api = new GeneratorAPI(id, this, options, rootOptions)
135-
apply(api, options, rootOptions, invoking)
140+
await apply(api, options, rootOptions, invoking)
136141

137142
if (apply.hooks) {
138-
apply.hooks(api, options, rootOptions, pluginIds)
143+
// while we execute the entire `hooks` function,
144+
// only the `afterInvoke` hook is respected
145+
// because `afterAnyHooks` is already determined by the `allPluginIds` loop aboe
146+
await apply.hooks(api, options, rootOptions, pluginIds)
139147
}
140-
})
141148

142-
// load "any" hooks
143-
this.afterAnyInvokeCbs = afterAnyInvokeCbsFromPlugins
149+
// restore "any" hooks
150+
this.afterAnyInvokeCbs = afterAnyInvokeCbsFromPlugins
151+
}
144152
}
145153

146154
async generate ({
147155
extractConfigFiles = false,
148156
checkExisting = false
149157
} = {}) {
158+
await this.initPlugins()
159+
150160
// save the file system before applying plugin for comparison
151161
const initialFiles = Object.assign({}, this.files)
152162
// extract configs from package.json into dedicated files.
@@ -284,7 +294,7 @@ module.exports = class Generator {
284294
hasPlugin (_id, _version) {
285295
return [
286296
...this.plugins.map(p => p.id),
287-
...this.allPlugins
297+
...this.allPluginIds
288298
].some(id => {
289299
if (!matchesPluginId(_id, id)) {
290300
return false

0 commit comments

Comments
 (0)