{"_id":"oxlint-plugin-boundaries","name":"oxlint-plugin-boundaries","dist-tags":{"latest":"0.1.0"},"versions":{"0.1.0":{"name":"oxlint-plugin-boundaries","version":"0.1.0","description":"Config-driven cross-package / element-type boundaries enforcement for oxlint — a resolver-less JS plugin. Each repo declares its own element table and allow-matrix in `.oxlintrc.json` settings.","keywords":["architecture","boundaries","dependency-rules","lint","monorepo","oxc","oxlint","oxlint-plugin"],"homepage":"https://github.com/paulcedrick/oxlint-plugin-boundaries#readme","bugs":{"url":"https://github.com/paulcedrick/oxlint-plugin-boundaries/issues"},"license":"MIT","author":{"name":"Paul Cedrick Artigo"},"repository":{"type":"git","url":"git+https://github.com/paulcedrick/oxlint-plugin-boundaries.git"},"type":"module","sideEffects":false,"main":"./dist/index.js","module":"./dist/index.js","types":"./dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","default":"./dist/index.js"}},"scripts":{"build":"tsdown","type-check":"tsc --noEmit","lint":"oxlint","lint:fix":"oxlint --fix","fmt":"oxfmt","fmt:check":"oxfmt --check","test":"bun test","prepublishOnly":"bun run build"},"dependencies":{"@oxlint/plugins":"^1.69.0"},"devDependencies":{"@arethetypeswrong/core":"^0.18.3","@types/bun":"latest","oxfmt":"^0.54.0","oxlint":"1.69.0","publint":"^0.3.21","tsdown":"^0.22.2","typescript":"^6.0.0"},"peerDependencies":{"oxlint":">=1.69.0 <2"},"engines":{"node":">=20.19"},"gitHead":"746b02ca3e8c6144be952cb70ab0d011c5e4542a","_id":"oxlint-plugin-boundaries@0.1.0","_nodeVersion":"24.16.0","_npmVersion":"11.13.0","dist":{"integrity":"sha512-mPY42ac2d0hXA30hA/7bh/RsRUlgQ+dSGnLu+81fw28ZFF8UfDJklhAZsCrnDbZAEn0QV5Pm7ldhbtAzeu10XQ==","shasum":"587b8dbaf94a9a50c1f795e36b3a48d297ab3614","tarball":"https://registry.npmjs.org/oxlint-plugin-boundaries/-/oxlint-plugin-boundaries-0.1.0.tgz","fileCount":7,"unpackedSize":73941,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCIQC8+PGM0qx/n7uhwhMCXbaswSuruLCbrYyyXrVTgyTxSgIfIrZYxfhZi3/oxPy+1LC/yks9oR9sBy1BUa7S6jfh1g=="}]},"_npmUser":{"name":"paulcedrick","email":"paulcedrick.artigo@gmail.com"},"directories":{},"maintainers":[{"name":"paulcedrick","email":"paulcedrick.artigo@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/oxlint-plugin-boundaries_0.1.0_1781437620569_0.07969866852369134"},"_hasShrinkwrap":false}},"time":{"created":"2026-06-14T11:47:00.504Z","0.1.0":"2026-06-14T11:47:00.715Z","modified":"2026-06-14T11:47:00.963Z"},"maintainers":[{"name":"paulcedrick","email":"paulcedrick.artigo@gmail.com"}],"description":"Config-driven cross-package / element-type boundaries enforcement for oxlint — a resolver-less JS plugin. Each repo declares its own element table and allow-matrix in `.oxlintrc.json` settings.","homepage":"https://github.com/paulcedrick/oxlint-plugin-boundaries#readme","keywords":["architecture","boundaries","dependency-rules","lint","monorepo","oxc","oxlint","oxlint-plugin"],"repository":{"type":"git","url":"git+https://github.com/paulcedrick/oxlint-plugin-boundaries.git"},"author":{"name":"Paul Cedrick Artigo"},"bugs":{"url":"https://github.com/paulcedrick/oxlint-plugin-boundaries/issues"},"license":"MIT","readme":"# oxlint-plugin-boundaries\n\nConfig-driven **cross-package / element-type boundaries** for [oxlint](https://oxc.rs) — enforce an architectural dependency matrix (which parts of your codebase may import which) **without a module resolver**.\n\nYou declare your own element table and allow-matrix in `.oxlintrc.json`; the plugin classifies every import by file path and flags edges your matrix disallows. Works in any monorepo (Bun / npm / pnpm / Yarn workspaces) and in single-package repos.\n\n> **Status: alpha.** oxlint's JS plugin system (`jsPlugins`) is itself alpha and not yet semver-stable. This plugin pins a tested `oxlint` range in `peerDependencies` and is exercised against an oxlint-version matrix in CI. Expect a new minor when oxlint changes the plugin API. See [Versioning & the alpha pin](#versioning--the-alpha-pin).\n\n## Why this exists\n\noxlint has no native cross-package boundaries rule, and the popular [`eslint-plugin-boundaries`](https://github.com/javierbrea/eslint-plugin-boundaries) **cannot run under oxlint's JS-plugin layer**: that layer intentionally exposes _no module resolver_ (no `context.resolve`, empty `parserServices`), and `eslint-plugin-boundaries` depends on one (`eslint-import-resolver-typescript`).\n\nThis plugin sidesteps the missing resolver by **classifying purely from the file path**. It walks up to your workspace root, reads each package's `package.json` `name` once to build a `name → directory` index, and resolves bare specifiers like `@scope/pkg` to a directory — then to an element type. No resolver required.\n\n## Install\n\n```sh\nbun add -D oxlint-plugin-boundaries\n# or: npm i -D oxlint-plugin-boundaries  /  pnpm add -D oxlint-plugin-boundaries\n```\n\n`oxlint` is a peer dependency — install it yourself and keep it within the supported range.\n\n## Usage\n\nReference the plugin in `jsPlugins`, describe your architecture under `settings.boundaries`, and turn the rules on:\n\n```jsonc\n{\n  \"jsPlugins\": [\"oxlint-plugin-boundaries\"],\n  \"settings\": {\n    \"boundaries\": {\n      // Ordered: more-specific patterns BEFORE their parents. First match wins.\n      \"elements\": [\n        { \"type\": \"core\", \"pattern\": \"packages/core/**\" },\n        { \"type\": \"db\", \"pattern\": \"packages/db/**\" },\n        { \"type\": \"schemas\", \"pattern\": \"packages/schemas/**\" },\n        { \"type\": \"api-client\", \"pattern\": \"packages/api-client/**\" },\n        { \"type\": \"app-web\", \"pattern\": \"apps/web/**\" },\n      ],\n      \"rules\": [\n        { \"from\": \"core\", \"allow\": [\"db\", \"schemas\"] },\n        { \"from\": \"db\", \"allow\": [\"schemas\"] },\n        { \"from\": \"app-web\", \"allow\": [\"api-client\", \"schemas\"] },\n        {\n          \"from\": \"api-client\",\n          \"allow\": [\"app-api\"],\n          \"importKind\": \"type\",\n          \"message\": \"api-client may import apps/api only as `import type`.\",\n        },\n      ],\n      \"default\": \"disallow\",\n      \"ignore\": [\"**/*.test.ts\", \"**/*.spec.ts\"],\n    },\n  },\n  \"rules\": {\n    \"boundaries/element-types\": \"error\",\n    \"boundaries/no-unknown\": \"error\",\n  },\n}\n```\n\n> oxlint resolves `jsPlugins` paths **relative to the config file**. Using the package name (as above) is the normal case; a relative path would be resolved against the `.oxlintrc.json` location, not your shell's cwd.\n\n## Configuration (`settings.boundaries`)\n\nThis object is the plugin's public API.\n\n| Field            | Type                      | Required                  | Meaning                                                                                                                                                                                                                                                                                                                              |\n| ---------------- | ------------------------- | ------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |\n| `elements`       | `Element[]`               | yes                       | Ordered list mapping path patterns to element types. **First match wins**, so list specific patterns before their parents.                                                                                                                                                                                                           |\n| `rules`          | `Rule[]`                  | yes                       | Directional allow-matrix. Each entry says what a `from` element may import.                                                                                                                                                                                                                                                          |\n| `default`        | `\"disallow\"` \\| `\"allow\"` | no (default `\"disallow\"`) | Verdict for an edge not covered by any rule.                                                                                                                                                                                                                                                                                         |\n| `ignore`         | `string[]`                | no                        | Glob-ish patterns for files to skip entirely.                                                                                                                                                                                                                                                                                        |\n| `workspaceScope` | `string`                  | no (derived)              | The package-name prefix marking workspace-internal imports (e.g. `\"@acme/\"`). A bare specifier starting with it is a candidate boundary edge; anything else is an external dependency and ignored. When omitted, it is derived from the common scope of your workspace packages; set it explicitly if your packages don't share one. |\n\n**`Element`**\n\n| Field     | Type     | Meaning                                                        |\n| --------- | -------- | -------------------------------------------------------------- |\n| `type`    | `string` | Element type name, referenced by `rules`.                      |\n| `pattern` | `string` | Path pattern (root-relative) classifying files into this type. |\n\n**`Rule`**\n\n| Field        | Type                  | Meaning                                                                                          |\n| ------------ | --------------------- | ------------------------------------------------------------------------------------------------ |\n| `from`       | `string`              | The importing element type this rule governs.                                                    |\n| `allow`      | `string[]`            | Element types `from` may import.                                                                 |\n| `importKind` | `\"value\"` \\| `\"type\"` | Optional. `\"type\"` narrows the `allow` list to `import type` edges only (a type-only carve-out). |\n| `message`    | `string`              | Optional. Custom message shown when this edge is violated.                                       |\n\nSelf-imports (an element importing its own type) are always allowed. External dependencies (npm packages outside your workspace) are never boundary edges and are ignored.\n\n### Rules\n\n- **`boundaries/element-types`** — the core rule. For every import, classifies both ends and reports edges your matrix disallows (honoring the type-only carve-out).\n- **`boundaries/no-unknown`** — flags a workspace-style specifier that resolves to _no_ package (typically a typo or a deleted package). Closes the gap that `element-types` leaves when a target classifies to nothing.\n\n## How classification works\n\n1. **Find the workspace root** — walk up from the file to the nearest `package.json` declaring `workspaces` (falls back to oxlint's cwd). Keying off the file path keeps results identical no matter which directory you run oxlint from.\n2. **Index packages** — read each workspace package's `name` once; memoize a `name → dir` map.\n3. **Classify both ends of each import** — the importing file by its path; the target by resolving a relative specifier against the file's directory, or a bare workspace specifier (`@scope/pkg[/sub]`) to its package dir via longest-prefix match.\n4. **Evaluate** — `self` → allowed; in the value allow-list → allowed; `import type` and in the type-only allow-list → allowed; otherwise the `default` decides.\n\n## Versioning & the alpha pin\n\noxlint's `jsPlugins` API is **alpha** and not semver-stable. The policy here:\n\n- `peerDependencies.oxlint` is pinned to a **tested range**; CI runs an oxlint-version matrix.\n- When oxlint ships a breaking plugin-API change, this package cuts a new release with an updated range. The test suite is the tripwire — there are no defensive version guards in the runtime.\n- Keep your `oxlint` within the supported range for predictable behavior.\n\n## Authoring / build (for contributors)\n\nAuthored in TypeScript, shipped as compiled **ESM `.js` + `.d.ts`** so the published artifact loads on any supported Node — including versions below the 22.18 floor that raw `.ts` oxlint plugins require. Built with [tsdown](https://tsdown.dev) (oxc / Rolldown).\n\n```sh\nbun install\nbun run build        # tsdown -> dist/ (.js + .d.ts), runs publint + attw\nbun run type-check\nbun run lint         # dogfoods this very plugin\nbun test\n```\n\n> tsdown requires Node ≥ 22.18 / ≥ 24 to _run the build_. This affects contributors and CI only — never consumers of the published package.\n\n## License\n\n[MIT](./LICENSE) © Paul Cedrick Artigo\n","readmeFilename":"README.md","_rev":"1-1ae6fa66f7f68f40434bf90789f24e62"}