feat(compiler): support v-for on named slots
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
import { isString } from '@vue/shared'
|
||||
import { ForParseResult } from './transforms/vFor'
|
||||
|
||||
// Vue template is a platform-agnostic superset of HTML (syntax only).
|
||||
// More namespaces like SVG and MathML are declared by platform specific
|
||||
@@ -115,6 +116,8 @@ export interface DirectiveNode extends Node {
|
||||
exp: ExpressionNode | undefined
|
||||
arg: ExpressionNode | undefined
|
||||
modifiers: string[]
|
||||
// optional property to cache the expression parse result for v-for
|
||||
parseResult?: ForParseResult
|
||||
}
|
||||
|
||||
export interface SimpleExpressionNode extends Node {
|
||||
@@ -249,13 +252,13 @@ export function createObjectExpression(
|
||||
}
|
||||
|
||||
export function createObjectProperty(
|
||||
key: Property['key'],
|
||||
key: Property['key'] | string,
|
||||
value: Property['value']
|
||||
): Property {
|
||||
return {
|
||||
type: NodeTypes.JS_PROPERTY,
|
||||
loc: locStub,
|
||||
key,
|
||||
key: isString(key) ? createSimpleExpression(key, true) : key,
|
||||
value
|
||||
}
|
||||
}
|
||||
|
||||
@@ -259,17 +259,23 @@ function genHoists(hoists: JSChildNode[], context: CodegenContext) {
|
||||
context.newline()
|
||||
}
|
||||
|
||||
function isText(n: string | CodegenNode) {
|
||||
return (
|
||||
isString(n) ||
|
||||
n.type === NodeTypes.SIMPLE_EXPRESSION ||
|
||||
n.type === NodeTypes.TEXT ||
|
||||
n.type === NodeTypes.INTERPOLATION ||
|
||||
n.type === NodeTypes.COMPOUND_EXPRESSION
|
||||
)
|
||||
}
|
||||
|
||||
function genNodeListAsArray(
|
||||
nodes: (string | CodegenNode | TemplateChildNode[])[],
|
||||
context: CodegenContext
|
||||
) {
|
||||
const multilines =
|
||||
nodes.length > 3 ||
|
||||
((!__BROWSER__ || __DEV__) &&
|
||||
nodes.some(
|
||||
n =>
|
||||
isArray(n) || (!isString(n) && n.type !== NodeTypes.SIMPLE_EXPRESSION)
|
||||
))
|
||||
((!__BROWSER__ || __DEV__) && nodes.some(n => isArray(n) || !isText(n)))
|
||||
context.push(`[`)
|
||||
multilines && context.indent()
|
||||
genNodeList(nodes, context, multilines)
|
||||
@@ -435,6 +441,10 @@ function genCallExpression(node: CallExpression, context: CodegenContext) {
|
||||
function genObjectExpression(node: ObjectExpression, context: CodegenContext) {
|
||||
const { push, indent, deindent, newline, resetMapping } = context
|
||||
const { properties } = node
|
||||
if (!properties.length) {
|
||||
push(`{}`, node)
|
||||
return
|
||||
}
|
||||
const multilines =
|
||||
properties.length > 1 ||
|
||||
((!__BROWSER__ || __DEV__) &&
|
||||
|
||||
@@ -12,7 +12,7 @@ import { transformElement } from './transforms/transformElement'
|
||||
import { transformOn } from './transforms/vOn'
|
||||
import { transformBind } from './transforms/vBind'
|
||||
import { defaultOnError, createCompilerError, ErrorCodes } from './errors'
|
||||
import { trackSlotScopes } from './transforms/vSlot'
|
||||
import { trackSlotScopes, trackVForSlotScopes } from './transforms/vSlot'
|
||||
import { optimizeText } from './transforms/optimizeText'
|
||||
|
||||
export type CompilerOptions = ParserOptions & TransformOptions & CodegenOptions
|
||||
@@ -45,7 +45,14 @@ export function baseCompile(
|
||||
nodeTransforms: [
|
||||
transformIf,
|
||||
transformFor,
|
||||
...(prefixIdentifiers ? [transformExpression, trackSlotScopes] : []),
|
||||
...(prefixIdentifiers
|
||||
? [
|
||||
// order is important
|
||||
trackVForSlotScopes,
|
||||
transformExpression,
|
||||
trackSlotScopes
|
||||
]
|
||||
: []),
|
||||
optimizeText,
|
||||
transformStyle,
|
||||
transformSlotOutlet,
|
||||
|
||||
@@ -14,6 +14,7 @@ export const RESOLVE_DIRECTIVE = `resolveDirective`
|
||||
export const APPLY_DIRECTIVES = `applyDirectives`
|
||||
export const RENDER_LIST = `renderList`
|
||||
export const RENDER_SLOT = `renderSlot`
|
||||
export const CREATE_SLOTS = `createSlots`
|
||||
export const TO_STRING = `toString`
|
||||
export const MERGE_PROPS = `mergeProps`
|
||||
export const TO_HANDLERS = `toHandlers`
|
||||
|
||||
@@ -15,8 +15,7 @@ import {
|
||||
import { isString, isArray } from '@vue/shared'
|
||||
import { CompilerError, defaultOnError } from './errors'
|
||||
import { TO_STRING, COMMENT, CREATE_VNODE, FRAGMENT } from './runtimeConstants'
|
||||
import { createBlockExpression } from './utils'
|
||||
import { isVSlot } from './transforms/vSlot'
|
||||
import { isVSlot, createBlockExpression } from './utils'
|
||||
|
||||
// There are two types of transforms:
|
||||
//
|
||||
|
||||
@@ -26,8 +26,8 @@ import {
|
||||
MERGE_PROPS,
|
||||
TO_HANDLERS
|
||||
} from '../runtimeConstants'
|
||||
import { getInnerRange } from '../utils'
|
||||
import { buildSlots, isVSlot } from './vSlot'
|
||||
import { getInnerRange, isVSlot } from '../utils'
|
||||
import { buildSlots } from './vSlot'
|
||||
|
||||
const toValidId = (str: string): string => str.replace(/[^\w]/g, '')
|
||||
|
||||
@@ -418,7 +418,7 @@ function createDirectiveArgs(
|
||||
createObjectExpression(
|
||||
dir.modifiers.map(modifier =>
|
||||
createObjectProperty(
|
||||
createSimpleExpression(modifier, true, loc),
|
||||
modifier,
|
||||
createSimpleExpression(`true`, false, loc)
|
||||
)
|
||||
),
|
||||
|
||||
@@ -35,11 +35,17 @@ export const transformExpression: NodeTransform = (node, context) => {
|
||||
// handle directives on element
|
||||
for (let i = 0; i < node.props.length; i++) {
|
||||
const dir = node.props[i]
|
||||
if (dir.type === NodeTypes.DIRECTIVE) {
|
||||
// do not process for v-for since it's special handled
|
||||
if (dir.type === NodeTypes.DIRECTIVE && dir.name !== 'for') {
|
||||
const exp = dir.exp as SimpleExpressionNode | undefined
|
||||
const arg = dir.arg as SimpleExpressionNode | undefined
|
||||
if (exp) {
|
||||
dir.exp = processExpression(exp, context, dir.name === 'slot')
|
||||
dir.exp = processExpression(
|
||||
exp,
|
||||
context,
|
||||
// slot args must be processed as function params
|
||||
dir.name === 'slot'
|
||||
)
|
||||
}
|
||||
if (arg && !arg.isStatic) {
|
||||
dir.arg = processExpression(arg, context)
|
||||
|
||||
@@ -90,26 +90,6 @@ export const transformFor = createStructuralDirectiveTransform(
|
||||
}
|
||||
|
||||
// finish the codegen now that all children have been traversed
|
||||
const params: ExpressionNode[] = []
|
||||
if (value) {
|
||||
params.push(value)
|
||||
}
|
||||
if (key) {
|
||||
if (!value) {
|
||||
params.push(createSimpleExpression(`_`, false))
|
||||
}
|
||||
params.push(key)
|
||||
}
|
||||
if (index) {
|
||||
if (!key) {
|
||||
if (!value) {
|
||||
params.push(createSimpleExpression(`_`, false))
|
||||
}
|
||||
params.push(createSimpleExpression(`__`, false))
|
||||
}
|
||||
params.push(index)
|
||||
}
|
||||
|
||||
let childBlock
|
||||
if (node.tagType === ElementTypes.TEMPLATE) {
|
||||
// <template v-for="...">
|
||||
@@ -118,7 +98,7 @@ export const transformFor = createStructuralDirectiveTransform(
|
||||
if (keyProp) {
|
||||
childBlockProps = createObjectExpression([
|
||||
createObjectProperty(
|
||||
createSimpleExpression(`key`, true),
|
||||
`key`,
|
||||
keyProp.type === NodeTypes.ATTRIBUTE
|
||||
? createSimpleExpression(keyProp.value!.content, true)
|
||||
: keyProp.exp!
|
||||
@@ -153,7 +133,7 @@ export const transformFor = createStructuralDirectiveTransform(
|
||||
|
||||
renderExp.arguments.push(
|
||||
createFunctionExpression(
|
||||
params,
|
||||
createForLoopParams(parseResult),
|
||||
childBlock,
|
||||
true /* force newline */
|
||||
)
|
||||
@@ -178,21 +158,21 @@ const forAliasRE = /([\s\S]*?)\s+(?:in|of)\s+([\s\S]*)/
|
||||
const forIteratorRE = /,([^,\}\]]*)(?:,([^,\}\]]*))?$/
|
||||
const stripParensRE = /^\(|\)$/g
|
||||
|
||||
interface ForParseResult {
|
||||
export interface ForParseResult {
|
||||
source: ExpressionNode
|
||||
value: ExpressionNode | undefined
|
||||
key: ExpressionNode | undefined
|
||||
index: ExpressionNode | undefined
|
||||
}
|
||||
|
||||
function parseForExpression(
|
||||
export function parseForExpression(
|
||||
input: SimpleExpressionNode,
|
||||
context: TransformContext
|
||||
): ForParseResult | null {
|
||||
): ForParseResult | undefined {
|
||||
const loc = input.loc
|
||||
const exp = input.content
|
||||
const inMatch = exp.match(forAliasRE)
|
||||
if (!inMatch) return null
|
||||
if (!inMatch) return
|
||||
|
||||
const [, LHS, RHS] = inMatch
|
||||
|
||||
@@ -274,3 +254,30 @@ function createAliasExpression(
|
||||
getInnerRange(range, offset, content.length)
|
||||
)
|
||||
}
|
||||
|
||||
export function createForLoopParams({
|
||||
value,
|
||||
key,
|
||||
index
|
||||
}: ForParseResult): ExpressionNode[] {
|
||||
const params: ExpressionNode[] = []
|
||||
if (value) {
|
||||
params.push(value)
|
||||
}
|
||||
if (key) {
|
||||
if (!value) {
|
||||
params.push(createSimpleExpression(`_`, false))
|
||||
}
|
||||
params.push(key)
|
||||
}
|
||||
if (index) {
|
||||
if (!key) {
|
||||
if (!value) {
|
||||
params.push(createSimpleExpression(`_`, false))
|
||||
}
|
||||
params.push(createSimpleExpression(`__`, false))
|
||||
}
|
||||
params.push(index)
|
||||
}
|
||||
return params
|
||||
}
|
||||
|
||||
@@ -229,8 +229,5 @@ function createChildrenCodegenNode(
|
||||
}
|
||||
|
||||
function createKeyProperty(index: number): Property {
|
||||
return createObjectProperty(
|
||||
createSimpleExpression(`key`, true),
|
||||
createSimpleExpression(index + '', false)
|
||||
)
|
||||
return createObjectProperty(`key`, createSimpleExpression(index + '', false))
|
||||
}
|
||||
|
||||
@@ -15,23 +15,23 @@ import {
|
||||
createConditionalExpression,
|
||||
ConditionalExpression,
|
||||
JSChildNode,
|
||||
SimpleExpressionNode
|
||||
SimpleExpressionNode,
|
||||
FunctionExpression,
|
||||
CallExpression,
|
||||
createCallExpression,
|
||||
createArrayExpression
|
||||
} from '../ast'
|
||||
import { TransformContext, NodeTransform } from '../transform'
|
||||
import { createCompilerError, ErrorCodes } from '../errors'
|
||||
import { mergeExpressions, findDir } from '../utils'
|
||||
|
||||
export const isVSlot = (p: ElementNode['props'][0]): p is DirectiveNode =>
|
||||
p.type === NodeTypes.DIRECTIVE && p.name === 'slot'
|
||||
import { findDir, isTemplateNode, assert, isVSlot } from '../utils'
|
||||
import { CREATE_SLOTS, RENDER_LIST } from '../runtimeConstants'
|
||||
import { parseForExpression, createForLoopParams } from './vFor'
|
||||
|
||||
const isStaticExp = (p: JSChildNode): p is SimpleExpressionNode =>
|
||||
p.type === NodeTypes.SIMPLE_EXPRESSION && p.isStatic
|
||||
|
||||
const defaultFallback = createSimpleExpression(`undefined`, false)
|
||||
|
||||
const hasSameName = (slot: Property, name: string): boolean =>
|
||||
isStaticExp(slot.key) && slot.key.content === name
|
||||
|
||||
// A NodeTransform that tracks scope identifiers for scoped slots so that they
|
||||
// don't get prefixed by transformExpression. This transform is only applied
|
||||
// in non-browser builds with { prefixIdentifiers: true }
|
||||
@@ -41,11 +41,42 @@ export const trackSlotScopes: NodeTransform = (node, context) => {
|
||||
(node.tagType === ElementTypes.COMPONENT ||
|
||||
node.tagType === ElementTypes.TEMPLATE)
|
||||
) {
|
||||
const vSlot = node.props.find(isVSlot)
|
||||
if (vSlot && vSlot.exp) {
|
||||
context.addIdentifiers(vSlot.exp)
|
||||
const vSlot = findDir(node, 'slot')
|
||||
if (vSlot) {
|
||||
const { addIdentifiers, removeIdentifiers } = context
|
||||
const slotProps = vSlot.exp
|
||||
slotProps && addIdentifiers(slotProps)
|
||||
return () => {
|
||||
context.removeIdentifiers(vSlot.exp!)
|
||||
slotProps && removeIdentifiers(slotProps)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// A NodeTransform that tracks scope identifiers for scoped slots with v-for.
|
||||
// This transform is only applied in non-browser builds with { prefixIdentifiers: true }
|
||||
export const trackVForSlotScopes: NodeTransform = (node, context) => {
|
||||
let vFor
|
||||
if (
|
||||
isTemplateNode(node) &&
|
||||
node.props.some(isVSlot) &&
|
||||
(vFor = findDir(node, 'for'))
|
||||
) {
|
||||
const result = (vFor.parseResult = parseForExpression(
|
||||
vFor.exp as SimpleExpressionNode,
|
||||
context
|
||||
))
|
||||
if (result) {
|
||||
const { value, key, index } = result
|
||||
const { addIdentifiers, removeIdentifiers } = context
|
||||
value && addIdentifiers(value)
|
||||
key && addIdentifiers(key)
|
||||
index && addIdentifiers(index)
|
||||
|
||||
return () => {
|
||||
value && removeIdentifiers(value)
|
||||
key && removeIdentifiers(key)
|
||||
index && removeIdentifiers(index)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -54,18 +85,20 @@ export const trackSlotScopes: NodeTransform = (node, context) => {
|
||||
// Instead of being a DirectiveTransform, v-slot processing is called during
|
||||
// transformElement to build the slots object for a component.
|
||||
export function buildSlots(
|
||||
{ props, children, loc }: ElementNode,
|
||||
node: ElementNode,
|
||||
context: TransformContext
|
||||
): {
|
||||
slots: ObjectExpression
|
||||
slots: ObjectExpression | CallExpression
|
||||
hasDynamicSlots: boolean
|
||||
} {
|
||||
const slots: Property[] = []
|
||||
const { children, loc } = node
|
||||
const slotsProperties: Property[] = []
|
||||
const dynamicSlots: (ConditionalExpression | CallExpression)[] = []
|
||||
let hasDynamicSlots = false
|
||||
|
||||
// 1. Check for default slot with slotProps on component itself.
|
||||
// <Comp v-slot="{ prop }"/>
|
||||
const explicitDefaultSlot = props.find(isVSlot)
|
||||
const explicitDefaultSlot = findDir(node, 'slot', true)
|
||||
if (explicitDefaultSlot) {
|
||||
const { arg, exp, loc } = explicitDefaultSlot
|
||||
if (arg) {
|
||||
@@ -73,7 +106,7 @@ export function buildSlots(
|
||||
createCompilerError(ErrorCodes.X_NAMED_SLOT_ON_COMPONENT, loc)
|
||||
)
|
||||
}
|
||||
slots.push(buildDefaultSlot(exp, children, loc))
|
||||
slotsProperties.push(buildDefaultSlot(exp, children, loc))
|
||||
}
|
||||
|
||||
// 2. Iterate through children and check for template slots
|
||||
@@ -86,12 +119,13 @@ export function buildSlots(
|
||||
let slotDir
|
||||
|
||||
if (
|
||||
slotElement.type !== NodeTypes.ELEMENT ||
|
||||
slotElement.tagType !== ElementTypes.TEMPLATE ||
|
||||
!(slotDir = slotElement.props.find(isVSlot))
|
||||
!isTemplateNode(slotElement) ||
|
||||
!(slotDir = findDir(slotElement, 'slot', true))
|
||||
) {
|
||||
// not a <template v-slot>, skip.
|
||||
extraneousChild = extraneousChild || slotElement
|
||||
if (slotElement.type !== NodeTypes.COMMENT && !extraneousChild) {
|
||||
extraneousChild = slotElement
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -126,76 +160,78 @@ export function buildSlots(
|
||||
slotChildren.length ? slotChildren[0].loc : slotLoc
|
||||
)
|
||||
|
||||
// check if this slot is conditional (v-if/else/else-if)
|
||||
// check if this slot is conditional (v-if/v-for)
|
||||
let vIf: DirectiveNode | undefined
|
||||
let vElse: DirectiveNode | undefined
|
||||
let vFor: DirectiveNode | undefined
|
||||
if ((vIf = findDir(slotElement, 'if'))) {
|
||||
hasDynamicSlots = true
|
||||
slots.push(
|
||||
createObjectProperty(
|
||||
slotName,
|
||||
createConditionalExpression(vIf.exp!, slotFunction, defaultFallback)
|
||||
dynamicSlots.push(
|
||||
createConditionalExpression(
|
||||
vIf.exp!,
|
||||
buildDynamicSlot(slotName, slotFunction),
|
||||
defaultFallback
|
||||
)
|
||||
)
|
||||
} else if (
|
||||
(vElse = findDir(slotElement, /^else(-if)?$/, true /* allow empty */))
|
||||
(vElse = findDir(slotElement, /^else(-if)?$/, true /* allowEmpty */))
|
||||
) {
|
||||
hasDynamicSlots = true
|
||||
|
||||
// find adjacent v-if slot
|
||||
let baseIfSlot: Property | undefined
|
||||
let baseIfSlotWithSameName: Property | undefined
|
||||
let i = slots.length
|
||||
while (i--) {
|
||||
if (slots[i].value.type === NodeTypes.JS_CONDITIONAL_EXPRESSION) {
|
||||
baseIfSlot = slots[i]
|
||||
if (staticSlotName && hasSameName(baseIfSlot, staticSlotName)) {
|
||||
baseIfSlotWithSameName = baseIfSlot
|
||||
break
|
||||
}
|
||||
// find adjacent v-if
|
||||
let j = i
|
||||
let prev
|
||||
while (j--) {
|
||||
prev = children[j]
|
||||
if (prev.type !== NodeTypes.COMMENT) {
|
||||
break
|
||||
}
|
||||
}
|
||||
if (!baseIfSlot) {
|
||||
context.onError(
|
||||
createCompilerError(ErrorCodes.X_ELSE_NO_ADJACENT_IF, vElse.loc)
|
||||
)
|
||||
continue
|
||||
}
|
||||
|
||||
if (baseIfSlotWithSameName) {
|
||||
// v-else branch has same slot name with base v-if branch
|
||||
let conditional = baseIfSlotWithSameName.value as ConditionalExpression
|
||||
// locate the deepest conditional in case we have nested ones
|
||||
if (prev && isTemplateNode(prev) && findDir(prev, 'if')) {
|
||||
// remove node
|
||||
children.splice(i, 1)
|
||||
i--
|
||||
__DEV__ && assert(dynamicSlots.length > 0)
|
||||
// attach this slot to previous conditional
|
||||
let conditional = dynamicSlots[
|
||||
dynamicSlots.length - 1
|
||||
] as ConditionalExpression
|
||||
while (
|
||||
conditional.alternate.type === NodeTypes.JS_CONDITIONAL_EXPRESSION
|
||||
) {
|
||||
conditional = conditional.alternate
|
||||
}
|
||||
// attach the v-else branch to the base v-if's conditional expression
|
||||
conditional.alternate = vElse.exp
|
||||
? createConditionalExpression(
|
||||
vElse.exp,
|
||||
slotFunction,
|
||||
buildDynamicSlot(slotName, slotFunction),
|
||||
defaultFallback
|
||||
)
|
||||
: slotFunction
|
||||
: buildDynamicSlot(slotName, slotFunction)
|
||||
} else {
|
||||
// not the same slot name. generate a separate property.
|
||||
slots.push(
|
||||
createObjectProperty(
|
||||
slotName,
|
||||
createConditionalExpression(
|
||||
// negate base branch condition
|
||||
mergeExpressions(
|
||||
`!(`,
|
||||
(baseIfSlot.value as ConditionalExpression).test,
|
||||
`)`,
|
||||
...(vElse.exp ? [` && (`, vElse.exp, `)`] : [])
|
||||
),
|
||||
slotFunction,
|
||||
defaultFallback
|
||||
context.onError(
|
||||
createCompilerError(ErrorCodes.X_ELSE_NO_ADJACENT_IF, vElse.loc)
|
||||
)
|
||||
}
|
||||
} else if ((vFor = findDir(slotElement, 'for'))) {
|
||||
hasDynamicSlots = true
|
||||
const parseResult =
|
||||
vFor.parseResult ||
|
||||
parseForExpression(vFor.exp as SimpleExpressionNode, context)
|
||||
if (parseResult) {
|
||||
// Render the dynamic slots as an array and add it to the createSlot()
|
||||
// args. The runtime knows how to handle it appropriately.
|
||||
dynamicSlots.push(
|
||||
createCallExpression(context.helper(RENDER_LIST), [
|
||||
parseResult.source,
|
||||
createFunctionExpression(
|
||||
createForLoopParams(parseResult),
|
||||
buildDynamicSlot(slotName, slotFunction),
|
||||
true
|
||||
)
|
||||
)
|
||||
])
|
||||
)
|
||||
} else {
|
||||
context.onError(
|
||||
createCompilerError(ErrorCodes.X_FOR_MALFORMED_EXPRESSION, vFor.loc)
|
||||
)
|
||||
}
|
||||
} else {
|
||||
@@ -209,7 +245,7 @@ export function buildSlots(
|
||||
}
|
||||
seenSlotNames.add(staticSlotName)
|
||||
}
|
||||
slots.push(createObjectProperty(slotName, slotFunction))
|
||||
slotsProperties.push(createObjectProperty(slotName, slotFunction))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -224,11 +260,22 @@ export function buildSlots(
|
||||
|
||||
if (!explicitDefaultSlot && !hasTemplateSlots) {
|
||||
// implicit default slot.
|
||||
slots.push(buildDefaultSlot(undefined, children, loc))
|
||||
slotsProperties.push(buildDefaultSlot(undefined, children, loc))
|
||||
}
|
||||
|
||||
let slots: ObjectExpression | CallExpression = createObjectExpression(
|
||||
slotsProperties,
|
||||
loc
|
||||
)
|
||||
if (dynamicSlots.length) {
|
||||
slots = createCallExpression(context.helper(CREATE_SLOTS), [
|
||||
slots,
|
||||
createArrayExpression(dynamicSlots)
|
||||
])
|
||||
}
|
||||
|
||||
return {
|
||||
slots: createObjectExpression(slots, loc),
|
||||
slots,
|
||||
hasDynamicSlots
|
||||
}
|
||||
}
|
||||
@@ -239,7 +286,7 @@ function buildDefaultSlot(
|
||||
loc: SourceLocation
|
||||
): Property {
|
||||
return createObjectProperty(
|
||||
createSimpleExpression(`default`, true),
|
||||
`default`,
|
||||
createFunctionExpression(
|
||||
slotProps,
|
||||
children,
|
||||
@@ -248,3 +295,13 @@ function buildDefaultSlot(
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
function buildDynamicSlot(
|
||||
name: ExpressionNode,
|
||||
fn: FunctionExpression
|
||||
): ObjectExpression {
|
||||
return createObjectExpression([
|
||||
createObjectProperty(`name`, name),
|
||||
createObjectProperty(`fn`, fn)
|
||||
])
|
||||
}
|
||||
|
||||
@@ -7,10 +7,10 @@ import {
|
||||
SequenceExpression,
|
||||
createSequenceExpression,
|
||||
createCallExpression,
|
||||
ExpressionNode,
|
||||
CompoundExpressionNode,
|
||||
createCompoundExpression,
|
||||
DirectiveNode
|
||||
DirectiveNode,
|
||||
ElementTypes,
|
||||
TemplateChildNode,
|
||||
RootNode
|
||||
} from './ast'
|
||||
import { parse } from 'acorn'
|
||||
import { walk } from 'estree-walker'
|
||||
@@ -121,7 +121,7 @@ export function findDir(
|
||||
if (
|
||||
p.type === NodeTypes.DIRECTIVE &&
|
||||
(allowEmpty || p.exp) &&
|
||||
p.name.match(name)
|
||||
(isString(name) ? p.name === name : name.test(p.name))
|
||||
) {
|
||||
return p
|
||||
}
|
||||
@@ -160,17 +160,10 @@ export function createBlockExpression(
|
||||
])
|
||||
}
|
||||
|
||||
export function mergeExpressions(
|
||||
...args: (string | ExpressionNode)[]
|
||||
): CompoundExpressionNode {
|
||||
const children: CompoundExpressionNode['children'] = []
|
||||
for (let i = 0; i < args.length; i++) {
|
||||
const exp = args[i]
|
||||
if (isString(exp) || exp.type === NodeTypes.SIMPLE_EXPRESSION) {
|
||||
children.push(exp)
|
||||
} else {
|
||||
children.push(...exp.children)
|
||||
}
|
||||
}
|
||||
return createCompoundExpression(children)
|
||||
}
|
||||
export const isVSlot = (p: ElementNode['props'][0]): p is DirectiveNode =>
|
||||
p.type === NodeTypes.DIRECTIVE && p.name === 'slot'
|
||||
|
||||
export const isTemplateNode = (
|
||||
node: RootNode | TemplateChildNode
|
||||
): node is ElementNode =>
|
||||
node.type === NodeTypes.ELEMENT && node.tagType === ElementTypes.TEMPLATE
|
||||
|
||||
Reference in New Issue
Block a user