[READ-ONLY] Mirror of https://github.com/bombshell-dev/tools. Internal CLI to standardize tooling across all Bombshell projects
0

Configure Feed

Select the types of activity you want to include in your feed.

feat(lint): update deps, build (tsdown), lint (oxlint, publint, tsdown)

Nate Moore (Mar 15, 2026, 9:44 PM EDT) df41c2ba 4d3bde25

+1291 -118
+197
conventions.md
··· 1 + ### Formatter settings (from `biome.jsonc`) 2 + 3 + - **Indent style:** Tabs (not spaces) for code files. 4 + - **Indent width:** 2. 5 + - **Line width:** 100 characters. 6 + - **Trailing commas:** `all` — always include trailing commas. 7 + - **Quote style:** Single quotes (`'`). 8 + - **Semicolons:** Always. 9 + - **Expand:** `auto` — Biome auto-decides when to expand arrays/objects. 10 + 11 + --- 12 + 13 + ## 2. Import Conventions 14 + 15 + ### Always use the `node:` protocol for Node.js builtins 16 + 17 + Enforced as an **error** via Biome's `useNodejsImportProtocol` rule. Every import of a Node built-in must use the `node:` prefix: 18 + 19 + ```ts 20 + // ✅ Correct 21 + import fs from "node:fs"; 22 + import path from "node:path"; 23 + 24 + // ❌ Wrong — will fail lint 25 + import fs from "fs"; 26 + import path from "path"; 27 + ``` 28 + 29 + ### Enforce `import type` for type-only imports 30 + 31 + Enforced as an **error** via Biome's `useImportType` rule. If an import is only used as a type (not at runtime), it must use `import type`: 32 + 33 + ```ts 34 + // ✅ Correct 35 + import type { AstroConfig } from "../types/public/config.js"; 36 + import { getFileInfo } from "../vite-plugin-utils/index.js"; 37 + 38 + // ❌ Wrong — pulls runtime code for a type-only import 39 + import { AstroConfig } from "../types/public/config.js"; 40 + ``` 41 + 42 + ### Import organization 43 + 44 + Biome's `organizeImports` assist is set to `"on"`, which auto-sorts and groups imports. 45 + 46 + --- 47 + 48 + ## 3. Restricted Use of Node.js APIs 49 + 50 + This is a foundational architectural constraint. Astro code may run in non-Node environments like **Bun** or **Cloudflare Workers**, so Node.js API usage is strictly limited to specific areas of the codebase. 51 + 52 + ### Rules 53 + 54 + - **Runtime-agnostic code** (code that shouldn't use Node.js APIs) should be placed inside folders or files called `runtime` (`runtime/` or `runtime.ts`). 55 + - **Vite plugin implementations** may use Node.js APIs, but if a Vite plugin returns a **virtual module**, that virtual module cannot use Node.js APIs. 56 + - Prefer **`URL` and file URL APIs** (`new URL(...)`, `import.meta.url`) over `node:path` string manipulation where possible. The codebase favors URL-based path handling for cross-runtime compatibility. 57 + 58 + --- 59 + 60 + ## 4. Function Signature Conventions 61 + 62 + ### Options bag pattern 63 + 64 + Functions with more than two parameters are collapsed into an **options object**. This is a pervasive pattern throughout the codebase: 65 + 66 + ```ts 67 + // ✅ Correct — options bag with a typed interface 68 + interface CompileAstroOption { 69 + compileProps: CompileProps; 70 + astroFileToCompileMetadata: Map<string, CompileMetadata>; 71 + logger: Logger; 72 + } 73 + 74 + export async function compileAstro({ 75 + compileProps, 76 + astroFileToCompileMetadata, 77 + logger, 78 + }: CompileAstroOption): Promise<CompileAstroResult> { ... } 79 + ``` 80 + 81 + ```ts 82 + // ✅ Also correct — inline destructuring for simpler cases 83 + export async function generateHydrateScript( 84 + scriptOptions: HydrateScriptOptions, 85 + metadata: Required<AstroComponentMetadata>, 86 + ): Promise<SSRElement> { ... } 87 + ``` 88 + 89 + ### Public APIs should default to `async` 90 + 91 + Public-facing functions default to being `async`, even if they don't strictly need to be today. This future-proofs the API against needing to introduce async operations later without a breaking change. 92 + 93 + --- 94 + 95 + ## 5. Error Handling Convention 96 + 97 + Astro has a **unified, structured error system**. All errors go through a central `AstroError` class and are defined in a single data file. 98 + 99 + ### Never throw generic `Error` — use `AstroError` 100 + 101 + ```ts 102 + // ❌ Wrong 103 + throw new Error("Something went wrong"); 104 + 105 + // ✅ Correct 106 + throw new AstroError({ 107 + ...AstroErrorData.NoMatchingImport, 108 + message: AstroErrorData.NoMatchingImport.message(metadata.displayName), 109 + }); 110 + ``` 111 + 112 + ### Error definitions live in `errors-data.ts` 113 + 114 + All error data is centralized in `packages/astro/src/core/errors/errors-data.ts`. Each error follows a strict shape: 115 + 116 + - **`name`** — A permanent, unique PascalCase identifier (never changed once published). 117 + - **`title`** — A brief user-facing summary. 118 + - **`message`** — Can be a static string or a function for dynamic context. Describes what happened and what action to take. 119 + - **`hint`** — Optional. Additional context, links to docs, or common causes. 120 + 121 + ### Error writing guidelines (from `errors/README.md`) 122 + 123 + - Write from the **user's perspective**, not Astro internals ("You are missing..." not "Astro cannot find..."). 124 + - **Avoid cutesy language** — no "Oops!" The developer may be frustrated. 125 + - Keep messages **skimmable** — short, clear sentences. 126 + - Don't prefix with "Error:" or "Hint:" — the UI already adds labels. 127 + - **Names are permanent** — never rename, so users can always search for them. 128 + - Dynamic messages use a function shape: `message: (param) => \`...\``. 129 + - Avoid complex logic inside error definitions. 130 + - Errors are **grouped by domain** (Content Collections, Images, Routing, etc.) and display on the error reference page in definition order. 131 + 132 + --- 133 + 134 + ## 6. Code Architecture Principles 135 + 136 + ### Decouple business logic from infrastructure 137 + 138 + The CONTRIBUTING guide explicitly calls this out as a core principle: 139 + 140 + - **Infrastructure:** Code that depends on external systems (DB calls, file system, randomness, etc.) or requires a special environment to run. 141 + - **Business logic (domain/core):** Pure logic that's easy to run from anywhere. 142 + 143 + In practice this means: avoid side effects, make external dependencies explicit, and **pass more things as arguments** (dependency injection style). 144 + 145 + ### Test all implementations 146 + 147 + If an infrastructure implementation is just a thin wrapper around an npm package, you may skip testing it and trust the package's own tests. But all business logic should be tested. 148 + 149 + --- 150 + 151 + ## 8. Code Cleanliness Rules 152 + 153 + All enforced at the **error** level: 154 + 155 + | Rule | Effect | 156 + | ---------------------------- | ----------------------------------------------------- | 157 + | `noUnusedVariables` | No unused variables (with `ignoreRestSiblings: true`) | 158 + | `noUnusedFunctionParameters` | No unused function parameters | 159 + | `noUnusedImports` | No unused imports | 160 + | `useNodejsImportProtocol` | Must use `node:` prefix for Node builtins | 161 + | `useImportType` | Must use `import type` for type-only imports | 162 + 163 + - Node.js engine requirement: `>=22.12.0`. 164 + - Package manager: `pnpm` (enforced via `only-allow`). 165 + 166 + --- 167 + 168 + ## 12. Code Style 169 + 170 + The observed patterns from the source code include: 171 + 172 + - **Explicit return types** on exported functions. 173 + - **Interface-first design** — define an interface for options bags, then destructure in the function signature. 174 + - **JSDoc comments** on exported functions and complex internal functions, using `/** */` style. Avoid excessive or self-documenting comments. 175 + - **Explicit `.ts` extensions in import paths** — even for TypeScript files, imports use literal extensions. 176 + - **Avoid barrel files via `index.ts`** — packages organize re-exports through index files. 177 + - **`const` by default** — `let` only when reassignment is needed. 178 + 179 + --- 180 + 181 + ## Summary of Key Rules 182 + 183 + | Convention | Details | 184 + | --------------- | ------------------------------------------------------------------------------------------ | 185 + | Formatter | Biome: tabs, single quotes, semicolons always, trailing commas always, 100-char line width | 186 + | Node builtins | Always `node:` prefix (error-level lint rule) | 187 + | Type imports | Always `import type` for type-only (error-level lint rule) | 188 + | Node.js APIs | Restricted to non-`runtime/` code; virtual modules must be runtime-agnostic | 189 + | Function params | >2 params → options bag with a typed interface | 190 + | Public APIs | Default to `async` | 191 + | Error handling | Always `AstroError` with centralized error data; never generic `Error` | 192 + | `console.log` | Warned; use `console.info`/`warn`/`error`/`debug` instead | 193 + | Unused code | Unused vars, params, and imports are errors | 194 + | Path handling | Prefer `URL` / file URLs over `node:path` string manipulation | 195 + | Testing | Vitest via `astro-scripts`; `bgproc` for dev servers; `agent-browser` for HMR | 196 + | Scripting | `node -e`, not Python | 197 + | Business logic | Decouple from infrastructure; make dependencies explicit via arguments |
+14 -1
oxlintrc.json
··· 31 31 } 32 32 ], 33 33 "import/no-commonjs": "error", 34 - "node/no-path-concat": "error" 34 + "node/no-path-concat": "error", 35 + 36 + "unicorn/prefer-node-protocol": "error", 37 + "typescript/consistent-type-imports": "error", 38 + "no-console": ["warn", { "allow": ["info", "warn", "error", "debug"] }], 39 + "prefer-const": "error", 40 + "no-var": "error", 41 + "max-params": ["error", { "max": 2 }], 42 + "typescript/explicit-function-return-type": ["warn", { "allowExpressions": true }], 43 + 44 + "import/no-default-export": "error", 45 + "bombshell-dev/no-generic-error": "error", 46 + "bombshell-dev/require-export-jsdoc": "warn", 47 + "bombshell-dev/exported-function-async": "warn" 35 48 } 36 49 }
+75 -60
package.json
··· 1 1 { 2 - "name": "@bomb.sh/tools", 3 - "version": "0.1.0", 4 - "type": "module", 5 - "license": "MIT", 6 - "bin": { 7 - "bsh": "dist/bin.mjs" 2 + "name": "@bomb.sh/tools", 3 + "version": "0.1.0", 4 + "description": "The internal dev, build, and lint CLI for Bombshell projects", 5 + "keywords": [ 6 + "bombshell", 7 + "cli", 8 + "internal" 9 + ], 10 + "homepage": "https://bomb.sh", 11 + "license": "MIT", 12 + "author": { 13 + "name": "Bombshell", 14 + "email": "oss@bomb.sh", 15 + "url": "https://bomb.sh" 16 + }, 17 + "repository": { 18 + "type": "git", 19 + "url": "git+https://github.com/bombshell-dev/tools.git" 20 + }, 21 + "bin": { 22 + "bsh": "dist/bin.mjs" 23 + }, 24 + "type": "module", 25 + "exports": { 26 + ".": { 27 + "import": "./dist/bin.mjs" 8 28 }, 9 - "description": "The internal dev, build, and lint CLI for Bombshell projects", 10 - "keywords": [ 11 - "cli", 12 - "bombshell", 13 - "internal" 29 + "./*": "./dist/*", 30 + "./package.json": "./package.json", 31 + "./tsconfig.json": "./tsconfig.json" 32 + }, 33 + "publishConfig": { 34 + "access": "public" 35 + }, 36 + "scripts": { 37 + "bsh": "node --experimental-strip-types --no-warnings ./src/bin.ts", 38 + "dev": "pnpm run bsh dev", 39 + "build": "pnpm run bsh build", 40 + "format": "pnpm run bsh format", 41 + "init": "pnpm run bsh init", 42 + "lint": "pnpm run bsh lint", 43 + "test": "pnpm run bsh test" 44 + }, 45 + "dependencies": { 46 + "@bomb.sh/args": "^0.3.1", 47 + "@typescript/native-preview": "7.0.0-dev.20260307.1", 48 + "knip": "^5.85.0", 49 + "oxfmt": "^0.36.0", 50 + "oxlint": "^1.51.0", 51 + "publint": "^0.3.18", 52 + "tinyexec": "^1.0.1", 53 + "tsdown": "^0.21.0-beta.2", 54 + "vitest": "^4.0.18" 55 + }, 56 + "devDependencies": { 57 + "@changesets/cli": "^2.28.1", 58 + "@types/node": "^22.13.14" 59 + }, 60 + "volta": { 61 + "node": "22.14.0" 62 + }, 63 + "packageManager": "pnpm@10.4.0", 64 + "pnpm": {}, 65 + "knip": { 66 + "ignore": [ 67 + "rules/**" 14 68 ], 15 - "homepage": "https://bomb.sh", 16 - "author": { 17 - "name": "Bombshell", 18 - "email": "oss@bomb.sh", 19 - "url": "https://bomb.sh" 20 - }, 21 - "repository": { 22 - "type": "git", 23 - "url": "git+https://github.com/bombshell-dev/tools.git" 24 - }, 25 - "publishConfig": { 26 - "access": "public" 27 - }, 28 - "exports": { 29 - ".": { 30 - "import": "./dist/bin.mjs" 31 - }, 32 - "./*": "./dist/*", 33 - "./package.json": "./package.json", 34 - "./tsconfig.json": "./tsconfig.json" 35 - }, 36 - "scripts": { 37 - "bsh": "node --experimental-strip-types --no-warnings ./src/bin.ts", 38 - "dev": "pnpm run bsh dev", 39 - "build": "pnpm run bsh build", 40 - "format": "pnpm run bsh format", 41 - "init": "pnpm run bsh init", 42 - "lint": "pnpm run bsh lint", 43 - "test": "pnpm run bsh test" 44 - }, 45 - "devDependencies": { 46 - "@changesets/cli": "^2.28.1", 47 - "@types/node": "^22.13.14" 48 - }, 49 - "dependencies": { 50 - "@bomb.sh/args": "^0.3.1", 51 - "escalade": "^3.2.0", 52 - "oxfmt": "^0.36.0", 53 - "oxlint": "^1.51.0", 54 - "publint": "^0.3.18", 55 - "tinyexec": "^1.0.1", 56 - "tsdown": "^0.21.0-beta.2", 57 - "vitest": "^4.0.18" 58 - }, 59 - "packageManager": "pnpm@10.4.0", 60 - "volta": { 61 - "node": "22.14.0" 62 - }, 63 - "pnpm": {} 69 + "ignoreDependencies": [ 70 + "@bomb.sh/args", 71 + "@typescript/native-preview", 72 + "oxfmt", 73 + "oxlint", 74 + "publint", 75 + "tsdown", 76 + "vitest" 77 + ] 78 + } 64 79 }
+600 -13
pnpm-lock.yaml
··· 11 11 '@bomb.sh/args': 12 12 specifier: ^0.3.1 13 13 version: 0.3.1 14 + '@typescript/native-preview': 15 + specifier: 7.0.0-dev.20260307.1 16 + version: 7.0.0-dev.20260307.1 14 17 escalade: 15 18 specifier: ^3.2.0 16 19 version: 3.2.0 20 + knip: 21 + specifier: ^5.85.0 22 + version: 5.85.0(@types/node@22.13.14)(typescript@5.9.3) 23 + magic-string: 24 + specifier: ^0.30.21 25 + version: 0.30.21 26 + oxc-parser: 27 + specifier: ^0.116.0 28 + version: 0.116.0 17 29 oxfmt: 18 30 specifier: ^0.36.0 19 31 version: 0.36.0 ··· 28 40 version: 1.0.1 29 41 tsdown: 30 42 specifier: ^0.21.0-beta.2 31 - version: 0.21.0-beta.2(publint@0.3.18) 43 + version: 0.21.0-beta.2(@typescript/native-preview@7.0.0-dev.20260307.1)(oxc-resolver@11.19.1)(publint@0.3.18)(typescript@5.9.3) 32 44 vitest: 33 45 specifier: ^4.0.18 34 - version: 4.0.18(@types/node@22.13.14) 46 + version: 4.0.18(@types/node@22.13.14)(jiti@2.6.1) 35 47 devDependencies: 36 48 '@changesets/cli': 37 49 specifier: ^2.28.1 ··· 324 336 resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} 325 337 engines: {node: '>= 8'} 326 338 339 + '@oxc-parser/binding-android-arm-eabi@0.116.0': 340 + resolution: {integrity: sha512-AOET7YIOU3+ANO/3xQeRVGN5Xx6+JGXaIwlqkcHSfxJ/zzw2B6jb0YaLhX45SeRluKVTU8rka4N/tHtNoJjoCg==} 341 + engines: {node: ^20.19.0 || >=22.12.0} 342 + cpu: [arm] 343 + os: [android] 344 + 345 + '@oxc-parser/binding-android-arm64@0.116.0': 346 + resolution: {integrity: sha512-yh0Zvth5cQ6XZkP3QF9MDrXf695zr5XxXq/wBQqpZb0uAgI9wpr98/Hx2RZITMfnNjkIq2VcyU44o3A0bdEmlQ==} 347 + engines: {node: ^20.19.0 || >=22.12.0} 348 + cpu: [arm64] 349 + os: [android] 350 + 351 + '@oxc-parser/binding-darwin-arm64@0.116.0': 352 + resolution: {integrity: sha512-plcTd/Jska55dToZz6XdRBPRVsj+asjD8QCpQFvt3Wj8pY+10D1pE53Mei3POAS/wSRSy7HiQ2twrm7H2A0CjA==} 353 + engines: {node: ^20.19.0 || >=22.12.0} 354 + cpu: [arm64] 355 + os: [darwin] 356 + 357 + '@oxc-parser/binding-darwin-x64@0.116.0': 358 + resolution: {integrity: sha512-ahqcF3e3x5Z2ZepzXpZ8ugREdmxvBL+g1nQ0SxO11pIZfck6UtbOtwtdAAxnQXBHHtidu7lPcrBq1SEx26t1PQ==} 359 + engines: {node: ^20.19.0 || >=22.12.0} 360 + cpu: [x64] 361 + os: [darwin] 362 + 363 + '@oxc-parser/binding-freebsd-x64@0.116.0': 364 + resolution: {integrity: sha512-yo2/LaSXtlzKBurvNbwun/sN/RJwW3XhbMr069FwNVtft7GBnaLLdPIz/sf47icxw/BPViEX6wFvzeD12mtrAg==} 365 + engines: {node: ^20.19.0 || >=22.12.0} 366 + cpu: [x64] 367 + os: [freebsd] 368 + 369 + '@oxc-parser/binding-linux-arm-gnueabihf@0.116.0': 370 + resolution: {integrity: sha512-EiZeliIPPdFsuaPx8PzDMVijD/4YaUxO46/eYPk5raRocJqjjxOG6GAacQ8UrG3fbrgYjaEChfYL1e8DyE445A==} 371 + engines: {node: ^20.19.0 || >=22.12.0} 372 + cpu: [arm] 373 + os: [linux] 374 + 375 + '@oxc-parser/binding-linux-arm-musleabihf@0.116.0': 376 + resolution: {integrity: sha512-Nf7hnKRYRSIgglQcLAqE2St4b/Yr6dh+Z7in8mxol065Knevw71XZAiV1fmPSojq6uKPLV9eoH/wFrgr4TnZXw==} 377 + engines: {node: ^20.19.0 || >=22.12.0} 378 + cpu: [arm] 379 + os: [linux] 380 + 381 + '@oxc-parser/binding-linux-arm64-gnu@0.116.0': 382 + resolution: {integrity: sha512-9SJI0S4Qggn3QHpT8Y1jtZceA0m4BlpvO3ne2Wxd33UdTHMmelAnrXryjWutHWQtjCzOwSnFBEoQAdNNyt1u3A==} 383 + engines: {node: ^20.19.0 || >=22.12.0} 384 + cpu: [arm64] 385 + os: [linux] 386 + 387 + '@oxc-parser/binding-linux-arm64-musl@0.116.0': 388 + resolution: {integrity: sha512-wMZ6//GI+q1JwO7G2OR51+eA5P8Gr3BobU8RAzCGJptvyGMkWb7KQ1E8s8naVZRr6bSGWAL2p3mCzKOxmEPmrA==} 389 + engines: {node: ^20.19.0 || >=22.12.0} 390 + cpu: [arm64] 391 + os: [linux] 392 + 393 + '@oxc-parser/binding-linux-ppc64-gnu@0.116.0': 394 + resolution: {integrity: sha512-5BO0KCzTG2HZTnp3r6SCAOeCs/GwFBQJ1WAOG/ROfDf1fVVEy6hrtLKTLCuUMaamH38v+1+RVEmzRkzBj+rMDQ==} 395 + engines: {node: ^20.19.0 || >=22.12.0} 396 + cpu: [ppc64] 397 + os: [linux] 398 + 399 + '@oxc-parser/binding-linux-riscv64-gnu@0.116.0': 400 + resolution: {integrity: sha512-M24gYb/ocVMnLwnH2wY5sLt4sRBkAUHDmfiYtyUYdKTkfPOKtpopd5otsL/BPLnIhpMD8zby4uXVvw7BU0UIlw==} 401 + engines: {node: ^20.19.0 || >=22.12.0} 402 + cpu: [riscv64] 403 + os: [linux] 404 + 405 + '@oxc-parser/binding-linux-riscv64-musl@0.116.0': 406 + resolution: {integrity: sha512-LHLXTHCH0bdvGjlitwr1ngeh32GAgq9HYzQ5VAgt0k0UT84AS8AkXj9Spoa9l20fXkVgSvAKcCEkydi4Ol23Dw==} 407 + engines: {node: ^20.19.0 || >=22.12.0} 408 + cpu: [riscv64] 409 + os: [linux] 410 + 411 + '@oxc-parser/binding-linux-s390x-gnu@0.116.0': 412 + resolution: {integrity: sha512-VE+XsztuE5jdHvLIDIQMuyDpz5NJGq1Vx/8EXYF0sS/gehlv9GhDpGVWU0SCZ/LjzIy4io/Z0W84UudqufvP3g==} 413 + engines: {node: ^20.19.0 || >=22.12.0} 414 + cpu: [s390x] 415 + os: [linux] 416 + 417 + '@oxc-parser/binding-linux-x64-gnu@0.116.0': 418 + resolution: {integrity: sha512-rxUkauyjjCmgA7BoR63ogRGEtgubROnCm8AXE9ydg+p42jCGLLqG05mFcS2eC+FYyAU58ZFJNXXeqFW1iCyTGQ==} 419 + engines: {node: ^20.19.0 || >=22.12.0} 420 + cpu: [x64] 421 + os: [linux] 422 + 423 + '@oxc-parser/binding-linux-x64-musl@0.116.0': 424 + resolution: {integrity: sha512-0zoZlk9MmXe6oTgSh5lT1D51SDC1bfwC96JmE1amMFAPdEbJk5MFRisfTN9TFBpBigQua65842tjaxqMiorAYw==} 425 + engines: {node: ^20.19.0 || >=22.12.0} 426 + cpu: [x64] 427 + os: [linux] 428 + 429 + '@oxc-parser/binding-openharmony-arm64@0.116.0': 430 + resolution: {integrity: sha512-PGS7Xqik77U9WMyW626gAD5A2rSN629UvyYJKAl/tgpT+KqZI4+56pJfExhv8IW/PpSHjYHwjmakwobLikz8ww==} 431 + engines: {node: ^20.19.0 || >=22.12.0} 432 + cpu: [arm64] 433 + os: [openharmony] 434 + 435 + '@oxc-parser/binding-wasm32-wasi@0.116.0': 436 + resolution: {integrity: sha512-lGNf/9PU8XxB4Gt1Gr1AKwSrjxGYa6os0PlrT4bpoQsfE3gaZonQTKwJyKhiQdgy7pBCI+ed1LB1NNib1FYULw==} 437 + engines: {node: '>=14.0.0'} 438 + cpu: [wasm32] 439 + 440 + '@oxc-parser/binding-win32-arm64-msvc@0.116.0': 441 + resolution: {integrity: sha512-tcsOHE31duBSRQXZ7NfdtjmMKZwQYlS00PwAMJ4w5oXs3iPCvisUuIXP7Ko4FzeOBTRvkd64btxtQ6cRM0Kwlw==} 442 + engines: {node: ^20.19.0 || >=22.12.0} 443 + cpu: [arm64] 444 + os: [win32] 445 + 446 + '@oxc-parser/binding-win32-ia32-msvc@0.116.0': 447 + resolution: {integrity: sha512-higCz/x+dOQ264YEk22hnu4RDqvjhfehjFORpxoh42QyUxsP6eIembYesBUu5ilALWo0HvRD+m89az2BSTwqpQ==} 448 + engines: {node: ^20.19.0 || >=22.12.0} 449 + cpu: [ia32] 450 + os: [win32] 451 + 452 + '@oxc-parser/binding-win32-x64-msvc@0.116.0': 453 + resolution: {integrity: sha512-Lg2SRmVHpGG85knDVLbv44r1bYn0OpIV0vg9jVmoEIpDj3Q4kwXuQ6MWVtuslwHR8o2CSiqdBeEn1n1URrs6Eg==} 454 + engines: {node: ^20.19.0 || >=22.12.0} 455 + cpu: [x64] 456 + os: [win32] 457 + 327 458 '@oxc-project/types@0.114.0': 328 459 resolution: {integrity: sha512-//nBfbzHQHvJs8oFIjv6coZ6uxQ4alLfiPe6D5vit6c4pmxATHHlVwgB1k+Hv4yoAMyncdxgRBF5K4BYWUCzvA==} 329 460 461 + '@oxc-project/types@0.116.0': 462 + resolution: {integrity: sha512-uOT8S1tlPmDckNxMNtIudN/yXpLdnhlJMX2oLS7cxCd7L0sUF09A/EbSVMWT3Y/iT44IwXCJSJfgfSxXAqWf9Q==} 463 + 464 + '@oxc-resolver/binding-android-arm-eabi@11.19.1': 465 + resolution: {integrity: sha512-aUs47y+xyXHUKlbhqHUjBABjvycq6YSD7bpxSW7vplUmdzAlJ93yXY6ZR0c1o1x5A/QKbENCvs3+NlY8IpIVzg==} 466 + cpu: [arm] 467 + os: [android] 468 + 469 + '@oxc-resolver/binding-android-arm64@11.19.1': 470 + resolution: {integrity: sha512-oolbkRX+m7Pq2LNjr/kKgYeC7bRDMVTWPgxBGMjSpZi/+UskVo4jsMU3MLheZV55jL6c3rNelPl4oD60ggYmqA==} 471 + cpu: [arm64] 472 + os: [android] 473 + 474 + '@oxc-resolver/binding-darwin-arm64@11.19.1': 475 + resolution: {integrity: sha512-nUC6d2i3R5B12sUW4O646qD5cnMXf2oBGPLIIeaRfU9doJRORAbE2SGv4eW6rMqhD+G7nf2Y8TTJTLiiO3Q/dQ==} 476 + cpu: [arm64] 477 + os: [darwin] 478 + 479 + '@oxc-resolver/binding-darwin-x64@11.19.1': 480 + resolution: {integrity: sha512-cV50vE5+uAgNcFa3QY1JOeKDSkM/9ReIcc/9wn4TavhW/itkDGrXhw9jaKnkQnGbjJ198Yh5nbX/Gr2mr4Z5jQ==} 481 + cpu: [x64] 482 + os: [darwin] 483 + 484 + '@oxc-resolver/binding-freebsd-x64@11.19.1': 485 + resolution: {integrity: sha512-xZOQiYGFxtk48PBKff+Zwoym7ScPAIVp4c14lfLxizO2LTTTJe5sx9vQNGrBymrf/vatSPNMD4FgsaaRigPkqw==} 486 + cpu: [x64] 487 + os: [freebsd] 488 + 489 + '@oxc-resolver/binding-linux-arm-gnueabihf@11.19.1': 490 + resolution: {integrity: sha512-lXZYWAC6kaGe/ky2su94e9jN9t6M0/6c+GrSlCqL//XO1cxi5lpAhnJYdyrKfm0ZEr/c7RNyAx3P7FSBcBd5+A==} 491 + cpu: [arm] 492 + os: [linux] 493 + 494 + '@oxc-resolver/binding-linux-arm-musleabihf@11.19.1': 495 + resolution: {integrity: sha512-veG1kKsuK5+t2IsO9q0DErYVSw2azvCVvWHnfTOS73WE0STdLLB7Q1bB9WR+yHPQM76ASkFyRbogWo1GR1+WbQ==} 496 + cpu: [arm] 497 + os: [linux] 498 + 499 + '@oxc-resolver/binding-linux-arm64-gnu@11.19.1': 500 + resolution: {integrity: sha512-heV2+jmXyYnUrpUXSPugqWDRpnsQcDm2AX4wzTuvgdlZfoNYO0O3W2AVpJYaDn9AG4JdM6Kxom8+foE7/BcSig==} 501 + cpu: [arm64] 502 + os: [linux] 503 + 504 + '@oxc-resolver/binding-linux-arm64-musl@11.19.1': 505 + resolution: {integrity: sha512-jvo2Pjs1c9KPxMuMPIeQsgu0mOJF9rEb3y3TdpsrqwxRM+AN6/nDDwv45n5ZrUnQMsdBy5gIabioMKnQfWo9ew==} 506 + cpu: [arm64] 507 + os: [linux] 508 + 509 + '@oxc-resolver/binding-linux-ppc64-gnu@11.19.1': 510 + resolution: {integrity: sha512-vLmdNxWCdN7Uo5suays6A/+ywBby2PWBBPXctWPg5V0+eVuzsJxgAn6MMB4mPlshskYbppjpN2Zg83ArHze9gQ==} 511 + cpu: [ppc64] 512 + os: [linux] 513 + 514 + '@oxc-resolver/binding-linux-riscv64-gnu@11.19.1': 515 + resolution: {integrity: sha512-/b+WgR+VTSBxzgOhDO7TlMXC1ufPIMR6Vj1zN+/x+MnyXGW7prTLzU9eW85Aj7Th7CCEG9ArCbTeqxCzFWdg2w==} 516 + cpu: [riscv64] 517 + os: [linux] 518 + 519 + '@oxc-resolver/binding-linux-riscv64-musl@11.19.1': 520 + resolution: {integrity: sha512-YlRdeWb9j42p29ROh+h4eg/OQ3dTJlpHSa+84pUM9+p6i3djtPz1q55yLJhgW9XfDch7FN1pQ/Vd6YP+xfRIuw==} 521 + cpu: [riscv64] 522 + os: [linux] 523 + 524 + '@oxc-resolver/binding-linux-s390x-gnu@11.19.1': 525 + resolution: {integrity: sha512-EDpafVOQWF8/MJynsjOGFThcqhRHy417sRyLfQmeiamJ8qVhSKAn2Dn2VVKUGCjVB9C46VGjhNo7nOPUi1x6uA==} 526 + cpu: [s390x] 527 + os: [linux] 528 + 529 + '@oxc-resolver/binding-linux-x64-gnu@11.19.1': 530 + resolution: {integrity: sha512-NxjZe+rqWhr+RT8/Ik+5ptA3oz7tUw361Wa5RWQXKnfqwSSHdHyrw6IdcTfYuml9dM856AlKWZIUXDmA9kkiBQ==} 531 + cpu: [x64] 532 + os: [linux] 533 + 534 + '@oxc-resolver/binding-linux-x64-musl@11.19.1': 535 + resolution: {integrity: sha512-cM/hQwsO3ReJg5kR+SpI69DMfvNCp+A/eVR4b4YClE5bVZwz8rh2Nh05InhwI5HR/9cArbEkzMjcKgTHS6UaNw==} 536 + cpu: [x64] 537 + os: [linux] 538 + 539 + '@oxc-resolver/binding-openharmony-arm64@11.19.1': 540 + resolution: {integrity: sha512-QF080IowFB0+9Rh6RcD19bdgh49BpQHUW5TajG1qvWHvmrQznTZZjYlgE2ltLXyKY+qs4F/v5xuX1XS7Is+3qA==} 541 + cpu: [arm64] 542 + os: [openharmony] 543 + 544 + '@oxc-resolver/binding-wasm32-wasi@11.19.1': 545 + resolution: {integrity: sha512-w8UCKhX826cP/ZLokXDS6+milN8y4X7zidsAttEdWlVoamTNf6lhBJldaWr3ukTDiye7s4HRcuPEPOXNC432Vg==} 546 + engines: {node: '>=14.0.0'} 547 + cpu: [wasm32] 548 + 549 + '@oxc-resolver/binding-win32-arm64-msvc@11.19.1': 550 + resolution: {integrity: sha512-nJ4AsUVZrVKwnU/QRdzPCCrO0TrabBqgJ8pJhXITdZGYOV28TIYystV1VFLbQ7DtAcaBHpocT5/ZJnF78YJPtQ==} 551 + cpu: [arm64] 552 + os: [win32] 553 + 554 + '@oxc-resolver/binding-win32-ia32-msvc@11.19.1': 555 + resolution: {integrity: sha512-EW+ND5q2Tl+a3pH81l1QbfgbF3HmqgwLfDfVithRFheac8OTcnbXt/JxqD2GbDkb7xYEqy1zNaVFRr3oeG8npA==} 556 + cpu: [ia32] 557 + os: [win32] 558 + 559 + '@oxc-resolver/binding-win32-x64-msvc@11.19.1': 560 + resolution: {integrity: sha512-6hIU3RQu45B+VNTY4Ru8ppFwjVS/S5qwYyGhBotmjxfEKk41I2DlGtRfGJndZ5+6lneE2pwloqunlOyZuX/XAw==} 561 + cpu: [x64] 562 + os: [win32] 563 + 330 564 '@oxfmt/binding-android-arm-eabi@0.36.0': 331 565 resolution: {integrity: sha512-Z4yVHJWx/swHHjtr0dXrBZb6LxS+qNz1qdza222mWwPTUK4L790+5i3LTgjx3KYGBzcYpjaiZBw4vOx94dH7MQ==} 332 566 engines: {node: ^20.19.0 || >=22.12.0} ··· 791 1025 '@types/node@22.13.14': 792 1026 resolution: {integrity: sha512-Zs/Ollc1SJ8nKUAgc7ivOEdIBM8JAKgrqqUYi2J997JuKO7/tpQC+WCetQ1sypiKCQWHdvdg9wBNpUPEWZae7w==} 793 1027 1028 + '@typescript/native-preview-darwin-arm64@7.0.0-dev.20260307.1': 1029 + resolution: {integrity: sha512-VpnrMP4iDLSTT9Hg/KrHwuIHLZr5dxYPMFErfv3ZDA0tv48u2H1lBhHVVMMopCuskuX3C35EOJbxLkxCJd6zDw==} 1030 + cpu: [arm64] 1031 + os: [darwin] 1032 + 1033 + '@typescript/native-preview-darwin-x64@7.0.0-dev.20260307.1': 1034 + resolution: {integrity: sha512-+4akGPxwfrPy2AYmQO1bp6CXxUVlBPrL0lSv+wY/E8vNGqwF0UtJCwAcR54ae1+k9EmoirT7Xn6LE3Io6mXntg==} 1035 + cpu: [x64] 1036 + os: [darwin] 1037 + 1038 + '@typescript/native-preview-linux-arm64@7.0.0-dev.20260307.1': 1039 + resolution: {integrity: sha512-u4kXuHN2p+HeWsnTixoEOwALsCoS+n3/ukWdnV/mwyg6BKuuU69qCv3/miY6YPFtE7mUwzPdflEXsvkZJbJ/RA==} 1040 + cpu: [arm64] 1041 + os: [linux] 1042 + 1043 + '@typescript/native-preview-linux-arm@7.0.0-dev.20260307.1': 1044 + resolution: {integrity: sha512-E0Pve6BjTVvPiHq9cPVQu6fbW/Qo/CEs1VN2NMILd0xzFVpVd9FIvzV+Ft6pZilu1SBcihThW3sQ92l03Cw2+Q==} 1045 + cpu: [arm] 1046 + os: [linux] 1047 + 1048 + '@typescript/native-preview-linux-x64@7.0.0-dev.20260307.1': 1049 + resolution: {integrity: sha512-MzuRjTYQIS7XrJcH0As18SbaQU+rFhf9LCpXs2QeHjhXQ33wjuFDNhQeurg2eKm6A0xE0GoW9K+sKsm8bhzzPg==} 1050 + cpu: [x64] 1051 + os: [linux] 1052 + 1053 + '@typescript/native-preview-win32-arm64@7.0.0-dev.20260307.1': 1054 + resolution: {integrity: sha512-UNZl8Q6lx1njEPU8+FNjYvqii5PtDjk6cyxmVPwwJI2Snz5T5qY6oadkUds6CJsLkt7s4UB3P5XgLu1+vwoYGw==} 1055 + cpu: [arm64] 1056 + os: [win32] 1057 + 1058 + '@typescript/native-preview-win32-x64@7.0.0-dev.20260307.1': 1059 + resolution: {integrity: sha512-aPJb4v0Df9GzWFWbO4YbLg0OjmjxZgXngkF1M746r4CgOdydWgosNPWypzzAwiliGKvCLwfAWYiV+T5Jf1vQ3g==} 1060 + cpu: [x64] 1061 + os: [win32] 1062 + 1063 + '@typescript/native-preview@7.0.0-dev.20260307.1': 1064 + resolution: {integrity: sha512-NcKdPiGjxxxdh7fLgRKTrn5hLntbt89NOodNaSrMChTfJwvLaDkgrRlnO7v5x+m7nQc87Qf1y7UoT1ZEZUBB4Q==} 1065 + hasBin: true 1066 + 794 1067 '@vitest/expect@4.0.18': 795 1068 resolution: {integrity: sha512-8sCWUyckXXYvx4opfzVY03EOiYVxyNrHS5QxX3DAIi5dpJAAkyJezHCP77VMX4HKA2LDT/Jpfo8i2r5BE3GnQQ==} 796 1069 ··· 834 1107 835 1108 argparse@1.0.10: 836 1109 resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==} 1110 + 1111 + argparse@2.0.1: 1112 + resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} 837 1113 838 1114 array-union@2.1.0: 839 1115 resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} ··· 943 1219 fastq@1.19.1: 944 1220 resolution: {integrity: sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==} 945 1221 1222 + fd-package-json@2.0.0: 1223 + resolution: {integrity: sha512-jKmm9YtsNXN789RS/0mSzOC1NUq9mkVd65vbSSVsKdjGvYXBuE4oWe2QOEoFeRmJg+lPuZxpmrfFclNhoRMneQ==} 1224 + 946 1225 fdir@6.5.0: 947 1226 resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} 948 1227 engines: {node: '>=12.0.0'} ··· 959 1238 find-up@4.1.0: 960 1239 resolution: {integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==} 961 1240 engines: {node: '>=8'} 1241 + 1242 + formatly@0.3.0: 1243 + resolution: {integrity: sha512-9XNj/o4wrRFyhSMJOvsuyMwy8aUfBaZ1VrqHVfohyXf0Sw0e+yfKG+xZaY3arGCOMdwFsqObtzVOc1gU9KiT9w==} 1244 + engines: {node: '>=18.3.0'} 1245 + hasBin: true 962 1246 963 1247 fs-extra@7.0.1: 964 1248 resolution: {integrity: sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==} ··· 1029 1313 isexe@2.0.0: 1030 1314 resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} 1031 1315 1316 + jiti@2.6.1: 1317 + resolution: {integrity: sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==} 1318 + hasBin: true 1319 + 1032 1320 js-yaml@3.14.1: 1033 1321 resolution: {integrity: sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==} 1034 1322 hasBin: true 1035 1323 1324 + js-yaml@4.1.1: 1325 + resolution: {integrity: sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==} 1326 + hasBin: true 1327 + 1036 1328 jsesc@3.1.0: 1037 1329 resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==} 1038 1330 engines: {node: '>=6'} ··· 1041 1333 jsonfile@4.0.0: 1042 1334 resolution: {integrity: sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==} 1043 1335 1336 + knip@5.85.0: 1337 + resolution: {integrity: sha512-V2kyON+DZiYdNNdY6GALseiNCwX7dYdpz9Pv85AUn69Gk0UKCts+glOKWfe5KmaMByRjM9q17Mzj/KinTVOyxg==} 1338 + engines: {node: '>=18.18.0'} 1339 + hasBin: true 1340 + peerDependencies: 1341 + '@types/node': '>=18' 1342 + typescript: '>=5.0.4 <7' 1343 + 1044 1344 locate-path@5.0.0: 1045 1345 resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==} 1046 1346 engines: {node: '>=8'} ··· 1058 1358 micromatch@4.0.8: 1059 1359 resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} 1060 1360 engines: {node: '>=8.6'} 1361 + 1362 + minimist@1.2.8: 1363 + resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} 1061 1364 1062 1365 mri@1.2.0: 1063 1366 resolution: {integrity: sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==} ··· 1078 1381 outdent@0.5.0: 1079 1382 resolution: {integrity: sha512-/jHxFIzoMXdqPzTaCpFzAAWhpkSjZPF4Vsn6jAfNpmbH/ymsmd7Qc6VE9BGn0L6YMj6uwpQLxCECpus4ukKS9Q==} 1080 1383 1384 + oxc-parser@0.116.0: 1385 + resolution: {integrity: sha512-ugEo6wwqaqCGcpi7GsLCwSkoD7gIXzvtdaTxE+mbrXFYazU5Q9YdpZdAj9z2b79i/xlv+uW2aAvyzGAlpUzhKQ==} 1386 + engines: {node: ^20.19.0 || >=22.12.0} 1387 + 1388 + oxc-resolver@11.19.1: 1389 + resolution: {integrity: sha512-qE/CIg/spwrTBFt5aKmwe3ifeDdLfA2NESN30E42X/lII5ClF8V7Wt6WIJhcGZjp0/Q+nQ+9vgxGk//xZNX2hg==} 1390 + 1081 1391 oxfmt@0.36.0: 1082 1392 resolution: {integrity: sha512-/ejJ+KoSW6J9bcNT9a9UtJSJNWhJ3yOLSBLbkoFHJs/8CZjmaZVZAJe4YgO1KMJlKpNQasrn/G9JQUEZI3p0EQ==} 1083 1393 engines: {node: ^20.19.0 || >=22.12.0} ··· 1258 1568 resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} 1259 1569 engines: {node: '>=8'} 1260 1570 1571 + smol-toml@1.6.0: 1572 + resolution: {integrity: sha512-4zemZi0HvTnYwLfrpk/CF9LOd9Lt87kAt50GnqhMpyF9U3poDAP2+iukq2bZsO/ufegbYehBkqINbsWxj4l4cw==} 1573 + engines: {node: '>= 18'} 1574 + 1261 1575 source-map-js@1.2.1: 1262 1576 resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} 1263 1577 engines: {node: '>=0.10.0'} ··· 1281 1595 strip-bom@3.0.0: 1282 1596 resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==} 1283 1597 engines: {node: '>=4'} 1598 + 1599 + strip-json-comments@5.0.3: 1600 + resolution: {integrity: sha512-1tB5mhVo7U+ETBKNf92xT4hrQa3pm0MZ0PQvuDnWgAAGHDsfp4lPSpiS6psrSiet87wyGPh9ft6wmhOMQ0hDiw==} 1601 + engines: {node: '>=14.16'} 1284 1602 1285 1603 term-size@2.2.1: 1286 1604 resolution: {integrity: sha512-wK0Ri4fOGjv/XPy8SBHZChl8CM7uMc5VML7SqiQ0zG7+J5Vr+RMQDoHa2CNT6KHUnTGIXH34UDMkPzAUyapBZg==} ··· 1347 1665 1348 1666 tslib@2.8.1: 1349 1667 resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} 1668 + 1669 + typescript@5.9.3: 1670 + resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==} 1671 + engines: {node: '>=14.17'} 1672 + hasBin: true 1350 1673 1351 1674 unconfig-core@7.5.0: 1352 1675 resolution: {integrity: sha512-Su3FauozOGP44ZmKdHy2oE6LPjk51M/TRRjHv2HNCWiDvfvCoxC2lno6jevMA91MYAdCdwP05QnWdWpSbncX/w==} ··· 1442 1765 jsdom: 1443 1766 optional: true 1444 1767 1768 + walk-up-path@4.0.0: 1769 + resolution: {integrity: sha512-3hu+tD8YzSLGuFYtPRb48vdhKMi0KQV5sn+uWr8+7dMEq/2G/dtLrdDinkLjqq5TIbIBjYJ4Ax/n3YiaW7QM8A==} 1770 + engines: {node: 20 || >=22} 1771 + 1445 1772 which@2.0.2: 1446 1773 resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} 1447 1774 engines: {node: '>= 8'} ··· 1451 1778 resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==} 1452 1779 engines: {node: '>=8'} 1453 1780 hasBin: true 1781 + 1782 + zod@4.3.6: 1783 + resolution: {integrity: sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==} 1454 1784 1455 1785 snapshots: 1456 1786 ··· 1767 2097 '@nodelib/fs.scandir': 2.1.5 1768 2098 fastq: 1.19.1 1769 2099 2100 + '@oxc-parser/binding-android-arm-eabi@0.116.0': 2101 + optional: true 2102 + 2103 + '@oxc-parser/binding-android-arm64@0.116.0': 2104 + optional: true 2105 + 2106 + '@oxc-parser/binding-darwin-arm64@0.116.0': 2107 + optional: true 2108 + 2109 + '@oxc-parser/binding-darwin-x64@0.116.0': 2110 + optional: true 2111 + 2112 + '@oxc-parser/binding-freebsd-x64@0.116.0': 2113 + optional: true 2114 + 2115 + '@oxc-parser/binding-linux-arm-gnueabihf@0.116.0': 2116 + optional: true 2117 + 2118 + '@oxc-parser/binding-linux-arm-musleabihf@0.116.0': 2119 + optional: true 2120 + 2121 + '@oxc-parser/binding-linux-arm64-gnu@0.116.0': 2122 + optional: true 2123 + 2124 + '@oxc-parser/binding-linux-arm64-musl@0.116.0': 2125 + optional: true 2126 + 2127 + '@oxc-parser/binding-linux-ppc64-gnu@0.116.0': 2128 + optional: true 2129 + 2130 + '@oxc-parser/binding-linux-riscv64-gnu@0.116.0': 2131 + optional: true 2132 + 2133 + '@oxc-parser/binding-linux-riscv64-musl@0.116.0': 2134 + optional: true 2135 + 2136 + '@oxc-parser/binding-linux-s390x-gnu@0.116.0': 2137 + optional: true 2138 + 2139 + '@oxc-parser/binding-linux-x64-gnu@0.116.0': 2140 + optional: true 2141 + 2142 + '@oxc-parser/binding-linux-x64-musl@0.116.0': 2143 + optional: true 2144 + 2145 + '@oxc-parser/binding-openharmony-arm64@0.116.0': 2146 + optional: true 2147 + 2148 + '@oxc-parser/binding-wasm32-wasi@0.116.0': 2149 + dependencies: 2150 + '@napi-rs/wasm-runtime': 1.1.1 2151 + optional: true 2152 + 2153 + '@oxc-parser/binding-win32-arm64-msvc@0.116.0': 2154 + optional: true 2155 + 2156 + '@oxc-parser/binding-win32-ia32-msvc@0.116.0': 2157 + optional: true 2158 + 2159 + '@oxc-parser/binding-win32-x64-msvc@0.116.0': 2160 + optional: true 2161 + 1770 2162 '@oxc-project/types@0.114.0': {} 2163 + 2164 + '@oxc-project/types@0.116.0': {} 2165 + 2166 + '@oxc-resolver/binding-android-arm-eabi@11.19.1': 2167 + optional: true 2168 + 2169 + '@oxc-resolver/binding-android-arm64@11.19.1': 2170 + optional: true 2171 + 2172 + '@oxc-resolver/binding-darwin-arm64@11.19.1': 2173 + optional: true 2174 + 2175 + '@oxc-resolver/binding-darwin-x64@11.19.1': 2176 + optional: true 2177 + 2178 + '@oxc-resolver/binding-freebsd-x64@11.19.1': 2179 + optional: true 2180 + 2181 + '@oxc-resolver/binding-linux-arm-gnueabihf@11.19.1': 2182 + optional: true 2183 + 2184 + '@oxc-resolver/binding-linux-arm-musleabihf@11.19.1': 2185 + optional: true 2186 + 2187 + '@oxc-resolver/binding-linux-arm64-gnu@11.19.1': 2188 + optional: true 2189 + 2190 + '@oxc-resolver/binding-linux-arm64-musl@11.19.1': 2191 + optional: true 2192 + 2193 + '@oxc-resolver/binding-linux-ppc64-gnu@11.19.1': 2194 + optional: true 2195 + 2196 + '@oxc-resolver/binding-linux-riscv64-gnu@11.19.1': 2197 + optional: true 2198 + 2199 + '@oxc-resolver/binding-linux-riscv64-musl@11.19.1': 2200 + optional: true 2201 + 2202 + '@oxc-resolver/binding-linux-s390x-gnu@11.19.1': 2203 + optional: true 2204 + 2205 + '@oxc-resolver/binding-linux-x64-gnu@11.19.1': 2206 + optional: true 2207 + 2208 + '@oxc-resolver/binding-linux-x64-musl@11.19.1': 2209 + optional: true 2210 + 2211 + '@oxc-resolver/binding-openharmony-arm64@11.19.1': 2212 + optional: true 2213 + 2214 + '@oxc-resolver/binding-wasm32-wasi@11.19.1': 2215 + dependencies: 2216 + '@napi-rs/wasm-runtime': 1.1.1 2217 + optional: true 2218 + 2219 + '@oxc-resolver/binding-win32-arm64-msvc@11.19.1': 2220 + optional: true 2221 + 2222 + '@oxc-resolver/binding-win32-ia32-msvc@11.19.1': 2223 + optional: true 2224 + 2225 + '@oxc-resolver/binding-win32-x64-msvc@11.19.1': 2226 + optional: true 1771 2227 1772 2228 '@oxfmt/binding-android-arm-eabi@0.36.0': 1773 2229 optional: true ··· 2031 2487 dependencies: 2032 2488 undici-types: 6.20.0 2033 2489 2490 + '@typescript/native-preview-darwin-arm64@7.0.0-dev.20260307.1': 2491 + optional: true 2492 + 2493 + '@typescript/native-preview-darwin-x64@7.0.0-dev.20260307.1': 2494 + optional: true 2495 + 2496 + '@typescript/native-preview-linux-arm64@7.0.0-dev.20260307.1': 2497 + optional: true 2498 + 2499 + '@typescript/native-preview-linux-arm@7.0.0-dev.20260307.1': 2500 + optional: true 2501 + 2502 + '@typescript/native-preview-linux-x64@7.0.0-dev.20260307.1': 2503 + optional: true 2504 + 2505 + '@typescript/native-preview-win32-arm64@7.0.0-dev.20260307.1': 2506 + optional: true 2507 + 2508 + '@typescript/native-preview-win32-x64@7.0.0-dev.20260307.1': 2509 + optional: true 2510 + 2511 + '@typescript/native-preview@7.0.0-dev.20260307.1': 2512 + optionalDependencies: 2513 + '@typescript/native-preview-darwin-arm64': 7.0.0-dev.20260307.1 2514 + '@typescript/native-preview-darwin-x64': 7.0.0-dev.20260307.1 2515 + '@typescript/native-preview-linux-arm': 7.0.0-dev.20260307.1 2516 + '@typescript/native-preview-linux-arm64': 7.0.0-dev.20260307.1 2517 + '@typescript/native-preview-linux-x64': 7.0.0-dev.20260307.1 2518 + '@typescript/native-preview-win32-arm64': 7.0.0-dev.20260307.1 2519 + '@typescript/native-preview-win32-x64': 7.0.0-dev.20260307.1 2520 + 2034 2521 '@vitest/expect@4.0.18': 2035 2522 dependencies: 2036 2523 '@standard-schema/spec': 1.1.0 ··· 2040 2527 chai: 6.2.2 2041 2528 tinyrainbow: 3.0.3 2042 2529 2043 - '@vitest/mocker@4.0.18(vite@7.3.1(@types/node@22.13.14))': 2530 + '@vitest/mocker@4.0.18(vite@7.3.1(@types/node@22.13.14)(jiti@2.6.1))': 2044 2531 dependencies: 2045 2532 '@vitest/spy': 4.0.18 2046 2533 estree-walker: 3.0.3 2047 2534 magic-string: 0.30.21 2048 2535 optionalDependencies: 2049 - vite: 7.3.1(@types/node@22.13.14) 2536 + vite: 7.3.1(@types/node@22.13.14)(jiti@2.6.1) 2050 2537 2051 2538 '@vitest/pretty-format@4.0.18': 2052 2539 dependencies: ··· 2080 2567 dependencies: 2081 2568 sprintf-js: 1.0.3 2082 2569 2570 + argparse@2.0.1: {} 2571 + 2083 2572 array-union@2.1.0: {} 2084 2573 2085 2574 assertion-error@2.0.1: {} ··· 2122 2611 dependencies: 2123 2612 path-type: 4.0.0 2124 2613 2125 - dts-resolver@2.1.3: {} 2614 + dts-resolver@2.1.3(oxc-resolver@11.19.1): 2615 + optionalDependencies: 2616 + oxc-resolver: 11.19.1 2126 2617 2127 2618 empathic@2.0.0: {} 2128 2619 ··· 2192 2683 dependencies: 2193 2684 reusify: 1.1.0 2194 2685 2686 + fd-package-json@2.0.0: 2687 + dependencies: 2688 + walk-up-path: 4.0.0 2689 + 2195 2690 fdir@6.5.0(picomatch@4.0.3): 2196 2691 optionalDependencies: 2197 2692 picomatch: 4.0.3 ··· 2204 2699 dependencies: 2205 2700 locate-path: 5.0.0 2206 2701 path-exists: 4.0.0 2702 + 2703 + formatly@0.3.0: 2704 + dependencies: 2705 + fd-package-json: 2.0.0 2207 2706 2208 2707 fs-extra@7.0.1: 2209 2708 dependencies: ··· 2267 2766 2268 2767 isexe@2.0.0: {} 2269 2768 2769 + jiti@2.6.1: {} 2770 + 2270 2771 js-yaml@3.14.1: 2271 2772 dependencies: 2272 2773 argparse: 1.0.10 2273 2774 esprima: 4.0.1 2274 2775 2776 + js-yaml@4.1.1: 2777 + dependencies: 2778 + argparse: 2.0.1 2779 + 2275 2780 jsesc@3.1.0: {} 2276 2781 2277 2782 jsonfile@4.0.0: 2278 2783 optionalDependencies: 2279 2784 graceful-fs: 4.2.11 2280 2785 2786 + knip@5.85.0(@types/node@22.13.14)(typescript@5.9.3): 2787 + dependencies: 2788 + '@nodelib/fs.walk': 1.2.8 2789 + '@types/node': 22.13.14 2790 + fast-glob: 3.3.3 2791 + formatly: 0.3.0 2792 + jiti: 2.6.1 2793 + js-yaml: 4.1.1 2794 + minimist: 1.2.8 2795 + oxc-resolver: 11.19.1 2796 + picocolors: 1.1.1 2797 + picomatch: 4.0.3 2798 + smol-toml: 1.6.0 2799 + strip-json-comments: 5.0.3 2800 + typescript: 5.9.3 2801 + zod: 4.3.6 2802 + 2281 2803 locate-path@5.0.0: 2282 2804 dependencies: 2283 2805 p-locate: 4.1.0 ··· 2295 2817 braces: 3.0.3 2296 2818 picomatch: 2.3.1 2297 2819 2820 + minimist@1.2.8: {} 2821 + 2298 2822 mri@1.2.0: {} 2299 2823 2300 2824 nanoid@3.3.11: {} ··· 2305 2829 2306 2830 outdent@0.5.0: {} 2307 2831 2832 + oxc-parser@0.116.0: 2833 + dependencies: 2834 + '@oxc-project/types': 0.116.0 2835 + optionalDependencies: 2836 + '@oxc-parser/binding-android-arm-eabi': 0.116.0 2837 + '@oxc-parser/binding-android-arm64': 0.116.0 2838 + '@oxc-parser/binding-darwin-arm64': 0.116.0 2839 + '@oxc-parser/binding-darwin-x64': 0.116.0 2840 + '@oxc-parser/binding-freebsd-x64': 0.116.0 2841 + '@oxc-parser/binding-linux-arm-gnueabihf': 0.116.0 2842 + '@oxc-parser/binding-linux-arm-musleabihf': 0.116.0 2843 + '@oxc-parser/binding-linux-arm64-gnu': 0.116.0 2844 + '@oxc-parser/binding-linux-arm64-musl': 0.116.0 2845 + '@oxc-parser/binding-linux-ppc64-gnu': 0.116.0 2846 + '@oxc-parser/binding-linux-riscv64-gnu': 0.116.0 2847 + '@oxc-parser/binding-linux-riscv64-musl': 0.116.0 2848 + '@oxc-parser/binding-linux-s390x-gnu': 0.116.0 2849 + '@oxc-parser/binding-linux-x64-gnu': 0.116.0 2850 + '@oxc-parser/binding-linux-x64-musl': 0.116.0 2851 + '@oxc-parser/binding-openharmony-arm64': 0.116.0 2852 + '@oxc-parser/binding-wasm32-wasi': 0.116.0 2853 + '@oxc-parser/binding-win32-arm64-msvc': 0.116.0 2854 + '@oxc-parser/binding-win32-ia32-msvc': 0.116.0 2855 + '@oxc-parser/binding-win32-x64-msvc': 0.116.0 2856 + 2857 + oxc-resolver@11.19.1: 2858 + optionalDependencies: 2859 + '@oxc-resolver/binding-android-arm-eabi': 11.19.1 2860 + '@oxc-resolver/binding-android-arm64': 11.19.1 2861 + '@oxc-resolver/binding-darwin-arm64': 11.19.1 2862 + '@oxc-resolver/binding-darwin-x64': 11.19.1 2863 + '@oxc-resolver/binding-freebsd-x64': 11.19.1 2864 + '@oxc-resolver/binding-linux-arm-gnueabihf': 11.19.1 2865 + '@oxc-resolver/binding-linux-arm-musleabihf': 11.19.1 2866 + '@oxc-resolver/binding-linux-arm64-gnu': 11.19.1 2867 + '@oxc-resolver/binding-linux-arm64-musl': 11.19.1 2868 + '@oxc-resolver/binding-linux-ppc64-gnu': 11.19.1 2869 + '@oxc-resolver/binding-linux-riscv64-gnu': 11.19.1 2870 + '@oxc-resolver/binding-linux-riscv64-musl': 11.19.1 2871 + '@oxc-resolver/binding-linux-s390x-gnu': 11.19.1 2872 + '@oxc-resolver/binding-linux-x64-gnu': 11.19.1 2873 + '@oxc-resolver/binding-linux-x64-musl': 11.19.1 2874 + '@oxc-resolver/binding-openharmony-arm64': 11.19.1 2875 + '@oxc-resolver/binding-wasm32-wasi': 11.19.1 2876 + '@oxc-resolver/binding-win32-arm64-msvc': 11.19.1 2877 + '@oxc-resolver/binding-win32-ia32-msvc': 11.19.1 2878 + '@oxc-resolver/binding-win32-x64-msvc': 11.19.1 2879 + 2308 2880 oxfmt@0.36.0: 2309 2881 dependencies: 2310 2882 tinypool: 2.1.0 ··· 2425 2997 2426 2998 reusify@1.1.0: {} 2427 2999 2428 - rolldown-plugin-dts@0.22.3(rolldown@1.0.0-rc.5): 3000 + rolldown-plugin-dts@0.22.3(@typescript/native-preview@7.0.0-dev.20260307.1)(oxc-resolver@11.19.1)(rolldown@1.0.0-rc.5)(typescript@5.9.3): 2429 3001 dependencies: 2430 3002 '@babel/generator': 8.0.0-rc.2 2431 3003 '@babel/helper-validator-identifier': 8.0.0-rc.2 ··· 2433 3005 '@babel/types': 8.0.0-rc.2 2434 3006 ast-kit: 3.0.0-beta.1 2435 3007 birpc: 4.0.0 2436 - dts-resolver: 2.1.3 3008 + dts-resolver: 2.1.3(oxc-resolver@11.19.1) 2437 3009 get-tsconfig: 4.13.6 2438 3010 obug: 2.1.1 2439 3011 rolldown: 1.0.0-rc.5 3012 + optionalDependencies: 3013 + '@typescript/native-preview': 7.0.0-dev.20260307.1 3014 + typescript: 5.9.3 2440 3015 transitivePeerDependencies: 2441 3016 - oxc-resolver 2442 3017 ··· 2516 3091 2517 3092 slash@3.0.0: {} 2518 3093 3094 + smol-toml@1.6.0: {} 3095 + 2519 3096 source-map-js@1.2.1: {} 2520 3097 2521 3098 spawndamnit@3.0.1: ··· 2534 3111 ansi-regex: 5.0.1 2535 3112 2536 3113 strip-bom@3.0.0: {} 3114 + 3115 + strip-json-comments@5.0.3: {} 2537 3116 2538 3117 term-size@2.2.1: {} 2539 3118 ··· 2562 3141 2563 3142 tree-kill@1.2.2: {} 2564 3143 2565 - tsdown@0.21.0-beta.2(publint@0.3.18): 3144 + tsdown@0.21.0-beta.2(@typescript/native-preview@7.0.0-dev.20260307.1)(oxc-resolver@11.19.1)(publint@0.3.18)(typescript@5.9.3): 2566 3145 dependencies: 2567 3146 ansis: 4.2.0 2568 3147 cac: 6.7.14 ··· 2573 3152 obug: 2.1.1 2574 3153 picomatch: 4.0.3 2575 3154 rolldown: 1.0.0-rc.5 2576 - rolldown-plugin-dts: 0.22.3(rolldown@1.0.0-rc.5) 3155 + rolldown-plugin-dts: 0.22.3(@typescript/native-preview@7.0.0-dev.20260307.1)(oxc-resolver@11.19.1)(rolldown@1.0.0-rc.5)(typescript@5.9.3) 2577 3156 semver: 7.7.4 2578 3157 tinyexec: 1.0.2 2579 3158 tinyglobby: 0.2.15 ··· 2582 3161 unrun: 0.2.28 2583 3162 optionalDependencies: 2584 3163 publint: 0.3.18 3164 + typescript: 5.9.3 2585 3165 transitivePeerDependencies: 2586 3166 - '@ts-macro/tsc' 2587 3167 - '@typescript/native-preview' ··· 2592 3172 tslib@2.8.1: 2593 3173 optional: true 2594 3174 3175 + typescript@5.9.3: {} 3176 + 2595 3177 unconfig-core@7.5.0: 2596 3178 dependencies: 2597 3179 '@quansync/fs': 1.0.0 ··· 2605 3187 dependencies: 2606 3188 rolldown: 1.0.0-rc.5 2607 3189 2608 - vite@7.3.1(@types/node@22.13.14): 3190 + vite@7.3.1(@types/node@22.13.14)(jiti@2.6.1): 2609 3191 dependencies: 2610 3192 esbuild: 0.27.3 2611 3193 fdir: 6.5.0(picomatch@4.0.3) ··· 2616 3198 optionalDependencies: 2617 3199 '@types/node': 22.13.14 2618 3200 fsevents: 2.3.3 3201 + jiti: 2.6.1 2619 3202 2620 - vitest@4.0.18(@types/node@22.13.14): 3203 + vitest@4.0.18(@types/node@22.13.14)(jiti@2.6.1): 2621 3204 dependencies: 2622 3205 '@vitest/expect': 4.0.18 2623 - '@vitest/mocker': 4.0.18(vite@7.3.1(@types/node@22.13.14)) 3206 + '@vitest/mocker': 4.0.18(vite@7.3.1(@types/node@22.13.14)(jiti@2.6.1)) 2624 3207 '@vitest/pretty-format': 4.0.18 2625 3208 '@vitest/runner': 4.0.18 2626 3209 '@vitest/snapshot': 4.0.18 ··· 2637 3220 tinyexec: 1.0.2 2638 3221 tinyglobby: 0.2.15 2639 3222 tinyrainbow: 3.0.3 2640 - vite: 7.3.1(@types/node@22.13.14) 3223 + vite: 7.3.1(@types/node@22.13.14)(jiti@2.6.1) 2641 3224 why-is-node-running: 2.3.0 2642 3225 optionalDependencies: 2643 3226 '@types/node': 22.13.14 ··· 2654 3237 - tsx 2655 3238 - yaml 2656 3239 3240 + walk-up-path@4.0.0: {} 3241 + 2657 3242 which@2.0.2: 2658 3243 dependencies: 2659 3244 isexe: 2.0.0 ··· 2662 3247 dependencies: 2663 3248 siginfo: 2.0.0 2664 3249 stackback: 0.0.2 3250 + 3251 + zod@4.3.6: {}
+143 -4
rules/plugin.js
··· 1 1 /** @type {import("oxlint").Plugin} */ 2 2 const plugin = { 3 - meta: { 4 - name: "bombshell-dev", 5 - }, 6 - rules: {}, 3 + meta: { 4 + name: 'bombshell-dev', 5 + }, 6 + rules: { 7 + /** 8 + * Disallow `throw new Error(...)` in favor of custom error classes. 9 + * 10 + * Generic `Error` objects lack structured metadata (error codes, hints, etc.) 11 + * and make it harder to provide actionable diagnostics. Use a project-specific 12 + * error class instead. 13 + * 14 + * Catches: 15 + * - `throw new Error(...)` 16 + * - `throw new TypeError(...)` / `throw new RangeError(...)` etc. 17 + * 18 + * Allows: 19 + * - `throw new MyCustomError(...)` (any non-builtin name) 20 + * - Re-throwing: `throw err` 21 + */ 22 + 'no-generic-error': { 23 + create(context) { 24 + const BUILTIN_ERRORS = new Set([ 25 + 'Error', 26 + 'TypeError', 27 + 'RangeError', 28 + 'ReferenceError', 29 + 'SyntaxError', 30 + 'URIError', 31 + 'EvalError', 32 + 'AggregateError', 33 + ]); 34 + 35 + return { 36 + ThrowStatement(node) { 37 + const arg = node.argument; 38 + if ( 39 + arg && 40 + arg.type === 'NewExpression' && 41 + arg.callee.type === 'Identifier' && 42 + BUILTIN_ERRORS.has(arg.callee.name) 43 + ) { 44 + context.report({ 45 + node: arg, 46 + message: `Do not throw generic \`${arg.callee.name}\`. Use a project-specific error class with structured metadata instead.`, 47 + }); 48 + } 49 + }, 50 + }; 51 + }, 52 + }, 53 + 54 + /** 55 + * Require JSDoc comments on exported functions and classes. 56 + * 57 + * Public APIs should have `/** ... *​/` documentation. Internal/unexported 58 + * functions are not flagged. 59 + */ 60 + 'require-export-jsdoc': { 61 + create(context) { 62 + function hasJSDoc(node) { 63 + const src = context.sourceCode.text; 64 + let idx = node.start - 1; 65 + // Walk backwards past whitespace 66 + while (idx >= 0 && (src[idx] === ' ' || src[idx] === '\t' || src[idx] === '\n' || src[idx] === '\r')) { 67 + idx--; 68 + } 69 + // Check if preceding non-whitespace ends with */ 70 + if (idx >= 1 && src[idx] === '/' && src[idx - 1] === '*') { 71 + // Find the opening /** 72 + const closeIdx = idx; 73 + const openIdx = src.lastIndexOf('/**', closeIdx); 74 + if (openIdx !== -1 && openIdx < closeIdx) { 75 + return true; 76 + } 77 + } 78 + return false; 79 + } 80 + 81 + function isExported(node) { 82 + const parent = node.parent; 83 + if (!parent) return false; 84 + return ( 85 + parent.type === 'ExportNamedDeclaration' || 86 + parent.type === 'ExportDefaultDeclaration' 87 + ); 88 + } 89 + 90 + function check(node) { 91 + const target = isExported(node) ? node.parent : null; 92 + if (!target) return; 93 + if (!hasJSDoc(target)) { 94 + context.report({ 95 + node, 96 + message: 'Exported functions and classes should have a JSDoc comment.', 97 + }); 98 + } 99 + } 100 + 101 + return { 102 + FunctionDeclaration: check, 103 + ClassDeclaration: check, 104 + }; 105 + }, 106 + }, 107 + 108 + /** 109 + * Require exported functions to be `async`. 110 + * 111 + * Public-facing functions should default to `async` to future-proof 112 + * the API — adding async later is a breaking change for callers that 113 + * don't `await`. 114 + * 115 + * Ignores: 116 + * - Non-exported functions 117 + * - Class methods (checked separately if needed) 118 + * - Functions that return explicit non-Promise types (type-level; 119 + * this rule only checks the `async` keyword at the syntax level) 120 + */ 121 + 'exported-function-async': { 122 + create(context) { 123 + function isExported(node) { 124 + const parent = node.parent; 125 + if (!parent) return false; 126 + return ( 127 + parent.type === 'ExportNamedDeclaration' || 128 + parent.type === 'ExportDefaultDeclaration' 129 + ); 130 + } 131 + 132 + return { 133 + FunctionDeclaration(node) { 134 + if (!isExported(node)) return; 135 + if (node.async) return; 136 + const name = node.id?.name ?? 'anonymous'; 137 + context.report({ 138 + node, 139 + message: `Exported function \`${name}\` should be \`async\`. Public APIs default to async to avoid breaking changes.`, 140 + }); 141 + }, 142 + }; 143 + }, 144 + }, 145 + }, 7 146 }; 8 147 9 148 export default plugin;
+13 -13
src/commands/build.ts
··· 3 3 import { local } from "../utils.ts"; 4 4 5 5 export async function build(ctx: CommandContext) { 6 - const stdio = x(local("tsdown"), [ 7 - "src/bin.ts", 8 - "--format", 9 - "esm", 10 - "--sourcemap", 11 - "--clean", 12 - "--unbundle", 13 - "--no-config", 14 - ...ctx.args, 15 - ]); 6 + const stdio = x(local("tsdown"), [ 7 + "src/bin.ts", 8 + "--format", 9 + "esm", 10 + "--sourcemap", 11 + "--clean", 12 + "--unbundle", 13 + "--no-config", 14 + ...ctx.args, 15 + ]); 16 16 17 - for await (const line of stdio) { 18 - console.log(line); 19 - } 17 + for await (const line of stdio) { 18 + console.log(line); 19 + } 20 20 }
+247 -9
src/commands/lint.ts
··· 1 1 import { fileURLToPath } from "node:url"; 2 + import { parse } from "@bomb.sh/args"; 3 + import { publint } from "publint"; 2 4 import { x } from "tinyexec"; 3 5 import type { CommandContext } from "../context.ts"; 4 6 import { local } from "../utils.ts"; 5 7 6 - const config = fileURLToPath(new URL("../../oxlintrc.json", import.meta.url)); 8 + const oxlintConfig = fileURLToPath(new URL("../../oxlintrc.json", import.meta.url)); 9 + 10 + // -- Types -- 11 + 12 + interface Violation { 13 + tool: "oxlint" | "publint" | "knip" | "tsc"; 14 + level: "error" | "warning" | "suggestion"; 15 + code: string; 16 + message: string; 17 + file?: string; 18 + line?: number; 19 + column?: number; 20 + } 21 + 22 + // -- Tool Runners -- 23 + 24 + async function runOxlint(targets: string[], fix?: boolean): Promise<Violation[]> { 25 + const args = ["-c", oxlintConfig, "--format=json", ...targets]; 26 + if (fix) args.push("--fix"); 27 + const result = await x(local("oxlint"), args, { throwOnError: false }); 28 + const json = JSON.parse(result.stdout); 29 + return (json.diagnostics ?? []).map( 30 + (d: { 31 + message: string; 32 + code: string; 33 + severity: string; 34 + filename?: string; 35 + labels?: Array<{ span?: { line?: number; column?: number } }>; 36 + }) => ({ 37 + tool: "oxlint" as const, 38 + level: d.severity === "error" ? "error" : "warning", 39 + code: d.code ?? "unknown", 40 + message: d.message, 41 + file: d.filename, 42 + line: d.labels?.[0]?.span?.line, 43 + column: d.labels?.[0]?.span?.column, 44 + }), 45 + ); 46 + } 47 + 48 + async function runPublint(): Promise<Violation[]> { 49 + const result = await publint({ strict: true }); 50 + return result.messages.map((m) => ({ 51 + tool: "publint" as const, 52 + level: m.type === "error" ? "error" : m.type === "warning" ? "warning" : "suggestion", 53 + code: m.code, 54 + message: m.code, 55 + file: "package.json", 56 + line: undefined, 57 + column: undefined, 58 + })); 59 + } 60 + 61 + interface KnipIssue { 62 + file: string; 63 + dependencies: Array<{ name: string; line: number; col: number }>; 64 + devDependencies: Array<{ name: string; line: number; col: number }>; 65 + exports: Array<{ name: string; line: number; col: number }>; 66 + types: Array<{ name: string; line: number; col: number }>; 67 + } 68 + 69 + async function runKnip(): Promise<Violation[]> { 70 + const args = ["--no-progress", "--reporter", "json"]; 71 + const result = await x(local("knip"), args, { throwOnError: false }); 72 + if (!result.stdout.trim()) return []; 73 + const json = JSON.parse(result.stdout); 74 + const violations: Violation[] = []; 75 + 76 + for (const issue of json.issues as KnipIssue[]) { 77 + for (const dep of issue.dependencies) { 78 + violations.push({ 79 + tool: "knip", 80 + level: "warning", 81 + code: "unused-dependency", 82 + message: `Unused dependency '${dep.name}'`, 83 + file: issue.file, 84 + line: dep.line, 85 + column: dep.col, 86 + }); 87 + } 88 + for (const dep of issue.devDependencies) { 89 + violations.push({ 90 + tool: "knip", 91 + level: "warning", 92 + code: "unused-devDependency", 93 + message: `Unused devDependency '${dep.name}'`, 94 + file: issue.file, 95 + line: dep.line, 96 + column: dep.col, 97 + }); 98 + } 99 + for (const exp of issue.exports) { 100 + violations.push({ 101 + tool: "knip", 102 + level: "warning", 103 + code: "unused-export", 104 + message: `Unused export '${exp.name}'`, 105 + file: issue.file, 106 + line: exp.line, 107 + column: exp.col, 108 + }); 109 + } 110 + for (const t of issue.types) { 111 + violations.push({ 112 + tool: "knip", 113 + level: "warning", 114 + code: "unused-type", 115 + message: `Unused type '${t.name}'`, 116 + file: issue.file, 117 + line: t.line, 118 + column: t.col, 119 + }); 120 + } 121 + } 122 + 123 + for (const file of json.files as string[]) { 124 + violations.push({ 125 + tool: "knip", 126 + level: "warning", 127 + code: "unused-file", 128 + message: `Unused file`, 129 + file, 130 + }); 131 + } 132 + 133 + return violations; 134 + } 135 + 136 + async function runTypeScript(targets: string[]): Promise<Violation[]> { 137 + const args = 138 + targets.length > 0 139 + ? ["--noEmit", "--pretty", "false", ...targets] 140 + : ["--noEmit", "--pretty", "false"]; 141 + const result = await x(local("tsgo"), args, { throwOnError: false }); 142 + const output = result.stdout + result.stderr; 143 + if (!output.trim()) return []; 144 + 145 + const violations: Violation[] = []; 146 + const re = /^(.+)\((\d+),(\d+)\): (error|warning) (TS\d+): (.+)$/gm; 147 + let match: RegExpExecArray | null; 148 + while ((match = re.exec(output)) !== null) { 149 + violations.push({ 150 + tool: "tsc", 151 + level: match[4] === "error" ? "error" : "warning", 152 + code: match[5]!, 153 + message: match[6]!, 154 + file: match[1]!, 155 + line: Number(match[2]), 156 + column: Number(match[3]), 157 + }); 158 + } 159 + return violations; 160 + } 161 + 162 + // -- Output -- 163 + 164 + function printViolations(violations: Violation[]) { 165 + const grouped = new Map<string, Violation[]>(); 166 + for (const v of violations) { 167 + const key = v.file ?? "(project)"; 168 + if (!grouped.has(key)) grouped.set(key, []); 169 + grouped.get(key)!.push(v); 170 + } 171 + 172 + const colors = { 173 + error: "\x1b[31m", 174 + warning: "\x1b[33m", 175 + suggestion: "\x1b[34m", 176 + dim: "\x1b[2m", 177 + reset: "\x1b[0m", 178 + }; 179 + 180 + for (const [file, items] of grouped) { 181 + console.log(`\n${file}`); 182 + for (const v of items) { 183 + const loc = v.line != null ? ` ${v.line}:${v.column ?? 0}` : " -"; 184 + const color = colors[v.level]; 185 + const tag = `${v.tool}/${v.code}`; 186 + console.log( 187 + `${colors.dim}${loc.padEnd(10)}${colors.reset}${color}${v.level.padEnd(12)}${colors.reset}${v.message} ${colors.dim}${tag}${colors.reset}`, 188 + ); 189 + } 190 + } 191 + 192 + const counts = { error: 0, warning: 0, suggestion: 0 }; 193 + for (const v of violations) counts[v.level]++; 194 + const parts = []; 195 + if (counts.error) 196 + parts.push(`${colors.error}${counts.error} error${counts.error > 1 ? "s" : ""}${colors.reset}`); 197 + if (counts.warning) 198 + parts.push( 199 + `${colors.warning}${counts.warning} warning${counts.warning > 1 ? "s" : ""}${colors.reset}`, 200 + ); 201 + if (counts.suggestion) 202 + parts.push( 203 + `${colors.suggestion}${counts.suggestion} suggestion${counts.suggestion > 1 ? "s" : ""}${colors.reset}`, 204 + ); 205 + if (parts.length > 0) { 206 + console.log(`\n${parts.join(", ")}`); 207 + } else { 208 + console.log("\nNo issues found."); 209 + } 210 + } 211 + 212 + // -- Main -- 213 + 214 + async function collectViolations(targets: string[]): Promise<Violation[]> { 215 + const results = await Promise.allSettled([ 216 + runOxlint(targets), 217 + runPublint(), 218 + runKnip(), 219 + runTypeScript(targets), 220 + ]); 221 + 222 + const violations: Violation[] = []; 223 + for (const result of results) { 224 + if (result.status === "fulfilled") { 225 + violations.push(...result.value); 226 + } else { 227 + console.error(result.reason); 228 + } 229 + } 230 + return violations; 231 + } 7 232 8 233 export async function lint(ctx: CommandContext) { 9 - const oxlint = x(local("oxlint"), ["-c", config, "./src", ...ctx.args]); 234 + const args = parse(ctx.args, { 235 + boolean: ["fix"], 236 + }); 237 + const targets = args._.length > 0 ? args._.map(String) : ["./src"]; 10 238 11 - for await (const line of oxlint) { 12 - console.log(line); 13 - } 239 + if (args.fix) { 240 + await runOxlint(targets, true); 14 241 15 - const publint = x(local("publint"), ["--strict"]); 242 + // Report remaining 243 + const remaining = await collectViolations(targets); 244 + if (remaining.length > 0) { 245 + printViolations(remaining); 246 + process.exit(1); 247 + } 248 + console.log("No issues found."); 249 + return; 250 + } 16 251 17 - for await (const line of publint) { 18 - console.log(line); 19 - } 252 + // Default: report only 253 + const violations = await collectViolations(targets); 254 + printViolations(violations); 255 + if (violations.some((v) => v.level === "error")) { 256 + process.exit(1); 257 + } 20 258 }
+2 -18
src/utils.ts
··· 1 - import { cwd } from "node:process"; 2 - import { fileURLToPath, pathToFileURL } from "node:url"; 3 - import escalade from "escalade"; 1 + import { fileURLToPath } from "node:url"; 4 2 5 3 export function local(file: string) { 6 - return fileURLToPath(new URL(`../node_modules/.bin/${file}`, import.meta.url)); 7 - } 8 - 9 - export async function getPackageJSON() { 10 - const pkg = await escalade(cwd(), (dir, files) => { 11 - return files.find((file) => file === "package.json"); 12 - }); 13 - if (!pkg) { 14 - throw new Error("No package.json found"); 15 - } 16 - const pkgPath = pathToFileURL(pkg); 17 - const { default: json } = await import(pkgPath.toString(), { 18 - with: { type: "json" }, 19 - }); 20 - return json; 4 + return fileURLToPath(new URL(`../node_modules/.bin/${file}`, import.meta.url)); 21 5 }