wip: element transforms
This commit is contained in:
87
packages/compiler-core/src/transforms/element.ts
Normal file
87
packages/compiler-core/src/transforms/element.ts
Normal 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
|
||||
}
|
||||
1
packages/compiler-core/src/transforms/optimizeBlocks.ts
Normal file
1
packages/compiler-core/src/transforms/optimizeBlocks.ts
Normal file
@@ -0,0 +1 @@
|
||||
// TODO
|
||||
1
packages/compiler-core/src/transforms/optimizeClass.ts
Normal file
1
packages/compiler-core/src/transforms/optimizeClass.ts
Normal file
@@ -0,0 +1 @@
|
||||
// TODO
|
||||
@@ -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.
|
||||
@@ -0,0 +1 @@
|
||||
// TODO
|
||||
1
packages/compiler-core/src/transforms/optimizeStyle.ts
Normal file
1
packages/compiler-core/src/transforms/optimizeStyle.ts
Normal file
@@ -0,0 +1 @@
|
||||
// TODO
|
||||
1
packages/compiler-core/src/transforms/vBind.ts
Normal file
1
packages/compiler-core/src/transforms/vBind.ts
Normal file
@@ -0,0 +1 @@
|
||||
// TODO
|
||||
128
packages/compiler-core/src/transforms/vFor.ts
Normal file
128
packages/compiler-core/src/transforms/vFor.ts
Normal 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
|
||||
}
|
||||
}
|
||||
}
|
||||
63
packages/compiler-core/src/transforms/vIf.ts
Normal file
63
packages/compiler-core/src/transforms/vIf.ts
Normal 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]
|
||||
}
|
||||
}
|
||||
1
packages/compiler-core/src/transforms/vModel.ts
Normal file
1
packages/compiler-core/src/transforms/vModel.ts
Normal file
@@ -0,0 +1 @@
|
||||
// TODO
|
||||
1
packages/compiler-core/src/transforms/vOn.ts
Normal file
1
packages/compiler-core/src/transforms/vOn.ts
Normal file
@@ -0,0 +1 @@
|
||||
// TODO
|
||||
1
packages/compiler-core/src/transforms/vPre.ts
Normal file
1
packages/compiler-core/src/transforms/vPre.ts
Normal file
@@ -0,0 +1 @@
|
||||
// TODO
|
||||
1
packages/compiler-core/src/transforms/vSlot.ts
Normal file
1
packages/compiler-core/src/transforms/vSlot.ts
Normal file
@@ -0,0 +1 @@
|
||||
// TODO
|
||||
1
packages/compiler-core/src/transforms/vText.ts
Normal file
1
packages/compiler-core/src/transforms/vText.ts
Normal file
@@ -0,0 +1 @@
|
||||
// TODO
|
||||
Reference in New Issue
Block a user