首页 > 解决方案 > Babel `import` 的 Codemod 到 commonjs `require`

问题描述

我正在寻找一种将完整节点项目的 Babel imports 转换为 CommonJS-style 的方法require()。目标是摆脱 Babel。

考虑到 node.js 现在内置了 async/await 之类的东西,运行 Babel 感觉是多余的。Babel 目前唯一剩下的就是将 ES6 样式import的 s 转换为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-syntax 并用于require()外部内容。

这不是我第一次有一个运行 Babel 的旧节点项目,随着时间的推移,它变得越来越冗余,所以我确信以前有人已经对此做了很好的解决方案。

标签: javascriptnode.jsimportbabeljsrequire

解决方案


我挖了源代码babel-plugin-transform-modules-commonjs。看起来不可能配置 babel 来输出你想要的结果。

背后的原因是 helpers like 的必要性_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,何时不添加。处理此问题的唯一正确方法是包装require()_interopRequireDefault进行运行时检查。

如果编译器确实跟踪了所需的模块并检查它是 ES 模块还是 commonjs 模块,那么它可以判断是否.default需要。然而,babel 是围绕一次单个文件的模型设计的,所以它不可能为你做到这一点。

我想如果你能找出一个可靠的规则来告诉你什么时候添加 a.default什么时候不添加,那么一个简单的 regex-replace 可能会解决你的问题。


边注。我确实有一些想法可以使用定制的 babel 插件来破解它。

您可以分叉babel-plugin-transform-modules-commonjs源,删除_interopRequireDefault包装逻辑,然后使用解析器执行上述 check-if-requiree-is-esmodule 工作,然后查看.default输出中是否需要。

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


推荐阅读