// 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'
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)
+ })
+ })
})
-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
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 {
let hasDefinePropsCall = false
let hasDefineEmitCall = false
let hasDefineExposeCall = false
+ let hasDefaultExportName = false
let propsRuntimeDecl: Node | undefined
let propsRuntimeDefaults: ObjectExpression | undefined
let propsDestructureDecl: Node | undefined
} 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!
// 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,`
}
import { createCache } from './cache'
import { hmrShouldReload, ImportBinding } from './compileScript'
+export const DEFAULT_FILENAME = 'anonymous.vue'
+
export interface SFCParseOptions {
filename?: string
sourceMap?: boolean
source: string,
{
sourceMap = true,
- filename = 'anonymous.vue',
+ filename = DEFAULT_FILENAME,
sourceRoot = '',
pad = false,
ignoreEmpty = true,