Webpack / Vue.js:在编译时使用 ESM 依赖项生成模块代码

问题描述

环境: webpack 5.44 + vue.js 3.0 + node 12.21

我试图在编译时生成一个模块,以避免在运行时进行昂贵的计算(以及 10Mb 的依赖项,除非在所述计算期间,否则永远不会使用这些依赖项)。基本上在编译时运行:

import * as BigModule from "big-module";
function extract_info(module) { ... }
export default extract_info(BigModule);

将在运行时导入为:

export default [ /* static info */ ];

我尝试使用 val-loader(最新的 4.0),它似乎专为这个用例设计。

问题: big-module 是 ESM,但 val-loader 显然只支持 CJS。所以我既不能import(“不能在模块外使用导入语句”错误)也不能require(“意外令牌'导出'”错误)。

有什么方法可以让 val-loader 以某种方式加载 ESM 模块?请注意,我不打算使用 val-loader,任何其他实现相同目标的技术同样受欢迎。

解决方法

在对这个问题和 node/webpack 内部结构进行了比我想要的更多了解之后,似乎有两种可能的方法可以从 CJS 导入 ESM:

  1. 使用动态 import()。但它是异步的,因此不适合这里,因为 val-loader 需要同步结果。

  2. 将 ESM 转译为 CJS,这是我采用的方法。

就我而言,完全转译是多余的,重写导入/导出就足够了,所以我使用 ascjs 重写 ESM 文件,并使用 eval 来安全地评估结果字符串。

总而言之:

// require-esm.js

const fs = require('fs');
const ascjs = require('ascjs');
const _eval = require('eval');
function requireESM(file) {
  file = require.resolve(file);
  return _eval(ascjs(fs.readFileSync(file)),file,{ require: requireESM },true);
}
module.exports = requireESM;

// val-loader-target.js

const requireESM = require('./require-esm');
const BigModule = requireESM('big-module');
function extract_info(module) { ... }
module.exports = extract_info(BigModule);

注意:

  • ascjs 在 CJS 模块上使用是安全的,因为它只重写 ESM 导入/导出。因此,big-module 或其依赖项可以要求 CJS 文件。
  • _eval 的第三个参数启用递归重写,否则只会翻译顶级文件(传递给 requireESM 的文件)。

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...