目 录CONTENT

文章目录

梳理一下Babel API

Hello!你好!我是村望~!
2023-11-24 / 0 评论 / 0 点赞 / 88 阅读 / 5,637 字
温馨提示:
我不想探寻任何东西的意义,我只享受当下思考的快乐~

Babel API

img

参考


我们知道 babel 的编译流程分为三步:parse、transform、generate,每一步都暴露了一些 api 出来。

  • parse 阶段有@babel/parser,功能是把源码转成 AST

  • transform 阶段有 @babel/traverse,可以遍历 AST,并调用 visitor 函数修改 AST,

    • 修改 AST 自然涉及到 AST 的判断、创建、修改等,这时候就需要 @babel/types
    • 当需要批量创建 AST 的时候可以使用 @babel/template 来简化 AST 创建逻辑。
  • generate 阶段会把 AST 打印为目标代码字符串,同时生成 sourcemap,需要 @babel/generator

  • 中途遇到错误想打印代码位置的时候,使用 @babel/code-frame
  • babel 的整体功能通过 @babel/core 提供,基于上面的包完成 babel 整体的编译流程,并应用 plugin 和 preset。

主要学习的就是

  • @babel/parser

  • @babel/traverse

  • @babel/generator

  • @babel/types

  • @babel/template

这五个包的 api 的使用。

这些包的 api 都可以在文档里查看:

@babel/parser

下面的介绍都可以在官方文档中看到

Babel 分析器(前身为 Babylon)是 Babel 中使用的 JavaScript 分析器。

默认启用最新的 ECMAScript 版本(ES2020)。

支持 JSX、Flow 和 Typescript。

支持实验性语言提案(接受任何至少stage-0的 PR)。

它提供了有两个 api:

  • babelParser.parse(code, [options])

    • parse() 将提供的代码作为整个 ECMAScript 程序进行解析
  • babelParser.parseExpression(code, [options])

    • parseExpression() 返回的 AST 根节点是是 Expression(表达式的 AST),粒度不同。

详细的 options 可以查看文档。其实主要分为两类,一是 parse 的内容是什么,二是以什么方式去 parse

parse 的内容是什么:

  • plugins: 指定jsx、typescript、flow 等插件来解析对应的语法

  • allowXxx: 指定一些语法是否允许,比如函数外的 await、没声明的 export等

  • sourceType : 指定是否支持解析模块语法,有 module、script、unambiguous 3个取值:

    • module:解析 es module 语法
    • script:不解析 es module 语法
    • unambiguous:根据内容是否有 importexport 来自动设置 module 还是 script
const parser = require('@babel/parser')

const code = `interface Shape {
  name: string;
  width: number;
  height: number;
  color?: string;
}

function area(shape : Shape) {
  var area = shape.width * shape.height;
  return "I'm " + shape.name + " with area " + area + " cm squared";
}

console.log( area( {name: "rectangle", width: 30, height: 15} ) );`

const ast = parser.parse("code", {
  sourceType: 'unambiguous',
  plugins: ['typescript']
});
console.log(JSON.stringify(ast))

当你使用在线astexplorer的时候,这里也同样支持 parser options 的设置

image-20231124094722652

@babel/traverse

文档地址:https://www.babeljs.cn/docs/babel-traverse

parse 出的 AST 由 @babel/traverse 来遍历和修改,babel traverse 包提供了 traverse 方法:

traverse 方法

下面是 traverse 方法的类型声明

declare const traverse: {
    <S>(parent: Node, opts: TraverseOptions<S>, scope: Scope | undefined, state: S, parentPath?: NodePath): void;
    (parent: Node, opts?: TraverseOptions, scope?: Scope, state?: any, parentPath?: NodePath): void;

    visitors: typeof visitors;
    verify: typeof visitors.verify;
    explode: typeof visitors.explode;

    cheap: (node: Node, enter: (node: Node) => void) => void;
    node: (
        node: Node,
        opts: TraverseOptions,
        scope?: Scope,
        state?: any,
        path?: NodePath,
        skipKeys?: Record<string, boolean>,
    ) => void;
    clearNode: (node: Node, opts?: RemovePropertiesOptions) => void;
    removeProperties: (tree: Node, opts?: RemovePropertiesOptions) => Node;
    hasType: (tree: Node, type: Node["type"], denylistTypes?: string[]) => boolean;

    cache: typeof cache;
};

