@shivaedev/quality 0.23.0 → 0.25.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +18 -0
- package/README.md +6 -2
- package/dist/baseline/compare.d.ts +2 -2
- package/dist/baseline/compare.d.ts.map +1 -1
- package/dist/baseline/compare.js +6 -4
- package/dist/baseline/compare.js.map +1 -1
- package/dist/baseline/format.d.ts +5 -5
- package/dist/baseline/format.d.ts.map +1 -1
- package/dist/baseline/format.js +23 -13
- package/dist/baseline/format.js.map +1 -1
- package/dist/baseline/legacy.d.ts +2 -2
- package/dist/baseline/legacy.d.ts.map +1 -1
- package/dist/baseline/legacy.js +4 -4
- package/dist/baseline/legacy.js.map +1 -1
- package/dist/baseline/rewrite.d.ts +1 -1
- package/dist/baseline/rewrite.d.ts.map +1 -1
- package/dist/baseline/rewrite.js +2 -2
- package/dist/baseline/rewrite.js.map +1 -1
- package/dist/baseline/update.d.ts +2 -2
- package/dist/baseline/update.d.ts.map +1 -1
- package/dist/baseline/update.js +27 -21
- package/dist/baseline/update.js.map +1 -1
- package/dist/cli/args.d.ts +2 -4
- package/dist/cli/args.d.ts.map +1 -1
- package/dist/cli/args.js +19 -18
- package/dist/cli/args.js.map +1 -1
- package/dist/cli/baseline.d.ts +1 -1
- package/dist/cli/baseline.d.ts.map +1 -1
- package/dist/cli/baseline.js +10 -6
- package/dist/cli/baseline.js.map +1 -1
- package/dist/cli/fix.d.ts +2 -2
- package/dist/cli/fix.d.ts.map +1 -1
- package/dist/cli/fix.js +35 -41
- package/dist/cli/fix.js.map +1 -1
- package/dist/cli/lint.d.ts +1 -2
- package/dist/cli/lint.d.ts.map +1 -1
- package/dist/cli/lint.js +1 -2
- package/dist/cli/lint.js.map +1 -1
- package/dist/cli/messages.d.ts +1 -1
- package/dist/cli/messages.d.ts.map +1 -1
- package/dist/cli/messages.js +2 -2
- package/dist/cli/migrate.d.ts.map +1 -1
- package/dist/cli/migrate.js +2 -2
- package/dist/cli/migrate.js.map +1 -1
- package/dist/cli/migrateExceptions.js +1 -1
- package/dist/cli/migrateExceptions.js.map +1 -1
- package/dist/cli/program.js +1 -1
- package/dist/cli/program.js.map +1 -1
- package/dist/cli/{run-main.d.ts → runMain.d.ts} +1 -1
- package/dist/cli/runMain.d.ts.map +1 -0
- package/dist/cli/{run-main.js → runMain.js} +3 -3
- package/dist/cli/runMain.js.map +1 -0
- package/dist/cli/shrink.d.ts +2 -2
- package/dist/cli/shrink.d.ts.map +1 -1
- package/dist/cli/shrink.js +9 -3
- package/dist/cli/shrink.js.map +1 -1
- package/dist/cli.js +1 -1
- package/dist/cli.js.map +1 -1
- package/dist/config/decode.d.ts +1 -1
- package/dist/config/decode.d.ts.map +1 -1
- package/dist/config/decode.js +13 -9
- package/dist/config/decode.js.map +1 -1
- package/dist/config/load.d.ts.map +1 -1
- package/dist/config/load.js +26 -20
- package/dist/config/load.js.map +1 -1
- package/dist/config/resolve.d.ts +23 -3
- package/dist/config/resolve.d.ts.map +1 -1
- package/dist/config/resolve.js +19 -11
- package/dist/config/resolve.js.map +1 -1
- package/dist/config.d.ts +3 -3
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +3 -1
- package/dist/config.js.map +1 -1
- package/dist/decoded.d.ts +3 -3
- package/dist/decoded.d.ts.map +1 -1
- package/dist/decoded.js +8 -6
- package/dist/decoded.js.map +1 -1
- package/dist/engine/{baseline-file.d.ts → baselineFile.d.ts} +2 -2
- package/dist/engine/baselineFile.d.ts.map +1 -0
- package/dist/engine/{baseline-file.js → baselineFile.js} +5 -3
- package/dist/engine/baselineFile.js.map +1 -0
- package/dist/engine/evaluate.d.ts +2 -3
- package/dist/engine/evaluate.d.ts.map +1 -1
- package/dist/engine/evaluate.js +6 -5
- package/dist/engine/evaluate.js.map +1 -1
- package/dist/engine/{run-rules.d.ts → runRules.d.ts} +2 -2
- package/dist/engine/runRules.d.ts.map +1 -0
- package/dist/engine/runRules.js +44 -0
- package/dist/engine/runRules.js.map +1 -0
- package/dist/engine/session.d.ts +1 -1
- package/dist/engine/session.d.ts.map +1 -1
- package/dist/engine/session.js +10 -6
- package/dist/engine/session.js.map +1 -1
- package/dist/engine/violation.d.ts +8 -8
- package/dist/engine/violation.d.ts.map +1 -1
- package/dist/engine/violation.js +23 -11
- package/dist/engine/violation.js.map +1 -1
- package/dist/exceptions/registry.d.ts +2 -2
- package/dist/exceptions/registry.d.ts.map +1 -1
- package/dist/exceptions/registry.js +10 -6
- package/dist/exceptions/registry.js.map +1 -1
- package/dist/git/base.d.ts +1 -2
- package/dist/git/base.d.ts.map +1 -1
- package/dist/git/base.js +22 -10
- package/dist/git/base.js.map +1 -1
- package/dist/git/command.d.ts +2 -2
- package/dist/git/command.d.ts.map +1 -1
- package/dist/git/command.js +13 -9
- package/dist/git/command.js.map +1 -1
- package/dist/git/history.d.ts +1 -1
- package/dist/git/history.d.ts.map +1 -1
- package/dist/git/history.js +5 -3
- package/dist/git/history.js.map +1 -1
- package/dist/hooks/preCommit.js +1 -1
- package/dist/hooks/preCommit.js.map +1 -1
- package/dist/imports/generated.js +1 -1
- package/dist/imports/generated.js.map +1 -1
- package/dist/imports/{pnpm-workspace.d.ts → pnpmWorkspacePatterns.d.ts} +1 -1
- package/dist/imports/pnpmWorkspacePatterns.d.ts.map +1 -0
- package/dist/imports/{pnpm-workspace.js → pnpmWorkspacePatterns.js} +1 -1
- package/dist/imports/pnpmWorkspacePatterns.js.map +1 -0
- package/dist/imports/resolve.js +1 -1
- package/dist/imports/resolve.js.map +1 -1
- package/dist/imports/workspace.js +1 -1
- package/dist/imports/workspace.js.map +1 -1
- package/dist/inventory/collect.d.ts +1 -1
- package/dist/inventory/collect.d.ts.map +1 -1
- package/dist/inventory/collect.js +8 -6
- package/dist/inventory/collect.js.map +1 -1
- package/dist/inventory/filesystem.d.ts +1 -1
- package/dist/inventory/filesystem.d.ts.map +1 -1
- package/dist/inventory/filesystem.js +11 -7
- package/dist/inventory/filesystem.js.map +1 -1
- package/dist/inventory/ignoreScope.d.ts +17 -0
- package/dist/inventory/ignoreScope.d.ts.map +1 -0
- package/dist/inventory/{ignore-scope.js → ignoreScope.js} +19 -10
- package/dist/inventory/ignoreScope.js.map +1 -0
- package/dist/inventory/walk.d.ts +1 -1
- package/dist/inventory/walk.d.ts.map +1 -1
- package/dist/inventory/walk.js +1 -1
- package/dist/inventory/walk.js.map +1 -1
- package/dist/manifests/sorted.d.ts +1 -1
- package/dist/manifests/sorted.d.ts.map +1 -1
- package/dist/manifests/sorted.js +15 -17
- package/dist/manifests/sorted.js.map +1 -1
- package/dist/report/plural.d.ts +1 -1
- package/dist/report/plural.d.ts.map +1 -1
- package/dist/report/plural.js +3 -1
- package/dist/report/plural.js.map +1 -1
- package/dist/report/render.d.ts +1 -3
- package/dist/report/render.d.ts.map +1 -1
- package/dist/report/render.js +30 -32
- package/dist/report/render.js.map +1 -1
- package/dist/report/stale.d.ts +1 -1
- package/dist/report/stale.d.ts.map +1 -1
- package/dist/report/stale.js +18 -8
- package/dist/report/stale.js.map +1 -1
- package/dist/rule.d.ts +14 -14
- package/dist/rule.d.ts.map +1 -1
- package/dist/rule.js +11 -9
- package/dist/rule.js.map +1 -1
- package/dist/rules/{built-in.d.ts → builtIn.d.ts} +3 -3
- package/dist/rules/builtIn.d.ts.map +1 -0
- package/dist/rules/{built-in.js → builtIn.js} +17 -13
- package/dist/rules/builtIn.js.map +1 -0
- package/dist/rules/comments/{pattern-rule.d.ts → definePattern.d.ts} +2 -2
- package/dist/rules/comments/definePattern.d.ts.map +1 -0
- package/dist/rules/comments/definePattern.js +18 -0
- package/dist/rules/comments/definePattern.js.map +1 -0
- package/dist/rules/comments/kinds.d.ts +4 -4
- package/dist/rules/comments/kinds.d.ts.map +1 -1
- package/dist/rules/comments/kinds.js +16 -8
- package/dist/rules/comments/kinds.js.map +1 -1
- package/dist/rules/comments/{max-per-file.d.ts → maxPerFile.d.ts} +1 -1
- package/dist/rules/comments/maxPerFile.d.ts.map +1 -0
- package/dist/rules/comments/{max-per-file.js → maxPerFile.js} +14 -10
- package/dist/rules/comments/maxPerFile.js.map +1 -0
- package/dist/rules/comments/{no-banner.d.ts → noBanner.d.ts} +1 -1
- package/dist/rules/comments/noBanner.d.ts.map +1 -0
- package/dist/rules/comments/{no-banner.js → noBanner.js} +2 -2
- package/dist/rules/comments/noBanner.js.map +1 -0
- package/dist/rules/comments/{no-environment-pragma.d.ts → noEnvironmentPragma.d.ts} +1 -1
- package/dist/rules/comments/noEnvironmentPragma.d.ts.map +1 -0
- package/dist/rules/comments/{no-environment-pragma.js → noEnvironmentPragma.js} +2 -2
- package/dist/rules/comments/noEnvironmentPragma.js.map +1 -0
- package/dist/rules/comments/{no-jsdoc.d.ts → noJsdoc.d.ts} +1 -1
- package/dist/rules/comments/noJsdoc.d.ts.map +1 -0
- package/dist/rules/comments/{no-jsdoc.js → noJsdoc.js} +1 -1
- package/dist/rules/comments/noJsdoc.js.map +1 -0
- package/dist/rules/comments/{no-line-reference.d.ts → noLineReference.d.ts} +1 -1
- package/dist/rules/comments/noLineReference.d.ts.map +1 -0
- package/dist/rules/comments/{no-line-reference.js → noLineReference.js} +2 -2
- package/dist/rules/comments/noLineReference.js.map +1 -0
- package/dist/rules/comments/{no-pr-reference.d.ts → noPrReference.d.ts} +1 -1
- package/dist/rules/comments/noPrReference.d.ts.map +1 -0
- package/dist/rules/comments/{no-pr-reference.js → noPrReference.js} +2 -2
- package/dist/rules/comments/noPrReference.js.map +1 -0
- package/dist/rules/comments/{no-todo.d.ts → noTodo.d.ts} +1 -1
- package/dist/rules/comments/noTodo.d.ts.map +1 -0
- package/dist/rules/comments/{no-todo.js → noTodo.js} +2 -2
- package/dist/rules/comments/noTodo.js.map +1 -0
- package/dist/rules/comments/scan.d.ts +3 -3
- package/dist/rules/comments/scan.d.ts.map +1 -1
- package/dist/rules/comments/scan.js +23 -19
- package/dist/rules/comments/scan.js.map +1 -1
- package/dist/rules/{manifests-sorted.d.ts → manifestsSorted.d.ts} +1 -1
- package/dist/rules/manifestsSorted.d.ts.map +1 -0
- package/dist/rules/{manifests-sorted.js → manifestsSorted.js} +1 -1
- package/dist/rules/manifestsSorted.js.map +1 -0
- package/dist/rules/manifestsSourceFirst.d.ts +2 -0
- package/dist/rules/manifestsSourceFirst.d.ts.map +1 -0
- package/dist/rules/manifestsSourceFirst.js +43 -0
- package/dist/rules/manifestsSourceFirst.js.map +1 -0
- package/dist/rules/{max-lines.d.ts → maxLines.d.ts} +1 -1
- package/dist/rules/maxLines.d.ts.map +1 -0
- package/dist/rules/{max-lines.js → maxLines.js} +13 -11
- package/dist/rules/maxLines.js.map +1 -0
- package/dist/rules/stories/origins.d.ts +10 -0
- package/dist/rules/stories/origins.d.ts.map +1 -0
- package/dist/rules/stories/origins.js +129 -0
- package/dist/rules/stories/origins.js.map +1 -0
- package/dist/rules/stories/prerequisites.d.ts +4 -0
- package/dist/rules/stories/prerequisites.d.ts.map +1 -0
- package/dist/rules/stories/prerequisites.js +32 -0
- package/dist/rules/stories/prerequisites.js.map +1 -0
- package/dist/rules/stories/program.d.ts +5 -0
- package/dist/rules/stories/program.d.ts.map +1 -0
- package/dist/rules/stories/program.js +22 -0
- package/dist/rules/stories/program.js.map +1 -0
- package/dist/rules/stories/specs.d.ts +2 -0
- package/dist/rules/stories/specs.d.ts.map +1 -0
- package/dist/rules/stories/specs.js +80 -0
- package/dist/rules/stories/specs.js.map +1 -0
- package/dist/rules/suppressions/biome/configs.d.ts +1 -1
- package/dist/rules/suppressions/biome/configs.d.ts.map +1 -1
- package/dist/rules/suppressions/biome/configs.js +23 -17
- package/dist/rules/suppressions/biome/configs.js.map +1 -1
- package/dist/rules/suppressions/biome/declarations.d.ts +4 -4
- package/dist/rules/suppressions/biome/declarations.d.ts.map +1 -1
- package/dist/rules/suppressions/biome/declarations.js +15 -7
- package/dist/rules/suppressions/biome/declarations.js.map +1 -1
- package/dist/rules/suppressions/biome/json.d.ts +6 -6
- package/dist/rules/suppressions/biome/json.d.ts.map +1 -1
- package/dist/rules/suppressions/biome/json.js +27 -13
- package/dist/rules/suppressions/biome/json.js.map +1 -1
- package/dist/rules/suppressions/biome/resolve.d.ts +1 -1
- package/dist/rules/suppressions/biome/resolve.d.ts.map +1 -1
- package/dist/rules/suppressions/biome/resolve.js +23 -17
- package/dist/rules/suppressions/biome/resolve.js.map +1 -1
- package/dist/rules/suppressions/biome/weakenings.d.ts +1 -1
- package/dist/rules/suppressions/biome/weakenings.d.ts.map +1 -1
- package/dist/rules/suppressions/biome/weakenings.js +78 -52
- package/dist/rules/suppressions/biome/weakenings.js.map +1 -1
- package/dist/rules/suppressions/{biome-overrides.d.ts → biomeOverrides.d.ts} +1 -1
- package/dist/rules/suppressions/biomeOverrides.d.ts.map +1 -0
- package/dist/rules/suppressions/biomeOverrides.js +75 -0
- package/dist/rules/suppressions/biomeOverrides.js.map +1 -0
- package/dist/rules/suppressions/directives.d.ts +1 -1
- package/dist/rules/suppressions/directives.d.ts.map +1 -1
- package/dist/rules/suppressions/directives.js +5 -3
- package/dist/rules/suppressions/directives.js.map +1 -1
- package/dist/rules/suppressions/{no-double-cast.d.ts → noDoubleCast.d.ts} +1 -1
- package/dist/rules/suppressions/noDoubleCast.d.ts.map +1 -0
- package/dist/rules/suppressions/{no-double-cast.js → noDoubleCast.js} +15 -11
- package/dist/rules/suppressions/noDoubleCast.js.map +1 -0
- package/dist/rules/suppressions/noIgnoreDeprecations.js +1 -1
- package/dist/rules/suppressions/noIgnoreDeprecations.js.map +1 -1
- package/dist/rules/suppressions/{no-inline.d.ts → noInline.d.ts} +1 -1
- package/dist/rules/suppressions/noInline.d.ts.map +1 -0
- package/dist/rules/suppressions/{no-inline.js → noInline.js} +10 -8
- package/dist/rules/suppressions/noInline.js.map +1 -0
- package/dist/rules/suppressions/stylesheetComments.d.ts +4 -0
- package/dist/rules/suppressions/stylesheetComments.d.ts.map +1 -0
- package/dist/rules/suppressions/{stylesheet-comments.js → stylesheetComments.js} +12 -6
- package/dist/rules/suppressions/stylesheetComments.js.map +1 -0
- package/dist/rules/syntax.d.ts +1 -1
- package/dist/rules/syntax.d.ts.map +1 -1
- package/dist/rules/syntax.js +2 -2
- package/dist/rules/syntax.js.map +1 -1
- package/dist/{standard-schema.d.ts → standardSchema.d.ts} +7 -7
- package/dist/standardSchema.d.ts.map +1 -0
- package/dist/{standard-schema.js → standardSchema.js} +1 -1
- package/dist/standardSchema.js.map +1 -0
- package/docs/debt-and-hooks.md +2 -2
- package/docs/modules.md +27 -24
- package/docs/roadmap.md +1 -1
- package/docs/rules.md +2 -0
- package/package.json +2 -3
- package/src/baseline/compare.ts +6 -4
- package/src/baseline/format.ts +17 -10
- package/src/baseline/legacy.ts +4 -4
- package/src/baseline/rewrite.ts +2 -2
- package/src/baseline/update.ts +23 -19
- package/src/cli/args.ts +20 -21
- package/src/cli/baseline.ts +36 -35
- package/src/cli/fix.ts +40 -43
- package/src/cli/lint.ts +17 -21
- package/src/cli/messages.ts +2 -2
- package/src/cli/migrate.ts +21 -22
- package/src/cli/migrateExceptions.ts +1 -1
- package/src/cli/program.ts +1 -1
- package/src/cli/{run-main.ts → runMain.ts} +2 -2
- package/src/cli/shrink.ts +11 -8
- package/src/cli.ts +1 -1
- package/src/config/decode.ts +15 -10
- package/src/config/load.ts +44 -37
- package/src/config/resolve.ts +34 -29
- package/src/config.ts +5 -3
- package/src/decoded.ts +6 -5
- package/src/engine/baselineFile.ts +29 -0
- package/src/engine/evaluate.ts +7 -8
- package/src/engine/{run-rules.ts → runRules.ts} +22 -14
- package/src/engine/session.ts +34 -27
- package/src/engine/violation.ts +24 -13
- package/src/exceptions/registry.ts +10 -8
- package/src/git/base.ts +45 -37
- package/src/git/command.ts +6 -4
- package/src/git/history.ts +5 -4
- package/src/hooks/preCommit.ts +1 -1
- package/src/imports/generated.ts +1 -1
- package/src/imports/resolve.ts +1 -1
- package/src/imports/workspace.ts +1 -1
- package/src/inventory/collect.ts +40 -35
- package/src/inventory/filesystem.ts +38 -30
- package/src/inventory/{ignore-scope.ts → ignoreScope.ts} +20 -10
- package/src/inventory/walk.ts +1 -1
- package/src/manifests/sorted.ts +19 -19
- package/src/report/plural.ts +3 -1
- package/src/report/render.ts +24 -39
- package/src/report/stale.ts +33 -25
- package/src/rule.ts +26 -20
- package/src/rules/{built-in.ts → builtIn.ts} +16 -12
- package/src/rules/comments/{pattern-rule.ts → definePattern.ts} +5 -4
- package/src/rules/comments/kinds.ts +16 -9
- package/src/rules/comments/{max-per-file.ts → maxPerFile.ts} +15 -11
- package/src/rules/comments/{no-banner.ts → noBanner.ts} +1 -1
- package/src/rules/comments/{no-environment-pragma.ts → noEnvironmentPragma.ts} +1 -1
- package/src/rules/comments/{no-line-reference.ts → noLineReference.ts} +1 -1
- package/src/rules/comments/{no-pr-reference.ts → noPrReference.ts} +1 -1
- package/src/rules/comments/{no-todo.ts → noTodo.ts} +1 -1
- package/src/rules/comments/scan.ts +18 -15
- package/src/rules/manifestsSourceFirst.ts +48 -0
- package/src/rules/{max-lines.ts → maxLines.ts} +3 -2
- package/src/rules/stories/origins.ts +147 -0
- package/src/rules/stories/prerequisites.ts +37 -0
- package/src/rules/stories/program.ts +29 -0
- package/src/rules/stories/specs.ts +93 -0
- package/src/rules/suppressions/biome/configs.ts +23 -17
- package/src/rules/suppressions/biome/declarations.ts +15 -10
- package/src/rules/suppressions/biome/json.ts +25 -13
- package/src/rules/suppressions/biome/resolve.ts +23 -18
- package/src/rules/suppressions/biome/weakenings.ts +60 -41
- package/src/rules/suppressions/{biome-overrides.ts → biomeOverrides.ts} +32 -22
- package/src/rules/suppressions/directives.ts +5 -4
- package/src/rules/suppressions/{no-double-cast.ts → noDoubleCast.ts} +14 -10
- package/src/rules/suppressions/noIgnoreDeprecations.ts +1 -1
- package/src/rules/suppressions/{no-inline.ts → noInline.ts} +6 -5
- package/src/rules/suppressions/{stylesheet-comments.ts → stylesheetComments.ts} +11 -6
- package/src/rules/syntax.ts +2 -2
- package/src/{standard-schema.ts → standardSchema.ts} +4 -4
- package/dist/cli/run-main.d.ts.map +0 -1
- package/dist/cli/run-main.js.map +0 -1
- package/dist/engine/baseline-file.d.ts.map +0 -1
- package/dist/engine/baseline-file.js.map +0 -1
- package/dist/engine/run-rules.d.ts.map +0 -1
- package/dist/engine/run-rules.js +0 -34
- package/dist/engine/run-rules.js.map +0 -1
- package/dist/imports/pnpm-workspace.d.ts.map +0 -1
- package/dist/imports/pnpm-workspace.js.map +0 -1
- package/dist/inventory/ignore-scope.d.ts +0 -17
- package/dist/inventory/ignore-scope.d.ts.map +0 -1
- package/dist/inventory/ignore-scope.js.map +0 -1
- package/dist/rules/built-in.d.ts.map +0 -1
- package/dist/rules/built-in.js.map +0 -1
- package/dist/rules/comments/max-per-file.d.ts.map +0 -1
- package/dist/rules/comments/max-per-file.js.map +0 -1
- package/dist/rules/comments/no-banner.d.ts.map +0 -1
- package/dist/rules/comments/no-banner.js.map +0 -1
- package/dist/rules/comments/no-environment-pragma.d.ts.map +0 -1
- package/dist/rules/comments/no-environment-pragma.js.map +0 -1
- package/dist/rules/comments/no-jsdoc.d.ts.map +0 -1
- package/dist/rules/comments/no-jsdoc.js.map +0 -1
- package/dist/rules/comments/no-line-reference.d.ts.map +0 -1
- package/dist/rules/comments/no-line-reference.js.map +0 -1
- package/dist/rules/comments/no-pr-reference.d.ts.map +0 -1
- package/dist/rules/comments/no-pr-reference.js.map +0 -1
- package/dist/rules/comments/no-todo.d.ts.map +0 -1
- package/dist/rules/comments/no-todo.js.map +0 -1
- package/dist/rules/comments/pattern-rule.d.ts.map +0 -1
- package/dist/rules/comments/pattern-rule.js +0 -16
- package/dist/rules/comments/pattern-rule.js.map +0 -1
- package/dist/rules/manifests-sorted.d.ts.map +0 -1
- package/dist/rules/manifests-sorted.js.map +0 -1
- package/dist/rules/max-lines.d.ts.map +0 -1
- package/dist/rules/max-lines.js.map +0 -1
- package/dist/rules/suppressions/biome-overrides.d.ts.map +0 -1
- package/dist/rules/suppressions/biome-overrides.js +0 -63
- package/dist/rules/suppressions/biome-overrides.js.map +0 -1
- package/dist/rules/suppressions/no-double-cast.d.ts.map +0 -1
- package/dist/rules/suppressions/no-double-cast.js.map +0 -1
- package/dist/rules/suppressions/no-inline.d.ts.map +0 -1
- package/dist/rules/suppressions/no-inline.js.map +0 -1
- package/dist/rules/suppressions/stylesheet-comments.d.ts +0 -4
- package/dist/rules/suppressions/stylesheet-comments.d.ts.map +0 -1
- package/dist/rules/suppressions/stylesheet-comments.js.map +0 -1
- package/dist/standard-schema.d.ts.map +0 -1
- package/dist/standard-schema.js.map +0 -1
- package/src/engine/baseline-file.ts +0 -27
- /package/src/imports/{pnpm-workspace.ts → pnpmWorkspacePatterns.ts} +0 -0
- /package/src/rules/comments/{no-jsdoc.ts → noJsdoc.ts} +0 -0
- /package/src/rules/{manifests-sorted.ts → manifestsSorted.ts} +0 -0
package/src/report/stale.ts
CHANGED
|
@@ -8,31 +8,39 @@ interface StaleFiles {
|
|
|
8
8
|
readonly registry: string;
|
|
9
9
|
}
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
lines.length === 0
|
|
11
|
+
function section(label: string, file: string, guidance: string, lines: readonly string[]): readonly string[] {
|
|
12
|
+
return lines.length === 0
|
|
13
|
+
? []
|
|
14
|
+
: [[`${label} ${file}: ${plural(lines.length, "stale entry", "stale entries")}`, ` ${guidance}`, ...lines].join("\n")];
|
|
15
|
+
}
|
|
13
16
|
|
|
14
|
-
|
|
17
|
+
function baselineLine({ entry, problem }: StaleBaselineEntry): string {
|
|
18
|
+
return ` ${entry.rule} ${entry.file} ${problem}.`;
|
|
19
|
+
}
|
|
15
20
|
|
|
16
|
-
|
|
17
|
-
` ${entry.rule} ${entry.file}${entry.subject === undefined ? "" : ` (${entry.subject})`} ${problem}.`;
|
|
21
|
+
function registryLine({ entry, problem }: StaleRegistryEntry): string {
|
|
22
|
+
return ` ${entry.rule} ${entry.file}${entry.subject === undefined ? "" : ` (${entry.subject})`} ${problem}.`;
|
|
23
|
+
}
|
|
18
24
|
|
|
19
|
-
export
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
25
|
+
export function staleSections(outcome: Outcome, files: StaleFiles): readonly string[] {
|
|
26
|
+
return [
|
|
27
|
+
...section(
|
|
28
|
+
"error",
|
|
29
|
+
files.baseline,
|
|
30
|
+
"An entry for an unknown rule covers nothing. Run `quality baseline prune` to drop it.",
|
|
31
|
+
outcome.staleBaseline.map(baselineLine),
|
|
32
|
+
),
|
|
33
|
+
...section(
|
|
34
|
+
"note",
|
|
35
|
+
files.baseline,
|
|
36
|
+
"These pass, and `quality baseline tighten` lowers them when their files change. `quality baseline prune` lowers them all.",
|
|
37
|
+
outcome.looseBaseline.map(baselineLine),
|
|
38
|
+
),
|
|
39
|
+
...section(
|
|
40
|
+
"error",
|
|
41
|
+
files.registry,
|
|
42
|
+
"Every registered exception must still apply. Remove the entries whose exception is gone.",
|
|
43
|
+
outcome.staleRegistry.map(registryLine),
|
|
44
|
+
),
|
|
45
|
+
];
|
|
46
|
+
}
|
package/src/rule.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { Bivariant } from "@shivaedev/types/bivariant.ts";
|
|
2
2
|
import type { ExceptionDeclaration } from "#exceptions/declaration.ts";
|
|
3
|
-
import { describeIssue, type StandardSchemaV1 } from "./
|
|
3
|
+
import { describeIssue, type StandardSchemaV1 } from "./standardSchema.ts";
|
|
4
4
|
|
|
5
5
|
export interface Finding {
|
|
6
6
|
readonly count?: number | undefined;
|
|
@@ -28,35 +28,35 @@ export interface RuleInputs {
|
|
|
28
28
|
readonly sources: readonly SourceFile[];
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
-
export interface RuleContext<
|
|
32
|
-
readonly options:
|
|
31
|
+
export interface RuleContext<TOptions> extends RuleInputs {
|
|
32
|
+
readonly options: TOptions;
|
|
33
33
|
}
|
|
34
34
|
|
|
35
35
|
export type Configured =
|
|
36
36
|
| { readonly _tag: "Invalid"; readonly issues: readonly string[] }
|
|
37
37
|
| { readonly _tag: "Ready"; readonly check: (inputs: RuleInputs) => Promise<Findings> };
|
|
38
38
|
|
|
39
|
-
export interface Rule<
|
|
39
|
+
export interface Rule<TId extends string = string, TInput = unknown> {
|
|
40
40
|
// Bivariance keeps rules with different options in one list; configure validates its input at runtime.
|
|
41
|
-
configure: Bivariant<(options:
|
|
41
|
+
configure: Bivariant<(options: TInput | undefined) => Promise<Configured>>;
|
|
42
42
|
readonly description: string;
|
|
43
43
|
readonly family?: boolean;
|
|
44
|
-
readonly id:
|
|
44
|
+
readonly id: TId;
|
|
45
45
|
readonly registrable?: boolean;
|
|
46
46
|
}
|
|
47
47
|
|
|
48
|
-
interface RuleDefinition<
|
|
49
|
-
readonly check: (context: RuleContext<
|
|
48
|
+
interface RuleDefinition<TId extends string, TOptions> {
|
|
49
|
+
readonly check: (context: RuleContext<TOptions>) => Findings | PromiseLike<Findings>;
|
|
50
50
|
readonly description: string;
|
|
51
|
-
readonly id:
|
|
51
|
+
readonly id: TId;
|
|
52
52
|
readonly registrable?: false;
|
|
53
53
|
}
|
|
54
54
|
|
|
55
|
-
interface RuleDefinitionWithOptions<
|
|
56
|
-
readonly options: StandardSchemaV1<
|
|
55
|
+
interface RuleDefinitionWithOptions<TId extends string, TInput, TOptions> extends RuleDefinition<TId, TOptions> {
|
|
56
|
+
readonly options: StandardSchemaV1<TInput, TOptions>;
|
|
57
57
|
}
|
|
58
58
|
|
|
59
|
-
interface RuleDefinitionWithoutOptions<
|
|
59
|
+
interface RuleDefinitionWithoutOptions<TId extends string> extends RuleDefinition<TId, undefined> {
|
|
60
60
|
readonly options?: undefined;
|
|
61
61
|
}
|
|
62
62
|
|
|
@@ -68,9 +68,12 @@ const noOptions: StandardSchemaV1<undefined, undefined> = {
|
|
|
68
68
|
},
|
|
69
69
|
};
|
|
70
70
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
71
|
+
function configureWith<TInput, TOptions>(
|
|
72
|
+
schema: StandardSchemaV1<TInput, TOptions>,
|
|
73
|
+
check: RuleDefinition<string, TOptions>["check"],
|
|
74
|
+
absent: unknown,
|
|
75
|
+
) {
|
|
76
|
+
return async (options: unknown): Promise<Configured> => {
|
|
74
77
|
const result = await schema["~standard"].validate(options ?? absent);
|
|
75
78
|
if (result.issues !== undefined) {
|
|
76
79
|
return { _tag: "Invalid", issues: result.issues.map(describeIssue) };
|
|
@@ -78,12 +81,15 @@ const configureWith =
|
|
|
78
81
|
const value = result.value;
|
|
79
82
|
return { _tag: "Ready", check: async (inputs) => check({ ...inputs, options: value }) };
|
|
80
83
|
};
|
|
84
|
+
}
|
|
81
85
|
|
|
82
|
-
export function defineRule<const
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
86
|
+
export function defineRule<const TId extends string, TInput, TOptions>(
|
|
87
|
+
definition: RuleDefinitionWithOptions<TId, TInput, TOptions>,
|
|
88
|
+
): Rule<TId, TInput>;
|
|
89
|
+
export function defineRule<const TId extends string>(definition: RuleDefinitionWithoutOptions<TId>): Rule<TId, undefined>;
|
|
90
|
+
export function defineRule<const TId extends string, TInput, TOptions>(
|
|
91
|
+
definition: RuleDefinitionWithOptions<TId, TInput, TOptions> | RuleDefinitionWithoutOptions<TId>,
|
|
92
|
+
): Rule<TId, TInput> | Rule<TId, undefined> {
|
|
87
93
|
const configure =
|
|
88
94
|
definition.options === undefined
|
|
89
95
|
? configureWith(noOptions, definition.check, undefined)
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { maxPerFile } from "#rules/comments/
|
|
2
|
-
import { noBanner } from "#rules/comments/
|
|
3
|
-
import { noEnvironmentPragma } from "#rules/comments/
|
|
4
|
-
import { noJsdoc } from "#rules/comments/
|
|
5
|
-
import { noLineReference } from "#rules/comments/
|
|
6
|
-
import { noPrReference } from "#rules/comments/
|
|
7
|
-
import { noTodo } from "#rules/comments/
|
|
1
|
+
import { maxPerFile } from "#rules/comments/maxPerFile.ts";
|
|
2
|
+
import { noBanner } from "#rules/comments/noBanner.ts";
|
|
3
|
+
import { noEnvironmentPragma } from "#rules/comments/noEnvironmentPragma.ts";
|
|
4
|
+
import { noJsdoc } from "#rules/comments/noJsdoc.ts";
|
|
5
|
+
import { noLineReference } from "#rules/comments/noLineReference.ts";
|
|
6
|
+
import { noPrReference } from "#rules/comments/noPrReference.ts";
|
|
7
|
+
import { noTodo } from "#rules/comments/noTodo.ts";
|
|
8
8
|
import { distinctCase } from "#rules/files/distinctCase.ts";
|
|
9
9
|
import { folderNames } from "#rules/files/folderNames.ts";
|
|
10
10
|
import { namedAfterExport } from "#rules/files/namedAfterExport.ts";
|
|
@@ -17,17 +17,19 @@ import { noTestCode } from "#rules/imports/noTestCode.ts";
|
|
|
17
17
|
import { importsResolvable } from "#rules/imports/resolvable.ts";
|
|
18
18
|
import { importsRuntime } from "#rules/imports/runtime.ts";
|
|
19
19
|
import { storySetup } from "#rules/stories/setup.ts";
|
|
20
|
-
import {
|
|
20
|
+
import { storySpecs } from "#rules/stories/specs.ts";
|
|
21
|
+
import { biomeOverrides } from "#rules/suppressions/biomeOverrides.ts";
|
|
21
22
|
import { biomeRecommended } from "#rules/suppressions/biomeRecommended.ts";
|
|
22
|
-
import { noDoubleCast } from "#rules/suppressions/
|
|
23
|
-
import { noInline } from "#rules/suppressions/no-inline.ts";
|
|
23
|
+
import { noDoubleCast } from "#rules/suppressions/noDoubleCast.ts";
|
|
24
24
|
import { noIgnoreDeprecations } from "#rules/suppressions/noIgnoreDeprecations.ts";
|
|
25
|
+
import { noInline } from "#rules/suppressions/noInline.ts";
|
|
25
26
|
import { testsColocated } from "#rules/test-names/colocated.ts";
|
|
26
27
|
import { testsFollow } from "#rules/test-names/follow.ts";
|
|
27
28
|
import { biome } from "./biome.ts";
|
|
28
|
-
import { manifestsSorted } from "./
|
|
29
|
-
import {
|
|
29
|
+
import { manifestsSorted } from "./manifestsSorted.ts";
|
|
30
|
+
import { manifestsSourceFirst } from "./manifestsSourceFirst.ts";
|
|
30
31
|
import { maxDepth } from "./maxDepth.ts";
|
|
32
|
+
import { maxLines } from "./maxLines.ts";
|
|
31
33
|
import { noBarrel } from "./noBarrel.ts";
|
|
32
34
|
|
|
33
35
|
export const builtInRules = [
|
|
@@ -55,6 +57,7 @@ export const builtInRules = [
|
|
|
55
57
|
importsAliased,
|
|
56
58
|
noTestCode,
|
|
57
59
|
manifestsSorted,
|
|
60
|
+
manifestsSourceFirst,
|
|
58
61
|
namedAfterExport,
|
|
59
62
|
folderNames,
|
|
60
63
|
distinctCase,
|
|
@@ -62,4 +65,5 @@ export const builtInRules = [
|
|
|
62
65
|
testsFollow,
|
|
63
66
|
testsColocated,
|
|
64
67
|
storySetup,
|
|
68
|
+
storySpecs,
|
|
65
69
|
] as const;
|
|
@@ -9,13 +9,13 @@ interface PatternRule<Id extends string> {
|
|
|
9
9
|
readonly patterns: readonly RegExp[];
|
|
10
10
|
}
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
function firstMatch(comment: SourceComment, patterns: readonly RegExp[]): string | undefined {
|
|
13
13
|
const text = textOf(comment);
|
|
14
14
|
return patterns.map((pattern) => pattern.exec(text)?.[0]).find((match) => match !== undefined);
|
|
15
|
-
}
|
|
15
|
+
}
|
|
16
16
|
|
|
17
|
-
export
|
|
18
|
-
defineRule({
|
|
17
|
+
export function definePatternRule<const Id extends string>({ description, id, message, patterns }: PatternRule<Id>): Rule<Id, undefined> {
|
|
18
|
+
return defineRule({
|
|
19
19
|
check: ({ sources }) =>
|
|
20
20
|
sources.flatMap((file) =>
|
|
21
21
|
commentsOf(file).flatMap((comment): readonly Finding[] => {
|
|
@@ -26,3 +26,4 @@ export const definePatternRule = <const Id extends string>({ description, id, me
|
|
|
26
26
|
description,
|
|
27
27
|
id,
|
|
28
28
|
});
|
|
29
|
+
}
|
|
@@ -15,21 +15,28 @@ const DIRECTIVES: readonly RegExp[] = [
|
|
|
15
15
|
|
|
16
16
|
const TRIPLE_SLASH = /^\/\/\/\s*</u;
|
|
17
17
|
|
|
18
|
-
|
|
18
|
+
function contentOf(comment: SourceComment): readonly string[] {
|
|
19
|
+
return comment.body.filter((line) => line !== "");
|
|
20
|
+
}
|
|
19
21
|
|
|
20
|
-
|
|
21
|
-
tags.some((tag) => line === tag || line.startsWith(`${tag} `) || line.startsWith(`${tag}\t`));
|
|
22
|
+
function startsWithTag(line: string, tags: readonly string[]): boolean {
|
|
23
|
+
return tags.some((tag) => line === tag || line.startsWith(`${tag} `) || line.startsWith(`${tag}\t`));
|
|
24
|
+
}
|
|
22
25
|
|
|
23
|
-
export
|
|
26
|
+
export function textOf(comment: SourceComment): string {
|
|
27
|
+
return comment.body.join("\n");
|
|
28
|
+
}
|
|
24
29
|
|
|
25
|
-
export
|
|
30
|
+
export function isJsdoc(comment: SourceComment): boolean {
|
|
31
|
+
return comment.kind === "block" && comment.text.startsWith("/**") && comment.text !== "/**/";
|
|
32
|
+
}
|
|
26
33
|
|
|
27
|
-
export
|
|
34
|
+
export function isPragma(comment: SourceComment, pragmas: readonly string[]): boolean {
|
|
28
35
|
const content = contentOf(comment);
|
|
29
36
|
return content.length > 0 && content.every((line) => startsWithTag(line, pragmas));
|
|
30
|
-
}
|
|
37
|
+
}
|
|
31
38
|
|
|
32
|
-
export
|
|
39
|
+
export function isDirective(comment: SourceComment): boolean {
|
|
33
40
|
const first = contentOf(comment)[0] ?? "";
|
|
34
41
|
return TRIPLE_SLASH.test(comment.text) || DIRECTIVES.some((directive) => directive.test(first));
|
|
35
|
-
}
|
|
42
|
+
}
|
|
@@ -9,18 +9,22 @@ const MaxPerFileOptions = Schema.Struct({
|
|
|
9
9
|
max: Schema.Int.check(Schema.isGreaterThanOrEqualTo(0), Schema.isLessThanOrEqualTo(2)).pipe(Schema.withDecodingDefaultKey(Effect.succeed(2))),
|
|
10
10
|
});
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
12
|
+
function continues(previous: SourceComment | undefined, comment: SourceComment): boolean {
|
|
13
|
+
return (
|
|
14
|
+
previous !== undefined
|
|
15
|
+
&& previous.kind === "line"
|
|
16
|
+
&& comment.kind === "line"
|
|
17
|
+
&& previous.ownLine
|
|
18
|
+
&& comment.ownLine
|
|
19
|
+
&& comment.line === previous.endLine + 1
|
|
20
|
+
);
|
|
21
|
+
}
|
|
19
22
|
|
|
20
|
-
|
|
21
|
-
comments.filter((comment, index) => !continues(comments[index - 1], comment));
|
|
23
|
+
function commentBlocks(comments: readonly SourceComment[]): readonly SourceComment[] {
|
|
24
|
+
return comments.filter((comment, index) => !continues(comments[index - 1], comment));
|
|
25
|
+
}
|
|
22
26
|
|
|
23
|
-
|
|
27
|
+
function overCap(file: SourceFile, max: number, allow: readonly string[]): readonly Finding[] {
|
|
24
28
|
const counted = commentsOf(file).filter((comment) => !(isDirective(comment) || isPragma(comment, allow)));
|
|
25
29
|
const blocks = commentBlocks(counted);
|
|
26
30
|
const first = blocks[max];
|
|
@@ -35,7 +39,7 @@ const overCap = (file: SourceFile, max: number, allow: readonly string[]): reado
|
|
|
35
39
|
threshold: max,
|
|
36
40
|
},
|
|
37
41
|
];
|
|
38
|
-
}
|
|
42
|
+
}
|
|
39
43
|
|
|
40
44
|
export const maxPerFile = defineRule({
|
|
41
45
|
check: ({ options, sources }) => sources.flatMap((file) => overCap(file, options.max, options.allow)),
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { definePatternRule } from "./
|
|
1
|
+
import { definePatternRule } from "./definePattern.ts";
|
|
2
2
|
|
|
3
3
|
const SOURCE_EXTENSION =
|
|
4
4
|
"(?:[cm]?[jt]sx?|json5?|mdx?|css|scss|less|html?|vue|svelte|astro|ya?ml|toml|sql|prisma|graphql|gql|go|rs|py|rb|java|kt|swift|sh)";
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { definePatternRule } from "./
|
|
1
|
+
import { definePatternRule } from "./definePattern.ts";
|
|
2
2
|
|
|
3
3
|
export const noPrReference = definePatternRule({
|
|
4
4
|
description: "History belongs in version control, not in the code. State the reason itself, so the comment holds without the link.",
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { definePatternRule } from "./
|
|
1
|
+
import { definePatternRule } from "./definePattern.ts";
|
|
2
2
|
|
|
3
3
|
export const noTodo = definePatternRule({
|
|
4
4
|
description: "A TODO in the code is never done. Do the work now, or track it where the repository tracks work.",
|
|
@@ -13,15 +13,15 @@ export interface SourceComment {
|
|
|
13
13
|
|
|
14
14
|
const DECLARATION = /\.d\.[cm]?ts$/u;
|
|
15
15
|
|
|
16
|
-
|
|
16
|
+
function commentRanges(source: ts.SourceFile): readonly ts.CommentRange[] {
|
|
17
17
|
const ranges = new Map<number, ts.CommentRange>();
|
|
18
18
|
const jsxText: ts.Node[] = [];
|
|
19
|
-
|
|
19
|
+
function add(found: readonly ts.CommentRange[] | undefined) {
|
|
20
20
|
for (const range of found ?? []) {
|
|
21
21
|
ranges.set(range.pos, range);
|
|
22
22
|
}
|
|
23
|
-
}
|
|
24
|
-
|
|
23
|
+
}
|
|
24
|
+
function visit(node: ts.Node) {
|
|
25
25
|
if (node.kind === ts.SyntaxKind.JsxText) {
|
|
26
26
|
jsxText.push(node);
|
|
27
27
|
}
|
|
@@ -30,22 +30,23 @@ const commentRanges = (source: ts.SourceFile): readonly ts.CommentRange[] => {
|
|
|
30
30
|
for (const child of node.getChildren(source)) {
|
|
31
31
|
visit(child);
|
|
32
32
|
}
|
|
33
|
-
}
|
|
33
|
+
}
|
|
34
34
|
visit(source);
|
|
35
35
|
return [...ranges.values()]
|
|
36
36
|
.filter((range) => !jsxText.some((text) => range.pos >= text.pos && range.pos < text.end))
|
|
37
37
|
.sort((left, right) => left.pos - right.pos);
|
|
38
|
-
}
|
|
38
|
+
}
|
|
39
39
|
|
|
40
|
-
export
|
|
41
|
-
kind === "line"
|
|
40
|
+
export function bodyOf(kind: SourceComment["kind"], text: string): readonly string[] {
|
|
41
|
+
return kind === "line"
|
|
42
42
|
? [text.slice(2).trim()]
|
|
43
43
|
: text
|
|
44
44
|
.slice(2, -2)
|
|
45
45
|
.split("\n")
|
|
46
46
|
.map((line) => line.trim().replace(/^\*(?=\s|$)\s*/u, ""));
|
|
47
|
+
}
|
|
47
48
|
|
|
48
|
-
|
|
49
|
+
function toComment(source: ts.SourceFile, range: ts.CommentRange): SourceComment {
|
|
49
50
|
const kind = range.kind === ts.SyntaxKind.SingleLineCommentTrivia ? "line" : "block";
|
|
50
51
|
const text = source.text.slice(range.pos, range.end);
|
|
51
52
|
const start = source.getLineAndCharacterOfPosition(range.pos);
|
|
@@ -58,16 +59,16 @@ const toComment = (source: ts.SourceFile, range: ts.CommentRange): SourceComment
|
|
|
58
59
|
ownLine: source.text.slice(lineStart, range.pos).trim() === "",
|
|
59
60
|
text,
|
|
60
61
|
};
|
|
61
|
-
}
|
|
62
|
+
}
|
|
62
63
|
|
|
63
64
|
const scanned = new WeakMap<SourceFile, readonly SourceComment[]>();
|
|
64
65
|
|
|
65
|
-
|
|
66
|
+
function scan(file: SourceFile): readonly SourceComment[] {
|
|
66
67
|
const source = parse(file);
|
|
67
68
|
return source === undefined ? [] : commentRanges(source).map((range) => toComment(source, range));
|
|
68
|
-
}
|
|
69
|
+
}
|
|
69
70
|
|
|
70
|
-
export
|
|
71
|
+
export function scanComments(file: SourceFile): readonly SourceComment[] {
|
|
71
72
|
const cached = scanned.get(file);
|
|
72
73
|
if (cached !== undefined) {
|
|
73
74
|
return cached;
|
|
@@ -75,6 +76,8 @@ export const scanComments = (file: SourceFile): readonly SourceComment[] => {
|
|
|
75
76
|
const comments = scan(file);
|
|
76
77
|
scanned.set(file, comments);
|
|
77
78
|
return comments;
|
|
78
|
-
}
|
|
79
|
+
}
|
|
79
80
|
|
|
80
|
-
export
|
|
81
|
+
export function commentsOf(file: SourceFile): readonly SourceComment[] {
|
|
82
|
+
return DECLARATION.test(file.path) ? [] : scanComments(file);
|
|
83
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import * as NodeFileSystem from "@effect/platform-node/NodeFileSystem";
|
|
2
|
+
import { Effect } from "effect";
|
|
3
|
+
import { manifestsIn } from "#manifests/sorted.ts";
|
|
4
|
+
import { defineRule, type Finding } from "#rule.ts";
|
|
5
|
+
import { entriesOf, itemsOf, type Json, member, parseJsonc } from "#rules/suppressions/biome/json.ts";
|
|
6
|
+
|
|
7
|
+
const CONDITION = "source";
|
|
8
|
+
|
|
9
|
+
function misplaced(path: string, field: string, node: Json): readonly Finding[] {
|
|
10
|
+
const conditions = entriesOf(node);
|
|
11
|
+
const first = conditions[0]?.[0];
|
|
12
|
+
const own: readonly Finding[] =
|
|
13
|
+
first === CONDITION || !conditions.some(([key]) => key === CONDITION)
|
|
14
|
+
? []
|
|
15
|
+
: [
|
|
16
|
+
{
|
|
17
|
+
file: path,
|
|
18
|
+
line: node.line,
|
|
19
|
+
message: `Lists the "${CONDITION}" condition after "${first}" in ${field}. TypeScript takes the first condition that matches, so once built output exists it reads that instead of the sources the "${CONDITION}" condition names. Move "${CONDITION}" to the top of this condition block.`,
|
|
20
|
+
subject: field,
|
|
21
|
+
},
|
|
22
|
+
];
|
|
23
|
+
const nested = [...conditions.map(([, value]) => value), ...itemsOf(node)];
|
|
24
|
+
return [...own, ...nested.flatMap((value) => misplaced(path, field, value))];
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function findingsIn(path: string, text: string): readonly Finding[] {
|
|
28
|
+
const parsed = parseJsonc(path, text);
|
|
29
|
+
if (parsed._tag !== "Parsed") {
|
|
30
|
+
return [];
|
|
31
|
+
}
|
|
32
|
+
return ["exports", "imports"].flatMap((field) => {
|
|
33
|
+
const map = member(parsed.json, field);
|
|
34
|
+
return map === undefined ? [] : misplaced(path, field, map);
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export const manifestsSourceFirst = defineRule({
|
|
39
|
+
check: async ({ readText, root }) => {
|
|
40
|
+
const manifests = await Effect.runPromise(manifestsIn(root).pipe(Effect.provide(NodeFileSystem.layer)));
|
|
41
|
+
const texts = await Promise.all(manifests.map(async (path) => ({ path, text: (await readText(path)) ?? "" })));
|
|
42
|
+
return texts.filter(({ text }) => text.includes(`"${CONDITION}"`)).flatMap(({ path, text }) => findingsIn(path, text));
|
|
43
|
+
},
|
|
44
|
+
description:
|
|
45
|
+
'Every package.json condition block that names the "source" condition lists it first in exports and imports, so TypeScript reads the sources whether or not built output exists.',
|
|
46
|
+
id: "manifests/source-first",
|
|
47
|
+
registrable: false,
|
|
48
|
+
});
|
|
@@ -11,8 +11,8 @@ const MaxLinesOptions = Schema.Struct({
|
|
|
11
11
|
test: Limit.check(Schema.isLessThanOrEqualTo(300)).pipe(Schema.withDecodingDefaultKey(Effect.succeed(300))),
|
|
12
12
|
});
|
|
13
13
|
|
|
14
|
-
|
|
15
|
-
file.lines.length <= limit
|
|
14
|
+
function oversized(file: SourceFile, limit: number): readonly Finding[] {
|
|
15
|
+
return file.lines.length <= limit
|
|
16
16
|
? []
|
|
17
17
|
: [
|
|
18
18
|
{
|
|
@@ -22,6 +22,7 @@ const oversized = (file: SourceFile, limit: number): readonly Finding[] =>
|
|
|
22
22
|
threshold: limit,
|
|
23
23
|
},
|
|
24
24
|
];
|
|
25
|
+
}
|
|
25
26
|
|
|
26
27
|
export const maxLines = defineRule({
|
|
27
28
|
check: ({ options, sources }) =>
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import { join } from "node:path";
|
|
2
|
+
import ts from "typescript";
|
|
3
|
+
import type { ImportGraph } from "#imports/graph.ts";
|
|
4
|
+
import type { RuleInputs } from "#rule.ts";
|
|
5
|
+
import { storyProgram } from "./program.ts";
|
|
6
|
+
|
|
7
|
+
type Origin = "factory" | "kit" | "story" | "plain" | "suite" | "vitest" | "stories" | "function" | undefined;
|
|
8
|
+
|
|
9
|
+
function importFrom(node: ts.Node): string | undefined {
|
|
10
|
+
for (let current: ts.Node | undefined = node; current !== undefined; current = current.parent) {
|
|
11
|
+
if (ts.isImportDeclaration(current) && ts.isStringLiteral(current.moduleSpecifier)) {
|
|
12
|
+
return current.moduleSpecifier.text;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
return undefined;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function moduleOrigin(from: string | undefined): Origin {
|
|
19
|
+
if (from === "vitest" || from === "@effect/vitest") {
|
|
20
|
+
return "vitest";
|
|
21
|
+
}
|
|
22
|
+
return from === "@shivaedev/test-story" || from === "@shivaedev/test-story/storyKit.ts" ? "stories" : undefined;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function imported(name: string, from: string | undefined): Origin {
|
|
26
|
+
const namespace = moduleOrigin(from);
|
|
27
|
+
if (name === "storyKit" && namespace === "stories") {
|
|
28
|
+
return "factory";
|
|
29
|
+
}
|
|
30
|
+
if ((name === "describe" || name === "suite") && namespace === "vitest") {
|
|
31
|
+
return "suite";
|
|
32
|
+
}
|
|
33
|
+
return (name === "it" || name === "test") && namespace === "vitest" ? "plain" : undefined;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function storyOrigins(inputs: RuleInputs, graph: ImportGraph) {
|
|
37
|
+
const program = storyProgram(inputs, graph);
|
|
38
|
+
const checker = program.getTypeChecker();
|
|
39
|
+
const visiting = new Set<ts.Node>();
|
|
40
|
+
const known = new Map<ts.Node, Origin>();
|
|
41
|
+
|
|
42
|
+
function binding(node: ts.BindingElement): Origin {
|
|
43
|
+
if (!ts.isVariableDeclaration(node.parent.parent)) {
|
|
44
|
+
return undefined;
|
|
45
|
+
}
|
|
46
|
+
const value = node.parent.parent.initializer;
|
|
47
|
+
const name = node.propertyName ?? node.name;
|
|
48
|
+
return value !== undefined && (ts.isIdentifier(name) || ts.isStringLiteral(name)) ? member(value, name.text) : undefined;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function declaration(node: ts.Node): Origin {
|
|
52
|
+
if (ts.isImportSpecifier(node)) {
|
|
53
|
+
return imported((node.propertyName ?? node.name).text, importFrom(node));
|
|
54
|
+
}
|
|
55
|
+
if (ts.isNamespaceImport(node)) {
|
|
56
|
+
return moduleOrigin(importFrom(node));
|
|
57
|
+
}
|
|
58
|
+
if (ts.isVariableDeclaration(node) || ts.isPropertyAssignment(node)) {
|
|
59
|
+
return node.initializer === undefined ? undefined : origin(node.initializer);
|
|
60
|
+
}
|
|
61
|
+
return localBinding(node);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function localBinding(node: ts.Node): Origin {
|
|
65
|
+
if (ts.isShorthandPropertyAssignment(node)) {
|
|
66
|
+
return symbol(checker.getShorthandAssignmentValueSymbol(node));
|
|
67
|
+
}
|
|
68
|
+
return ts.isBindingElement(node) ? binding(node) : undefined;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function symbol(value: ts.Symbol | undefined): Origin {
|
|
72
|
+
if (value === undefined) {
|
|
73
|
+
return undefined;
|
|
74
|
+
}
|
|
75
|
+
const direct = value.declarations?.map(visit).find((result) => result !== undefined);
|
|
76
|
+
return direct ?? (value.flags & ts.SymbolFlags.Alias ? symbol(checker.getAliasedSymbol(value)) : undefined);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function object(node: ts.ObjectLiteralExpression): Origin {
|
|
80
|
+
for (const property of [...node.properties].reverse()) {
|
|
81
|
+
if (property.name?.getText() === "it") {
|
|
82
|
+
return visit(property) === "story" ? "kit" : undefined;
|
|
83
|
+
}
|
|
84
|
+
if (ts.isSpreadAssignment(property) && visit(property.expression) === "kit") {
|
|
85
|
+
return "kit";
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
return undefined;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function member(value: ts.Expression, name: string): Origin {
|
|
92
|
+
const receiver = visit(value);
|
|
93
|
+
if (receiver === "kit" && name === "it") {
|
|
94
|
+
return "story";
|
|
95
|
+
}
|
|
96
|
+
if (receiver === "story" || receiver === "plain" || receiver === "suite") {
|
|
97
|
+
return receiver;
|
|
98
|
+
}
|
|
99
|
+
const from = receiver === "vitest" ? "vitest" : undefined;
|
|
100
|
+
return imported(name, receiver === "stories" ? "@shivaedev/test-story/storyKit.ts" : from);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function identifier(node: ts.Identifier): Origin {
|
|
104
|
+
if (node.text === "describe" || node.text === "suite") {
|
|
105
|
+
return symbol(checker.getSymbolAtLocation(node)) ?? "suite";
|
|
106
|
+
}
|
|
107
|
+
const value = checker.getSymbolAtLocation(node);
|
|
108
|
+
return symbol(value) ?? (value === undefined && (node.text === "it" || node.text === "test") ? "plain" : undefined);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function origin(node: ts.Node): Origin {
|
|
112
|
+
if (ts.isFunctionLike(node)) {
|
|
113
|
+
return "function";
|
|
114
|
+
}
|
|
115
|
+
if (ts.isObjectLiteralExpression(node)) {
|
|
116
|
+
return object(node);
|
|
117
|
+
}
|
|
118
|
+
return expression(node);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function expression(node: ts.Node): Origin {
|
|
122
|
+
if (ts.isParenthesizedExpression(node) || ts.isAsExpression(node) || ts.isSatisfiesExpression(node)) {
|
|
123
|
+
return visit(node.expression);
|
|
124
|
+
}
|
|
125
|
+
if (ts.isCallExpression(node)) {
|
|
126
|
+
const callee = visit(node.expression);
|
|
127
|
+
return callee === "factory" ? "kit" : callee;
|
|
128
|
+
}
|
|
129
|
+
if (ts.isPropertyAccessExpression(node)) {
|
|
130
|
+
return member(node.expression, node.name.text) ?? symbol(checker.getSymbolAtLocation(node.name));
|
|
131
|
+
}
|
|
132
|
+
return ts.isIdentifier(node) ? identifier(node) : declaration(node);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function visit(node: ts.Node): Origin {
|
|
136
|
+
if (visiting.has(node) || known.has(node)) {
|
|
137
|
+
return known.get(node);
|
|
138
|
+
}
|
|
139
|
+
visiting.add(node);
|
|
140
|
+
const result = origin(node);
|
|
141
|
+
visiting.delete(node);
|
|
142
|
+
known.set(node, result);
|
|
143
|
+
return result;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
return { origin: visit, source: (path: string) => program.getSourceFile(join(inputs.root, path)) };
|
|
147
|
+
}
|