@ms-cloudpack/esm-stub-utilities
v0.15.49
Published
Generates ESM stubs for CommonJS entry files.
Downloads
1,789
Keywords
Readme
@ms-cloudpack/esm-stub-utilities
This library contains utilities for generating ESM stubs for CommonJS modules. Some bundlers require this for extracting named exports needed to produce a browser-compatible ESM bundle.
Usage
Call writeESMStubsInWorker to generate stubs for CJS entries of a package:
import { writeESMStubsInWorker } from '@ms-cloudpack/esm-stub-utilities';
const esmStub = await writeESMStubsInWorker({
inputPath: '/path/to/package',
entries: {
'./entry1': './cjsEntry1.js',
'./entry2': './cjsEntry2.js',
},
});What is an ESM stub?
"ESM stubs" are a workaround for issues presented by CommonJS modules for Cloudpack's ESM library mode approach. The biggest problem is that CJS has many possible ways to declare exports, some of which are nontrivial or even impossible to find through static analysis. This isn't a problem in a monolithic bundle, but for an ESM bundle in library mode, all the export names must be declared in the bundle output so they can be imported by name in consuming packages. Some bundlers may attempt to convert common patterns to named exports, but others will just provide a default export.
The utilities in this package are used to run the package code, detect the actual exported names, and create an ES module format stub file with the proper exports (which can then be used as the bundler entry point). For example:
// Original file: index.js
module.exports.default = { value: 'I am the default export' };
// Example of a pattern that can't be statically analyzed (suppose it returns "a")
module.exports[computePropertyName()] = 'why';
module.exports.b = 'b';
// Generated stub file: index-stub.mjs
import moduleExport from './index.js';
const { a, b } = moduleExport;
const defaultExport = moduleExport?.default?.default ?? moduleExport?.default;
export default defaultExport;
export { a, b };Currently, our primary approach for detecting export names is to run the code in a worker thread, with a browser-like environment provided for packages that need it. This has multiple significant downsides and we're actively considering other options (tracking issue), but running the code is the only way to approach 100% accuracy.
Special considerations
When evaluating named entries in the exports of the cjs file, the library is loaded in the Node process. A few libraries are used to simulate the browser environment in order for the script to load. (e.g. some libraries will reference window on load, and therefore must be run in an environment that accommodates this.)
Libraries which include a default entry in their exports will have that value preserved as the default export in the stub. However, libraries which export an object that doesn't have a default key will have the entire object exported as the default.
Libraries which export a function or literal value as the exports result will have a default export for that entry.
Some libraries don't export anything. In this case, the stub will simply import the entry.
Exported members that are keywords will be ignored. (e.g. module.exports = { 'delete': "foo" }; would be considered an empty export.)
