babel必知必会源码篇(三)

91 阅读3分钟

入口文件

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: '>' }
]
 */

image.png

./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);
    }
}); */