Skip to content

Commit 9db436d

Browse files
committed
feat(compiler): support native v-slot directive
1 parent f5af4ae commit 9db436d

22 files changed

+802
-303
lines changed

packages/compiler/src/compile.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import { transformText } from './transforms/transformText'
1717
import { transformVBind } from './transforms/vBind'
1818
import { transformVOn } from './transforms/vOn'
1919
import { transformVSlot } from './transforms/vSlot'
20+
import { transformVSlots } from './transforms/vSlots'
2021
import { transformVModel } from './transforms/vModel'
2122
import { transformVShow } from './transforms/vShow'
2223
import { transformVHtml } from './transforms/vHtml'
@@ -113,6 +114,7 @@ export function getBaseTransformPreset(): TransformPreset {
113114
transformTemplateRef,
114115
transformText,
115116
transformElement,
117+
transformVSlots,
116118
transformVSlot,
117119
transformChildren,
118120
],

packages/compiler/src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ export { transformTemplateRef } from './transforms/transformTemplateRef'
1717
export { transformVBind } from './transforms/vBind'
1818
export { transformVOn } from './transforms/vOn'
1919
export { transformVSlot } from './transforms/vSlot'
20+
export { transformVSlots } from './transforms/vSlots'
2021
export { transformVModel } from './transforms/vModel'
2122
export { transformVShow } from './transforms/vShow'
2223
export { transformVHtml } from './transforms/vHtml'

packages/compiler/src/transform.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ import {
1919
type RootNode,
2020
} from './ir'
2121
import { newBlock, newDynamic } from './transforms/utils'
22-
import { findProp, getText, isConstantExpression } from './utils'
22+
import { findProp, getText, isConstantExpression, isTemplate } from './utils'
2323
import type { JSXAttribute, JSXElement, JSXFragment } from '@babel/types'
2424

