Context
Jiti's lazy-loading approach for the Babel transform is the best option for most use cases. Since the Babel transform requires loading a substantial dependency, deferring its load until needed provides meaningful performance benefits when the transform isn't required. However, I think I ran into a use case where it might make sense to eagerly load it.
We work with a runtime environment that only supports CommonJS execution. The workflow involves:
- Writing source code in ESM for better developer experience
- Using
jiti to dynamically load configuration files at runtime
- Bundling everything with Webpack into a single CJS file for deployment
import { createJiti } from "jiti";
async function loadConfig(name) {
const jiti = createJiti(__filename);
const path = resolve(__dirname, `./config/config.${name}`);
const config = await jiti.import(path, { default: true });
return config;
}
Problem
When bundling with Webpack, the ESM version of jiti (dist/lib/jiti.mjs) contains this code:
let _transform;
function lazyTransform(...args) {
if (!_transform) {
_transform = createRequire(import.meta.url)("../dist/babel.cjs");
}
return _transform(...args);
}
And Webpack's default behavior is to replace import.meta.url with a static file path at build time:
// After Webpack processes it
_transform = createRequire("file:///Users/username/project/node_modules/jiti/lib/jiti.mjs")("../dist/babel.cjs");
This hardcoded absolute path doesn't exist in the production runtime, causing the lazy load to fail:
Error: Cannot find module '../dist/babel.cjs'
Workaround
I managed to workaround it by using the CJS distributed version via require('jiti') as it does not use import.meta.url under the hood:
const { createJiti } = require("jiti"); // Uses lib/jiti.cjs
Webpack can properly handle and bundle this require() call. However, the CommonJS entry point is marked as deprecated, and I'm concerned about future compatibility.
Proposed solutions
Of course, if someone has a better solution please let me know. From my point of view I think either of these two options below could work:
Option 1: Configuration Flag
const jiti = createJiti(__filename, {
eagerTransform: true // Loads babel immediately
});
Option 2: Separate Entry Point
import { createJiti } from "jiti/eager";
Additional information
Context
Jiti's lazy-loading approach for the Babel transform is the best option for most use cases. Since the Babel transform requires loading a substantial dependency, deferring its load until needed provides meaningful performance benefits when the transform isn't required. However, I think I ran into a use case where it might make sense to eagerly load it.
We work with a runtime environment that only supports CommonJS execution. The workflow involves:
jitito dynamically load configuration files at runtimeProblem
When bundling with Webpack, the ESM version of
jiti(dist/lib/jiti.mjs) contains this code:And Webpack's default behavior is to replace
import.meta.urlwith a static file path at build time:This hardcoded absolute path doesn't exist in the production runtime, causing the lazy load to fail:
Workaround
I managed to workaround it by using the CJS distributed version via
require('jiti')as it does not useimport.meta.urlunder the hood:Webpack can properly handle and bundle this require() call. However, the CommonJS entry point is marked as deprecated, and I'm concerned about future compatibility.
Proposed solutions
Of course, if someone has a better solution please let me know. From my point of view I think either of these two options below could work:
Option 1: Configuration Flag
Option 2: Separate Entry Point
Additional information