Reputation: 409
There is the isomorphic-webcrypto that pretends doing that but doesn't : it builds separate build for each target.
There is the noble-crypto way to do it, but it's based on if-else conditions and fails if I want an isomorphic mjs code.
Finally, there is the eval require way way to pass-through bundler, but node fails to use it in mjs.
In brief :
const crypto = require("crypto"); // work only in node.js but not in mjs file.
const crypto = eval(`require("crypto")`); // pass-thru bundler, then work only in node.js but not in mjs file.
window.crypto; // work only in browser
import * as crypto from "crypto"; // could work from both but must be at top level of a module, so it can't be a conditional import.
I would like to use native crypto in node.js and in browser, in an isomorphic way, to be able to use native import mjs in node and browser transparently.
How can I do this?
Upvotes: 4
Views: 1020
Reputation: 954
In Node version 20 & up there is a global crypto
object that provides the webcrypto API that you needed to manually import in earlier versions:
globalThis.crypto === require('node:crypto').webcrypto
So the code:
globalThis.crypto.randomUUID()
Would work the same in the browser & in Node with no imports and no special tooling.
Upvotes: 1
Reputation: 163538
Alright. Ready for something ugly? :-) Behold, my latest hackjob… IsomorphicCyrpto.js:
export default
globalThis.crypto ||
(await import('node:crypto')).default.webcrypto
;
This works in Node.js v16 in module mode ("type": "module"
in package.json, or equivalent CLI args), and will probably work with your bundler too… but who knows. ;-) Anyone using this code snippet should test thoroughly on whatever platforms they want to use it on.
In a nutshell:
globalThis
, which represents global
under Node.js, window
for most browser contexts, and could perhaps even be a Worker context.crypto
is a thing. If it is, we're probably on a browser, and can just use that directly.crypto
is not a thing, we're probably on Node.js and we need to import the module.import()
rather than a true import
.import()
is async and returns a Promise. But hey, it's all good, because top-level await
is a thing in Node.js now!To then use the module:
import crypto from './lib/IsomorphicCrypto.js';
console.log( crypto.randomUUID() );
Uggggly, but works for now. Hopefully, someone comes up with a better solution, or Node.js and browser contexts converge on naming in the future.
Upvotes: 4