昨天我們分析 HTML Tokens 要轉換成 AST 需要利用 elementStack 來輔助,今天我們就來實作一下。
tagStart
handler
// 遇到 tagStart,node A 當作 elementStack 的最後一個元素的 children,
// 並將 node A push elementStack
const handleTagStart = elementStack => token => {
const node = {type: token.name, children: []}; // node A
elementStack[elementStack.length - 1].children.push(node);// last children
elementStack.push(node); // 並將 node A push elementStack
};
tagEnd
handler
// 遇到 tagEnd,則 pop elementStack 的最後一個元素
const handleTagEnd = elementStack => token => {
elementStack.pop(); // pop elementStack 的最後一個元素
};
tagSelfClose
handler
// 遇到 tagSelfClose,新增 node 放到 elementStack 的最後一個元素的 children 中
const handleTagSelfClose = elementStack => token => {
const node = {type: token.name}; // tagSelfClose node
elementStack[elementStack.length - 1].children.push(node);// last children
};
Text
handler
// 遇到 text,新增 node 放到 elementStack 的最後一個元素的 children 中
const handleText = elementStack => token => {
const node = {type: 'text', content: token.content}; // text node
elementStack[elementStack.length - 1].children.push(node);// last children
};
const assert = require('node:assert');
const tokens = [
{"type": "tagStart", "name": "div"},
{"type": "tagStart", "name": "p"},
{"type": "text", "content": "Vue"},
{"type": "tagEnd", "name": "p"},
{"type": "tagSelfClose", "name": "input"},
{"type": "tagStart", "name": "p"},
{"type": "text", "content": "Template"},
{"type": "tagEnd", "name": "p"},
{"type": "tagEnd", "name": "div"},
];
const expected_AST = {
type: 'root',
children: [
{
"type": "div",
"children": [
{
"type": "p",
"children": [
{
"type": "text",
"content": "Vue"
}
]
},
{"type": "input"},
{
"type": "p",
"children": [
{
"type": "text",
"content": "Template"
}
]
}
]
}
]
}
const root = {type: 'root', children: []};
const output_AST = root;
const elementStack = [root];
// 遇到 tagStart,node A 當作 elementStack 的最後一個元素的 children,
// 並將 node A push elementStack
const handleTagStart = elementStack => token => {
const node = {type: token.name, children: []}; // node A
elementStack[elementStack.length - 1].children.push(node);// last children
elementStack.push(node); // 並將 node A push elementStack
};
// 遇到 tagEnd,則 pop elementStack 的最後一個元素
const handleTagEnd = elementStack => token => {
elementStack.pop(); // pop elementStack 的最後一個元素
};
// 遇到 text,當作 elementStack 的最後一個元素的 children
const handleTagSelfClose = elementStack => token => {
const node = {type: token.name}; // tagSelfClose node
elementStack[elementStack.length - 1].children.push(node);// last children
};
// 遇到 tagSelfClose,當作 elementStack 的最後一個元素的 children
const handleText = elementStack => token => {
const node = {type: 'text', content: token.content}; // text node
elementStack[elementStack.length - 1].children.push(node);// last children
};
while (tokens.length > 0) {
// 取出目前要檢查的 token
const token = tokens.shift();
if (token.type === 'tagStart') handleTagStart(elementStack)(token);
if (token.type === 'tagEnd') handleTagEnd(elementStack)(token);
if (token.type === 'text') handleText(elementStack)(token);
if (token.type === 'tagSelfClose') handleTagSelfClose(elementStack)(token);
}
assert.deepEqual(output_AST, expected_AST);
相信有許多邦友發現了,最近討論的 HTML 範例都是沒有屬性的,那如果有屬性要如何分析呢?先留給大家思考了,我們下一篇再來討論。