使用@babel/register注册hook对nodejs支持加载ES模块文件

1,041 阅读2分钟

背景

最近在改造公司原有前端框架CLI,需要在兼容原有配置文件的前提下,通过webpack对文件进行读取组装。原有配置文件是使用的ESModule模块写法导出,nodejs直接读取ESModule模块文件会报错。例如:

export default routes;
^^^^^^

最近在阅读Taro源码中,发现他们使用了@babel/register,为 require 方法注册了 babel,为它加上一个钩子。此后,每当使用 require 加载指定文件,就会先用 Babel 进行转码。

那我们就可以利用这个功能,对CLI加载的ESModule文件进行一次编译后再读取。

@babel/register

先来介绍一下@babel/register

使用 Babel 的方法之一就是通过 require 钩子(hook),require 钩子将自身绑定到 node 的 require 模块上,并在运行时自动编译文件。

安装

@babel/register 只有一个功能,就是重写 node 的 require 方法。

安装命令如下所示:

npm install @babel/core @babel/register --save-dev

安装好后可以通过 require 引用,如下所示:

require("@babel/register");

@babel/register 在底层改写了 node 的 require 方法,在代码里引入 @babel/register 模块后,所有通过require引入并且以 .es6,.es,.jsx, .mjs,和 .js 为后缀名的模块都会被 Babel 转译。

指定参数

require("@babel/register")({
  ignore: [
    /regex/,
    function(filepath) {
      return filepath !== "/path/to/es6-file.js";
    },
  ],
  only: [
    /my_es6_folder/,
    function(filepath) {
      return filepath === "/path/to/es6-file.js";
    }
  ],
  extensions: [".es6", ".es", ".jsx", ".js", ".mjs"],
  cache: true,
});

或者还可以传递其他的参数,例如 plugins 和 presets。需要注意的是,配置文件也将被加载,并且编程方式的配置也将被合并进来,放在这些配置项的顶部。

具体实现

原有文件:

// src/pages/moduleName/router.js
const router = [
    // ...
]

export default routes;

现在需要通过nodejs读取此类文件,直接在nodejs代码中requier读取报错。我们在代码require之前进行require 钩子(hook)注册。

babel插件

首先,我们需要知道babel哪个插件是用于编译commonjs文件的。

通过官网了解,babel用于ESModule文件读取的插件为@babel/plugin-transform-modules-commonjs

  • 安装babel插件
npm install --save-dev @babel/plugin-transform-modules-commonjs

注册函数

实现 createBabelRegister 注册函数。

function createBabelRegister() {
    require('@babel/register')({
        only: [
            // 只对指定文件进行编译
            function (filepath) {
                return /pages.*router\.js/.test(filepath);
            }
        ],
        plugins: [
            // 支持js文件的commonjs导出
            '@babel/plugin-transform-modules-commonjs'
        ],
        babelrc: false,
        configFile: false,
        cache: false
    });
}

代码调用

我们在代码require之前进行注册即可。

// 调用注册hook
createBabelRegister();

//...
const routerContent = require(path.resolve(process.cwd(), `./src/pages/${item}/router.js`)).default;

console.log(routerContent);
//...

保存重新运行cli命令,观察打印内容正常展示:

微信截图_20220226104558.png

说明功能实现了,最后优化下代码。

// createBabelRegister.js
function createBabelRegister({ only }) {
    require('@babel/register')({
        only: Array.from(new Set([...only])),
        plugins: [
            // 支持js文件的ESModule导出
            '@babel/plugin-transform-modules-commonjs'
        ],
        extensions: ['.js'],
        babelrc: false,
        configFile: false,
        cache: false
    });
}
// 注册时指定文件内容
createBabelRegister({
    only: [(filepath) => /pages.*router\.js/.test(filepath)]
});

@babel/register
@babel/plugin-transform-modules-commonjs
Taro-createBabelRegister源码