@e18e/mcp 0.0.1 → 0.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +146 -0
- package/dist/docs/docs.json +52 -52
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/index.test.js +164 -0
- package/dist/index.test.js.map +1 -0
- package/dist/prompts/task/index.js +1 -0
- package/dist/prompts/task/index.js.map +1 -1
- package/package.json +17 -4
package/README.md
ADDED
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
# `@e18e/mcp` (STDIO)
|
|
2
|
+
|
|
3
|
+
MCP server that flags inefficient or outdated npm packages and serves migration docs. It exposes tools for checking install commands or source files, a resource template with curated replacement guides, and a helper prompt for task-oriented workflows.
|
|
4
|
+
|
|
5
|
+
## Capabilities
|
|
6
|
+
|
|
7
|
+
- **Tools**
|
|
8
|
+
- `npm-i-checker`: Input an install command (`npm i`, `pnpm add`, `yarn add`, `bun i`). Returns `suggestions[]` for packages that have better-native or better-maintained alternatives.
|
|
9
|
+
- `code-checker`: Input a full source file (JS/TS/JSX/TSX). Parses imports and returns `suggestions[]` when a listed module should be replaced.
|
|
10
|
+
- **Resources**
|
|
11
|
+
- `replacement-docs` (template): URI `e18e://docs/{slug}`. Lists/reads text guides for migrating away from specific packages (auto-complete on slug; `list` returns all available docs).
|
|
12
|
+
- **Prompts**
|
|
13
|
+
- `task`: Returns a task-focused system prompt that reminds the model to run `npm-i-checker` for installs and `code-checker` on code before replying.
|
|
14
|
+
|
|
15
|
+
## Quick start
|
|
16
|
+
|
|
17
|
+
Prereqs: Node.js ≥18 and npm/pnpm (build uses pnpm, runtime works via `npx`/`pnpm dlx`/`bunx`).
|
|
18
|
+
|
|
19
|
+
The local (or stdio) version of the MCP server is available via the [`@e18e/mcp`](https://www.npmjs.com/package/@e18e/mcp) npm package. You can either install it globally and then reference it in your configuration or run it with `npx`:
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
npx -y @e18e/mcp
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Here's how to set it up in some common MCP clients:
|
|
26
|
+
|
|
27
|
+
## Claude Code
|
|
28
|
+
|
|
29
|
+
To include the local MCP version in Claude Code, simply run the following command:
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
claude mcp add -t stdio -s [scope] e18e -- npx -y @e18e/mcp
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
The `[scope]` must be `user`, `project` or `local`.
|
|
36
|
+
|
|
37
|
+
## Claude Desktop
|
|
38
|
+
|
|
39
|
+
In the Settings > Developer section, click on Edit Config. It will open the folder with a `claude_desktop_config.json` file in it. Edit the file to include the following configuration:
|
|
40
|
+
|
|
41
|
+
```json
|
|
42
|
+
{
|
|
43
|
+
"mcpServers": {
|
|
44
|
+
"e18e": {
|
|
45
|
+
"command": "npx",
|
|
46
|
+
"args": ["-y", "@e18e/mcp"]
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## Codex CLI
|
|
53
|
+
|
|
54
|
+
Add the following to your `config.toml` (which defaults to `~/.codex/config.toml`, but refer to [the configuration documentation](https://github.com/openai/codex/blob/main/docs/config.md) for more advanced setups):
|
|
55
|
+
|
|
56
|
+
```toml
|
|
57
|
+
[mcp_servers.e18e]
|
|
58
|
+
command = "npx"
|
|
59
|
+
args = ["-y", "@e18e/mcp"]
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## Gemini CLI
|
|
63
|
+
|
|
64
|
+
To include the local MCP version in Gemini CLI, simply run the following command:
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
gemini mcp add -t stdio -s [scope] e18e npx -y @e18e/mcp
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
The `[scope]` must be `user`, `project` or `local`.
|
|
71
|
+
|
|
72
|
+
## OpenCode
|
|
73
|
+
|
|
74
|
+
Run the command:
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
opencode mcp add
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
and follow the instructions, selecting 'Local' under the 'Select MCP server type' prompt:
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
opencode mcp add
|
|
84
|
+
|
|
85
|
+
┌ Add MCP server
|
|
86
|
+
│
|
|
87
|
+
◇ Enter MCP server name
|
|
88
|
+
│ e18e
|
|
89
|
+
│
|
|
90
|
+
◇ Select MCP server type
|
|
91
|
+
│ Local
|
|
92
|
+
│
|
|
93
|
+
◆ Enter command to run
|
|
94
|
+
│ npx -y @e18e/mcp
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
## VS Code
|
|
98
|
+
|
|
99
|
+
- Open the command palette
|
|
100
|
+
- Select "MCP: Add Server..."
|
|
101
|
+
- Select "Command (stdio)"
|
|
102
|
+
- Insert `npx -y @e18e/mcp` in the input and press `Enter`
|
|
103
|
+
- When prompted for a name, insert `e18e`
|
|
104
|
+
- Select if you want to add it as a `Global` or `Workspace` MCP server
|
|
105
|
+
|
|
106
|
+
## Cursor
|
|
107
|
+
|
|
108
|
+
- Open the command palette
|
|
109
|
+
- Select "View: Open MCP Settings"
|
|
110
|
+
- Click on "Add custom MCP"
|
|
111
|
+
|
|
112
|
+
It will open a file with your MCP servers where you can add the following configuration:
|
|
113
|
+
|
|
114
|
+
```json
|
|
115
|
+
{
|
|
116
|
+
"mcpServers": {
|
|
117
|
+
"e18e": {
|
|
118
|
+
"command": "npx",
|
|
119
|
+
"args": ["-y", "@e18e/mcp"]
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
## Zed
|
|
126
|
+
|
|
127
|
+
- Open the command palette
|
|
128
|
+
- Search and select "agent:open settings"
|
|
129
|
+
- In settings panel look for `Model Context Protocol (MCP) Servers`
|
|
130
|
+
- Click on "Add Server"
|
|
131
|
+
- Select: "Add Custom Server"
|
|
132
|
+
|
|
133
|
+
It will open a popup with MCP server config where you can add the following configuration:
|
|
134
|
+
|
|
135
|
+
```json
|
|
136
|
+
{
|
|
137
|
+
"e18e": {
|
|
138
|
+
"command": "npx",
|
|
139
|
+
"args": ["-y", "@e18e/mcp"]
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
## Other clients
|
|
145
|
+
|
|
146
|
+
If we didn't include the MCP client you are using, refer to their documentation for `stdio` servers and use `npx` as the command and `-y @e18e/mcp` as the arguments.
|
package/dist/docs/docs.json
CHANGED
|
@@ -1,69 +1,69 @@
|
|
|
1
1
|
{
|
|
2
|
-
"
|
|
3
|
-
"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",
|
|
2
|
+
"lint-staged.md": "---\ndescription: Modern alternatives to lint-staged for running commands on staged Git files\n---\n\n# Replacements for `lint-staged`\n\n## `nano-staged`\n\n[`nano-staged`](https://www.npmjs.com/package/nano-staged) is a tiny pre-commit runner for staged (and more) files; much smaller and faster than `lint-staged`, with a simple config.\n\npackage.json config:\n\n<!-- eslint-skip -->\n```json\n{\n \"lint-staged\": { // [!code --]\n \"nano-staged\": { // [!code ++]\n \"*.{js,ts}\": [\"prettier --write\"]\n },\n}\n```\n\n> [!NOTE]\n> Differences to be aware of:\n> - `lint-staged` has advanced features like backup stashing, partial-staging handling, per-directory configs in monorepos, and detailed concurrency controls.\n> - `nano-staged` focuses on simplicity and speed. If you rely on `lint-staged`’s stash/partial-staging features, keep using `lint-staged`.\n",
|
|
4
3
|
"axios.md": "---\ndescription: Modern alternatives to the axios package for making HTTP requests in browsers and Node.js\n---\n\n# Replacements for `axios`\n\n## Native `fetch` API\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 most HTTP requests, it can replace `axios` without extra dependencies.\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## `ky`\n\n[`ky`](https://github.com/sindresorhus/ky) is a lightweight HTTP client based on the Fetch API with timeout support, hooks (interceptors) and other helpers.\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\n## `ofetch`\n\n[`ofetch`](https://github.com/unjs/ofetch) s a fetch wrapper with automatic JSON parsing, request/response interceptors, and retries.\n\nExample:\n\n```ts\nimport { ofetch } from 'ofetch'\n\nconst api = ofetch.create({\n baseURL: 'https://api.example.com',\n})\n\nconst data = await api('/user', { query: { id: 123 } })\nconst created = await api('/items', { method: 'POST', body: { name: 'A' } })\n```\n",
|
|
4
|
+
"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",
|
|
5
5
|
"README.md": "# Module replacements\n\nThis is a list of all modules we suggest replacing, along with documentation\nto suggest alternatives.\n\n## ESLint plugin\n\nYou can set your project up to automatically warn on usage of these modules\nby using the\n[`eslint-plugin-depend`](https://github.com/es-tooling/eslint-plugin-depend)\nESLint plugin.\n\n## List of modules\n\n- [`@jsdevtools/ezspawn`](./ez-spawn.md)\n- [`axios`](./axios.md)\n- [`bluebird`](./bluebird-q.md)\n- [`body-parser`](./body-parser.md)\n- [`buf-compare`](./buf-compare.md)\n- [`buffer-equal`](./buffer-equal.md)\n- [`buffer-equals`](./buffer-equals.md)\n- [`builtin-modules`](./builtin-modules.md)\n- [`chalk`](./chalk.md)\n- [`core-util-is`](./core-util-is.md)\n- [`cpx`](./cpx.md)\n- [`crypto-js`](./crypto-js.md)\n- [`deep-equal`](./deep-equal.md)\n- [`depcheck`](./depcheck.md)\n- [`dot-prop`](./dot-prop.md)\n- [`dotenv`](./dotenv.md)\n- [`emoji-regex`](./emoji-regex.md)\n- [`eslint-plugin-es`](./eslint-plugin-es.md)\n- [`eslint-plugin-eslint-comments`](./eslint-plugin-eslint-comments.md)\n- [`eslint-plugin-import`](./eslint-plugin-import.md)\n- [`eslint-plugin-node`](./eslint-plugin-node.md)\n- [`eslint-plugin-react`](./eslint-plugin-react.md)\n- [`eslint-plugin-vitest`](./eslint-plugin-vitest.md)\n- [`execa`](./execa.md)\n- [`ezspawn`](./ez-spawn.md)\n- [`faker`](./faker.md)\n- [`fast-glob`](./fast-glob.md)\n- [`find-cache-dir`](./find-cache-dir.md)\n- [`find-cache-directory`](./find-cache-directory.md)\n- [`find-file-up`](./find-file-up.md)\n- [`find-pkg`](./find-pkg.md)\n- [`find-up`](./find-up.md)\n- [`fs-extra`](./fs-extra.md)\n- [`glob`](./glob.md)\n- [`globby`](./globby.md)\n- [`graphemer`](./graphemer.md)\n- [`invariant`](./invariant.md)\n- [`is-builtin-module`](./is-builtin-module.md)\n- [`jQuery`](./jquery.md)\n- [`js-yaml`](./js-yaml.md)\n- [`jsx-ast-utils`](./jsx-ast-utils.md)\n- [`lint-staged`](./lint-staged.md)\n- [`lodash`, `underscore` and related](./lodash-underscore.md)\n- [`materialize-css`](./materialize-css.md)\n- [`md5`](./md5.md)\n- [`mkdirp`](./mkdirp.md)\n- [`moment.js`](./moment.md)\n- [`npm-run-all`](./npm-run-all.md)\n- [`object-hash`](./object-hash.md)\n- [`ora`](./ora.md)\n- [`path-exists`](./path-exists.md)\n- [`pkg-dir`](./pkg-dir.md)\n- [`qs`](./qs.md)\n- [`read-pkg`](./read-pkg.md)\n- [`read-pkg-up`](./read-pkg-up.md)\n- [`read-package-up`](./read-package-up.md)\n- [`readable-stream`](./readable-stream.md)\n- [`rimraf`](./rimraf.md)\n- [`shortid`](./shortid.md)\n- [`sort-object`](./sort-object.md)\n- [`string-width`](./string-width.md)\n- [`strip-ansi`](./strip-ansi.md)\n- [`tempy`](./tempy.md)\n- [`traverse`](./traverse.md)\n- [`uri-js`](./uri-js.md)\n- [`utf8`](./utf8.md)\n- [`xmldom`](./xmldom.md)\n",
|
|
6
|
+
"js-yaml.md": "---\ndescription: Modern alternatives to js-yaml for YAML parsing and stringifying\n---\n\n# Replacements for `js-yaml`\n\n`js-yaml` appears to be unmaintained and has known spec-compliance issues.\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/blog/release-notes/bun-v1.2.21#native-yaml-support) is supported in Bun since v1.2.21.\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",
|
|
7
|
+
"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## Node.js (since 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",
|
|
8
|
+
"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",
|
|
9
|
+
"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",
|
|
10
|
+
"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, consider using a stronger algorithm where possible. If you must keep MD5 for compatibility, Node.js provides a native alternative via the `crypto` module.\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",
|
|
11
|
+
"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` method since Node 0.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",
|
|
12
|
+
"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## Node.js\n\nModern Node.js includes built-in fs and fs/promises APIs that cover what [`fs-extra`](https://github.com/jprichardson/node-fs-extra) historically provided. The table below maps `fs-extra` methods to Node.js APIs and highlights significant differences\n\n| fs-extra | node:fs | Notes |\n| --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------- |\n| [`copy(src, dest[, options])`](https://github.com/jprichardson/node-fs-extra/blob/master/docs/copy.md) | [`fsPromises.cp(src, dest[, options])`](https://nodejs.org/api/fs.html#fspromisescpsrc-dest-options) | If src is a file and dest is an existing directory, `fs-extra` throws; `fs.cp` copies into the directory. filter must be sync. |\n| [`copySync(src, dest[, options])`](https://github.com/jprichardson/node-fs-extra/blob/master/docs/copy-sync.md) | [`fs.cpSync(src, dest[, options])`](https://nodejs.org/api/fs.html#fscpsyncsrc-dest-options) | |\n| [`remove(path[, callback])`](https://github.com/jprichardson/node-fs-extra/blob/master/docs/remove.md) | [`fsPromises.rm(path[, options])`](https://nodejs.org/api/fs.html#fspromisesrmpath-options) | Use `force: true` to match fs-extra’s \"silently ignore missing path\". Consider `maxRetries`/`retryDelay`. |\n| [`removeSync(path)`](https://github.com/jprichardson/node-fs-extra/blob/master/docs/remove-sync.md) | [`fs.rmSync(path[, options])`](https://nodejs.org/api/fs.html#fsrmsyncpath-options) | |\n| [`mkdirs(dir[, options][, callback])`](https://github.com/jprichardson/node-fs-extra/blob/master/docs/ensureDir.md), [`mkdirp(...)`](https://github.com/jprichardson/node-fs-extra/blob/master/docs/ensureDir.md), [`ensureDir(...)`](https://github.com/jprichardson/node-fs-extra/blob/master/docs/ensureDir.md) | [`fsPromises.mkdir(path[, options])`](https://nodejs.org/api/fs.html#fspromisesmkdirpath-options) | Use `{ recursive: true }`. |\n| [`mkdirsSync(dir[, options])`](https://github.com/jprichardson/node-fs-extra/blob/master/docs/ensureDir-sync.md), [`mkdirpSync(...)`](https://github.com/jprichardson/node-fs-extra/blob/master/docs/ensureDir-sync.md), [`ensureDirSync(...)`](https://github.com/jprichardson/node-fs-extra/blob/master/docs/ensureDir-sync.md) | [`fs.mkdirSync(path[, options])`](https://nodejs.org/api/fs.html#fsmkdirsyncpath-options) | Use `{ recursive: true }`. |\n| [`pathExists(path)`](https://github.com/jprichardson/node-fs-extra/blob/master/docs/pathExists.md) | [`fsPromises.access(path[, mode])`](https://nodejs.org/api/fs.html#fspromisesaccesspath-mode) | Return `boolean` (wrap resolve/reject). |\n| [`pathExistsSync(path)`](https://github.com/jprichardson/node-fs-extra/blob/master/docs/pathExists-sync.md) | [`fs.existsSync(path)`](https://nodejs.org/api/fs.html#fsexistssyncpath) | |\n| [`outputFile(file, data[, options])`](https://github.com/jprichardson/node-fs-extra/blob/master/docs/outputFile.md) | [`fsPromises.mkdir(path[, options])`](https://nodejs.org/api/fs.html#fspromisesmkdirpath-options) + [`fsPromises.writeFile(file, data[, options])`](https://nodejs.org/api/fs.html#fspromiseswritefilefile-data-options) | Ensure parent directory. |\n| [`outputFileSync(file, data[, options])`](https://github.com/jprichardson/node-fs-extra/blob/master/docs/outputFile-sync.md) | [`fs.mkdirSync(path[, options])`](https://nodejs.org/api/fs.html#fsmkdirsyncpath-options) + [`fs.writeFileSync(file, data[, options])`](https://nodejs.org/api/fs.html#fswritefilesyncfile-data-options) | |\n| [`readJson(file[, options])`](https://github.com/jprichardson/node-fs-extra/blob/master/docs/readJson.md) | [`fsPromises.readFile(path[, options])`](https://nodejs.org/api/fs.html#fspromisesreadfilepath-options) | Use `JSON.parse`; ensure 'utf8'. fs-extra’s `throws:false` is not built-in. |\n| [`writeJson(file, obj[, options])`](https://github.com/jprichardson/node-fs-extra/blob/master/docs/writeJson.md) | [`fsPromises.writeFile(file, data[, options])`](https://nodejs.org/api/fs.html#fspromiseswritefilefile-data-options) | Use `JSON.stringify`; EOL option not built-in. |\n| [`outputJson(file, obj[, options])`](https://github.com/jprichardson/node-fs-extra/blob/master/docs/outputJson.md) | [`fsPromises.mkdir(path[, options])`](https://nodejs.org/api/fs.html#fspromisesmkdirpath-options) + [`fsPromises.writeFile(file, data[, options])`](https://nodejs.org/api/fs.html#fspromiseswritefilefile-data-options) | Ensure parent directory; EOL not built-in. |\n| [`ensureFile(file)`](https://github.com/jprichardson/node-fs-extra/blob/master/docs/ensureFile.md) / [`createFile(file)`](https://github.com/jprichardson/node-fs-extra/blob/master/docs/ensureFile.md) | [`fsPromises.mkdir(path[, options])`](https://nodejs.org/api/fs.html#fspromisesmkdirpath-options) + [`fsPromises.writeFile(file, data[, options])`](https://nodejs.org/api/fs.html#fspromiseswritefilefile-data-options) | Non-truncating create (e.g., `{ flag: 'a' }`). |\n| [`ensureFileSync(file)`](https://github.com/jprichardson/node-fs-extra/blob/master/docs/ensureFile-sync.md) / [`createFileSync(file)`](https://github.com/jprichardson/node-fs-extra/blob/master/docs/ensureFile-sync.md) | [`fs.mkdirSync(path[, options])`](https://nodejs.org/api/fs.html#fsmkdirsyncpath-options) + [`fs.writeFileSync(file, data[, options])`](https://nodejs.org/api/fs.html#fswritefilesyncfile-data-options) | |\n| [`ensureLink(src, dst)`](https://github.com/jprichardson/node-fs-extra/blob/master/docs/ensureLink.md) / [`createLink(src, dst)`](https://github.com/jprichardson/node-fs-extra/blob/master/docs/ensureLink.md) | [`fsPromises.mkdir(path[, options])`](https://nodejs.org/api/fs.html#fspromisesmkdirpath-options) + [`fsPromises.link(existingPath, newPath)`](https://nodejs.org/api/fs.html#fspromiseslinkexistingpath-newpath) | Same device only. |\n| [`ensureLinkSync(src, dst)`](https://github.com/jprichardson/node-fs-extra/blob/master/docs/ensureLink-sync.md) / [`createLinkSync(src, dst)`](https://github.com/jprichardson/node-fs-extra/blob/master/docs/ensureLink-sync.md) | [`fs.mkdirSync(path[, options])`](https://nodejs.org/api/fs.html#fsmkdirsyncpath-options) + [`fs.linkSync(existingPath, newPath)`](https://nodejs.org/api/fs.html#fslinksyncexistingpath-newpath) | |\n| [`ensureSymlink(src, dst[, type])`](https://github.com/jprichardson/node-fs-extra/blob/master/docs/ensureSymlink.md) / [`createSymlink(...)`](https://github.com/jprichardson/node-fs-extra/blob/master/docs/ensureSymlink.md) | [`fsPromises.mkdir(path[, options])`](https://nodejs.org/api/fs.html#fspromisesmkdirpath-options) + [`fsPromises.symlink(target, path[, type])`](https://nodejs.org/api/fs.html#fspromisessymlinktarget-path-type) | |\n| [`ensureSymlinkSync(src, dst[, type])`](https://github.com/jprichardson/node-fs-extra/blob/master/docs/ensureSymlink-sync.md) / [`createSymlinkSync(...)`](https://github.com/jprichardson/node-fs-extra/blob/master/docs/ensureSymlink-sync.md) | [`fs.mkdirSync(path[, options])`](https://nodejs.org/api/fs.html#fsmkdirsyncpath-options) + [`fs.symlinkSync(target, path[, type])`](https://nodejs.org/api/fs.html#fssymlinksynctarget-path-type) | |\n| [`emptyDir(dir)`](https://github.com/jprichardson/node-fs-extra/blob/master/docs/emptyDir.md) / `emptydir(dir)` | [`fsPromises.rm(path[, options])`](https://nodejs.org/api/fs.html#fspromisesrmpath-options) + [`fsPromises.mkdir(path[, options])`](https://nodejs.org/api/fs.html#fspromisesmkdirpath-options) | Preserves dir inode vs `rm`+`mkdir` (inode changes). |\n| [`move(src, dest[, options])`](https://github.com/jprichardson/node-fs-extra/blob/master/docs/move.md) | [`fsPromises.rename(oldPath, newPath)`](https://nodejs.org/api/fs.html#fspromisesrenameoldpath-newpath) | Not cross-device; add `cp` + `rm` fallback. No overwrite option - handle existing dest. |\n",
|
|
13
|
+
"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` 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)).map(b => b.toString(16).padStart(2, '0')).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",
|
|
14
|
+
"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",
|
|
15
|
+
"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## Node.js (since 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## Node.js 6.x to 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```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\n return builtinModules.includes(name) // [!code ++]\n} // [!code ++]\n\nisBuiltin('fs') // true [!code ++]\nisBuiltinModule('fs') // true [!code --]\n```\n",
|
|
16
|
+
"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",
|
|
17
|
+
"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## Native JavaScript `Date`\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```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",
|
|
18
|
+
"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",
|
|
19
|
+
"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 ? JSON.parse(await readFile(packageJsonPath, 'utf8')) : 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",
|
|
20
|
+
"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## Node.js (async)\n\nUse [`fs/promises.access`](https://nodejs.org/docs/latest/api/fs.html#fspromisesaccesspath-mode) and return a boolean.\n\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(() => true, () => false) // [!code ++]\n```\n\n## Node.js (sync)\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\nif (await pathExists('/etc/passwd')) // [!code --]\nif (existsSync('/etc/passwd')) // [!code ++]\n console.log('The path exists.')\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",
|
|
21
|
+
"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",
|
|
22
|
+
"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",
|
|
23
|
+
"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",
|
|
24
|
+
"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 ? JSON.parse(await readFile(packageJsonPath, 'utf8')) : 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",
|
|
25
|
+
"qs.md": "---\ndescription: Modern alternatives to the qs package for parsing and serializing query strings\n---\n\n# Replacements for `qs`\n\n## `URLSearchParams`\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://www.npmjs.com/package/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://www.npmjs.com/package/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```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({ user: { name: 'foo' }, tags: ['bar', 'baz'] }, { arrayFormat: 'brackets' }) // [!code --]\nconst str = stringify({ user: { name: 'foo' }, tags: ['bar', 'baz'] }, opts) // [!code ++]\n```\n\n## `neoqs`\n\n[`neoqs`](https://www.npmjs.com/package/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",
|
|
6
26
|
"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` 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",
|
|
7
|
-
"
|
|
27
|
+
"sort-object.md": "---\ndescription: Modern alternatives to the sort-object package for sorting object keys\n---\n\n# Replacements for `sort-object`\n\n## JavaScript APIs (`Object.keys` + `Array.sort`)\n\nFor simple cases:\n\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```ts\nimport sortObj from 'sort-object' // [!code --]\n\nconst sorted = sortObj(object, { 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\nconst sortBy = obj => Object.keys(obj).filter(k => obj[k].startsWith('a')).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://www.npmjs.com/package/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://www.npmjs.com/package/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",
|
|
28
|
+
"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://www.npmjs.com/package/readable-stream) mirrors Node’s core streams and works in browsers. In most cases, prefer native options.\n\n## Node.js (since 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## Streams API (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",
|
|
29
|
+
"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",
|
|
30
|
+
"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",
|
|
31
|
+
"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",
|
|
8
32
|
"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` method since Node 0.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",
|
|
9
|
-
"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```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",
|
|
10
|
-
"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",
|
|
11
|
-
"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 `node:crypto` (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",
|
|
12
|
-
"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` + `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## `object-path`\n\n[`object-path`](https://github.com/mariocasciaro/object-path) provides get/set/has/delete operations plus array methods like push, insert, and empty.\n\n```ts\nimport { deleteProperty, getProperty, hasProperty, setProperty } from 'dot-prop' // [!code --]\nimport objectPath from 'object-path' // [!code ++]\n\nconst value = getProperty(obj, 'foo.bar.baz') // [!code --]\nconst value = objectPath.get(obj, 'foo.bar.baz') // [!code ++]\n\nsetProperty(obj, 'foo.bar.baz', 'value') // [!code --]\nobjectPath.set(obj, 'foo.bar.baz', 'value') // [!code ++]\n\nconst exists = hasProperty(obj, 'foo.bar.baz') // [!code --]\nconst exists = objectPath.has(obj, 'foo.bar.baz') // [!code ++]\n\ndeleteProperty(obj, 'foo.bar.baz') // [!code --]\nobjectPath.del(obj, 'foo.bar.baz') // [!code ++]\n```\n",
|
|
13
33
|
"deep-equal.md": "---\ndescription: Modern alternatives to the deep-equal package for deep object comparison\n---\n\n# Replacements for `deep-equal`\n\n## `dequal`\n\n[`dequal`](https://www.npmjs.com/package/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## `fast-deep-equal`\n\n[`fast-deep-equal`](https://www.npmjs.com/package/fast-deep-equal) again has the same API shape.\n\nExample:\n\n```ts\nimport deepEqual from 'deep-equal' // [!code --]\nimport fastDeepEqual from 'fast-deep-equal' // [!code ++]\n\nconst a = { foo: 'bar' }\nconst b = { foo: 'bar' }\n\ndeepEqual(a, b) // true [!code --]\nfastDeepEqual(a, b) // true [!code ++]\n```\n\n> [!NOTE]\n> This library has a separate entrypoint for supporting Maps, Sets, etc. You can use `fast-deep-equal/es6` to support those types.\n",
|
|
14
|
-
"
|
|
34
|
+
"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",
|
|
15
35
|
"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",
|
|
16
|
-
"
|
|
36
|
+
"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## Node.js (since 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",
|
|
37
|
+
"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 `node:crypto` (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",
|
|
38
|
+
"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",
|
|
39
|
+
"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` + `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## `object-path`\n\n[`object-path`](https://github.com/mariocasciaro/object-path) provides get/set/has/delete operations plus array methods like push, insert, and empty.\n\n```ts\nimport { deleteProperty, getProperty, hasProperty, setProperty } from 'dot-prop' // [!code --]\nimport objectPath from 'object-path' // [!code ++]\n\nconst value = getProperty(obj, 'foo.bar.baz') // [!code --]\nconst value = objectPath.get(obj, 'foo.bar.baz') // [!code ++]\n\nsetProperty(obj, 'foo.bar.baz', 'value') // [!code --]\nobjectPath.set(obj, 'foo.bar.baz', 'value') // [!code ++]\n\nconst exists = hasProperty(obj, 'foo.bar.baz') // [!code --]\nconst exists = objectPath.has(obj, 'foo.bar.baz') // [!code ++]\n\ndeleteProperty(obj, 'foo.bar.baz') // [!code --]\nobjectPath.del(obj, 'foo.bar.baz') // [!code ++]\n```\n",
|
|
40
|
+
"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",
|
|
41
|
+
"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## 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",
|
|
42
|
+
"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",
|
|
17
43
|
"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## Node.js util\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```",
|
|
18
|
-
"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 faster module resolution via a Rust-based resolver, a smaller dependency footprint\n\n### Flat config\n\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': [createTypeScriptImportResolver(), createNodeResolver()], // [!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",
|
|
19
|
-
"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",
|
|
20
|
-
"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",
|
|
21
|
-
"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",
|
|
22
44
|
"dotenv.md": "---\ndescription: Modern alternatives to the dotenv package for loading and managing .env files in Node.js\n---\n\n# Replacements for `dotenv`\n\nAlthough dotenv is reliable, it may not be necessary or may lack certain features.\n\n## Node.js --env-file / --env-file-if-exists\n\nBuilt into Node.js (v20.6.0+; v22.9.0 for `--env-file-if-exists`). Zero dependencies—perfect for most apps that just need to load a `.env` at startup.\n\n[`--env-file`](https://nodejs.org/dist/latest-v20.x/docs/api/cli.html#--env-fileconfig) throws if the file is missing. If the file may be absent, use [`--env-file-if-exists`](https://nodejs.org/docs/latest-v22.x/api/cli.html#--env-file-if-existsconfig).\n\n```bash\nnode --env-file=.env index.js\n```\n\nAlso supported by:\n\n- [tsx](https://www.npmjs.com/package/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",
|
|
23
|
-
"
|
|
24
|
-
"
|
|
25
|
-
"
|
|
26
|
-
"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## Node.js\n\nModern Node.js includes built-in fs and fs/promises APIs that cover what [`fs-extra`](https://github.com/jprichardson/node-fs-extra) historically provided. The table below maps `fs-extra` methods to Node.js APIs and highlights significant differences\n\n| fs-extra | node:fs | Notes |\n| --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------- |\n| [`copy(src, dest[, options])`](https://github.com/jprichardson/node-fs-extra/blob/master/docs/copy.md) | [`fsPromises.cp(src, dest[, options])`](https://nodejs.org/api/fs.html#fspromisescpsrc-dest-options) | If src is a file and dest is an existing directory, `fs-extra` throws; `fs.cp` copies into the directory. filter must be sync. |\n| [`copySync(src, dest[, options])`](https://github.com/jprichardson/node-fs-extra/blob/master/docs/copy-sync.md) | [`fs.cpSync(src, dest[, options])`](https://nodejs.org/api/fs.html#fscpsyncsrc-dest-options) | |\n| [`remove(path[, callback])`](https://github.com/jprichardson/node-fs-extra/blob/master/docs/remove.md) | [`fsPromises.rm(path[, options])`](https://nodejs.org/api/fs.html#fspromisesrmpath-options) | Use `force: true` to match fs-extra’s \"silently ignore missing path\". Consider `maxRetries`/`retryDelay`. |\n| [`removeSync(path)`](https://github.com/jprichardson/node-fs-extra/blob/master/docs/remove-sync.md) | [`fs.rmSync(path[, options])`](https://nodejs.org/api/fs.html#fsrmsyncpath-options) | |\n| [`mkdirs(dir[, options][, callback])`](https://github.com/jprichardson/node-fs-extra/blob/master/docs/ensureDir.md), [`mkdirp(...)`](https://github.com/jprichardson/node-fs-extra/blob/master/docs/ensureDir.md), [`ensureDir(...)`](https://github.com/jprichardson/node-fs-extra/blob/master/docs/ensureDir.md) | [`fsPromises.mkdir(path[, options])`](https://nodejs.org/api/fs.html#fspromisesmkdirpath-options) | Use `{ recursive: true }`. |\n| [`mkdirsSync(dir[, options])`](https://github.com/jprichardson/node-fs-extra/blob/master/docs/ensureDir-sync.md), [`mkdirpSync(...)`](https://github.com/jprichardson/node-fs-extra/blob/master/docs/ensureDir-sync.md), [`ensureDirSync(...)`](https://github.com/jprichardson/node-fs-extra/blob/master/docs/ensureDir-sync.md) | [`fs.mkdirSync(path[, options])`](https://nodejs.org/api/fs.html#fsmkdirsyncpath-options) | Use `{ recursive: true }`. |\n| [`pathExists(path)`](https://github.com/jprichardson/node-fs-extra/blob/master/docs/pathExists.md) | [`fsPromises.access(path[, mode])`](https://nodejs.org/api/fs.html#fspromisesaccesspath-mode) | Return `boolean` (wrap resolve/reject). |\n| [`pathExistsSync(path)`](https://github.com/jprichardson/node-fs-extra/blob/master/docs/pathExists-sync.md) | [`fs.existsSync(path)`](https://nodejs.org/api/fs.html#fsexistssyncpath) | |\n| [`outputFile(file, data[, options])`](https://github.com/jprichardson/node-fs-extra/blob/master/docs/outputFile.md) | [`fsPromises.mkdir(path[, options])`](https://nodejs.org/api/fs.html#fspromisesmkdirpath-options) + [`fsPromises.writeFile(file, data[, options])`](https://nodejs.org/api/fs.html#fspromiseswritefilefile-data-options) | Ensure parent directory. |\n| [`outputFileSync(file, data[, options])`](https://github.com/jprichardson/node-fs-extra/blob/master/docs/outputFile-sync.md) | [`fs.mkdirSync(path[, options])`](https://nodejs.org/api/fs.html#fsmkdirsyncpath-options) + [`fs.writeFileSync(file, data[, options])`](https://nodejs.org/api/fs.html#fswritefilesyncfile-data-options) | |\n| [`readJson(file[, options])`](https://github.com/jprichardson/node-fs-extra/blob/master/docs/readJson.md) | [`fsPromises.readFile(path[, options])`](https://nodejs.org/api/fs.html#fspromisesreadfilepath-options) | Use `JSON.parse`; ensure 'utf8'. fs-extra’s `throws:false` is not built-in. |\n| [`writeJson(file, obj[, options])`](https://github.com/jprichardson/node-fs-extra/blob/master/docs/writeJson.md) | [`fsPromises.writeFile(file, data[, options])`](https://nodejs.org/api/fs.html#fspromiseswritefilefile-data-options) | Use `JSON.stringify`; EOL option not built-in. |\n| [`outputJson(file, obj[, options])`](https://github.com/jprichardson/node-fs-extra/blob/master/docs/outputJson.md) | [`fsPromises.mkdir(path[, options])`](https://nodejs.org/api/fs.html#fspromisesmkdirpath-options) + [`fsPromises.writeFile(file, data[, options])`](https://nodejs.org/api/fs.html#fspromiseswritefilefile-data-options) | Ensure parent directory; EOL not built-in. |\n| [`ensureFile(file)`](https://github.com/jprichardson/node-fs-extra/blob/master/docs/ensureFile.md) / [`createFile(file)`](https://github.com/jprichardson/node-fs-extra/blob/master/docs/ensureFile.md) | [`fsPromises.mkdir(path[, options])`](https://nodejs.org/api/fs.html#fspromisesmkdirpath-options) + [`fsPromises.writeFile(file, data[, options])`](https://nodejs.org/api/fs.html#fspromiseswritefilefile-data-options) | Non-truncating create (e.g., `{ flag: 'a' }`). |\n| [`ensureFileSync(file)`](https://github.com/jprichardson/node-fs-extra/blob/master/docs/ensureFile-sync.md) / [`createFileSync(file)`](https://github.com/jprichardson/node-fs-extra/blob/master/docs/ensureFile-sync.md) | [`fs.mkdirSync(path[, options])`](https://nodejs.org/api/fs.html#fsmkdirsyncpath-options) + [`fs.writeFileSync(file, data[, options])`](https://nodejs.org/api/fs.html#fswritefilesyncfile-data-options) | |\n| [`ensureLink(src, dst)`](https://github.com/jprichardson/node-fs-extra/blob/master/docs/ensureLink.md) / [`createLink(src, dst)`](https://github.com/jprichardson/node-fs-extra/blob/master/docs/ensureLink.md) | [`fsPromises.mkdir(path[, options])`](https://nodejs.org/api/fs.html#fspromisesmkdirpath-options) + [`fsPromises.link(existingPath, newPath)`](https://nodejs.org/api/fs.html#fspromiseslinkexistingpath-newpath) | Same device only. |\n| [`ensureLinkSync(src, dst)`](https://github.com/jprichardson/node-fs-extra/blob/master/docs/ensureLink-sync.md) / [`createLinkSync(src, dst)`](https://github.com/jprichardson/node-fs-extra/blob/master/docs/ensureLink-sync.md) | [`fs.mkdirSync(path[, options])`](https://nodejs.org/api/fs.html#fsmkdirsyncpath-options) + [`fs.linkSync(existingPath, newPath)`](https://nodejs.org/api/fs.html#fslinksyncexistingpath-newpath) | |\n| [`ensureSymlink(src, dst[, type])`](https://github.com/jprichardson/node-fs-extra/blob/master/docs/ensureSymlink.md) / [`createSymlink(...)`](https://github.com/jprichardson/node-fs-extra/blob/master/docs/ensureSymlink.md) | [`fsPromises.mkdir(path[, options])`](https://nodejs.org/api/fs.html#fspromisesmkdirpath-options) + [`fsPromises.symlink(target, path[, type])`](https://nodejs.org/api/fs.html#fspromisessymlinktarget-path-type) | |\n| [`ensureSymlinkSync(src, dst[, type])`](https://github.com/jprichardson/node-fs-extra/blob/master/docs/ensureSymlink-sync.md) / [`createSymlinkSync(...)`](https://github.com/jprichardson/node-fs-extra/blob/master/docs/ensureSymlink-sync.md) | [`fs.mkdirSync(path[, options])`](https://nodejs.org/api/fs.html#fsmkdirsyncpath-options) + [`fs.symlinkSync(target, path[, type])`](https://nodejs.org/api/fs.html#fssymlinksynctarget-path-type) | |\n| [`emptyDir(dir)`](https://github.com/jprichardson/node-fs-extra/blob/master/docs/emptyDir.md) / `emptydir(dir)` | [`fsPromises.rm(path[, options])`](https://nodejs.org/api/fs.html#fspromisesrmpath-options) + [`fsPromises.mkdir(path[, options])`](https://nodejs.org/api/fs.html#fspromisesmkdirpath-options) | Preserves dir inode vs `rm`+`mkdir` (inode changes). |\n| [`move(src, dest[, options])`](https://github.com/jprichardson/node-fs-extra/blob/master/docs/move.md) | [`fsPromises.rename(oldPath, newPath)`](https://nodejs.org/api/fs.html#fspromisesrenameoldpath-newpath) | Not cross-device; add `cp` + `rm` fallback. No overwrite option - handle existing dest. |\n",
|
|
27
|
-
"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",
|
|
45
|
+
"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\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 ++]\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> `styleText` does not support RGB and hex colors (e.g. `#EFEFEF` or `255, 239, 235`). 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- [`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",
|
|
46
|
+
"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```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",
|
|
47
|
+
"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",
|
|
48
|
+
"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 faster module resolution via a Rust-based resolver, a smaller dependency footprint\n\n### Flat config\n\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': [createTypeScriptImportResolver(), createNodeResolver()], // [!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",
|
|
28
49
|
"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://eslint-react.xyz/docs/migration) to map rules/options and automate changes where possible.\n",
|
|
50
|
+
"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",
|
|
51
|
+
"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",
|
|
52
|
+
"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",
|
|
29
53
|
"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```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",
|
|
30
|
-
"
|
|
31
|
-
"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",
|
|
32
|
-
"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",
|
|
33
|
-
"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",
|
|
34
|
-
"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 ? JSON.parse(await readFile(packageJsonPath, 'utf8')) : 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",
|
|
35
|
-
"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",
|
|
36
|
-
"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",
|
|
37
|
-
"lint-staged.md": "---\ndescription: Modern alternatives to lint-staged for running commands on staged Git files\n---\n\n# Replacements for `lint-staged`\n\n## `nano-staged`\n\n[`nano-staged`](https://www.npmjs.com/package/nano-staged) is a tiny pre-commit runner for staged (and more) files; much smaller and faster than `lint-staged`, with a simple config.\n\npackage.json config:\n\n<!-- eslint-skip -->\n```json\n{\n \"lint-staged\": { // [!code --]\n \"nano-staged\": { // [!code ++]\n \"*.{js,ts}\": [\"prettier --write\"]\n },\n}\n```\n\n> [!NOTE]\n> Differences to be aware of:\n> - `lint-staged` has advanced features like backup stashing, partial-staging handling, per-directory configs in monorepos, and detailed concurrency controls.\n> - `nano-staged` focuses on simplicity and speed. If you rely on `lint-staged`’s stash/partial-staging features, keep using `lint-staged`.\n",
|
|
54
|
+
"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",
|
|
38
55
|
"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```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",
|
|
39
|
-
"
|
|
40
|
-
"
|
|
41
|
-
"
|
|
42
|
-
"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` 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)).map(b => b.toString(16).padStart(2, '0')).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",
|
|
56
|
+
"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 (built-in)\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 (Node.js)\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\nconst text = \"€\";\nconst utf8Buffer = Buffer.from(text, 'utf8'); // Buffer of UTF-8 bytes\n```\n",
|
|
57
|
+
"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",
|
|
58
|
+
"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",
|
|
43
59
|
"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```",
|
|
44
|
-
"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",
|
|
45
|
-
"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",
|
|
46
|
-
"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```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## `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```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', { // [!code --]\n cwd, // [!code --]\n maxDepth: 6 // [!code --]\n}) // [!code --]\n```\n",
|
|
47
|
-
"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 ? JSON.parse(await readFile(packageJsonPath, 'utf8')) : 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",
|
|
48
|
-
"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",
|
|
49
|
-
"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://www.npmjs.com/package/readable-stream) mirrors Node’s core streams and works in browsers. In most cases, prefer native options.\n\n## Node.js (since 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## Streams API (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",
|
|
50
|
-
"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",
|
|
51
|
-
"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## Node.js (since 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## Node.js 6.x to 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```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\n return builtinModules.includes(name) // [!code ++]\n} // [!code ++]\n\nisBuiltin('fs') // true [!code ++]\nisBuiltinModule('fs') // true [!code --]\n```\n",
|
|
52
|
-
"qs.md": "---\ndescription: Modern alternatives to the qs package for parsing and serializing query strings\n---\n\n# Replacements for `qs`\n\n## `URLSearchParams`\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://www.npmjs.com/package/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://www.npmjs.com/package/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```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({ user: { name: 'foo' }, tags: ['bar', 'baz'] }, { arrayFormat: 'brackets' }) // [!code --]\nconst str = stringify({ user: { name: 'foo' }, tags: ['bar', 'baz'] }, opts) // [!code ++]\n```\n\n## `neoqs`\n\n[`neoqs`](https://www.npmjs.com/package/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",
|
|
53
|
-
"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",
|
|
54
|
-
"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",
|
|
55
60
|
"tempy.md": "---\ndescription: Modern alternatives to the tempy package for creating temporary files and directories\n---\n\n# Replacements for `tempy`\n\n## Node.js (since 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), available in v14.14.0 and up.\n\nExample:\n\n```ts\nimport { temporaryDirectory } from 'tempy' // [!code --]\nimport { mkdtemp, realpath } from 'node:fs/promises' // [!code ++]\nimport { join } from 'node:path' // [!code ++]\nimport { tmpdir } from 'node:os' // [!code ++]\n\nconst tempDir = temporaryDirectory() // [!code --]\nconst tempDir = await mkdtemp(join(await realpath(tmpdir()), 'foo-')) // [!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",
|
|
56
|
-
"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",
|
|
57
|
-
"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, consider using a stronger algorithm where possible. If you must keep MD5 for compatibility, Node.js provides a native alternative via the `crypto` module.\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",
|
|
58
|
-
"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",
|
|
59
|
-
"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 (built-in)\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 (Node.js)\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\nconst text = \"€\";\nconst utf8Buffer = Buffer.from(text, 'utf8'); // Buffer of UTF-8 bytes\n```\n",
|
|
60
|
-
"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",
|
|
61
|
-
"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## Native `URL`\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({ scheme: 'http', host: 'example.com', fragment: 'footer' })\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",
|
|
62
|
-
"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## Node.js (async)\n\nUse [`fs/promises.access`](https://nodejs.org/docs/latest/api/fs.html#fspromisesaccesspath-mode) and return a boolean.\n\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(() => true, () => false) // [!code ++]\n```\n\n## Node.js (sync)\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\nif (await pathExists('/etc/passwd')) // [!code --]\nif (existsSync('/etc/passwd')) // [!code ++]\n console.log('The path exists.')\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",
|
|
63
|
-
"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",
|
|
64
|
-
"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",
|
|
65
|
-
"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## Native JavaScript `Date`\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```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",
|
|
66
61
|
"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- `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### 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```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",
|
|
62
|
+
"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",
|
|
63
|
+
"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",
|
|
67
64
|
"rimraf.md": "---\ndescription: Native Node.js alternatives to the rimraf package for recursive directory removal\n---\n\n# Replacements for `rimraf`\n\n## Node.js\n\nNode.js v14.14.0 and above provide a native alternative: [`fs.rm`](https://nodejs.org/api/fs.html#fspromisesrmpath-options). It supports recursive deletion and works as a direct replacement.\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## 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",
|
|
68
|
-
"
|
|
65
|
+
"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## Native `URL`\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({ scheme: 'http', host: 'example.com', fragment: 'footer' })\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",
|
|
66
|
+
"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```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## `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```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', { // [!code --]\n cwd, // [!code --]\n maxDepth: 6 // [!code --]\n}) // [!code --]\n```\n",
|
|
67
|
+
"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",
|
|
68
|
+
"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```"
|
|
69
69
|
}
|
package/dist/index.js
CHANGED
|
@@ -4,7 +4,7 @@ import { ValibotJsonSchemaAdapter } from '@tmcp/adapter-valibot';
|
|
|
4
4
|
import { StdioTransport } from '@tmcp/transport-stdio';
|
|
5
5
|
import { setup_tools, setup_prompts, setup_resources } from './setup.js';
|
|
6
6
|
import { icons } from './icons/index.js';
|
|
7
|
-
const server = new McpServer({
|
|
7
|
+
export const server = new McpServer({
|
|
8
8
|
name: 'e18e-mcp',
|
|
9
9
|
version: '1.0.0',
|
|
10
10
|
icons,
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,SAAS,EAAE,MAAM,MAAM,CAAC;AACjC,OAAO,EAAE,wBAAwB,EAAE,MAAM,uBAAuB,CAAC;AACjE,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AACzE,OAAO,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AAEzC,MAAM,MAAM,GAAG,IAAI,SAAS,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,SAAS,EAAE,MAAM,MAAM,CAAC;AACjC,OAAO,EAAE,wBAAwB,EAAE,MAAM,uBAAuB,CAAC;AACjE,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AACzE,OAAO,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AAEzC,MAAM,CAAC,MAAM,MAAM,GAAG,IAAI,SAAS,CAClC;IACC,IAAI,EAAE,UAAU;IAChB,OAAO,EAAE,OAAO;IAChB,KAAK;IACL,WAAW,EACV,2IAA2I;CAC5I,EACD;IACC,OAAO,EAAE,IAAI,wBAAwB,EAAE;IACvC,YAAY,EAAE;QACb,KAAK,EAAE,EAAE,WAAW,EAAE,IAAI,EAAE;QAC5B,SAAS,EAAE,EAAE,WAAW,EAAE,IAAI,EAAE;QAChC,OAAO,EAAE,EAAE,WAAW,EAAE,IAAI,EAAE;QAC9B,WAAW,EAAE,EAAE;KACf;CACD,CACD,CAAC;AAIF,WAAW,CAAC,MAAM,CAAC,CAAC;AACpB,eAAe,CAAC,MAAM,CAAC,CAAC;AACxB,aAAa,CAAC,MAAM,CAAC,CAAC;AAEtB,MAAM,eAAe,GAAG,IAAI,cAAc,CAAC,MAAM,CAAC,CAAC;AACnD,eAAe,CAAC,MAAM,EAAE,CAAC"}
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
import { InMemoryTransport, Session } from '@tmcp/transport-in-memory';
|
|
2
|
+
import { beforeEach, describe, expect, it } from 'vitest';
|
|
3
|
+
import { server } from './index.js';
|
|
4
|
+
let transport;
|
|
5
|
+
let session;
|
|
6
|
+
beforeEach(async () => {
|
|
7
|
+
transport = new InMemoryTransport(server);
|
|
8
|
+
session = transport.session();
|
|
9
|
+
await session.initialize('2025-11-25', {}, {
|
|
10
|
+
name: 'test-client',
|
|
11
|
+
version: '1.0.0',
|
|
12
|
+
});
|
|
13
|
+
});
|
|
14
|
+
describe('npm-i-checker', () => {
|
|
15
|
+
it('is an available tool', async () => {
|
|
16
|
+
const tools = await session.listTools();
|
|
17
|
+
expect(tools.tools).toContainEqual(expect.objectContaining({
|
|
18
|
+
name: 'npm-i-checker',
|
|
19
|
+
}));
|
|
20
|
+
});
|
|
21
|
+
describe.each(['npm', 'pnpm', 'yarn', 'bun'])('using "%s" package manager', (package_manager) => {
|
|
22
|
+
// these are all the alias of `npm i`...they technically don't work with other package managers but we literally don't even check this
|
|
23
|
+
// the test is just here to make sure that we at least support all the common aliases of `install`
|
|
24
|
+
describe.each([
|
|
25
|
+
'add',
|
|
26
|
+
'a',
|
|
27
|
+
'i',
|
|
28
|
+
'in',
|
|
29
|
+
'ins',
|
|
30
|
+
'inst',
|
|
31
|
+
'insta',
|
|
32
|
+
'instal',
|
|
33
|
+
'isnt',
|
|
34
|
+
'isnta',
|
|
35
|
+
'isntal',
|
|
36
|
+
'isntall',
|
|
37
|
+
])('using alias "%s"', (alias) => {
|
|
38
|
+
it("doesn't suggest anything for packages that don't have replacements", async () => {
|
|
39
|
+
const tool = await session.callTool('npm-i-checker', {
|
|
40
|
+
command: `${package_manager} ${alias} svelte`,
|
|
41
|
+
});
|
|
42
|
+
expect(tool.structuredContent).toEqual({
|
|
43
|
+
suggestions: [],
|
|
44
|
+
});
|
|
45
|
+
});
|
|
46
|
+
it('provides documentations for packages that have package replacements', async () => {
|
|
47
|
+
const tool = await session.callTool('npm-i-checker', {
|
|
48
|
+
command: `${package_manager} ${alias} chalk`,
|
|
49
|
+
});
|
|
50
|
+
expect(tool.structuredContent).toEqual({
|
|
51
|
+
suggestions: [
|
|
52
|
+
expect.stringContaining("Don't use `chalk` instead read the following document:"),
|
|
53
|
+
],
|
|
54
|
+
});
|
|
55
|
+
});
|
|
56
|
+
it("provides documentations for packages that have package replacements even if there's a flag after", async () => {
|
|
57
|
+
const tool = await session.callTool('npm-i-checker', {
|
|
58
|
+
command: `${package_manager} ${alias} chalk --save-dev`,
|
|
59
|
+
});
|
|
60
|
+
expect(tool.structuredContent).toEqual({
|
|
61
|
+
suggestions: [
|
|
62
|
+
expect.stringContaining("Don't use `chalk` instead read the following document:"),
|
|
63
|
+
],
|
|
64
|
+
});
|
|
65
|
+
});
|
|
66
|
+
it("provides documentations for packages that have package replacements even if there's a flag before", async () => {
|
|
67
|
+
const tool = await session.callTool('npm-i-checker', {
|
|
68
|
+
command: `${package_manager} ${alias} -w chalk`,
|
|
69
|
+
});
|
|
70
|
+
expect(tool.structuredContent).toEqual({
|
|
71
|
+
suggestions: [
|
|
72
|
+
expect.stringContaining("Don't use `chalk` instead read the following document:"),
|
|
73
|
+
],
|
|
74
|
+
});
|
|
75
|
+
});
|
|
76
|
+
it('provides documentations for packages that have micro utilities replacements', async () => {
|
|
77
|
+
const tool = await session.callTool('npm-i-checker', {
|
|
78
|
+
command: `${package_manager} ${alias} arr-diff`,
|
|
79
|
+
});
|
|
80
|
+
expect(tool.structuredContent).toEqual({
|
|
81
|
+
suggestions: [
|
|
82
|
+
"Don't use `arr-diff` instead Use a.filter((item) => !b.includes(item))",
|
|
83
|
+
],
|
|
84
|
+
});
|
|
85
|
+
});
|
|
86
|
+
it('provides documentations for multiple packages skipping the non problematic one', async () => {
|
|
87
|
+
const tool = await session.callTool('npm-i-checker', {
|
|
88
|
+
command: `${package_manager} ${alias} arr-diff svelte chalk`,
|
|
89
|
+
});
|
|
90
|
+
expect(tool.structuredContent).toEqual({
|
|
91
|
+
suggestions: [
|
|
92
|
+
"Don't use `arr-diff` instead Use a.filter((item) => !b.includes(item))",
|
|
93
|
+
expect.stringContaining("Don't use `chalk` instead read the following document:"),
|
|
94
|
+
],
|
|
95
|
+
});
|
|
96
|
+
});
|
|
97
|
+
});
|
|
98
|
+
});
|
|
99
|
+
});
|
|
100
|
+
describe('code-checker', () => {
|
|
101
|
+
it('is an available tool', async () => {
|
|
102
|
+
const tools = await session.listTools();
|
|
103
|
+
expect(tools.tools).toContainEqual(expect.objectContaining({
|
|
104
|
+
name: 'code-checker',
|
|
105
|
+
}));
|
|
106
|
+
});
|
|
107
|
+
it("doesn't suggest anything for packages that don't have replacements", async () => {
|
|
108
|
+
const tool = await session.callTool('code-checker', {
|
|
109
|
+
code: `import { onMount } from 'svelte';`,
|
|
110
|
+
});
|
|
111
|
+
expect(tool.structuredContent).toEqual({
|
|
112
|
+
suggestions: [],
|
|
113
|
+
});
|
|
114
|
+
});
|
|
115
|
+
it('provides documentations for packages that have package replacements', async () => {
|
|
116
|
+
const tool = await session.callTool('code-checker', {
|
|
117
|
+
code: `import chalk from 'chalk';
|
|
118
|
+
|
|
119
|
+
console.log(chalk.blue('Hello world!'));`,
|
|
120
|
+
});
|
|
121
|
+
expect(tool.structuredContent).toEqual({
|
|
122
|
+
suggestions: [
|
|
123
|
+
expect.stringContaining("Don't use `chalk` instead read the following document:"),
|
|
124
|
+
],
|
|
125
|
+
});
|
|
126
|
+
});
|
|
127
|
+
it('provides documentations for packages that have micro utilities replacements', async () => {
|
|
128
|
+
const tool = await session.callTool('code-checker', {
|
|
129
|
+
code: `import diff from 'arr-diff';
|
|
130
|
+
|
|
131
|
+
const a = ['a', 'b', 'c', 'd'];
|
|
132
|
+
const b = ['b', 'c'];
|
|
133
|
+
|
|
134
|
+
console.log(diff(a, b));`,
|
|
135
|
+
});
|
|
136
|
+
expect(tool.structuredContent).toEqual({
|
|
137
|
+
suggestions: [
|
|
138
|
+
"Don't use `arr-diff` instead Use a.filter((item) => !b.includes(item))",
|
|
139
|
+
],
|
|
140
|
+
});
|
|
141
|
+
});
|
|
142
|
+
it('provides documentations for multiple packages skipping the non problematic one', async () => {
|
|
143
|
+
const tool = await session.callTool('code-checker', {
|
|
144
|
+
code: `import chalk from 'chalk';
|
|
145
|
+
import { onMount } from 'svelte';
|
|
146
|
+
import diff from 'arr-diff';
|
|
147
|
+
|
|
148
|
+
onMount(()=>{
|
|
149
|
+
const a = ['a', 'b', 'c', 'd'];
|
|
150
|
+
const b = ['b', 'c'];
|
|
151
|
+
|
|
152
|
+
console.log(diff(a, b));
|
|
153
|
+
});
|
|
154
|
+
`,
|
|
155
|
+
});
|
|
156
|
+
expect(tool.structuredContent).toEqual({
|
|
157
|
+
suggestions: [
|
|
158
|
+
expect.stringContaining("Don't use `chalk` instead read the following document:"),
|
|
159
|
+
"Don't use `arr-diff` instead Use a.filter((item) => !b.includes(item))",
|
|
160
|
+
],
|
|
161
|
+
});
|
|
162
|
+
});
|
|
163
|
+
});
|
|
164
|
+
//# sourceMappingURL=index.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.test.js","sourceRoot":"","sources":["../src/index.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,OAAO,EAAE,MAAM,2BAA2B,CAAC;AACvE,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAC1D,OAAO,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AAEpC,IAAI,SAA4B,CAAC;AACjC,IAAI,OAAgB,CAAC;AAErB,UAAU,CAAC,KAAK,IAAI,EAAE,CAAC;IACtB,SAAS,GAAG,IAAI,iBAAiB,CAAC,MAAM,CAAC,CAAC;IAC1C,OAAO,GAAG,SAAS,CAAC,OAAO,EAAE,CAAC;IAC9B,MAAM,OAAO,CAAC,UAAU,CACvB,YAAY,EACZ,EAAE,EACF;QACC,IAAI,EAAE,aAAa;QACnB,OAAO,EAAE,OAAO;KAChB,CACD,CAAC;AAAA,CACF,CAAC,CAAC;AAEH,QAAQ,CAAC,eAAe,EAAE,GAAG,EAAE,CAAC;IAC/B,EAAE,CAAC,sBAAsB,EAAE,KAAK,IAAI,EAAE,CAAC;QACtC,MAAM,KAAK,GAAG,MAAM,OAAO,CAAC,SAAS,EAAE,CAAC;QACxC,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,cAAc,CACjC,MAAM,CAAC,gBAAgB,CAAC;YACvB,IAAI,EAAE,eAAe;SACrB,CAAC,CACF,CAAC;IAAA,CACF,CAAC,CAAC;IAEH,QAAQ,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,CAAC,CAAC,CAC5C,4BAA4B,EAC5B,CAAC,eAAe,EAAE,EAAE,CAAC;QACpB,sIAAsI;QACtI,kGAAkG;QAClG,QAAQ,CAAC,IAAI,CAAC;YACb,KAAK;YACL,GAAG;YACH,GAAG;YACH,IAAI;YACJ,KAAK;YACL,MAAM;YACN,OAAO;YACP,QAAQ;YACR,MAAM;YACN,OAAO;YACP,QAAQ;YACR,SAAS;SACT,CAAC,CAAC,kBAAkB,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC;YACjC,EAAE,CAAC,oEAAoE,EAAE,KAAK,IAAI,EAAE,CAAC;gBACpF,MAAM,IAAI,GAAG,MAAM,OAAO,CAAC,QAAQ,CAAC,eAAe,EAAE;oBACpD,OAAO,EAAE,GAAG,eAAe,IAAI,KAAK,SAAS;iBAC7C,CAAC,CAAC;gBAEH,MAAM,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAC,OAAO,CAAC;oBACtC,WAAW,EAAE,EAAE;iBACf,CAAC,CAAC;YAAA,CACH,CAAC,CAAC;YAEH,EAAE,CAAC,qEAAqE,EAAE,KAAK,IAAI,EAAE,CAAC;gBACrF,MAAM,IAAI,GAAG,MAAM,OAAO,CAAC,QAAQ,CAAC,eAAe,EAAE;oBACpD,OAAO,EAAE,GAAG,eAAe,IAAI,KAAK,QAAQ;iBAC5C,CAAC,CAAC;gBAEH,MAAM,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAC,OAAO,CAAC;oBACtC,WAAW,EAAE;wBACZ,MAAM,CAAC,gBAAgB,CACtB,wDAAwD,CACxD;qBACD;iBACD,CAAC,CAAC;YAAA,CACH,CAAC,CAAC;YAEH,EAAE,CAAC,kGAAkG,EAAE,KAAK,IAAI,EAAE,CAAC;gBAClH,MAAM,IAAI,GAAG,MAAM,OAAO,CAAC,QAAQ,CAAC,eAAe,EAAE;oBACpD,OAAO,EAAE,GAAG,eAAe,IAAI,KAAK,mBAAmB;iBACvD,CAAC,CAAC;gBAEH,MAAM,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAC,OAAO,CAAC;oBACtC,WAAW,EAAE;wBACZ,MAAM,CAAC,gBAAgB,CACtB,wDAAwD,CACxD;qBACD;iBACD,CAAC,CAAC;YAAA,CACH,CAAC,CAAC;YAEH,EAAE,CAAC,mGAAmG,EAAE,KAAK,IAAI,EAAE,CAAC;gBACnH,MAAM,IAAI,GAAG,MAAM,OAAO,CAAC,QAAQ,CAAC,eAAe,EAAE;oBACpD,OAAO,EAAE,GAAG,eAAe,IAAI,KAAK,WAAW;iBAC/C,CAAC,CAAC;gBAEH,MAAM,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAC,OAAO,CAAC;oBACtC,WAAW,EAAE;wBACZ,MAAM,CAAC,gBAAgB,CACtB,wDAAwD,CACxD;qBACD;iBACD,CAAC,CAAC;YAAA,CACH,CAAC,CAAC;YAEH,EAAE,CAAC,6EAA6E,EAAE,KAAK,IAAI,EAAE,CAAC;gBAC7F,MAAM,IAAI,GAAG,MAAM,OAAO,CAAC,QAAQ,CAAC,eAAe,EAAE;oBACpD,OAAO,EAAE,GAAG,eAAe,IAAI,KAAK,WAAW;iBAC/C,CAAC,CAAC;gBAEH,MAAM,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAC,OAAO,CAAC;oBACtC,WAAW,EAAE;wBACZ,wEAAwE;qBACxE;iBACD,CAAC,CAAC;YAAA,CACH,CAAC,CAAC;YAEH,EAAE,CAAC,gFAAgF,EAAE,KAAK,IAAI,EAAE,CAAC;gBAChG,MAAM,IAAI,GAAG,MAAM,OAAO,CAAC,QAAQ,CAAC,eAAe,EAAE;oBACpD,OAAO,EAAE,GAAG,eAAe,IAAI,KAAK,wBAAwB;iBAC5D,CAAC,CAAC;gBAEH,MAAM,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAC,OAAO,CAAC;oBACtC,WAAW,EAAE;wBACZ,wEAAwE;wBACxE,MAAM,CAAC,gBAAgB,CACtB,wDAAwD,CACxD;qBACD;iBACD,CAAC,CAAC;YAAA,CACH,CAAC,CAAC;QAAA,CACH,CAAC,CAAC;IAAA,CACH,CACD,CAAC;AAAA,CACF,CAAC,CAAC;AAEH,QAAQ,CAAC,cAAc,EAAE,GAAG,EAAE,CAAC;IAC9B,EAAE,CAAC,sBAAsB,EAAE,KAAK,IAAI,EAAE,CAAC;QACtC,MAAM,KAAK,GAAG,MAAM,OAAO,CAAC,SAAS,EAAE,CAAC;QACxC,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,cAAc,CACjC,MAAM,CAAC,gBAAgB,CAAC;YACvB,IAAI,EAAE,cAAc;SACpB,CAAC,CACF,CAAC;IAAA,CACF,CAAC,CAAC;IAEH,EAAE,CAAC,oEAAoE,EAAE,KAAK,IAAI,EAAE,CAAC;QACpF,MAAM,IAAI,GAAG,MAAM,OAAO,CAAC,QAAQ,CAAC,cAAc,EAAE;YACnD,IAAI,EAAE,mCAAmC;SACzC,CAAC,CAAC;QAEH,MAAM,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAC,OAAO,CAAC;YACtC,WAAW,EAAE,EAAE;SACf,CAAC,CAAC;IAAA,CACH,CAAC,CAAC;IAEH,EAAE,CAAC,qEAAqE,EAAE,KAAK,IAAI,EAAE,CAAC;QACrF,MAAM,IAAI,GAAG,MAAM,OAAO,CAAC,QAAQ,CAAC,cAAc,EAAE;YACnD,IAAI,EAAE;;yCAEgC;SACtC,CAAC,CAAC;QAEH,MAAM,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAC,OAAO,CAAC;YACtC,WAAW,EAAE;gBACZ,MAAM,CAAC,gBAAgB,CACtB,wDAAwD,CACxD;aACD;SACD,CAAC,CAAC;IAAA,CACH,CAAC,CAAC;IAEH,EAAE,CAAC,6EAA6E,EAAE,KAAK,IAAI,EAAE,CAAC;QAC7F,MAAM,IAAI,GAAG,MAAM,OAAO,CAAC,QAAQ,CAAC,cAAc,EAAE;YACnD,IAAI,EAAE;;;;;yBAKgB;SACtB,CAAC,CAAC;QAEH,MAAM,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAC,OAAO,CAAC;YACtC,WAAW,EAAE;gBACZ,wEAAwE;aACxE;SACD,CAAC,CAAC;IAAA,CACH,CAAC,CAAC;IAEH,EAAE,CAAC,gFAAgF,EAAE,KAAK,IAAI,EAAE,CAAC;QAChG,MAAM,IAAI,GAAG,MAAM,OAAO,CAAC,QAAQ,CAAC,cAAc,EAAE;YACnD,IAAI,EAAE;;;;;;;;;;CAUR;SACE,CAAC,CAAC;QAEH,MAAM,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAC,OAAO,CAAC;YACtC,WAAW,EAAE;gBACZ,MAAM,CAAC,gBAAgB,CACtB,wDAAwD,CACxD;gBACD,wEAAwE;aACxE;SACD,CAAC,CAAC;IAAA,CACH,CAAC,CAAC;AAAA,CACH,CAAC,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/prompts/task/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,CAAC,MAAM,SAAS,CAAC;AAC7B,OAAO,EAAE,KAAK,EAAE,MAAM,sBAAsB,CAAC;AAC7C,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AAE9C,MAAM,UAAU,IAAI,CAAC,MAAqB,EAAE;IAC3C,MAAM,CAAC,MAAM,CACZ;QACC,IAAI,EAAE,MAAM;QACZ,WAAW,EACV,oFAAoF;QACrF,KAAK;QACL,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC;YAChB,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE;SAChB,CAAC;QACF,QAAQ,EAAE;YACT,IAAI,GAAG;gBACN,yCAAyC;gBACzC,OAAO,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;YAAA,CAC3B;SACD;KACD,EACD,KAAK,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC;QACnB,OAAO,MAAM,CAAC,IAAI,CACjB;;;;gBAIY,IAAI,EAAE,CAClB,CAAC;IAAA,CACF,CACD,CAAC;AAAA,CACF"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/prompts/task/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,CAAC,MAAM,SAAS,CAAC;AAC7B,OAAO,EAAE,KAAK,EAAE,MAAM,sBAAsB,CAAC;AAC7C,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AAE9C,MAAM,UAAU,IAAI,CAAC,MAAqB,EAAE;IAC3C,MAAM,CAAC,MAAM,CACZ;QACC,IAAI,EAAE,MAAM;QACZ,KAAK,EAAE,WAAW;QAClB,WAAW,EACV,oFAAoF;QACrF,KAAK;QACL,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC;YAChB,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE;SAChB,CAAC;QACF,QAAQ,EAAE;YACT,IAAI,GAAG;gBACN,yCAAyC;gBACzC,OAAO,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;YAAA,CAC3B;SACD;KACD,EACD,KAAK,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC;QACnB,OAAO,MAAM,CAAC,IAAI,CACjB;;;;gBAIY,IAAI,EAAE,CAClB,CAAC;IAAA,CACF,CACD,CAAC;AAAA,CACF"}
|
package/package.json
CHANGED
|
@@ -1,12 +1,23 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@e18e/mcp",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.3",
|
|
4
4
|
"publishConfig": {
|
|
5
5
|
"access": "public"
|
|
6
6
|
},
|
|
7
7
|
"description": "The official e18e MCP server for advising agents on modern and performant best practices",
|
|
8
8
|
"type": "module",
|
|
9
|
-
"bin":
|
|
9
|
+
"bin": {
|
|
10
|
+
"e18e-mcp": "dist/index.js"
|
|
11
|
+
},
|
|
12
|
+
"homepage": "https://github.com/e18e/mcp#readme",
|
|
13
|
+
"bugs": {
|
|
14
|
+
"url": "https://github.com/e18e/mcp/issues"
|
|
15
|
+
},
|
|
16
|
+
"repository": {
|
|
17
|
+
"type": "git",
|
|
18
|
+
"url": "git+https://github.com/e18e/mcp.git",
|
|
19
|
+
"path": "packages/stdio"
|
|
20
|
+
},
|
|
10
21
|
"files": [
|
|
11
22
|
"./dist"
|
|
12
23
|
],
|
|
@@ -22,7 +33,8 @@
|
|
|
22
33
|
"es-module-lexer": "^1.7.0",
|
|
23
34
|
"module-replacements": "^2.10.1",
|
|
24
35
|
"tmcp": "^1.18.1",
|
|
25
|
-
"valibot": "^1.2.0"
|
|
36
|
+
"valibot": "^1.2.0",
|
|
37
|
+
"vitest": "^4.0.14"
|
|
26
38
|
},
|
|
27
39
|
"devDependencies": {
|
|
28
40
|
"@modelcontextprotocol/inspector": "^0.17.2",
|
|
@@ -36,6 +48,7 @@
|
|
|
36
48
|
"generate:docs:dev": "node ./scripts/fetch-docs.ts dev",
|
|
37
49
|
"generate:docs": "node ./scripts/fetch-docs.ts",
|
|
38
50
|
"inspect": "pnpm build && mcp-inspector --transport stdio node ./dist/index.js",
|
|
39
|
-
"update:version": "node scripts/update-version.ts"
|
|
51
|
+
"update:version": "node scripts/update-version.ts",
|
|
52
|
+
"test": "vitest"
|
|
40
53
|
}
|
|
41
54
|
}
|