mirror of
https://github.com/vuejs/vue-cli.git
synced 2026-03-20 18:11:07 -05:00
feat: make injectImports & injectRootOptions work for .vue files (#4168)
closes #1702
This commit is contained in:
@@ -23,6 +23,19 @@ new Vue({
|
||||
}).$mount('#app')
|
||||
`.trim())
|
||||
fs.writeFileSync(path.resolve(templateDir, 'empty-entry.js'), `;`)
|
||||
fs.writeFileSync(path.resolve(templateDir, 'hello.vue'), `
|
||||
<template>
|
||||
<p>Hello, {{ msg }}</p>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
name: 'HelloWorld',
|
||||
props: {
|
||||
msg: String
|
||||
}
|
||||
}
|
||||
</script>
|
||||
`)
|
||||
|
||||
// replace stubs
|
||||
fs.writeFileSync(path.resolve(templateDir, 'replace.js'), `
|
||||
@@ -505,6 +518,25 @@ test('api: addEntryDuplicateImport', async () => {
|
||||
expect(fs.readFileSync('/main.js', 'utf-8')).toMatch(/^import foo from 'foo'\s+new Vue/)
|
||||
})
|
||||
|
||||
test('api: injectImport for .vue files', async () => {
|
||||
const generator = new Generator('/', { plugins: [
|
||||
{
|
||||
id: 'test',
|
||||
apply: api => {
|
||||
api.injectImports('hello.vue', `import foo from 'foo'`)
|
||||
api.render({
|
||||
'hello.vue': path.join(templateDir, 'hello.vue')
|
||||
})
|
||||
}
|
||||
}
|
||||
] })
|
||||
|
||||
await generator.generate()
|
||||
const content = fs.readFileSync('/hello.vue', 'utf-8')
|
||||
expect(content).toMatch(/import foo from 'foo'/)
|
||||
expect(content).toMatch(/<template>([\s\S]*)<\/template>/)
|
||||
})
|
||||
|
||||
test('api: addEntryDuplicateInjection', async () => {
|
||||
const generator = new Generator('/', { plugins: [
|
||||
{
|
||||
|
||||
@@ -5,7 +5,7 @@ const sortObject = require('./util/sortObject')
|
||||
const writeFileTree = require('./util/writeFileTree')
|
||||
const inferRootOptions = require('./util/inferRootOptions')
|
||||
const normalizeFilePaths = require('./util/normalizeFilePaths')
|
||||
const injectImportsAndOptions = require('./util/injectImportsAndOptions')
|
||||
const runCodemod = require('./util/runCodemod')
|
||||
const { toShortPluginId, matchesPluginId } = require('@vue/cli-shared-utils')
|
||||
const ConfigTransform = require('./ConfigTransform')
|
||||
|
||||
@@ -215,11 +215,25 @@ module.exports = class Generator {
|
||||
|
||||
// handle imports and root option injections
|
||||
Object.keys(files).forEach(file => {
|
||||
files[file] = injectImportsAndOptions(
|
||||
files[file],
|
||||
this.imports[file],
|
||||
this.rootOptions[file]
|
||||
)
|
||||
let imports = this.imports[file]
|
||||
imports = imports instanceof Set ? Array.from(imports) : imports
|
||||
if (imports && imports.length > 0) {
|
||||
files[file] = runCodemod(
|
||||
require('./util/codemods/injectImports'),
|
||||
{ path: file, source: files[file] },
|
||||
{ imports }
|
||||
)
|
||||
}
|
||||
|
||||
let injections = this.rootOptions[file]
|
||||
injections = injections instanceof Set ? Array.from(injections) : injections
|
||||
if (injections && injections.length > 0) {
|
||||
files[file] = runCodemod(
|
||||
require('./util/codemods/injectOptions'),
|
||||
{ path: file, source: files[file] },
|
||||
{ injections }
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
for (const postProcess of this.postProcessFilesCbs) {
|
||||
|
||||
29
packages/@vue/cli/lib/util/codemods/injectImports.js
Normal file
29
packages/@vue/cli/lib/util/codemods/injectImports.js
Normal file
@@ -0,0 +1,29 @@
|
||||
module.exports = function injectImports (fileInfo, api, { imports }) {
|
||||
const j = api.jscodeshift
|
||||
const root = j(fileInfo.source)
|
||||
|
||||
const toImportAST = i => j(`${i}\n`).nodes()[0].program.body[0]
|
||||
const toImportHash = node => JSON.stringify({
|
||||
specifiers: node.specifiers.map(s => s.local.name),
|
||||
source: node.source.raw
|
||||
})
|
||||
|
||||
const declarations = root.find(j.ImportDeclaration)
|
||||
const importSet = new Set(declarations.nodes().map(toImportHash))
|
||||
const nonDuplicates = node => !importSet.has(toImportHash(node))
|
||||
|
||||
const importASTNodes = imports.map(toImportAST).filter(nonDuplicates)
|
||||
|
||||
if (declarations.length) {
|
||||
declarations
|
||||
.at(-1)
|
||||
// a tricky way to avoid blank line after the previous import
|
||||
.forEach(({ node }) => delete node.loc)
|
||||
.insertAfter(importASTNodes)
|
||||
} else {
|
||||
// no pre-existing import declarations
|
||||
root.get().node.program.body.unshift(...importASTNodes)
|
||||
}
|
||||
|
||||
return root.toSource()
|
||||
}
|
||||
27
packages/@vue/cli/lib/util/codemods/injectOptions.js
Normal file
27
packages/@vue/cli/lib/util/codemods/injectOptions.js
Normal file
@@ -0,0 +1,27 @@
|
||||
module.exports = function injectOptions (fileInfo, api, { injections }) {
|
||||
const j = api.jscodeshift
|
||||
const root = j(fileInfo.source)
|
||||
|
||||
const toPropertyAST = i => {
|
||||
return j(`({${i}})`).nodes()[0].program.body[0].expression.properties[0]
|
||||
}
|
||||
|
||||
const properties = root
|
||||
.find(j.NewExpression, {
|
||||
callee: { name: 'Vue' },
|
||||
arguments: [{ type: 'ObjectExpression' }]
|
||||
})
|
||||
.map(path => path.get('arguments', 0))
|
||||
.get()
|
||||
.node
|
||||
.properties
|
||||
|
||||
const toPropertyHash = p => `${p.key.name}: ${j(p.value).toSource()}`
|
||||
const propertySet = new Set(properties.map(toPropertyHash))
|
||||
const nonDuplicates = p => !propertySet.has(toPropertyHash(p))
|
||||
|
||||
// inject at index length - 1 as it's usually the render fn
|
||||
properties.splice(-1, 0, ...injections.map(toPropertyAST).filter(nonDuplicates))
|
||||
|
||||
return root.toSource()
|
||||
}
|
||||
@@ -1,64 +0,0 @@
|
||||
module.exports = function injectImportsAndOptions (source, imports, injections) {
|
||||
imports = imports instanceof Set ? Array.from(imports) : imports
|
||||
injections = injections instanceof Set ? Array.from(injections) : injections
|
||||
|
||||
const hasImports = imports && imports.length > 0
|
||||
const hasInjections = injections && injections.length > 0
|
||||
|
||||
if (!hasImports && !hasInjections) {
|
||||
return source
|
||||
}
|
||||
|
||||
const j = require('jscodeshift')
|
||||
const root = j(source)
|
||||
|
||||
if (hasImports) {
|
||||
const toImportAST = i => j(`${i}\n`).nodes()[0].program.body[0]
|
||||
const toImportHash = node => JSON.stringify({
|
||||
specifiers: node.specifiers.map(s => s.local.name),
|
||||
source: node.source.raw
|
||||
})
|
||||
|
||||
const declarations = root.find(j.ImportDeclaration)
|
||||
const importSet = new Set(declarations.nodes().map(toImportHash))
|
||||
const nonDuplicates = node => !importSet.has(toImportHash(node))
|
||||
|
||||
const importASTNodes = imports.map(toImportAST).filter(nonDuplicates)
|
||||
|
||||
if (declarations.length) {
|
||||
declarations
|
||||
.at(-1)
|
||||
// a tricky way to avoid blank line after the previous import
|
||||
.forEach(({ node }) => delete node.loc)
|
||||
.insertAfter(importASTNodes)
|
||||
} else {
|
||||
// no pre-existing import declarations
|
||||
root.get().node.program.body.unshift(...importASTNodes)
|
||||
}
|
||||
}
|
||||
|
||||
if (hasInjections) {
|
||||
const toPropertyAST = i => {
|
||||
return j(`({${i}})`).nodes()[0].program.body[0].expression.properties[0]
|
||||
}
|
||||
|
||||
const properties = root
|
||||
.find(j.NewExpression, {
|
||||
callee: { name: 'Vue' },
|
||||
arguments: [{ type: 'ObjectExpression' }]
|
||||
})
|
||||
.map(path => path.get('arguments', 0))
|
||||
.get()
|
||||
.node
|
||||
.properties
|
||||
|
||||
const toPropertyHash = p => `${p.key.name}: ${j(p.value).toSource()}`
|
||||
const propertySet = new Set(properties.map(toPropertyHash))
|
||||
const nonDuplicates = p => !propertySet.has(toPropertyHash(p))
|
||||
|
||||
// inject at index length - 1 as it's usually the render fn
|
||||
properties.splice(-1, 0, ...injections.map(toPropertyAST).filter(nonDuplicates))
|
||||
}
|
||||
|
||||
return root.toSource()
|
||||
}
|
||||
6
packages/@vue/cli/lib/util/runCodemod.js
Normal file
6
packages/@vue/cli/lib/util/runCodemod.js
Normal file
@@ -0,0 +1,6 @@
|
||||
const jscodeshift = require('jscodeshift')
|
||||
const adapt = require('vue-jscodeshift-adapter')
|
||||
|
||||
module.exports = function runCodemod (transform, fileInfo, options) {
|
||||
return adapt(transform)(fileInfo, { jscodeshift }, options || {})
|
||||
}
|
||||
@@ -56,6 +56,7 @@
|
||||
"shortid": "^2.2.11",
|
||||
"slash": "^3.0.0",
|
||||
"validate-npm-package-name": "^3.0.0",
|
||||
"vue-jscodeshift-adapter": "2.0.2",
|
||||
"yaml-front-matter": "^3.4.1"
|
||||
},
|
||||
"engines": {
|
||||
|
||||
Reference in New Issue
Block a user