Skip to content

Commit

Permalink
feat(cli): Generator support async (#3897)
Browse files Browse the repository at this point in the history
  • Loading branch information
xierenyuan authored and sodatea committed Jul 29, 2019
1 parent 42ecd08 commit 74f629e
Show file tree
Hide file tree
Showing 7 changed files with 97 additions and 21 deletions.
19 changes: 14 additions & 5 deletions packages/@vue/cli/__tests__/Generator.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -256,7 +256,7 @@ test('api: extendPackage merge dependencies', async () => {
})

test('api: warn invalid dep range', async () => {
new Generator('/', { plugins: [
const generator = new Generator('/', { plugins: [
{
id: 'test1',
apply: api => {
Expand All @@ -269,6 +269,8 @@ test('api: warn invalid dep range', async () => {
}
] })

await generator.generate()

expect(logs.warn.some(([msg]) => {
return (
msg.match(/invalid version range for dependency "foo"/) &&
Expand All @@ -278,7 +280,7 @@ test('api: warn invalid dep range', async () => {
})

test('api: extendPackage dependencies conflict', async () => {
new Generator('/', { plugins: [
const generator = new Generator('/', { plugins: [
{
id: 'test1',
apply: api => {
Expand All @@ -301,6 +303,8 @@ test('api: extendPackage dependencies conflict', async () => {
}
] })

await generator.generate()

expect(logs.warn.some(([msg]) => {
return (
msg.match(/conflicting versions for project dependency "foo"/) &&
Expand All @@ -312,7 +316,7 @@ test('api: extendPackage dependencies conflict', async () => {
})

test('api: extendPackage merge warn nonstrictly semver deps', async () => {
new Generator('/', { plugins: [
const generator = new Generator('/', { plugins: [
{
id: 'test3',
apply: api => {
Expand All @@ -335,6 +339,8 @@ test('api: extendPackage merge warn nonstrictly semver deps', async () => {
}
] })

await generator.generate()

expect(logs.warn.some(([msg]) => {
return (
msg.match(/conflicting versions for project dependency "bar"/) &&
Expand Down Expand Up @@ -436,10 +442,10 @@ test('api: hasPlugin', () => {
] })
})

test('api: onCreateComplete', () => {
test('api: onCreateComplete', async () => {
const fn = () => {}
const cbs = []
new Generator('/', {
const generator = new Generator('/', {
plugins: [
{
id: 'test',
Expand Down Expand Up @@ -467,6 +473,9 @@ test('api: afterInvoke', () => {
],
afterInvokeCbs: cbs
})

await generator.generate()

expect(cbs).toContain(fn)
})

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
const sleep = n => new Promise(resolve => setTimeout(resolve, n))

module.exports = async (api, options) => {
api.render('./template', options)

// add asynchronous code test
await sleep(1000)

api.extendPackage({
scripts: {
testasync: 'this is the test'
},
devDependencies: {
'vue-cli-plugin-async-generator': 'v0.0.1'
}
})
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<%= ok %>
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"plugins": {
"@vue/cli-plugin-babel": {}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
module.exports = [{
type: 'confirm',
name: 'ok',
message: 'Are you ok?'
}]
29 changes: 29 additions & 0 deletions packages/@vue/cli/__tests__/preset.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,3 +56,32 @@ test('should recognize generator/index.js in a local preset directory', async ()
const pkg = require(path.resolve(cwd, name, 'package.json'))
expect(pkg.devDependencies).toHaveProperty('@vue/cli-plugin-babel')
})

test('should recognize generator/index.js in a local preset directory by async generatory', async () => {
const cwd = path.resolve(__dirname, '../../../test')
const name = 'test-preset-template-async-generator'

expectPrompts([{
message: 'Are you ok',
confirm: true
}])

await create(
name,
{
force: true,
git: false,
cwd,
preset: path.resolve(__dirname, './mock-preset-with-async-generator')
}
)

const testFile = await fs.readFile(path.resolve(cwd, name, 'test.js'), 'utf-8')
expect(testFile).toBe('true\n')

const pkg = require(path.resolve(cwd, name, 'package.json'))
expect(pkg.devDependencies).toHaveProperty('@vue/cli-plugin-babel')
expect(pkg.devDependencies).toHaveProperty('vue-cli-plugin-async-generator')
expect(pkg.scripts).toHaveProperty('testasync')
})

42 changes: 26 additions & 16 deletions packages/@vue/cli/lib/Generator.js
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ module.exports = class Generator {
this.pm = new PackageManager({ context })
this.imports = {}
this.rootOptions = {}
this.afterInvokeCbs = []
this.afterInvokeCbs = afterInvokeCbs
this.afterAnyInvokeCbs = afterAnyInvokeCbs
this.configTransforms = {}
this.defaultConfigTransforms = defaultConfigTransforms
Expand All @@ -98,10 +98,8 @@ module.exports = class Generator {
// exit messages
this.exitLogs = []

const pluginIds = plugins.map(p => p.id)

// load all the other plugins
this.allPlugins = Object.keys(this.pkg.dependencies || {})
this.allPluginIds = Object.keys(this.pkg.dependencies || {})
.concat(Object.keys(this.pkg.devDependencies || {}))
.filter(isPlugin)

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

this.rootOptions = rootOptions
}

async initPlugins () {
const { rootOptions, invoking } = this
const pluginIds = this.plugins.map(p => p.id)

// apply hooks from all plugins
this.allPlugins.forEach(id => {
for (const id of this.allPluginIds) {
const api = new GeneratorAPI(id, this, {}, rootOptions)
const pluginGenerator = loadModule(`${id}/generator`, context)
const pluginGenerator = loadModule(`${id}/generator`, this.context)

if (pluginGenerator && pluginGenerator.hooks) {
pluginGenerator.hooks(api, {}, rootOptions, pluginIds)
await pluginGenerator.hooks(api, {}, rootOptions, pluginIds)
}
})
}

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

// reset hooks
this.afterInvokeCbs = afterInvokeCbs
this.afterAnyInvokeCbs = []
this.postProcessFilesCbs = []

// apply generators from plugins
plugins.forEach(({ id, apply, options }) => {
for (const plugin of this.plugins) {
const { id, apply, options } = plugin
const api = new GeneratorAPI(id, this, options, rootOptions)
apply(api, options, rootOptions, invoking)
await apply(api, options, rootOptions, invoking)

if (apply.hooks) {
apply.hooks(api, options, rootOptions, pluginIds)
// while we execute the entire `hooks` function,
// only the `afterInvoke` hook is respected
// because `afterAnyHooks` is already determined by the `allPluginIds` loop aboe
await apply.hooks(api, options, rootOptions, pluginIds)
}
})

// load "any" hooks
this.afterAnyInvokeCbs = afterAnyInvokeCbsFromPlugins
// restore "any" hooks
this.afterAnyInvokeCbs = afterAnyInvokeCbsFromPlugins
}
}

async generate ({
extractConfigFiles = false,
checkExisting = false
} = {}) {
await this.initPlugins()

// save the file system before applying plugin for comparison
const initialFiles = Object.assign({}, this.files)
// extract configs from package.json into dedicated files.
Expand Down Expand Up @@ -284,7 +294,7 @@ module.exports = class Generator {
hasPlugin (_id, _version) {
return [
...this.plugins.map(p => p.id),
...this.allPlugins
...this.allPluginIds
].some(id => {
if (!matchesPluginId(_id, id)) {
return false
Expand Down

0 comments on commit 74f629e

Please sign in to comment.