-
Notifications
You must be signed in to change notification settings - Fork 1.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Dual package hazard #168
Comments
This is more like a feature than an issue but I'll leave it open so users are aware of such possibilities like this 😉 |
Would it perhaps be useful to add such documentation to the readme? 🤔 I think that might be better than a perpetually open issue 🤣 |
#172 fixes this problem so closing. |
I don't think so? There's still two copies of the code: one that gets run when you @jcbhmr ➜ /workspaces/js-base64 (main) $ node
Welcome to Node.js v20.11.0.
Type ".help" for more information.
> let esm = await import("js-base64")
undefined
> let cjs = require("js-base64")
undefined
> esm
[Module: null prototype] {
// ...
}
> cjs
{
// ...
}
> esm.atob === cjs.atob
false
> esm.Base64 === cjs.Base64
false
> using commit 53644d0 |
The solution is to treat one implementation as "the implementation" (probably the CJS one) and then just re-export that in the ESM version like this: // base64.mjs
import exports from "./base64.js";
export const {
version,
VERSION,
atob,
atobPolyfill,
btoa,
btoaPolyfill,
fromBase64,
toBase64,
encode,
encodeURI,
encodeURL,
utob,
btou,
decode,
isValid,
fromUint8Array,
toUint8Array,
extendString,
extendUint8Array,
extendBuiltins,
Base64,
} = exports;
export default exports; @jcbhmr ➜ /workspaces/js-base64 (main) $ node
Welcome to Node.js v20.11.0.
Type ".help" for more information.
> let esm = await import("js-base64")
undefined
> let cjs = require("js-base64")
undefined
> esm.atob === cjs.atob
true
> esm.Base64 === cjs.Base64
true
> |
It is not only okay but necessary for const funcA = function(){}
const funcB = function(){}
funcA === funcB // false
|
You're absolutely right! The functions would be different! And that's actually exactly what's happening. There's two pieces of code running each creating their own function that is therefore NOT This rears its head in bundlers since bundlers will then include both the base64.js and the base64.mjs file effectively making the library 2x the size lol. It can also cause issues with The solution is to delegate to the "canonical" CJS function and wrap it in a nice ESM interface with appropriate named exports. Normally Node.js does this for you using https://github.com/nodejs/cjs-module-lexer to extract the named exports so you can normally do The official Node.js docs recommend this solution:
|
Sure it doubles the size of the library but it is still 8.3kB. It also make both cjs and mjs stand alone which matters a lot especially when you want to use CDN versions like: <script src="https://cdn.jsdelivr.net/npm/[email protected]/base64.min.js"></script>
|
Yes, it's minor. I'll drop this since it's OK to duplicate code since you're right it's small enough to not matter 👍 For CDNs you're right that raw esm code is nice. I'm a user of https://esm.sh and https://esm.run (from jsDelivr) so I haven't had reason to use the raw .min.js file directly before 🤷♂️ |
It would appear that this package encounters the dual package hazard with the base64 code being run potentially twice: once when
import
-ed and once whenrequire()
-ed.https://nodejs.org/api/packages.html#packages_dual_package_hazard
This hasn't been an issue for me in practice, but it does happen!
This isn't terrible, it's just a quirk that should probably be fixed. 😉
(You can close this if this isn't a big deal.)
The text was updated successfully, but these errors were encountered: