cbor-redux
cbor-redux copied to clipboard
Support ESM in Node, or move to ESM-only
First, thanks so much for writing this library! It's been awesome to watch the Deno ecosystem grow, and great to have a minimal modern CBOR implementation.
I'm writing an ESM-only library for use in Node, the browser, and Deno. However, the way cbor-redux is structured doesn't actually let me use it in Node by default:
// test.mjs
import { CBOR } from "cbor-redux"
console.log(CBOR)
% node test.mjs
file:///Users/.../test.mjs:1
import { CBOR } from "cbor-redux";
^^^^
SyntaxError: Named export 'CBOR' not found. The requested module 'cbor-redux' is a CommonJS module, which may not support all module.exports as named exports.
CommonJS modules can always be imported via the default export, for example using:
import pkg from 'cbor-redux';
const { CBOR } = pkg;
at ModuleJob._instantiate (internal/modules/esm/module_job.js:121:21)
at async ModuleJob.run (internal/modules/esm/module_job.js:166:5)
at async Loader.import (internal/modules/esm/loader.js:178:24)
at async Object.loadESM (internal/process/esm_loader.js:68:5)
This is because Node.js supports native ESM modules, but only interprets an imported library as an ESM module if it has "type": "module"
in package.json.
This is independent of the exports
map in package.json. Node.js is trying to load ./esm/CBOR.js
, but when it does will try to parse it as a CommonJS module (which will fail). For example:
// test2.mjs
import CBOR from "cbor-redux"
console.log(CBOR)
% node test2.mjs
(node:23296) Warning: To load an ES module, set "type": "module" in the package.json or use the .mjs extension.
(Use `node --trace-warnings ...` to show where the warning was created)
/Users/.../node_modules/cbor-redux/esm/CBOR.js:22
export class TaggedValue {
^^^^^^
SyntaxError: Unexpected token 'export'
at wrapSafe (internal/modules/cjs/loader.js:988:16)
at Module._compile (internal/modules/cjs/loader.js:1036:27)
at Object.Module._extensions..js (internal/modules/cjs/loader.js:1101:10)
at Module.load (internal/modules/cjs/loader.js:937:32)
at Function.Module._load (internal/modules/cjs/loader.js:778:12)
at ModuleWrap.<anonymous> (internal/modules/esm/translators.js:199:29)
at ModuleJob.run (internal/modules/esm/module_job.js:170:25)
at async Loader.import (internal/modules/esm/loader.js:178:24)
at async Object.loadESM (internal/process/esm_loader.js:68:5)
There are a variety of ways to support this, but the simplest is just moving ESM-only and leaving CommonJS behind entirely. There's an massive ongoing movement across the OSS web ecosystem to do this, summarized by Sindre Sorhus here: https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c
The upside is that you could have a single unified ESM dist
target that works for Node and the browser (Deno is obviously a separate thing); the downside is that it involves tinkering with however the library is built, which is always annoying, and releasing a new major version that no longer support old versions of Node (< v12).
Is this something you'd consider? I know it's exhausting to continually change export formats but I think it's become clear that ESM-only is truly the one long-term stable universal solution for JS.
I'll be cuing this up this month. My plan is to move wholly over to Deno-only support in the repo and then deploy Node-compatible version to NPM using https://github.com/denoland/dnt.
Awesome, that sounds like a great plan for everyone!