wip: element transforms

This commit is contained in:
Evan You
2019-09-21 15:47:26 -04:00
parent b275f8697d
commit 93440bba97
18 changed files with 158 additions and 19 deletions

View File

@@ -0,0 +1,87 @@
import { Transform, TransformContext } from '../transform'
import {
NodeTypes,
ElementTypes,
CallExpression,
ObjectExpression,
ElementNode
} from '../ast'
// generate a JavaScript AST for this element's codegen
export const prepareElementForCodegen: Transform = (node, context) => {
if (node.type === NodeTypes.ELEMENT) {
if (
node.tagType === ElementTypes.ELEMENT ||
node.tagType === ElementTypes.COMPONENT
) {
const isComponent = node.tagType === ElementTypes.ELEMENT
const hasProps = node.attrs.length > 0 || node.directives.length > 0
const hasChildren = node.children.length > 0
const args: CallExpression['arguments'] = [
isComponent ? node.tag : `"${node.tag}"`
]
// props
if (hasProps) {
args.push(buildProps(node))
}
// children
if (hasChildren) {
if (!hasProps) {
// placeholder for null props, but use `0` for more condense code
args.push(`0`)
}
args.push(isComponent ? buildSlots(node, context) : node.children)
}
node.codegenNode = {
type: NodeTypes.CALL_EXPRESSION,
loc: node.loc,
callee: `h`,
arguments: args
}
}
}
}
function buildProps({ loc, attrs }: ElementNode): ObjectExpression {
return {
type: NodeTypes.OBJECT_EXPRESSION,
loc,
// At this stage we will only process static attrs. Directive bindings will
// be handled by their respective transforms which adds/modifies the props.
properties: attrs.map(({ name, value, loc }) => {
return {
type: NodeTypes.PROPERTY,
loc,
key: {
type: NodeTypes.EXPRESSION,
loc,
content: name,
isStatic: true
},
value: {
type: NodeTypes.EXPRESSION,
loc: value ? value.loc : loc,
content: value ? value.content : '',
isStatic: true
}
}
})
}
}
function buildSlots(
{ loc, children }: ElementNode,
context: TransformContext
): ObjectExpression {
const slots: ObjectExpression = {
type: NodeTypes.OBJECT_EXPRESSION,
loc,
properties: []
}
// TODO
return slots
}

View File

@@ -0,0 +1 @@
// TODO

View File

@@ -0,0 +1 @@
// TODO

View File

@@ -0,0 +1,2 @@
// TODO merge adjacent text nodes and expressions into a single expression
// e.g. <div>abc {{ d }} {{ e }}</div> should have a single expression node as child.

View File

@@ -0,0 +1 @@
// TODO

View File

@@ -0,0 +1 @@
// TODO

View File

@@ -0,0 +1 @@
// TODO

View File

