]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
fix(compiler-sfc): automatically infer component name from filename when using script...
authorygj6 <7699524+ygj6@users.noreply.github.com>
Tue, 10 May 2022 01:16:28 +0000 (09:16 +0800)
committerGitHub <noreply@github.com>
Tue, 10 May 2022 01:16:28 +0000 (21:16 -0400)
close #4993

packages/compiler-sfc/__tests__/__snapshots__/compileScript.spec.ts.snap
packages/compiler-sfc/__tests__/compileScript.spec.ts
packages/compiler-sfc/__tests__/utils.ts
packages/compiler-sfc/src/compileScript.ts
packages/compiler-sfc/src/parse.ts

index de223bf91d8df2e94af8acd48f7422ff1fc9b35b..cbe274ef9e8f6fdd3cf290c016e732afa5e4431a 100644 (file)
@@ -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'
       
index 17c97745e32df41506d732aa226a9dc875f71f4e..51ef6c9e7ec265290cf5a12ff5e1b8b9d4de7c8a 100644 (file)
@@ -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)
+    })
+  })
 })
index 206ff3681dd60e31fbf364b250fd8c09137e00f8..ffa12652c83293f4fbe34db591c569064d96a377 100644 (file)
@@ -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
index 3a3a260332e528faa820ac458b66dd9eaa37e245..7c84f29b3b3fa26720e46c61a39b7065a7d0f309 100644 (file)
@@ -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 {
@@ -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
@@ -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!
@@ -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,`
   }
index ed44e53682934e5d01affa1da03a49d55e7f0b91..cc7873c29a8c9384e3ed3b261e689d8e3fa034aa 100644 (file)
@@ -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
@@ -95,7 +97,7 @@ export function parse(
   source: string,
   {
     sourceMap = true,
-    filename = 'anonymous.vue',
+    filename = DEFAULT_FILENAME,
     sourceRoot = '',
     pad = false,
     ignoreEmpty = true,