Babel的Codemod: 将`import`转换为commonjs的`require`

4

我正在寻找一种方法将完整的Node.js项目中的Babel import转换为CommonJS风格的require()。目标是摆脱Babel。

考虑到现在node.js已经内置了像async/await这样的功能,运行Babel就感觉有些多余了。目前Babel所做的唯一事情就是将ES6风格的import转换为require()

我一直在搜索,但找不到任何优雅的半自动化解决方案。编译Babel的输出结果不够清晰,不能直接复制而需大量手动工作。

如果我有一个像这样的输入文件:

import express from 'express'
import bodyParser from 'body-parser'
import authMiddleware from './middlewares/auth'
import { get } from 'lodash'

export const myVar = 1
export default function doSomething() {
  // ...
}

...我希望你能为我提供类似于以下内容的输出

const express = require('express')
const bodyParser = require('body-parser')
const authMiddleware = require('./middlewares/auth').default
const { get } = require('lodash')

export.myVar = 1
export.default = function doSomething() {
  // ...
}

另一种选择是将文件转换为 .mjs 语法来处理相对路径,并在外部使用 require()

这不是我第一次在运行 Babel 的旧的 node 项目中遇到越来越多冗余的情况,因此我相信有人之前已经找到了一个很好的解决方案。


那么您只想要将 import/export 语法转换,但保持所有其他代码不变,是这样理解吗? - hackape
理想情况下是这样的,但我不介意进行一些小的格式更改,因为我已经设置了一个 eslint --fix 的 precommit 钩子。 - Alex
3个回答

3
简单的解决方案是在能够浏览所有文件的编辑器中打开源代码,我使用VSCode,并在node_modules文件夹上设置忽略,在所有文件上进行正则表达式替换,如果需要多个导出,则以下是完整的方法。 正则表达式方式 搜索:
import[\s*]([a-zA-Z0-9,]*)[\s*]from[\s*]['|"]([a-zA-Z0-9\{\},\.\/\\]*)['|"][\s*]

替换为

const $1 = require('$2')

如果您使用as,也要这样做。 搜索:
import[\s*][a-zA-Z0-9,]*[\s*]as[\s*]([a-zA-Z0-9]*)[\s*]from[\s*]['|"]([a-zA-Z0-9\{\},\.\/\\]*)['|"][\s*]

替换为:

替换为

const $1 = require('$2')

这里有一些缺点,你不能使用多个导出项,需要使用完整的方式。

长方法

好的,对于任何感兴趣的人,这是我用来解决问题的过程,然后您可以将源代码从构建文件夹中复制到新位置作为新源或覆盖旧的src,然后从项目中删除所有babel (npm prune)。

这将保留该模块所需的所有支持内容,包括支持 export default _interopRequireDefault() 的支持。唯一摆脱它的方法是创建自己的插件而不执行此操作。

第1步

确定ECMA babel使用的内容。因此,我转到https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import

它显示自ES6(也称为ECMA2015)以来,import 已成为规范的一部分。

第2步

预设只是一组软件包,因此要确定特定转译的软件包。

我打开了 package.json 文件并查找了 babel-preset-es2015 ,找到了它。然后进入 node_modules\babel-preset-es2015,打开它的 package.json 文件以查找。

    "babel-plugin-transform-es2015-modules-amd": "^6.24.1",
    "babel-plugin-transform-es2015-modules-commonjs": "^6.24.1",
    "babel-plugin-transform-es2015-modules-systemjs": "^6.24.1",
    "babel-plugin-transform-es2015-modules-umd": "^6.24.1",

第三步

进行一些测试,使用--plugins=参数来测试babel的每个插件在两个文件中一个文件需要另一个文件进行测试,我发现需要commonjs版本才能使用require();

第四步

进行转换

确保已安装以下节点模块:babel-cli, babel-core, babel-plugin-transform-es2015-modules-commonjs

然后启动CLI并执行以下操作:

babel --plugins=transform-es2015-modules-commonjs ./src/ --out-dir build/

摘自https://babeljs.io/docs/en/babel-cli


1
非常好的指南!关于第3步骤的说明,https://babeljs.io/repl有一个名为“时间旅行”的功能,以令人惊叹的方式分解“哪个插件完成了工作的哪个部分”。值得一试。 - hackape

3
我找到了babel-plugin-transform-modules-commonjs的源代码。看起来不可能通过配置Babel来输出您想要的结果。
原因在于,像_interopRequireDefault这样的助手仍然是必需的,因为ES模块与commonjs不兼容,特别是export default
例如:
// input
import bodyParser from 'body-parser'
import authMiddleware from './middlewares/auth'

// your desired output
const bodyParser = require('body-parser') // <-- no default
const authMiddleware = require('./middlewares/auth').default // <-- default

// actual babel output
var _bodyParser = _interopRequireDefault(require("body-parser"));
var _auth = _interopRequireDefault(require("./middlewares/auth"));

您没有办法确定何时添加.default和何时不添加。处理这个问题的唯一正确方式是使用_interopRequireDefault包装require()并进行运行时检查。

如果编译器追踪所需的模块并检查它是否为ES模块或commonjs模块,则可以知道是否需要使用.default。但是babel是基于单个文件模型设计的,因此无法为您执行此操作。

我认为,如果您能够找到可靠的规则来告诉您何时添加.default以及何时不添加,则可能可以通过简单的正则表达式替换来解决问题。


顺便说一下,我有一些想法可以使用自定义babel插件来解决这个问题。

您可以fork babel-plugin-transform-modules-commonjs源代码,删除_interopRequireDefault包装逻辑,然后使用解析器来执行上述检查-如果-requiree-is-esmodule工作,然后查看输出是否需要.default

但是,说起来容易做起来难,这需要一些认真的努力。


非常感谢您的回复。我知道为什么要使用 _interopRequireDefault。但是在我的情况下,我基本上只是想将所有相对路径重映射为 require().default,将所有第三方重映射为 require()。也许其中一些已经是 ES 模块化了,但如果它们引起问题,我可以手动使用正则表达式解决。 - Alex
是的,我前几天实际上正在研究这个问题,但是我不得不学习很多关于Babel的知识,所以手动完成似乎更容易。 - Alex

1
你可以使用putout插件@putout/plugin-convert-esm-to-commonjs来实现此目的。它可以将以下内容转换为CommonJS格式:
import {readFile} from 'fs/promises';

至:

const readFile = require('fs/promises');

为了完成任务,请设置基线:

npx putout . --disable-all

在配置文件.putout.json中启用convert-esm-to-commonjs

{
    "rules": {
        "convert-esm-to-commonjs": "on"
    }
}

并应用修复:

npx putout . --fix

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接