Environment: webpack 5.44 + vue.js 3.0 + node 12.21
I'm trying to generate a module at compile-time, in order to avoid a costly computation at run-time (as well as 10Mb of dependencies that will never be used except during said computation). Basically run this at compile-time:
import * as BigModule from "big-module";
function extract_info(module) { ... }
export default extract_info(BigModule);
which will be imported at run-time as:
export default [ /* static info */ ];
I tried using val-loader (latest 4.0) which seems designed exactly for this use case.
Problem: big-module
is an ESM, but val-loader
apparently only supports CJS. So I can neither import
("Cannot use import statement outside a module" error) nor require
("Unexpected token 'export'" error).
Is there any way to make val-loader
somehow load the ESM module? Note that I'm not bent on using val-loader
, any other technique that achieves the same goal is just as welcome.
After learning way more than I wanted about this issue and node/webpack internals, there seems to be two possible approaches to import ESM from CJS:
Use dynamic import()
. But it is asynchronous which makes it unfit here, as val-loader
requires a synchronous result.
Transpile the ESM into CJS, which is the approach I took.
In my case, full transpiling is overkill and rewriting imports/exports is sufficient, so I'm using ascjs to rewrite the ESM files, along with eval to safely evaluate the resulting string.
All in all:
// 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);
Note that:
ascjs
is safe to use on CJS modules, since it only rewrites ESM imports/exports. So it's OK for big-module
or its dependencies to require CJS files._eval
enables recursive rewriting, otherwise only the top-level file (the one passed to requireESM
) is translated.