前面两个参数是比较常用的,parent 指定要遍历的 AST 节点,opts 指定 visitor 函数。babel 会在遍历 parent 对应的 AST 时调用相应的 visitor 函数。

visitor 函数

export interface VisitNodeObject<S, P extends Node> {
    enter?: VisitNodeFunction<S, P>; // 进入节点执行
    exit?: VisitNodeFunction<S, P>; // 离开节点执行
}
traverse(ast, {
	enter(path) {
		console.log('enter')
	},
	exit(path) {
		console.log('exit')
	},
})
$ node app.js 
enter
enter
enter
exit
exit
exit

这里我们输出遍历节点的type会更清晰

traverse(ast, {
		enter(path) {
			console.log('enter',path.type)
		},
		exit(path) {
			console.log('exit',path.type)
		},
})
$ node app.js 
enter Program
enter ExpressionStatement
enter Identifier
exit Identifier
exit ExpressionStatement
exit Program

基本可以看出很像一个栈数据结构的感觉,先进后出

先从最外面的节点 Program enter

然后 enter 内部子节点ExpressionStatement

继续 enter 到最内层的Identifier

然后在反向exit 先退出Identifier

然后exit ExpressionStatement

最后exit Program

image-20231124104446704

此外,我们可以针对语法树中的特定节点类型,比如下面是针对 Program 节点的

traverse(ast, {
	Program: {
		enter(path) {
			console.log('enter', path.type)
		},
		exit(path) {
			console.log('exit', path.type)
		},
	},
})
$ node app.js 
enter Program
exit Program

可以看到其他只进行了 Program 节点的遍历

如果只存在一个函数的话,那么默认就是 enter 阶段的调用

traverse(ast, {
	Program(path) {}
})

这里可以从源码中看到https://github.com/babel/babel/blob/main/packages/babel-types/src/traverse/traverse.ts

image-20231124111317283


enter 时调用是在**遍历当前节点的子节点** 调用,exit 时调用是**遍历完当前节点的子节点** 调用。

visitor 函数的参数 - path

AST 是棵树,遍历过程中肯定是有个路径的,path 就记录了这个路径

在整个过程中,祖先信息(包括父节点、父节点的键以及父节点的索引)被保存在一个ancestors数组中,并传递给每个处理程序。这个就是path 记录了遍历过的所有节点!

// 定义TraversalAncestors类型,用于存储祖先信息
export type TraversalAncestors = Array<{
  node: t.Node; // 祖先节点
  key: string; // 祖先节点的键
  index?: number; // 如果祖先节点是数组类型,那么这个字段表示当前节点在数组中的索引
}>;

// 定义TraversalHandler类型,用于描述一个处理程序
export type TraversalHandler<T> = (
  this: undefined,
  node: t.Node, // 当前节点
  parent: TraversalAncestors, // 祖先信息
  state: T, // 用户提供的状态对象
) => void;

// 定义TraversalHandlers类型,用于描述一组处理程序
export type TraversalHandlers<T> = {
  enter?: TraversalHandler<T>; // 预处理程序
  exit?: TraversalHandler<T>; // 后处理程序
};

// 实现一个通用的AST遍历器
export default function traverse<T>(
  node: t.Node, // 要遍历的节点
  handlers: TraversalHandler<T> | TraversalHandlers<T>, // 处理程序或者一组处理程序
  state?: T, // 用户提供的状态对象
): void {
  if (typeof handlers === "function") {
    handlers = { enter: handlers }; // 如果handlers只是一个函数,则将其转化为{ enter: handlers }
  }

  const { enter, exit } = handlers; // 获取处理程序

  traverseSimpleImpl(node, enter, exit, state, []); // 调用内部函数进行实际的遍历操作
}

