{"_id":"@bravobit/eslint-plugin-import-sorter","_rev":"2-bbbabf1d4b39ac85a508154a6c412321","name":"@bravobit/eslint-plugin-import-sorter","dist-tags":{"latest":"1.0.0"},"versions":{"1.0.0":{"name":"@bravobit/eslint-plugin-import-sorter","version":"1.0.0","keywords":["eslint","eslintplugin","eslint-plugin","imports","sort","line-length"],"license":"MIT","_id":"@bravobit/eslint-plugin-import-sorter@1.0.0","maintainers":[{"name":"stanvanheumen","email":"stanvanheumen@gmail.com"}],"homepage":"https://github.com/bravobit/eslint-plugin-import-sorter#readme","bugs":{"url":"https://github.com/bravobit/eslint-plugin-import-sorter/issues"},"dist":{"shasum":"a40d5a5cf4b6199341ccaff509a331f920e6d6c6","tarball":"https://registry.npmjs.org/@bravobit/eslint-plugin-import-sorter/-/eslint-plugin-import-sorter-1.0.0.tgz","fileCount":7,"integrity":"sha512-N+2LQVLW6SBaMrHKiab6vig8xjeuc1+VQOrZQM3oZPa2VDF9bFWwVaeQ/yA1I0qZ9OsHHoQpH1XteG6PFOf1yg==","signatures":[{"sig":"MEYCIQDUprGiMDauufVCWdmL5TNWvTFlYL5LyGcOPYzACMelvAIhAM/sh0LSYlqCfd52ec2C0iiUCFPSbcbjT8F8dFg+hbfa","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":42493},"main":"./dist/index.cjs","type":"module","types":"./dist/index.d.ts","engines":{"node":"^20.19.0 || ^22.13.0 || >=24"},"exports":{".":{"import":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"require":{"types":"./dist/index.d.cts","default":"./dist/index.cjs"}},"./package.json":"./package.json"},"gitHead":"28a3d9e4b7f7b43cf460777f6c5c5f138776b402","scripts":{"attw":"attw --pack .","lint":"eslint .","test":"vitest run","bench":"vitest bench --run","build":"tsdown","publint":"publint","release":"pnpm build && changeset publish","changeset":"changeset","typecheck":"tsc --noEmit","test:coverage":"vitest run --coverage"},"_npmUser":{"name":"stanvanheumen","email":"stanvanheumen@gmail.com"},"repository":{"url":"git+https://github.com/bravobit/eslint-plugin-import-sorter.git","type":"git"},"_npmVersion":"12.0.2","description":"ESLint plugin that sorts top-level imports by line length, longest first.","directories":{},"sideEffects":false,"_nodeVersion":"24.18.0","publishConfig":{"access":"public","provenance":true},"_hasShrinkwrap":false,"packageManager":"pnpm@11.22.0","devDependencies":{"jiti":"^2.0.0","eslint":"^10.8.1","espree":"^11.2.0","tsdown":"^0.22.14","vitest":"^4.1.11","publint":"^0.3.24","fast-check":"^4.9.0","typescript":"6.0.3","@types/node":"^24.0.0","@types/estree":"^1.0.6","@changesets/cli":"^3.0.1","typescript-eslint":"^8.67.0","@vitest/coverage-v8":"^4.1.11","@arethetypeswrong/cli":"^0.18.5","@typescript-eslint/parser":"^8.67.0","eslint-vitest-rule-tester":"^3.1.0"},"peerDependencies":{"eslint":"^9.15.0 || ^10.0.0"},"_npmOperationalInternal":{"tmp":"tmp/eslint-plugin-import-sorter_1.0.0_1787175924362_0.7450502714159717","host":"s3://npm-registry-packages-npm-production"},"deprecated":"Renamed to @bravobit/eslint-plugin-waterfall"}},"time":{"created":"2026-08-19T21:45:24.202Z","modified":"2026-08-20T09:42:47.224Z","1.0.0":"2026-08-19T21:45:24.506Z"},"bugs":{"url":"https://github.com/bravobit/eslint-plugin-import-sorter/issues"},"license":"MIT","homepage":"https://github.com/bravobit/eslint-plugin-import-sorter#readme","keywords":["eslint","eslintplugin","eslint-plugin","imports","sort","line-length"],"repository":{"url":"git+https://github.com/bravobit/eslint-plugin-import-sorter.git","type":"git"},"description":"ESLint plugin that sorts top-level imports by line length, longest first.","maintainers":[{"name":"stanvanheumen","email":"stanvanheumen@gmail.com"}],"readme":"# @bravobit/eslint-plugin-import-sorter\n\nESLint plugin that sorts top-level imports by **line length, longest first** —\nthe visual \"waterfall\" — with an autofix. No grouping by origin, no\nalphabetizing; just the ladder.\n\n```ts\n// Before\nimport {toSignal} from '@angular/core/rxjs-interop';\nimport {RouterLink} from '@angular/router';\nimport {ChangeDetectionStrategy, Component, ElementRef, inject, OnInit, Renderer2, ViewEncapsulation} from '@angular/core';\nimport {BbAvatar, BbButton, BbFormControl, BbFormSubmit, BbIcon, BbInput} from '@bravobit/bb-foundation/elements';\n\n// After\nimport {ChangeDetectionStrategy, Component, ElementRef, inject, OnInit, Renderer2, ViewEncapsulation} from '@angular/core';\nimport {BbAvatar, BbButton, BbFormControl, BbFormSubmit, BbIcon, BbInput} from '@bravobit/bb-foundation/elements';\nimport {toSignal} from '@angular/core/rxjs-interop';\nimport {RouterLink} from '@angular/router';\n```\n\nThe sort key, in order:\n\n1. **Length of the import as one line** — longest first. Multiline imports are\n   *measured* as if they were a single line, but never reformatted. Length is\n   counted in UTF-16 code units (`String.prototype.length`), so an emoji in a\n   module path counts as 2.\n2. **On equal length: the position of the `from` keyword** — furthest right\n   first. A longer specifier list with a shorter module path wins.\n3. **On a full tie: the original order** (stable).\n\nComments travel with their import, license headers and directive comments\n(`eslint-disable`, `@ts-*`, `prettier-ignore`, …) stay exactly where they are,\nand blank lines split imports into independently sorted blocks.\n\n## Install\n\n```bash\npnpm add -D @bravobit/eslint-plugin-import-sorter\nnpm install -D @bravobit/eslint-plugin-import-sorter\nyarn add -D @bravobit/eslint-plugin-import-sorter\n```\n\nRequires ESLint `^9.15.0 || ^10.0.0` (flat config) and Node 20.19+, 22.13+ or 24+.\n\n## Usage (flat config, ESM)\n\n```js\n// eslint.config.js\nimport importSorter from '@bravobit/eslint-plugin-import-sorter';\n\nexport default [\n  ...importSorter.configs.recommended, // sort-imports as \"warn\"\n  // or: ...importSorter.configs.all   // both rules as \"error\"\n];\n```\n\n## Usage (flat config, CommonJS)\n\nAngular CLI still generates a CommonJS `eslint.config.js`; that works too:\n\n```js\n// eslint.config.js\nconst importSorter = require('@bravobit/eslint-plugin-import-sorter');\n\nmodule.exports = [...importSorter.configs.recommended];\n```\n\nOr configure the rules by hand:\n\n```js\nexport default [\n  {\n    plugins: { 'import-sorter': importSorter },\n    rules: {\n      'import-sorter/sort-imports': ['warn', { order: 'desc' }],\n      'import-sorter/sort-named-imports': 'warn',\n    },\n  },\n];\n```\n\n## Rules\n\n| Rule | Description |\n| --- | --- |\n| [`import-sorter/sort-imports`](docs/rules/sort-imports.md) | Sort top-level imports by line length, longest first. |\n| [`import-sorter/sort-named-imports`](docs/rules/sort-named-imports.md) | Sort the specifiers inside the braces by length, longest first. |\n\n## Options — `sort-imports`\n\n| Option | Type | Default | Behaviour |\n| --- | --- | --- | --- |\n| `order` | `'desc' \\| 'asc'` | `'desc'` | `desc` puts the longest on top. `asc` also flips the `from` tiebreaker. |\n| `groupByBlankLine` | `boolean` | `true` | `false` ignores blank lines between imports and sorts the whole contiguous import block as one (the blank lines disappear). |\n| `pinSideEffectImports` | `boolean` | `true` | `import 'polyfill'` keeps its position; the rest sorts around it. See the warning below before turning this off. |\n| `moveComments` | `boolean` | `true` | `false` leaves every comment where it is; comments then act as block breakers. |\n| `pinnedCommentPattern` | `string` (regex source) | – | Extra patterns for comments that must never move, on top of the built-in list (`eslint-disable`, `@ts-*`, `@license`, `prettier-ignore`, `istanbul ignore`, `webpackChunkName`, …). |\n| `minimumImports` | `integer >= 2` | `2` | Blocks with fewer imports than this are never reported. |\n\n## Options — `sort-named-imports`\n\n| Option | Type | Default | Behaviour |\n| --- | --- | --- | --- |\n| `order` | `'desc' \\| 'asc'` | `'desc'` | Longest specifier first, or shortest first. |\n\n## Does this work with Prettier?\n\n**Watch out.** At a normal `printWidth` (80–120) Prettier wraps long imports\nonto multiple lines, and a wrapped import no longer *looks* long — the visual\nwaterfall is gone. The rule still measures multiline imports correctly (as if\nthey were one line), but the aesthetic effect only survives if imports stay on\none line. Recommendations:\n\n- set `printWidth` high enough that imports do not wrap, or\n- exclude imports from wrapping (keep them out of Prettier's reach), or\n- accept that only the not-yet-wrapped imports show the ladder.\n\n## Why not eslint-plugin-perfectionist?\n\nPerfectionist's `sort-imports` supports `type: 'line-length', order: 'desc'`,\nbut its `fallbackSort` only supports *alphabetical / natural / line-length /\ncustom* — not \"position of the `from` keyword\". Two imports of equal length\nthen tie-break alphabetically instead of by where `from` sits, which breaks the\nwaterfall on exactly the lines where it is most visible.\n\nIf you run both plugins, disable the overlapping rules — they will fight over\nthe same lines otherwise:\n\n```js\nrules: {\n  'perfectionist/sort-imports': 'off',\n  'import/order': 'off',\n  'sort-imports': 'off', // ESLint core\n}\n```\n\n## Side-effect imports and execution order\n\nSide-effect imports (`import 'zone.js';`) run code when they load, and the\norder in which they run can matter. That is why `pinSideEffectImports`\ndefaults to `true`: those imports stay exactly where they are and everything\nelse sorts around them. If you set it to `false`, side-effect imports sort by\nlength like the rest — **this can change your program's runtime behaviour.**\nOnly do that when you know the order is irrelevant.\n\n## Adopting in an existing codebase\n\nThe first `--fix` over a large repo touches a lot of lines. Do that run as a\nseparate, mechanical commit so reviews and `git blame` stay usable:\n\n```bash\nnpx eslint --fix .\ngit commit -am \"style: sort imports by line length\"\n```\n\n## Non-goals\n\nThis plugin deliberately does **not**:\n\n- group imports by category (builtin/external/internal/…) — use\n  `eslint-plugin-perfectionist` or `import/order` for that;\n- sort alphabetically;\n- add or remove blank lines between groups;\n- reformat imports (multiline ↔ single line) — it only measures;\n- deduplicate imports;\n- sort `export … from` statements.\n\n## TypeScript note\n\nTooling in this repo pins TypeScript to 6.x: `typescript-eslint` declares\n`typescript >=4.8.4 <6.1.0` as its peer range, so TypeScript 7 is blocked in\n`renovate.json` until typescript-eslint supports it. This only affects\ndeveloping the plugin itself, not consuming it.\n\n## License\n\nMIT © Bravobit\n","readmeFilename":"README.md"}