diff --git a/packages/compiler-core/__tests__/transforms/transformElement.spec.ts b/packages/compiler-core/__tests__/transforms/transformElement.spec.ts
index 33cfe51d..37c7c7b5 100644
--- a/packages/compiler-core/__tests__/transforms/transformElement.spec.ts
+++ b/packages/compiler-core/__tests__/transforms/transformElement.spec.ts
@@ -70,6 +70,14 @@ describe('compiler: element transform', () => {
expect(root.components).toContain(`Foo`)
})
+ test('resolve implcitly self-referencing component', () => {
+ const { root } = parseWithElementTransform(``, {
+ filename: `/foo/bar/Example.vue?vue&type=template`
+ })
+ expect(root.helpers).toContain(RESOLVE_COMPONENT)
+ expect(root.components).toContain(`_self`)
+ })
+
test('static props', () => {
const { node } = parseWithElementTransform(`
`)
expect(node).toMatchObject({
diff --git a/packages/compiler-core/src/options.ts b/packages/compiler-core/src/options.ts
index 02ee4068..2850da19 100644
--- a/packages/compiler-core/src/options.ts
+++ b/packages/compiler-core/src/options.ts
@@ -128,6 +128,12 @@ interface SharedTransformCodegenOptions {
* Indicates that transforms and codegen should try to output valid TS code
*/
isTS?: boolean
+ /**
+ * Filename for source map generation.
+ * Also used for self-recursive reference in templates
+ * @default 'template.vue.html'
+ */
+ filename?: string
}
export interface TransformOptions extends SharedTransformCodegenOptions {
@@ -218,11 +224,6 @@ export interface CodegenOptions extends SharedTransformCodegenOptions {
* @default false
*/
sourceMap?: boolean
- /**
- * Filename for source map generation.
- * @default 'template.vue.html'
- */
- filename?: string
/**
* SFC scoped styles ID
*/
diff --git a/packages/compiler-core/src/transform.ts b/packages/compiler-core/src/transform.ts
index b21bb73a..474f4915 100644
--- a/packages/compiler-core/src/transform.ts
+++ b/packages/compiler-core/src/transform.ts
@@ -24,7 +24,9 @@ import {
NOOP,
PatchFlags,
PatchFlagNames,
- EMPTY_OBJ
+ EMPTY_OBJ,
+ capitalize,
+ camelize
} from '@vue/shared'
import { defaultOnError } from './errors'
import {
@@ -79,7 +81,9 @@ export interface ImportItem {
path: string
}
-export interface TransformContext extends Required {
+export interface TransformContext
+ extends Required> {
+ selfName: string | null
root: RootNode
helpers: Set
components: Set
@@ -112,6 +116,7 @@ export interface TransformContext extends Required {
export function createTransformContext(
root: RootNode,
{
+ filename = '',
prefixIdentifiers = false,
hoistStatic = false,
cacheHandlers = false,
@@ -130,8 +135,10 @@ export function createTransformContext(
onError = defaultOnError
}: TransformOptions
): TransformContext {
+ const nameMatch = filename.replace(/\?.*$/, '').match(/([^/\\]+)\.\w+$/)
const context: TransformContext = {
// options
+ selfName: nameMatch && capitalize(camelize(nameMatch[1])),
prefixIdentifiers,
hoistStatic,
cacheHandlers,
diff --git a/packages/compiler-core/src/transforms/transformElement.ts b/packages/compiler-core/src/transforms/transformElement.ts
index ca1ae9d1..53b148b4 100644
--- a/packages/compiler-core/src/transforms/transformElement.ts
+++ b/packages/compiler-core/src/transforms/transformElement.ts
@@ -263,7 +263,16 @@ export function resolveComponentType(
}
}
- // 4. user component (resolve)
+ // 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`)
+ }
+ }
+
+ // 5. user component (resolve)
context.helper(RESOLVE_COMPONENT)
context.components.add(tag)
return toValidAssetId(tag, `component`)
diff --git a/packages/compiler-sfc/src/parse.ts b/packages/compiler-sfc/src/parse.ts
index 1df52721..9a065a45 100644
--- a/packages/compiler-sfc/src/parse.ts
+++ b/packages/compiler-sfc/src/parse.ts
@@ -33,7 +33,7 @@ export interface SFCBlock {
export interface SFCTemplateBlock extends SFCBlock {
type: 'template'
- functional?: boolean
+ ast: ElementNode
}
export interface SFCScriptBlock extends SFCBlock {
@@ -79,7 +79,7 @@ export function parse(
source: string,
{
sourceMap = true,
- filename = 'component.vue',
+ filename = 'anonymous.vue',
sourceRoot = '',
pad = false,
compiler = CompilerDOM
@@ -143,31 +143,32 @@ export function parse(
switch (node.tag) {
case 'template':
if (!descriptor.template) {
- descriptor.template = createBlock(
+ const templateBlock = (descriptor.template = createBlock(
node,
source,
false
- ) as SFCTemplateBlock
+ ) as SFCTemplateBlock)
+ templateBlock.ast = node
} else {
errors.push(createDuplicateBlockError(node))
}
break
case 'script':
- const block = createBlock(node, source, pad) as SFCScriptBlock
- const isSetup = !!block.attrs.setup
+ const scriptBlock = createBlock(node, source, pad) as SFCScriptBlock
+ const isSetup = !!scriptBlock.attrs.setup
if (isSetup && !descriptor.scriptSetup) {
- descriptor.scriptSetup = block
+ descriptor.scriptSetup = scriptBlock
break
}
if (!isSetup && !descriptor.script) {
- descriptor.script = block
+ descriptor.script = scriptBlock
break
}
errors.push(createDuplicateBlockError(node, isSetup))
break
case 'style':
- const style = createBlock(node, source, pad) as SFCStyleBlock
- if (style.attrs.vars) {
+ const styleBlock = createBlock(node, source, pad) as SFCStyleBlock
+ if (styleBlock.attrs.vars) {
errors.push(
new SyntaxError(
`