function traverseSimpleImpl<T>(
  node: any, // 当前节点
  enter: Function | undefined, // 预处理程序
  exit: Function | undefined, // 后处理程序
  state: T | undefined, // 用户提供的状态对象
  ancestors: TraversalAncestors, // 祖先信息
) {
  const keys = VISITOR_KEYS[node.type]; // 获取当前节点类型的所有子节点键名
  if (!keys) return; // 如果没有子节点键名,则直接返回

  if (enter) enter(node, ancestors, state); // 如果有预处理程序,则调用预处理程序

  for (const key of keys) { // 对每个子节点键进行遍历
    const subNode = node[key];

    if (Array.isArray(subNode)) { // 如果子节点是数组类型
      for (let i = 0; i < subNode.length; i++) { // 遍历数组中的每个元素
        const child = subNode[i];
        if (!child) continue; // 如果元素为null或undefined,则跳过

        ancestors.push({ // 将祖先信息推入栈顶
          node,
          key,
          index: i,
        });

        traverseSimpleImpl(child, enter, exit, state, ancestors); // 递归地遍历元素

        ancestors.pop(); // 弹出栈顶的祖先信息
      }
    } else if (subNode) { // 如果子节点是非数组类型
      ancestors.push({ // 将祖先信息推入栈顶
        node,
        key,
      });

      traverseSimpleImpl(subNode, enter, exit, state, ancestors); // 递归地遍历子节点

      ancestors.pop(); // 弹出栈顶的祖先信息
    }
  }

  if (exit) exit(node, ancestors, state); // 如果有后处理程序,则调用后处理程序
}

path 有很多属性和方法,比如记录父子、兄弟等关系的:

  • path.node 指向当前 AST 节点
  • path.parent 指向父级 AST 节点
  • path.getSiblingpath.getNextSiblingpath.getPrevSibling 获取兄弟节点
  • path.find 从当前节点向上查找节点
  • path.getpath.set 获取 / 设置属性的 path

还有作用域相关的:

  • path.scope 获取当前节点的作用域信息

判断 AST 类型的:

  • path.isXxx 判断当前节点是不是 xx 类型
  • path.assertXxx 判断当前节点是不是 xx 类型,不是则抛出异常

增删改 AST 的:

  • path.insertBeforepath.insertAfter 插入节点
  • path.replaceWithpath.replaceWithMultiplereplaceWithSourceString 替换节点
  • path.remove 删除节点

跳过遍历的:

  • path.skip 跳过当前节点的子节点的遍历
  • path.stop 结束后续遍历

path相关的方法都可以在源码这里找到

image-20231124114237921

image-20231124114324107

visitor 函数的参数 - state

这个很容易理解,节点之间是有传输数据的需求的。不同状态下可能会做不同的处理,这就是为什么这个参数叫做 state

源码中是有递归传递state的,并且为 enter or exit 的回调参数

image-20231124114716545

插件会通过 state 传递 optionsfile 信息,我们也可以通过 state 存储一些遍历过程中的共享数据。

image-20231124114738673

@babel/types

This module contains methods for building ASTs manually and for checking the types of AST nodes.

该模块包含手动构建 AST 和检查 AST 节点类型的方法。

举例来说,如果要创建IfStatement就可以调用

t.ifStatement(test, consequent, alternate);

而判断节点是否是 IfStatement 就可以调用 isIfStatement 或者 assertIfStatement

t.isIfStatement(node, opts);
t.assertIfStatement(node, opts);

opts 可以指定一些属性是什么值,增加更多限制条件,做更精确的判断。

t.isIdentifier(node, { name: "paths" })

isXxxassertXxx 看起来很像,但是功能不大一样:isXxx 会返回 boolean,而 assertXxx 则会在类型不一致时抛异常。

所有的 AST 的 buildassert 的 api 可以在 babel types 文档中查。

@babel/template

提供了代码直接创建为AST树的能力

通过上面@babel/types 创建 AST 还是比较麻烦的,要一个个的创建然后组装,如果 AST 节点比较多的话需要写很多代码,这时候就可以使用 @babel/template 包来批量创建。

const template = require('@babel/template').default

const code = template(`
  function add(a, b) {
    return a + b;
  }
`)()

console.log(code) 

输出

