入口文件
const {parser} = require('./parser');
const {transformer} = require('./transformer');
const {codeGenerator} = require('./codeGenerator');
let code = '<h1 id="title"><span>hello</span>world</h1>';
let ast = parser(code);//把源代码转成语法树
transformer(ast);//把语法树转成新的语法树
let result = codeGenerator(ast);
console.log(result);
./parser
const {tokenizer} = require('./tokenizer');//标识符token
const tokenTypes = require('./tokenTypes');//token类型
const nodeTypes = require('./nodeTypes');//ast节点的类型
/**
<h1 id="title"><span>hello</span>world</h1>
01 2 3
jsxElement => <JSXIdentifier attribute*>child*</JSXIdentifier>
attribute => AttributeKey="AttributeStringValue"
child => jsxElement|JSXText
*/
function parser(sourceCode){
let tokens = tokenizer(sourceCode);//tokens数组
let pos = 0;//当前的token的索引0
function walk(parent){
let token = tokens[pos];//取出当前的token peek
let nextToken = tokens[pos+1];//取出下一个token peek下一个
//jsxElement语法规则
if(token.type === tokenTypes.LeftParentheses//当前是<,下一个token标识符,说是它就是一个JSX元素的开始
&&nextToken.type === tokenTypes.JSXIdentifier){
let node = {
type:nodeTypes.JSXElement,
openingElement:null,
closingElement:null,
children:[]
}
//第一步给开始元素赋值
token = tokens[++pos];//h1
node.openingElement={
type:nodeTypes.JSXOpeningElement,
name:{
type:nodeTypes.JSXIdentifier,
name:token.value //h1
},
attributes:[]
}
token = tokens[++pos];// AttributeKey=id
//循环给属性数组赋值
while(token.type === tokenTypes.AttributeKey){
node.openingElement.attributes.push(walk());
token = tokens[pos];
}
//pos指向谁 <h1 id="title"><span>hello</span>world</h1>
token = tokens[++pos];//直接把>跳过去了直接取<
nextToken = tokens[pos+1];
//child => JSXText|jsxElement
while(token.type != tokenTypes.LeftParentheses //对应的文本类型的子节点
//对应的元素类型的子节点 子元素的开始位置
||(token.type === tokenTypes.LeftParentheses && nextToken.type !== tokenTypes.BackSlash)
){
node.children.push(walk());
token = tokens[pos];
nextToken = tokens[pos+1];
}
node.closingElement = walk(node);
return node;
//while结束之后
//attribute规则
}else if(token.type === tokenTypes.AttributeKey){
let nextToken = tokens[++pos];//value
let node = {
type:nodeTypes.JSXAttribute,
name:{
type:nodeTypes.JSXIdentifier,
name:token.value //id
},
value:{
type:nodeTypes.Literal,
value:nextToken.value
}
}
pos++;
return node;
}else if(token.type === tokenTypes.JSXText){//JSXText
pos++;
return {
type:nodeTypes.JSXText,
value:token.value
}
//处理结束标签
}else if(parent
&& token.type === tokenTypes.LeftParentheses//<
&& nextToken.type === tokenTypes.BackSlash// /
){ //pos指向谁 <h1 id="title"><span>hello</span>world</h1>
pos++;// 跳过<
pos++;// 跳过/
token = tokens[pos];//span h1
pos++;// 跳过span
pos++;// 跳过>
if(parent.openingElement.name.name !== token.value){
throw new TypeError(`开始标签[${parent.openingElement.name.name}]和结束标签[${token.value}]不匹配`);
}
return {
type:nodeTypes.JSXClosingElement,
name:{
type:nodeTypes.JSXIdentifier,
name:token.value
}
}
}
throw new TypeError('不可能走到这');
}
let ast = {
type:nodeTypes.Program,
body:[
{
type:nodeTypes.ExpressionStatement,
expression:walk()
}
]
}
return ast;
}
module.exports ={
parser
}
/* let sourceCode = '<h1 id="title" name="zhufeng"><span>hello</span>world</h1>';
console.log(JSON.stringify(parser(sourceCode),null,2)); */
/**
[
{ type: 'LeftParentheses', value: '<' },
{ type: 'JSXIdentifier', value: 'h1' },
{ type: 'AttributeKey', value: 'id' },
{ type: 'AttributeStringValue', value: 'title' },
{ type: 'AttributeKey', value: 'name' },
{ type: 'AttributeStringValue', value: 'zhufeng' },
{ type: 'RightParentheses', value: '>' },
{ type: 'LeftParentheses', value: '<' },
{ type: 'JSXIdentifier', value: 'span' },
{ type: 'RightParentheses', value: '>' },
{ type: 'JSXText', value: 'hello' },
{ type: 'LeftParentheses', value: '<' },
{ type: 'BackSlash', value: '/' },
{ type: 'JSXIdentifier', value: 'span' },
{ type: 'RightParentheses', value: '>' },
{ type: 'JSXText', value: 'world' },
{ type: 'LeftParentheses', value: '<' },
{ type: 'BackSlash', value: '/' },
{ type: 'JSXIdentifier', value: 'h1' },
{ type: 'RightParentheses', value: '>' }
]
*/
./transformer
const {traverse} = require('./traverse');//babel-types
//const t = require('babel-types');
const nodeTypes = require('./nodeTypes');
class t{
//生成一个新的AST节点
static nullLiteral(){
return {type:nodeTypes.NullLiteral}
}
static stringLiteral(value){//value是字符串的值
return {type:nodeTypes.StringLiteral,value};
}
static memberExpression(object,property){
return {type:nodeTypes.MemberExpression,object,property}
}
static identifier(name){//value是字符串的值
return {type:nodeTypes.Identifier,name};
}
static objectExpression(properties){
return {type:nodeTypes.ObjectExpression,properties};
}
static property(key,value){
return{
type:nodeTypes.Property,
key,
value
}
}
static callExpression(callee,_arguments){
return {
type:nodeTypes.CallExpression,
callee,
arguments:_arguments
}
}
//判断某种节点是不是某种类型
static isJSXElement(node){
return node.type === nodeTypes.JSXElement;
}
static isJSXText(node){
return node.type === nodeTypes.JSXText;
}
}
function transformer(ast){
traverse(ast,{
JSXElement(nodePath,parent){//节点的路径 {node} parent
//传入一个JSXElement语法树节点,返回一个方法调用的新节点
function transform(node){
if(!node) return t.nullLiteral();//null
if(t.isJSXElement(node)){//如果要转换的节点是一个JSX元素转成一个方法调用
let memberExpression = t.memberExpression(
t.identifier('React'),
t.identifier('createElement')
);
let elementType = t.stringLiteral(node.openingElement.name.name);//h1 span
let attributes = node.openingElement.attributes;//JSXAttribute数组
let objectExpression;
if(attributes.length>0){
objectExpression=t.objectExpression(attributes.map(attr=>(
t.property(t.identifier(attr.name.name),t.stringLiteral(attr.value.value))
)));
}else{
objectExpression=t.nullLiteral();
}
let _arguments = [elementType,objectExpression,...node.children.map(child=>transform(child))];
return t.callExpression(memberExpression,_arguments);
}else if(t.isJSXText(node)){//如果要转换的节点一个文本
return t.stringLiteral(node.value);
}
}
let newNode = transform(nodePath.node);
nodePath.replaceWith(newNode);
}
});
}
module.exports = {transformer}
./traverse
const {parser} = require('./parser');
const nodeTypes = require('./nodeTypes');
function replace(parent,oldNode,newNode){
if(parent){
for(const key in parent){
if(parent.hasOwnProperty(key)){
if(parent[key] === oldNode){
parent[key]=newNode;
}
}
}
}
}
function traverse(ast,visitor){
function traverseArray(array,parent){
array.forEach(child=>traverseNode(child,parent));
}
//使用遍历 转换 都是使用 babel的方式
function traverseNode(node,parent){
//bind把一个老函数进行绑定得到新函数,绑定1this指针 第2个参数是第一个参数 第3个参数是方法执行的第二2个参数
let replaceWith = replace.bind(null,parent,node);
let method = visitor[node.type];
//当开始准备遍历子节点的时候执行进入方法
if(method){
if(typeof method === 'function')
method({node,replaceWith},parent);
else
if(typeof method.enter === 'function')
method.enter({node,replaceWith},parent);
}
switch(node.type){
case nodeTypes.Program:
traverseArray(node.body,node);
break;
case nodeTypes.ExpressionStatement:
traverseNode(node.expression,node);
break;
case nodeTypes.JSXElement:
traverseNode(node.openingElement,node);
traverseArray(node.children,node);
traverseNode(node.closingElement,node);
break;
case nodeTypes.openingElement:
traverseNode(node.name,node);
traverseArray(node.attributes,node);
break;
case nodeTypes.JSXAttribute:
traverseNode(node.name,node);
traverseNode(node.value,node);
break;
case nodeTypes.closingElement:
traverseNode(node.name,node);
break;
case nodeTypes.JSXIdentifier:
case nodeTypes.JSXText:
case nodeTypes.Literal:
break;
default:
break;
}
//当遍历完子节点离开此节点的时候要执行离开方法
if(method && method.exit){
method.exit({node,replaceWith},parent);
}
}
traverseNode(ast,null);
}
module.exports = {traverse}
/*
let sourceCode = '<h1 id="title"><span>hello</span>world</h1>';
let ast = parser(sourceCode);
traverse(ast,{
JSXOpeningElement:{
enter:(nodePath,parent)=>{
console.log(`进入开始元素`,nodePath.node);
},
exit:(nodePath,parent)=>{
console.log(`离开开始元素`,nodePath.node);
}
},
JSXClosingElement(nodePath,parent){
console.log(`进入结束元素`,nodePath.node);
}
}); */