@nodefill/util
v0.2.0
Published
🧰 node:util for use anywhere
Downloads
15
Maintainers
Readme
node:util
ponyfill
🧰 node:util
for use anywhere
✅ Mostly correct
📦 Works with Node.js, Deno, Bun, and the browser
🔬 Has TypeScript types
✨ Uses the node:util
native package if available
⚠️ Distributed as CommonJS for widest compatibility
Installation
You can install this package via npm, pnpm, or Yarn. It's also available on npm CDNs like jsDelivr and esm.sh if you want to import it straight from your browser!
npm install @nodefill/util
import {} from "https://esm.sh/@nodefill/util";
import {} from "https://esm.run/@nodefill/util";
If you're using Deno, you can use the new npm:
specifier to import this
package straight from npm, or use the esm.sh CDN which has support for Deno.
import {} from "npm:@nodefill/util";
import {} from "https://esm.sh/@nodefill/util";
Usage
You can import this package as though it were the normal node:util
package! It
will automatically use the native node:util
package if it is available.
Otherwise, you'll get some really nice shims that replicate the functionality.
This is useful when authoring packages intended to be used in Node.js and the
browser.
import { isArrayBuffer } from "@nodefill/util/types";
import { format } from "@nodefill/util";
console.log(isAnyArrayBuffer(new ArrayBuffer(1)));
//=> true
console.dir(format("Hello %s!", "world"));
//=> "Hello world!"
Limitations
⚠️ Some of the util.types.*
functions in this package are loose (using
plain-old Object.prototype.toString.call()
or Symbol.toStringTag
). Make sure
you understand what each check does and whether or not it's looser than the
native node:util/types
function.
Some of the other utility functions like transferableAbortSignal()
and
transferableAbortController()
don't really do much on non-Node.js platforms
since Node.js is the only place you can do magic like that. They're included for
API completeness.
Development
This package uses TypeScript! That makes it easy to co-locate types and code
side-by-side without complicated /** @param */
declarations that get unweildy
with larger projects.
To get started, clone this repository (or your fork) and run any of these commands to play around with the code:
npm test
npm run build
npm run build:docs
There's also a file naming convention where -default.ts
files get exported via
the default
condition (via the exports field in package.json
) and
-node.ts
files get exported via the node
condition. This way, we can switch
on "is this Node.js or something else?" statically instead of at runtime. This
helps bundlers, loading performance, and more. The convention is that if there's
more than one file for a given export, then explicitly suffix the file name with
said export. For non-ambiguous exports (no conditions), we don't need to do this
explicit naming (ex: lib/argsToTokens.ts
doesn't have alternate
implementations, so it's plain-old argsToTokens.ts
).
Why use CommonJS? ESM is the onvious choice. The reason we still use
CommonJS is because some packages may want a drop-in replacement for
node:util/types
when they use require()
. Since ESM packages cannot be
require()
-ed, but CommonJS packages can be import()
-ed, we use CommonJS for
maximum compatibility. This may cause issues. If it is deemed such, this
package may migrate to ESM-only in the future.
Why not distribute ESM and CJS versions? We could, but it would lead to the
dual package hazard. The best way to support ESM and CJS at the same time is
to write good CJS that imports well in ESM environments, possibly with a
short index.mjs
file to wrap the main CJS part. This is what we do.