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'
import { TransformContext, NodeTransform } from '../transform'
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 =>
p.type === NodeTypes.DIRECTIVE && p.name === 'slot'
@ -29,6 +29,9 @@ const isStaticExp = (p: JSChildNode): p is SimpleExpressionNode =>
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 }
@ -109,7 +112,7 @@ export function buildSlots(
} = slotDir
// check if name is dynamic.
let staticSlotName
let staticSlotName: string | undefined
if (isStaticExp(slotName)) {
staticSlotName = slotName ? slotName.content : `default`
} else {
@ -124,9 +127,9 @@ export function buildSlots(
)
// check if this slot is conditional (v-if/else/else-if)
let vIf
let vElse
if ((vIf = findNonEmptyDir(slotElement, 'if'))) {
let vIf: DirectiveNode | undefined
let vElse: DirectiveNode | undefined
if ((vIf = findDir(slotElement, 'if'))) {
hasDynamicSlots = true
slots.push(
createObjectProperty(
@ -134,29 +137,41 @@ export function buildSlots(
createConditionalExpression(vIf.exp!, slotFunction, defaultFallback)
)
)
} else if ((vElse = findNonEmptyDir(slotElement, /^else(-if)?$/))) {
} else if (
(vElse = findDir(slotElement, /^else(-if)?$/, true /* allow empty */))
) {
hasDynamicSlots = true
// find adjacent v-if slot
let vIfBase
let baseIfSlot: Property | undefined
let baseIfSlotWithSameName: Property | undefined
let i = slots.length
while (i--) {
if (slots[i].value.type === NodeTypes.JS_CONDITIONAL_EXPRESSION) {
vIfBase = slots[i]
baseIfSlot = slots[i]
if (staticSlotName && hasSameName(baseIfSlot, staticSlotName)) {
baseIfSlotWithSameName = baseIfSlot
break
}
}
if (vIfBase) {
// check if the v-else and the base v-if has the same slot name
if (
isStaticExp(vIfBase.key) &&
vIfBase.key.content === staticSlotName
) {
let conditional = vIfBase.value as ConditionalExpression
}
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
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,
@ -170,10 +185,10 @@ export function buildSlots(
createObjectProperty(
slotName,
createConditionalExpression(
// negate baseVIf
// negate base branch condition
mergeExpressions(
`!(`,
(vIfBase.value as ConditionalExpression).test,
(baseIfSlot.value as ConditionalExpression).test,
`)`,
...(vElse.exp ? [` && (`, vElse.exp, `)`] : [])
),
@ -183,11 +198,6 @@ export function buildSlots(
)
)
}
} else {
context.onError(
createCompilerError(ErrorCodes.X_ELSE_NO_ADJACENT_IF, vElse.loc)
)
}
} else {
// check duplicate static names
if (staticSlotName) {

View File

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