fix(compiler-sfc): automatically infer component name from filename when using script setup (#4997)
close #4993
This commit is contained in:
parent
7dfe146096
commit
16939241b0
@ -1,5 +1,48 @@
|
|||||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
|
exports[`SFC analyze <script> bindings auto name inference basic 1`] = `
|
||||||
|
"export default {
|
||||||
|
name: 'FooBar',
|
||||||
|
setup(__props, { expose }) {
|
||||||
|
expose();
|
||||||
|
const a = 1
|
||||||
|
return { a }
|
||||||
|
}
|
||||||
|
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`SFC analyze <script> bindings auto name inference do not overwrite manual name (call) 1`] = `
|
||||||
|
"import { defineComponent } from 'vue'
|
||||||
|
const __default__ = defineComponent({
|
||||||
|
name: 'Baz'
|
||||||
|
})
|
||||||
|
|
||||||
|
export default /*#__PURE__*/Object.assign(__default__, {
|
||||||
|
setup(__props, { expose }) {
|
||||||
|
expose();
|
||||||
|
const a = 1
|
||||||
|
return { a, defineComponent }
|
||||||
|
}
|
||||||
|
|
||||||
|
})"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`SFC analyze <script> bindings auto name inference do not overwrite manual name (object) 1`] = `
|
||||||
|
"const __default__ = {
|
||||||
|
name: 'Baz'
|
||||||
|
}
|
||||||
|
|
||||||
|
export default /*#__PURE__*/Object.assign(__default__, {
|
||||||
|
setup(__props, { expose }) {
|
||||||
|
expose();
|
||||||
|
const a = 1
|
||||||
|
return { a }
|
||||||
|
}
|
||||||
|
|
||||||
|
})"
|
||||||
|
`;
|
||||||
|
|
||||||
exports[`SFC compile <script setup> <script> and <script setup> co-usage script first 1`] = `
|
exports[`SFC compile <script setup> <script> and <script setup> co-usage script first 1`] = `
|
||||||
"import { x } from './x'
|
"import { x } from './x'
|
||||||
|
|
||||||
|
@ -1550,4 +1550,59 @@ describe('SFC analyze <script> bindings', () => {
|
|||||||
foo: BindingTypes.PROPS
|
foo: BindingTypes.PROPS
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
describe('auto name inference', () => {
|
||||||
|
test('basic', () => {
|
||||||
|
const { content } = compile(
|
||||||
|
`<script setup>const a = 1</script>
|
||||||
|
<template>{{ a }}</template>`,
|
||||||
|
undefined,
|
||||||
|
{
|
||||||
|
filename: 'FooBar.vue'
|
||||||
|
}
|
||||||
|
)
|
||||||
|
expect(content).toMatch(`export default {
|
||||||
|
name: 'FooBar'`)
|
||||||
|
assertCode(content)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('do not overwrite manual name (object)', () => {
|
||||||
|
const { content } = compile(
|
||||||
|
`<script>
|
||||||
|
export default {
|
||||||
|
name: 'Baz'
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<script setup>const a = 1</script>
|
||||||
|
<template>{{ a }}</template>`,
|
||||||
|
undefined,
|
||||||
|
{
|
||||||
|
filename: 'FooBar.vue'
|
||||||
|
}
|
||||||
|
)
|
||||||
|
expect(content).not.toMatch(`name: 'FooBar'`)
|
||||||
|
expect(content).toMatch(`name: 'Baz'`)
|
||||||
|
assertCode(content)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('do not overwrite manual name (call)', () => {
|
||||||
|
const { content } = compile(
|
||||||
|
`<script>
|
||||||
|
import { defineComponent } from 'vue'
|
||||||
|
export default defineComponent({
|
||||||
|
name: 'Baz'
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
<script setup>const a = 1</script>
|
||||||
|
<template>{{ a }}</template>`,
|
||||||
|
undefined,
|
||||||
|
{
|
||||||
|
filename: 'FooBar.vue'
|
||||||
|
}
|
||||||
|
)
|
||||||
|
expect(content).not.toMatch(`name: 'FooBar'`)
|
||||||
|
expect(content).toMatch(`name: 'Baz'`)
|
||||||
|
assertCode(content)
|
||||||
|
})
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
@ -1,13 +1,19 @@
|
|||||||
import { parse, SFCScriptCompileOptions, compileScript } from '../src'
|
import {
|
||||||
|
parse,
|
||||||
|
SFCScriptCompileOptions,
|
||||||
|
compileScript,
|
||||||
|
SFCParseOptions
|
||||||
|
} from '../src'
|
||||||
import { parse as babelParse } from '@babel/parser'
|
import { parse as babelParse } from '@babel/parser'
|
||||||
|
|
||||||
export const mockId = 'xxxxxxxx'
|
export const mockId = 'xxxxxxxx'
|
||||||
|
|
||||||
export function compileSFCScript(
|
export function compileSFCScript(
|
||||||
src: string,
|
src: string,
|
||||||
options?: Partial<SFCScriptCompileOptions>
|
options?: Partial<SFCScriptCompileOptions>,
|
||||||
|
parseOptions?: SFCParseOptions
|
||||||
) {
|
) {
|
||||||
const { descriptor } = parse(src)
|
const { descriptor } = parse(src, parseOptions)
|
||||||
return compileScript(descriptor, {
|
return compileScript(descriptor, {
|
||||||
...options,
|
...options,
|
||||||
id: mockId
|
id: mockId
|
||||||
|
@ -11,7 +11,7 @@ import {
|
|||||||
isFunctionType,
|
isFunctionType,
|
||||||
walkIdentifiers
|
walkIdentifiers
|
||||||
} from '@vue/compiler-dom'
|
} from '@vue/compiler-dom'
|
||||||
import { SFCDescriptor, SFCScriptBlock } from './parse'
|
import { DEFAULT_FILENAME, SFCDescriptor, SFCScriptBlock } from './parse'
|
||||||
import { parse as _parse, ParserOptions, ParserPlugin } from '@babel/parser'
|
import { parse as _parse, ParserOptions, ParserPlugin } from '@babel/parser'
|
||||||
import { camelize, capitalize, generateCodeFrame, makeMap } from '@vue/shared'
|
import { camelize, capitalize, generateCodeFrame, makeMap } from '@vue/shared'
|
||||||
import {
|
import {
|
||||||
@ -263,6 +263,7 @@ export function compileScript(
|
|||||||
let hasDefinePropsCall = false
|
let hasDefinePropsCall = false
|
||||||
let hasDefineEmitCall = false
|
let hasDefineEmitCall = false
|
||||||
let hasDefineExposeCall = false
|
let hasDefineExposeCall = false
|
||||||
|
let hasDefaultExportName = false
|
||||||
let propsRuntimeDecl: Node | undefined
|
let propsRuntimeDecl: Node | undefined
|
||||||
let propsRuntimeDefaults: ObjectExpression | undefined
|
let propsRuntimeDefaults: ObjectExpression | undefined
|
||||||
let propsDestructureDecl: Node | undefined
|
let propsDestructureDecl: Node | undefined
|
||||||
@ -811,6 +812,25 @@ export function compileScript(
|
|||||||
} else if (node.type === 'ExportDefaultDeclaration') {
|
} else if (node.type === 'ExportDefaultDeclaration') {
|
||||||
// export default
|
// export default
|
||||||
defaultExport = node
|
defaultExport = node
|
||||||
|
|
||||||
|
// check if user has manually specified `name` option in export default
|
||||||
|
// if yes, skip infer later
|
||||||
|
let optionProperties
|
||||||
|
if (defaultExport.declaration.type === 'ObjectExpression') {
|
||||||
|
optionProperties = defaultExport.declaration.properties
|
||||||
|
} else if (
|
||||||
|
defaultExport.declaration.type === 'CallExpression' &&
|
||||||
|
defaultExport.declaration.arguments[0].type === 'ObjectExpression'
|
||||||
|
) {
|
||||||
|
optionProperties = defaultExport.declaration.arguments[0].properties
|
||||||
|
}
|
||||||
|
hasDefaultExportName = !!optionProperties?.some(
|
||||||
|
s =>
|
||||||
|
s.type === 'ObjectProperty' &&
|
||||||
|
s.key.type === 'Identifier' &&
|
||||||
|
s.key.name === 'name'
|
||||||
|
)
|
||||||
|
|
||||||
// export default { ... } --> const __default__ = { ... }
|
// export default { ... } --> const __default__ = { ... }
|
||||||
const start = node.start! + scriptStartOffset!
|
const start = node.start! + scriptStartOffset!
|
||||||
const end = node.declaration.start! + scriptStartOffset!
|
const end = node.declaration.start! + scriptStartOffset!
|
||||||
@ -1364,6 +1384,12 @@ export function compileScript(
|
|||||||
|
|
||||||
// 11. finalize default export
|
// 11. finalize default export
|
||||||
let runtimeOptions = ``
|
let runtimeOptions = ``
|
||||||
|
if (!hasDefaultExportName && filename && filename !== DEFAULT_FILENAME) {
|
||||||
|
const match = filename.match(/([^/\\]+)\.\w+$/)
|
||||||
|
if (match) {
|
||||||
|
runtimeOptions += `\n name: '${match[1]}',`
|
||||||
|
}
|
||||||
|
}
|
||||||
if (hasInlinedSsrRenderFn) {
|
if (hasInlinedSsrRenderFn) {
|
||||||
runtimeOptions += `\n __ssrInlineRender: true,`
|
runtimeOptions += `\n __ssrInlineRender: true,`
|
||||||
}
|
}
|
||||||
|
@ -13,6 +13,8 @@ import { parseCssVars } from './cssVars'
|
|||||||
import { createCache } from './cache'
|
import { createCache } from './cache'
|
||||||
import { hmrShouldReload, ImportBinding } from './compileScript'
|
import { hmrShouldReload, ImportBinding } from './compileScript'
|
||||||
|
|
||||||
|
export const DEFAULT_FILENAME = 'anonymous.vue'
|
||||||
|
|
||||||
export interface SFCParseOptions {
|
export interface SFCParseOptions {
|
||||||
filename?: string
|
filename?: string
|
||||||
sourceMap?: boolean
|
sourceMap?: boolean
|
||||||
@ -95,7 +97,7 @@ export function parse(
|
|||||||
source: string,
|
source: string,
|
||||||
{
|
{
|
||||||
sourceMap = true,
|
sourceMap = true,
|
||||||
filename = 'anonymous.vue',
|
filename = DEFAULT_FILENAME,
|
||||||
sourceRoot = '',
|
sourceRoot = '',
|
||||||
pad = false,
|
pad = false,
|
||||||
ignoreEmpty = true,
|
ignoreEmpty = true,
|
||||||
|
Loading…
Reference in New Issue
Block a user