]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
fix(component): prioritize registered component over implicit self-reference via...
authorEvan You <yyx990803@gmail.com>
Fri, 26 Mar 2021 14:04:29 +0000 (10:04 -0400)
committerEvan You <yyx990803@gmail.com>
Fri, 26 Mar 2021 14:04:36 +0000 (10:04 -0400)
ref: #2827

packages/compiler-core/__tests__/__snapshots__/codegen.spec.ts.snap
packages/compiler-core/__tests__/codegen.spec.ts
packages/compiler-core/__tests__/transforms/transformElement.spec.ts
packages/compiler-core/src/codegen.ts
packages/compiler-core/src/transforms/transformElement.ts
packages/runtime-core/__tests__/helpers/resolveAssets.spec.ts
packages/runtime-core/src/helpers/resolveAssets.ts
packages/template-explorer/src/options.ts

index 6975881ee7468a89d3ebd12bfc9b293f978bf722..74d0ab70d38e6de42805467044b5414772b3ccb9 100644 (file)
@@ -66,6 +66,7 @@ return function render(_ctx, _cache) {
     const _component_Foo = _resolveComponent(\\"Foo\\")
     const _component_bar_baz = _resolveComponent(\\"bar-baz\\")
     const _component_barbaz = _resolveComponent(\\"barbaz\\")
+    const _component_Qux = _resolveComponent(\\"Qux\\", true)
     const _directive_my_dir_0 = _resolveDirective(\\"my_dir_0\\")
     const _directive_my_dir_1 = _resolveDirective(\\"my_dir_1\\")
     let _temp0, _temp1, _temp2
index 809c209f80be0a449c9b9e43f9df003fcfbe1bb0..75001f94aff1448c69f04b6265f938165eb1023f 100644 (file)
@@ -126,7 +126,7 @@ describe('compiler: codegen', () => {
 
   test('assets + temps', () => {
     const root = createRoot({
-      components: [`Foo`, `bar-baz`, `barbaz`],
+      components: [`Foo`, `bar-baz`, `barbaz`, `Qux__self`],
       directives: [`my_dir_0`, `my_dir_1`],
       temps: 3
     })
@@ -144,6 +144,12 @@ describe('compiler: codegen', () => {
         helperNameMap[RESOLVE_COMPONENT]
       }("barbaz")\n`
     )
+    // implicit self reference from SFC filename
+    expect(code).toMatch(
+      `const _component_Qux = _${
+        helperNameMap[RESOLVE_COMPONENT]
+      }("Qux", true)\n`
+    )
     expect(code).toMatch(
       `const _directive_my_dir_0 = _${
         helperNameMap[RESOLVE_DIRECTIVE]
index 6f49aa70f63444b0a5779fe3311672832ba13216..c07bb0e55337aa9773b83b5e44fbc8c25bdb414c 100644 (file)
@@ -75,7 +75,7 @@ describe('compiler: element transform', () => {
       filename: `/foo/bar/Example.vue?vue&type=template`
     })
     expect(root.helpers).toContain(RESOLVE_COMPONENT)
-    expect(root.components).toContain(`_self`)
+    expect(root.components).toContain(`Example__self`)
   })
 
   test('static props', () => {
index cd572313286abed05f0020e1ace39eac375f4c66..3acb8cf5423a6c13b4cd7fa38b7d8313ec1be532 100644 (file)
@@ -434,9 +434,16 @@ function genAssets(
     type === 'component' ? RESOLVE_COMPONENT : RESOLVE_DIRECTIVE
   )
   for (let i = 0; i < assets.length; i++) {
-    const id = assets[i]
+    let id = assets[i]
+    // potential component implicit self-reference inferred from SFC filename
+    const maybeSelfReference = id.endsWith('__self')
+    if (maybeSelfReference) {
+      id = id.slice(0, -6)
+    }
     push(
-      `const ${toValidAssetId(id, type)} = ${resolver}(${JSON.stringify(id)})`
+      `const ${toValidAssetId(id, type)} = ${resolver}(${JSON.stringify(id)}${
+        maybeSelfReference ? `, true` : ``
+      })`
     )
     if (i < assets.length - 1) {
       newline()
index 0283d3eec5eb3e900f40b073d088fba0f9641ede..5c34c6d2eee756c11e5827ff6cf6d8d51adf7ad5 100644 (file)
@@ -264,12 +264,17 @@ export function resolveComponentType(
   }
 
   // 4. Self referencing component (inferred from filename)
-  if (!__BROWSER__ && context.selfName) {
-    if (capitalize(camelize(tag)) === context.selfName) {
-      context.helper(RESOLVE_COMPONENT)
-      context.components.add(`_self`)
-      return toValidAssetId(`_self`, `component`)
-    }
+  if (
+    !__BROWSER__ &&
+    context.selfName &&
+    capitalize(camelize(tag)) === context.selfName
+  ) {
+    context.helper(RESOLVE_COMPONENT)
+    // codegen.ts has special check for __self postfix when generating
+    // component imports, which will pass additional `maybeSelfReference` flag
+    // to `resolveComponent`.
+    context.components.add(tag + `__self`)
+    return toValidAssetId(tag, `component`)
   }
 
   // 5. user component (resolve)
index 5d5e895ca531d4dafc901d6b320cff35f45ca3e6..44434a256d52d6b9d112375c53ec762e5b1c88bb 100644 (file)
@@ -65,6 +65,37 @@ describe('resolveAssets', () => {
     expect(directive4!).toBe(BarBaz)
   })
 
+  test('maybeSelfReference', async () => {
+    let component1: Component | string
+    let component2: Component | string
+    let component3: Component | string
+
+    const Foo = () => null
+
+    const Root = {
+      name: 'Root',
+      components: {
+        Foo,
+        Root: Foo
+      },
+      setup() {
+        return () => {
+          component1 = resolveComponent('Root', true)
+          component2 = resolveComponent('Foo', true)
+          component3 = resolveComponent('Bar', true)
+        }
+      }
+    }
+
+    const app = createApp(Root)
+    const root = nodeOps.createElement('div')
+    app.mount(root)
+
+    expect(component1!).toBe(Root) // explicit self name reference
+    expect(component2!).toBe(Foo) // successful resolve take higher priority
+    expect(component3!).toBe(Root) // fallback when resolve fails
+  })
+
   describe('warning', () => {
     test('used outside render() or setup()', () => {
       resolveComponent('foo')
index 1d6a96bf99942c569815aef477502ca361c60d3b..e867cc51d57953aae57a706c6b6844084dabfa37 100644 (file)
@@ -16,8 +16,11 @@ const DIRECTIVES = 'directives'
 /**
  * @private
  */
-export function resolveComponent(name: string): ConcreteComponent | string {
-  return resolveAsset(COMPONENTS, name) || name
+export function resolveComponent(
+  name: string,
+  maybeSelfReference?: boolean
+): ConcreteComponent | string {
+  return resolveAsset(COMPONENTS, name, true, maybeSelfReference) || name
 }
 
 export const NULL_DYNAMIC_COMPONENT = Symbol()
@@ -48,7 +51,8 @@ export function resolveDirective(name: string): Directive | undefined {
 function resolveAsset(
   type: typeof COMPONENTS,
   name: string,
-  warnMissing?: boolean
+  warnMissing?: boolean,
+  maybeSelfReference?: boolean
 ): ConcreteComponent | undefined
 // overload 2: directives
 function resolveAsset(
@@ -59,20 +63,15 @@ function resolveAsset(
 function resolveAsset(
   type: typeof COMPONENTS | typeof DIRECTIVES,
   name: string,
-  warnMissing = true
+  warnMissing = true,
+  maybeSelfReference = false
 ) {
   const instance = currentRenderingInstance || currentInstance
   if (instance) {
     const Component = instance.type
 
-    // self name has highest priority
+    // explicit self name has highest priority
     if (type === COMPONENTS) {
-      // special self referencing call generated by compiler
-      // inferred from SFC filename
-      if (name === `_self`) {
-        return Component
-      }
-
       const selfName = getComponentName(Component)
       if (
         selfName &&
@@ -90,9 +89,16 @@ function resolveAsset(
       resolve(instance[type] || (Component as ComponentOptions)[type], name) ||
       // global registration
       resolve(instance.appContext[type], name)
+
+    if (!res && maybeSelfReference) {
+      // fallback to implicit self-reference
+      return Component
+    }
+
     if (__DEV__ && warnMissing && !res) {
       warn(`Failed to resolve ${type.slice(0, -1)}: ${name}`)
     }
+
     return res
   } else if (__DEV__) {
     warn(
index dab7f8adf8a1605aa402db97cca63994e2db03d5..e8669bc7e2ea90872733fdc0dbd6636a9b4b51c8 100644 (file)
@@ -6,6 +6,7 @@ export const ssrMode = ref(false)
 
 export const compilerOptions: CompilerOptions = reactive({
   mode: 'module',
+  filename: 'Foo.vue',
   prefixIdentifiers: false,
   optimizeImports: false,
   hoistStatic: false,