esm2umd
Fork of esm2umd, namespaces under
@leichtgewicht
that transforms all .mjs files found in a folder to .cjs files and makes sure that they run in older node versions by replacing the package name inside the files.
Transforms ESM to UMD, i.e. to use ESM by default with UMD as a legacy fallback.
Usage
npx @leichtgewicht/esm2umd MyModule
MyModule
is used as the name of the vanilla JS global.
If the module has a default
export, it becomes the value obtained when require
d.
API
Installation as a dependency is optional (pulls in megabytes of babel), but if so desired exposes the CLI as an API:
import esm2umd from 'esm2umd'
const esmCode = '...'
const umdCode = esm2umd('ModuleName', esmCode)
Example
ESM-first hybrid module with legacy fallback and prepublish build step.
package.json
{
"type": "module",
"main": "./umd/index.js",
"types": "index.d.ts",
"exports": {
"import": "./index.js",
"require": "./umd/index.js"
},
"scripts": {
"build": "npx esm2umd MyModule",
"prepublishOnly": "npm run build"
}
}
Treat .js files in umd/
as CommonJS.
umd/package.json
{
"type": "commonjs"
}
Keep the generated artifact out of version control to avoid PRs against it.
.gitignore
umd/index.js
For typings, if there is a default
export, stick to the "old" format for compatibility.
index.d.ts
export = MyModule;
Filename
__filename
is not available in es modules while the import.meta.url
is not available
in common js modules. Since older Node.js versions do not support URL's in fs
operations
the easiest way to work with fs
operations is to use in node.js specific code.
The __filename
equivalent in es modules is:
import path from 'path'
const filename = decodeURI(import.meta.url).replace(/^file:\/\/(\/(\w+:))?/, '$2').replace(/\//g, path.sep);
@leichtgewicht/esm2umd
will automatically replace the above string to const filename = __filename
in umd modules.
Why is this so long?
Now, you might be wondering why the filename is soo complicated, so lets explain all parts here.
-
import.meta.url
contains a URL and spaces (and other special characters) are escaped like/my%20folder/
decodeURI(...)
will replace the encodings. -
import.meta.url
prefixes the path withfile://
but on windows it will also prefix an additional/
before theC:/...
. In the regexp^file:\/\/(\/(\w+:))?
the second part(\/(\w+:))?
will only match on windows¥ and put theC:
in the$2
variable. So the.replace(..., '$2')
call will remove thefile://
prefix on *nix and if there is aC:
on windows, it will also replace the unnecessary/
. -
import.meta.url
is normalized for the/
prefix but the__filename
on windows is separated using the\
character. To have consistent behavior.replace(/\//g, path.sep)
will do nothing on *nix, but on windows it will make sure that the/
is replaced by\
.