{
    "type": "FunctionDeclaration",
    "params": [
        {
            "type": "Identifier",
            "name": "a"
        },
        {
            "type": "Identifier",
            "name": "b"
        }
    ],
    "generator": false,
    "async": false,
    "id": {
        "type": "Identifier",
        "name": "add"
    },
    "body": {
        "type": "BlockStatement",
        "directives": [],
        "body": [
            {
                "type": "ReturnStatement",
                "argument": {
                    "type": "BinaryExpression",
                    "operator": "+",
                    "left": {
                        "type": "Identifier",
                        "name": "a"
                    },
                    "right": {
                        "type": "Identifier",
                        "name": "b"
                    }
                }
            }
        ]
    }
}

https://babeljs.io/docs/babel-template

这个包有这些 api

template

模板会根据解析结果返回单个语句或语句数组,上面演示过了!

template.smart

这与默认模板 API 相同,根据解析结果返回单个节点或节点数组。(下面是个输出节点数组的例子)

const code = template.smart(`
  function add(a, b) {
    return a + b;
  }
  add(1,2)
`)()

console.log(JSON.stringify(code)) 

输出

[
    {
        "type": "FunctionDeclaration",
        "params": [
            {
                "type": "Identifier",
                "name": "a"
            },
            {
                "type": "Identifier",
                "name": "b"
            }
        ],
        "generator": false,
        "async": false,
        "id": {
            "type": "Identifier",
            "name": "add"
        },
        "body": {
            "type": "BlockStatement",
            "directives": [],
            "body": [
                {
                    "type": "ReturnStatement",
                    "argument": {
                        "type": "BinaryExpression",
                        "operator": "+",
                        "left": {
                            "type": "Identifier",
                            "name": "a"
                        },
                        "right": {
                            "type": "Identifier",
                            "name": "b"
                        }
                    }
                }
            ]
        }
    },
    {
        "type": "ExpressionStatement",
        "expression": {
            "type": "CallExpression",
            "callee": {
                "type": "Identifier",
                "name": "add"
            },
            "arguments": [
                {
                    "type": "NumericLiteral",
                    "value": 1,
                    "extra": {
                        "rawValue": 1,
                        "raw": "1"
                    }
                },
                {
                    "type": "NumericLiteral",
                    "value": 2,
                    "extra": {
                        "rawValue": 2,
                        "raw": "2"
                    }
                }
            ]
        }
    }
]

template.statement

返回单个语句节点,如果结果不是单个语句,则抛出异常。(例如下面输出是 IfStatement)

const template = require('@babel/template').default

const code = template.statement(`
    if(a){
      console.log(a)
    }
`)()

console.log(JSON.stringify(code)) 

输出

{
    "type": "IfStatement",
    "test": {
        "type": "Identifier",
        "name": "a"
    },
    "consequent": {
        "type": "BlockStatement",
        "directives": [],
        "body": [
            {
                "type": "ExpressionStatement",
                "expression": {
                    "type": "CallExpression",
                    "callee": {
                        "type": "MemberExpression",
                        "object": {
                            "type": "Identifier",
                            "name": "console"
                        },
                        "property": {
                            "type": "Identifier",
                            "name": "log"
                        },
                        "computed": false
                    },
                    "arguments": [
                        {
                            "type": "Identifier",
                            "name": "a"
                        }
                    ]
                }
            }
        ]
    },
    "alternate": null
}

如果不为单个语句

const template = require('@babel/template').default

const code = template.statement(`
    if(a){
      console.log(a)
    }
    if(b){
      console.log(b)
    }
`)()

console.log(JSON.stringify(code)) 

执行的时候就会抛出异常 Found multiple statements but wanted one

$ node app.js 
E:\better\babel_go\node_modules\@babel\template\lib\builder.js:64
      throw err;
      ^

Error: Found multiple statements but wanted one
    at E:\better\babel_go\node_modules\@babel\template\lib\formatters.js:35:11
    at Object.unwrap (E:\better\babel_go\node_modules\@babel\template\lib\formatters.js:16:14)

template.statements

如其名,返回一个语句节点数组。(上面那个报错的拿这个就OK啦!)

const template = require('@babel/template').default

const code = template.statements(`
    if(a){
      console.log(a)
    }
    if(b){
      console.log(b)
    }
`)()

console.log(JSON.stringify(code)) 

