Skip to content

Commit

Permalink
实现代码生成三种联合类型
Browse files Browse the repository at this point in the history
  • Loading branch information
jindy committed Jun 27, 2022
1 parent 43d2774 commit c13829e
Show file tree
Hide file tree
Showing 10 changed files with 186 additions and 5 deletions.
14 changes: 14 additions & 0 deletions src/compiler-core/src/ast.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { CREATE_ELEMENT_VNODE } from "./runtimeHelpers"

export const enum NodeTypes {
// interpolation
INTERPOLATION,
Expand All @@ -6,4 +8,16 @@ export const enum NodeTypes {
ELEMENT,
TEXT,
ROOT,
COMPOUND_EXPRESSION,
}

export function createVNodeCall(context, tag, props, children) {
context.helper(CREATE_ELEMENT_VNODE)

return {
type: NodeTypes.ELEMENT,
tag,
props,
children,
}
}
62 changes: 61 additions & 1 deletion src/compiler-core/src/codegen.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
import { helperMapName, TO_DISPLAY_STRING } from "./runtimeHelpers"
import {
CREATE_ELEMENT_VNODE,
helperMapName,
TO_DISPLAY_STRING,
} from "./runtimeHelpers"
import { NodeTypes } from "./ast"
import { isString } from "../../shared"

export function generate(ast) {
// let code = ""
Expand Down Expand Up @@ -65,11 +70,66 @@ function genNode(node, context) {
break
case NodeTypes.SIMPLE_EXPRESSION:
genExpression(node, context)
break
case NodeTypes.ELEMENT:
genElement(node, context)
break
case NodeTypes.COMPOUND_EXPRESSION:
genCompoundExpression(node, context)
break
default:
break
}
}

function genCompoundExpression(node, context) {
const { children } = node
const { push } = context
for (let index = 0; index < children.length; index++) {
const child = children[index]
if (isString(child)) {
push(child)
} else {
genNode(child, context)
}
}
}

function genElement(node, context) {
const { push, helper } = context
const { tag, children, props } = node
push(`${helper(CREATE_ELEMENT_VNODE)}(`)
// const child = children[0]
// for (let index = 0; index < children.length; index++) {
// const child = children[index]
// genNode(child, context)
// }
genNodeList(genNullable([tag, props, children]), context)
// genNode(children, context)
push(")")
}

function genNodeList(nodes, context) {
const { push } = context

for (let index = 0; index < nodes.length; index++) {
const node = nodes[index]
if (isString(node)) {
push(node)
} else {
genNode(node, context)
}

if (index < nodes.length - 1) {
push(",")
}
}
}

function genNullable(args: any) {
return args.map((arg) => arg || "null")
}

function genExpression(node, context) {
const { push } = context
push(`${node.content}`)
Expand Down
2 changes: 2 additions & 0 deletions src/compiler-core/src/runtimeHelpers.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
export const TO_DISPLAY_STRING = Symbol("toDisplayString")
export const CREATE_ELEMENT_VNODE = Symbol("createElementVNode")

export const helperMapName = {
[TO_DISPLAY_STRING]: "toDisplayString",
[CREATE_ELEMENT_VNODE]: "createElementVNode",
}
20 changes: 16 additions & 4 deletions src/compiler-core/src/transform.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,18 @@ export function transform(root, options = {}) {
traverseNode(root, context)
// 2.修改 text content

createCodegen(root)
createRootCodegen(root)

root.helpers = [...context.helpers.keys()]
}

function createCodegen(root) {
root.codegenNode = root.children[0]
function createRootCodegen(root) {
const child = root.children[0]
if (child.type === NodeTypes.ELEMENT) {
root.codegenNode = child.codegenNode
} else {
root.codegenNode = root.children[0]
}
}

function createTransformContext(root, options) {
Expand All @@ -35,9 +40,11 @@ function traverseNode(node, context) {
// node.content = node.content + "mini-vue"
// }
const nodeTransforms = context.nodeTransforms
const exitFns: any = []
for (let index = 0; index < nodeTransforms.length; index++) {
const transform = nodeTransforms[index]
transform(node)
const onExit = transform(node, context)
if (onExit) exitFns.push(onExit)
}

switch (node.type) {
Expand All @@ -51,6 +58,11 @@ function traverseNode(node, context) {
default:
break
}

let i = exitFns.length
while (i--) {
exitFns[i]()
}
}

function traverseChildren(node, context) {
Expand Down
33 changes: 33 additions & 0 deletions src/compiler-core/src/transform/transformElement.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { createVNodeCall, NodeTypes } from "../ast"
import { CREATE_ELEMENT_VNODE } from "../runtimeHelpers"

export function transformElement(node: any, context) {
if (node.type === NodeTypes.ELEMENT) {
return () => {
// context.helper(CREATE_ELEMENT_VNODE)

// 中间层
// tag
const vnodeTag = `'${node.tag}'`
// children
const children = node.children
const vnodeChildren = children[0]
// props
let vnodeProps

// const vnodeElement = {
// type: NodeTypes.ELEMENT,
// tag: vnodeTag,
// props: vnodeProps,
// children: vnodeChildren,
// }

node.codegenNode = createVNodeCall(
context,
vnodeTag,
vnodeProps,
vnodeChildren
)
}
}
}
34 changes: 34 additions & 0 deletions src/compiler-core/src/transform/transformText.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { NodeTypes } from "../ast"
import { isText } from "../util"

export function transformText(node) {
return () => {
const { children, type } = node
if (type === NodeTypes.ELEMENT) {
let currentContainer
for (let index = 0; index < children.length; index++) {
const child = children[index]
if (isText(child)) {
for (let j = index + 1; j < children.length; j++) {
const next = children[j]
if (isText(next)) {
if (!currentContainer) {
currentContainer = children[index] = {
type: NodeTypes.COMPOUND_EXPRESSION,
children: [child],
}
}
currentContainer.children.push(" + ")
currentContainer.children.push(next)
children.splice(j, 1)
j--
} else {
currentContainer = undefined
break
}
}
}
}
}
}
}
5 changes: 5 additions & 0 deletions src/compiler-core/src/util.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { NodeTypes } from "./ast"

export function isText(node) {
return node.type === NodeTypes.INTERPOLATION || node.type === NodeTypes.TEXT
}
5 changes: 5 additions & 0 deletions src/compiler-core/tests/__snapshots__/codegen.spec.ts.snap
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`codegen element 1`] = `
"const { toDisplayString: _toDisplayString,createElementVNode: _createElementVNode } = Vue
return function render(_ctx,_cache){return _createElementVNode('div',null,'hi,' + _toDisplayString(_ctx.message))}"
`;

exports[`codegen interpolation 1`] = `
"const { toDisplayString: _toDisplayString } = Vue
return function render(_ctx,_cache){return _toDisplayString(_ctx.message)}"
Expand Down
14 changes: 14 additions & 0 deletions src/compiler-core/tests/codegen.spec.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import { generate } from "../src/codegen"
import { baseParse } from "../src/parse"
import { transform } from "../src/transform"
import { transformElement } from "../src/transform/transformElement"
import { transformExpression } from "../src/transform/transformExpression"
import { transformText } from "../src/transform/transformText"

describe("codegen", () => {
it("string", () => {
Expand All @@ -21,4 +23,16 @@ describe("codegen", () => {
// 快照测试
expect(code).toMatchSnapshot()
})

it("element", () => {
const ast = baseParse("<div>hi,{{message}}</div>")
transform(ast, {
nodeTransforms: [transformExpression, transformElement, transformText],
})

console.log("ast", ast)
const { code } = generate(ast)
// 快照测试
expect(code).toMatchSnapshot()
})
})
2 changes: 2 additions & 0 deletions src/shared/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ export const isObject = (value) => {
return value !== null && typeof value === "object"
}

export const isString = (val) => typeof val === "string"

export const hasChanged = (val, newVal) => {
return !Object.is(val, newVal)
}
Expand Down

0 comments on commit c13829e

Please sign in to comment.