Webpack / Vue.js:在编译时使用 ESM 依赖项生成模块代码
Webpack / Vue.js: generate module code at compile-time using ESM dependencies
环境: 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
(“意外令牌'export'”错误)。
有没有办法让 val-loader
以某种方式加载 ESM 模块? 请注意,我不打算使用 val-loader
任何其他技术达到同样的目标同样受欢迎。
在了解了关于这个问题和 node/webpack 内部的更多信息之后,似乎有两种可能的方法可以从 CJS 导入 ESM:
使用动态 import()
。但它是异步的,这使得它不适合这里,因为 val-loader
需要同步结果。
将ESM转译为CJS,这是我采用的方法。
在我的例子中,完全转译是矫枉过正,重写 imports/exports 就足够了,所以我使用 ascjs to rewrite the ESM files, along with 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 imports/exports。因此 big-module
或其依赖项需要 CJS 文件是可以的。
_eval
的第三个参数启用递归重写,否则只有顶级文件(传递给 requireESM
的文件)被翻译。
环境: 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
(“意外令牌'export'”错误)。
有没有办法让 val-loader
以某种方式加载 ESM 模块? 请注意,我不打算使用 val-loader
任何其他技术达到同样的目标同样受欢迎。
在了解了关于这个问题和 node/webpack 内部的更多信息之后,似乎有两种可能的方法可以从 CJS 导入 ESM:
使用动态
import()
。但它是异步的,这使得它不适合这里,因为val-loader
需要同步结果。将ESM转译为CJS,这是我采用的方法。
在我的例子中,完全转译是矫枉过正,重写 imports/exports 就足够了,所以我使用 ascjs to rewrite the ESM files, along with 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 imports/exports。因此big-module
或其依赖项需要 CJS 文件是可以的。_eval
的第三个参数启用递归重写,否则只有顶级文件(传递给requireESM
的文件)被翻译。