输出

[
    {
        "type": "IfStatement",
        "test": {
            "type": "Identifier",
            "name": "a"
        },
        "consequent": {
            "type": "BlockStatement",
            "directives": [],
            "body": [
                {
                    "type": "ExpressionStatement",
                    "expression": {
                        "type": "CallExpression",
                        "callee": {
                            "type": "MemberExpression",
                            "object": {
                                "type": "Identifier",
                                "name": "console"
                            },
                            "property": {
                                "type": "Identifier",
                                "name": "log"
                            },
                            "computed": false
                        },
                        "arguments": [
                            {
                                "type": "Identifier",
                                "name": "a"
                            }
                        ]
                    }
                }
            ]
        },
        "alternate": null
    },
    {
        "type": "IfStatement",
        "test": {
            "type": "Identifier",
            "name": "b"
        },
        "consequent": {
            "type": "BlockStatement",
            "directives": [],
            "body": [
                {
                    "type": "ExpressionStatement",
                    "expression": {
                        "type": "CallExpression",
                        "callee": {
                            "type": "MemberExpression",
                            "object": {
                                "type": "Identifier",
                                "name": "console"
                            },
                            "property": {
                                "type": "Identifier",
                                "name": "log"
                            },
                            "computed": false
                        },
                        "arguments": [
                            {
                                "type": "Identifier",
                                "name": "b"
                            }
                        ]
                    }
                }
            ]
        },
        "alternate": null
    }
]

template.expression

返回表达式节点的AST

const template = require('@babel/template').default

const code = template.expression(`
   a = 1
`)()

console.log(JSON.stringify(code)) 

输出

{
    "type": "AssignmentExpression",
    "operator": "=",
    "left": {
        "type": "Identifier",
        "name": "a"
    },
    "right": {
        "type": "NumericLiteral",
        "value": 1,
        "extra": {
            "rawValue": 1,
            "raw": "1"
        }
    },
    "extra": {
        "parenthesized": true,
        "parenStart": 0
    }
}

template.program

返回模板的 Program 节点。

const template = require('@babel/template').default

const code = template.program(`
  let a = 0
  if(a === 0){
    a = 1
  }else{
    a = 2
  }
  console.log(a)
`)()

console.log(JSON.stringify(code)) 

输出

{
    "type": "Program",
    "sourceType": "module",
    "interpreter": null,
    "directives": [],
    "body": [
        {
            "type": "VariableDeclaration",
            "kind": "let",
            "declarations": [
                {
                    "type": "VariableDeclarator",
                    "id": {
                        "type": "Identifier",
                        "name": "a"
                    },
                    "init": {
                        "type": "NumericLiteral",
                        "value": 0,
                        "extra": {
                            "rawValue": 0,
                            "raw": "0"
                        }
                    }
                }
            ]
        },
        {
            "type": "IfStatement",
            "test": {
                "type": "BinaryExpression",
                "operator": "===",
                "left": {
                    "type": "Identifier",
                    "name": "a"
                },
                "right": {
                    "type": "NumericLiteral",
                    "value": 0,
                    "extra": {
                        "rawValue": 0,
                        "raw": "0"
                    }
                }
            },
            "consequent": {
                "type": "BlockStatement",
                "directives": [],
                "body": [
                    {
                        "type": "ExpressionStatement",
                        "expression": {
                            "type": "AssignmentExpression",
                            "operator": "=",
                            "left": {
                                "type": "Identifier",
                                "name": "a"
                            },
                            "right": {
                                "type": "NumericLiteral",
                                "value": 1,
                                "extra": {
                                    "rawValue": 1,
                                    "raw": "1"
                                }
                            }
                        }
                    }
                ]
            },
            "alternate": {
                "type": "BlockStatement",
                "directives": [],
                "body": [
                    {
                        "type": "ExpressionStatement",
                        "expression": {
                            "type": "AssignmentExpression",
                            "operator": "=",
                            "left": {
                                "type": "Identifier",
                                "name": "a"
                            },
                            "right": {
                                "type": "NumericLiteral",
                                "value": 2,
                                "extra": {
                                    "rawValue": 2,
                                    "raw": "2"
                                }
                            }
                        }
                    }
                ]
            }
        },
        {
            "type": "ExpressionStatement",
            "expression": {
                "type": "CallExpression",
                "callee": {
                    "type": "MemberExpression",
                    "object": {
                        "type": "Identifier",
                        "name": "console"
                    },
                    "property": {
                        "type": "Identifier",
                        "name": "log"
                    },
                    "computed": false
                },
                "arguments": [
                    {
                        "type": "Identifier",
                        "name": "a"
                    }
                ]
            }
        }
    ]
}