2525
export type NodeTransform = (
@@ -267,11 +267,11 @@ export function createStructuralDirectiveTransform(
267267
return (node, context) => {
268268
if (node.type === 'JSXElement') {
269269
const {
270-
openingElement: { attributes, name },
270+
openingElement: { attributes },
271271
} = node
272272
// structural directive transforms are not concerned with slots
273273
// as they are handled separately in vSlot.ts
274-
if (getText(name, context) === 'template' && findProp(node, 'v-slot')) {
274+
if (isTemplate(node) && findProp(node, 'v-slot')) {
275275
return
276276
}
277277
const exitFns = []

packages/compiler/src/transforms/transformChildren.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,14 @@ import {
44
type TransformContext,
55
transformNode,
66
} from '../transform'
7-
import { isJSXComponent } from '../utils'
7+
import { isJSXComponent, isTemplate } from '../utils'
88
import type { Node } from '@babel/types'
99

1010
export const transformChildren: NodeTransform = (node, context) => {
1111
const isFragment =
1212
node.type === IRNodeTypes.ROOT ||
1313
node.type === 'JSXFragment' ||
14-
isJSXComponent(node)
14+
(node.type === 'JSXElement' && (isTemplate(node) || isJSXComponent(node)))
1515

1616
if (node.type !== 'JSXElement' && !isFragment) return
1717

packages/compiler/src/transforms/transformElement.ts

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import {
1414
} from '../ir'
1515
import {
1616
isJSXComponent,
17+
isTemplate,
1718
resolveExpression,
1819
resolveSimpleExpression,
1920
} from '../utils'
@@ -36,7 +37,7 @@ const isDirectiveRegex = /^v-[a-z]/
3637
export const transformElement: NodeTransform = (node, context) => {
3738
return function postTransformElement() {
3839
;({ node } = context)
39-
if (node.type !== 'JSXElement') return
40+
if (node.type !== 'JSXElement' || isTemplate(node)) return
4041

4142
const {
4243
openingElement: { name },
@@ -54,10 +55,19 @@ export const transformElement: NodeTransform = (node, context) => {
5455
isComponent,
5556
)
5657

58+
let { parent } = context
59+
while (
60+
parent &&
61+
parent.parent &&
62+
parent.node.type === 'JSXElement' &&
63+
isTemplate(parent.node)
64+
) {
65+
parent = parent.parent
66+
}
5767
const singleRoot =
58-
context.root === context.parent &&
59-
context.parent.node.children.filter((child) => !isJSXComponent(child))
60-
.length === 1
68+
context.root === parent &&
69+
parent.node.children.filter((child) => !isJSXComponent(child)).length ===
70+
1
6171
;(isComponent ? transformComponentElement : transformNativeElement)(
6272
tag,
6373
propsResult,
@@ -248,6 +258,7 @@ function transformProp(
248258
: prop.name.type === 'JSXNamespacedName'
249259
? prop.name.namespace.name
250260
: ''
261+
name = name.split('_')[0]
251262

252263
if (
253264
!isDirectiveRegex.test(name) &&

packages/compiler/src/transforms/transformText.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import {
88
getLiteralExpressionValue,
99
isEmptyText,
1010
isJSXComponent,
11+
isTemplate,
1112
resolveExpression,
1213
resolveJSXText,
1314
} from '../utils'
@@ -35,6 +36,7 @@ export const transformText: NodeTransform = (node, context) => {
3536

3637
if (
3738
node.type === 'JSXElement' &&
39+
!isTemplate(node) &&
3840
!(isJSXComponent(node) as boolean) &&
3941
isAllTextLike(node.children)
4042
) {

packages/compiler/src/transforms/utils.ts

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import {
1414
type IRDynamicInfo,
1515
type IRNodeTypes,
1616
} from '../ir/index'
17+
import { isTemplate } from '../utils'
1718
import type { TransformContext } from '../transform'
1819

1920
export function newDynamic(): IRDynamicInfo {
@@ -48,10 +49,8 @@ export function createBranch(
4849
return [branch, exitBlock]
4950
}
5051

51-
export function wrapFragment(
52-
node: JSXElement | JSXFragment | Expression,
53-
): JSXFragment {
54-
if (node.type === 'JSXFragment') {
52+
export function wrapFragment(node: JSXElement | JSXFragment | Expression) {
53+
if (node.type === 'JSXFragment' || isTemplate(node)) {
5554
return node
5655
}
5756

packages/compiler/src/transforms/vFor.ts

Lines changed: 43 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -2,27 +2,24 @@ import {
22
ErrorCodes,
33
type SimpleExpressionNode,
44
createCompilerError,
5-
forAliasRE,
65
isConstantNode,
76
} from '@vue/compiler-dom'
8-
import { parseExpression } from '@babel/parser'
97
import { DynamicFlag, IRNodeTypes } from '../ir'
108
import {
119
findProp,
12-
getText,
1310
isJSXComponent,
1411
propToExpression,
1512
resolveExpression,
13+
resolveExpressionWithFn,
1614
resolveLocation,
17-
resolveSimpleExpression,
1815
} from '../utils'
1916
import {
2017
type NodeTransform,
2118
type TransformContext,
2219
createStructuralDirectiveTransform,
2320
} from '../transform'
2421
import { createBranch } from './utils'
25-
import type { JSXAttribute, JSXElement, Node } from '@babel/types'
22+
import type { JSXAttribute, JSXElement } from '@babel/types'
2623

2724
export const transformVFor: NodeTransform = createStructuralDirectiveTransform(
2825
'for',
@@ -34,16 +31,8 @@ export function processFor(
3431
dir: JSXAttribute,
3532
context: TransformContext,
3633
) {
37-
if (!dir.value) {
38-
context.options.onError(
39-
createCompilerError(
40-
ErrorCodes.X_V_FOR_NO_EXPRESSION,
41-
resolveLocation(dir.loc, context),
42-
),
43-
)
44-
return
45-
}
46-
if (!forAliasRE) {
34+
const { value, index, key, source } = getForParseResult(dir, context)
35+
if (!source) {
4736
context.options.onError(
4837
createCompilerError(
4938
ErrorCodes.X_V_FOR_MALFORMED_EXPRESSION,
@@ -53,25 +42,6 @@ export function processFor(
5342
return
5443
}
5544

56-
let value: SimpleExpressionNode | undefined,
57-
index: SimpleExpressionNode | undefined,
58-
key: SimpleExpressionNode | undefined,
59-
source: SimpleExpressionNode
60-
if (
61-
dir.value.type === 'JSXExpressionContainer' &&
62-
dir.value.expression.type === 'BinaryExpression'
63-
) {
64-
if (dir.value.expression.left.type === 'SequenceExpression') {
65-
const expressions = dir.value.expression.left.expressions
66-
value = expressions[0] && resolveValueExpression(expressions[0], context)
67-
key = expressions[1] && resolveExpression(expressions[1], context)
68-
index = expressions[2] && resolveExpression(expressions[2], context)
69-
} else {
70-
value = resolveValueExpression(dir.value.expression.left, context)
71-
}
72-
source = resolveExpression(dir.value.expression.right, context)
73-
}
74-
7545
const keyProp = findProp(node, 'key')
7646
const keyProperty = keyProp && propToExpression(keyProp, context)
7747
const isComponent = isJSXComponent(node)
@@ -106,16 +76,43 @@ export function processFor(
10676
}
10777
}
10878

109-
function resolveValueExpression(node: Node, context: TransformContext) {
110-
const text = getText(node, context)
111-
return node.type === 'Identifier'
112-
? resolveSimpleExpression(text, false, node.loc)
113-
: resolveSimpleExpression(
114-
text,
115-
false,
116-
node.loc,
117-
parseExpression(`(${text})=>{}`, {
118-
plugins: ['typescript'],
119-
}),
120-
)
79+
export function getForParseResult(
80+
dir: JSXAttribute,
81+
context: TransformContext,
82+
) {
83+
let value: SimpleExpressionNode | undefined,
84+
index: SimpleExpressionNode | undefined,
85+
key: SimpleExpressionNode | undefined,
86+
source: SimpleExpressionNode | undefined
87+
if (dir.value) {
88+
if (
89+
dir.value.type === 'JSXExpressionContainer' &&
90+
dir.value.expression.type === 'BinaryExpression'
91+
) {
92+
if (dir.value.expression.left.type === 'SequenceExpression') {
93+
const expressions = dir.value.expression.left.expressions
94+
value =
95+
expressions[0] && resolveExpressionWithFn(expressions[0], context)
96+
key = expressions[1] && resolveExpression(expressions[1], context)
97+
index = expressions[2] && resolveExpression(expressions[2], context)
98+
} else {
99+
value = resolveExpressionWithFn(dir.value.expression.left, context)
100+
}
101+
source = resolveExpression(dir.value.expression.right, context)
102+
}
103+
} else {
104+
context.options.onError(
105+
createCompilerError(
106+
ErrorCodes.X_V_FOR_NO_EXPRESSION,
107+
resolveLocation(dir.loc, context),
108+
),
109+
)
110+
}
111+
112+
return {
113+
value,
114+
index,
115+
key,
116+
source,
117+
}
121118
}

0 commit comments

Comments
 (0)