createTransformContext,
traverseNode,
createStructuralDirectiveTransform,
+ getSelfName,
type NodeTransform,
type StructuralDirectiveTransform,
type DirectiveTransform,
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,
{
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,
}"
`;
+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)
})
})
- 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')
})
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')
}
type TemplateChildNode,
defaultOnError,
defaultOnWarn,
+ getSelfName,
isVSlot,
} from '@vue/compiler-dom'
import { EMPTY_OBJ, NOOP, extend, isArray, isString } from '@vue/shared'
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
) {
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 {
}
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)
}
}