@@ -0,0 +1,128 @@
import { createDirectiveTransform } from '../transform'
import { NodeTypes, ExpressionNode } from '../ast'
import { createCompilerError, ErrorCodes } from '../errors'
import { getInnerRange } from '../utils'
const forAliasRE = /([\s\S]*?)(?:(?<=\))|\s+)(?:in|of)\s+([\s\S]*)/
const forIteratorRE = /,([^,\}\]]*)(?:,([^,\}\]]*))?$/
const stripParensRE = /^\(|\)$/g
export const transformFor = createDirectiveTransform(
'for',
(node, dir, context) => {
if (dir.exp) {
const aliases = parseAliasExpressions(dir.exp.content)
if (aliases) {
context.replaceNode({
type: NodeTypes.FOR,
loc: node.loc,
source: maybeCreateExpression(
aliases.source,
dir.exp
) as ExpressionNode,
valueAlias: maybeCreateExpression(aliases.value, dir.exp),
keyAlias: maybeCreateExpression(aliases.key, dir.exp),
objectIndexAlias: maybeCreateExpression(aliases.index, dir.exp),
children: [node]
})
} else {
context.emitError(
createCompilerError(
ErrorCodes.X_FOR_MALFORMED_EXPRESSION,
dir.loc.start
)
)
}
} else {
context.emitError(
createCompilerError(ErrorCodes.X_FOR_NO_EXPRESSION, dir.loc.start)
)
}
}
)
interface AliasExpression {
offset: number
content: string
}
interface AliasExpressions {
source: AliasExpression
value: AliasExpression | undefined
key: AliasExpression | undefined
index: AliasExpression | undefined
}
function parseAliasExpressions(source: string): AliasExpressions | null {
const inMatch = source.match(forAliasRE)
if (!inMatch) return null
const [, LHS, RHS] = inMatch
const result: AliasExpressions = {
source: {
offset: source.indexOf(RHS, LHS.length),
content: RHS.trim()
},
value: undefined,
key: undefined,
index: undefined
}
let valueContent = LHS.trim()
.replace(stripParensRE, '')
.trim()
const trimmedOffset = LHS.indexOf(valueContent)
const iteratorMatch = valueContent.match(forIteratorRE)
if (iteratorMatch) {
valueContent = valueContent.replace(forIteratorRE, '').trim()
const keyContent = iteratorMatch[1].trim()
if (keyContent) {
result.key = {
offset: source.indexOf(keyContent, trimmedOffset + valueContent.length),
content: keyContent
}
}
if (iteratorMatch[2]) {
const indexContent = iteratorMatch[2].trim()
if (indexContent) {
result.index = {
offset: source.indexOf(
indexContent,
result.key
? result.key.offset + result.key.content.length
: trimmedOffset + valueContent.length
),
content: indexContent
}
}
}
}
if (valueContent) {
result.value = {
offset: trimmedOffset,
content: valueContent
}
}
return result
}
function maybeCreateExpression(
alias: AliasExpression | undefined,
node: ExpressionNode
): ExpressionNode | undefined {
if (alias) {
return {
type: NodeTypes.EXPRESSION,
loc: getInnerRange(node.loc, alias.offset, alias.content.length),
content: alias.content,
isStatic: false
}
}
}

View File

@@ -0,0 +1,63 @@
import { createDirectiveTransform } from '../transform'
import {
NodeTypes,
ElementTypes,
ElementNode,
DirectiveNode,
IfBranchNode
} from '../ast'
import { createCompilerError, ErrorCodes } from '../errors'
export const transformIf = createDirectiveTransform(
/^(if|else|else-if)$/,
(node, dir, context) => {
if (dir.name === 'if') {
context.replaceNode({
type: NodeTypes.IF,
loc: node.loc,
branches: [createIfBranch(node, dir)]
})
} else {
// locate the adjacent v-if
const siblings = context.parent.children
const comments = []
let i = siblings.indexOf(node)
while (i-- >= -1) {
const sibling = siblings[i]
if (__DEV__ && sibling && sibling.type === NodeTypes.COMMENT) {
context.removeNode(sibling)
comments.unshift(sibling)
continue
}
if (sibling && sibling.type === NodeTypes.IF) {
// move the node to the if node's branches
context.removeNode()
const branch = createIfBranch(node, dir)
if (__DEV__ && comments.length) {
branch.children = [...comments, ...branch.children]
}
sibling.branches.push(branch)
} else {
context.emitError(
createCompilerError(
dir.name === 'else'
? ErrorCodes.X_ELSE_NO_ADJACENT_IF
: ErrorCodes.X_ELSE_IF_NO_ADJACENT_IF,
node.loc.start
)
)
}
break
}
}
}
)
function createIfBranch(node: ElementNode, dir: DirectiveNode): IfBranchNode {
return {
type: NodeTypes.IF_BRANCH,
loc: node.loc,
condition: dir.name === 'else' ? undefined : dir.exp,
children: node.tagType === ElementTypes.TEMPLATE ? node.children : [node]
}
}

View File

@@ -0,0 +1 @@
// TODO

View File

@@ -0,0 +1 @@
// TODO

View File

@@ -0,0 +1 @@
// TODO

View File

@@ -0,0 +1 @@
// TODO

View File

@@ -0,0 +1 @@
// TODO