使用字符串占位符

const template = require('@babel/template').default
const generator = require("@babel/generator").default
const { identifier,stringLiteral } = require('@babel/types')
const buildRequire = template(`
  var IMPORT_NAME = require(SOURCE);
`);

const ast = buildRequire({
  IMPORT_NAME: identifier("cunwang"),
  SOURCE: stringLiteral("cunwang"),
});

console.log(generator(ast).code)

or 使用这样 %%XXX%%占位符

  var %%IMPORT_NAME%% = require(%%SOURCE%%);

这里我们使用 generator 来打印AST 生成的代码,更直观的感受字符串占位符的效果!

var cunwang = require("cunwang");

不使用占位符,使用es6模板字符串代替

刚开始我就在想为什么不能直接使用模板字符串呢?其实是 template 这个不支持内部嵌入外部通过模板字符串引入的变量!

const template = require('@babel/template').default
const generator = require('@babel/generator').default
const { identifier, stringLiteral } = require('@babel/types')

const IMPORT_NAME = 'cunwang',
	SOURCE = 'cunwang'
const buildRequire = template(`
  var ${IMPORT_NAME} = require(${SOURCE});
`)

console.log(generator(buildRequire).code)

image-20231124150806510

如果想使用模板字符串,那种方式更方便 通过简单的方法将字符串解析为 AST,则可以使用 .ast 版本的模板。

const template = require('@babel/template').default
const generator = require('@babel/generator').default
const { identifier, stringLiteral } = require('@babel/types')

const IMPORT_NAME = 'cunwang',
	SOURCE = 'cunwang'
const buildRequire = template.ast(`
  var ${IMPORT_NAME} = require(${SOURCE});
`)

console.log(generator(buildRequire).code)

image-20231124150916845

这些方法同样也接受一些 options 具体可见文档 https://babeljs.io/docs/babel-template#options

下面是一个保留注释的配置options

const template = require('@babel/template').default
const generator = require('@babel/generator').default
const { identifier, stringLiteral } = require('@babel/types')

const IMPORT_NAME = 'cunwang',
	SOURCE = 'cunwang'
const buildRequire = template.ast(
	`
  // 注释
  var ${IMPORT_NAME} = require(${SOURCE});
`,
	{
		preserveComments: true, // 保留注释
	}
)

console.log(generator(buildRequire).code)

输出

Administrator@□□□□ MINGW64 /e/better/babel_go
$ node app.js 
// 注释
var cunwang = require(cunwang);

@babel/generator

Turns an AST into code.

这个提供了一些将AST转换为目标代码字符串的能力(上面有使用过)

export default function generate(
    ast: t.Node,
    opts?: GeneratorOptions,
    code?: string | { [filename: string]: string },
): GeneratorResult;
  • 第一个参数是要打印的 AST。

  • 第二个参数是 options,指定打印的一些细节,比如通过 comments 指定是否包含注释,通过 minified 指定是否包含空白字符。

  • 第三个参数当多个文件合并打印的时候需要用到文档

    image-20231124155029272

image-20231124155138108

可以看到,虽然转为AST设置了保留注释,但是generator生成code字符串因为配置了不保留注释,所以注释仍然被干掉了~

配置了 minified 代码可以去掉的空白字符也被干掉了!

生成sourcemap

const generate = require('@babel/generator').default
const { parse } = require('@babel/parser')
const a = 'var a = 1;'
const b = 'var b = 2;'
const astA = parse(a, { sourceFilename: 'a.js' })
const astB = parse(b, { sourceFilename: 'b.js' })
const ast = {
	type: 'Program',
	body: [].concat(astA.program.body, astB.program.body),
}

