refactor: improve vSlot.ts readability

This commit is contained in:
Evan You 2019-10-02 18:03:42 -04:00
parent 35cb3700b8
commit f401ac6b88
2 changed files with 63 additions and 52 deletions

View File

@ -19,7 +19,7 @@ import {
} from '../ast' } from '../ast'
import { TransformContext, NodeTransform } from '../transform' import { TransformContext, NodeTransform } from '../transform'
import { createCompilerError, ErrorCodes } from '../errors' import { createCompilerError, ErrorCodes } from '../errors'
import { mergeExpressions, findNonEmptyDir } from '../utils' import { mergeExpressions, findDir } from '../utils'
export const isVSlot = (p: ElementNode['props'][0]): p is DirectiveNode => export const isVSlot = (p: ElementNode['props'][0]): p is DirectiveNode =>
p.type === NodeTypes.DIRECTIVE && p.name === 'slot' p.type === NodeTypes.DIRECTIVE && p.name === 'slot'
@ -29,6 +29,9 @@ const isStaticExp = (p: JSChildNode): p is SimpleExpressionNode =>
const defaultFallback = createSimpleExpression(`undefined`, false) 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 // A NodeTransform that tracks scope identifiers for scoped slots so that they
// don't get prefixed by transformExpression. This transform is only applied // don't get prefixed by transformExpression. This transform is only applied
// in non-browser builds with { prefixIdentifiers: true } // in non-browser builds with { prefixIdentifiers: true }
@ -109,7 +112,7 @@ export function buildSlots(
} = slotDir } = slotDir
// check if name is dynamic. // check if name is dynamic.
let staticSlotName let staticSlotName: string | undefined
if (isStaticExp(slotName)) { if (isStaticExp(slotName)) {
staticSlotName = slotName ? slotName.content : `default` staticSlotName = slotName ? slotName.content : `default`
} else { } else {
@ -124,9 +127,9 @@ export function buildSlots(
) )
// check if this slot is conditional (v-if/else/else-if) // check if this slot is conditional (v-if/else/else-if)
let vIf let vIf: DirectiveNode | undefined
let vElse let vElse: DirectiveNode | undefined
if ((vIf = findNonEmptyDir(slotElement, 'if'))) { if ((vIf = findDir(slotElement, 'if'))) {
hasDynamicSlots = true hasDynamicSlots = true
slots.push( slots.push(
createObjectProperty( createObjectProperty(
@ -134,29 +137,41 @@ export function buildSlots(
createConditionalExpression(vIf.exp!, slotFunction, defaultFallback) createConditionalExpression(vIf.exp!, slotFunction, defaultFallback)
) )
) )
} else if ((vElse = findNonEmptyDir(slotElement, /^else(-if)?$/))) { } else if (
(vElse = findDir(slotElement, /^else(-if)?$/, true /* allow empty */))
) {
hasDynamicSlots = true hasDynamicSlots = true
// find adjacent v-if slot // find adjacent v-if slot
let vIfBase let baseIfSlot: Property | undefined
let baseIfSlotWithSameName: Property | undefined
let i = slots.length let i = slots.length
while (i--) { while (i--) {
if (slots[i].value.type === NodeTypes.JS_CONDITIONAL_EXPRESSION) { if (slots[i].value.type === NodeTypes.JS_CONDITIONAL_EXPRESSION) {
vIfBase = slots[i] baseIfSlot = slots[i]
if (staticSlotName && hasSameName(baseIfSlot, staticSlotName)) {
baseIfSlotWithSameName = baseIfSlot
break break
} }
} }
if (vIfBase) { }
// check if the v-else and the base v-if has the same slot name if (!baseIfSlot) {
if ( context.onError(
isStaticExp(vIfBase.key) && createCompilerError(ErrorCodes.X_ELSE_NO_ADJACENT_IF, vElse.loc)
vIfBase.key.content === staticSlotName )
) { continue
let conditional = vIfBase.value as ConditionalExpression }
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
while ( while (
conditional.alternate.type === NodeTypes.JS_CONDITIONAL_EXPRESSION conditional.alternate.type === NodeTypes.JS_CONDITIONAL_EXPRESSION
) { ) {
conditional = conditional.alternate conditional = conditional.alternate
} }
// attach the v-else branch to the base v-if's conditional expression
conditional.alternate = vElse.exp conditional.alternate = vElse.exp
? createConditionalExpression( ? createConditionalExpression(
vElse.exp, vElse.exp,
@ -170,10 +185,10 @@ export function buildSlots(
createObjectProperty( createObjectProperty(
slotName, slotName,
createConditionalExpression( createConditionalExpression(
// negate baseVIf // negate base branch condition
mergeExpressions( mergeExpressions(
`!(`, `!(`,
(vIfBase.value as ConditionalExpression).test, (baseIfSlot.value as ConditionalExpression).test,
`)`, `)`,
...(vElse.exp ? [` && (`, vElse.exp, `)`] : []) ...(vElse.exp ? [` && (`, vElse.exp, `)`] : [])
), ),
@ -183,11 +198,6 @@ export function buildSlots(
) )
) )
} }
} else {
context.onError(
createCompilerError(ErrorCodes.X_ELSE_NO_ADJACENT_IF, vElse.loc)
)
}
} else { } else {
// check duplicate static names // check duplicate static names
if (staticSlotName) { if (staticSlotName) {

View File

@ -111,16 +111,17 @@ export function assert(condition: boolean, msg?: string) {
} }
} }
export function findNonEmptyDir( export function findDir(
node: ElementNode, node: ElementNode,
name: string | RegExp name: string | RegExp,
allowEmpty: boolean = false
): DirectiveNode | undefined { ): DirectiveNode | undefined {
for (let i = 0; i < node.props.length; i++) { for (let i = 0; i < node.props.length; i++) {
const p = node.props[i] const p = node.props[i]
if ( if (
p.type === NodeTypes.DIRECTIVE && p.type === NodeTypes.DIRECTIVE &&
p.exp && (allowEmpty || p.exp) &&
(isString(name) ? p.name === name : name.test(p.name)) p.name.match(name)
) { ) {
return p return p
} }