{
  "bluebird-q.md": "---\ndescription: Modern alternatives to the Bluebird and Q Promise libraries for async control flow in JavaScript\n---\n\n# Replacements for `bluebird` / `q`\n\n## `Promise` (native)\n\n[`bluebird`](https://github.com/petkaantonov/bluebird?tab=readme-ov-file#%EF%B8%8Fnote%EF%B8%8F) and [`q`](https://github.com/kriskowal/q#note) recommend switching away from them to native promises.\n\n## `nativebird`\n\n[`NativeBird`](https://github.com/doodlewind/nativebird) is an ultralight native `Promise` extension that provides Bluebird-like helpers if you miss a few conveniences from Bluebird.\n",
  "bcrypt.md": "---\ndescription: Modern alternatives to the bcrypt package for password hashing\n---\n\n# Replacements for `bcrypt`\n\nThe `bcrypt` package can be replaced by native functionality in most runtimes, or more performant packages.\n\n## `bcryptjs`\n\n[`bcryptjs`](https://github.com/dcodeIO/bcrypt.js) is a widely used pure JavaScript implementation with a very similar API surface to `bcrypt`.\n\n```ts\nimport bcrypt from 'bcrypt' // [!code --]\nimport bcrypt from 'bcryptjs' // [!code ++]\n\nconst salt = await bcrypt.genSalt(10)\n\nconst hash = await bcrypt.hash('password', salt)\n```\n\n## `node:crypto` (native, Node.js built-in)\n\nNode provides [`node:crypto`](https://nodejs.org/api/crypto.html) for secure password hashing primitives (for example `scrypt` and `pbkdf2`). This is not a drop-in `bcrypt` API replacement, but is a good option if you can switch to a more secure cryptographic algorithm it supports.\n\n## Web Crypto API (native)\n\nThe [Web Crypto API](https://developer.mozilla.org/docs/Web/API/Web_Crypto_API) provides native functionality for cryptographic operations in both web browsers and Node.\n\n> [!NOTE]\n> A few legacy algorithms are intentionally omitted for security reasons (e.g. MD5).\n\n## Bun (built-in)\n\nBun supports the Web Crypto API natively, and also provides support for streaming hashing via [`Bun.CryptoHasher`](https://bun.sh/docs/api/hashing).\n\nAs with the Web Crypto API, many legacy algorithms are intentionally omitted for security reasons (e.g. MD5).\n",
  "buf-compare.md": "---\ndescription: Native Node.js alternatives to the buf-compare package for buffer comparison\n---\n\n# Replacements for `buf-compare`\n\n## `Buffer.compare` (native)\n\n[`Buffer.compare`](https://nodejs.org/api/buffer.html#static-method-buffercomparebuf1-buf2) is a native method which achieves the same result as `buf-compare`, available since Node v0.11.13.\n\nExample:\n\n```ts\nimport { Buffer } from 'node:buffer'\nimport bufCompare from 'buf-compare' // [!code --]\n\nconst buf1 = Buffer.from('303')\nconst buf2 = Buffer.from('808')\n\nbufCompare(buf1, buf2) // [!code --]\nBuffer.compare(buf1, buf2) // [!code ++]\n```\n",
  "body-parser.md": "---\ndescription: Modern alternatives to the body-parser package for parsing HTTP request bodies in Node.js servers\n---\n\n# Replacements for `body-parser`\n\n## `milliparsec`\n\n[`milliparsec`](https://github.com/tinyhttp/milliparsec) is a lightweight alternative to [`body-parser`](https://github.com/expressjs/body-parser) with a smaller footprint.\n\nExample:\n\n```ts\nimport bodyParser from 'body-parser' // [!code --]\nimport { json, urlencoded } from 'milliparsec' // [!code ++]\nimport express from 'express'\n\nconst app = express()\n\napp.use(bodyParser.json()) // [!code --]\napp.use(bodyParser.urlencoded({ extended: true })) // [!code --]\n\napp.use(json()) // [!code ++]\napp.use(urlencoded()) // [!code ++]\n```\n\nFor API differences and feature comparison, see the [migration.md](https://github.com/tinyhttp/milliparsec/blob/master/migration.md).\n",
  "buffer-equal-constant-time.md": "---\ndescription: Native Node.js alternatives to the buffer-equal-constant-time package for safe buffer equality checks\n---\n\n# Replacements for `buffer-equal-constant-time`\n\n## `crypto.timingSafeEqual` (native)\n\nYou can use the [`timingSafeEqual`](https://nodejs.org/api/crypto.html#cryptotimingsafeequala-b) function from the `node:crypto` module.\n\nExample:\n\n```ts\nimport { Buffer } from 'node:buffer'\nimport bufferEqual from 'buffer-equal-constant-time' // [!code --]\nimport * as crypto from 'node:crypto' // [!code ++]\n\nconst bufUser = Buffer.from('303')\nconst bufSecret = Buffer.from('303')\n\nbufferEqual(bufUser, bufSecret) // [!code --]\nbufUser.length === bufSecret.length // [!code ++]\n  ? crypto.timingSafeEqual(bufUser, bufSecret) // [!code ++]\n  : !crypto.timingSafeEqual(bufUser, bufUser) // [!code ++]\n```\n",
  "buffer-equal.md": "---\ndescription: Native Node.js alternatives to the buffer-equal package for buffer equality checks\n---\n\n# Replacements for `buffer-equal`\n\n## `Buffer#equals` (native)\n\nBuffers have an [`equals`](https://nodejs.org/api/buffer.html#bufequalsotherbuffer) method since Node v0.12.\n\nExample:\n\n```ts\nimport { Buffer } from 'node:buffer'\nimport bufferEqual from 'buffer-equal' // [!code --]\n\nconst buf1 = Buffer.from('303')\nconst buf2 = Buffer.from('303')\n\nbufferEqual(buf1, buf2) // [!code --]\nbuf1.equals(buf2) // [!code ++]\n```\n",
  "buffer-equals.md": "---\ndescription: Native Node.js alternatives to the buffer-equals package for buffer equality checks\n---\n\n# Replacements for `buffer-equals`\n\n## `Buffer#equals` (native)\n\nBuffers have an [`equals`](https://nodejs.org/api/buffer.html#bufequalsotherbuffer) method since Node v0.12.\n\nExample:\n\n```ts\nimport { Buffer } from 'node:buffer'\nimport bufferEquals from 'buffer-equals' // [!code --]\n\nconst buf1 = Buffer.from('303')\nconst buf2 = Buffer.from('303')\n\nbufferEquals(buf1, buf2) // [!code --]\nbuf1.equals(buf2) // [!code ++]\n```\n",
  "builtin-modules.md": "---\ndescription: Native Node.js alternatives to the builtin-modules package for listing built-in modules\n---\n\n# Replacements for `builtin-modules`\n\n## `builtinModules` (native, since Node.js 6.x)\n\nFor getting the list of built-in modules, you can use [`builtinModules`](https://nodejs.org/api/module.html#modulebuiltinmodules):\n\n```ts\nimport builtinModulesList from 'builtin-modules' // [!code --]\nimport { builtinModules } from 'node:module' // [!code ++]\n\nbuiltinModulesList.includes('fs') // true [!code --]\nbuiltinModules.includes('fs') // true [!code ++]\n```\n",
  "chalk.md": "---\ndescription: Modern alternatives to the chalk package for terminal string styling and colors, with notes on browser console support\n---\n\n# Replacements for `chalk`\n\n## `styleText` (native)\n\nSince Node 20.x, you can use the [`styleText`](https://nodejs.org/api/util.html#utilstyletextformat-text-options) function from the `node:util` module to style text in the terminal.\n\n> [!TIP]\n> Node.js provides a codemod which can migrate some of this automatically, see the docs at [@nodejs/chalk-to-util-styletext](https://app.codemod.com/registry/@nodejs/chalk-to-util-styletext) for more details.\n\nExample:\n\n```ts\nimport { styleText } from 'node:util' // [!code ++]\nimport chalk from 'chalk' // [!code --]\n\nconsole.log(`Hello ${chalk.blue('blue')} world!`) // [!code --]\nconsole.log(`Hello ${styleText('blue', 'blue')} world!`) // [!code ++]\nconsole.log(`I am ${chalk.hex('#ec8f5e')('hex color')}!`) // [!code --]\nconsole.log(`I am ${styleText('#ec8f5e', 'hex color')}!`) // [!code ++]\n```\n\nWhen using multiple styles, you can pass an array to `styleText`:\n\n```ts\nconsole.log(`I am ${chalk.blue.bgRed('blue on red')}!`) // [!code --]\nconsole.log(`I am ${styleText(['blue', 'bgRed'], 'blue on red')}!`) // [!code ++]\n```\n\n> [!NOTE]\n> Before Node v26.1.0, `styleText` did not support hex colors or RGB colors (e.g. `#EFEFEF` or `255, 239, 235`). Hex colors are supported starting in v26.1.0, but RGB colors are still not supported. You can view the available styles in the [Node documentation](https://nodejs.org/api/util.html#modifiers).\n\n## `picocolors`\n\n[`picocolors`](https://github.com/alexeyraspopov/picocolors) follows a similar API but without chaining:\n\n```ts\nimport chalk from 'chalk' // [!code --]\nimport picocolors from 'picocolors' // [!code ++]\n\nconsole.log(`Hello ${chalk.blue('blue')} world!`) // [!code --]\nconsole.log(`Hello ${picocolors.blue('blue')} world!`) // [!code ++]\n\n// A chained example\nconsole.log(chalk.blue.bgRed('blue on red')) // [!code --]\nconsole.log(picocolors.blue(picocolors.bgRed('blue on red'))) // [!code ++]\n```\n\n> [!NOTE]\n> `picocolors` currently does not support RGB and hex colors (e.g. `#EFEFEF` or `255, 239, 235`).\n\n## `ansis`\n\n[`ansis`](https://github.com/webdiscus/ansis/) supports a chaining syntax similar to chalk and supports both RGB, and hex colors.\n\nExample:\n\n```ts\nimport ansis from 'ansis' // [!code ++]\nimport chalk from 'chalk' // [!code --]\n\nconsole.log(`Hello ${chalk.blue('blue')} world!`) // [!code --]\nconsole.log(`Hello ${ansis.blue('blue')} world!`) // [!code ++]\n```\n\nWhen using multiple styles, you can chain them just like in chalk:\n\n```ts\nconsole.log(chalk.blue.bgRed('blue on red')) // [!code --]\nconsole.log(ansis.blue.bgRed('blue on red')) // [!code ++]\n```\n\nSimilarly, you can use RGB and hex colors:\n\n```ts\nconsole.log(chalk.rgb(239, 239, 239)('Hello world!')) // [!code --]\nconsole.log(ansis.rgb(239, 239, 239)('Hello world!')) // [!code ++]\n```\n\n## Browser support\n\nWhile these libraries are primarily designed for terminal output, some projects may need colored output in browser environments.\n\nFollowing [MDN documentation](https://developer.mozilla.org/en-US/docs/Web/API/console#styling_console_output), the native approach is `%c` directive in `console.log`:\n\n```ts\nconsole.log(\n  'Hello %ce%c1%c8%ce',\n  'color: #ec8f5e;',\n  'color: #f2ca60;',\n  'color: #bece57;',\n  'color: #7bb560;',\n  'Ecosystem Performance'\n)\n```\n\nLibrary support:\n\n- [`ansis`](https://github.com/webdiscus/ansis#browser-compatibility-for-ansi-codes) - colors are supported in _Chromium_ browsers\n- `picocolors` - strips colors in browser environments\n- `node:util` - is not available in browsers\n",
  "cli-builders.md": "---\ndescription: Modern alternatives to packages for building CLI applications\n---\n\n# Replacements for CLI builders\n\n## `sade`\n\n[`sade`](https://github.com/lukeed/sade) is a small but powerful tool for building CLI applications for Node.js\n\n```ts\nimport sade from 'sade'\n\nconst prog = sade('my-cli')\n\nprog\n  .version('1.0.5')\n  .option('--global, -g', 'An example global flag')\n  .option('-c, --config', 'Provide path to custom config', 'foo.config.js')\n\nprog\n  .command('build <src> <dest>')\n  .describe('Build the source directory. Expects an `index.js` entry file.')\n  .option('-o, --output', 'Change the name of the output file', 'bundle.js')\n  .example('build src build --global --config my-conf.js')\n  .example('build app public -o main.js')\n  .action((src, dest, opts) => {\n    console.log(`> building from ${src} to ${dest}`)\n    console.log('> these are extra opts', opts)\n  })\n\nprog.parse(process.argv)\n```\n\n## `cleye`\n\n[`cleye`](https://github.com/privatenumber/cleye) is another powerful tool for building CLI applications for Node.js\n\n```ts\nimport { cli } from 'cleye'\n\n// Parse argv\nconst argv = cli({\n  name: 'greet.js',\n\n  // Define parameters\n  parameters: [\n    '<first name>', // First name is required\n    '[last name]' // Last name is optional\n  ],\n\n  // Define flags/options\n  flags: {\n    // Parses `--time` as a string\n    time: {\n      type: String,\n      description: 'Time of day to greet (morning or evening)',\n      default: 'morning'\n    }\n  }\n})\n\nconst name = [argv._.firstName, argv._.lastName].filter(Boolean).join(' ')\n\nif (argv.flags.time === 'morning') {\n  console.log(`Good morning ${name}!`)\n} else {\n  console.log(`Good evening ${name}!`)\n}\n```\n\n## `cac`\n\n[`cac`](https://github.com/cacjs/cac) is a simple yet powerful framework for building CLI applications for Node.js\n\n```ts\nimport cac from 'cac'\n\nconst cli = cac()\n\ncli\n  .command('deploy <folder>', 'Deploy a folder to AWS')\n  .option('--scale [level]', 'Scaling level')\n  .action((folder, options) => {\n    // ...\n  })\n\ncli\n  .command('build [project]', 'Build a project')\n  .option('--out <dir>', 'Output directory')\n  .action((folder, options) => {\n    // ...\n  })\n\ncli.parse()\n```\n\n## Argument parsers\n\nIf you only need an argument parser, check the [argument parsers page](https://e18e.dev/docs/replacements/parseargs).\n",
  "clipboardy.md": "---\ndescription: Modern alternatives to the clipboardy package for copy/pasting on Node.js and in the browser\n---\n\n# Replacements for `clipboardy`\n\n## `tinyclip`\n\n[`tinyclip`](https://github.com/tinylibs/tinyclip) provides cross-platform clipboard functionality to Node.js.\n\n```js\nimport clipboard from 'clipboardy' // [!code --]\nimport * as clipboard from 'tinyclip' // [!code ++]\n\nawait clipboard.write('hello world') // [!code --]\nawait clipboard.writeText('hello world') // [!code ++]\nawait clipboard.read() // [!code --]\nawait clipboard.readText() // [!code ++]\n```\n\n> [!NOTE]\n> `writeSync()` and `readSync()` do not have any equivalent in `tinyclip`, we recommend you migrate to their async versions.\n\n## Clipboard API (native, browser)\n\nLearn more about the [Clipboard API](https://developer.mozilla.org/en-US/docs/Web/API/Clipboard_API).\n\n```js\nimport clipboard from 'clipboardy' // [!code --]\n\nawait clipboard.write('hello world') // [!code --]\nawait navigator.clipboard.writeText('hello world') // [!code ++]\nawait clipboard.read() // [!code --]\nawait navigator.clipboard.readText() // [!code ++]\n```\n\n> [!NOTE]\n> `writeSync()` and `readSync()` do not have an equivalent in browsers.\n",
  "core-util-is.md": "---\ndescription: Native Node.js alternatives to the core-util-is package\n---\n\n# Replacements for `core-util-is`\n\n## `util.types` (native, Node.js)\n\n[`util.types`](https://nodejs.org/api/util.html#utiltypes) is an official, cross‑realm type checks for built-in objects (Date, RegExp, Error, typed arrays, etc.)\n\nExample:\n\n```ts\nimport * as cui from 'core-util-is' // [!code --]\nimport { types } from 'node:util' // [!code ++]\n\nconst isDate = cui.isDate(value) // [!code --]\nconst isDate = types.isDate(value) // [!code ++]\n```\n",
  "cosmiconfig.md": "---\ndescription: Modern alternatives to the cosmiconfig package\n---\n\n# Replacements for `cosmiconfig`\n\n## `lilconfig`\n\n[`lilconfig`](https://github.com/antonk52/lilconfig) is a zero-dependency alternative to cosmiconfig with the same API\n\n> [!NOTE]\n> `lilconfig` does not support YAML files out of the box unless you provide custom loaders.\n\n### Example\n\n```js\nimport { lilconfig, lilconfigSync } from 'lilconfig'\n\nconst options = {\n  searchPlaces: [\n    'package.json', // Will use \"myapp\" field\n    '.myapprc',\n    '.myapprc.json',\n    'myapp.config.js'\n  ]\n}\n\nawait lilconfig('myapp', options).search()\n\n// Sync version\nlilconfigSync('myapp', options).search()\n```\n\n### Loading YAML files\n\n```js\nimport { lilconfig } from 'lilconfig'\nimport { parse } from 'yaml'\n\nconst yamlLoader = (filepath, content) => parse(content)\n\nconst options = {\n  searchPlaces: [\n    'package.json',\n    '.myapprc',\n    '.myapprc.yaml',\n    '.myapprc.yml',\n    'myapp.config.js'\n  ],\n  loaders: {\n    '.yaml': yamlLoader,\n    '.yml': yamlLoader,\n    noExt: yamlLoader\n  }\n}\n\nawait lilconfig('myapp', options).search()\n```\n",
  "cpx.md": "---\ndescription: Modern alternatives to the cpx package for copying file globs with watch mode\n---\n\n# Replacements for `cpx`\n\n## `cpx2`\n\n[`cpx`](https://github.com/mysticatea/cpx) is unmaintained. [`cpx2`](https://github.com/bcomnes/cpx2) is an actively maintained fork that keeps the same CLI bin name (`cpx`), so it works as a drop-in replacement for CLI usage. For the Node API, switch your import to `cpx2`.\n\n```sh\nnpm i -D cpx # [!code --]\nnpm i -D cpx2 # [!code ++]\n\n# CLI stays the same (bin name is still \"cpx\")\ncpx \"src/**/*.{html,png,jpg}\" app --watch\n```\n\nNode API replacement:\n\n<!-- eslint-skip -->\n\n```ts\nconst cpx = require('cpx') // [!code --]\nconst cpx = require('cpx2') // [!code ++]\n\ncpx.copy('src/**/*.js', 'dist', (err) => {\n  if (err) throw err\n})\n```\n",
  "create-hmac.md": "---\ndescription: Modern alternatives to the `create-hmac` package for HMAC\n---\n\n# Replacements for `create-hmac`\n\n## `crypto.createHmac` (native, Node.js)\n\n```js\nimport { createHmac } from 'node:crypto' // [!code ++]\nimport createHmac from 'create-hmac' // [!code --]\n\nconst secret = 'secret-key'\nconst data = 'message-to-sign'\n\nconst hash = createHmac('sha256', secret).update(data).digest('hex')\n```\n\n## `crypto.subtle.sign` (native, browser)\n\n```js\nconst encoder = new TextEncoder()\n\nasync function generateHMAC(secret, data) {\n  const key = await crypto.subtle.importKey(\n    'raw',\n    encoder.encode(secret),\n    { name: 'HMAC', hash: 'SHA-256' },\n    false,\n    ['sign']\n  )\n\n  const signature = await crypto.subtle.sign('HMAC', key, encoder.encode(data))\n\n  return new Uint8Array(signature).toHex()\n}\n\nconst secret = 'secret-key'\nconst data = 'message-to-sign'\n\nconst hash = await generateHMAC(secret, data)\n```\n\n## `@noble/hashes`\n\n```js\nimport { hmac } from '@noble/hashes/hmac.js' // [!code ++]\nimport { sha256 } from '@noble/hashes/sha256.js' // [!code ++]\nimport { bytesToHex } from '@noble/hashes/utils.js' // [!code ++]\nimport createHmac from 'create-hmac' // [!code --]\n\nconst secret = 'secret-key'\nconst data = 'message-to-sign'\n\nconst hash = createHmac('sha256', secret).update(data).digest('hex') // [!code --]\nconst hash = bytesToHex(hmac(sha256, secret, data)) // [!code ++]\n```\n",
  "cross-spawn.md": "---\ndescription: Modern alternatives to the cross-spawn package for spawning child processes\n---\n\n# Replacements for `cross-spawn`\n\n> [!NOTE]\n> If your application does not need to support Windows, you can likely drop `cross-spawn` entirely as it solely exists to provide Windows spawn compatibility. You can use the built-in `node:child_process` in that case.\n\n## `tinyexec`\n\n[`tinyexec`](https://github.com/tinylibs/tinyexec) is a minimal, dependency-free process execution library.\n\n`cross-spawn` follows the Node.js `spawn` API and returns a `ChildProcess`. In comparison, `tinyexec` returns an awaitable result containing the process output and exit code.\n\nExample:\n\n```ts\nimport spawn from 'cross-spawn' // [!code --]\nimport { x } from 'tinyexec' // [!code ++]\n\nconst child = spawn('npm', ['list', '-g']) // [!code --]\nconst { stdout, exitCode } = await x('npm', ['list', '-g']) // [!code ++]\n\nchild.stdout?.on('data', (output) => console.log(output.toString())) // [!code --]\nconsole.log(stdout, exitCode) // [!code ++]\n```\n",
  "crypto-js.md": "---\ndescription: Modern alternatives to the `crypto-js` package for cryptographic operations\n---\n\n# Replacements for `crypto-js`\n\n`crypto-js` is no longer actively maintained and has been discontinued since engines now come with this functionality built-in.\n\n## `node:crypto` (native, Node.js built-in)\n\nNode provides a [`node:crypto`](https://nodejs.org/api/crypto.html) module as part of its standard library.\n\nThis supports hashes/HMAC, AES-GCM, PBKDF2/scrypt, RSA/ECDSA/Ed25519, and secure RNG.\n\n```ts\nimport sha256 from 'crypto-js/sha256' // [!code --]\nimport { createHash } from 'node:crypto' // [!code ++]\n\nconst secret = 'abcdefg'\nconst hash = sha256(secret).toString() // [!code --]\nconst hash = createHash('sha256') // [!code ++]\n  .update(secret) // [!code ++]\n  .digest('hex') // [!code ++]\n```\n\n## Web Crypto API (native)\n\nThe [Web Crypto API](https://developer.mozilla.org/docs/Web/API/Web_Crypto_API) provides native functionality for cryptographic operations in both web browsers and Node.\n\n> [!NOTE]\n> A few legacy algorithms are intentionally omitted for security reasons (e.g. MD5).\n\n## Bun (built-in)\n\nBun supports the Web Crypto API natively, and also provides support for streaming hashing via [`Bun.CryptoHasher`](https://bun.sh/docs/api/hashing).\n\nAs with the Web Crypto API, many legacy algorithms are intentionally omitted for security reasons (e.g. MD5).\n",
  "debug.md": "---\ndescription: Modern alternatives to the debug package\n---\n\n# Replacements for `debug`\n\n## `obug`\n\n[`obug`](https://github.com/sxzz/obug) is a lightweight JavaScript debugging utility, forked from debug, featuring TypeScript and ESM support.\n\n`obug` v1 retains compatibility with debug, but drops support for older browsers and Node.js versions, making it a drop-in replacement.\n\n`obug` v2 refactors some API imports and usage for better support of ESM and TypeScript, easier customization, and an even smaller package size.\n\n```ts\nimport debug from 'debug' // [!code --]\nimport { createDebug } from 'obug' // [!code ++]\n\nconst logger = debug('name') // [!code --]\nconst logger = createDebug('name') // [!code ++]\n```\n",
  "deep-equal.md": "---\ndescription: Modern alternatives to the deep-equal package for deep object comparison\n---\n\n# Replacements for `deep-equal`\n\n## `util.isDeepStrictEqual` (native, since Node.js v9.0.0)\n\nNode.js has a builtin function [`isDeepStrictEqual`](https://nodejs.org/api/util.html#utilisdeepstrictequalval1-val2-options). Consider using that if you don’t need browser support.\n\nExample:\n\n```ts\nimport equal from 'deep-equal' // [!code --]\nimport { isDeepStrictEqual } from 'node:util' // [!code ++]\n\nconst a = { foo: 'bar' }\nconst b = { foo: 'bar' }\n\nequal(a, b) // true [!code --]\nisDeepStrictEqual(a, b) // true [!code ++]\n```\n\n## `dequal`\n\n[`dequal`](https://github.com/lukeed/dequal) has the same simple API as `deep-equal`.\n\nExample:\n\n```ts\nimport equal from 'deep-equal' // [!code --]\nimport dequal from 'dequal' // [!code ++]\n\nconst a = { foo: 'bar' }\nconst b = { foo: 'bar' }\n\nequal(a, b) // true [!code --]\ndequal(a, b) // true [!code ++]\n```\n\n## `Bun.deepEquals` (native, Bun)\n\nBun has a built-in [`Bun.deepEquals`](https://bun.com/docs/runtime/utils#bun-deepequals) function. It accepts two values to compare, and an optional `strict` flag (default `false`).\n\nExample:\n\n```ts\nimport equal from 'deep-equal' // [!code --]\n\nconst a = { foo: 'bar' }\nconst b = { foo: 'bar' }\n\nequal(a, b) // true [!code --]\nBun.deepEquals(a, b) // true [!code ++]\n\n// Strict Mode\nequal(a, b, { strict: true }) // true [!code --]\nBun.deepEquals(a, b, true) // true [!code ++]\n```\n",
  "depcheck.md": "---\ndescription: Modern alternatives to depcheck for analyzing project dependencies and unused code\n---\n\n# Replacements for `depcheck`\n\n## `knip`\n\n[knip](https://github.com/webpro-nl/knip) is a more actively maintained and feature-rich alternative to [`depcheck`](https://github.com/depcheck/depcheck). In most cases, knip works out of the box without any configuration - just run `npx knip`. For projects that need customization, you can create a configuration file.\n\nExample:\n\n```json\n{\n  \"$schema\": \"https://unpkg.com/knip@5/schema.json\",\n  \"ignoreDependencies\": [\"@types/*\", \"eslint-*\"]\n}\n```\n",
  "desm.md": "---\ndescription: Native alternatives to the desm package\n---\n\n# Replacements for `desm`\n\n## `import.meta.dirname` and `import.meta.filename`\n\n```ts\nimport { join } from 'desm' // [!code --]\nimport { join } from 'node:path' // [!code ++]\n\njoin('./foo.ts') // [!code --]\njoin(import.meta.dirname, './foo.ts') // [!code ++]\n```\n\n```ts\nimport { dirname } from 'desm' // [!code --]\n\ndirname(import.meta.url) // [!code --]\nimport.meta.dirname // [!code ++]\n```\n\n```ts\nimport { filename } from 'desm' // [!code --]\n\nfilename(import.meta.url) // [!code --]\nimport.meta.filename // [!code ++]\n```\n",
  "detect-package-manager.md": "---\ndescription: Lightweight alternative to detect-package-manager for detecting the package manager with zero dependencies\n---\n\n# Replacements for `detect-package-manager`\n\n## `package-manager-detector`\n\n[`package-manager-detector`](https://github.com/antfu-collective/package-manager-detector) is a lightweight alternative for detecting the package manager being used in a project.\n\nIt has zero dependencies, is ESM-only, and provides utilities such as constructing commands for different package managers.\n\nExample migration from [`detect-package-manager`](https://www.npmjs.com/package/detect-package-manager):\n\n```ts\nimport { detect } from 'detect-package-manager' // [!code --]\nimport { detect } from 'package-manager-detector' // [!code ++]\n\nconst pm = await detect() // [!code --]\nconst result = await detect() // [!code ++]\nconst pm = result?.name // [!code ++]\n```\n\n> [!NOTE]\n> `package-manager-detector` returns an object with `name` and `agent` properties, while `detect-package-manager` returns a string directly.\n\nExample migration from [`preferred-pm`](https://www.npmjs.com/package/preferred-pm):\n\n```ts\nimport { preferredPM } from 'preferred-pm' // [!code --]\nimport { detect } from 'package-manager-detector' // [!code ++]\n\nconst result = await preferredPM() // [!code --]\nconst result = await detect() // [!code ++]\n```\n\nExample migration from [`which-pm`](https://www.npmjs.com/package/which-pm):\n\n<!-- prettier-ignore -->\n```ts\nimport { whichPM } from 'which-pm' // [!code --]\nimport { detect } from 'package-manager-detector' // [!code ++]\n\nconst result = await whichPM() // [!code --]\nconst result = await detect({ // [!code ++]\n  // Include the `install-metadata` strategy to have the package manager // [!code ++]\n  // that's used for installation take precedence // [!code ++]\n  strategies: [ // [!code ++]\n    'install-metadata', // [!code ++]\n    'lockfile', // [!code ++]\n    'packageManager-field', // [!code ++]\n    'devEngines-field' // [!code ++]\n  ] // [!code ++]\n}) // [!code ++]\n```\n",
  "dev-null.md": "---\ndescription: Modern alternatives to the dev-null package\n---\n\n# Replacements for `dev-null`\n\n## `stream.Writable` (native, Node.js)\n\nThe native [`stream.Writable`](https://nodejs.org/api/stream.html#class-streamwritable) class can be used to construct a custom writable stream that discards incoming data.\n\n<!-- prettier-ignore -->\n```ts\nimport devnull from 'dev-null' // [!code --]\nimport { Writable } from 'node:stream' // [!code ++]\n\nstream.pipe(devnull()) // [!code --]\n\nconst empty = new Writable({ // [!code ++]\n  write(chunk, encoding, callback) { // [!code ++]\n    callback() // [!code ++]\n  } // [!code ++]\n}) // [!code ++]\n\nstream.pipe(empty) // [!code ++]\n```\n",
  "dotenv.md": "---\ndescription: Native Node.js alternatives to the dotenv package for loading and managing .env files in Node.js\n---\n\n# Replacements for `dotenv`\n\n## `process.loadEnvFile` (native, since Node.js v20.12.0)\n\n[`process.loadEnvFile`](https://nodejs.org/api/process.html#processloadenvfilepath) (Node.js v20.12.0+) loads a `.env` file into `process.env` at runtime.\n\nIt throws if the file is missing. Defaults to `.env` in the current working directory when called without a path.\n\n```ts\nimport dotenv from 'dotenv' // [!code --]\n\ndotenv.config({ path: '.env' }) // [!code --]\nprocess.loadEnvFile('.env') // [!code ++]\n```\n\n## `--env-file` / `--env-file-if-exists` (native, Node.js)\n\n[`--env-file`](https://nodejs.org/dist/latest-v20.x/docs/api/cli.html#--env-fileconfig) (Node.js v20.6.0+) and [`--env-file-if-exists`](https://nodejs.org/docs/latest-v22.x/api/cli.html#--env-file-if-existsfile) (Node.js v22.9.0+) can be passed as command-line flags to load environment variables from a specified file.\n\n`--env-file` throws if the file is missing. If the file may be absent, use `--env-file-if-exists`.\n\n```bash\nnode --env-file=.env index.js\n```\n\nAlso supported by:\n\n- [tsx](https://github.com/privatenumber/tsx)\n- [Bun](https://bun.sh/docs/runtime/env#manually-specifying-env-files)\n- [Deno](https://docs.deno.com/runtime/reference/env_variables/#.env-file)\n\nRemove dotenv preload:\n\n```ts\nimport 'dotenv/config' // [!code --]\n// No import needed when using --env-file\n```\n\nRemove explicit dotenv config:\n\n```ts\nimport dotenv from 'dotenv' // [!code --]\n\ndotenv.config({ path: '.env' }) // [!code --]\n// No runtime configuration needed\n```\n\nIn package.json scripts:\n\n```json\n{\n  \"scripts\": {\n    \"start\": \"node index.js\", // [!code --]\n    \"start\": \"node --env-file=.env index.js\" // [!code ++]\n  }\n}\n```\n\n## Node.js `parseEnv`\n\n[`parseEnv`](https://nodejs.org/docs/latest-v22.x/api/util.html#utilparseenvcontent) (Node.js v20.12.0+) can be used to parse a `.env` string into an object without loading it into `process.env`.\n\n```ts\nimport { parse } from 'dotenv' // [!code --]\nimport { parseEnv } from 'node:util' // [!code ++]\n\nconst envContent = '...'\n\nconst env = parse(envContent) // [!code --]\nconst env = parseEnv(envContent) // [!code ++]\n```\n\nIf the `.env` is a Node.js Buffer, convert it to a string first with `.toString()`.\n",
  "error-stack-parser.md": "---\ndescription: Modern alternatives to error-stack-parser for parsing error stack traces\n---\n\n# Replacements for `error-stack-parser`\n\n## `error-stack-parser-es`\n\n[`error-stack-parser-es`](https://github.com/antfu/error-stack-parser-es) is a smaller, maintained, ESM port of `error-stack-parser` with the same stack frame output.\n\n`error-stack-parser` exposes a default export with a `parse` method. With `error-stack-parser-es`, use the named `parse` export instead.\n\nExample:\n\n```ts\nimport ErrorStackParser from 'error-stack-parser' // [!code --]\nimport { parse } from 'error-stack-parser-es' // [!code ++]\n\nconst frames = ErrorStackParser.parse(error) // [!code --]\nconst frames = parse(error) // [!code ++]\n```\n\nIf you only need the raw parsed lines and not the full stack frame objects, the `error-stack-parser-es/lite` entry provides an even lighter parser.\n",
  "eslint-plugin-vitest.md": "---\ndescription: Modern alternatives to the eslint-plugin-vitest package for Vitest-specific linting rules\n---\n\n# Replacements for `eslint-plugin-vitest`\n\n## `@vitest/eslint-plugin`\n\n[`@vitest/eslint-plugin`](https://github.com/vitest-dev/eslint-plugin-vitest) is the same project as `eslint-plugin-vitest` but re-published under a different name. `eslint-plugin-vitest` is no longer maintained because the [original maintainer has lost access to their old npm account](https://github.com/vitest-dev/eslint-plugin-vitest/issues/537).\n\n```ts\nimport vitest from '@vitest/eslint-plugin' // [!code ++]\nimport vitest from 'eslint-plugin-vitest' // [!code --]\n\nexport default [\n  {\n    files: ['tests/**'], // or any other pattern\n    plugins: {\n      vitest\n    },\n    rules: {\n      ...vitest.configs.recommended.rules, // you can also use vitest.configs.all.rules to enable all rules\n      'vitest/max-nested-describe': ['error', { max: 3 }] // you can also modify rules' behavior using option like this\n    }\n  }\n]\n```\n",
  "execa.md": "---\ndescription: Modern alternatives to the execa package for running child processes\n---\n\n# Replacements for `execa`\n\n## `tinyexec`\n\n[`tinyexec`](https://github.com/tinylibs/tinyexec) is a minimal process execution library.\n\nExample:\n\n```ts\nimport { execa } from 'execa' // [!code --]\nimport { x } from 'tinyexec' // [!code ++]\n\nconst { stdout } = await execa('ls', ['-l']) // [!code --]\nconst { stdout } = await x('ls', ['-l'], { throwOnError: true }) // [!code ++]\n```\n\n## `nanoexec`\n\nIf you prefer a very thin wrapper over `child_process.spawn` (including full spawn options and optional shell), [`nanoexec`](https://github.com/fabiospampinato/nanoexec) is another light alternative. Its `stdout`/`stderr` are Buffers.\n\nExample:\n\n```ts\nimport { execa } from 'execa' // [!code --]\nimport exec from 'nanoexec' // [!code ++]\n\nconst { stdout } = await execa('echo', ['example']) // [!code --]\nconst res = await exec('echo', ['example']) // [!code ++]\nconst stdout = res.stdout.toString('utf8') // [!code ++]\n```\n\n## Bun\n\nIf you’re on Bun, its built-in [`$`](https://bun.com/reference/bun/$) template tag can replace `execa`’s script-style usage:\n\nExample:\n\n```ts\nimport { $ } from 'execa' // [!code --]\nimport { $ } from 'bun' // [!code ++]\n\nconst { stdout } = await $`echo \"Hello\"` // [!code --]\nconst stdout = await $`echo \"Hello\"`.text() // [!code ++]\n```\n",
  "express.md": "---\ndescription: Modern alternatives to the express package\n---\n\n# Replacements for `express`\n\nExpress has been the industry standard for years, but the ecosystem has shifted toward ESM-first, type-safe, and runtime-agnostic frameworks that offer significantly better performance and developer experience.\n\n## `h3`\n\n[`h3`](https://github.com/h3js/h3) is a minimal H(TTP) framework built for high performance and portability.\n\nExample:\n\n<!-- prettier-ignore -->\n```ts\nimport express from 'express' // [!code --]\nimport { H3, defineHandler, toNodeHandler } from 'h3' // [!code ++]\nimport { createServer } from 'node:http' // [!code ++]\n\nconst app = express() // [!code --]\nconst app = new H3() // [!code ++]\n\napp.get('/', (req, res) => res.send('Hello world')) // [!code --]\napp.get('/', defineHandler(() => 'Hello world')) // [!code ++]\n\napp.listen(3000) // [!code --]\ncreateServer(toNodeHandler(app)).listen(3000) // [!code ++]\n```\n\n## `tinyhttp`\n\n[`tinyhttp`](https://github.com/tinyhttp/tinyhttp) is designed to be a drop-in replacement remaining compatible with many Express middlewares.\n\nExample:\n\n```ts\nimport express from 'express' // [!code --]\nimport { App } from '@tinyhttp/app' // [!code ++]\n\nconst app = express() // [!code --]\nconst app = new App() // [!code ++]\n\napp.get('/', (req, res) => res.send('Hello world'))\n\napp.listen(3000)\n```\n\n## `hono`\n\n[`hono`](https://github.com/honojs/hono) is a small, simple, and fast web framework for the Edges.\n\nExample:\n\n```ts\nimport express from 'express' // [!code --]\nimport { Hono } from 'hono' // [!code ++]\n\nconst app = express() // [!code --]\nconst app = new Hono() // [!code ++]\n\napp.get('/', (req, res) => res.send('Hello world')) // [!code --]\napp.get('/', (context) => context.text('Hello world')) // [!code ++]\n\nexport default app // [!code ++]\n```\n\n## `elysia`\n\nIf you are using Bun, [`elysia`](https://github.com/elysiajs/elysia) is often the best choice as it is specifically optimized for the Bun runtime.\n\nExample:\n\n```ts\nimport express from 'express' // [!code --]\nimport { Elysia } from 'elysia' // [!code ++]\n\nconst app = express() // [!code --]\nconst app = new Elysia() // [!code ++]\n\napp.get('/', (req, res) => res.send('Hello world')) // [!code --]\napp.get('/', () => 'Hello world') // [!code ++]\n\napp.listen(3000)\n```\n",
  "extend.md": "---\ndescription: Native alternatives to the extend package for deep cloning and merging objects\n---\n\n# Replacements for `extend`\n\n## `structuredClone` (native)\n\nIf you only need to deep clone an object, you can use [`structuredClone`](https://developer.mozilla.org/en-US/docs/Web/API/Window/structuredClone):\n\n```ts\nimport extend from 'extend' // [!code --]\n\nextend(true, {}, config) // true [!code --]\nstructuredClone(config) // true [!code ++]\n```\n\n## Spread syntax\n\nIf you need to merge two shallow objects, you can use [spread syntax](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax#spread_in_object_literals):\n\n```ts\nconst obj1 = { foo: 'bar', x: 42 }\nconst obj2 = { bar: 'baz', y: 13 }\n\nconst mergedObj = { ...obj1, ...obj2 }\n```\n\n## `defu`\n\nIf you need to deep merge two objects, you can use [`defu`](https://github.com/unjs/defu):\n\n```ts\nimport { defu } from 'defu'\n\nconst options = defu(object, ...defaults)\n```\n",
  "faker.md": "---\ndescription: Modern replacements for the unmaintained faker package generating massive amounts of fake (but realistic) data\n---\n\n# Replacements for `faker`\n\n## `@faker-js/faker`\n\n[`@faker-js/faker`](https://github.com/faker-js/faker) is a direct, community‑maintained fork of `faker` with new features, bugfixes, modern ESM/CJS builds, and updated data/locales.\n\n```ts\nconst faker = require('faker') // [!code --]\nconst { faker } = require('@faker-js/faker') // [!code ++]\n\nfaker.datatype.boolean()\n\nfaker.image.avatar()\n```\n",
  "find-cache-directory.md": "---\ndescription: Modern alternatives to the find-cache-directory package for locating cache directories\n---\n\n# Replacements for `find-cache-directory`\n\n## `empathic`\n\n[`empathic`](https://github.com/lukeed/empathic) provides a more generic way to find files and directories upwards.\n\nExample:\n\n```ts\nimport * as pkg from 'empathic' // [!code ++]\nimport findCacheDirectory from 'find-cache-directory' // [!code --]\n\nfindCacheDirectory({ name: 'foo' }) // [!code --]\npkg.cache('foo') // [!code ++]\n```\n",
  "graphemer.md": "---\ndescription: Modern alternatives to the grapheme-splitter and graphemer packages for splitting strings into Unicode grapheme clusters\n---\n\n# Replacements for `grapheme-splitter` / `graphemer`\n\n## `Intl.Segmenter` (native)\n\n[`Intl.Segmenter`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/Segmenter) is the modern native JavaScript API for text segmentation, available in Node.js 16+, Chrome 87+, Safari 14.1+, and Firefox 132+.\n\n```ts\nimport GraphemeSplitter from 'grapheme-splitter' // [!code --]\n\nconst splitter = new GraphemeSplitter() // [!code --]\nconst segmenter = new Intl.Segmenter() // [!code ++]\n\nconst graphemes = splitter.splitGraphemes(text) // [!code --]\nconst graphemes = [...segmenter.segment(text)].map((s) => s.segment) // [!code ++]\n\nconst count = splitter.countGraphemes(text) // [!code --]\nconst count = [...segmenter.segment(text)].length // [!code ++]\n```\n\n## `unicode-segmenter`\n\n[`unicode-segmenter`](https://github.com/cometkim/unicode-segmenter) is a lightweight, fast alternative with zero dependencies and excellent browser compatibility.\n\n```ts\nimport GraphemeSplitter from 'grapheme-splitter' // [!code --]\nimport { countGraphemes, splitGraphemes } from 'unicode-segmenter/grapheme' // [!code ++]\n\nconst splitter = new GraphemeSplitter() // [!code --]\n\nconst graphemes = splitter.splitGraphemes(text) // [!code --]\nconst graphemes = [...splitGraphemes(text)] // [!code ++]\n\nconst count = splitter.countGraphemes(text) // [!code --]\nconst count = countGraphemes(text) // [!code ++]\n```\n\nYou can also use it as an `Intl.Segmenter` polyfill:\n\n```ts\nimport 'unicode-segmenter/intl-polyfill'\n\nconst segmenter = new Intl.Segmenter()\nconst graphemes = [...segmenter.segment(text)].map((s) => s.segment)\n```\n",
  "gray-matter.md": "---\ndescription: Modern alternatives to the gray-matter package for parsing front matter\n---\n\n# Replacements for `gray-matter`\n\n`gray-matter` has not been maintained since 2019 and carries known security issues, including eval-based JavaScript front matter (RCE) and an outdated `js-yaml` dependency with DoS vulnerabilities. [`@11ty/gray-matter`](https://github.com/11ty/gray-matter) is a maintained fork used by Eleventy v4 and Docusaurus.\n\n## `@11ty/gray-matter`\n\n[`@11ty/gray-matter`](https://github.com/11ty/gray-matter) upgrades `js-yaml` to v4, uses `Uint8Array` internally for better runtime compatibility, and removes the built-in JavaScript front matter engine that relied on `eval`.\n\nExample:\n\n```ts\nimport matter from 'gray-matter' // [!code --]\nimport matter from '@11ty/gray-matter' // [!code ++]\n\nconst { data, content } = matter('---\\ntitle: Hello\\n---\\n\\nBody')\n```\n",
  "ez-spawn.md": "---\ndescription: Modern alternatives to the ez-spawn package for spawning child processes\n---\n\n# Replacements for `@jsdevtools/ez-spawn`\n\n## `tinyexec`\n\n`ez-spawn` accepts shell-like command strings, which `tinyexec` does not.\n\nFor example:\n\n```ts\nimport ezSpawn from '@jsdevtools/ez-spawn' // [!code --]\nimport { x } from 'tinyexec' // [!code ++]\n\nawait ezSpawn.async('ls -l') // [!code --]\nawait x('ls', ['-l']) // [!code ++]\n```\n\nAlternatively, you can use [`args-tokenizer`](https://github.com/TrySound/args-tokenizer/) to convert a shell string to a command and arguments:\n\n```ts\nimport ezSpawn from '@jsdevtools/ez-spawn' // [!code --]\nimport { tokenizeArgs } from 'args-tokenizer' // [!code ++]\nimport { x } from 'tinyexec' // [!code ++]\n\nconst [command, ...args] = tokenizeArgs('ls -l') // [!code ++]\nawait ezSpawn.async('ls -l') // [!code --]\nawait x(command, args) // [!code ++]\n```\n",
  "clean-webpack-plugin.md": "---\ndescription: Modern alternatives to the clean-webpack-plugin package\n---\n\n# Replacements for `clean-webpack-plugin`\n\n## `output.clean` (built-in, since webpack 5)\n\nExample:\n\n```js\nconst { CleanWebpackPlugin } = require('clean-webpack-plugin') // [!code --]\n\nmodule.exports = {\n  plugins: [new CleanWebpackPlugin()], // [!code --]\n  output: { clean: true } // [!code ++]\n}\n```\n",
  "fs-extra.md": "---\ndescription: Modern alternatives to the fs-extra package for working with the file system\n---\n\n# Replacements for `fs-extra`\n\n## `fs` and `fs/promises` (native, Node.js)\n\nModern Node.js includes built-in [`fs`](https://nodejs.org/api/fs.html) and [`fs/promises`](https://nodejs.org/api/fs.html#promises-api) APIs that cover what [`fs-extra`](https://github.com/jprichardson/node-fs-extra) historically provided.\n\n```js\nimport fsExtra from 'fs-extra' // [!code --]\nimport * as fs from 'node:fs' // [!code ++]\nimport * as fsPromises from 'node:fs/promises' // [!code ++]\n```\n\n### `copy`\n\n```js\nawait fsExtra.copy(src, dest) // [!code --]\nawait fsPromises.cp(src, dest, { recursive: true }) // [!code ++]\n```\n\n> [!NOTE]\n> If src is a file and dest is an existing directory, `fs-extra` throws; `fs.cp` copies into the directory.\n\n### `copySync`\n\n```js\nfsExtra.copySync(src, dest) // [!code --]\nfs.cpSync(src, dest, { recursive: true }) // [!code ++]\n```\n\n### `remove`\n\n```js\nawait fsExtra.remove(path) // [!code --]\nawait fsPromises.rm(path, { recursive: true, force: true }) // [!code ++]\n```\n\n> [!IMPORTANT]\n> Remember to set `{ recursive: true, force: true }` to match the behavior of `fs-extra`.\n\n```js\nfsExtra.removeSync(path) // [!code --]\nfs.rmSync(path, { recursive: true, force: true }) // [!code ++]\n```\n\n### `mkdirs` / `mkdirp` / `ensureDir`\n\n```js\nawait fsExtra.mkdirs(dir) // [!code --]\nawait fsPromises.mkdir(dir, { recursive: true }) // [!code ++]\n```\n\n### `mkdirsSync` / `mkdirpSync` / `ensureDirSync`\n\n```js\nfsExtra.mkdirsSync(dir) // [!code --]\nfs.mkdirSync(dir, { recursive: true }) // [!code ++]\n```\n\n### `pathExists`\n\n<!-- prettier-ignore -->\n```js\nawait fsExtra.pathExists(path) // [!code --]\nawait fsPromises.access(path).then(() => true, () => false) // [!code ++]\n```\n\n### `pathExistsSync`\n\n```js\nfsExtra.pathExistsSync(path) // [!code --]\nfs.existsSync(path) // [!code ++]\n```\n\n### `outputFile`\n\n```js\nawait fsExtra.outputFile(file, data) // [!code --]\n\nawait fsPromises.mkdir(path.dirname(file), { recursive: true }) // [!code ++]\nawait fsPromises.writeFile(file, data) // [!code ++]\n```\n\n### `outputFileSync`\n\n```js\nfsExtra.outputFileSync(file, data) // [!code --]\n\nfs.mkdirSync(path.dirname(file), { recursive: true }) // [!code ++]\nfs.writeFileSync(file, data) // [!code ++]\n```\n\n### `readJson`\n\n```js\nawait fsExtra.readJson(file) // [!code --]\nawait fsPromises.readFile(file, 'utf8').then(JSON.parse) // [!code ++]\n```\n\n### `writeJson` / `outputJson`\n\n```js\nawait fsExtra.writeJson(file, obj) // [!code --]\n\nawait fsPromises.mkdir(path.dirname(file), { recursive: true }) // [!code ++]\nawait fsPromises.writeFile(file, JSON.stringify(obj, null, 2)) // [!code ++]\n```\n\n### `ensureFile` / `createFile`\n\n```js\nawait fsExtra.ensureFile(file) // [!code --]\n\nawait fsPromises.mkdir(path.dirname(file), { recursive: true }) // [!code ++]\nawait fsPromises.access(file).catch(() => fsPromises.writeFile(file, '')) // [!code ++]\n```\n\n### `ensureFileSync` / `createFileSync`\n\n```js\nfsExtra.ensureFileSync(file) // [!code --]\n\nfs.mkdirSync(path.dirname(file), { recursive: true }) // [!code ++]\nfs.writeFileSync(file, '') // [!code ++]\n```\n\n### `ensureLink` / `createLink`\n\n```js\nawait fsExtra.ensureLink(src, dest) // [!code --]\n\nawait fsPromises.mkdir(path.dirname(dest), { recursive: true }) // [!code ++]\nawait fsPromises.link(src, dest) // [!code ++]\n```\n\n### `ensureLinkSync` / `createLinkSync`\n\n```js\nfsExtra.ensureLinkSync(src, dest) // [!code --]\nfs.mkdirSync(path.dirname(dest), { recursive: true }) // [!code ++]\nfs.linkSync(src, dest) // [!code ++]\n```\n\n### `ensureSymlink` / `createSymlink`\n\n```js\nawait fsExtra.ensureSymlink(src, dest) // [!code --]\n\nawait fsPromises.mkdir(path.dirname(dest), { recursive: true }) // [!code ++]\nawait fsPromises.symlink(src, dest) // [!code ++]\n```\n\n### `ensureSymlinkSync` / `createSymlinkSync`\n\n```js\nfsExtra.ensureSymlinkSync(src, dest) // [!code --]\n\nfs.mkdirSync(path.dirname(dest), { recursive: true }) // [!code ++]\nfs.symlinkSync(src, dest) // [!code ++]\n```\n\n### `emptyDir` / `emptydir`\n\n```js\nawait fsExtra.emptyDir(dir) // [!code --]\n\nawait fsPromises.rm(dir, { recursive: true, force: true }) // [!code ++]\nawait fsPromises.mkdir(dir, { recursive: true }) // [!code ++]\n```\n\n### `move`\n\n```js\nawait fsExtra.move(src, dest) // [!code --]\nawait fsPromises.rename(src, dest) // [!code ++]\n```\n\n> [!NOTE]\n> Does not work cross-device; add `cp` + `rm` fallback.\n\nFor example:\n\n```js\ntry {\n  await fsPromises.rename(src, dest)\n} catch (err) {\n  if (err.code === 'EXDEV') {\n    await fsPromises.cp(src, dest, { recursive: true })\n    await fsPromises.rm(src, { recursive: true, force: true })\n  } else {\n    throw err\n  }\n}\n```\n",
  "get-stream.md": "---\ndescription: Modern alternatives to the get-stream package\n---\n\n# Replacements for `get-stream`\n\n## Converting stream to string\n\nYou can convert a stream to a string by using `Buffer.from` and a `for await`:\n\n```ts\nasync function streamToString(stream) {\n  const chunks = []\n  for await (const chunk of stream) {\n    chunks.push(Buffer.from(chunk))\n  }\n  return Buffer.concat(chunks).toString('utf-8')\n}\n```\n\n## Converting stream to Array\n\nYou can convert a stream to an array using [`Array.fromAsync`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/fromAsync) in Node 22+:\n\n```ts\nawait Array.fromAsync(stream)\n\n// or before Node 22\n\nasync function streamToArray(stream) {\n  const chunks = []\n  for await (const chunk of stream) {\n    chunks.push(chunk)\n  }\n  return chunks\n}\n```\n\n## Converting stream to Buffer\n\nYou can convert a stream to a `Buffer` using [`Array.fromAsync`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/fromAsync) with a mapper:\n\n```ts\nasync function streamToBuffer(stream) {\n  const chunks = await Array.fromAsync(stream, (chunk) => Buffer.from(chunk))\n  return Buffer.concat(chunks)\n}\n\n// or before Node 22\n\nasync function streamToBuffer(stream) {\n  const chunks = []\n  for await (const chunk of stream) {\n    chunks.push(Buffer.from(chunk))\n  }\n  return Buffer.concat(chunks)\n}\n```\n",
  "fetch.md": "---\ndescription: Shared alternatives and examples for fetch based HTTP clients used across related modules\n---\n\n# Fetch-based HTTP clients (shared)\n\nThis page contains the common, recommended alternatives and examples for fetch based HTTP clients used by `axios`, `node-fetch`, and `cross-fetch` replacement docs.\n\n## `fetch` API (native)\n\nThe native [`fetch`](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API) API is available in Node.js (since v18.x) and all modern browsers. For many use cases it replaces `axios`/`node-fetch`/`cross-fetch` without adding dependencies.\n\n> [!TIP]\n> Node.js provides a codemod which can migrate some of this automatically, see the docs at [@nodejs/axios-to-whatwg-fetch](https://app.codemod.com/registry/@nodejs/axios-to-whatwg-fetch) for more details.\n\nExample:\n\n```ts\n// GET\nconst res = await fetch('https://api.example.com/data')\nconst data = await res.json()\n\n// POST\nawait fetch('https://api.example.com/data', {\n  method: 'POST',\n  headers: { 'Content-Type': 'application/json' },\n  body: JSON.stringify({ key: 'value' })\n})\n```\n\n## `ofetch`\n\n[`ofetch`](https://github.com/unjs/ofetch) is a small fetch wrapper with automatic JSON parsing, request/response interceptors, and retry support.\n\nExample:\n\n```ts\nimport { ofetch } from 'ofetch'\n\nconst api = ofetch.create({ baseURL: 'https://api.example.com' })\n\nconst data = await api('/user', { query: { id: 123 } })\nconst created = await api('/items', { method: 'POST', body: { name: 'A' } })\n```\n\n## `ky`\n\n[`ky`](https://github.com/sindresorhus/ky) is a lightweight HTTP client built on top of the Fetch API. It adds convenience features like timeouts, hooks (interceptors), and a simpler API surface.\n\nExample:\n\n```ts\nimport ky from 'ky'\n\nconst api = ky.create({\n  prefixUrl: 'https://api.example.com',\n  timeout: 5000 // ms\n})\n\nconst data = await api.get('users').json()\n```\n",
  "img-colour.md": "---\ndescription: Modern alternatives to the @img/colour package for color parsing and manipulation\n---\n\n# Replacements for `@img/colour`\n\n## `color`\n\n[`@img/colour`](https://github.com/lovell/colour) is not a fork or alternative. It is an esbuild bundle of [`color`](https://github.com/Qix-/color) (and its dependencies `color-convert` and `color-string`) regenerated for CommonJS. Depending on `color` directly gives you the same API and dedupes with the many packages already using `color`, `color-convert` and `color-string`.\n\nExample:\n\n```ts\nimport Color from '@img/colour' // [!code --]\nimport Color from 'color' // [!code ++]\n\nconst red = Color('red').hex()\n```\n",
  "gzip-size.md": "---\ndescription: Native Node.js alternatives to the gzip-size for calculating gzipped size of a string or buffer\n---\n\n# Replacements for `gzip-size`\n\n## `gzipSync` (native, since Node.js 9.4.0)\n\nTo calculate the gzipped size of a string or an `ArrayBuffer`, you can use [gzipSync](https://nodejs.org/api/zlib.html#zlibgzipsync) which exist inside the `zlib` module:\n\n### Synchronous\n\n```ts\nimport { gzipSizeSync } from 'gzip-size' // [!code --]\nimport { gzipSync } from 'node:zlib' // [!code ++]\n\nconst text = 'Lorem ipsum dolor sil amet'\n\nconsole.log(gzipSizeSync(text)) // [!code --]\nconsole.log(gzipSync(text).length) // [!code ++]\n```\n\n### Asynchronous\n\n```ts\nimport { gzipSize } from 'gzip-size' // [!code --]\nimport { gzipSync } from 'node:zlib' // [!code ++]\nimport { promisify } from 'node:util' // [!code ++]\n\nconst gzipAsync = promisify(gzip) // [!code ++]\n\nconst text = 'Lorem ipsum dolor sil amet'\n\nconsole.log(await gzipSize(text)) // [!code --]\nconsole.log((await gzipAsync(text)).length) // [!code ++]\n```\n\n### Calculating from a file\n\n```ts\nimport { gzipSizeFromFileSync } from 'gzip-size' // [!code --]\nimport { gzipSync } from 'node:zlib' // [!code ++]\nimport { readFileSync } from 'node:fs' // [!code ++]\n\nconst path = '/path/to/file'\n\nconsole.log(gzipSizeFromFileSync(path)) // [!code --]\nconsole.log(gzipSync(readFileSync(path)).length) // [!code ++]\n```\n",
  "inherits.md": "---\ndescription: Modern alternatives to the inherits package\n---\n\n# Replacements for `inherits`\n\n## ES6 classes `extends` syntax\n\nES6 classes [`extends`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes/extends) syntax is a native way to implement prototype inheritance.\n\nExample:\n\n<!-- prettier-ignore -->\n```js\nimport EventEmitter from 'node:events'\nimport inherits from 'inherits' // [!code --]\n\nfunction MyStream() { // [!code --]\n  EventEmitter.call(this) // [!code --]\n} // [!code --]\n\nMyStream.prototype.write = function (data) { // [!code --]\n  this.emit('data', data) // [!code --]\n} // [!code --]\n\ninherits(MyStream, EventEmitter) // [!code --]\n\nclass MyStream extends EventEmitter { // [!code ++]\n  write(data) { // [!code ++]\n    this.emit('data', data) // [!code ++]\n  } // [!code ++]\n} // [!code ++]\n\nconst stream = new MyStream()\n\nstream.on('data', (data) => {\n  console.log(`Received data: \"${data}\"`)\n})\nstream.write('Hello world!')\n```\n\n## `utils.inherits` (native, since Node.js v5.0.0)\n\n[`utils.inherits`](https://nodejs.org/docs/latest/api/util.html#utilinheritsconstructor-superconstructor) is a native legacy Node.js api.\n\nExample:\n\n```js\nimport inherits from 'inherits' // [!code --]\nimport { inherits } from 'node:util' // [!code ++]\n\ninherits(Target, Base)\n```\n\n## `Object.create` (native)\n\n[`Object.create`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/create) allows you to implement inheritance.\n\nExample:\n\n<!-- prettier-ignore -->\n```js\nimport inherits from 'inherits' // [!code --]\n\ninherits(Target, Base) // [!code --]\n\nTarget.prototype = Object.create(Base.prototype, { // [!code ++]\n  constructor: { // [!code ++]\n    value: Target, // [!code ++]\n    enumerable: false, // [!code ++]\n    writable: true, // [!code ++]\n    configurable: true // [!code ++]\n  } // [!code ++]\n}) // [!code ++]\n```\n",
  "eslint-plugin-es.md": "---\ndescription: Modern alternatives to the eslint-plugin-es package for ECMAScript feature linting\n---\n\n# Replacements for `eslint-plugin-es`\n\n## `eslint-plugin-es-x`\n\n[eslint-plugin-es-x](https://github.com/eslint-community/eslint-plugin-es-x) is a direct fork which is actively maintained. It has new features, bugfixes and updated dependencies.\n\n```ts\nimport { FlatCompat } from '@eslint/eslintrc' // [!code --]\nimport pluginES from 'eslint-plugin-es' // [!code --]\nimport pluginESx from 'eslint-plugin-es-x' // [!code ++]\n\nconst compat = new FlatCompat() // [!code --]\n\nexport default [\n  {\n    files: ['**/*.js'],\n    languageOptions: {\n      ecmaVersion: 2020\n    },\n    plugins: {\n      es: pluginES, // [!code --]\n      'es-x': pluginESx // [!code ++]\n    },\n    rules: {\n      'es/no-regexp-lookbehind-assertions': 'error', // [!code --]\n      'es-x/no-regexp-lookbehind-assertions': 'error' // [!code ++]\n    }\n  },\n\n  ...compat.extends('plugin:es/restrict-to-es2018'), // [!code --]\n  pluginESx.configs['flat/restrict-to-es2018'] // [!code ++]\n]\n```\n\nIf you're using a legacy config format:\n\n```ts\nmodule.exports = {\n  extends: [\n    'eslint:recommended',\n    'plugin:es/restrict-to-es2018', // [!code --]\n    'plugin:es-x/restrict-to-es2018' // [!code ++]\n  ],\n  plugins: [\n    'es', // [!code --]\n    'es-x' // [!code ++]\n  ],\n  rules: {\n    'es/no-regexp-lookbehind-assertions': 'error', // [!code --]\n    'es-x/no-regexp-lookbehind-assertions': 'error' // [!code ++]\n  }\n}\n```\n",
  "find-file-up.md": "---\ndescription: Modern alternatives to the find-file-up package for finding files by walking up parent directories\n---\n\n# Replacements for `find-file-up`\n\n## `empathic`\n\n[`empathic`](https://github.com/lukeed/empathic) provides a more generic way to find files and directories upwards.\n\nThe main difference is that `empathic` is _synchronous_, so you should no longer `await` the result.\n\nExample:\n\n```ts\nimport * as find from 'empathic/find' // [!code ++]\nimport findUp from 'find-file-up' // [!code --]\n\nawait findUp('package.json', cwd) // [!code --]\nfind.file('package.json', { cwd }) // [!code ++]\n```\n",
  "jquery.md": "---\ndescription: Modern alternatives to the jQuery library for DOM traversal, events, and AJAX\n---\n\n# Replacements for `jQuery`\n\n## You might not need jQuery\n\n[You might not need jQuery](https://youmightnotneedjquery.com/) is a side‑by‑side catalog of native JavaScript equivalents for common jQuery patterns (selectors, traversal, manipulation, events, AJAX), with concise before/after examples.\n\n## You (Might) Don't Need jQuery\n\n[You‑Dont‑Need‑jQuery](https://github.com/camsong/You-Dont-Need-jQuery) is a community‑maintained guide that shows how to handle querying, styling, DOM manipulation, AJAX, and events with plain JavaScript.\n",
  "lodash-underscore.md": "---\ndescription: Modern alternatives for Lodash for array/object manipulation and common programming tasks\n---\n\n# `lodash` / `underscore`\n\n## You don’t (may not) need Lodash/Underscore\n\nHere you could read how to replace Lodash or Underscore in your project.\n\n[Website](https://you-dont-need.github.io/You-Dont-Need-Lodash-Underscore)\n\n## es-toolkit\n\n[es-toolkit](https://es-toolkit.dev/) is a utility library similar to lodash that is designed to replace lodash by offering a seamless compat layer. It supports tree shaking out of the box and offers better performances for modern JavaScript runtimes.\n",
  "duplexer.md": "---\ndescription: Modern alternatives to the duplexer package\n---\n\n# Replacements for `duplexer`\n\n## `Duplex.from` (native, Node.js)\n\n<!-- prettier-ignore -->\n```js\nimport duplexer from 'duplexer' // [!code --]\nimport { Duplex } from 'node:stream' // [!code ++]\n\nduplexer(writableStream, readableStream) // [!code --]\n\nDuplex.from({ // [!code ++]\n  writable: writableStream, // [!code ++]\n  readable: readableStream // [!code ++]\n}) // [!code ++]\n```\n",
  "materialize-css.md": "---\ndescription: Modern alternatives to materialize-css (Materialize) and modern Material Design UI libraries\n---\n\n# Replacements for `materialize-css`\n\n## `@materializecss/materialize`\n\n[`@materializecss/materialize`](https://github.com/materializecss/materialize) is a community-maintained fork of [`Materialize`](https://github.com/Dogfalo/materialize). Acts as a practical replacement for the original materialize-css package.\n\n## `@material/web`\n\n> [!NOTE]\n> The project is currently in maintenance mode pending new maintainers.\n\nModern Web Components implementing Material Design 3.\n\n[Project Page](https://github.com/material-components/material-web)\n",
  "collection-map.md": "---\ndescription: Native alternatives to the collection-map package\n---\n\n# Replacements for `collection-map`\n\n## Objects\n\n<!-- prettier-ignore -->\n```js\nconst map = require('collection-map') // [!code --]\nconst data = { a: 'foo', b: 'bar', c: 'baz' }\n\n// Mapping values\nconst res = map(data, (item) => item) // [!code --]\nconst res = Object.values(data).map((item) => item) // [!code ++]\n\n// Mapping keys\nconst res = map(data, (item, key) => key) // [!code --]\nconst res = Object.keys(data).map((key) => key) // [!code ++]\n\n// Mapping keys and values\nconst res = map(data, (item, key) => fn(item, key)) // [!code --]\nconst res = Object.fromEntries( // [!code ++]\n  Object.entries(data).map(([key, value]) => fn(value, key)) // [!code ++]\n) // [!code ++]\n```\n\n## Arrays\n\n```js\nconst map = require('collection-map') // [!code --]\nconst data = ['foo', 'bar', 'baz']\n\n// Mapping items\nconst res = map(data, (item) => item) // [!code --]\nconst res = data.map((item) => item) // [!code ++]\n\n// Mapping indices\nconst res = map(data, (item, index) => index) // [!code --]\nconst res = data.map((_, index) => index) // [!code ++]\n\n// Mapping items indices\nconst res = map(data, (item, index) => fn(item, index)) // [!code --]\nconst res = data.map((item, index) => fn(item, index)) // [!code ++]\n```\n\n## Property Strings\n\nWhen passing a string to extract a specific property, use a standard map with property access.\n\n```js\nconst map = require('collection-map') // [!code --]\n\nconst obj = {\n  a: { aaa: 'one', bbb: 'four', ccc: 'seven' },\n  b: { aaa: 'two', bbb: 'five', ccc: 'eight' },\n  c: { aaa: 'three', bbb: 'six', ccc: 'nine' }\n}\n\n// Objects: Map values to property\nmap(obj, 'aaa') // [!code --]\nObject.values(obj).map((item) => item.aaa) // [!code ++]\n\n// Arrays: Map items to property\nconst array = [obj.a, obj.b, obj.c]\nmap(array, 'bbb') // [!code --]\narray.map((item) => item.bbb) // [!code ++]\n```\n\n## `thisArg` (Context)\n\nNative `.map()` supports a second argument for `thisArg`.\n\n<!-- prettier-ignore -->\n```js\nconst map = require('collection-map') // [!code --]\n\nconst array = ['a', 'b', 'c']\nconst ctx = { a: 'aaa', b: 'bbb', c: 'ccc' }\n\nconst res = map(array, function(item) { // [!code --]\nconst res = array.map(function(item) { // [!code ++]\n  return this[item]\n}, ctx)\n```\n",
  "http-proxy.md": "---\ndescription: Modern alternatives to the http-proxy package for HTTP and WebSocket proxying in Node.js\n---\n\n# Replacements for `http-proxy`\n\n[`http-proxy`](https://github.com/http-party/node-http-proxy) has not been maintained since 2020 and has known crashes, memory leaks, and socket issues on modern Node.js versions.\n\n## `httpxy`\n\n[`httpxy`](https://github.com/unjs/httpxy) is a maintained fork of `http-proxy` with critical upstream bug fixes, zero dependencies, and additional helpers such as `proxyFetch` and `proxyUpgrade`.\n\nExample:\n\n```ts\nimport httpProxy from 'http-proxy' // [!code --]\nimport { createProxyServer } from 'httpxy' // [!code ++]\nimport { createServer } from 'node:http'\n\nconst proxy = httpProxy.createProxyServer({}) // [!code --]\nconst proxy = createProxyServer({}) // [!code ++]\n\nconst server = createServer((req, res) => {\n  proxy.web(req, res, { target: 'http://localhost:8080' }) // [!code --]\n  proxy.web(req, res, { target: 'http://localhost:8080' }) // [!code ++]\n})\n\nserver.on('upgrade', (req, socket, head) => {\n  proxy.ws(req, socket, head, { target: 'http://localhost:8080' }) // [!code --]\n  proxy.ws(req, socket, { target: 'http://localhost:8080' }, head) // [!code ++]\n})\n```\n\n## `http-proxy-3`\n\n[`http-proxy-3`](https://github.com/sagemathinc/http-proxy-3) is a TypeScript rewrite with API compatibility, HTTP/2 support, and production use in Vite, JupyterHub, and CoCalc.\n\nExample:\n\n```ts\nimport httpProxy from 'http-proxy' // [!code --]\nimport { createProxyServer } from 'http-proxy-3' // [!code ++]\nimport { createServer } from 'node:http'\n\nconst proxy = httpProxy.createProxyServer({}) // [!code --]\nconst proxy = createProxyServer({}) // [!code ++]\n\nconst server = createServer((req, res) => {\n  proxy.web(req, res, { target: 'http://localhost:8080' })\n})\n\nserver.on('upgrade', (req, socket, head) => {\n  proxy.ws(req, socket, head, { target: 'http://localhost:8080' }) // [!code --]\n  proxy.ws(req, socket, { target: 'http://localhost:8080' }, head) // [!code ++]\n})\n```\n",
  "md5.md": "---\ndescription: Native Node.js alternatives to the md5 package for MD5 hash generation\n---\n\n# Replacements for `md5`\n\n## `crypto` (native)\n\nIf you're using the [`md5`](https://github.com/pvorb/node-md5) package, _strongly_ consider moving to a stronger algorithm if your usage is security-sensitive (for example, passwords). MD5 is [not secure](https://en.wikipedia.org/wiki/MD5#Security) and hasn't been secure for many years.\n\nIf you must keep MD5 for compatibility, Node.js provides a native alternative via the [`crypto` module](https://nodejs.org/api/crypto.html#crypto).\n\n```ts\nimport crypto from 'node:crypto' // [!code ++]\nimport md5 from 'md5' // [!code --]\n\nmd5('message') // [!code --]\ncrypto.createHash('md5').update('message').digest('hex') // [!code ++]\n```\n",
  "mime.md": "---\ndescription: Lighter alternative to the mime package for looking up MIME types from file extensions\n---\n\n# Replacements for `mime`\n\n## `mrmime`\n\n[`mrmime`](https://github.com/lukeed/mrmime) is a smaller alternative to [`mime`](https://github.com/broofa/mime) for looking up MIME types from extensions or filenames. It only includes standard MIME types; experimental and vendor-specific types are omitted.\n\nExample:\n\n```js\nimport mime from 'mime' // [!code --]\nimport { lookup } from 'mrmime' // [!code ++]\n\nmime.getType('txt') // [!code --]\nlookup('txt') // [!code ++]\n// also works: lookup('.txt'), lookup('a.txt')\n```\n",
  "concat-stream.md": "---\ndescription: Modern alternatives to the concat-stream package\n---\n\n# Replacements for `concat-stream`\n\n## Utility Consumers (native, since Node.js 16.7.0)\n\n### Consuming as a string\n\n<!-- prettier-ignore -->\n```js\nimport concat from 'concat-stream' // [!code --]\nimport { text } from 'node:stream/consumers' // [!code ++]\n\nreadable.pipe(concat((data) => { // [!code --]\n  const str = data.toString('utf-8') // [!code --]\n  console.log(str) // [!code --]\n})) // [!code --]\n\nconst str = await text(readable) // [!code ++]\nconsole.log(str) // [!code ++]\n```\n\n### Consuming as Buffer\n\n<!-- prettier-ignore -->\n```js\nimport concat from 'concat-stream' // [!code --]\nimport { buffer } from 'node:stream/consumers' // [!code ++]\n\nreadable.pipe(concat((data) => { // [!code --]\n  console.log(data.length) // [!code --]\n})) // [!code --]\n\nconst data = await buffer(readable) // [!code ++]\nconsole.log(data.length) // [!code ++]\n```\n\n### Consuming as an Array\n\n<!-- prettier-ignore -->\n```js\nimport concat from 'concat-stream' // [!code --]\nimport { toArray } from 'node:stream/consumers' // [!code ++]\n\nreadable.pipe(concat({ encoding: 'object' }, (items) => { // [!code --]\n  console.log(items) // [!code --]\n})) // [!code --]\n\nconst items = await toArray(readable) // [!code ++]\nconsole.log(items) // [!code ++]\n```\n\nAlternatively, using an async iterable loop:\n\n```js\nfor await (const chunk of readable) {\n  // ...\n}\n```\n",
  "js-yaml.md": "---\ndescription: Modern alternatives to js-yaml for YAML parsing and stringifying\n---\n\n# Replacements for `js-yaml`\n\nThe alternatives below can offer better spec compliance, smaller install sizes.\n\n## `yaml`\n\n[`yaml`](https://github.com/eemeli/yaml) is a well maintained YAML 1.2/1.1 parser/stringifier with better spec compliance, comment/AST support, and no deps.\n\nParse (load):\n\n```ts\nimport yaml from 'js-yaml' // [!code --]\nimport { parse } from 'yaml' // [!code ++]\n\nconst obj = yaml.load(src) // [!code --]\nconst obj = parse(src) // [!code ++]\n```\n\nStringify (dump):\n\n```ts\nimport yaml from 'js-yaml' // [!code --]\nimport { stringify } from 'yaml' // [!code ++]\n\nconst text = yaml.dump(obj) // [!code --]\nconst text = stringify(obj) // [!code ++]\n```\n\nMulti-document:\n\n```ts\nimport yaml from 'js-yaml' // [!code --]\nimport { parseAllDocuments } from 'yaml' // [!code ++]\n\nconst out: any[] = [] // [!code --]\nyaml.loadAll(src, (d) => out.push(d)) // [!code --]\nconst out = parseAllDocuments(src).map((d) => d.toJSON()) // [!code ++]\n```\n\n## Bun `YAML` API\n\n[Native YAML parsing](https://bun.com/docs/runtime/yaml) is supported in Bun since [v1.2.21](https://bun.com/blog/release-notes/bun-v1.2.21#native-yaml-support).\n\nExample:\n\n```ts\nimport yaml from 'js-yaml' // [!code --]\nimport { YAML } from 'bun' // [!code ++]\n\nyaml.load(src) // [!code --]\nYAML.parse(src) // [!code ++]\n```\n",
  "eslint-plugin-react.md": "---\ndescription: Modern alternatives to the eslint-plugin-react package for React/JSX-specific linting rules\n---\n\n# Replacements for `eslint-plugin-react`\n\n## `@eslint-react/eslint-plugin`\n\n[`@eslint-react/eslint-plugin`](https://github.com/Rel1cx/eslint-react) is not a drop-in replacement, but a feature‑rich alternative that covers many of the same (and additional) rules.\n\nFlat config example:\n\n```ts\nimport eslintReact from '@eslint-react/eslint-plugin' // [!code ++]\nimport reactPlugin from 'eslint-plugin-react' // [!code --]\n\nexport default [\n  {\n    files: ['**/*.{jsx,tsx}'],\n    plugins: {\n      react: reactPlugin, // [!code --]\n      '@eslint-react': eslintReact // [!code ++]\n    },\n    rules: {\n      ...reactPlugin.configs.recommended.rules, // [!code --]\n      ...eslintReact.configs.recommended.rules, // [!code ++]\n\n      'react/no-unknown-property': 'error', // [!code --]\n      '@eslint-react/dom/no-unknown-property': 'error' // [!code ++]\n    }\n  }\n]\n```\n\n> [!NOTE]\n> `@eslint-react/eslint-plugin` is not a drop‑in replacement. Use [their migration guide](https://www.eslint-react.xyz/docs/migrating-from-eslint-plugin-react) to map rules/options and automate changes where possible.\n\n## Oxlint\n\n[Oxlint](https://oxc.rs/docs/guide/usage/linter.html) is a high-performance linter for JavaScript and TypeScript, built on the Rust-based `Oxc` compiler stack. It's intended to be fully backwards-compatible with ESLint, having ported most of the ESLint rules, as well as those from popular plugins including `eslint-plugin-react`.\n\nThe migration process from ESLint is covered in [the Oxlint documentation](https://oxc.rs/docs/guide/usage/linter/migrate-from-eslint.html), and can be done automatically from an ESLint flat config using [`npx @oxlint/migrate`](https://github.com/oxc-project/oxlint-migrate).\n\n> [!NOTE]\n> Oxlint is not necessarily a full drop-in replacement, as not all of the `eslint-plugin-react` rules have been, or will be, implemented. Check [the GitHub issue](https://github.com/oxc-project/oxc/issues/1022) to view the progress.\n",
  "graphviz.md": "---\ndescription: Modern alternatives to the graphviz package\n---\n\n# Replacements for `graphviz`\n\n## `ts-graphviz`\n\n[`ts-graphviz`](https://github.com/ts-graphviz/ts-graphviz) is an actively maintained graphviz implementation in pure TypeScript.\n\nExample:\n\n```ts\nimport {\n  attribute as _,\n  Digraph,\n  Subgraph,\n  Node,\n  Edge,\n  toDot\n} from 'ts-graphviz'\n\nconst G = new Digraph()\nconst A = new Subgraph('A')\nconst node1 = new Node('node1', {\n  [_.color]: 'red'\n})\nconst node2 = new Node('node2', {\n  [_.color]: 'blue'\n})\nconst edge = new Edge([node1, node2], {\n  [_.label]: 'Edge Label',\n  [_.color]: 'pink'\n})\nG.addSubgraph(A)\nA.addNode(node1)\nA.addNode(node2)\nA.addEdge(edge)\nconst dot = toDot(G)\n// digraph {\n//   subgraph \"A\" {\n//     \"node1\" [\n//       color = \"red\",\n//     ]\n//     \"node2\" [\n//       color = \"blue\",\n//     ]\n//     \"node1\" -> \"node2\" [\n//       label = \"Edge Label\",\n//       color = \"pink\",\n//     ]\n//   }\n// }\n```\n",
  "find-cache-dir.md": "---\ndescription: Modern alternatives to the find-cache-dir package for locating cache directories\n---\n\n# Replacements for `find-cache-dir`\n\n## `empathic`\n\n[`empathic`](https://github.com/lukeed/empathic) provides a more generic way to find files and directories upwards.\n\nExample:\n\n```ts\nimport * as pkg from 'empathic' // [!code ++]\nimport findCacheDirectory from 'find-cache-dir' // [!code --]\n\nfindCacheDirectory({ name: 'foo' }) // [!code --]\npkg.cache('foo') // [!code ++]\n```\n",
  "eslint-plugin-node.md": "---\ndescription: Modern alternatives to the eslint-plugin-node package for Node.js-specific linting rules\n---\n\n# Replacements for `eslint-plugin-node`\n\n## `eslint-plugin-n`\n\n[`eslint-plugin-n`](https://github.com/eslint-community/eslint-plugin-n) is a direct fork which is actively maintained. It has new features, bugfixes and updated dependencies.\n\n```ts\nimport nPlugin from 'eslint-plugin-n' // [!code ++]\nimport nodePlugin from 'eslint-plugin-node' // [!code --]\n\nexport default [\n  {\n    files: ['**/*.js'], // or any other pattern\n    plugins: {\n      node: nodePlugin, // [!code --]\n      n: nPlugin // [!code ++]\n    },\n    rules: {\n      ...nodePlugin.configs['recommended-script'].rules, // [!code --]\n      ...nPlugin.configs['recommended-script'].rules, // [!code ++]\n      'node/exports-style': ['error', 'module.exports'], // [!code --]\n      'n/exports-style': ['error', 'module.exports'] // [!code ++]\n    }\n  }\n]\n```\n\nIf you're using a legacy config format:\n\n```ts\nmodule.exports = {\n  extends: [\n    'eslint:recommended',\n    'plugin:node/recommended', // [!code --]\n    'plugin:n/recommended' // [!code ++]\n  ]\n}\n```\n",
  "eslint-config-airbnb.md": "---\ndescription: Modern, maintained alternative to the eslint-config-airbnb shared config\n---\n\n# Replacements for `eslint-config-airbnb`\n\n## `eslint-config-airbnb-extended`\n\n[`eslint-config-airbnb-extended`](https://github.com/eslint-config/airbnb-extended) is a more modern, actively maintained successor to [`eslint-config-airbnb`](https://github.com/airbnb/javascript). It supports both JavaScript and TypeScript, and includes rules for React, JSX, and React Hooks, and supports ESLint v9/10 configs.\n\nIt consolidates `eslint-config-airbnb-base`, `eslint-config-airbnb`, and `eslint-config-airbnb-typescript` into a single config, only supports flat config (`eslint.config.mjs`), and uses a faster, modern equivalent of the `import` plugin (`eslint-plugin-import-x`).\n\nThe upstream project provides an official [migration guide](https://eslint-airbnb-extended.nishargshah.dev/migration/upgrade-to-extended) and a CLI scaffolder to set up a new config.\n\nNote that any rules referencing the old `import/*` plugin will need to be updated to use the `import-x/*` equivalents (e.g. `import/no-unresolved` -> `import-x/no-unresolved`).\n",
  "eslint-plugin-import.md": "---\ndescription: Modern alternative to eslint-plugin-import, which helps with linting of ES6+ import/export syntax\n---\n\n# Replacements for `eslint-plugin-import`\n\n## `eslint-plugin-import-x`\n\n[`eslint-plugin-import-x`](https://github.com/un-ts/eslint-plugin-import-x) is a modern fork of [`eslint-plugin-import`](https://github.com/import-js/eslint-plugin-import). `import-x` focuses on having a smaller dependency footprint and faster module resolution via a Rust-based resolver.\n\n### Flat config\n\n<!-- prettier-ignore -->\n```ts\nimport importPlugin from 'eslint-plugin-import' // [!code --]\nimport { createNodeResolver, importX } from 'eslint-plugin-import-x' // [!code ++]\nimport { createTypeScriptImportResolver } from 'eslint-import-resolver-typescript' // [!code ++]\n\nexport default [\n  importPlugin.flatConfigs.recommended, // [!code --]\n  importX.flatConfigs.recommended, // [!code ++]\n  {\n    settings: {\n      'import/resolver': { typescript: true }, // [!code --]\n      'import-x/resolver-next': [ // [!code ++]\n        createTypeScriptImportResolver(), // [!code ++]\n        createNodeResolver() // [!code ++]\n      ] // [!code ++]\n    },\n    rules: {\n      'import/no-unresolved': 'error', // [!code --]\n      'import-x/no-unresolved': 'error', // [!code ++]\n      'import/no-nodejs-modules': 'warn', // [!code --]\n      'import-x/no-nodejs-modules': 'warn' // [!code ++]\n    }\n  }\n]\n```\n\n### Legacy config\n\n```ts\nmodule.exports = {\n  extends: [\n    'eslint:recommended',\n    'plugin:import/recommended', // [!code --]\n    'plugin:import-x/recommended', // [!code ++]\n    'plugin:import/typescript', // [!code --]\n    'plugin:import-x/typescript' // [!code ++]\n  ],\n  plugins: [\n    'import', // [!code --]\n    'import-x' // [!code ++]\n  ],\n  settings: {\n    'import/resolver': { typescript: true }, // [!code --]\n    'import-x/resolver': { typescript: true } // [!code ++]\n  },\n  rules: {\n    'import/no-unresolved': 'error', // [!code --]\n    'import-x/no-unresolved': 'error' // [!code ++]\n  }\n}\n```\n\n## Oxlint\n\n[Oxlint](https://oxc.rs/docs/guide/usage/linter.html) is a high-performance linter for JavaScript and TypeScript, built on the Rust-based `Oxc` compiler stack. It's intended to be fully backwards-compatible with ESLint, having ported most of the ESLint rules, as well as those from popular plugins including `eslint-plugin-import`.\n\nThe migration process from ESLint is covered in [the Oxlint documentation](https://oxc.rs/docs/guide/usage/linter/migrate-from-eslint.html), and can be done automatically from an ESLint flat config using [`npx @oxlint/migrate`](https://github.com/oxc-project/oxlint-migrate).\n\n> [!NOTE]\n> Oxlint is not necessarily a full drop-in replacement, as not all of the `eslint-plugin-import` rules have been, or will be, implemented. Check [the GitHub issue](https://github.com/oxc-project/oxc/issues/1117) to view the progress.\n",
  "moment.md": "---\ndescription: Modern alternatives to moment.js for date manipulation and formatting\n---\n\n# Replacements for `Moment.js`\n\n## `day.js`\n\n[Day.js](https://github.com/iamkun/dayjs/) provides a similar API to Moment.js with a much smaller footprint.\n\nExample:\n\n```ts\nimport moment from 'moment' // [!code --]\nimport dayjs from 'dayjs' // [!code ++]\n\nconst now = moment() // [!code --]\nconst now = dayjs() // [!code ++]\n\nconst formatted = moment().format('YYYY-MM-DD') // [!code --]\nconst formatted = dayjs().format('YYYY-MM-DD') // [!code ++]\n```\n\n## `date-fns`\n\n[date-fns](https://github.com/date-fns/date-fns) offers tree-shakable functions for working with native JavaScript dates.\n\nExample:\n\n```ts\nimport moment from 'moment' // [!code --]\nimport { addDays, format, subWeeks } from 'date-fns' // [!code ++]\n\nconst formatted = moment().format('YYYY-MM-DD') // [!code --]\nconst formatted = format(new Date(), 'yyyy-MM-dd') // [!code ++]\n\nconst tomorrow = moment().add(1, 'day') // [!code --]\nconst tomorrow = addDays(new Date(), 1) // [!code ++]\n\nconst lastWeek = moment().subtract(1, 'week') // [!code --]\nconst lastWeek = subWeeks(new Date(), 1) // [!code ++]\n```\n\n## `luxon`\n\n[Luxon](https://github.com/moment/luxon) is created by a Moment.js maintainer and offers powerful internationalization support.\n\nExample:\n\n```ts\nimport moment from 'moment' // [!code --]\nimport { DateTime } from 'luxon' // [!code ++]\n\nconst now = moment() // [!code --]\nconst now = DateTime.now() // [!code ++]\n\nconst formatted = moment().format('YYYY-MM-DD') // [!code --]\nconst formatted = DateTime.now().toFormat('yyyy-MM-dd') // [!code ++]\n\nconst tomorrow = moment().add(1, 'day') // [!code --]\nconst tomorrow = DateTime.now().plus({ days: 1 }) // [!code ++]\n```\n\n## `Date` (native)\n\nFor simple use cases, native JavaScript [`Date`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date) and [`Intl`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl) APIs may be sufficient:\n\n<!-- prettier-ignore -->\n```ts\nimport moment from 'moment' // [!code --]\n\nconst formatted = moment().format('YYYY-MM-DD') // [!code --]\nconst formatted = new Date().toISOString().split('T')[0] // [!code ++]\n\nconst localized = moment().format('MMMM Do YYYY') // [!code --]\nconst localized = new Intl.DateTimeFormat('en-US', { // [!code ++]\n  year: 'numeric', // [!code ++]\n  month: 'long', // [!code ++]\n  day: 'numeric' // [!code ++]\n}).format(new Date()) // [!code ++]\n```\n",
  "object-hash.md": "---\ndescription: Modern alternatives to object-hash for hashing objects and values\n---\n\n# Replacements for `object-hash`\n\n## `ohash`\n\n[`ohash`](https://github.com/unjs/ohash) is actively maintained and provides hashing, stable serialization, equality checks, and diffs. It uses stable serialization + SHA-256, returning Base64URL by default. Its serializer was originally based on `object-hash`.\n\nExample:\n\n```ts\nimport objectHash from 'object-hash' // [!code --]\nimport { hash } from 'ohash' // [!code ++]\n\nconst h = objectHash(obj) // [!code --]\nconst h = hash(obj) // [!code ++]\n```\n\n## Web Crypto\n\nUse the standard [`SubtleCrypto.digest`](https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/digest) available in modern runtimes. Pair it with a stable serializer (e.g., [`safe-stable-stringify`](https://github.com/BridgeAR/safe-stable-stringify)) to ensure deterministic key ordering.\n\nExample:\n\n```ts\nimport objectHash from 'object-hash' // [!code --]\nimport stringify from 'safe-stable-stringify' // [!code ++]\n\nconst h = objectHash(obj, { algorithm: 'sha256' }) // [!code --]\nconst data = new TextEncoder().encode(stringify(obj)) // [!code ++]\nconst buf = await crypto.subtle.digest('SHA-256', data) // [!code ++]\nconst h = Array.from(new Uint8Array(buf)) // [!code ++]\n  .map((b) => b.toString(16).padStart(2, '0')) // [!code ++]\n  .join('') // [!code ++]\n```\n\n## Bun `CryptoHasher`\n\nBun provides a native incremental hasher (e.g., SHA-256). Combine it with a stable serializer for object hashing. For fast non-crypto fingerprints, see [`Bun.hash`](https://bun.com/reference/bun/hash).\n\nDocs: https://bun.com/reference/bun/CryptoHasher\n\nExample:\n\n```ts\nimport objectHash from 'object-hash' // [!code --]\nimport stringify from 'safe-stable-stringify' // [!code ++]\n\nconst h = objectHash(obj, { algorithm: 'sha256' }) // [!code --]\nconst hasher = new CryptoHasher('sha256') // [!code ++]\nhasher.update(stringify(obj)) // [!code ++]\nconst h = hasher.digest('hex') // [!code ++]\n```\n",
  "npm-run-all.md": "---\ndescription: Modern alternatives to the npm-run-all package for running multiple npm scripts\n---\n\n# Replacements for `npm-run-all`\n\n## `npm-run-all2`\n\n[npm-run-all2](https://github.com/bcomnes/npm-run-all2) is an actively maintained fork with important fixes, dependency updates.\n\n```json\n{\n  \"scripts\": {\n    \"build\": \"npm-run-all clean lint compile\"\n  }\n}\n```\n\nThe commands remain the same: `npm-run-all`, `run-s`, and `run-p`.\n\n## `concurrently`\n\nAnother option is [concurrently](https://github.com/open-cli-tools/concurrently), which focuses on running scripts in parallel with colored output and process control. It uses a slightly different syntax but works well for replacing the `--parallel` use case.\n\n```json\n{\n  \"scripts\": {\n    \"dev\": \"npm-run-all --parallel \\\"watch-*\\\" start\", // [!code --]\n    \"dev\": \"concurrently \\\"npm:watch-*\\\" \\\"npm:start\\\"\" // [!code ++]\n  }\n}\n```\n\n## `wireit`\n\nFor more advanced workflows, consider [Wireit](https://github.com/google/wireit). It integrates directly into `package.json` to add caching, dependency graphs, watch mode, and incremental builds. Unlike `npm-run-all`, Wireit upgrades your existing `npm run` experience instead of providing a separate CLI.\n\n```json\n{\n  \"scripts\": {\n    \"build\": \"wireit\",\n    \"compile\": \"wireit\",\n    \"bundle\": \"wireit\"\n  },\n  \"wireit\": {\n    \"build\": {\n      \"dependencies\": [\"compile\", \"bundle\"]\n    },\n    \"compile\": {\n      \"command\": \"tsc\",\n      \"files\": [\"src/**/*.ts\"],\n      \"output\": [\"lib/**\"]\n    },\n    \"bundle\": {\n      \"command\": \"rollup -c\",\n      \"dependencies\": [\"compile\"],\n      \"files\": [\"rollup.config.js\"],\n      \"output\": [\"dist/**\"]\n    }\n  }\n}\n```\n\n## `bun run --parallel` / `bun run --sequential`\n\nIf you are using bun you can use [`bun run --parallel`](https://bun.com/docs/runtime#param-parallel) and [`bun run --sequential`](https://bun.com/docs/runtime#param-sequential).\n\n### Parallel\n\n```json\n{\n  \"scripts\": {\n    \"dev\": \"npm-run-all --parallel \\\"dev:*\\\"\", // [!code --]\n    \"dev\": \"bun run --parallel dev:*\" // [!code ++]\n  }\n}\n```\n\n### Sequential\n\n```json\n{\n  \"scripts\": {\n    \"build\": \"run-s clean lint compile\", // [!code --]\n    \"build\": \"bun run --sequential clean lint compile\" // [!code ++]\n  }\n}\n```\n\n## `pnpm run \"/<regex>/\"`\n\nIf you are using pnpm you can use [regex script matching](https://pnpm.io/cli/run#running-multiple-scripts) to run multiple scripts at once. Wrap the regex in quotes so the shell does not mangle it.\n\n```json\n{\n  \"scripts\": {\n    \"dev\": \"npm-run-all --parallel \\\"dev:*\\\"\", // [!code --]\n    \"dev\": \"pnpm run \\\"/^dev:.*/\\\"\" // [!code ++]\n  }\n}\n```\n\nTo run them sequentially instead of in parallel, add [`--sequential`](https://pnpm.io/cli/run#--sequential--s):\n\n```json\n{\n  \"scripts\": {\n    \"build\": \"pnpm run --sequential \\\"/^build:.*/\\\"\"\n  }\n}\n```\n",
  "glob.md": "---\ndescription: Modern alternatives to the glob package for file pattern matching and globbing\n---\n\n# Replacements for `glob`\n\n## `tinyglobby`\n\n[`tinyglobby`](https://github.com/SuperchupuDev/tinyglobby) provides a similar API.\n\nExample:\n\n```ts\nimport { glob } from 'glob' // [!code --]\nimport { glob } from 'tinyglobby' // [!code ++]\n\nconst files = await glob('**/*.ts')\n```\n\nMost options available to `glob` are available in `tinyglobby`, read more at the [tinyglobby documentation](https://superchupu.dev/tinyglobby/documentation).\n\n## `fs.glob` (native, since Node 22.x)\n\n[`fs.glob`](https://nodejs.org/api/fs.html#fspromisesglobpattern-options) is built into modern versions of Node.\n\nExample:\n\n<!-- eslint-skip -->\n\n```ts\nimport { glob } from 'glob' // [!code --]\nimport { glob } from 'node:fs/promises' // [!code ++]\n\nconst files = await glob('src/**/*.ts', { // [!code --]\nconst files = await Array.fromAsync(glob('src/**/*.ts', { // [!code ++]\n  cwd,\n}) // [!code --]\n})) // [!code ++]\n```\n\nYou can also iterate over the results asynchronously:\n\n```ts\nfor await (const result of glob('src/**/*.ts', { cwd })) {\n  // result is an individual path\n  console.log(result)\n}\n```\n\n> [!NOTE]\n> Node's built-in `glob` is more minimal and does not support negation patterns or fine-grained options like setting a max depth out of the box.\n\n## `fdir`\n\n[`fdir`](https://github.com/thecodrr/fdir/) offers similar functionality but through a different API (and `tinyglobby` is actually built on top of it).\n\nExample:\n\n<!-- eslint-skip -->\n\n```ts\nimport { fdir } from 'fdir' // [!code ++]\nimport { glob } from 'glob' // [!code --]\n\nconst files = new fdir() // [!code ++]\n  .withBasePath() // [!code ++]\n  .glob('src/**/*.ts') // [!code ++]\n  .crawl(cwd) // [!code ++]\n  .withPromise() // [!code ++]\nconst files = await glob('src/**/*.ts', { cwd, maxDepth: 6 }) // [!code --]\n```\n",
  "html-minifier.md": "---\ndescription: Modern replacement for the html-minifier package for minifying HTML with embedded CSS, JavaScript, and SVG\n---\n\n# Replacements for `html-minifier`\n\n## `html-minifier-next`\n\n[`html-minifier-next`](https://github.com/j9t/html-minifier-next) is the official successor to [`html-minifier`](https://github.com/kangax/html-minifier). It is actively maintained, faster, and adds features such as SVG minification and preset configurations.\n\n```ts\nimport minify from 'html-minifier' // [!code --]\nimport { minify } from 'html-minifier-next' // [!code ++]\n\nconst html = '<p title=\"example\">foo</p>'\n\nconst result = minify(html, { collapseWhitespace: true }) // [!code --]\nconst result = await minify(html, { collapseWhitespace: true }) // [!code ++]\n```\n",
  "jsonwebtoken.md": "---\ndescription: Modern alternatives to the jsonwebtoken package for JWT signing and verification\n---\n\n# Replacements for `jsonwebtoken`\n\n## `jose`\n\n[`jose`](https://github.com/panva/jose) implements the full JOSE standard (JWK, JWS, JWE, JWT, JWKS) using the Web Crypto API\n\nCompared to `jsonwebtoken`, it works everywhere globalThis.crypto is available: Node.js 18+, Deno, Bun, Cloudflare Workers, and browsers.\n\n```ts\nimport jwt from 'jsonwebtoken' // [!code --]\nimport { SignJWT, jwtVerify } from 'jose' // [!code ++]\n\nconst secret = 'mysecretkey'\nconst payload = { userId: 123 }\n\nconst token = jwt.sign(payload, secret, { expiresIn: '1h' }) // [!code --]\nconst decoded = jwt.verify(token, secret) // [!code --]\n\nconst joseSecret = new TextEncoder().encode(secret) // [!code ++]\n\nconst token = await new SignJWT(payload) // [!code ++]\n  .setProtectedHeader({ alg: 'HS256' }) // [!code ++]\n  .setExpirationTime('1h') // [!code ++]\n  .sign(joseSecret) // [!code ++]\n\nconst { payload: decoded } = await jwtVerify(token, joseSecret) // [!code ++]\n```\n",
  "globby.md": "---\ndescription: Modern alternatives to the globby package for globbing and .gitignore support\n---\n\n# Replacements for `globby`\n\n## `tinyglobby`\n\n[`globby`](https://github.com/sindresorhus/globby) is a convenience wrapper around [`fast-glob`](https://github.com/mrmlnc/fast-glob).\n\nYou can follow roughly the same migration process as documented in the [`fast-glob`](./fast-glob.md) replacement guide, since `globby` is built on top of it and the main differences are its extra conveniences.\n\nIf you don’t need `.gitignore` handling, prefer [`tinyglobby`](https://github.com/SuperchupuDev/tinyglobby/) - it’s smaller and faster. If you do need `.gitignore` behavior, pair `tinyglobby` with a small git-based helper. For most cases, this will likely be good enough:\n\n```ts\nimport { execSync } from 'node:child_process'\nimport { glob, escapePath } from 'tinyglobby'\n\nasync function globWithGitignore(patterns, options = {}) {\n  const { cwd = process.cwd(), ...restOptions } = options\n\n  try {\n    const gitIgnored = execSync(\n      'git ls-files --others --ignored --exclude-standard --directory',\n      { cwd, encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] }\n    )\n      .split('\\n')\n      .filter(Boolean)\n      .map((p) => escapePath(p))\n\n    return glob(patterns, {\n      ...restOptions,\n      cwd,\n      ignore: [...(restOptions.ignore || []), ...gitIgnored]\n    })\n  } catch {\n    return glob(patterns, options)\n  }\n}\n\nconst paths = await globWithGitignore(['**/*'], { cwd })\n```\n",
  "pkg-dir.md": "---\ndescription: Modern alternatives to the pkg-dir package for finding package root directories\n---\n\n# Replacements for `pkg-dir`\n\n## `empathic`\n\n[`empathic`](https://github.com/lukeed/empathic) provides a more generic way to find files and directories upwards.\n\nThe main difference is that `empathic` is _synchronous_, so you should no longer `await` the result.\n\nExample:\n\n```ts\nimport { dirname } from 'node:path' // [!code ++]\nimport * as pkg from 'empathic/package' // [!code ++]\nimport { packageDirectory } from 'pkg-dir' // [!code --]\n\nconst dir = await packageDirectory() // [!code --]\nconst dir = dirname(pkg.up()) // [!code ++]\n```\n",
  "find-pkg.md": "---\ndescription: Modern alternatives to the find-pkg package for finding package.json files\n---\n\n# Replacements for `find-pkg`\n\n## `empathic`\n\n[`empathic`](https://github.com/lukeed/empathic) provides a more generic way to find files and directories upwards.\n\nThe main difference is that `empathic` is _synchronous_, so you should no longer `await` the result.\n\nExample:\n\n```ts\nimport * as pkg from 'empathic/package' // [!code ++]\nimport findPkg from 'find-pkg' // [!code --]\n\nawait findPkg(path) // [!code --]\npkg.up(path) // [!code ++]\n```\n",
  "shortid.md": "---\ndescription: Modern, secure alternatives to the shortid package for generating URL‑friendly unique IDs\n---\n\n# Replacements for `shortid`\n\n## `nanoid`\n\n[`nanoid`](https://github.com/ai/nanoid) is a tiny, secure, URL‑friendly, unique string ID generator. It’s also faster than [`shortid`](https://github.com/dylang/shortid).\n\n:::info Good to know before migration\n\n- `shortid.isValid(id)`: there’s no direct equivalent. Validate with a regex that matches your chosen alphabet and length, e.g. `/^[A-Za-z0-9_-]{21}$/`.\n\n- `shortid.seed()`/`shortid.worker()`: not needed and not provided by `nanoid` (it uses a secure random source). Avoid seeded/deterministic IDs for security.\n\n:::\n\n### Basic migration\n\n```ts\nimport shortid from 'shortid' // [!code --]\nimport { nanoid } from 'nanoid' // [!code ++]\n\nconst id = shortid.generate() // [!code --]\nconst id = nanoid() // [!code ++] => \"V1StGXR8_Z5jdHi6B-myT\"\n```\n\n### Control length\n\n```ts\n// shortid produced ~7-14 chars; with nanoid you pick the size explicitly:\nnanoid(10) // e.g., \"NG3oYbq9qE\"\n```\n\n### Custom alphabet (replacement for `shortid.characters`)\n\n<!-- eslint-skip -->\n<!-- prettier-ignore -->\n```ts\nshortid.characters('0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ$@') // [!code --]\nimport { customAlphabet } from 'nanoid' // [!code ++]\n\nconst alphabet = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ$@' // [!code ++]\nconst makeId = customAlphabet(alphabet, 12) // [!code ++]\nconst id = makeId() // [!code ++]\n```\n",
  "path-exists.md": "---\ndescription: Modern alternatives to the path-exists package for checking if a path exists\n---\n\n# Replacements for `path-exists`\n\n## Async `fs.access` (native, Node.js)\n\nUse [`fs/promises.access`](https://nodejs.org/docs/latest/api/fs.html#fspromisesaccesspath-mode) and return a boolean.\n\n<!-- prettier-ignore -->\n```ts\nimport pathExists from 'path-exists' // [!code --]\nimport { access } from 'node:fs/promises' // [!code ++]\n\nconst exists = await pathExists('/etc/passwd') // [!code --]\nconst exists = await access('/etc/passwd').then( // [!code ++]\n  () => true, // [!code ++]\n  () => false // [!code ++]\n) // [!code ++]\n```\n\n## Sync `fs.existsSync` (native, Node.js)\n\nAdded in v0.1.21: synchronous path/file existence check via [`fs.existsSync`](https://nodejs.org/docs/latest/api/fs.html#fsexistssyncpath).\n\n```ts\nimport pathExists from 'path-exists' // [!code --]\nimport { existsSync } from 'node:fs' // [!code ++]\n\nconst exists = await pathExists('/etc/passwd') // [!code --]\nconst exists = existsSync('/etc/passwd') // [!code ++]\n```\n\n## Bun\n\n[`Bun.file()`](https://bun.sh/reference/bun/BunFile) returns a BunFile with an `.exists()` method.\n\n```ts\nimport pathExists from 'path-exists' // [!code --]\n\nconst path = '/path/to/package.json'\nconst exists = await pathExists(path) // [!code --]\nconst file = Bun.file(path) // [!code ++]\nconst exists = await file.exists() // boolean [!code ++]\n```\n",
  "parseargs.md": "---\ndescription: Modern alternatives to CLI argument parsing packages using Node.js built-in util.parseArgs\n---\n\n# Replacements for argument parsers\n\n## `util.parseArgs` (native, since Node.js 16.x)\n\n[`util.parseArgs`](https://nodejs.org/api/util.html#utilparseargsconfig) is built into Node.js (since 18.3.0 and 16.17.0) and can replace many common CLI options parsing libraries.\n\nExample:\n\n```ts\nimport { parseArgs } from 'node:util'\n\nconst { values, positionals } = parseArgs({\n  args: process.argv.slice(2),\n  options: {\n    force: { type: 'boolean', short: 'f' },\n    output: { type: 'string', short: 'o' }\n  },\n  allowPositionals: true\n})\n```\n\n> [!NOTE]\n> `parseArgs` only supports `string` and `boolean` types. If you'd like to support stronger types, one of the other options may be a better fit.\n\n## `mri`\n\n[`mri`](https://github.com/lukeed/mri) is a minimalistic argument parser that supports both short and long options, as well as positional arguments.\n\nExample:\n\n```ts\nimport mri from 'mri'\n\nconst options = mri(process.argv.slice(2), {\n  alias: {\n    f: 'force',\n    o: 'output'\n  },\n  boolean: ['force']\n})\n```\n\n## CLI builders\n\nIf you need a CLI builder, check the the [CLI builders page](https://e18e.dev/docs/replacements/cli-builders).\n",
  "readable-stream.md": "---\ndescription: Modern alternatives to the readable-stream package for working with streaming data in Node.js\n---\n\n# Replacements for `readable-stream`\n\n[`readable-stream`](https://github.com/nodejs/readable-stream) mirrors Node’s core streams and works in browsers. In most cases, prefer native options.\n\n## `node:stream` (native, since Node.js v0.9.4)\n\nUse the built-in `stream` module ([Node Streams docs](https://nodejs.org/api/stream.html)).\n\n```ts\nimport { Duplex, Readable, Transform, Writable } from 'readable-stream' // [!code --]\nimport { Duplex, Readable, Transform, Writable } from 'node:stream' // [!code ++]\n```\n\n## Web Streams (native, browsers and Node.js 16.5.0+)\n\nUse the [Web Streams API (MDN)](https://developer.mozilla.org/en-US/docs/Web/API/Streams_API) in browsers and modern Node. It’s global in Node 18+ ([Node Web Streams docs](https://nodejs.org/api/webstreams.html)); on 16.5–17.x import from `stream/web` ([details](https://nodejs.org/api/webstreams.html#streamweb-the-web-streams-api)). Interop with Node streams is available via [Readable.toWeb](https://nodejs.org/api/stream.html#streamreadabletowebstreamreadable-options) and [Writable.fromWeb](https://nodejs.org/api/stream.html#streamwritablefromwebwritablestream-options).\n\nExample: convert a Web ReadableStream (from fetch) to a Node stream and pipe it to a file.\n\n```ts\nimport { Readable } from 'node:stream'\nimport { pipeline } from 'node:stream/promises'\nimport { createWriteStream } from 'node:fs'\n\nconst res = await fetch('https://example.com/data.txt') // Web ReadableStream\nconst nodeReadable = Readable.fromWeb(res.body)\nawait pipeline(nodeReadable, createWriteStream('data.txt'))\n```\n",
  "portfinder.md": "---\ndescription: Modern alternatives to the portfinder package for finding an available port\n---\n\n# Replacements for `portfinder`\n\n## `get-port`\n\n[`get-port`](https://github.com/sindresorhus/get-port) is a lighter and more modern package for finding an available port.\n\nExample:\n\n```ts\nimport portfinder from 'portfinder' // [!code --]\nimport getPort from 'get-port' // [!code ++]\n\nconst port = await portfinder.getPortPromise() // [!code --]\nconst port = await getPort() // [!code ++]\n```\n",
  "read-package-up.md": "---\ndescription: Modern alternatives to the read-package-up package for reading package.json files up the directory tree\n---\n\n# Replacements for `read-package-up`\n\n## `pkg-types`\n\n[`pkg-types`](https://github.com/unjs/pkg-types) provides a similar API and strong types.\n\nFor example:\n\n```ts\nimport { readPackageJSON } from 'pkg-types' // [!code ++]\nimport { readPackageUp } from 'read-package-up' // [!code --]\n\nconst packageJson = await readPackageUp() // [!code --]\nconst packageJson = await readPackageJSON() // [!code ++]\n```\n\nSimilarly, you can get hold of the path via `resolvePackageJSON`:\n\n```ts\nimport { resolvePackageJSON } from 'pkg-types'\n\nconst packageJsonPath = await resolvePackageJSON()\n```\n\n## `empathic`\n\n[`empathic`](https://github.com/lukeed/empathic) provides a more generic way to find files and directories upwards.\n\nIt can be combined with `node:fs` to read `package.json` files:\n\n```ts\nimport fs from 'node:fs/promises' // [!code ++]\nimport * as pkg from 'empathic' // [!code ++]\nimport { readPackageUp } from 'read-package-up' // [!code --]\n\nconst packageJson = await readPackageUp() // [!code --]\nconst packageJsonPath = pkg.up() // [!code ++]\nconst packageJson = packageJsonPath // [!code ++]\n  ? JSON.parse(await readFile(packageJsonPath, 'utf8')) // [!code ++]\n  : undefined // [!code ++]\n```\n\n> [!NOTE]\n> This is of course a more manual way to read the `package.json` file, so one of the other options may be more attractive.\n",
  "tsc.md": "---\ndescription: Modern alternatives to the tsc package for the TypeScript compiler\n---\n\n# Replacements for `tsc`\n\nThe npm package [`tsc`](https://github.com/basarat/tsc) is **not** the TypeScript compiler — it is a deprecated stub that prints _\"This is not the tsc command you are looking for\"_ when run.\n\n## `typescript`\n\nThe [`typescript`](https://github.com/microsoft/TypeScript) package is the official compiler and provides the real `tsc` binary via `node_modules/.bin`. Replace `tsc` in your dependencies:\n\n```json\n{\n  \"devDependencies\": {\n    \"tsc\": \"^2.0.4\", // [!code --]\n    \"typescript\": \"^5.9.0\" // [!code ++]\n  }\n}\n```\n\nExisting `\"build\": \"tsc\"` scripts work unchanged once `typescript` is installed. For `npx` or global installs, use `typescript` — not `tsc`:\n\n```bash\nnpx tsc --init # [!code --]\nnpx --package typescript tsc --init # [!code ++]\n\nnpm install -g tsc # [!code --]\nnpm install -g typescript # [!code ++]\n```\n",
  "mockdate.md": "---\ndescription: Modern alternatives to the mockdate package for mocking time in tests\n---\n\n# Replacements for `mockdate`\n\n`mockdate` is mainly used in tests, and modern test runners already include built-in APIs for mocking time without pulling in an extra dependency.\n\n## `vitest`\n\n[`vitest`](https://vitest.dev/guide/mocking.html#mock-the-current-date) provides `vi.useFakeTimers()` and `vi.setSystemTime()` for mocking the current date during tests.\n\n```ts\nimport MockDate from 'mockdate' // [!code --]\nimport { vi, test, expect } from 'vitest' // [!code ++]\n\ntest('freeze date', () => {\n  MockDate.set('2026-01-01') // [!code --]\n  vi.useFakeTimers() // [!code ++]\n  vi.setSystemTime(new Date('2026-01-01')) // [!code ++]\n\n  expect(new Date().toISOString()).toBe('2026-01-01T00:00:00.000Z')\n\n  MockDate.reset() // [!code --]\n  vi.useRealTimers() // [!code ++]\n})\n```\n\n## `node:test`\n\n[`node:test`](https://nodejs.org/en/learn/test-runner/mocking#time) supports mocking time via `mock.timers` since node 20.4.0 and later.\n\n```ts\nimport MockDate from 'mockdate' // [!code --]\nimport { test } from 'node:test' // [!code ++]\nimport assert from 'node:assert/strict' // [!code ++]\n\ntest('freeze date', (t) => {\n  MockDate.set('2026-01-01') // [!code --]\n  t.mock.timers.enable({ apis: ['Date'], now: new Date('2026-01-01') }) // [!code ++]\n\n  assert.equal(new Date().toISOString(), '2026-01-01T00:00:00.000Z')\n\n  MockDate.reset() // [!code --]\n})\n```\n\n## `bun:test`\n\n[`bun:test`](https://bun.com/docs/guides/test/mock-clock) provides `mock.timers.enable()` for mocking time in tests.\n\n```ts\nimport MockDate from 'mockdate' // [!code --]\nimport { test, expect, mock } from 'bun:test' // [!code ++]\n\ntest('freeze date', () => {\n  MockDate.set('2026-01-01') // [!code --]\n  mock.timers.enable({ now: new Date('2026-01-01') }) // [!code ++]\n\n  expect(new Date().toISOString()).toBe('2026-01-01T00:00:00.000Z')\n\n  MockDate.reset() // [!code --]\n  mock.timers.reset() // [!code ++]\n})\n```\n",
  "resolve.md": "---\ndescription: Modern alternatives to the resolve package\n---\n\n# Replacements for `resolve`\n\n## `import.meta.resolve` (native)\n\n[`import.meta.resolve`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/import.meta/resolve) is built into browsers and Node.js (>= 20).\n\nExample:\n\n```ts\nconst path = import.meta.resolve('my-module')\n```\n\n## `exsolve`\n\n[`exsolve`](https://github.com/unjs/exsolve)\n\n```ts\nimport { resolveModulePath } from 'exsolve'\n\nconst path = resolveModulePath('my-module')\n```\n\n## `oxc-resolver`\n\n[`oxc-resolver`](https://github.com/oxc-project/oxc-resolver)\n\n```ts\nimport { ResolverFactory } from 'oxc-resolver'\n\nconst resolver = new ResolverFactory()\nconst { path } = resolver.sync(process.cwd(), 'my-module')\n```\n",
  "source-map-explorer.md": "---\ndescription: Modern alternatives to the source-map-explorer package for exploring bundle sourcemaps\n---\n\n# Replacements for `source-map-explorer`\n\n## `rollup-plugin-visualizer`\n\n[`rollup-plugin-visualizer`](https://github.com/btd/rollup-plugin-visualizer) allows you to visualize rollup, rolldown, and vite bundles\n\n### Rollup (`rollup.config.js`)\n\n```js\nimport { visualizer } from 'rollup-plugin-visualizer'\n\nexport default {\n  plugins: [\n    // Keep it last.\n    visualizer()\n  ]\n}\n```\n\n### Rolldown (`rolldown.config.ts`)\n\n```js\nimport { defineConfig, type RolldownPlugin } from 'rolldown'\nimport { visualizer } from 'rollup-plugin-visualizer'\n\nexport default defineConfig({\n  plugins: [visualizer() as RolldownPlugin]\n})\n```\n\n### Vite (`vite.config.js`)\n\n```js\nimport { visualizer } from 'rollup-plugin-visualizer'\n\nexport default {\n  plugins: [visualizer()]\n}\n```\n\n### Vite + TypeScript (`vite.config.ts`)\n\n```ts\nimport { defineConfig, type PluginOption } from 'vite'\nimport { visualizer } from 'rollup-plugin-visualizer'\n\nexport default defineConfig({\n  plugins: [visualizer() as PluginOption]\n})\n```\n\n## `sonda`\n\n[`sonda`](https://github.com/filipsobol/sonda) allows you to visualize vite, rollup, rolldown, esbuild, webpack, and rspack bundles\n\nExample:\n\n```js\nimport { defineConfig } from 'vite'\nimport Sonda from 'sonda/vite'\n\nexport default defineConfig({\n  build: {\n    sourcemap: true\n  },\n  plugins: [Sonda()]\n})\n```\n\nFor full docs check their [website](https://sonda.dev/getting-started).\n\n## `webpack-bundle-analyzer`\n\n[`webpack-bundle-analyzer`](https://github.com/webpack/webpack-bundle-analyzer) allows you to visualize webpack bundles\n\nExample:\n\n```js\nimport { BundleAnalyzerPlugin } from 'webpack-bundle-analyzer'\n\nmodule.exports = {\n  plugins: [new BundleAnalyzerPlugin()]\n}\n```\n",
  "fast-glob.md": "---\ndescription: Modern alternatives to the fast-glob package for fast file system pattern matching\n---\n\n# Replacements for `fast-glob`\n\n## `tinyglobby`\n\n[`tinyglobby`](https://github.com/SuperchupuDev/tinyglobby) is a modern, lightweight alternative that provides similar functionality with better performance.\n\nExample:\n\n<!-- eslint-skip -->\n\n```ts\nimport fg from 'fast-glob' // [!code --]\nimport { glob } from 'tinyglobby' // [!code ++]\n\nconst files = await fg('**/*.ts', { // [!code --]\nconst files = await glob('**/*.ts', { // [!code ++]\n  cwd: process.cwd(),\n  ignore: ['**/node_modules/**'],\n  expandDirectories: false // [!code ++]\n})\n```\n\nMost options from `fast-glob` have direct equivalents in `tinyglobby`. Check the [tinyglobby documentation](https://superchupu.dev/tinyglobby/migration) for the complete list of supported options.\n\n## `fs.glob` (native, since Node 22.x)\n\n[`fs.glob`](https://nodejs.org/api/fs.html#fspromisesglobpattern-options) is built into modern versions of Node.\n\nExample:\n\n<!-- eslint-skip -->\n\n```ts\nimport fg from 'fast-glob' // [!code --]\nimport { glob } from 'node:fs/promises' // [!code ++]\n\nconst files = await fg('src/**/*.ts', { // [!code --]\nconst files = await Array.fromAsync(glob('src/**/*.ts', { // [!code ++]\n  cwd,\n}) // [!code --]\n})) // [!code ++]\n```\n\nYou can also iterate over the results asynchronously:\n\n```ts\nfor await (const result of glob('src/**/*.ts', { cwd })) {\n  // result is an individual path\n  console.log(result)\n}\n```\n\n> [!NOTE]\n> Node's built-in `glob` is more minimal and does not support negation patterns or fine-grained options like setting a max depth out of the box.\n",
  "slice-ansi.md": "---\ndescription: Modern alternatives for the slice-ansi package for slicing terminal strings\n---\n\n# Replacements for `slice-ansi`\n\n## `fast-slice-ansi`\n\n[`fast-slice-ansi`](https://github.com/43081j/fast-slice-ansi) is a faster and smaller alternative to `slice-ansi` for slicing strings with ANSI escape codes.\n\n```ts\nimport sliceAnsi from 'slice-ansi' // [!code --]\nimport { sliceAnsi } from 'fast-slice-ansi' // [!code ++]\nimport { styleText } from 'node:util'\n\nconst value = `The quick ${styleText('red', 'brown fox')} jumped over the lazy dog`\n\nconsole.log(sliceAnsi(value, 10, 25))\n```\n",
  "read-pkg.md": "---\ndescription: Native Node.js alternatives to the read-pkg package for reading package.json files\n---\n\n# Replacements for `read-pkg`\n\n## `pkg-types`\n\n[`pkg-types`](https://github.com/unjs/pkg-types) provides a similar API and strong types.\n\nFor example:\n\n```ts\nimport { readPackageJSON } from 'pkg-types' // [!code ++]\nimport { readPackage } from 'read-pkg' // [!code --]\n\nconst packageJson = await readPackage() // [!code --]\nconst packageJson = await readPackageJSON() // [!code ++]\n```\n\nYou may also specify a `cwd`:\n\n```ts\nimport { readPackageJSON } from 'pkg-types'\n\nconst packageJson = await readPackageJson({ cwd })\n```\n\n## Native `node:fs`\n\nYou can use `node:fs` to read a known `package.json`:\n\n```ts\nimport fs from 'node:fs/promises' // [!code ++]\nimport { readPackage } from 'read-pkg' // [!code --]\n\nconst packageJson = await readPackageUp() // [!code --]\nconst packageJson = JSON.parse(await readFile('./package.json', 'utf8')) // [!code ++]\n```\n\n> [!NOTE]\n> Using this approach, you will have to handle errors yourself (e.g. failure to read the file).\n",
  "split.md": "---\ndescription: Native Node.js alternatives to the split package for splitting stream by lines\n---\n\n# Replacements for `split`\n\n## `readline.createInterface` (native, since Node.js v6.6.0)\n\nThe [`readline.createInterface`](https://nodejs.org/api/readline.html#readlinecreateinterfaceoptions)\nmethod can be used to read a stream line by line, effectively replacing the functionality of the `split` package.\n\nExample:\n\n<!-- prettier-ignore -->\n```js\nimport split from 'split' // [!code --]\nimport { createInterface } from 'node:readline' // [!code ++]\nimport * as fs from 'node:fs'\n\nconst input = fs.createReadStream('file.txt')\n\nconst stream = input.pipe(split()) // [!code --]\nstream.on('data', (line) => { // [!code --]\n  fn(line) // [!code --]\n}) // [!code --]\n\nconst lines = createInterface({ input, crlfDelay: Infinity }) // [!code ++]\n\nfor await (const line of lines) { // [!code ++]\n  fn(line) // [!code ++]\n} // [!code ++]\n```\n",
  "micromatch.md": "---\ndescription: Modern alternatives to the micromatch package for glob pattern matching\n---\n\n# Replacements for `micromatch`\n\n## `path.matchesGlob` (native, since Node 20.17 / 22.5)\n\n[`path.matchesGlob`](https://nodejs.org/api/path.html#pathmatchesglobpath-pattern-options) is built into modern versions of Node. Use it when you only need to test whether a path matches a single glob pattern.\n\nExample:\n\n```ts\nimport micromatch from 'micromatch' // [!code --]\nimport path from 'node:path' // [!code ++]\n\nmicromatch.isMatch('foo.bar', '*.bar') // [!code --]\npath.matchesGlob('foo.bar', '*.bar') // [!code ++]\n```\n\nFor multiple patterns, test each pattern explicitly:\n\n```ts\nconst patterns = ['*.js', '*.ts']\nconst matches = patterns.some((pattern) => path.matchesGlob(file, pattern))\n```\n\n## `picomatch`\n\n[`picomatch`](https://github.com/micromatch/picomatch) is the matcher micromatch is built on. It is a strong drop-in for most `isMatch` and list-filtering use cases.\n\nExample:\n\n```ts\nimport micromatch from 'micromatch' // [!code --]\nimport picomatch from 'picomatch' // [!code ++]\n\nmicromatch.isMatch('foo.bar', '*.bar') // [!code --]\npicomatch.isMatch('foo.bar', '*.bar') // [!code ++]\n```\n\nFor a reusable matcher function:\n\n```ts\nconst isMatch = picomatch('*.js')\nisMatch('a.js') // true\n```\n\n## `zeptomatch`\n\n[`zeptomatch`](https://github.com/fabiospampinato/zeptomatch) is a tiny glob matcher with brace expansion, ranges, and negation. Note that its argument order is **glob first, path second**.\n\nExample:\n\n```ts\nimport micromatch from 'micromatch' // [!code --]\nimport zeptomatch from 'zeptomatch' // [!code ++]\n\nmicromatch.isMatch('foo.bar', '*.bar') // [!code --]\nzeptomatch('*.bar', 'foo.bar') // [!code ++]\n```\n\nZeptomatch also supports partial matching for filesystem walks and brace expansion:\n\n```ts\nzeptomatch('foo/bar/*.js', 'foo', { partial: true }) // true\nzeptomatch('{a,b}.js', 'a.js') // true\nzeptomatch('!*.test.js', 'foo.js') // true\n```\n",
  "tokml.md": "---\ndescription: Modern replacements for the tokml package for converting GeoJSON to KML\n---\n\n# Replacements for `tokml`\n\n## `@placemarkio/tokml`\n\n[`@placemarkio/tokml`](https://github.com/placemark/tokml) is a maintained alternative for `tokml` package.\n\nExample:\n\n```js\nimport tokml from 'tokml' // [!code --]\nimport { toKML } from '@placemarkio/tokml' // [!code ++]\n\nconst geojson = {\n  type: 'FeatureCollection',\n  features: [\n    {\n      type: 'Feature',\n      geometry: { type: 'Point', coordinates: [1, 2] },\n      properties: { name: 'Marker' }\n    }\n  ]\n}\n\nconst kml = tokml(geojson) // [!code --]\nconst kml = toKML(geojson) // [!code ++]\n```\n",
  "schema-validation.md": "---\ndescription: Modern alternatives for TypeScript schema validation\n---\n\n# TypeScript schema validation (shared)\n\nThis page contains the common, recommended alternatives and examples for TypeScript schema validation libraries.\n\nSee [Schema Benchmarks](https://schemabenchmarks.dev/download) to explore other schema validation libraries and compare their bundle sizes and runtime behavior.\n\nWhen migrating between schema libraries, verify optionality, empty-string handling, type coercion, unknown object keys, error messages, and synchronous versus asynchronous validation.\n\n## `valibot`\n\n[`valibot`](https://valibot.dev/) is a modular schema validation library with a functional, tree-shakable API.\n\nExample:\n\n```ts\nimport * as v from 'valibot'\n\nconst personSchema = v.object({\n  name: v.pipe(v.string(), v.nonEmpty()),\n  age: v.pipe(v.number(), v.minValue(0))\n})\n\nconst person = v.parse(personSchema, input)\n```\n\nValibot schemas are required by default; wrap optional fields with `v.optional(...)`. Use `v.nonEmpty()` when empty strings are invalid, and compose further checks with `v.pipe(...)`. `v.parse` is synchronous; use `v.parseAsync` for asynchronous validation.\n\nValibot makes transformations explicit (for example, `v.toNumber()`), and `v.object` removes unknown keys. Use `v.looseObject` when those keys need to be preserved.\n\n## `zod/mini`\n\n[`zod/mini`](https://zod.dev/packages/mini) is the tree-shakable, functional API included with Zod 4. Install `zod@^4.0.0` and import the `zod/mini` subpath.\n\nExample:\n\n```ts\nimport * as z from 'zod/mini'\n\nconst personSchema = z.object({\n  name: z.string().check(z.minLength(1)),\n  age: z.number().check(z.gte(0))\n})\n\nconst person = personSchema.parse(input)\n```\n\nZod Mini schemas are required by default; wrap optional fields with `z.optional(...)`. Its functional checks are passed to the schema with `.check(...)`, while `parse`, `safeParse`, `parseAsync`, and `safeParseAsync` remain schema methods. As with Valibot, `z.object` strips unknown keys by default; use `z.looseObject` to preserve them.\n\nZod Mini does not load a default error-message locale. Configure one explicitly, for example with `z.config(z.locales.en())`, when callers depend on English error messages.\n",
  "react-helmet.md": "---\ndescription: Modern alternatives to the react-helmet package\n---\n\n# Replacements for `react-helmet`\n\n## Support for Document Metadata (native, since React 19)\n\n[Support for Document Metadata](https://react.dev/blog/2024/12/05/react-19#support-for-metadata-tags) is available since React 19\n\nExample:\n\n```jsx\nfunction BlogPost({ post }) {\n  return (\n    <article>\n      <h1>{post.title}</h1>\n      <title>{post.title}</title>\n      <meta name=\"keywords\" content={post.keywords} />\n      <p>Eee equals em-see-squared...</p>\n    </article>\n  )\n}\n```\n\n## `react-helmet-async`\n\n[`react-helmet-async`](https://github.com/staylor/react-helmet-async) is a fork of `react-helmet`.\n\nExample:\n\n<!-- prettier-ignore -->\n```jsx\nimport { Helmet } from 'react-helmet' // [!code --]\nimport { Helmet, HelmetProvider } from 'react-helmet-async' // [!code ++]\n\nconst app = (\n  <HelmetProvider> // [!code ++]\n    <App>\n      <Helmet>\n        <title>Hello World</title>\n        <link rel=\"canonical\" href=\"https://e18e.dev/\" />\n      </Helmet>\n      <h1>Hello World</h1>\n    </App>\n  </HelmetProvider> // [!code ++]\n)\n```\n",
  "portal-vue.md": "---\ndescription: Modern alternatives to the `portal-vue` package for making portals in Vue applications\n---\n\n# Replacements for `portal-vue`\n\n## Vue `Teleport` API\n\nSince Vue 3, the [Teleport](https://vuejs.org/guide/built-ins/teleport.html) component has been introduced which replaces portal-vue for most use cases, especially modals and overlays.\n\n`<Teleport>` only moves DOM nodes to an existing target — it does not manage destinations, layouts, or component structure.\n\n```html\n<!-- Using a modal -->\n<div class=\"outer\">\n  <h3>Vue Teleport Example</h3>\n  <div>\n    <MyModal />\n  </div>\n</div>\n```\n\n```html\n<!-- MyModal.vue -->\n<Teleport to=\"body\">\n  <p>The content inside of Teleport will render in html body</p>\n</Teleport>\n```\n",
  "tweetnacl.md": "---\ndescription: Native Web Crypto alternatives to tweetnacl for signatures, key exchange, hashing, and random bytes\n---\n\n# Replacements for `tweetnacl`\n\nThe [Web Crypto API](https://developer.mozilla.org/en-US/docs/Web/API/Web_Crypto_API) natively supports most of the primitives implemented by `tweetnacl`: Ed25519 signatures (`nacl.sign.detached`), X25519 key exchange (`nacl.scalarMult`), SHA-512 hashing (`nacl.hash`), and random bytes (`nacl.randomBytes`).\n\nTwo differences to keep in mind when migrating:\n\n- Web Crypto is asynchronous (`await`), while `tweetnacl` is synchronous.\n- Ed25519 and X25519 support in Web Crypto is a recent addition across browsers, so older engines may need a fallback.\n\n## Signatures (Ed25519)\n\n`nacl.sign.detached` and `nacl.sign.detached.verify` map to `crypto.subtle.sign` and `crypto.subtle.verify` with the `Ed25519` algorithm. Raw public keys can be imported directly with `crypto.subtle.importKey`.\n\nVerifying a signature:\n\n<!-- prettier-ignore -->\n```ts\nimport nacl from 'tweetnacl' // [!code --]\n\nconst isVerified = nacl.sign.detached.verify(message, sig, pubKey) // [!code --]\nconst publicKey = await crypto.subtle.importKey( // [!code ++]\n  'raw', // [!code ++]\n  pubKey, // [!code ++]\n  { name: 'Ed25519' }, // [!code ++]\n  false, // [!code ++]\n  ['verify'] // [!code ++]\n) // [!code ++]\nconst isVerified = await crypto.subtle.verify( // [!code ++]\n  { name: 'Ed25519' }, // [!code ++]\n  publicKey, // [!code ++]\n  sig, // [!code ++]\n  message // [!code ++]\n) // [!code ++]\n```\n\nSigning a message:\n\n<!-- prettier-ignore -->\n```ts\nimport nacl from 'tweetnacl' // [!code --]\n\nconst keyPair = nacl.sign.keyPair() // [!code --]\nconst sig = nacl.sign.detached(message, keyPair.secretKey) // [!code --]\nconst keyPair = await crypto.subtle.generateKey( // [!code ++]\n  { name: 'Ed25519' }, // [!code ++]\n  false, // [!code ++]\n  ['sign', 'verify'] // [!code ++]\n) // [!code ++]\nconst sig = new Uint8Array( // [!code ++]\n  await crypto.subtle.sign({ name: 'Ed25519' }, keyPair.privateKey, message) // [!code ++]\n) // [!code ++]\n```\n\nNote that `nacl.sign` (which prepends the signature to the message) has no direct equivalent; use detached signatures and concatenate manually if needed.\n\n## Key exchange (X25519)\n\n`nacl.scalarMult` maps to `crypto.subtle.deriveBits` with the `X25519` algorithm:\n\n<!-- prettier-ignore -->\n```ts\nimport nacl from 'tweetnacl' // [!code --]\n\nconst sharedSecret = nacl.scalarMult(mySecretKey, theirPublicKey) // [!code --]\nconst publicKey = await crypto.subtle.importKey( // [!code ++]\n  'raw', // [!code ++]\n  theirPublicKey, // [!code ++]\n  { name: 'X25519' }, // [!code ++]\n  false, // [!code ++]\n  [] // [!code ++]\n) // [!code ++]\nconst sharedSecret = new Uint8Array( // [!code ++]\n  await crypto.subtle.deriveBits( // [!code ++]\n    { name: 'X25519', public: publicKey }, // [!code ++]\n    myPrivateKey, // [!code ++]\n    256 // [!code ++]\n  ) // [!code ++]\n) // [!code ++]\n```\n\n## Hashing (SHA-512)\n\n`nacl.hash` maps to `crypto.subtle.digest`:\n\n```ts\nimport nacl from 'tweetnacl' // [!code --]\n\nconst hash = nacl.hash(data) // [!code --]\nconst hash = new Uint8Array(await crypto.subtle.digest('SHA-512', data)) // [!code ++]\n```\n\n## Random bytes\n\n`nacl.randomBytes` maps to `crypto.getRandomValues`:\n\n```ts\nimport nacl from 'tweetnacl' // [!code --]\n\nconst bytes = nacl.randomBytes(32) // [!code --]\nconst bytes = crypto.getRandomValues(new Uint8Array(32)) // [!code ++]\n```\n\n## Not covered: `nacl.secretbox` and `nacl.box`\n\nXSalsa20-Poly1305 authenticated encryption (`nacl.secretbox`, `nacl.box`) has no Web Crypto equivalent. Code using these primitives cannot migrate to native APIs and should keep using `tweetnacl` or a maintained alternative implementing the same construction.\n",
  "timezone-support.md": "---\ndescription: Native alternatives to the timezone-support package for listing, resolving and converting time zones\n---\n\n# Replacements for `timezone-support`\n\n`timezone-support` ships its own copy of the IANA time zone database and helpers to convert between zones. Modern runtimes already ship this data: `Intl.supportedValuesOf` lists the available zones, and [`Temporal`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal) covers the conversion helpers.\n\n## `Intl.supportedValuesOf`\n\n[`Intl.supportedValuesOf`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/supportedValuesOf) replaces `listTimeZones`:\n\n```ts\nimport { listTimeZones } from 'timezone-support' // [!code --]\n\nconst timeZones = listTimeZones() // [!code --]\nconst timeZones = Intl.supportedValuesOf('timeZone') // [!code ++]\n```\n\n## `Temporal`\n\n`findTimeZone` is no longer needed — pass the IANA identifier directly.\n\nGetting the wall-clock time in a zone (`getZonedTime`, `convertDateToTime`):\n\n```ts\nimport { findTimeZone, getZonedTime } from 'timezone-support' // [!code --]\n\nconst date = new Date()\nconst berlin = findTimeZone('Europe/Berlin') // [!code --]\nconst time = getZonedTime(date, berlin) // [!code --]\nconst time = date.toTemporalInstant().toZonedDateTimeISO('Europe/Berlin') // [!code ++]\n```\n\nGetting the UTC offset (`getUTCOffset`):\n\n```ts\nimport { findTimeZone, getUTCOffset } from 'timezone-support' // [!code --]\n\nconst berlin = findTimeZone('Europe/Berlin') // [!code --]\nconst { offset } = getUTCOffset(new Date(), berlin) // [!code --]\nconst { offset } = Temporal.Now.zonedDateTimeISO('Europe/Berlin') // [!code ++]\n```\n\nNote that `Temporal` returns the offset as a string (`'+02:00'`) or via `offsetNanoseconds`, rather than minutes.\n\nGetting a timestamp or `Date` from a zoned time (`getUnixTime`, `convertTimeToDate`):\n\n<!-- prettier-ignore -->\n```ts\nimport { findTimeZone, getUnixTime } from 'timezone-support' // [!code --]\n\nconst berlin = findTimeZone('Europe/Berlin') // [!code --]\nconst time = { year: 2018, month: 9, day: 2, hours: 10, minutes: 0 } // [!code --]\nconst epoch = getUnixTime(time, berlin) // [!code --]\nconst epoch = Temporal.ZonedDateTime.from({ // [!code ++]\n  timeZone: 'Europe/Berlin', // [!code ++]\n  year: 2018, month: 9, day: 2, hour: 10, minute: 0 // [!code ++]\n}).epochMilliseconds // [!code ++]\n\nconst date = new Date(epoch)\n```\n\nAttaching a zone to a wall-clock time (`setTimeZone`):\n\n<!-- prettier-ignore -->\n```ts\nimport { findTimeZone, setTimeZone } from 'timezone-support' // [!code --]\n\nconst berlin = findTimeZone('Europe/Berlin') // [!code --]\nconst time = { year: 2018, month: 9, day: 2, hours: 10, minutes: 0 } // [!code --]\nconst berlinTime = setTimeZone(time, berlin) // [!code --]\nconst berlinTime = Temporal.PlainDateTime // [!code ++]\n  .from({ year: 2018, month: 9, day: 2, hour: 10, minute: 0 }) // [!code ++]\n  .toZonedDateTime('Europe/Berlin') // [!code ++]\n```\n\nParsing and formatting (`parseZonedTime`, `formatZonedTime`) — `Temporal` parses ISO 8601 strings and `toLocaleString` (backed by `Intl.DateTimeFormat`) handles output; custom format strings such as `'D.M.YYYY H:mm zZ'` are not supported:\n\n<!-- prettier-ignore -->\n```ts\nimport { parseZonedTime, formatZonedTime } from 'timezone-support/parse-format' // [!code --]\n\nconst time = parseZonedTime('2.9.2018 10:00 CEST+02:00', 'D.M.YYYY H:mm zZ') // [!code --]\nconst time = Temporal.ZonedDateTime.from('2018-09-02T10:00:00+02:00[Europe/Berlin]') // [!code ++]\n\nconst output = formatZonedTime(time, 'D.M.YYYY H:mm zZ') // [!code --]\nconst output = time.toLocaleString('de-DE', { dateStyle: 'medium', timeStyle: 'short' }) // [!code ++]\n```\n",
  "mkdirp.md": "---\ndescription: Modern alternatives to the mkdirp and make-dir packages for recursively creating directories in Node.js\n---\n\n# Replacements for `mkdirp` / `make-dir`\n\n## Recursive `fs.mkdir` (native, since Node.js v10.12.0)\n\nNode.js v10.12.0 and up supports the `recursive` option in the [`fs.mkdir`](https://nodejs.org/api/fs.html#fsmkdirpath-options-callback) function, which allows parent directories to be created automatically.\n\nExample migration from [`mkdirp`](https://github.com/isaacs/node-mkdirp):\n\n```ts\nimport { mkdirp } from 'mkdirp' // [!code --]\nimport { mkdir, mkdirSync } from 'node:fs' // [!code ++]\nimport { mkdir as mkdirAsync } from 'node:fs/promises' // [!code ++]\n\n// Async\nawait mkdirp('/tmp/foo/bar/baz') // [!code --]\nawait mkdirAsync('/tmp/foo/bar/baz', { recursive: true }) // [!code ++]\n\n// Sync\nmkdirp.sync('/tmp/foo/bar/baz') // [!code --]\nmkdirSync('/tmp/foo/bar/baz', { recursive: true }) // [!code ++]\n```\n\nExample migration from [`make-dir`](https://github.com/sindresorhus/make-dir):\n\n```ts\nimport { makeDirectory, makeDirectorySync } from 'make-dir' // [!code --]\nimport { mkdir, mkdirSync } from 'node:fs' // [!code ++]\nimport { mkdir as mkdirAsync } from 'node:fs/promises' // [!code ++]\n\n// Async\nawait makeDirectory('/tmp/foo/bar/baz') // [!code --]\nawait mkdirAsync('/tmp/foo/bar/baz', { recursive: true }) // [!code ++]\n\n// Sync\nmakeDirectorySync('/tmp/foo/bar/baz') // [!code --]\nmkdirSync('/tmp/foo/bar/baz', { recursive: true }) // [!code ++]\n```\n",
  "traverse.md": "---\ndescription: Modern alternative to the traverse package to traverse and transform objects by visiting every node on a recursive walk\n---\n\n# Replacements for `traverse`\n\n## `neotraverse`\n\n[`neotraverse`](https://github.com/puruvj/neotraverse) is a TypeScript rewrite of [`traverse`](https://github.com/ljharb/js-traverse) with no dependencies. It offers a drop‑in compatible build as well as a modern API.\n\n```ts\nimport traverse from 'traverse' // [!code --]\nimport traverse from 'neotraverse' // [!code ++]\n\nconst obj = [5, 6, -3, [7, 8, -2, 1], { f: 10, g: -13 }]\n\ntraverse(obj).forEach(function (x) {\n  if (x < 0) this.update(x + 128)\n})\n\nconsole.log(obj)\n```\n",
  "jsx-ast-utils.md": "---\ndescription: Modern alternatives to the jsx-ast-utils package for statically analyzing JSX ASTs\n---\n\n# Replacements for `jsx-ast-utils`\n\n## `jsx-ast-utils-x`\n\n[`jsx-ast-utils-x`](https://github.com/eslinter/jsx-ast-utils-x) is a zero‑dependency alternative to [`jsx-ast-utils`](https://github.com/jsx-eslint/jsx-ast-utils) that aims to maintain API compatibility while reducing package size.\n\n```ts\nimport { hasProp } from 'jsx-ast-utils' // [!code --]\nimport { hasProp } from 'jsx-ast-utils-x' // [!code ++]\n\nimport hasProp from 'jsx-ast-utils/hasProp' // [!code --]\nimport hasProp from 'jsx-ast-utils-x/hasProp' // [!code ++]\n\nmodule.exports = (context) => ({\n  JSXOpeningElement: (node) => {\n    const onChange = hasProp(node.attributes, 'onChange')\n    if (onChange) {\n      context.report({ node, message: 'No onChange!' })\n    }\n  }\n})\n```\n",
  "pbkdf2.md": "---\ndescription: Native alternatives to the pbkdf2 package for secure, iterative password-based key derivation\n---\n\n# Replacements for `pbkdf2`\n\n## `crypto.subtle.deriveBits` (native)\n\nFrom [`MDN documentation`](https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/deriveBits#pbkdf2):\n\n```ts\nasync function deriveKey(password: string, salt: Uint8Array) {\n  const enc = new TextEncoder()\n  const keyMaterial = await crypto.subtle.importKey(\n    'raw',\n    enc.encode(password),\n    'PBKDF2',\n    false,\n    ['deriveBits']\n  )\n  const derivedBits = await crypto.subtle.deriveBits(\n    { name: 'PBKDF2', salt, iterations: 100000, hash: 'SHA-512' },\n    keyMaterial,\n    256\n  )\n  return new Uint8Array(derivedBits)\n}\n\nconst salt = crypto.getRandomValues(new Uint8Array(16))\nconst derivedKey = await deriveKey('password', salt)\n```\n\n## `crypto.pbkdf2` (native, since Node.js v0.5.5)\n\n<!-- prettier-ignore -->\n```ts\nimport pbkdf2 from 'pbkdf2' // [!code --]\nimport * as crypto from 'node:crypto' // [!code ++]\n\nconst salt = crypto.getRandomValues(new Uint8Array(16))\nconst iterations = 100000\nconst keylen = 32\n\nconst derivedKey = pbkdf2.pbkdf2Sync('password', salt, iterations, keylen, 'sha512') // [!code --]\nconst derivedKey = crypto.pbkdf2Sync('password', salt, iterations, keylen, 'sha512') // [!code ++]\n```\n",
  "is-builtin-module.md": "---\ndescription: Native Node.js alternatives to the is-builtin-module package for checking built-in modules\n---\n\n# Replacements for `is-builtin-module`\n\n## `isBuiltin` (native, since Node.js 16.x)\n\nFor determining if a module is built-in or not, you can use [isBuiltin](https://nodejs.org/api/module.html#moduleisbuiltinmodulename):\n\n```ts\nimport { isBuiltin } from 'node:module' // [!code ++]\nimport isBuiltinModule from 'is-builtin-module' // [!code --]\n\nisBuiltin('fs') // true [!code ++]\nisBuiltinModule('fs') // true [!code --]\n```\n\n## `builtInModules` (native, since Node.js 6.x and 15.x)\n\nBefore Node.js 16.x, `isBuiltin` was not available, so you need to implement your own check using [builtinModules](https://nodejs.org/api/module.html#modulebuiltinmodules):\n\n<!-- prettier-ignore -->\n```ts\nimport { builtinModules } from 'node:module' // [!code ++]\nimport isBuiltinModule from 'is-builtin-module' // [!code --]\n\nfunction isBuiltin(moduleName) { // [!code ++]\n  const name = moduleName.startsWith('node:') // [!code ++]\n    ? moduleName.slice(5) // [!code ++]\n    : moduleName // [!code ++]\n  return builtinModules.includes(name) // [!code ++]\n} // [!code ++]\n\nisBuiltin('fs') // true [!code ++]\nisBuiltinModule('fs') // true [!code --]\n```\n",
  "sqlite3.md": "---\ndescription: Modern alternatives to the sqlite3 package for working with SQLite in Node.js\n---\n\n# Replacements for `sqlite3`\n\n## `node:sqlite` (native, since Node.js 22.13.0)\n\nNode.js ships a built-in SQLite module, [`node:sqlite`](https://nodejs.org/api/sqlite.html), which is the preferred option when you can target a recent Node runtime.\n\nExample:\n\n```ts\nimport sqlite3 from 'sqlite3' // [!code --]\nimport { DatabaseSync } from 'node:sqlite' // [!code ++]\n\nconst db = new sqlite3.Database('app.db') // [!code --]\nconst db = new DatabaseSync('app.db') // [!code ++]\n\ndb.all('SELECT * FROM users', (err, rows) => {}) // [!code --]\nconst rows = db.prepare('SELECT * FROM users').all() // [!code ++]\n```\n\n## `better-sqlite3`\n\n[`better-sqlite3`](https://github.com/WiseLibs/better-sqlite3) is a popular and actively maintained SQLite library for Node.js.\n\nExample:\n\n```ts\nimport sqlite3 from 'sqlite3' // [!code --]\nimport Database from 'better-sqlite3' // [!code ++]\n\nconst db = new sqlite3.Database('app.db') // [!code --]\nconst db = new Database('app.db') // [!code ++]\n\ndb.all('SELECT * FROM users', (err, rows) => {}) // [!code --]\nconst rows = db.prepare('SELECT * FROM users').all() // [!code ++]\n```\n\n## `@libsql/client`\n\n[`@libsql/client`](https://github.com/tursodatabase/libsql-client-ts) is a modern, promise-based client that is fully compatible with SQLite. It supports local SQLite files, remote LibSQL servers, and Turso databases, making it easy to transition to the edge in the future.\n\nExample:\n\n```ts\nimport sqlite3 from 'sqlite3' // [!code --]\nimport { createClient } from '@libsql/client' // [!code ++]\n\nconst db = new sqlite3.Database('app.db') // [!code --]\nconst db = createClient({ url: 'file:app.db' }) // [!code ++]\n\ndb.all('SELECT * FROM users', (err, rows) => {}) // [!code --]\nconst { rows } = await db.execute('SELECT * FROM users') // [!code ++]\n```\n",
  "mississippi.md": "---\ndescription: Modern alternatives to the mississippi package\n---\n\n# Replacements for `mississippi`\n\n`mississippi` exposes stream helpers as `miss.pipe`, `miss.each`, `miss.pipeline`, and so on. Each export wraps a separate npm package internally, but migration targets the **`miss.*` call sites** and replaces each with the corresponding native `node:stream` API below.\n\n## `miss.pipe`\n\n```ts\nimport miss from 'mississippi' // [!code --]\nimport { pipeline } from 'node:stream/promises' // [!code ++]\n\nmiss.pipe(s1, s2, s3, cb) // [!code --]\nawait pipeline(s1, s2, s3) // [!code ++]\n```\n\nFor callback-style usage, use [`stream.pipeline`](https://nodejs.org/api/stream.html#streampipelinestreams-callback).\n\n## `miss.each`\n\n<!-- prettier-ignore -->\n```ts\nimport miss from 'mississippi' // [!code --]\n \nmiss.each(stream, (data, cb) => { // [!code --]\n  fn(data) // [!code --]\n  cb() // [!code --]\n}) // [!code --]\n \nfor await (const data of stream) { // [!code ++]\n  fn(data) // [!code ++]\n} // [!code ++]\n```\n\nNote that `miss.each` reports stream errors through its done callback, while `for await` throws them wrap the loop in `try/catch` to handle errors.\n\n## `miss.pipeline`\n\n```ts\nimport miss from 'mississippi' // [!code --]\nimport { compose } from 'node:stream' // [!code ++]\n\nmiss.pipeline(s1, s2, s3) // [!code --]\ncompose(s1, s2, s3) // [!code ++]\n```\n\nRequires Node.js ≥ 16.9.0.\n\n## `miss.duplex`\n\nSee also [duplexer replacements](./duplexer.md).\n\n<!-- prettier-ignore -->\n```ts\nimport miss from 'mississippi' // [!code --]\nimport { Duplex } from 'node:stream' // [!code ++]\n \nmiss.duplex(writableStream, readableStream) // [!code --]\nDuplex.from({ // [!code ++]\n  writable: writableStream, // [!code ++]\n  readable: readableStream // [!code ++]\n}) // [!code ++]\n```\n\nRequires Node.js ≥ 16.8.0 for `Duplex.from`.\n\n## `miss.through`\n\nSee also [through replacements](./through.md).\n\n<!-- prettier-ignore -->\n```ts\nimport miss from 'mississippi' // [!code --]\nimport { Transform } from 'node:stream' // [!code ++]\n \nmiss.through((chunk, enc, cb) => { // [!code --]\n  cb(null, fn(chunk)) // [!code --]\n}) // [!code --]\nnew Transform({ // [!code ++]\n  transform(chunk, encoding, callback) { // [!code ++]\n    callback(null, fn(chunk)) // [!code ++]\n  } // [!code ++]\n}) // [!code ++]\n```\n\n## `miss.concat`\n\nSee also [get-stream replacements](./get-stream.md).\n\n```ts\nimport miss from 'mississippi' // [!code --]\nimport { buffer } from 'node:stream/consumers' // [!code ++]\n\nstream.pipe(miss.concat((data) => fn(data))) // [!code --]\nconst data = await buffer(stream) // [!code ++]\nfn(data) // [!code ++]\n```\n\nUnlike `miss.concat`, which adapts its output to the input type, `buffer` always returns a `Buffer`. `node:stream/consumers` also exports `text`, `json`, and `arrayBuffer` for those cases.\n\nRequires Node.js ≥ 16.7.0 for `node:stream/consumers`.\n\n## `miss.finished`\n\n```ts\nimport miss from 'mississippi' // [!code --]\nimport { finished } from 'node:stream/promises' // [!code ++]\n\nmiss.finished(stream, cb) // [!code --]\nawait finished(stream) // [!code ++]\n```\n\nFor callback-style usage, use [`stream.finished`](https://nodejs.org/api/stream.html#streamfinishedstream-options-callback).\n\n## `miss.from`\n\n<!-- prettier-ignore -->\n```ts\nimport miss from 'mississippi' // [!code --]\nimport { Readable } from 'node:stream' // [!code ++]\n \nmiss.from((size, cb) => cb(null, chunk)) // [!code --]\nReadable.from( // [!code ++]\n  (async function* () { // [!code ++]\n    yield chunk // [!code ++]\n  })() // [!code ++]\n) // [!code ++]\n```\n\n## `miss.to`\n\n<!-- prettier-ignore -->\n```ts\nimport miss from 'mississippi' // [!code --]\nimport { Writable } from 'node:stream' // [!code ++]\n \nmiss.to( // [!code --]\n  (chunk, enc, cb) => cb(), // [!code --]\n  (cb) => cb() // [!code --]\n) // [!code --]\nnew Writable({ // [!code ++]\n  write(chunk, encoding, callback) { // [!code ++]\n    callback() // [!code ++]\n  }, // [!code ++]\n  final(callback) { // [!code ++]\n    callback() // [!code ++]\n  } // [!code ++]\n}) // [!code ++]\n```\n\n## `miss.parallel`\n\n<!-- prettier-ignore -->\n```ts\nimport miss from 'mississippi' // [!code --]\n \nstream.pipe( // [!code --]\n  miss.parallel(10, (data, cb) => cb(null, fn(data))) // [!code --]\n) // [!code --]\nstream.map((data) => fn(data), { concurrency: 10 }) // [!code ++]\n```\n\nLike `miss.parallel`, `map` preserves the order of the input stream.\n\nRequires Node.js >= 17.4.0 for `Readable.prototype.map`.\n",
  "through.md": "---\ndescription: Modern alternatives to the through package\n---\n\n# Replacements for `through`\n\n## `stream.Writable` (native, Node.js)\n\nThe native [`stream.Writable`](https://nodejs.org/api/stream.html#class-streamwritable) class can be used to create writable streams. It is a suitable replacement when `through` is being used only as a sink that consumes data, rather than as a duplex/transform stream that forwards data.\n\n<!-- prettier-ignore -->\n```ts\nimport through from 'through' // [!code --]\nimport { Writable } from 'node:stream' // [!code ++]\n\nthrough(fn) // [!code --]\nnew Writable({ // [!code ++]\n  write: (chunk, encoding, callback) => { // [!code ++]\n    fn(chunk) // [!code ++]\n    callback() // [!code ++]\n  } // [!code ++]\n}) // [!code ++]\n```\n",
  "vue3-markdown-it.md": "---\ndescription: Modern alternatives to the vue3-markdown-it package for rendering markdown in Vue\n---\n\n# Replacements for `vue3-markdown-it`\n\n## `vue-markdown-render`\n\n[`vue3-markdown-it`](https://github.com/JanGuillermo/vue3-markdown-it) is archived and unmaintained, and has a known vulnerability through one of its direct dependencies ([GHSA-6v5v-wf23-fmfq](https://github.com/advisories/GHSA-6v5v-wf23-fmfq)).\n\n[`vue-markdown-render`](https://github.com/cloudacy/vue-markdown-render) is an actively maintained, near drop-in replacement built on `markdown-it`. In most cases only the import needs to change:\n\n```ts\nimport Markdown from 'vue3-markdown-it' // [!code --]\nimport Markdown from 'vue-markdown-render' // [!code ++]\n```\n\nOptions and plugins are still passed to the component via props (`options`, `plugins`); check the [vue-markdown-render documentation](https://github.com/cloudacy/vue-markdown-render#readme) if you rely on less common `vue3-markdown-it` props.\n",
  "tsup.md": "---\ndescription: Modern alternative to tsup for bundling TypeScript libraries\n---\n\n# Replacements for `tsup`\n\n## `tsdown`\n\n[tsdown](https://github.com/rolldown/tsdown) is the successor recommended by [tsup's own README](https://github.com/egoist/tsup). The project is effectively in maintenance mode. tsdown is built on Rolldown and is typically faster for library builds, with a largely drop-in config surface.\n\n### Config migration\n\nMost `tsup.config.ts` files map 1:1 to `tsdown.config.ts`:\n\n```ts\nimport { defineConfig } from 'tsup' // [!code --]\nimport { defineConfig } from 'tsdown' // [!code ++]\n\nexport default defineConfig({\n  entry: ['src/index.ts'],\n  format: ['esm', 'cjs'],\n  dts: true,\n  clean: true\n})\n```\n\n### Package scripts\n\n```json\n{\n  \"scripts\": {\n    \"build\": \"tsup\" // [!code --]\n    \"build\": \"tsdown\" // [!code ++]\n  }\n}\n```\n\n### Automated migration\n\n- `npx tsdown-migrate`\n- `npx skills add rolldown/tsdown --skill tsdown-migrate`\n",
  "wrap-ansi.md": "---\ndescription: Modern replacement for the wrap-ansi package for wrapping terminal strings (including ANSI-colored output)\n---\n\n# Replacements for `wrap-ansi`\n\n## `fast-wrap-ansi`\n\n[`fast-wrap-ansi`](https://github.com/43081j/fast-wrap-ansi) is a drop‑in replacement for `wrap-ansi` that’s faster and smaller.\n\n```ts\nimport wrapAnsi from 'wrap-ansi' // [!code --]\nimport wrapAnsi from 'fast-wrap-ansi' // [!code ++]\n\nconst value = '\\u001B[36mhello-super-long-token-without-spaces\\u001B[39m'\n\nconsole.log(wrapAnsi(value, 12, { hard: true, trim: false }))\n```\n",
  "string-width.md": "---\ndescription: Modern alternatives to the string-width package for measuring the visual width of a string\n---\n\n# Replacements for `string-width`\n\n## `fast-string-width`\n\n[`fast-string-width`](https://github.com/fabiospampinato/fast-string-width) is a drop‑in replacement for `string-width` that’s faster and smaller.\n\n```ts\nimport stringWidth from 'string-width' // [!code --]\nimport stringWidth from 'fast-string-width' // [!code ++]\n\nconsole.log(stringWidth('abc')) // 3\nconsole.log(stringWidth('👩‍👩‍👧‍👦')) // 1\nconsole.log(stringWidth('\\u001B[31mhello\\u001B[39m')) // 5\n```\n\n## Bun API (native)\n\nIf you’re on Bun ≥ 1.0.29, you can use the built‑in [`stringWidth`](https://bun.com/reference/bun/stringWidth):\n\n```ts\nimport stringWidth from 'string-width' // [!code --]\nimport { stringWidth } from 'bun' // [!code ++]\n\nconsole.log(stringWidth('abc')) // 3\nconsole.log(stringWidth('👩‍👩‍👧‍👦')) // 1\nconsole.log(stringWidth('\\u001B[31mhello\\u001B[39m')) // 5\nconsole.log(\n  stringWidth('\\u001B[31mhello\\u001B[39m', { countAnsiEscapeCodes: false })\n) // 5\n```\n",
  "uri-js.md": "---\ndescription: Modern alternatives to uri-js for RFC 3986 URI parsing, resolving, and normalization\n---\n\n# Replacements for `uri-js`\n\n[`uri-js`](https://github.com/garycourt/uri-js) is unmaintained and triggers deprecation warnings on modern Node.js ([due to `punycode`](https://github.com/garycourt/uri-js/pull/95)).\n\n## `URL` (native)\n\nGood for standard web URLs (http/https/ws/wss/file/mailto, etc.).\n\n- MDN URL: https://developer.mozilla.org/en-US/docs/Web/API/URL\n- Node.js URL: https://nodejs.org/api/url.html#class-url\n\nExample:\n\n```ts\nimport * as URI from 'uri-js' // [!code --]\n\nURI.resolve('https://a/b/c/d?q', '../../g') // [!code --]\nnew URL('../../g', 'https://a/b/c/d?q').href // [!code ++]\n```\n\n> [!NOTE]\n> [WHATWG URL](https://url.spec.whatwg.org/) differs from [RFC 3986](https://datatracker.ietf.org/doc/html/rfc3986) in some details and may not cover arbitrary custom schemes/URNs.\n\n## `uri-js-replace`\n\n[`uri-js-replace`](https://github.com/andreinwald/uri-js-replace) is a drop-in, zero-dependency replacement for `uri-js` with the same API and no deprecation warnings.\n\n```ts\nimport * as URI from 'uri-js' // [!code --]\nimport * as URI from 'uri-js-replace' // [!code ++]\n\nconst parsed = URI.parse('uri://user:pass@example.com:123/one/two?q=a#f')\nconst out = URI.serialize({\n  scheme: 'http',\n  host: 'example.com',\n  fragment: 'footer'\n})\nconst norm = URI.normalize('URI://www.example.org/red%09ros\\xE9#red')\n```\n\n## `fast-uri`\n\n[`fast-uri`](https://github.com/fastify/fast-uri) is a zero-dependency, high-performance RFC 3986 URI toolbox (parse/serialize/resolve/equal) with options similar to `uri-js`.\n\n```ts\nimport * as uri from 'uri-js' // [!code --]\nimport * as uri from 'fast-uri' // [!code ++]\n\nuri.parse('uri://user:pass@example.com:123/one/two.three?q1=a1#a')\nuri.serialize({ scheme: 'http', host: 'example.com', fragment: 'footer' })\nuri.resolve('uri://a/b/c/d?q', '../../g')\nuri.equal('example://a/b/%7Bfoo%7D', 'eXAMPLE://a/./b/../b/%63/%7bfoo%7d')\n```\n",
  "invariant.md": "---\ndescription: Modern alternatives to the invariant package for runtime assertions\n---\n\n# Replacements for `invariant`\n\n## `tiny-invariant`\n\n[`tiny-invariant`](https://github.com/alexreardon/tiny-invariant) provides a similar API with zero dependencies.\n\nFor example:\n\n```ts\nimport invariant from 'invariant' // [!code --]\nimport invariant from 'tiny-invariant' // [!code ++]\n\ninvariant(ok, 'Hello %s, code %d', name, code) // [!code --]\ninvariant(ok, `Hello ${name}, code ${code}`) // [!code ++]\n```\n\nSimilarly, you can lazily compute messages to avoid unnecessary work:\n\n```ts\nimport invariant from 'invariant' // [!code --]\nimport invariant from 'tiny-invariant' // [!code ++]\n\ninvariant(value, getExpensiveMessage()) // [!code --]\ninvariant(value, () => getExpensiveMessage()) // [!code ++]\n```\n",
  "utf8.md": "---\ndescription: Modern alternatives to the utf8 package for UTF-8 encoding and decoding\n---\n\n# Replacements for `utf8`\n\nModern Node and browsers provide native UTF-8 APIs, so this dependency is rarely needed.\n\n## TextEncoder/TextDecoder (native)\n\nThe built-in [`TextEncoder`](https://developer.mozilla.org/en-US/docs/Web/API/TextEncoder) and [`TextDecoder`](https://developer.mozilla.org/en-US/docs/Web/API/TextDecoder) APIs provide a native way to handle UTF-8 encoding and decoding.\n\n```ts\nconst text = '€'\nconst encoder = new TextEncoder()\nconst utf8Bytes = encoder.encode(text) // Uint8Array of UTF-8 bytes\n\n// and to decode:\n\nconst decoder = new TextDecoder('utf-8', { fatal: true })\nconst decodedText = decoder.decode(utf8Bytes) // \"€\"\n```\n\n## Buffer (native)\n\nNode's built-in [`Buffer`](https://nodejs.org/api/buffer.html) provides both `Buffer.from(str, 'utf8')` and `buf.toString('utf8')` methods for UTF-8 encoding and decoding.\n\n```ts\nimport { Buffer } from 'node:buffer'\n\nconst text = '€'\nconst utf8Buffer = Buffer.from(text, 'utf8') // Buffer of UTF-8 bytes\n```\n",
  "yaml-ast-parser.md": "---\ndescription: Modern alternatives to yaml-ast-parser for parsing YAML into an AST\n---\n\n# Replacements for `yaml-ast-parser`\n\n## `yaml`\n\n[`yaml`](https://github.com/eemeli/yaml) is an actively maintained YAML parser with support for YAML 1.1 and 1.2, comments, and AST access.\n\n`yaml-ast-parser` returns a `YAMLNode` from `load`. With `yaml`, use `parseDocument` and access the root node through `document.contents`.\n\nExample:\n\n```ts\nimport { load } from 'yaml-ast-parser' // [!code --]\nimport { parseDocument } from 'yaml' // [!code ++]\n\nconst ast = load(source) // [!code --]\nconst document = parseDocument(source) // [!code ++]\nconst ast = document.contents // [!code ++]\n```\n\nThe AST node types differ between the two packages, so code that traverses or modifies nodes will need to use the node APIs provided by `yaml`.\n",
  "semver.md": "---\ndescription: Modern alternatives to the semver package\n---\n\n# Replacements for `semver`\n\n## `verkit`\n\n[`verkit`](https://github.com/sxzz/verkit) is a modern SemVer toolkit designed for ESM and TypeScript projects. It is faster and smaller than `semver`.\n\n```ts\nimport semver from 'semver' // [!code --]\nimport { isLess, satisfies } from 'verkit' // [!code ++]\n\nsemver.lt(version1, version2) // [!code --]\nsemver.satisfies(version, range) // [!code --]\n\nisLess(version1, version2) // [!code ++]\nsatisfies(version, range) // [!code ++]\n```\n",
  "eslint-plugin-eslint-comments.md": "---\ndescription: Modern alternatives to the eslint-plugin-eslint-comments package for ESLint comment linting\n---\n\n# Replacements for `eslint-plugin-eslint-comments`\n\n## `@eslint-community/eslint-plugin-eslint-comments`\n\n[`@eslint-community/eslint-plugin-eslint-comments`](https://github.com/eslint-community/eslint-plugin-eslint-comments) is the actively maintained successor with updated dependencies, flat config support, and continued development.\n\n```ts\nimport eslintComments from 'eslint-plugin-eslint-comments' // [!code --]\nimport commentsCommunity from '@eslint-community/eslint-plugin-eslint-comments/configs' // [!code ++]\n\nexport default [\n  commentsCommunity.recommended, // [!code ++]\n  {\n    plugins: {\n      'eslint-comments': eslintComments // [!code --]\n    },\n    rules: {\n      'eslint-comments/no-unused-disable': 'error', // [!code --]\n      '@eslint-community/eslint-comments/no-unused-disable': 'error' // [!code ++]\n    }\n  }\n]\n```\n\nIf you're using a legacy config format:\n\n```ts\nmodule.exports = {\n  extends: [\n    'eslint:recommended',\n    'plugin:eslint-comments/recommended', // [!code --]\n    'plugin:@eslint-community/eslint-comments/recommended' // [!code ++]\n  ],\n  rules: {\n    'eslint-comments/no-unused-disable': 'error', // [!code --]\n    '@eslint-community/eslint-comments/no-unused-disable': 'error' // [!code ++]\n  }\n}\n```\n",
  "emoji-regex.md": "---\ndescription: Modern alternatives to the emoji-regex package for emoji detection and matching\n---\n\n# Replacements for `emoji-regex`\n\n## `emoji-regex-xs`\n\n[`emoji-regex-xs`](https://github.com/slevithan/emoji-regex-xs) offers the same API and features whilst being 98% smaller.\n\n```ts\nimport emojiRegex from 'emoji-regex' // [!code --]\nimport emojiRegex from 'emoji-regex-xs' // [!code ++]\n\nconst text = `\n\\u{231A}: ⌚ default emoji presentation character (Emoji_Presentation)\n\\u{2194}\\u{FE0F}: ↔️ default text presentation character rendered as emoji\n\\u{1F469}: 👩 emoji modifier base (Emoji_Modifier_Base)\n\\u{1F469}\\u{1F3FF}: 👩🏿 emoji modifier base followed by a modifier\n`\n\nconst regex = emojiRegex()\nfor (const match of text.matchAll(regex)) {\n  const emoji = match[0]\n  console.log(`Matched sequence ${emoji} — code points: ${[...emoji].length}`)\n}\n```\n\n## Unicode RegExp (native)\n\nIf your target runtime supports ES2024 Unicode property sets, you can use the native [`\\p{RGI_Emoji}`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Regular_expressions/Unicode_character_class_escape) property in a regular expression. This relies on the engine's built‑in Unicode handling.\n\n```ts\nimport emojiRegex from 'emoji-regex' // [!code --]\n\nconst regex = emojiRegex() // [!code --]\nconst regex = /\\p{RGI_Emoji}/gv // [!code ++]\n\nfor (const match of text.matchAll(regex)) {\n  const emoji = match[0]\n  console.log(`Matched sequence ${emoji} — code points: ${[...emoji].length}`)\n}\n```\n",
  "stream-buffers.md": "---\ndescription: Modern alternatives to the stream-buffers package for buffering and generating streams in Node.js\n---\n\n# Replacements for `stream-buffers`\n\n## Utility Consumers (native, Node.js)\n\nSince Node.js ≥ 16.7.0 [Utility Consumers](https://nodejs.org/api/webstreams.html#webstreams_utility_consumers) let you consume a Readable stream fully into memory (`Buffer`/`string`/`JSON`/`Blob`/`ArrayBuffer`).\n\nExample:\n\n```ts\nimport streamBuffers from 'stream-buffers' // [!code --]\nimport { pipeline } from 'node:stream/promises' // [!code --]\nimport { buffer } from 'node:stream/consumers' // [!code ++]\n\nconst sink = new streamBuffers.WritableStreamBuffer() // [!code --]\nawait pipeline(readable, sink) // [!code --]\n\nconst out = sink.getContents() // [!code --]\nconst out = await buffer(readable) // [!code ++]\n```\n\nCapturing output when an API expects a Writable example:\n\n```ts\nimport streamBuffers from 'stream-buffers' // [!code --]\nimport { PassThrough } from 'node:stream' // [!code ++]\nimport { buffer, text } from 'node:stream/consumers' // [!code ++]\n\nconst sink = new streamBuffers.WritableStreamBuffer() // [!code --]\nconst sink = new PassThrough() // [!code ++]\n\nconst outPromise = buffer(sink) // [!code ++]\nawait someFnThatWritesTo(sink)\nconst out = sink.getContents() // [!code --]\nsink.end() // [!code ++]\nconst out = await outPromise // [!code ++]\n```\n\nPush data over time example:\n\n```ts\nimport streamBuffers from 'stream-buffers' // [!code --]\nimport { Readable } from 'node:stream' // [!code ++]\n\nconst rs = new streamBuffers.ReadableStreamBuffer() // [!code --]\nconst rs = new Readable({ read() {} }) // [!code ++]\n\nrs.put('first chunk') // [!code --]\nrs.push('first chunk') // [!code ++]\n\nrs.put(Buffer.from('second chunk')) // [!code --]\nrs.push(Buffer.from('second chunk')) // [!code ++]\n\nrs.stop() // [!code --]\nrs.push(null) // [!code ++]\n```\n\nControl chunk size and frequency example:\n\n<!-- prettier-ignore -->\n```ts\nimport streamBuffers from 'stream-buffers' // [!code --]\nimport { Readable } from 'node:stream' // [!code ++]\nimport { setTimeout } from 'node:timers/promises' // [!code ++]\n\nconst data = Buffer.from('...your data...')\nconst frequencyMs = 10\nconst chunkSize = 2048\n\nconst rs = new streamBuffers.ReadableStreamBuffer({ frequency: frequencyMs, chunkSize: chunkSize }) // [!code --]\nrs.put(data) // [!code --]\nrs.stop() // [!code --]\n\nconst rs = Readable.from(async function* () { // [!code ++]\n  for (let i = 0; i < data.length; i += chunkSize) { // [!code ++]\n    yield data.slice(i, i + chunkSize) // [!code ++]\n    await setTimeout(frequencyMs) // [!code ++]\n  } // [!code ++]\n}()) // [!code ++]\n```\n",
  "tar-fs.md": "---\ndescription: Lighter alternative to the tar-fs package for packing and extracting tar archives on the filesystem\n---\n\n# Replacements for `tar-fs`\n\n## `modern-tar`\n\n[`tar-fs`](https://github.com/mafintosh/tar-fs) provides filesystem bindings on top of [`tar-stream`](https://github.com/mafintosh/tar-stream). [`modern-tar`](https://github.com/ayuhito/modern-tar) covers the same workflows through [`modern-tar/fs`](https://github.com/ayuhito/modern-tar), with zero dependencies and built-in TypeScript types.\n\nExample:\n\n<!-- prettier-ignore -->\n```js\nimport tar from 'tar-fs' // [!code --]\nimport { createReadStream, createWriteStream } from 'node:fs' // [!code ++]\nimport { packTar, unpackTar } from 'modern-tar/fs' // [!code ++]\nimport { pipeline } from 'node:stream/promises' // [!code ++]\n\ntar.pack('./my-directory').pipe(createWriteStream('my-tarball.tar')) // [!code --]\nawait pipeline(packTar('./my-directory'), createWriteStream('my-tarball.tar')) // [!code ++]\n\ncreateReadStream('my-other-tarball.tar').pipe( // [!code --]\n  tar.extract('./my-other-directory') // [!code --]\n) // [!code --]\nawait pipeline( // [!code ++]\n  createReadStream('my-other-tarball.tar'), // [!code ++]\n  unpackTar('./my-other-directory') // [!code ++]\n) // [!code ++]\n```\n",
  "cwd.md": "---\ndescription: Modern alternatives to the cwd package for resolving project-relative paths\n---\n\n# Replacements for `cwd`\n\n## `empathic`\n\n[`empathic`](https://github.com/lukeed/empathic) provides a more generic way to find files and directories upwards.\n\n[`cwd`](https://github.com/jonschlinkert/cwd) is not `process.cwd()`. It finds the nearest `package.json` and returns an absolute path anchored to that package root. Unlike [`pkg-dir`](./pkg-dir.md), which returns the package root directory, `cwd` returns a resolved path relative to that root.\n\nThe main difference is that `empathic` is _synchronous_, so you should no longer `await` the result.\n\nExample:\n\n```ts\nimport { resolve, dirname } from 'node:path' // [!code ++]\nimport * as pkg from 'empathic/package' // [!code ++]\nimport cwd from 'cwd' // [!code --]\n\nconst filepath = resolve('one/two.js') // [!code ++]\nconst pkgPath = pkg.up({ cwd: filepath }) // [!code ++]\nconst result = cwd('one/two.js') // [!code --]\nconst result = pkgPath ? resolve(dirname(pkgPath), filepath) : filepath // [!code ++]\n```\n",
  "sort-object.md": "---\ndescription: Modern alternatives to the sort-object package for sorting object keys\n---\n\n# Replacements for `sort-object`\n\n## `Object.entries` and `Array.sort` (native)\n\nFor simple cases:\n\n<!-- prettier-ignore -->\n```ts\nimport sortObj from 'sort-object' // [!code --]\n\nconst sorted = sortObj(object) // [!code --]\n\n// Ascending A→Z\nconst sorted = Object.fromEntries( // [!code ++]\n  Object.entries(object).sort((a, b) => a[0].localeCompare(b[0])) // [!code ++]\n) // [!code ++]\n```\n\nReplicating `sortBy` (function returns an ordered key list):\n\n<!-- prettier-ignore -->\n```ts\nimport sortObj from 'sort-object' // [!code --]\n\nconst sorted = sortObj(object, { // [!code --]\n  sortBy: (obj) => { // [!code --]\n    const arr = [] // [!code --]\n    Object.keys(obj).forEach((k) => { // [!code --]\n      if (obj[k].startsWith('a')) // [!code --]\n        arr.push(k) // [!code --]\n    }) // [!code --]\n    return arr.reverse() // [!code --]\n  } // [!code --]\n}) // [!code --]\n\nconst sortBy = (obj) => // [!code ++]\n  Object.keys(obj) // [!code ++]\n    .filter((k) => obj[k].startsWith('a')) // [!code ++]\n    .reverse() // [!code ++]\nconst sorted = Object.fromEntries( // [!code ++]\n  sortBy(object).map((k) => [k, object[k]]) // [!code ++]\n) // [!code ++]\n```\n\n## `sort-object-keys`\n\n[`sort-object-keys`](https://github.com/keithamus/sort-object-keys) is zero‑dependency and matches common `sort-object` use cases (custom order array or comparator).\n\n```ts\nimport sortObj from 'sort-object' // [!code --]\nimport sortObjectKeys from 'sort-object-keys' // [!code ++]\n\n// Default A→Z\nconst sorted = sortObj(object) // [!code --]\nconst sorted = sortObjectKeys(object) // [!code ++]\n\n// With comparator\nconst sortedByCmp = sortObj(object, { sort: (a, b) => a.localeCompare(b) }) // [!code --]\nconst sortedByCmp = sortObjectKeys(object, (a, b) => a.localeCompare(b)) // [!code ++]\n```\n\n## `sortobject`\n\n[`sortobject`](https://github.com/bevry/sortobject) is zero‑dependency and deeply sorts nested objects.\n\n```ts\nimport sortObj from 'sort-object' // [!code --]\nimport sortobject from 'sortobject' // [!code ++]\n\nconst sorted = sortObj(object) // [!code --]\nconst sorted = sortobject(object) // [!code ++]\n```\n",
  "xmldom.md": "---\ndescription: Modern alternatives to the xmldom package for XML DOM parsing and serialization\n---\n\n# Replacements for `xmldom`\n\n## `@xmldom/xmldom`\n\n[`@xmldom/xmldom`](https://github.com/xmldom/xmldom) is the maintained fork of the original `xmldom`.\n\nFor example:\n\n```ts\nimport { DOMParser, XMLSerializer } from 'xmldom' // [!code --]\nimport { DOMParser, XMLSerializer } from '@xmldom/xmldom' // [!code ++]\n\nconst doc = new DOMParser().parseFromString(source, 'text/xml')\nconst xml = new XMLSerializer().serializeToString(doc)\n```\n\nCommonJS:\n\n```ts\nconst { DOMParser, XMLSerializer } = require('xmldom') // [!code --]\nconst { DOMParser, XMLSerializer } = require('@xmldom/xmldom') // [!code ++]\n```\n",
  "strip-ansi.md": "---\ndescription: Native Node.js alternatives to the strip-ansi package for removing ANSI escape codes from strings\n---\n\n# Replacements for `strip-ansi`\n\n## `util.stripVTControlCharacters` (native, Node.js)\n\nAdded in v16.11.0, [`util.stripVTControlCharacters`](https://nodejs.org/api/util.html#utilstripvtcontrolcharactersstr) can be used to strip ANSI escape codes from a string.\n\n```ts\nimport stripAnsi from 'strip-ansi' // [!code --]\nimport { stripVTControlCharacters } from 'node:util' // [!code ++]\n\nconsole.log(stripAnsi('\\u001B[4me18e\\u001B[0m')) // [!code --]\nconsole.log(stripVTControlCharacters('\\u001B[4me18e\\u001B[0m')) // [!code ++]\n```\n\n> [!NOTE]\n> Due to [a bug](https://github.com/nodejs/node/issues/53697), in older Node versions this utility doesn't handle ANSI hyperlinks correctly. This behavior has been fixed as of NodeJS v22.10.\n\n## Deno\n\nDeno implements the Node `util` API, and also provides [`util.stripVTControlCharacters`](https://docs.deno.com/api/node/util/~/stripVTControlCharacters). The usage is identical:\n\n```ts\nimport stripAnsi from 'strip-ansi' // [!code --]\nimport { stripVTControlCharacters } from 'node:util' // [!code ++]\n\nconsole.log(stripAnsi('\\u001B[4me18e\\u001B[0m')) // [!code --]\nconsole.log(stripVTControlCharacters('\\u001B[4me18e\\u001B[0m')) // [!code ++]\n```\n\n## Bun\n\n### Using Node‑compatible API\n\nBun also implements Node’s [`util.stripVTControlCharacters`](https://bun.sh/reference/node/util/stripVTControlCharacters) through its Node compat layer:\n\n```ts\nimport stripAnsi from 'strip-ansi' // [!code --]\nimport { stripVTControlCharacters } from 'node:util' // [!code ++]\n\nconsole.log(stripAnsi('\\u001B[1mHello\\u001B[0m')) // [!code --]\nconsole.log(stripVTControlCharacters('\\u001B[1mHello\\u001B[0m')) // [!code ++]\n```\n\n### Using Bun's native API (>=1.2.21)\n\nSince Bun v1.2.21, you can use the built-in [`Bun.stripANSI`](https://bun.com/blog/release-notes/bun-v1.2.21#bun-stripansi-simd-accelerated-ansi-escape-removal) method.\n\n```ts\nimport stripAnsi from 'strip-ansi' // [!code --]\nimport { stripANSI } from 'bun' // [!code ++]\n\nconsole.log(stripAnsi('\\u001B[31mHello World\\u001B[0m')) // [!code --]\nconsole.log(Bun.stripANSI('\\u001B[31mHello World\\u001B[0m')) // [!code ++]\n```\n",
  "deep-merge.md": "---\ndescription: Modern alternatives to packages for deep merging objects\n---\n\n# Replacements for deep merge packages\n\n## `defu`\n\nIf you need to deep merge two objects, you can use [`defu`](https://github.com/unjs/defu):\n\n```ts\nimport { defu } from 'defu'\n\nconst options = defu(object, defaults)\n```\n\n## `@fastify/deepmerge`\n\nThe [`@fastify/deepmerge`](https://github.com/fastify/deepmerge) package is another fast and small alternative.\n\nIt also offers more options for customizing the merge behavior than other libraries.\n\n```ts\nimport deepMerge from '@fastify/deepmerge'\n\nconst options = deepMerge(object, defaults)\n```\n",
  "typescript-native-preview.md": "---\ndescription: Modern alternative to the @typescript/native-preview package for the native version of TypeScript\n---\n\n# Replacements for `@typescript/native-preview`\n\n## `typescript`\n\nTypeScript v7 is no longer a preview and is now published under the `typescript` name, so `@typescript/native-preview` is no longer needed\n\n```json\n{\n  \"dependencies\": {\n    \"@typescript/native-preview\": \"7.0.0-dev.20260707.2\", // [!code --]\n    \"typescript\": \"7.0.2\" // [!code ++]\n  }\n}\n```\n",
  "node-telegram-bot-api.md": "---\ndescription: Modern alternatives to the node-telegram-bot-api package\n---\n\n# Replacements for `node-telegram-bot-api`\n\n## `grammy`\n\n[`grammy`](https://grammy.dev) is a modern library for creating telegram bots.\n\nExample:\n\n```ts\nimport { Bot } from 'grammy'\n\nconst bot = new Bot(process.env.TOKEN)\n\nbot.on('message', (ctx) => ctx.reply('Hi there!'))\n\nbot.start()\n```\n",
  "read-pkg-up.md": "---\ndescription: Modern alternatives to the read-pkg-up package for reading package.json files up the directory tree\n---\n\n# Replacements for `read-pkg-up`\n\n## `pkg-types`\n\n[`pkg-types`](https://github.com/unjs/pkg-types) provides a similar API and strong types.\n\nFor example:\n\n```ts\nimport { readPackageUp } from 'read-pkg-up' // [!code --]\nimport { readPackageJSON } from 'pkg-types' // [!code ++]\n\nconst packageJson = await readPackageJSON() // [!code ++]\nconst packageJson = await readPackageUp() // [!code --]\n```\n\nSimilarly, you can get hold of the path via `resolvePackageJSON`:\n\n```ts\nimport { resolvePackageJSON } from 'pkg-types'\n\nconst packageJsonPath = await resolvePackageJSON()\n```\n\n## `empathic`\n\n[`empathic`](https://github.com/lukeed/empathic) provides a more generic way to find files and directories upwards.\n\nIt can be combined with `node:fs` to read `package.json` files:\n\n```ts\nimport { readPackageUp } from 'read-pkg-up' // [!code --]\nimport fs from 'node:fs/promises' // [!code ++]\nimport * as pkg from 'empathic' // [!code ++]\n\nconst packageJson = await readPackageUp() // [!code --]\nconst packageJsonPath = pkg.up() // [!code ++]\nconst packageJson = packageJsonPath // [!code ++]\n  ? JSON.parse(await readFile(packageJsonPath, 'utf8')) // [!code ++]\n  : undefined // [!code ++]\n```\n\n> [!NOTE]\n> This is of course a more manual way to read the `package.json` file, so one of the other options may be more attractive.\n",
  "find-up.md": "---\ndescription: Modern alternatives to the find-up package for finding files by walking up parent directories\n---\n\n# Replacements for `find-up`\n\n## `empathic`\n\n[`empathic`](https://github.com/lukeed/empathic) provides a more generic way to find files and directories upwards.\n\nThe main difference is that `empathic` is _synchronous_, so you should no longer `await` the result.\n\nExample:\n\n```ts\nimport * as find from 'empathic/find' // [!code ++]\nimport { findUp } from 'find-up' // [!code --]\n\nawait findUp('package.json') // [!code --]\nfind.up('package.json') // [!code ++]\n```\n\n### `findUpMultiple`\n\nWhen finding multiple files, you can use `find.any`:\n\n```ts\nimport * as find from 'empathic/find' // [!code ++]\nimport { findUpMultiple } from 'find-up' // [!code --]\n\nconst files = await findUpMultiple(['package.json', 'tsconfig.json']) // [!code --]\nconst files = find.any(['package.json', 'tsconfig.json']) // [!code ++]\n```\n\n### Options\n\n#### `type`\n\nThe `type` option can be replaced by using the equivalent function.\n\nFor example, finding a file:\n\n```ts\nimport * as find from 'empathic/find' // [!code ++]\nimport { findUp } from 'find-up' // [!code --]\n\nawait findUp('package.json', { type: 'file' }) // [!code --]\nfind.file('package.json') // [!code ++]\n```\n\n#### `cwd`\n\nThis option is supported just the same:\n\n```ts\nfind.file('package.json', { cwd })\n```\n\n#### `stopAt`\n\nThis option is replaced by `last`:\n\n<!-- eslint-skip -->\n\n```ts\nimport { findUp } from 'find-up' // [!code --]\nimport * as find from 'empathic/find' // [!code ++]\n\nawait findUp( // [!code --]\nfind.file( // [!code ++]\n  'package.json',\n  { stopAt: '/some/dir' }, // [!code --]\n  { last: '/some/dir' }, // [!code ++]\n)\n```\n\n## `pkg-types`\n\n[`pkg-types`](https://github.com/unjs/pkg-types) provides utilities for reading and writing package.json, tsconfig.json, and other configuration files with TypeScript support.\n\n```ts\nimport { findUp } from 'find-up' // [!code --]\nimport { readPackageJSON } from 'pkg-types' // [!code ++]\n\nconst packagePath = await findUp('package.json') // [!code --]\nconst packageJson = await readPackageJSON() // [!code ++]\n```\n",
  "wellknown.md": "---\ndescription: Modern replacements for the wellknown package, a WKT parser and stringifier\n---\n\n# Replacements for `wellknown`\n\n## `betterknown`\n\n[`betterknown`](https://github.com/placemark/betterknown) is a maintained alternative for `wellknown` package.\n\nExample:\n\n```js\nimport wellknown from 'wellknown' // [!code --]\nimport { wktToGeoJSON, geoJSONToWkt } from 'betterknown' // [!code ++]\n\nwellknown.parse('POINT(1 2)') // [!code --]\nwktToGeoJSON('POINT(1 2)') // [!code ++]\n\nwellknown.stringify({ // [!code --]\ngeoJSONToWkt({ // [!code ++]\n  type: 'Point',\n  coordinates: [1, 2]\n})\n\nwellknown.stringify({ // [!code --]\ngeoJSONToWkt({ // [!code ++]\n  type: 'Feature',\n  geometry: { type: 'Point', coordinates: [1, 2] },\n  properties: {}\n})\n```\n",
  "tempy.md": "---\ndescription: Modern alternatives to the temp and tempy packages for creating temporary files and directories\n---\n\n# Replacements for `temp` / `tempy`\n\n## `fs.mkdtemp` (native, since Node.js v14.x)\n\nNode.js has the [`fs.mkdtemp`](https://nodejs.org/api/fs.html#fsmkdtempprefix-options-callback) function for creating a unique temporary directory. Directory cleanup can be done by passing `{recursive: true}` to [`fs.rm`](https://nodejs.org/api/fs.html#fsrmpath-options-callback).\n\nExample:\n\n```ts\nimport temp from 'temp' // [!code --]\nimport { mkdtemp, realpath } from 'node:fs/promises' // [!code ++]\nimport { join } from 'node:path' // [!code ++]\nimport { tmpdir } from 'node:os' // [!code ++]\n\nconst tempDirPath = temp.mkdirSync('foo') // [!code --]\nconst tempDirPath = await mkdtemp(join(await realpath(tmpdir()), 'foo-')) // [!code ++]\n```\n\n## `fs.mkdtempDisposable` (native, since Node.js v24.4.0)\n\nNode.js now provides [`fs.mkdtempDisposable`](https://nodejs.org/api/fs.html#fspromisesmkdtempdisposableprefix-options) which leverages the `using` keyword for automatic cleanup. This eliminates the need for `temp.track()` or manual cleanup logic.\n\nExample:\n\n```ts\nimport temp from 'temp' // [!code --]\nimport { mkdtempDisposable } from 'node:fs/promises' // [!code ++]\nimport { join } from 'node:path' // [!code ++]\nimport { tmpdir } from 'node:os' // [!code ++]\n\ntemp.track() // [!code --]\nconst tempDirPath = temp.mkdirSync('foo') // [!code --]\nawait using tempDir = await mkdtempDisposable(join(tmpdir(), 'foo-')) // [!code ++]\nconst tempDirPath = tempDir.path // [!code ++]\n```\n\n## Deno\n\nDeno provides built-in [`Deno.makeTempDir`](https://docs.deno.com/api/deno/~/Deno.makeTempDir) and [`Deno.makeTempFile`](https://docs.deno.com/api/deno/~/Deno.makeTempFile) for creating unique temporary directories and files in the system temp directory (or a custom `dir`). You can also set `prefix` and `suffix`. Both return the full path and require `--allow-write`.\n\n```ts\nimport { temporaryDirectory } from 'tempy' // [!code --]\n\nconst tempDir = temporaryDirectory({ prefix: 'foo-' }) // [!code --]\nconst tempDir = await Deno.makeTempDir({ prefix: 'foo-' }) // [!code ++]\n```\n\n```ts\nimport { temporaryFile } from 'tempy' // [!code --]\n\nconst tempFile = temporaryFile({ extension: 'txt' }) // [!code --]\nconst tempFile = await Deno.makeTempFile({ suffix: '.txt' }) // [!code ++]\n```\n\n> [!NOTE]\n> See also: [secure tempfiles in Node.js without dependencies (Advanced Web Machinery)](https://advancedweb.hu/secure-tempfiles-in-nodejs-without-dependencies)\n",
  "uuidv4.md": "---\ndescription: Native alternatives to the uuidv4 package for UUID v4 generation\n---\n\n# Replacements for `uuidv4`\n\n## `crypto.randomUUID` (native)\n\nYou can use [`crypto.randomUUID`](https://developer.mozilla.org/en-US/docs/Web/API/Crypto/randomUUID) for UUID v4 generation.\n\nExample:\n\n```ts\nconst uuid = crypto.randomUUID()\n```\n\nOr in Node.js via [`node:crypto`](https://nodejs.org/api/crypto.html#cryptorandomuuidoptions):\n\n```ts\nimport * as crypto from 'node:crypto'\n\nconst uuid = crypto.randomUUID()\n```\n",
  "qs.md": "---\ndescription: Modern alternatives to the qs package for parsing and serializing query strings\n---\n\n# Replacements for `qs`\n\n## `URLSearchParams` (native)\n\n[`URLSearchParams`](https://developer.mozilla.org/docs/Web/API/URLSearchParams) is built into browsers and Node.js (>= 10). Use it when you don’t need nested objects or automatic array parsing. It preserves multiple values via `getAll`, and `toString()` gives you a URL-safe query string.\n\nExample:\n\n```ts\nimport qs from 'qs' // [!code --]\n\nconst query = 'a=1&a=2&b=3'\n\nconst obj = qs.parse(query) // [!code --]\nconst sp = new URLSearchParams(query) // [!code ++]\nconst obj = Object.fromEntries(sp) // [!code ++]\nconst a = sp.getAll('a') // [!code ++]\n```\n\n## `fast-querystring`\n\n[`fast-querystring`](https://github.com/anonrig/fast-querystring) is tiny and very fast. It handles flat key/value pairs and repeated keys as arrays; it does not support nested objects. Use it when you need arrays but not nesting.\n\nExample:\n\n```ts\nimport qs from 'qs' // [!code --]\nimport fqs from 'fast-querystring' // [!code ++]\n\nconst obj = qs.parse('tag=a&tag=b') // [!code --]\nconst obj = fqs.parse('tag=a&tag=b') // [!code ++]\n\nconst str = qs.stringify({ tag: ['a', 'b'], q: 'x y' }) // [!code --]\nconst str = fqs.stringify({ tag: ['a', 'b'], q: 'x y' }) // [!code ++]\n```\n\n## `picoquery`\n\n[`picoquery`](https://github.com/43081j/picoquery) supports nesting and arrays with a fast single‑pass parser and configurable syntax. v2.x and above are ESM‑only; v1.x is CommonJS and will be maintained with non‑breaking changes. `nestingSyntax: 'js'` offers the highest compatibility with `qs`, though you can pick other syntaxes for performance.\n\nExample:\n\n<!-- prettier-ignore -->\n```ts\nimport qs from 'qs' // [!code --]\nimport { parse, stringify } from 'picoquery' // [!code ++]\n\nconst opts = { // [!code ++]\n  nestingSyntax: 'js', // [!code ++]\n  arrayRepeat: true, // [!code ++]\n  arrayRepeatSyntax: 'bracket' // [!code ++]\n} // [!code ++]\n\nconst obj = qs.parse('user[name]=foo&tags[]=bar&tags[]=baz') // [!code --]\nconst obj = parse('user[name]=foo&tags[]=bar&tags[]=baz', opts) // [!code ++]\n\nconst str = qs.stringify( // [!code --]\n  { user: { name: 'foo' }, tags: ['bar', 'baz'] }, // [!code --]\n  { arrayFormat: 'brackets' } // [!code --]\n) // [!code --]\nconst str = stringify({ user: { name: 'foo' }, tags: ['bar', 'baz'] }, opts) // [!code ++]\n```\n\n## `neoqs`\n\n[`neoqs`](https://github.com/PuruVJ/neoqs) is a fork of `qs` without legacy polyfills, with TypeScript types included, and with both ESM and CommonJS builds (plus a legacy ES5 mode). Choose it when you want `qs`‑level compatibility with modern packaging options.\n\nExample:\n\n```ts\nimport qs from 'qs' // [!code --]\nimport * as qs from 'neoqs' // [!code ++]\n\nconst obj = qs.parse('a[b][c]=1&arr[]=2&arr[]=3')\nconst str = qs.stringify(obj, { arrayFormat: 'brackets' })\n```\n",
  "ora.md": "---\ndescription: Modern alternatives to the ora package for displaying elegant terminal spinners with status indicators\n---\n\n# Replacements for `ora`\n\n## `nanospinner`\n\n[`nanospinner`](https://github.com/usmanyunusov/nanospinner) provides simple start/success/error/warning methods with one dependency (`picocolors`).\n\n```ts\nimport ora from 'ora' // [!code --]\nimport { createSpinner } from 'nanospinner' // [!code ++]\n\nconst spinner = ora('Loading...').start() // [!code --]\nconst spinner = createSpinner('Loading...').start() // [!code ++]\n\nspinner.succeed('Done!') // [!code --]\nspinner.success('Done!') // [!code ++]\n\nspinner.fail('Error!') // [!code --]\nspinner.error('Error!') // [!code ++]\n```\n\n## `picospinner`\n\n[`picospinner`](https://github.com/tinylibs/picospinner) has zero dependencies with support for custom symbols, frames, and colors through Node.js built-in styling.\n\n```ts\nimport ora from 'ora' // [!code --]\nimport { Spinner } from 'picospinner' // [!code ++]\n\nconst spinner = ora('Loading...').start() // [!code --]\nconst spinner = new Spinner('Loading...') // [!code ++]\nspinner.start() // [!code ++]\n```\n\nIf you want to customize the color of the spinner, you can specify this when creating an instance:\n\n```ts\nconst spinner = new Spinner('Loading...', { colors: { spinner: 'yellow' } })\n```\n",
  "dot-prop.md": "---\ndescription: Modern alternatives to the dot-prop package for getting, setting, and deleting nested object properties using dot notation\n---\n\n# Replacements for `dot-prop`\n\n## `dlv` and `dset`\n\n[`dlv`](https://github.com/developit/dlv) gets nested values with default fallbacks and [`dset`](https://github.com/lukeed/dset) sets nested values with automatic intermediate object creation.\n\n```ts\nimport { getProperty, setProperty } from 'dot-prop' // [!code --]\nimport delve from 'dlv' // [!code ++]\nimport { dset } from 'dset' // [!code ++]\n\nconst value = getProperty(obj, 'foo.bar.baz') // [!code --]\nconst value = delve(obj, 'foo.bar.baz') // [!code ++]\n\nsetProperty(obj, 'foo.bar.baz', 'value') // [!code --]\ndset(obj, 'foo.bar.baz', 'value') // [!code ++]\n```\n\n## `String.prototype.split` + `Array.prototype.reduce` (native)\n\nIf you only need to get a nested value you can use a simple function:\n\n```js\nfunction getProperty(obj, key) {\n  return key.split('.').reduce((acc, k) => acc?.[k], obj)\n}\n\nconst value = getProperty(obj, 'foo.bar.baz')\n```\n\n> [!NOTE]\n> This assumes that you do not consume dot paths as user input.\n> If you do, ensure you sanitize keys before accessing them (e.g. through `Object.hasOwn`).\n",
  "event-stream.md": "---\ndescription: Modern alternatives to the event-stream package\n---\n\n# Replacements for `event-stream`\n\n## `es.pipeline()` replacement\n\n```ts\nimport es from 'event-stream' // [!code --]\nimport { pipeline } from 'node:stream/promises' // [!code ++]\n\nes.pipeline(s1, s2, s3) // [!code --]\nawait pipeline(s1, s2, s3) // [!code ++]\n```\n\n## `es.split()` replacement\n\n```ts\nimport es from 'event-stream' // [!code --]\nimport { createReadStream } from 'node:fs' // [!code ++]\nimport { createInterface } from 'node:readline' // [!code ++]\n\ncreateReadStream('file.txt').pipe(es.split()) // [!code --]\nconst lines = createInterface({ input: createReadStream('file.txt') }) // [!code ++]\n```\n\n## `es.map()` replacement\n\n<!-- prettier-ignore -->\n```ts\nimport es from 'event-stream' // [!code --]\n\nreadableStream.pipe( // [!code --]\n  es.map((data, cb) => cb(null, fn(data))) // [!code --]\n) // [!code --]\n\nreadableStream.map((data) => fn(data)) // [!code ++]\n```\n\n## `es.merge()` replacement\n\n```ts\nimport es from 'event-stream' // [!code --]\nimport { PassThrough } from 'node:stream' // [!code ++]\n\nes.merge([s1, s2]) // [!code --]\nconst combined = new PassThrough() // [!code ++]\ns1.pipe(combined, { end: false }) // [!code ++]\ns2.pipe(combined, { end: false }) // [!code ++]\n```\n",
  "rimraf.md": "---\ndescription: Native Node.js alternatives to the rimraf package for recursive directory removal\n---\n\n# Replacements for `rimraf`\n\n## `fs.rm` (native, Node.js)\n\nNode.js v14.14.0 and above provide a native alternative: [`fs.rm`](https://nodejs.org/api/fs.html#fspromisesrmpath-options) and [`fs.rmSync`](https://nodejs.org/api/fs.html#fsrmsyncpath-options). It supports recursive deletion and works as a direct replacement.\n\n### Async methods\n\n```ts\nimport rimraf from 'rimraf' // [!code --]\nimport { rm } from 'node:fs/promises' // [!code ++]\n\nawait rimraf('./dist') // [!code --]\nawait rm('./dist', { recursive: true, force: true }) // [!code ++]\n```\n\n### Sync methods\n\n```ts\nimport rimraf from 'rimraf' // [!code --]\nimport * as fs from 'node:fs' // [!code ++]\n\nrimraf.sync('./dist') // [!code --]\nfs.rmSync('./dist', { recursive: true, force: true }) // [!code ++]\n```\n\n> [!IMPORTANT]\n> Remember to set `{ recursive: true, force: true }` to match the behavior of rimraf.\n\n## `fs.rmdir` (native, Node.js before v14.14.0)\n\nIf you need to support Node.js 12 up to 14.13, you can use [`fs.rmdir`](https://nodejs.org/api/fs.html#fsrmdirpath-options-callback) with the recursive option. This was added in Node v12.10.0, though it’s deprecated as of Node v14.\n\n```ts\nimport rimraf from 'rimraf' // [!code --]\nimport { rmdir } from 'node:fs/promises' // [!code ++]\n\nawait rimraf('./dist') // [!code --]\nawait rmdir('./dist', { recursive: true }) // [!code ++]\n```\n\n## CLI usage\n\nTo replace `rimraf` inside npm scripts, you can run Node directly in eval mode:\n\n```sh\nnode -e \"require('fs').rmSync('./dist', { recursive: true, force: true, maxRetries: process.platform === 'win32' ? 10 : 0 })\"\n```\n\n## `premove`\n\nIf you are on an older Node.js version (before v12.10) or you specifically need a CLI replacement, you can use [`premove`](https://github.com/lukeed/premove). It provides both an API and a CLI and works on Node.js v8 and newer.\n\n```json\n{\n  \"scripts\": {\n    \"clean\": \"rimraf lib\", // [!code --]\n    \"clean\": \"premove lib\" // [!code ++]\n  }\n}\n```\n",
  "feather.md": "---\ndescription: Modern alternatives to feather packages\n---\n\n# Replacements for `feather-icons` and `react-feather`\n\n## `lucide` for `feather-icons`\n\n[`lucide`](https://lucide.dev/) is a community-maintained fork of Feather Icons.\n\nExample:\n\n<!-- prettier-ignore -->\n```js\nimport feather from 'feather-icons' // [!code --]\nimport { icons } from 'lucide' // [!code ++]\n\nfeather.icons.x.toSvg({ class: 'icon icon-x' }) // [!code --]\nicons.x.toSvg({ class: 'icon icon-x' }) // [!code ++]\n```\n\n## `lucide-react` for `react-feather`\n\n[`lucide-react`](https://lucide.dev/guide/packages/lucide-react) provides React components for the Lucide icon set and is the natural replacement for `react-feather`.\n\nExample:\n\n<!-- prettier-ignore -->\n```jsx\nimport { Camera, ArrowRight } from 'react-feather' // [!code --]\nimport { Camera, ArrowRight } from 'lucide-react' // [!code ++]\n\nexport function Header() {\n  return (\n    <>\n      <Camera size={18} />\n      <ArrowRight className=\"cta-icon\" />\n    </>\n  )\n}\n```\n\n> [!NOTE]\n> Lucide provides information on the [comparison](https://lucide.dev/guide/comparison#lucide-vs-feather-icons) of it vs feather-icons\n"
}