const { code, map } = generate(
	ast,
	{ sourceMaps: true },
	{
		'a.js': a,
		'b.js': b,
	}
)
console.log(map)
{
  version: 3,
  file: undefined,
  names: [ 'a', 'b' ],
  sourceRoot: undefined,
  sources: [ 'a.js', 'b.js' ],
  sourcesContent: [ 'var a = 1;', 'var b = 2;' ],
  mappings: 'AAAA,IAAIA,CAAC,GAAG,CAAC;ACAT,IAAIC,CAAC,GAAG,CAAC'
}

@babel/code-frame

https://babeljs.io/docs/babel-code-frame

babel 的报错一半都会直接打印错误位置的代码,而且还能高亮,

我们打印错误信息的时候也可以用,就是 @babel/code-frame 这个包。

const { codeFrameColumns } = require('@babel/code-frame')

// 原始 code 字符串
const rawLines = `class Foo {
  constructor() {
    console.log("hello");
  }
}`

// 指定位置
const location = {
	start: { line: 2, column: 17 },
	end: { line: 4, column: 3 },
}

const result = codeFrameColumns(rawLines, location, {
	/* options */
	highlightCode: true, // 高亮代码
})

console.log(result)

执行效果

image-20231124161414337

Options

highlightCode boolean默认为 false,切换语法高亮显示代码的 JavaScript 终端。
linesAbove 行以上number, defaults to 2. 提示的地方上面展示几行代码(调整在错误上方显示的行数。)
linesBelow number,默认为 3 (调整显示在错误下方的行数。)
forceColor boolean, defaults to false.启用此选项可强制将代码以 JavaScript 语法高亮显示(适用于非终端);覆盖 highlightCode。
message string, 字符串,否则不显示输入一个字符串,该字符串将在代码中高亮显示的位置旁边内嵌显示(如果可能)。如果无法内嵌显示,则将置于代码框架上方。
const { codeFrameColumns } = require('@babel/code-frame')

// 原始 code 字符串
const rawLines = `class Foo {
  constructor() {
    console.log("hello");
  }
}`

// 指定位置
const location = {
	start: { line: 2, column: 17 },
	end: { line: 4, column: 3 },
}

const result = codeFrameColumns(rawLines, location, {
	/* options */
	highlightCode: true, // 高亮代码
	message: 'Some custome error!',
})

console.log(result)
Administrator@□□□□ MINGW64 /e/better/babel_go
$ node app.js 
  1 | class Foo {
> 2 |   constructor() {
    |                 ^
> 3 |     console.log("hello");
    | ^^^^^^^^^^^^^^^^^^^^^^^^^
> 4 |   }
    | ^^^ Some custome error!
  5 | }

@babel/core

https://babeljs.io/docs/babel-core

babel 的功能就是通过前面讲的包实现的(@babel/parser、@babel/traverse、@babel/generaotr、@babel/types、@babel/template 等)

babel 基于这些包来实现编译、插件、预设等功能的包就是 @babel/core。完成整个编译流程,从源码到目标代码,生成 sourcemap。实现 pluginpreset 的调用。

看一些API,注意下面有一些API的过期提示 例如 babel.transform 这种只能在babel 6 版本使用

In Babel 6, this method was synchronous and transformSync did not exist. For backward-compatibility, this function will behave synchronously if no callback is given. If you’re starting with Babel 7 and need synchronous behavior, please use transformSync since this backward-compatibility will be dropped in Babel 8.

在 Babel 6 中,该方法是同步的,而 transformSync 并不存在。为了向后兼容,如果没有给出回调,本函数将同步运行。如果您使用的是 Babel 7,并且需要同步行为,请使用 transformSync,因为这种向后兼容性将在 Babel 8 中取消。

注意:不带 sync、async 的 api 已经被标记过时了,也就是 transformXxx 这些,后续会删掉,不建议用

直接用 transformXxxSynctransformXxxAsync。也就是明确是同步还是异步。

如果用了错误的版本会报错!

image-20231124165546196

@babel/core 支持 pluginpreset,一般我们配置的都是对象的格式,其实也有一个 api 来创建,也就是 createConfigItem

createConfigItem(value, options) // configItem
0

评论区