Babel将import导入commonjs的codemod

时间:2019-04-19 23:48:27

标签: javascript node.js import babel require

我正在寻找一种将整个节点项目的Babel import转换为CommonJS风格的require()的方法。目标是摆脱通天塔。

考虑到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的旧节点项目,随着时间的流逝,它变得越来越多余,所以我敢肯定有人以前对此做了巧妙的解决方案。

3 个答案:

答案 0 :(得分:2)

我挖掘了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,何时不添加。解决此问题的唯一正确方法是用require()包装_interopRequireDefault并进行运行时检查。

如果编译器确实跟踪所需的模块并检查它是ES模块还是commonjs模块,则它可以判断是否需要.default。但是babel是围绕一次一次文件模式设计的,因此它不可能为您做到这一点。

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


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

您可以派生babel-plugin-transform-modules-commonjs源代码,删除_interopRequireDefault包装逻辑,然后使用解析器执行上述check-if-requiree-is-esmodule工作,然后查看是否{{1 }}在输出中是必需的。

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

答案 1 :(得分:1)

简单的解决方案,在可以使用我使用VSCode的所有文件的编辑器中打开源代码,并在node_modules文件夹上设置忽略,如果需要多个导出,我可以用全方式对所有文件进行正则表达式替换吗?

RegEx方式

搜索:

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')

这里有些缺点,不能完全使用多个导出

很远

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

这将留下模块所需的所有支持内容,包括对导出默认_interopRequireDefault()的支持。摆脱此问题的唯一方法是创建自己的插件,而不执行此操作。

第1步

确定ECMA babel为此使用了什么。所以我去了https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import

它表明自ES6(也称为ECMA2015)以来,导入已成为规范的一部分

第2步

预设只是一组软件包,因此请标识该特定转换的软件包。

打开我的package.json,寻找babel-preset-es2015找到了它。转到node_moduels \ 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",

第3步

进行了一些测试,因此使用--plugins=参数进行通天塔测试,我测试了每个文件在一组2个文件中的作用,一个文件又需要另一个文件进行测试,我测试了每个文件,得出的结果是commonjs版本require();

第4步

进行转换

因此请确保您已安装以下节点模块bable-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

答案 2 :(得分:0)

为此目的,您可以使用名为 putout@putout/plugin-convert-esm-to-commonjs 中的 plugin。它转换:

import {readFile} from 'fs/promises';

致:

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

要完成工作,请使用以下命令设置基线:

npx putout . --disable-all

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

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

并应用修复:

npx putout . --fix