Просмотр исходного кода

feat(compiler-vapor): resolve implicitly self-referencing component

daiwei 11 месяцев назад
Родитель
Сommit
584c04fa99

+ 1 - 0
packages/compiler-core/src/index.ts

@@ -17,6 +17,7 @@ export {
   createTransformContext,
   traverseNode,
   createStructuralDirectiveTransform,
+  getSelfName,
   type NodeTransform,
   type StructuralDirectiveTransform,
   type DirectiveTransform,

+ 6 - 2
packages/compiler-core/src/transform.ts

@@ -123,6 +123,11 @@ export interface TransformContext
   filters?: Set<string>
 }
 
+export function getSelfName(filename: string): string | null {
+  const nameMatch = filename.replace(/\?.*$/, '').match(/([^/\\]+)\.\w+$/)
+  return nameMatch ? capitalize(camelize(nameMatch[1])) : null
+}
+
 export function createTransformContext(
   root: RootNode,
   {
@@ -150,11 +155,10 @@ export function createTransformContext(
     compatConfig,
   }: TransformOptions,
 ): TransformContext {
-  const nameMatch = filename.replace(/\?.*$/, '').match(/([^/\\]+)\.\w+$/)
   const context: TransformContext = {
     // options
     filename,
-    selfName: nameMatch && capitalize(camelize(nameMatch[1])),
+    selfName: getSelfName(filename),
     prefixIdentifiers,
     hoistStatic,
     hmr,

+ 10 - 0
packages/compiler-vapor/__tests__/transforms/__snapshots__/transformElement.spec.ts.snap

@@ -77,6 +77,16 @@ export function render(_ctx, $props, $emit, $attrs, $slots) {
 }"
 `;
 
+exports[`compiler: element transform > component > resolve implicitly self-referencing component 1`] = `
+"import { resolveComponent as _resolveComponent, createComponentWithFallback as _createComponentWithFallback } from 'vue';
+
+export function render(_ctx) {
+  const _component_Example__self = _resolveComponent("Example", true)
+  const n0 = _createComponentWithFallback(_component_Example__self, null, null, true)
+  return n0
+}"
+`;
+
 exports[`compiler: element transform > component > resolve namespaced component from props bindings (inline) 1`] = `
 "
   const n0 = _createComponent(Foo.Example, null, null, true)

+ 2 - 1
packages/compiler-vapor/__tests__/transforms/transformElement.spec.ts

@@ -39,11 +39,12 @@ describe('compiler: element transform', () => {
       })
     })
 
-    test.todo('resolve implicitly self-referencing component', () => {
+    test('resolve implicitly self-referencing component', () => {
       const { code, helpers } = compileWithElementTransform(`<Example/>`, {
         filename: `/foo/bar/Example.vue?vue&type=template`,
       })
       expect(code).toMatchSnapshot()
+      expect(code).toContain('_resolveComponent("Example", true)')
       expect(helpers).toContain('resolveComponent')
     })
 

+ 15 - 1
packages/compiler-vapor/src/generators/block.ts

@@ -44,7 +44,21 @@ export function genBlockContent(
   const resetBlock = context.enterBlock(block)
 
   if (root) {
-    genResolveAssets('component', 'resolveComponent')
+    for (let name of context.ir.component) {
+      const id = toValidAssetId(name, 'component')
+      const maybeSelfReference = name.endsWith('__self')
+      if (maybeSelfReference) name = name.slice(0, -6)
+      push(
+        NEWLINE,
+        `const ${id} = `,
+        ...genCall(
+          context.helper('resolveComponent'),
+          JSON.stringify(name),
+          // pass additional `maybeSelfReference` flag
+          maybeSelfReference ? 'true' : undefined,
+        ),
+      )
+    }
     genResolveAssets('directive', 'resolveDirective')
   }
 

+ 3 - 0
packages/compiler-vapor/src/transform.ts

@@ -11,6 +11,7 @@ import {
   type TemplateChildNode,
   defaultOnError,
   defaultOnWarn,
+  getSelfName,
   isVSlot,
 } from '@vue/compiler-dom'
 import { EMPTY_OBJ, NOOP, extend, isArray, isString } from '@vue/shared'
@@ -61,6 +62,7 @@ export type StructuralDirectiveTransform = (
 export type TransformOptions = HackOptions<BaseTransformOptions>
 
 export class TransformContext<T extends AllNode = AllNode> {
+  selfName: string | null = null
   parent: TransformContext<RootNode | ElementNode> | null = null
   root: TransformContext<RootNode>
   index: number = 0
@@ -92,6 +94,7 @@ export class TransformContext<T extends AllNode = AllNode> {
   ) {
     this.options = extend({}, defaultOptions, options)
     this.root = this as TransformContext<RootNode>
+    if (options.filename) this.selfName = getSelfName(options.filename)
   }
 
   enterBlock(ir: BlockIRNode, isVFor: boolean = false): () => void {

+ 7 - 0
packages/compiler-vapor/src/transforms/transformElement.ts

@@ -119,6 +119,13 @@ function transformComponentElement(
     }
 
     if (asset) {
+      // self referencing component (inferred from filename)
+      if (context.selfName && capitalize(camelize(tag)) === context.selfName) {
+        // generators/block.ts has special check for __self postfix when generating
+        // component imports, which will pass additional `maybeSelfReference` flag
+        // to `resolveComponent`.
+        tag += `__self`
+      }
       context.component.add(tag)
     }
   }