写一个Babel 7 插件

634 阅读2分钟
原文链接: zhuanlan.zhihu.com

Babel 已经更新到7了。plugin-handbook还是三年前,有一些文档比较陈旧,这里整理一下7相关的api.整体的流程差不多,先阅读

jamiebuilds/babel-handbook

Babel 主要分Parse->Transform->Generator三个部分。

Parse 原本用的是babylon. transform用的babel-traverse,以及generator用的babel-generator。现在已经全部封装在一个包里面

@babel/core

这个包提供了一个transform方法,plugin里面返回一个visitor对象即可。具体可看

@babel/core · Babel

当然,上面的几个包都还可以用的,只是从a-b的形式,改成了@a/b形式,如下,其中babylon变成了parser

export * as types from "@babel/types";
export { tokTypes } from "@babel/parser";

export { default as traverse } from "@babel/traverse";
export { default as template } from "@babel/template";

例子,旧代码转换。

 define([
        'base/klass',
        'base/element',
        'base/event',
        'util/template/tpl',
        '{pro}module/module.js'
    ], function (_k, _e, _v, _t0, _m, _tpl, _p) {
        var _pro;

        _p._?ModuleSlothOperation = _k._$klass();
        _pro = _p._?ModuleSlothOperation._$extend(_m._?Module);

        _pro.__doBuild = function () {
            this.__body = _e._$html2node(
                _t0._$getTextTemplate('tpl-sloth-operation-view')
            );
            var _flag = _e._$getByClassName(this.__body, 'j-flag');
            this.__export = {
                parent: _flag[0]
            };
        };

        _m._$regist('sloth-operation-view', _p._?ModuleSlothOperation);
    });

上诉是老的define AMD格式旧代码,希望能转成ES6 import 模式,如下。

import _k from "base/klass";
import _e from "base/element";
import _v from "base/event";
import _t0 from "util/template/tpl";
import _m from "{pro}module/module.js";

var _pro;

_p._?ModuleSlothOperation = _k._$klass();
_pro = _p._?ModuleSlothOperation._$extend(_m._?Module);

_pro.__doBuild = function () {
  this.__body = _e._$html2node(_t0._$getTextTemplate('tpl-sloth-operation-view'));

  var _flag = _e._$getByClassName(this.__body, 'j-flag');

  this.__export = {
    parent: _flag[0]
  };
};

_m._$regist('sloth-operation-view', _p._?ModuleSlothOperation);

碰到的问题

1. 如何将define变成只有参数数组,并且转为import的模式。

2. function body如何截取出来。

思路

1.将define节点替换,用replace方法,然后转化但是这样难度比较大。

2.提取数组->transform,然后重新插入到ast树里面,最后generate code.这种方式方便,因此被采用。

对于参数数组处理。

let defineList = path.parent.arguments[0].elements;
let defineFnParams = path.parent.arguments[1].params;

上面的节点的层次结构得到。这个就用到

AST explorer

把代码帖进去,就可以看到完整的ast结构。

然后可以用@babel/template,来处理。参数大写。数据的类型同样可以看astexplorer.

template(`import NAME from 'LIB' `);
 var imp = buildImport({
                        NAME: t.identifier(defineFnParams[i].name),
                        LIB: t.stringLiteral(defineList[i].value)
                    });

完整的代码可以看。

xff1874/nej2es6