|
|
@@ -6,22 +6,127 @@ import {
|
|
|
createCompoundExpression,
|
|
|
createCallExpression,
|
|
|
CompoundExpressionNode,
|
|
|
- CallExpression
|
|
|
+ CallExpression,
|
|
|
+ createObjectProperty,
|
|
|
+ createSimpleExpression,
|
|
|
+ createFunctionExpression,
|
|
|
+ DirectiveNode,
|
|
|
+ ElementTypes,
|
|
|
+ ExpressionNode,
|
|
|
+ Property,
|
|
|
+ ChildNode,
|
|
|
+ SourceLocation
|
|
|
} from '../ast'
|
|
|
import { TransformContext } from '../transform'
|
|
|
import { buildProps } from './transformElement'
|
|
|
import { createCompilerError, ErrorCodes } from '../errors'
|
|
|
import { isSimpleIdentifier } from '../utils'
|
|
|
import { RENDER_SLOT } from '../runtimeConstants'
|
|
|
+import { isString } from '@vue/shared'
|
|
|
+
|
|
|
+const isVSlot = (p: ElementNode['props'][0]): p is DirectiveNode =>
|
|
|
+ p.type === NodeTypes.DIRECTIVE && p.name === 'slot'
|
|
|
|
|
|
export function buildSlots(
|
|
|
- { loc, children }: ElementNode,
|
|
|
+ { props, children, loc }: ElementNode,
|
|
|
context: TransformContext
|
|
|
): ObjectExpression {
|
|
|
- const slots = createObjectExpression([], loc)
|
|
|
- // TODO
|
|
|
+ const slots: Property[] = []
|
|
|
+
|
|
|
+ // 1. Check for default slot with slotProps on component itself.
|
|
|
+ // <Comp v-slot="{ prop }"/>
|
|
|
+ const explicitDefaultSlot = props.find(isVSlot)
|
|
|
+ if (explicitDefaultSlot) {
|
|
|
+ const { arg, exp, loc } = explicitDefaultSlot
|
|
|
+ if (arg) {
|
|
|
+ context.onError(
|
|
|
+ createCompilerError(ErrorCodes.X_NAMED_SLOT_ON_COMPONENT, loc)
|
|
|
+ )
|
|
|
+ }
|
|
|
+ slots.push(buildSlot(`default`, exp, children, loc))
|
|
|
+ }
|
|
|
|
|
|
- return slots
|
|
|
+ // 2. Iterate through children and check for template slots
|
|
|
+ // <template v-slot:foo="{ prop }">
|
|
|
+ let hasTemplateSlots = false
|
|
|
+ const seenSlotNames = new Set<string>()
|
|
|
+ const nonSlotChildren: ChildNode[] = []
|
|
|
+ for (let i = 0; i < children.length; i++) {
|
|
|
+ const child = children[i]
|
|
|
+ if (
|
|
|
+ child.type === NodeTypes.ELEMENT &&
|
|
|
+ child.tagType === ElementTypes.TEMPLATE
|
|
|
+ ) {
|
|
|
+ const { props, children, loc: nodeLoc } = child
|
|
|
+ const slotDir = props.find(isVSlot)
|
|
|
+ if (slotDir) {
|
|
|
+ hasTemplateSlots = true
|
|
|
+ const { arg: slotName, exp: slotProps, loc: dirLoc } = slotDir
|
|
|
+ if (explicitDefaultSlot) {
|
|
|
+ // already has on-component default slot - this is incorrect usage.
|
|
|
+ context.onError(
|
|
|
+ createCompilerError(ErrorCodes.X_MIXED_SLOT_USAGE, dirLoc)
|
|
|
+ )
|
|
|
+ break
|
|
|
+ } else {
|
|
|
+ // check duplicate slot names
|
|
|
+ if (
|
|
|
+ !slotName ||
|
|
|
+ (slotName.type === NodeTypes.SIMPLE_EXPRESSION && slotName.isStatic)
|
|
|
+ ) {
|
|
|
+ const name = slotName ? slotName.content : `default`
|
|
|
+ if (seenSlotNames.has(name)) {
|
|
|
+ context.onError(
|
|
|
+ createCompilerError(ErrorCodes.X_DUPLICATE_SLOT_NAMES, dirLoc)
|
|
|
+ )
|
|
|
+ continue
|
|
|
+ }
|
|
|
+ seenSlotNames.add(name)
|
|
|
+ }
|
|
|
+ slots.push(
|
|
|
+ buildSlot(slotName || `default`, slotProps, children, nodeLoc)
|
|
|
+ )
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ nonSlotChildren.push(child)
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ nonSlotChildren.push(child)
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (hasTemplateSlots && nonSlotChildren.length) {
|
|
|
+ context.onError(
|
|
|
+ createCompilerError(
|
|
|
+ ErrorCodes.X_EXTRANEOUS_NON_SLOT_CHILDREN,
|
|
|
+ nonSlotChildren[0].loc
|
|
|
+ )
|
|
|
+ )
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!explicitDefaultSlot && !hasTemplateSlots) {
|
|
|
+ // implicit default slot.
|
|
|
+ slots.push(buildSlot(`default`, undefined, children, loc))
|
|
|
+ }
|
|
|
+
|
|
|
+ return createObjectExpression(slots, loc)
|
|
|
+}
|
|
|
+
|
|
|
+function buildSlot(
|
|
|
+ name: string | ExpressionNode,
|
|
|
+ slotProps: ExpressionNode | undefined,
|
|
|
+ children: ChildNode[],
|
|
|
+ loc: SourceLocation
|
|
|
+): Property {
|
|
|
+ return createObjectProperty(
|
|
|
+ isString(name) ? createSimpleExpression(name, true, loc) : name,
|
|
|
+ createFunctionExpression(
|
|
|
+ slotProps,
|
|
|
+ children,
|
|
|
+ children.length ? children[0].loc : loc
|
|
|
+ ),
|
|
|
+ loc
|
|
|
+ )
|
|
|
}
|
|
|
|
|
|
export function buildSlotOutlet(node: ElementNode, context: TransformContext) {
|
|
|
@@ -84,7 +189,10 @@ export function buildSlotOutlet(node: ElementNode, context: TransformContext) {
|
|
|
)
|
|
|
if (directives.length) {
|
|
|
context.onError(
|
|
|
- createCompilerError(ErrorCodes.X_UNEXPECTED_DIRECTIVE_ON_SLOT_OUTLET)
|
|
|
+ createCompilerError(
|
|
|
+ ErrorCodes.X_UNEXPECTED_DIRECTIVE_ON_SLOT_OUTLET,
|
|
|
+ directives[0].loc
|
|
|
+ )
|
|
|
)
|
|
|
}
|
|
|
slotArgs.push(propsExpression)
|