{"_id":"@bravobit/eslint-plugin-waterfall","_rev":"3-30722493705d57195dc1c3974ef9ed0f","name":"@bravobit/eslint-plugin-waterfall","dist-tags":{"latest":"2.0.2"},"versions":{"2.0.0":{"name":"@bravobit/eslint-plugin-waterfall","version":"2.0.0","keywords":["eslint","eslintplugin","eslint-plugin","imports","css","scss","sort","line-length","waterfall"],"license":"MIT","_id":"@bravobit/eslint-plugin-waterfall@2.0.0","maintainers":[{"name":"stanvanheumen","email":"stanvanheumen@gmail.com"}],"homepage":"https://github.com/bravobit/eslint-plugin-waterfall#readme","bugs":{"url":"https://github.com/bravobit/eslint-plugin-waterfall/issues"},"dist":{"shasum":"f0e26a32b9a53b5a8e19a992efb9a61d6ae2d661","tarball":"https://registry.npmjs.org/@bravobit/eslint-plugin-waterfall/-/eslint-plugin-waterfall-2.0.0.tgz","fileCount":7,"integrity":"sha512-uWS1fvXyP+AHg5m18tDoyZie5zLqAhCaxTCEylStifOJlahi79Lq9O7BFpt0nuj9mJDDsfFUVk2n7wzd61qV5w==","signatures":[{"sig":"MEYCIQCeTW2fYmwZOy639E4brG1OCDYU4P5X/sAD5usQ/0A+VQIhAPRwhIWiS6Uwc9D4sRO3CWe2qjXx08vSHaSWrzzUwffo","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":80908},"main":"./dist/index.cjs","type":"module","types":"./dist/index.d.cts","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":"c6a35411b2febe5100e71b7ae9702eaff7b469d8","scripts":{"attw":"attw --pack .","lint":"eslint .","test":"vitest run","build":"tsdown && node scripts/fix-cjs-types.mjs","publint":"publint","typecheck":"tsc --noEmit","test:coverage":"vitest run --coverage","prepublishOnly":"npm run typecheck && npm run lint && npm test && npm run build && npm run publint && npm run attw"},"_npmUser":{"name":"stanvanheumen","email":"stanvanheumen@gmail.com"},"repository":{"url":"git+https://github.com/bravobit/eslint-plugin-waterfall.git","type":"git"},"_npmVersion":"12.0.2","description":"ESLint plugin that sorts imports and CSS/SCSS declarations by line length, longest first — the waterfall.","directories":{},"sideEffects":false,"_nodeVersion":"24.18.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"jiti":"^2.0.0","unrun":"^0.3.1","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","@eslint/core":"^1.2.1","@types/estree":"^1.0.6","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-waterfall_2.0.0_1787218932567_0.8213784441306256","host":"s3://npm-registry-packages-npm-production"}},"2.0.1":{"name":"@bravobit/eslint-plugin-waterfall","version":"2.0.1","keywords":["eslint","eslintplugin","eslint-plugin","imports","css","scss","sort","line-length","waterfall"],"license":"MIT","_id":"@bravobit/eslint-plugin-waterfall@2.0.1","maintainers":[{"name":"stanvanheumen","email":"stanvanheumen@gmail.com"}],"homepage":"https://github.com/bravobit/eslint-plugin-waterfall#readme","bugs":{"url":"https://github.com/bravobit/eslint-plugin-waterfall/issues"},"dist":{"shasum":"e63cc9a23992a1658428143b9c279e158044c12c","tarball":"https://registry.npmjs.org/@bravobit/eslint-plugin-waterfall/-/eslint-plugin-waterfall-2.0.1.tgz","fileCount":7,"integrity":"sha512-wnPEswJlzRxM5JLp1g+WArtq2B+86YqznqfAL4fSRsKnZJR+OhGil+wbvf+EtdqmgRxuaVEQC9P7/H25gOJumA==","signatures":[{"sig":"MEQCIG+Si6p4Am997bR77SGmTRJJDv5hKDhpl++ez5TsEosRAiBZvcBazNSunc/qj59ry7AHRRVUmiE1pAYmMmAIeXY99w==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":81498},"main":"./dist/index.cjs","type":"module","types":"./dist/index.d.cts","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":"0b14ae2cc22f6cdd782d9d419bde21224822ac5e","scripts":{"attw":"attw --pack .","lint":"eslint .","test":"vitest run","build":"tsdown && node scripts/fix-cjs-types.mjs","publint":"publint","typecheck":"tsc --noEmit","test:coverage":"vitest run --coverage","prepublishOnly":"npm run typecheck && npm run lint && npm test && npm run build && npm run publint && npm run attw"},"_npmUser":{"name":"stanvanheumen","email":"stanvanheumen@gmail.com"},"repository":{"url":"git+https://github.com/bravobit/eslint-plugin-waterfall.git","type":"git"},"_npmVersion":"12.0.2","description":"ESLint plugin that sorts imports and CSS/SCSS declarations by line length, longest first — the waterfall.","directories":{},"sideEffects":false,"_nodeVersion":"24.18.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"jiti":"^2.0.0","unrun":"^0.3.1","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","@eslint/core":"^1.2.1","@types/estree":"^1.0.6","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-waterfall_2.0.1_1787219411113_0.7456648488679631","host":"s3://npm-registry-packages-npm-production"}},"2.0.2":{"name":"@bravobit/eslint-plugin-waterfall","version":"2.0.2","description":"ESLint plugin that sorts imports and CSS/SCSS declarations by line length, longest first — the waterfall.","type":"module","license":"MIT","repository":{"type":"git","url":"git+https://github.com/bravobit/eslint-plugin-waterfall.git"},"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"},"main":"./dist/index.cjs","types":"./dist/index.d.cts","sideEffects":false,"scripts":{"build":"tsdown && node scripts/fix-cjs-types.mjs","typecheck":"tsc --noEmit","lint":"eslint .","test":"vitest run","test:coverage":"vitest run --coverage","publint":"publint","attw":"attw --pack .","prepublishOnly":"npm run typecheck && npm run lint && npm test && npm run build && npm run publint && npm run attw"},"peerDependencies":{"eslint":"^9.15.0 || ^10.0.0"},"devDependencies":{"@arethetypeswrong/cli":"^0.18.5","@eslint/core":"^1.2.1","@types/estree":"^1.0.6","@types/node":"^24.0.0","@typescript-eslint/parser":"^8.67.0","@vitest/coverage-v8":"^4.1.11","eslint":"^10.8.1","eslint-vitest-rule-tester":"^3.1.0","espree":"^11.2.0","fast-check":"^4.9.0","jiti":"^2.0.0","publint":"^0.3.24","tsdown":"^0.22.14","typescript":"6.0.3","typescript-eslint":"^8.67.0","unrun":"^0.3.1","vitest":"^4.1.11"},"publishConfig":{"access":"public"},"keywords":["eslint","eslintplugin","eslint-plugin","imports","css","scss","sort","line-length","waterfall"],"gitHead":"8f35a0f21689f2fec777aaa959608f890e549dd1","_id":"@bravobit/eslint-plugin-waterfall@2.0.2","bugs":{"url":"https://github.com/bravobit/eslint-plugin-waterfall/issues"},"homepage":"https://github.com/bravobit/eslint-plugin-waterfall#readme","_nodeVersion":"24.18.0","_npmVersion":"12.0.2","dist":{"integrity":"sha512-QNqdn06cGyxOki/UNCi0jeIA7C7GpyvRIuvGZvRT+WdVX6qkam8N9807jFTTpLD+kwseJOlyHzV/hWGfOB+niw==","shasum":"898bd79f2e67c3eb1058516e09ac5eef0be44405","tarball":"https://registry.npmjs.org/@bravobit/eslint-plugin-waterfall/-/eslint-plugin-waterfall-2.0.2.tgz","fileCount":7,"unpackedSize":82819,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEYCIQCo/e+J7WhaRYGF4qO4UTgv/UNu1gm2Mm4qq7fudTD6ZAIhAKAOlsk5D5eqLF64JFLGaxY8ZXuyPOLkdcXZKkf3OedJ"}]},"_npmUser":{"name":"stanvanheumen","email":"stanvanheumen@gmail.com"},"directories":{},"maintainers":[{"name":"stanvanheumen","email":"stanvanheumen@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/eslint-plugin-waterfall_2.0.2_1787220807954_0.9365023896843709"},"_hasShrinkwrap":false}},"time":{"created":"2026-08-20T09:42:12.285Z","modified":"2026-08-20T10:13:28.354Z","2.0.0":"2026-08-20T09:42:12.726Z","2.0.1":"2026-08-20T09:50:11.276Z","2.0.2":"2026-08-20T10:13:28.121Z"},"bugs":{"url":"https://github.com/bravobit/eslint-plugin-waterfall/issues"},"license":"MIT","homepage":"https://github.com/bravobit/eslint-plugin-waterfall#readme","keywords":["eslint","eslintplugin","eslint-plugin","imports","css","scss","sort","line-length","waterfall"],"repository":{"type":"git","url":"git+https://github.com/bravobit/eslint-plugin-waterfall.git"},"description":"ESLint plugin that sorts imports and CSS/SCSS declarations by line length, longest first — the waterfall.","maintainers":[{"name":"stanvanheumen","email":"stanvanheumen@gmail.com"}],"readme":"# @bravobit/eslint-plugin-waterfall\n\nESLint plugin that sorts by **line length, longest first** — the visual\n\"waterfall\". It does this in two places:\n\n- **`waterfall/sort-imports`** — top-level imports in JS/TS files;\n- **`waterfall/sort-declarations`** — declarations inside the braces of CSS\n  and SCSS rules (there the waterfall runs the other way: shortest first),\n  via the built-in `waterfall/css` language.\n\nNo grouping by origin, no alphabetizing, no options to study. Just the ladder.\n\n```ts\n// imports, after --fix\nimport {ChangeDetectionStrategy, Component, ElementRef, inject, OnInit} from '@angular/core';\nimport {BbAvatar, BbButton, BbFormControl, BbIcon} from '@bravobit/bb-foundation/elements';\nimport {toSignal} from '@angular/core/rxjs-interop';\nimport {RouterLink} from '@angular/router';\n```\n\n```scss\n// scss, after --fix — shortest first\n.button {\n    color: white;\n    display: flex;\n    border-radius: 4px;\n    background-color: $color-primary;\n\n    &:hover {\n        cursor: pointer;\n        background-color: $color-primary-dark;\n    }\n}\n```\n\nThe sort key for imports: length of the statement as one line (UTF-16 code\nunits), then — on a tie — the position of the `from` keyword (furthest right\nfirst), then the original order (stable). Declarations sort the other way\naround — shortest first, with the position of the `:` (furthest left first)\nas tiebreaker — so a block widens toward its base.\nMultiline statements are *measured* as one line but never reformatted.\n\nComments travel with their statement; license headers and directive comments\n(`eslint-disable`, `stylelint-disable`, `@ts-*`, `prettier-ignore`, …) stay\nexactly where they are and act as sort boundaries. Blank lines split\nstatements into independently sorted groups.\n\n## Install\n\n```bash\nnpm install -D @bravobit/eslint-plugin-waterfall\n```\n\nRequires ESLint `^9.15.0 || ^10.0.0` (flat config) and Node 20.19+, 22.13+ or 24+.\n\n## Usage\n\n```js\n// eslint.config.js / eslint.config.mjs\nimport waterfall from '@bravobit/eslint-plugin-waterfall';\n\nexport default [\n  ...waterfall.configs.recommended, // both rules as \"warn\", js/ts + css/scss\n];\n```\n\nCommonJS works identically (`const waterfall = require('@bravobit/eslint-plugin-waterfall')`).\n\nOr configure by hand:\n\n```js\nexport default [\n  {\n    files: ['**/*.{js,ts}'],\n    plugins: { waterfall },\n    rules: { 'waterfall/sort-imports': 'warn' },\n  },\n  {\n    files: ['**/*.{css,scss}'],\n    plugins: { waterfall },\n    language: 'waterfall/css',\n    rules: { 'waterfall/sort-declarations': 'warn' },\n  },\n];\n```\n\n## Rules\n\n| Rule | Description |\n| --- | --- |\n| [`waterfall/sort-imports`](docs/rules/sort-imports.md) | Sort top-level imports by line length, longest first. |\n| [`waterfall/sort-declarations`](docs/rules/sort-declarations.md) | Sort CSS/SCSS declarations by line length, shortest first. |\n\n## Options\n\nDeliberately minimal:\n\n| Rule | Option | Default | Behaviour |\n| --- | --- | --- | --- |\n| `sort-imports` | `pinSideEffectImports` | `true` | `import 'polyfill'` keeps its position; the rest sorts around it. Turning this off can change runtime behaviour. |\n| both | `pinnedCommentPattern` | – | Extra regex for comments that must never move, on top of the built-in directive list. |\n\nEverything else is fixed behaviour: imports longest first, declarations\nshortest first, blank lines split groups, comments move with their statement,\ngroups need at least two entries.\n\n## What sort-declarations will not touch\n\n- **SCSS variables** (`$x: …`) — order-dependent, they stay put and split groups.\n- **At-rules** (`@include`, `@extend`, …) and nested rules — boundaries.\n- **Duplicate properties** in one group (`display: -webkit-box; display: flex;`)\n  — cascade fallbacks whose order is meaning, the whole group is left alone.\n- Anything the tolerant scanner does not recognise — unknown syntax becomes a\n  boundary, never a rewrite.\n\n## Does this work with Prettier / stylelint?\n\nAt a normal `printWidth`, Prettier wraps long imports onto multiple lines and\nthe visual waterfall disappears (the rule still measures wrapped statements\ncorrectly). Set `printWidth` high enough that imports stay on one line, or\naccept the effect only where they do. Stylelint's ordering rules\n(`order/properties-alphabetical-order` etc.) fight `sort-declarations` —\nenable one, not both.\n\n## Sorting inside the import braces\n\n`waterfall/sort-imports` orders whole lines, not the names inside `{ … }`.\nFor alphabetical specifiers use ESLint core, which composes cleanly:\n\n```js\nrules: {\n  'waterfall/sort-imports': 'warn',\n  'sort-imports': ['warn', { ignoreDeclarationSort: true, ignoreCase: true }],\n}\n```\n\n## Why not eslint-plugin-perfectionist?\n\nPerfectionist can sort by line length, but its `fallbackSort` cannot break\nties on the position of the `from` keyword — exactly the lines where the\nwaterfall is most visible. If you run both, disable\n`perfectionist/sort-imports` and `import/order`.\n\n## Adopting in an existing codebase\n\nThe first `--fix` touches a lot of lines. Do it as a separate, mechanical\ncommit so reviews and `git blame` stay usable.\n\n## Non-goals\n\nNo grouping by category, no alphabetical sorting of statements, no blank-line\nmanagement, no reformatting (multiline ↔ single line), no deduplication, no\n`export … from` sorting, no LESS/Stylus.\n\n## License\n\nMIT © Bravobit\n","readmeFilename":"README.md"}