Skip to content

Commit

Permalink
fix(compiler-sfc): automatically infer component name from filename w…
Browse files Browse the repository at this point in the history
…hen using script setup (#4997)

close #4993
  • Loading branch information
ygj6 committed May 10, 2022
1 parent 7dfe146 commit 1693924
Show file tree
Hide file tree
Showing 5 changed files with 137 additions and 5 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,48 @@
// 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`] = `
"import { x } from './x'

Expand Down
55 changes: 55 additions & 0 deletions packages/compiler-sfc/__tests__/compileScript.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1550,4 +1550,59 @@ describe('SFC analyze <script> bindings', () => {
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)
})
})
})
12 changes: 9 additions & 3 deletions packages/compiler-sfc/__tests__/utils.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,19 @@
import { parse, SFCScriptCompileOptions, compileScript } from '../src'
import {
parse,
SFCScriptCompileOptions,
compileScript,
SFCParseOptions
} from '../src'
import { parse as babelParse } from '@babel/parser'

export const mockId = 'xxxxxxxx'

export function compileSFCScript(
src: string,
options?: Partial<SFCScriptCompileOptions>
options?: Partial<SFCScriptCompileOptions>,
parseOptions?: SFCParseOptions
) {
const { descriptor } = parse(src)
const { descriptor } = parse(src, parseOptions)
return compileScript(descriptor, {
...options,
id: mockId
Expand Down
28 changes: 27 additions & 1 deletion packages/compiler-sfc/src/compileScript.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import {
isFunctionType,
walkIdentifiers
} 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 { camelize, capitalize, generateCodeFrame, makeMap } from '@vue/shared'
import {
Expand Down Expand Up @@ -263,6 +263,7 @@ export function compileScript(
let hasDefinePropsCall = false
let hasDefineEmitCall = false
let hasDefineExposeCall = false
let hasDefaultExportName = false
let propsRuntimeDecl: Node | undefined
let propsRuntimeDefaults: ObjectExpression | undefined
let propsDestructureDecl: Node | undefined
Expand Down Expand Up @@ -811,6 +812,25 @@ export function compileScript(
} else if (node.type === 'ExportDefaultDeclaration') {
// export default
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__ = { ... }
const start = node.start! + scriptStartOffset!
const end = node.declaration.start! + scriptStartOffset!
Expand Down Expand Up @@ -1364,6 +1384,12 @@ export function compileScript(

// 11. finalize default export
let runtimeOptions = ``
if (!hasDefaultExportName && filename && filename !== DEFAULT_FILENAME) {
const match = filename.match(/([^/\\]+)\.\w+$/)
if (match) {
runtimeOptions += `\n name: '${match[1]}',`
}
}
if (hasInlinedSsrRenderFn) {
runtimeOptions += `\n __ssrInlineRender: true,`
}
Expand Down
4 changes: 3 additions & 1 deletion packages/compiler-sfc/src/parse.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ import { parseCssVars } from './cssVars'
import { createCache } from './cache'
import { hmrShouldReload, ImportBinding } from './compileScript'

export const DEFAULT_FILENAME = 'anonymous.vue'

export interface SFCParseOptions {
filename?: string
sourceMap?: boolean
Expand Down Expand Up @@ -95,7 +97,7 @@ export function parse(
source: string,
{
sourceMap = true,
filename = 'anonymous.vue',
filename = DEFAULT_FILENAME,
sourceRoot = '',
pad = false,
ignoreEmpty = true,
Expand Down

0 comments on commit 1693924

Please sign in to comment.