@nielspeter/eess-ts 0.2.0 → 0.4.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/README.md +87 -34
- package/dist/builders/call-rule-builder.d.ts +66 -26
- package/dist/builders/call-rule-builder.d.ts.map +1 -1
- package/dist/builders/call-rule-builder.js +73 -31
- package/dist/builders/call-rule-builder.js.map +1 -1
- package/dist/builders/class-rule-builder.d.ts +125 -26
- package/dist/builders/class-rule-builder.d.ts.map +1 -1
- package/dist/builders/class-rule-builder.js +134 -30
- package/dist/builders/class-rule-builder.js.map +1 -1
- package/dist/builders/correspondence-builder.d.ts +287 -0
- package/dist/builders/correspondence-builder.d.ts.map +1 -0
- package/dist/builders/correspondence-builder.js +393 -0
- package/dist/builders/correspondence-builder.js.map +1 -0
- package/dist/builders/correspondence-findings.d.ts +104 -0
- package/dist/builders/correspondence-findings.d.ts.map +1 -0
- package/dist/builders/correspondence-findings.js +301 -0
- package/dist/builders/correspondence-findings.js.map +1 -0
- package/dist/builders/cross-layer-builder.d.ts +99 -16
- package/dist/builders/cross-layer-builder.d.ts.map +1 -1
- package/dist/builders/cross-layer-builder.js +181 -35
- package/dist/builders/cross-layer-builder.js.map +1 -1
- package/dist/builders/function-rule-builder.d.ts +144 -25
- package/dist/builders/function-rule-builder.d.ts.map +1 -1
- package/dist/builders/function-rule-builder.js +173 -26
- package/dist/builders/function-rule-builder.js.map +1 -1
- package/dist/builders/jsx-rule-builder.d.ts +70 -14
- package/dist/builders/jsx-rule-builder.d.ts.map +1 -1
- package/dist/builders/jsx-rule-builder.js +73 -14
- package/dist/builders/jsx-rule-builder.js.map +1 -1
- package/dist/builders/module-rule-builder.d.ts +2 -2
- package/dist/builders/module-rule-builder.d.ts.map +1 -1
- package/dist/builders/module-rule-builder.js +12 -3
- package/dist/builders/module-rule-builder.js.map +1 -1
- package/dist/builders/scoped-function-rule-builder.d.ts +11 -0
- package/dist/builders/scoped-function-rule-builder.d.ts.map +1 -1
- package/dist/builders/scoped-function-rule-builder.js +12 -0
- package/dist/builders/scoped-function-rule-builder.js.map +1 -1
- package/dist/builders/slice-discovery-message.d.ts +36 -0
- package/dist/builders/slice-discovery-message.d.ts.map +1 -0
- package/dist/builders/slice-discovery-message.js +181 -0
- package/dist/builders/slice-discovery-message.js.map +1 -0
- package/dist/builders/slice-rule-builder.d.ts +151 -12
- package/dist/builders/slice-rule-builder.d.ts.map +1 -1
- package/dist/builders/slice-rule-builder.js +242 -35
- package/dist/builders/slice-rule-builder.js.map +1 -1
- package/dist/builders/type-rule-builder.d.ts +95 -24
- package/dist/builders/type-rule-builder.d.ts.map +1 -1
- package/dist/builders/type-rule-builder.js +105 -29
- package/dist/builders/type-rule-builder.js.map +1 -1
- package/dist/{helpers → builders}/within.d.ts +2 -2
- package/dist/builders/within.d.ts.map +1 -0
- package/dist/{helpers → builders}/within.js +1 -1
- package/dist/builders/within.js.map +1 -0
- package/dist/cli/commands/baseline.d.ts +3 -2
- package/dist/cli/commands/baseline.d.ts.map +1 -1
- package/dist/cli/commands/baseline.js +68 -6
- package/dist/cli/commands/baseline.js.map +1 -1
- package/dist/cli/commands/check.d.ts +7 -2
- package/dist/cli/commands/check.d.ts.map +1 -1
- package/dist/cli/commands/check.js +241 -45
- package/dist/cli/commands/check.js.map +1 -1
- package/dist/cli/commands/doctor.d.ts +29 -0
- package/dist/cli/commands/doctor.d.ts.map +1 -0
- package/dist/cli/commands/doctor.js +224 -0
- package/dist/cli/commands/doctor.js.map +1 -0
- package/dist/cli/commands/explain.d.ts +2 -1
- package/dist/cli/commands/explain.d.ts.map +1 -1
- package/dist/cli/commands/explain.js +19 -10
- package/dist/cli/commands/explain.js.map +1 -1
- package/dist/cli/commands/init.d.ts +9 -3
- package/dist/cli/commands/init.d.ts.map +1 -1
- package/dist/cli/commands/init.js +162 -137
- package/dist/cli/commands/init.js.map +1 -1
- package/dist/cli/import-rule-module.d.ts +29 -0
- package/dist/cli/import-rule-module.d.ts.map +1 -0
- package/dist/cli/import-rule-module.js +60 -0
- package/dist/cli/import-rule-module.js.map +1 -0
- package/dist/cli/index.d.ts +2 -2
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +98 -33
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/load-rules.d.ts +6 -11
- package/dist/cli/load-rules.d.ts.map +1 -1
- package/dist/cli/load-rules.js +21 -29
- package/dist/cli/load-rules.js.map +1 -1
- package/dist/cli/resolve-config.d.ts.map +1 -1
- package/dist/cli/resolve-config.js +24 -14
- package/dist/cli/resolve-config.js.map +1 -1
- package/dist/cli/rule-file-findings.d.ts +178 -0
- package/dist/cli/rule-file-findings.d.ts.map +1 -0
- package/dist/cli/rule-file-findings.js +293 -0
- package/dist/cli/rule-file-findings.js.map +1 -0
- package/dist/cli/watch.d.ts +20 -7
- package/dist/cli/watch.d.ts.map +1 -1
- package/dist/cli/watch.js +39 -14
- package/dist/cli/watch.js.map +1 -1
- package/dist/conditions/body-analysis-function.d.ts.map +1 -1
- package/dist/conditions/body-analysis-function.js +16 -7
- package/dist/conditions/body-analysis-function.js.map +1 -1
- package/dist/conditions/body-analysis-module.d.ts.map +1 -1
- package/dist/conditions/body-analysis-module.js +14 -9
- package/dist/conditions/body-analysis-module.js.map +1 -1
- package/dist/conditions/body-analysis.d.ts.map +1 -1
- package/dist/conditions/body-analysis.js +16 -7
- package/dist/conditions/body-analysis.js.map +1 -1
- package/dist/conditions/call.d.ts.map +1 -1
- package/dist/conditions/call.js +38 -17
- package/dist/conditions/call.js.map +1 -1
- package/dist/conditions/catch-analysis.d.ts +2 -1
- package/dist/conditions/catch-analysis.d.ts.map +1 -1
- package/dist/conditions/catch-analysis.js.map +1 -1
- package/dist/conditions/cross-layer.d.ts +2 -10
- package/dist/conditions/cross-layer.d.ts.map +1 -1
- package/dist/conditions/cross-layer.js +227 -23
- package/dist/conditions/cross-layer.js.map +1 -1
- package/dist/conditions/dependency.d.ts +35 -6
- package/dist/conditions/dependency.d.ts.map +1 -1
- package/dist/conditions/dependency.js +299 -45
- package/dist/conditions/dependency.js.map +1 -1
- package/dist/conditions/exports.d.ts.map +1 -1
- package/dist/conditions/exports.js +10 -0
- package/dist/conditions/exports.js.map +1 -1
- package/dist/conditions/function.d.ts.map +1 -1
- package/dist/conditions/function.js +19 -5
- package/dist/conditions/function.js.map +1 -1
- package/dist/conditions/helpers.d.ts +2 -1
- package/dist/conditions/helpers.d.ts.map +1 -1
- package/dist/conditions/helpers.js +4 -1
- package/dist/conditions/helpers.js.map +1 -1
- package/dist/conditions/jsx.d.ts.map +1 -1
- package/dist/conditions/jsx.js +7 -2
- package/dist/conditions/jsx.js.map +1 -1
- package/dist/conditions/match-identity.d.ts +43 -0
- package/dist/conditions/match-identity.d.ts.map +1 -0
- package/dist/conditions/match-identity.js +43 -0
- package/dist/conditions/match-identity.js.map +1 -0
- package/dist/conditions/members.d.ts.map +1 -1
- package/dist/conditions/members.js +17 -4
- package/dist/conditions/members.js.map +1 -1
- package/dist/conditions/reverse-dependency.d.ts +18 -5
- package/dist/conditions/reverse-dependency.d.ts.map +1 -1
- package/dist/conditions/reverse-dependency.js +109 -92
- package/dist/conditions/reverse-dependency.js.map +1 -1
- package/dist/conditions/slice.d.ts +86 -2
- package/dist/conditions/slice.d.ts.map +1 -1
- package/dist/conditions/slice.js +234 -53
- package/dist/conditions/slice.js.map +1 -1
- package/dist/conditions/structural.d.ts.map +1 -1
- package/dist/conditions/structural.js +16 -4
- package/dist/conditions/structural.js.map +1 -1
- package/dist/core/check-all.d.ts +19 -0
- package/dist/core/check-all.d.ts.map +1 -0
- package/dist/core/check-all.js +96 -0
- package/dist/core/check-all.js.map +1 -0
- package/dist/core/combinators.d.ts +48 -0
- package/dist/core/combinators.d.ts.map +1 -0
- package/dist/core/combinators.js +56 -0
- package/dist/core/combinators.js.map +1 -0
- package/dist/core/descendant-cache.d.ts +36 -0
- package/dist/core/descendant-cache.d.ts.map +1 -0
- package/dist/core/descendant-cache.js +119 -0
- package/dist/core/descendant-cache.js.map +1 -0
- package/dist/core/diagnose.d.ts +234 -0
- package/dist/core/diagnose.d.ts.map +1 -0
- package/dist/core/diagnose.js +382 -0
- package/dist/core/diagnose.js.map +1 -0
- package/dist/core/disk-set.d.ts +48 -0
- package/dist/core/disk-set.d.ts.map +1 -0
- package/dist/core/disk-set.js +218 -0
- package/dist/core/disk-set.js.map +1 -0
- package/dist/core/element-cache.d.ts +75 -0
- package/dist/core/element-cache.d.ts.map +1 -0
- package/dist/core/element-cache.js +32 -0
- package/dist/core/element-cache.js.map +1 -0
- package/dist/core/empty-project-advice.d.ts +75 -0
- package/dist/core/empty-project-advice.d.ts.map +1 -0
- package/dist/core/empty-project-advice.js +61 -0
- package/dist/core/empty-project-advice.js.map +1 -0
- package/dist/core/exclusion-comments.d.ts +54 -0
- package/dist/core/exclusion-comments.d.ts.map +1 -0
- package/dist/core/exclusion-comments.js +310 -0
- package/dist/core/exclusion-comments.js.map +1 -0
- package/dist/core/execute-rule.d.ts +150 -0
- package/dist/core/execute-rule.d.ts.map +1 -0
- package/dist/core/execute-rule.js +580 -0
- package/dist/core/execute-rule.js.map +1 -0
- package/dist/core/glob-diagnosis.d.ts +78 -0
- package/dist/core/glob-diagnosis.d.ts.map +1 -0
- package/dist/core/glob-diagnosis.js +134 -0
- package/dist/core/glob-diagnosis.js.map +1 -0
- package/dist/core/glob-evaluator.d.ts +48 -0
- package/dist/core/glob-evaluator.d.ts.map +1 -0
- package/dist/core/glob-evaluator.js +89 -0
- package/dist/core/glob-evaluator.js.map +1 -0
- package/dist/core/import-candidates.d.ts +60 -0
- package/dist/core/import-candidates.d.ts.map +1 -0
- package/dist/core/import-candidates.js +93 -0
- package/dist/core/import-candidates.js.map +1 -0
- package/dist/core/import-options.d.ts +55 -2
- package/dist/core/import-options.d.ts.map +1 -1
- package/dist/core/import-options.js +64 -0
- package/dist/core/import-options.js.map +1 -1
- package/dist/core/line-index.d.ts +3 -0
- package/dist/core/line-index.d.ts.map +1 -0
- package/dist/core/line-index.js +229 -0
- package/dist/core/line-index.js.map +1 -0
- package/dist/core/metric-violation.d.ts +86 -0
- package/dist/core/metric-violation.d.ts.map +1 -0
- package/dist/core/metric-violation.js +132 -0
- package/dist/core/metric-violation.js.map +1 -0
- package/dist/core/module-edges.d.ts +361 -0
- package/dist/core/module-edges.d.ts.map +1 -0
- package/dist/core/module-edges.js +592 -0
- package/dist/core/module-edges.js.map +1 -0
- package/dist/core/object-literal-functions.d.ts +42 -0
- package/dist/core/object-literal-functions.d.ts.map +1 -0
- package/dist/core/object-literal-functions.js +75 -0
- package/dist/core/object-literal-functions.js.map +1 -0
- package/dist/core/orphan-exclusions.d.ts +44 -0
- package/dist/core/orphan-exclusions.d.ts.map +1 -0
- package/dist/core/orphan-exclusions.js +214 -0
- package/dist/core/orphan-exclusions.js.map +1 -0
- package/dist/core/pair-condition.d.ts +32 -3
- package/dist/core/pair-condition.d.ts.map +1 -1
- package/dist/core/path-universe.d.ts +54 -0
- package/dist/core/path-universe.d.ts.map +1 -0
- package/dist/core/path-universe.js +65 -0
- package/dist/core/path-universe.js.map +1 -0
- package/dist/core/per-root-compiler-options.d.ts +20 -0
- package/dist/core/per-root-compiler-options.d.ts.map +1 -0
- package/dist/core/per-root-compiler-options.js +57 -0
- package/dist/core/per-root-compiler-options.js.map +1 -0
- package/dist/core/project-relative.d.ts +98 -0
- package/dist/core/project-relative.d.ts.map +1 -0
- package/dist/core/project-relative.js +185 -0
- package/dist/core/project-relative.js.map +1 -0
- package/dist/core/project.d.ts +7 -1
- package/dist/core/project.d.ts.map +1 -1
- package/dist/core/project.js +24 -0
- package/dist/core/project.js.map +1 -1
- package/dist/core/rule-builder.d.ts +282 -0
- package/dist/core/rule-builder.d.ts.map +1 -0
- package/dist/core/rule-builder.js +445 -0
- package/dist/core/rule-builder.js.map +1 -0
- package/dist/core/rule-declaration.d.ts +32 -0
- package/dist/core/rule-declaration.d.ts.map +1 -0
- package/dist/core/rule-declaration.js +126 -0
- package/dist/core/rule-declaration.js.map +1 -0
- package/dist/core/terminal-builder.d.ts +625 -0
- package/dist/core/terminal-builder.d.ts.map +1 -0
- package/dist/core/terminal-builder.js +787 -0
- package/dist/core/terminal-builder.js.map +1 -0
- package/dist/core/terminal-execution.d.ts +23 -0
- package/dist/core/terminal-execution.d.ts.map +1 -0
- package/dist/core/terminal-execution.js +71 -0
- package/dist/core/terminal-execution.js.map +1 -0
- package/dist/core/vacuity-diagnosis.d.ts +103 -0
- package/dist/core/vacuity-diagnosis.d.ts.map +1 -0
- package/dist/core/vacuity-diagnosis.js +415 -0
- package/dist/core/vacuity-diagnosis.js.map +1 -0
- package/dist/core/violation.d.ts +16 -11
- package/dist/core/violation.d.ts.map +1 -1
- package/dist/core/violation.js +33 -13
- package/dist/core/violation.js.map +1 -1
- package/dist/graphql/index.js +1 -1
- package/dist/graphql/index.js.map +1 -1
- package/dist/graphql/resolver-rule-builder.d.ts +98 -23
- package/dist/graphql/resolver-rule-builder.d.ts.map +1 -1
- package/dist/graphql/resolver-rule-builder.js +151 -21
- package/dist/graphql/resolver-rule-builder.js.map +1 -1
- package/dist/graphql/schema-loader.d.ts +21 -0
- package/dist/graphql/schema-loader.d.ts.map +1 -1
- package/dist/graphql/schema-loader.js +81 -12
- package/dist/graphql/schema-loader.js.map +1 -1
- package/dist/graphql/schema-rule-builder.d.ts +57 -20
- package/dist/graphql/schema-rule-builder.d.ts.map +1 -1
- package/dist/graphql/schema-rule-builder.js +101 -25
- package/dist/graphql/schema-rule-builder.js.map +1 -1
- package/dist/helpers/baseline-diagnostics.d.ts +45 -0
- package/dist/helpers/baseline-diagnostics.d.ts.map +1 -0
- package/dist/helpers/baseline-diagnostics.js +168 -0
- package/dist/helpers/baseline-diagnostics.js.map +1 -0
- package/dist/helpers/baseline-generator.d.ts +19 -0
- package/dist/helpers/baseline-generator.d.ts.map +1 -0
- package/dist/helpers/baseline-generator.js +18 -0
- package/dist/helpers/baseline-generator.js.map +1 -0
- package/dist/helpers/baseline.d.ts +336 -0
- package/dist/helpers/baseline.d.ts.map +1 -0
- package/dist/helpers/baseline.js +664 -0
- package/dist/helpers/baseline.js.map +1 -0
- package/dist/helpers/body-traversal.d.ts +27 -2
- package/dist/helpers/body-traversal.d.ts.map +1 -1
- package/dist/helpers/body-traversal.js +122 -16
- package/dist/helpers/body-traversal.js.map +1 -1
- package/dist/helpers/callback-extractor.d.ts.map +1 -1
- package/dist/helpers/callback-extractor.js +48 -47
- package/dist/helpers/callback-extractor.js.map +1 -1
- package/dist/helpers/complexity.d.ts +13 -5
- package/dist/helpers/complexity.d.ts.map +1 -1
- package/dist/helpers/complexity.js +15 -6
- package/dist/helpers/complexity.js.map +1 -1
- package/dist/helpers/diff-aware.d.ts +44 -0
- package/dist/helpers/diff-aware.d.ts.map +1 -0
- package/dist/helpers/diff-aware.js +84 -0
- package/dist/helpers/diff-aware.js.map +1 -0
- package/dist/helpers/matchers.d.ts +68 -7
- package/dist/helpers/matchers.d.ts.map +1 -1
- package/dist/helpers/matchers.js +114 -20
- package/dist/helpers/matchers.js.map +1 -1
- package/dist/helpers/slice-graph.d.ts +100 -8
- package/dist/helpers/slice-graph.d.ts.map +1 -1
- package/dist/helpers/slice-graph.js +126 -34
- package/dist/helpers/slice-graph.js.map +1 -1
- package/dist/helpers/tarjan.d.ts +0 -14
- package/dist/helpers/tarjan.d.ts.map +1 -1
- package/dist/helpers/tarjan.js +29 -16
- package/dist/helpers/tarjan.js.map +1 -1
- package/dist/index.d.ts +56 -17
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +57 -13
- package/dist/index.js.map +1 -1
- package/dist/models/arch-call.d.ts +3 -2
- package/dist/models/arch-call.d.ts.map +1 -1
- package/dist/models/arch-call.js.map +1 -1
- package/dist/models/arch-function.d.ts +28 -6
- package/dist/models/arch-function.d.ts.map +1 -1
- package/dist/models/arch-function.js +106 -3
- package/dist/models/arch-function.js.map +1 -1
- package/dist/models/arch-jsx-element.js +2 -2
- package/dist/models/arch-jsx-element.js.map +1 -1
- package/dist/models/slice.d.ts +33 -9
- package/dist/models/slice.d.ts.map +1 -1
- package/dist/models/slice.js +129 -19
- package/dist/models/slice.js.map +1 -1
- package/dist/predicates/call.d.ts.map +1 -1
- package/dist/predicates/call.js +5 -0
- package/dist/predicates/call.js.map +1 -1
- package/dist/predicates/identity.d.ts +21 -0
- package/dist/predicates/identity.d.ts.map +1 -1
- package/dist/predicates/identity.js +75 -3
- package/dist/predicates/identity.js.map +1 -1
- package/dist/predicates/index.d.ts +1 -1
- package/dist/predicates/index.d.ts.map +1 -1
- package/dist/predicates/index.js +1 -1
- package/dist/predicates/index.js.map +1 -1
- package/dist/predicates/metrics.d.ts +4 -2
- package/dist/predicates/metrics.d.ts.map +1 -1
- package/dist/predicates/metrics.js +4 -2
- package/dist/predicates/metrics.js.map +1 -1
- package/dist/predicates/module.d.ts +4 -12
- package/dist/predicates/module.d.ts.map +1 -1
- package/dist/predicates/module.js +57 -37
- package/dist/predicates/module.js.map +1 -1
- package/dist/presets/agent-guardrails.d.ts +22 -15
- package/dist/presets/agent-guardrails.d.ts.map +1 -1
- package/dist/presets/agent-guardrails.js +124 -19
- package/dist/presets/agent-guardrails.js.map +1 -1
- package/dist/presets/boundaries.d.ts +45 -2
- package/dist/presets/boundaries.d.ts.map +1 -1
- package/dist/presets/boundaries.js +132 -23
- package/dist/presets/boundaries.js.map +1 -1
- package/dist/presets/data-layer.d.ts +22 -2
- package/dist/presets/data-layer.d.ts.map +1 -1
- package/dist/presets/data-layer.js +58 -16
- package/dist/presets/data-layer.js.map +1 -1
- package/dist/presets/index.d.ts +7 -5
- package/dist/presets/index.d.ts.map +1 -1
- package/dist/presets/index.js +9 -3
- package/dist/presets/index.js.map +1 -1
- package/dist/presets/layered.d.ts +46 -2
- package/dist/presets/layered.d.ts.map +1 -1
- package/dist/presets/layered.js +66 -29
- package/dist/presets/layered.js.map +1 -1
- package/dist/presets/recommended.d.ts +80 -9
- package/dist/presets/recommended.d.ts.map +1 -1
- package/dist/presets/recommended.js +25 -23
- package/dist/presets/recommended.js.map +1 -1
- package/dist/presets/shared.d.ts +270 -2
- package/dist/presets/shared.d.ts.map +1 -1
- package/dist/presets/shared.js +338 -4
- package/dist/presets/shared.js.map +1 -1
- package/dist/rules/code-quality.d.ts.map +1 -1
- package/dist/rules/code-quality.js +15 -6
- package/dist/rules/code-quality.js.map +1 -1
- package/dist/rules/hygiene.d.ts +1 -1
- package/dist/rules/hygiene.js +1 -1
- package/dist/rules/metrics-function.d.ts +2 -1
- package/dist/rules/metrics-function.d.ts.map +1 -1
- package/dist/rules/metrics-function.js +33 -6
- package/dist/rules/metrics-function.js.map +1 -1
- package/dist/rules/metrics.d.ts +2 -1
- package/dist/rules/metrics.d.ts.map +1 -1
- package/dist/rules/metrics.js +39 -9
- package/dist/rules/metrics.js.map +1 -1
- package/dist/smells/duplicate-bodies.d.ts +58 -5
- package/dist/smells/duplicate-bodies.d.ts.map +1 -1
- package/dist/smells/duplicate-bodies.js +137 -56
- package/dist/smells/duplicate-bodies.js.map +1 -1
- package/dist/smells/fingerprint.d.ts +8 -1
- package/dist/smells/fingerprint.d.ts.map +1 -1
- package/dist/smells/fingerprint.js +21 -3
- package/dist/smells/fingerprint.js.map +1 -1
- package/dist/smells/inconsistent-siblings.d.ts +128 -7
- package/dist/smells/inconsistent-siblings.d.ts.map +1 -1
- package/dist/smells/inconsistent-siblings.js +288 -86
- package/dist/smells/inconsistent-siblings.js.map +1 -1
- package/dist/smells/sibling-files.d.ts +47 -0
- package/dist/smells/sibling-files.d.ts.map +1 -0
- package/dist/smells/sibling-files.js +104 -0
- package/dist/smells/sibling-files.js.map +1 -0
- package/dist/smells/similar-pairs.d.ts +27 -0
- package/dist/smells/similar-pairs.d.ts.map +1 -0
- package/dist/smells/similar-pairs.js +52 -0
- package/dist/smells/similar-pairs.js.map +1 -0
- package/dist/smells/smell-builder.d.ts +57 -12
- package/dist/smells/smell-builder.d.ts.map +1 -1
- package/dist/smells/smell-builder.js +111 -14
- package/dist/smells/smell-builder.js.map +1 -1
- package/dist/tsconfig/index.d.ts +19 -0
- package/dist/tsconfig/index.d.ts.map +1 -0
- package/dist/tsconfig/index.js +20 -0
- package/dist/tsconfig/index.js.map +1 -0
- package/dist/tsconfig/tsconfig-builder.d.ts +42 -9
- package/dist/tsconfig/tsconfig-builder.d.ts.map +1 -1
- package/dist/tsconfig/tsconfig-builder.js +64 -16
- package/dist/tsconfig/tsconfig-builder.js.map +1 -1
- package/package.json +3 -2
- package/dist/core/module-dependencies.d.ts +0 -28
- package/dist/core/module-dependencies.d.ts.map +0 -1
- package/dist/core/module-dependencies.js +0 -35
- package/dist/core/module-dependencies.js.map +0 -1
- package/dist/helpers/within.d.ts.map +0 -1
- package/dist/helpers/within.js.map +0 -1
|
@@ -1,12 +1,17 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
1
|
+
import { groupFilesByFolder, inertElement, inertMessage, partitionByPattern, } from './sibling-files.js';
|
|
2
|
+
import { selectionMemo } from '@nielspeter/eess';
|
|
3
3
|
import { SmellBuilder } from './smell-builder.js';
|
|
4
|
-
import { collectFunctions } from '../models/arch-function.js';
|
|
5
|
-
import { searchFunctionBody } from '../helpers/body-traversal.js';
|
|
6
4
|
/** Majority threshold — flag when >= 60% of siblings match but a file doesn't. */
|
|
7
5
|
const MAJORITY_THRESHOLD = 0.6;
|
|
8
6
|
/** Test file patterns for ignoreTests(). */
|
|
9
|
-
const
|
|
7
|
+
const selectionOf = selectionMemo();
|
|
8
|
+
/**
|
|
9
|
+
* 0102: diagnostic-first. N ships false (diagnose previews, check passes); the
|
|
10
|
+
* flip release sets true (check fails). Not a warn-first migration — a
|
|
11
|
+
* warning is invisible in a test run (bug 0024) and trains suppression.
|
|
12
|
+
* The N+1 flip is tracked in plan 0105.
|
|
13
|
+
*/
|
|
14
|
+
const INERT_FINDING_EMIT = false;
|
|
10
15
|
export class InconsistentSiblingsBuilder extends SmellBuilder {
|
|
11
16
|
_pattern;
|
|
12
17
|
constructor(project) {
|
|
@@ -14,40 +19,98 @@ export class InconsistentSiblingsBuilder extends SmellBuilder {
|
|
|
14
19
|
}
|
|
15
20
|
/** The pattern that most siblings should follow. */
|
|
16
21
|
forPattern(matcher) {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
22
|
+
const next = this.copy();
|
|
23
|
+
next._pattern = matcher;
|
|
24
|
+
return next;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Sibling files in folders large enough to be compared — plan 0096, and the
|
|
28
|
+
* ONE method both readers call.
|
|
29
|
+
*
|
|
30
|
+
* The `>= 2` threshold lives HERE rather than in `detect()`'s loop, which is
|
|
31
|
+
* what makes this a shared derivation instead of two that agree by luck.
|
|
32
|
+
* Filtering before the caller's sort is safe: `groupFilesByFolder()` returns
|
|
33
|
+
* insertion order and `detect()` sorts by folder name, a total order, so
|
|
34
|
+
* removing entries cannot reorder the survivors.
|
|
35
|
+
*/
|
|
36
|
+
selected() {
|
|
37
|
+
return selectionOf(this, () => [...groupFilesByFolder(this.scope()).entries()].filter(([, files]) => files.length >= 2));
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Units this detector examined — plan 0096: sibling files in folders large
|
|
41
|
+
* enough to be compared.
|
|
42
|
+
*
|
|
43
|
+
* Counted in units ITERATED, never in pattern matches: a tripwire that
|
|
44
|
+
* examines every sibling and matches none has non-empty evidence, which is the
|
|
45
|
+
* 0.34.0 carve-out this must not break.
|
|
46
|
+
*/
|
|
47
|
+
examinedUnits() {
|
|
48
|
+
return this.selected().reduce((total, [, files]) => total + files.length, 0);
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Pure recomputation of the corpus split — never accumulated on `this`. It is
|
|
52
|
+
* the SINGLE source the finding's message, the gate, and the diagnostic preview
|
|
53
|
+
* all read, so the preview and the finding cannot diverge, and calling it twice
|
|
54
|
+
* cannot double-count (each call recomputes). Mirrors `examinedUnits()`: one
|
|
55
|
+
* derivation, not an instance field.
|
|
56
|
+
*
|
|
57
|
+
* `canFireSoon` is true when any folder is at or within ONE edit of a majority —
|
|
58
|
+
* such a folder can fire, so it is not inert even though it does not fire today.
|
|
59
|
+
*
|
|
60
|
+
* The GUARD lives here, not at the emit site. A preview read through this
|
|
61
|
+
* accessor can then never report on a rule that can fire: a healthy control
|
|
62
|
+
* (majority present, `editsToMajority <= 1`) gets `canFireSoon = true`, and the
|
|
63
|
+
* hook below returns the empty string for it. The check-time emit and the
|
|
64
|
+
* diagnostic preview share this one predicate by construction.
|
|
65
|
+
*/
|
|
66
|
+
inertAssessment(pattern) {
|
|
67
|
+
let matching = 0;
|
|
68
|
+
let total = 0;
|
|
69
|
+
let canFireSoon = false;
|
|
70
|
+
const folders = [];
|
|
71
|
+
for (const [folder, files] of this.selected()) {
|
|
72
|
+
const { matching: m, nonMatching } = partitionByPattern(files, pattern, this._minLines);
|
|
73
|
+
const t = m.length + nonMatching.length;
|
|
74
|
+
// Unreachable today: selected() already filters to files.length >= 2
|
|
75
|
+
// and partitionByPattern drops nothing, so t always equals a folder's
|
|
76
|
+
// file count and is never 0 here. Kept as future-proofing against a
|
|
77
|
+
// change to selected(), not a live branch.
|
|
78
|
+
if (t === 0)
|
|
28
79
|
continue;
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
80
|
+
matching += m.length;
|
|
81
|
+
total += t;
|
|
82
|
+
folders.push({ folder, matching: m, nonMatching });
|
|
83
|
+
// A folder at or within one edit of a majority is a live tripwire. Latched
|
|
84
|
+
// regardless of nonMatching===0: a fully-conforming majority folder is one
|
|
85
|
+
// divergent file away from firing, so it must suppress the inert finding.
|
|
86
|
+
//
|
|
87
|
+
// KNOWN LIMIT, conservative direction only (review: architect): for t=2,
|
|
88
|
+
// m=1 this reads editsToMajority = ceil(1.2) - 1 = 1 <= 1 and latches —
|
|
89
|
+
// but the only available edit (adopting the second file) yields m=2,
|
|
90
|
+
// nonMatching=0, which still cannot fire; a THIRD file would be needed.
|
|
91
|
+
// A two-file folder with one match (e.g. an index.ts plus one impl) is
|
|
92
|
+
// an ordinary shape, so this latch is reachable in practice, not a
|
|
93
|
+
// corner case. Never a false POSITIVE (a rule that can genuinely fire
|
|
94
|
+
// soon is never reported inert) — only a false latch that keeps a
|
|
95
|
+
// genuinely-inert corpus silent, and it is OR'd across every folder in
|
|
96
|
+
// scope, so one such folder anywhere suppresses the whole rule's inert
|
|
97
|
+
// finding. Not fixed here: the arithmetic is a documented approximation
|
|
98
|
+
// of "close to firing", not a precise one, and narrowing it is a
|
|
99
|
+
// separate, non-urgent piece of work.
|
|
100
|
+
const editsToMajority = Math.ceil(MAJORITY_THRESHOLD * t) - m.length;
|
|
101
|
+
if (editsToMajority <= 1)
|
|
102
|
+
canFireSoon = true;
|
|
45
103
|
}
|
|
46
|
-
return { matching,
|
|
104
|
+
return { matching, total, canFireSoon, folders };
|
|
47
105
|
}
|
|
48
106
|
/** Build violations for non-matching files in a folder where the majority matches. */
|
|
49
|
-
|
|
50
|
-
|
|
107
|
+
/**
|
|
108
|
+
* The two description strings travel together as one `describe` — adjacent
|
|
109
|
+
* positional strings are the transposition the parameter cap guards against,
|
|
110
|
+
* and these two read alike.
|
|
111
|
+
*/
|
|
112
|
+
buildFolderViolations(folder, matching, nonMatching, describe) {
|
|
113
|
+
const { rule: ruleDescription, pattern: patternDesc } = describe;
|
|
51
114
|
const total = matching.length + nonMatching.length;
|
|
52
115
|
const violations = [];
|
|
53
116
|
for (const sf of nonMatching) {
|
|
@@ -58,78 +121,217 @@ export class InconsistentSiblingsBuilder extends SmellBuilder {
|
|
|
58
121
|
line: 1,
|
|
59
122
|
message: `${String(matching.length)} of ${String(total)} files in ${folder} use ${patternDesc}, ` +
|
|
60
123
|
`but ${sf.getBaseName()} does not`,
|
|
124
|
+
// The message states the population ("3 of 5"), which is a fact about
|
|
125
|
+
// the folder rather than about this file: adding one unrelated sibling
|
|
126
|
+
// rewrites it, and every already-accepted finding in the folder loses
|
|
127
|
+
// its identity. The finding itself is "this file, in this folder, does
|
|
128
|
+
// not follow this pattern" — that, and only that, is the identity.
|
|
129
|
+
identity: `inconsistent-sibling::${sf.getFilePath()}::${patternDesc}`,
|
|
61
130
|
because: this._reason,
|
|
62
131
|
});
|
|
63
132
|
}
|
|
64
133
|
return violations;
|
|
65
134
|
}
|
|
135
|
+
/**
|
|
136
|
+
* Whether this rule states an assertion at all — the assertion gate's question.
|
|
137
|
+
*
|
|
138
|
+
* True once a condition has been added.
|
|
139
|
+
*
|
|
140
|
+
* Overrides the `TerminalBuilder` default (`true`), whose JSDoc carries the
|
|
141
|
+
* contract and the reason this is public rather than protected.
|
|
142
|
+
*/
|
|
143
|
+
assertsSomething() {
|
|
144
|
+
return this._pattern !== undefined;
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Named by id or by what the detector checks — the inherited version says
|
|
148
|
+
* 'unnamed'. Uses `describe()` rather than a call-site locator for the same
|
|
149
|
+
* reason as `SliceRuleBuilder`: `explain --format agent` reads this field.
|
|
150
|
+
*/
|
|
151
|
+
/**
|
|
152
|
+
* This detector counts sibling FILES, not function bodies — plan 0099.
|
|
153
|
+
*
|
|
154
|
+
* It inherited `SmellBuilder`'s noun while `examinedUnits()` sums
|
|
155
|
+
* `files.length`, so the message read "examined 0 function bodies" for a
|
|
156
|
+
* file-counting detector: the category error this plan exists to remove, in the
|
|
157
|
+
* family sitting directly under the override.
|
|
158
|
+
*/
|
|
159
|
+
examinedUnitNoun() {
|
|
160
|
+
return 'sibling files';
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* A structural description of this rule without executing it — what `explain`
|
|
164
|
+
* prints and what the ADR/citation gates read.
|
|
165
|
+
*/
|
|
166
|
+
describeRule() {
|
|
167
|
+
return {
|
|
168
|
+
...super.describeRule(),
|
|
169
|
+
rule: this._metadata?.id ?? this.describe(),
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* The remedy for this builder's assertion-less state, as one string.
|
|
174
|
+
*
|
|
175
|
+
* One channel, so `diagnose()`'s advice and the finding's own message cannot
|
|
176
|
+
* disagree. Overrides `TerminalBuilder`'s generic text with wording specific to
|
|
177
|
+
* what this builder is missing.
|
|
178
|
+
*/
|
|
179
|
+
assertionAdvice() {
|
|
180
|
+
return ('this detector has no pattern, so it detects nothing and can never fail. Add ' +
|
|
181
|
+
'.forPattern(...), or use smells.duplicateBodies() for patternless duplicate detection.');
|
|
182
|
+
}
|
|
183
|
+
/** The file-selection facts `sibling-files.ts` needs, stated as a value. */
|
|
184
|
+
scope() {
|
|
185
|
+
return {
|
|
186
|
+
project: this.project,
|
|
187
|
+
folders: this._folders,
|
|
188
|
+
ignorePaths: this._ignorePaths,
|
|
189
|
+
ignoreTests: this._ignoreTests,
|
|
190
|
+
minLines: this._minLines,
|
|
191
|
+
pattern: this._pattern,
|
|
192
|
+
};
|
|
193
|
+
}
|
|
66
194
|
detect() {
|
|
67
|
-
|
|
195
|
+
// Unreachable at runtime as of 0.23.0: the assertion gate reports a
|
|
196
|
+
// patternless detector as a configuration finding before `detect()` is
|
|
197
|
+
// called (bug 0019), so `_pattern` is always set by the time we get here.
|
|
198
|
+
// The branch stays only because strict null checks need the narrowing —
|
|
199
|
+
// hence the local, which the rest of the method reads instead of the field.
|
|
200
|
+
// Do not treat it as the answer to "what happens with no pattern": the loud
|
|
201
|
+
// answer is the gate, and if this ever returns `[]` again the gate is gone.
|
|
202
|
+
const pattern = this._pattern;
|
|
203
|
+
if (!pattern)
|
|
68
204
|
return [];
|
|
69
|
-
const filesByFolder = this.groupFilesByFolder();
|
|
70
205
|
const ruleDescription = this.describe();
|
|
71
|
-
const patternDesc =
|
|
72
|
-
const folderEntries = [...filesByFolder.entries()];
|
|
73
|
-
if (this._groupByFolder) {
|
|
74
|
-
folderEntries.sort((a, b) => a[0].localeCompare(b[0]));
|
|
75
|
-
}
|
|
206
|
+
const patternDesc = pattern.description;
|
|
76
207
|
const violations = [];
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
208
|
+
// ONE assessment — the single partition walk. It returns the aggregate
|
|
209
|
+
// (matching/total/canFireSoon) AND the per-folder partitions; detect() uses
|
|
210
|
+
// both. Do NOT call partitionByPattern again below: the searchFunctionBody
|
|
211
|
+
// walk is the dominant cost and must run once per check().
|
|
212
|
+
const a = this.inertAssessment(pattern);
|
|
213
|
+
// Preserves groupByFolder()'s existing violation-ordering contract for
|
|
214
|
+
// THIS family (review: testing — an earlier version of this comment cited
|
|
215
|
+
// tests/integration/coverage-gaps.test.ts's "groupByFolder produces
|
|
216
|
+
// violations sorted by directory" row, which covers duplicateBodies and
|
|
217
|
+
// asserts only `.toThrow()`, not order, for either family; the real guard
|
|
218
|
+
// for inconsistentSiblings is
|
|
219
|
+
// "groupByFolder() sorts violations across folders" below). Sorting the
|
|
220
|
+
// returned folder array is O(k log k) on folder COUNT, not a second AST
|
|
221
|
+
// walk: inertAssessment()'s searchFunctionBody pass already ran once,
|
|
222
|
+
// above, and this sort touches only its already-computed output.
|
|
223
|
+
const folders = this._groupByFolder
|
|
224
|
+
? [...a.folders].sort((x, y) => x.folder.localeCompare(y.folder))
|
|
225
|
+
: a.folders;
|
|
226
|
+
for (const { folder, matching, nonMatching } of folders) {
|
|
81
227
|
const total = matching.length + nonMatching.length;
|
|
82
|
-
if (total === 0)
|
|
83
|
-
continue;
|
|
84
228
|
if (matching.length / total < MAJORITY_THRESHOLD)
|
|
85
229
|
continue;
|
|
86
230
|
if (nonMatching.length === 0)
|
|
87
231
|
continue;
|
|
88
|
-
violations.push(...this.buildFolderViolations({
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
nonMatching,
|
|
92
|
-
ruleDescription,
|
|
93
|
-
patternDesc,
|
|
232
|
+
violations.push(...this.buildFolderViolations(folder, matching, nonMatching, {
|
|
233
|
+
rule: ruleDescription,
|
|
234
|
+
pattern: patternDesc,
|
|
94
235
|
}));
|
|
95
236
|
}
|
|
237
|
+
// The adequacy floor, VERSION-GATED and SHARED-SOURCE. NOT a floor extension —
|
|
238
|
+
// the real floor fires at violations===0 && examined===0; this fires at
|
|
239
|
+
// violations===0 && examined>0 && no folder is within one edit of a majority.
|
|
240
|
+
// The guard lives in the shared advice function, not here: the emit and the
|
|
241
|
+
// diagnostic preview are the same derivation, so the preview can never report
|
|
242
|
+
// on a rule that can fire. detect() passes the assessment it ALREADY computed
|
|
243
|
+
// (no second partition walk); the empty-string return means "not inert".
|
|
244
|
+
const advice = this.inertAdviceFor(a);
|
|
245
|
+
if (violations.length === 0 && this.inertEmitEnabled() && advice !== '') {
|
|
246
|
+
return [this.inertViolation(advice), ...violations];
|
|
247
|
+
}
|
|
96
248
|
return violations;
|
|
97
249
|
}
|
|
250
|
+
/**
|
|
251
|
+
* The `DiagnosableRule` hook — public, no-arg, exactly the shape the
|
|
252
|
+
* interface requires. Recomputes `inertAssessment()` fresh, so `diagnose()`
|
|
253
|
+
* on a builder that never ran `check()` still gets truthful numbers.
|
|
254
|
+
*/
|
|
255
|
+
inertAdvice() {
|
|
256
|
+
const pattern = this._pattern;
|
|
257
|
+
if (!pattern)
|
|
258
|
+
return '';
|
|
259
|
+
return this.inertAdviceFor(this.inertAssessment(pattern));
|
|
260
|
+
}
|
|
261
|
+
/**
|
|
262
|
+
* THE single derivation for both surfaces, and the guard. Returns the empty
|
|
263
|
+
* string unless the rule is genuinely inert (`matching > 0 && !canFireSoon &&
|
|
264
|
+
* !declaresEmpty()`), so a healthy control (majority present, within one edit
|
|
265
|
+
* of one, or declared empty) gets `''` and diagnose() reports nothing for it.
|
|
266
|
+
* Both `inertAdvice()` above and `detect()`'s emit call this with an
|
|
267
|
+
* `Assessment` — the SAME guard and the SAME message either way, so the
|
|
268
|
+
* preview and the finding cannot diverge.
|
|
269
|
+
*
|
|
270
|
+
* `!this.declaresEmpty()` lives HERE, not only in `inertEmitEnabled()` (review:
|
|
271
|
+
* architect) — a declared-empty rule reports its expiry, not the inert finding,
|
|
272
|
+
* on BOTH surfaces, so `diagnose()`'s preview and `check()`'s eventual failure
|
|
273
|
+
* name the same cause for the same rule state. Before this fix the two clauses
|
|
274
|
+
* were split across two functions and only the emit path read the declaration,
|
|
275
|
+
* so a rule carrying `.expectEmpty()` could preview one cause via `diagnose()`
|
|
276
|
+
* and fail with a different one via `check()` — the exact drift this shared-
|
|
277
|
+
* derivation design exists to make impossible "by construction".
|
|
278
|
+
*/
|
|
279
|
+
inertAdviceFor(a) {
|
|
280
|
+
if (a.matching === 0 || a.canFireSoon || this.declaresEmpty())
|
|
281
|
+
return ''; // the GUARD lives here
|
|
282
|
+
return inertMessage(a, this._pattern?.description ?? 'unknown pattern');
|
|
283
|
+
}
|
|
284
|
+
inertViolation(advice) {
|
|
285
|
+
return {
|
|
286
|
+
rule: this.describe(),
|
|
287
|
+
ruleId: this._metadata?.id,
|
|
288
|
+
element: inertElement(this.scope()),
|
|
289
|
+
file: '',
|
|
290
|
+
line: 0,
|
|
291
|
+
message: advice,
|
|
292
|
+
// Its own remedy, never the author's (bug 0021): the message IS the remedy.
|
|
293
|
+
suggestion: advice,
|
|
294
|
+
because: this._reason,
|
|
295
|
+
// Adequacy finding: the rule enforces nothing, which is not a severity the
|
|
296
|
+
// author grades (ADR-009 rule 1). Config-level: no file to attribute, so it
|
|
297
|
+
// must survive diff/baseline or the guard re-greens under standard CI.
|
|
298
|
+
bypassFilters: true,
|
|
299
|
+
// No `identity` set, deliberately, NOT an oversight (review: architect
|
|
300
|
+
// flagged the gap; this is why it stays a gap): `bypassFilters: true`
|
|
301
|
+
// means baseline/diff never read one today, so setting one buys nothing
|
|
302
|
+
// yet — and `message` embeds the population count ("examined N... only
|
|
303
|
+
// M of them"), which ADR-009 rule 4 forbids putting in an identity (a
|
|
304
|
+
// count rots). If `bypassFilters` is ever relaxed for this family, an
|
|
305
|
+
// `identity` has to be added HERE, population-free, before that ships —
|
|
306
|
+
// this comment is the tripwire for that future change, not a promise
|
|
307
|
+
// the coupling is already handled.
|
|
308
|
+
};
|
|
309
|
+
}
|
|
310
|
+
/**
|
|
311
|
+
* The pure version gate — nothing else. The `_expectEmpty` precedence
|
|
312
|
+
* (`!this.declaresEmpty()`) moved into `inertAdviceFor()` above, so it applies
|
|
313
|
+
* identically to `diagnose()`'s preview and to this emit path; this function's
|
|
314
|
+
* only job is "has the N+1 release happened yet."
|
|
315
|
+
*
|
|
316
|
+
* `INERT_FINDING_EMIT` is deliberately a bare, non-exported module `const` —
|
|
317
|
+
* not an env var, constructor option, or per-project override. The
|
|
318
|
+
* `bypassFilters: true` / unsuppressable design above depends on there being
|
|
319
|
+
* no configuration surface a future contributor could "helpfully" expose,
|
|
320
|
+
* which would undermine it silently.
|
|
321
|
+
*
|
|
322
|
+
* `protected`, not `private` — a test-only subclass overrides this to exercise
|
|
323
|
+
* the emit path (`inertViolation()`, `inertElement()`, the `detect()` branch
|
|
324
|
+
* that pushes them) before the N+1 flip ships, so that path is not shipped
|
|
325
|
+
* with zero test coverage merely because the real gate is off today (review:
|
|
326
|
+
* testing). See `tests/smells/inconsistent-siblings.test.ts`'s
|
|
327
|
+
* `EmittingSiblings` subclass.
|
|
328
|
+
*/
|
|
329
|
+
inertEmitEnabled() {
|
|
330
|
+
return INERT_FINDING_EMIT;
|
|
331
|
+
}
|
|
98
332
|
describe() {
|
|
99
333
|
const pattern = this._pattern?.description ?? 'unknown pattern';
|
|
100
334
|
return `Sibling files should consistently use ${pattern}`;
|
|
101
335
|
}
|
|
102
|
-
/** Group source files by parent folder, applying all filters. */
|
|
103
|
-
groupFilesByFolder() {
|
|
104
|
-
const sourceFiles = this.project.getSourceFiles();
|
|
105
|
-
const folderMatchers = this._folders.map((g) => picomatch(g));
|
|
106
|
-
const ignoreMatchers = this._ignorePaths.map((g) => picomatch(g));
|
|
107
|
-
const testMatchers = this._ignoreTests ? TEST_PATTERNS.map((g) => picomatch(g)) : [];
|
|
108
|
-
const groups = new Map();
|
|
109
|
-
for (const sf of sourceFiles) {
|
|
110
|
-
const filePath = sf.getFilePath();
|
|
111
|
-
// Folder filter: if folders specified, file must match at least one
|
|
112
|
-
if (folderMatchers.length > 0 && !folderMatchers.some((m) => m(filePath))) {
|
|
113
|
-
continue;
|
|
114
|
-
}
|
|
115
|
-
// Ignore paths filter
|
|
116
|
-
if (ignoreMatchers.some((m) => m(filePath))) {
|
|
117
|
-
continue;
|
|
118
|
-
}
|
|
119
|
-
// Test file filter
|
|
120
|
-
if (testMatchers.some((m) => m(filePath))) {
|
|
121
|
-
continue;
|
|
122
|
-
}
|
|
123
|
-
const folder = path.dirname(filePath);
|
|
124
|
-
const existing = groups.get(folder);
|
|
125
|
-
if (existing) {
|
|
126
|
-
existing.push(sf);
|
|
127
|
-
}
|
|
128
|
-
else {
|
|
129
|
-
groups.set(folder, [sf]);
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
return groups;
|
|
133
|
-
}
|
|
134
336
|
}
|
|
135
337
|
//# sourceMappingURL=inconsistent-siblings.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"inconsistent-siblings.js","sourceRoot":"","sources":["../../src/smells/inconsistent-siblings.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"inconsistent-siblings.js","sourceRoot":"","sources":["../../src/smells/inconsistent-siblings.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,kBAAkB,EAClB,YAAY,EACZ,YAAY,EACZ,kBAAkB,GACnB,MAAM,oBAAoB,CAAA;AAI3B,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAA;AAChD,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAA;AAKjD,kFAAkF;AAClF,MAAM,kBAAkB,GAAG,GAAG,CAAA;AAE9B,4CAA4C;AAE5C,MAAM,WAAW,GAAG,aAAa,EAA0B,CAAA;AAE3D;;;;;GAKG;AACH,MAAM,kBAAkB,GAAG,KAAK,CAAA;AAUhC,MAAM,OAAO,2BAA4B,SAAQ,YAAY;IACnD,QAAQ,CAAoB;IAEpC,YAAY,OAAoB;QAC9B,KAAK,CAAC,OAAO,CAAC,CAAA;IAChB,CAAC;IAED,oDAAoD;IACpD,UAAU,CAAC,OAA0B;QACnC,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,EAAE,CAAA;QACxB,IAAI,CAAC,QAAQ,GAAG,OAAO,CAAA;QACvB,OAAO,IAAI,CAAA;IACb,CAAC;IAED;;;;;;;;;OASG;IACK,QAAQ;QACd,OAAO,WAAW,CAAC,IAAI,EAAE,GAAG,EAAE,CAC5B,CAAC,GAAG,kBAAkB,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,MAAM,IAAI,CAAC,CAAC,CACzF,CAAA;IACH,CAAC;IAED;;;;;;;OAOG;IACH,aAAa;QACX,OAAO,IAAI,CAAC,QAAQ,EAAE,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,CAAC,EAAE,KAAK,CAAC,EAAE,EAAE,CAAC,KAAK,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC,CAAA;IAC9E,CAAC;IAED;;;;;;;;;;;;;;;OAeG;IACK,eAAe,CAAC,OAA0B;QAChD,IAAI,QAAQ,GAAG,CAAC,CAAA;QAChB,IAAI,KAAK,GAAG,CAAC,CAAA;QACb,IAAI,WAAW,GAAG,KAAK,CAAA;QACvB,MAAM,OAAO,GAA0B,EAAE,CAAA;QACzC,KAAK,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,IAAI,IAAI,CAAC,QAAQ,EAAE,EAAE,CAAC;YAC9C,MAAM,EAAE,QAAQ,EAAE,CAAC,EAAE,WAAW,EAAE,GAAG,kBAAkB,CAAC,KAAK,EAAE,OAAO,EAAE,IAAI,CAAC,SAAS,CAAC,CAAA;YACvF,MAAM,CAAC,GAAG,CAAC,CAAC,MAAM,GAAG,WAAW,CAAC,MAAM,CAAA;YACvC,qEAAqE;YACrE,sEAAsE;YACtE,oEAAoE;YACpE,2CAA2C;YAC3C,IAAI,CAAC,KAAK,CAAC;gBAAE,SAAQ;YACrB,QAAQ,IAAI,CAAC,CAAC,MAAM,CAAA;YACpB,KAAK,IAAI,CAAC,CAAA;YACV,OAAO,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,EAAE,WAAW,EAAE,CAAC,CAAA;YAClD,2EAA2E;YAC3E,2EAA2E;YAC3E,0EAA0E;YAC1E,EAAE;YACF,yEAAyE;YACzE,wEAAwE;YACxE,qEAAqE;YACrE,wEAAwE;YACxE,uEAAuE;YACvE,mEAAmE;YACnE,sEAAsE;YACtE,kEAAkE;YAClE,uEAAuE;YACvE,uEAAuE;YACvE,wEAAwE;YACxE,iEAAiE;YACjE,sCAAsC;YACtC,MAAM,eAAe,GAAG,IAAI,CAAC,IAAI,CAAC,kBAAkB,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,MAAM,CAAA;YACpE,IAAI,eAAe,IAAI,CAAC;gBAAE,WAAW,GAAG,IAAI,CAAA;QAC9C,CAAC;QACD,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,WAAW,EAAE,OAAO,EAAE,CAAA;IAClD,CAAC;IAED,sFAAsF;IACtF;;;;OAIG;IACK,qBAAqB,CAC3B,MAAc,EACd,QAAsB,EACtB,WAAyB,EACzB,QAA2C;QAE3C,MAAM,EAAE,IAAI,EAAE,eAAe,EAAE,OAAO,EAAE,WAAW,EAAE,GAAG,QAAQ,CAAA;QAChE,MAAM,KAAK,GAAG,QAAQ,CAAC,MAAM,GAAG,WAAW,CAAC,MAAM,CAAA;QAClD,MAAM,UAAU,GAAoB,EAAE,CAAA;QACtC,KAAK,MAAM,EAAE,IAAI,WAAW,EAAE,CAAC;YAC7B,UAAU,CAAC,IAAI,CAAC;gBACd,IAAI,EAAE,eAAe;gBACrB,OAAO,EAAE,EAAE,CAAC,WAAW,EAAE;gBACzB,IAAI,EAAE,EAAE,CAAC,WAAW,EAAE;gBACtB,IAAI,EAAE,CAAC;gBACP,OAAO,EACL,GAAG,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,OAAO,MAAM,CAAC,KAAK,CAAC,aAAa,MAAM,QAAQ,WAAW,IAAI;oBACxF,OAAO,EAAE,CAAC,WAAW,EAAE,WAAW;gBACpC,sEAAsE;gBACtE,uEAAuE;gBACvE,sEAAsE;gBACtE,uEAAuE;gBACvE,mEAAmE;gBACnE,QAAQ,EAAE,yBAAyB,EAAE,CAAC,WAAW,EAAE,KAAK,WAAW,EAAE;gBACrE,OAAO,EAAE,IAAI,CAAC,OAAO;aACtB,CAAC,CAAA;QACJ,CAAC;QACD,OAAO,UAAU,CAAA;IACnB,CAAC;IAED;;;;;;;OAOG;IACM,gBAAgB;QACvB,OAAO,IAAI,CAAC,QAAQ,KAAK,SAAS,CAAA;IACpC,CAAC;IAED;;;;OAIG;IACH;;;;;;;OAOG;IACgB,gBAAgB;QACjC,OAAO,eAAe,CAAA;IACxB,CAAC;IAED;;;OAGG;IACM,YAAY;QACnB,OAAO;YACL,GAAG,KAAK,CAAC,YAAY,EAAE;YACvB,IAAI,EAAE,IAAI,CAAC,SAAS,EAAE,EAAE,IAAI,IAAI,CAAC,QAAQ,EAAE;SAC5C,CAAA;IACH,CAAC;IAED;;;;;;OAMG;IACM,eAAe;QACtB,OAAO,CACL,8EAA8E;YAC9E,wFAAwF,CACzF,CAAA;IACH,CAAC;IAED,4EAA4E;IACpE,KAAK;QACX,OAAO;YACL,OAAO,EAAE,IAAI,CAAC,OAAO;YACrB,OAAO,EAAE,IAAI,CAAC,QAAQ;YACtB,WAAW,EAAE,IAAI,CAAC,YAAY;YAC9B,WAAW,EAAE,IAAI,CAAC,YAAY;YAC9B,QAAQ,EAAE,IAAI,CAAC,SAAS;YACxB,OAAO,EAAE,IAAI,CAAC,QAAQ;SACvB,CAAA;IACH,CAAC;IAES,MAAM;QACd,oEAAoE;QACpE,uEAAuE;QACvE,0EAA0E;QAC1E,wEAAwE;QACxE,4EAA4E;QAC5E,4EAA4E;QAC5E,4EAA4E;QAC5E,MAAM,OAAO,GAAG,IAAI,CAAC,QAAQ,CAAA;QAC7B,IAAI,CAAC,OAAO;YAAE,OAAO,EAAE,CAAA;QAEvB,MAAM,eAAe,GAAG,IAAI,CAAC,QAAQ,EAAE,CAAA;QACvC,MAAM,WAAW,GAAG,OAAO,CAAC,WAAW,CAAA;QACvC,MAAM,UAAU,GAAoB,EAAE,CAAA;QAEtC,uEAAuE;QACvE,4EAA4E;QAC5E,2EAA2E;QAC3E,2DAA2D;QAC3D,MAAM,CAAC,GAAG,IAAI,CAAC,eAAe,CAAC,OAAO,CAAC,CAAA;QAEvC,uEAAuE;QACvE,0EAA0E;QAC1E,oEAAoE;QACpE,wEAAwE;QACxE,0EAA0E;QAC1E,8BAA8B;QAC9B,wEAAwE;QACxE,wEAAwE;QACxE,sEAAsE;QACtE,iEAAiE;QACjE,MAAM,OAAO,GAAG,IAAI,CAAC,cAAc;YACjC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,aAAa,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;YACjE,CAAC,CAAC,CAAC,CAAC,OAAO,CAAA;QAEb,KAAK,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,WAAW,EAAE,IAAI,OAAO,EAAE,CAAC;YACxD,MAAM,KAAK,GAAG,QAAQ,CAAC,MAAM,GAAG,WAAW,CAAC,MAAM,CAAA;YAClD,IAAI,QAAQ,CAAC,MAAM,GAAG,KAAK,GAAG,kBAAkB;gBAAE,SAAQ;YAC1D,IAAI,WAAW,CAAC,MAAM,KAAK,CAAC;gBAAE,SAAQ;YAEtC,UAAU,CAAC,IAAI,CACb,GAAG,IAAI,CAAC,qBAAqB,CAAC,MAAM,EAAE,QAAQ,EAAE,WAAW,EAAE;gBAC3D,IAAI,EAAE,eAAe;gBACrB,OAAO,EAAE,WAAW;aACrB,CAAC,CACH,CAAA;QACH,CAAC;QAED,+EAA+E;QAC/E,wEAAwE;QACxE,8EAA8E;QAC9E,4EAA4E;QAC5E,8EAA8E;QAC9E,8EAA8E;QAC9E,yEAAyE;QACzE,MAAM,MAAM,GAAG,IAAI,CAAC,cAAc,CAAC,CAAC,CAAC,CAAA;QACrC,IAAI,UAAU,CAAC,MAAM,KAAK,CAAC,IAAI,IAAI,CAAC,gBAAgB,EAAE,IAAI,MAAM,KAAK,EAAE,EAAE,CAAC;YACxE,OAAO,CAAC,IAAI,CAAC,cAAc,CAAC,MAAM,CAAC,EAAE,GAAG,UAAU,CAAC,CAAA;QACrD,CAAC;QACD,OAAO,UAAU,CAAA;IACnB,CAAC;IAED;;;;OAIG;IACH,WAAW;QACT,MAAM,OAAO,GAAG,IAAI,CAAC,QAAQ,CAAA;QAC7B,IAAI,CAAC,OAAO;YAAE,OAAO,EAAE,CAAA;QACvB,OAAO,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,eAAe,CAAC,OAAO,CAAC,CAAC,CAAA;IAC3D,CAAC;IAED;;;;;;;;;;;;;;;;;OAiBG;IACK,cAAc,CAAC,CAAa;QAClC,IAAI,CAAC,CAAC,QAAQ,KAAK,CAAC,IAAI,CAAC,CAAC,WAAW,IAAI,IAAI,CAAC,aAAa,EAAE;YAAE,OAAO,EAAE,CAAA,CAAC,uBAAuB;QAChG,OAAO,YAAY,CAAC,CAAC,EAAE,IAAI,CAAC,QAAQ,EAAE,WAAW,IAAI,iBAAiB,CAAC,CAAA;IACzE,CAAC;IAEO,cAAc,CAAC,MAAc;QACnC,OAAO;YACL,IAAI,EAAE,IAAI,CAAC,QAAQ,EAAE;YACrB,MAAM,EAAE,IAAI,CAAC,SAAS,EAAE,EAAE;YAC1B,OAAO,EAAE,YAAY,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC;YACnC,IAAI,EAAE,EAAE;YACR,IAAI,EAAE,CAAC;YACP,OAAO,EAAE,MAAM;YACf,4EAA4E;YAC5E,UAAU,EAAE,MAAM;YAClB,OAAO,EAAE,IAAI,CAAC,OAAO;YACrB,2EAA2E;YAC3E,4EAA4E;YAC5E,uEAAuE;YACvE,aAAa,EAAE,IAAI;YACnB,uEAAuE;YACvE,sEAAsE;YACtE,wEAAwE;YACxE,uEAAuE;YACvE,sEAAsE;YACtE,sEAAsE;YACtE,wEAAwE;YACxE,qEAAqE;YACrE,mCAAmC;SACpC,CAAA;IACH,CAAC;IAED;;;;;;;;;;;;;;;;;;OAkBG;IACO,gBAAgB;QACxB,OAAO,kBAAkB,CAAA;IAC3B,CAAC;IAES,QAAQ;QAChB,MAAM,OAAO,GAAG,IAAI,CAAC,QAAQ,EAAE,WAAW,IAAI,iBAAiB,CAAA;QAC/D,OAAO,yCAAyC,OAAO,EAAE,CAAA;IAC3D,CAAC;CACF"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import type { SourceFile } from 'ts-morph';
|
|
2
|
+
import type { ArchProject } from '../core/project.js';
|
|
3
|
+
import type { ExpressionMatcher } from '../helpers/matchers.js';
|
|
4
|
+
/**
|
|
5
|
+
* Which files the sibling detector looks at, and how they are grouped.
|
|
6
|
+
*
|
|
7
|
+
* Separated from the builder because selecting and grouping files is not
|
|
8
|
+
* detecting inconsistency — the builder collects a fluent configuration and
|
|
9
|
+
* hands it here. The scope arrives as a value rather than being read off a
|
|
10
|
+
* builder, so the grouping is callable and testable without one.
|
|
11
|
+
*/
|
|
12
|
+
export interface SiblingScope {
|
|
13
|
+
readonly project: ArchProject;
|
|
14
|
+
readonly folders: readonly string[];
|
|
15
|
+
readonly ignorePaths: readonly string[];
|
|
16
|
+
readonly ignoreTests: boolean;
|
|
17
|
+
readonly minLines: number;
|
|
18
|
+
readonly pattern: ExpressionMatcher | undefined;
|
|
19
|
+
}
|
|
20
|
+
/** Partition files into matching and non-matching based on the pattern. */
|
|
21
|
+
export declare function partitionByPattern(files: SourceFile[], pattern: ExpressionMatcher, minLines: number): {
|
|
22
|
+
matching: SourceFile[];
|
|
23
|
+
nonMatching: SourceFile[];
|
|
24
|
+
};
|
|
25
|
+
/** Group source files by parent folder, applying all filters. */
|
|
26
|
+
export declare function groupFilesByFolder(scope: SiblingScope): Map<string, SourceFile[]>;
|
|
27
|
+
/** Single source of the inert message. Pure — takes the assessment, returns text. */
|
|
28
|
+
export declare function inertMessage(a: {
|
|
29
|
+
matching: number;
|
|
30
|
+
total: number;
|
|
31
|
+
}, patternDesc: string): string;
|
|
32
|
+
/**
|
|
33
|
+
* Dedupe key for the inert finding. `dedupeConfigFindings` keys on
|
|
34
|
+
* `${file} ${ruleId ?? rule} ${element}`; `file` is always `''` here and
|
|
35
|
+
* `rule`/`ruleId` fall back to `describe()`, which is scope-blind (reads
|
|
36
|
+
* `_pattern` only). So without a scope-aware `element`, two same-pattern/
|
|
37
|
+
* different-scope inert detectors with no explicit `.rule({id})` would
|
|
38
|
+
* collapse into one finding under `checkAll`.
|
|
39
|
+
*
|
|
40
|
+
* Folds in every field `groupFilesByFolder()`/`fileMatchesPattern()` read to
|
|
41
|
+
* decide what's examined — `_folders`, `_ignorePaths`, `_ignoreTests`,
|
|
42
|
+
* `_minLines` — sorted so option order cannot split one semantic scope into
|
|
43
|
+
* two keys. Two rules with identical scope correctly collapse to one finding
|
|
44
|
+
* (one edit fixes both); two rules differing in ANY of these stay distinct.
|
|
45
|
+
*/
|
|
46
|
+
export declare function inertElement(scope: SiblingScope): string;
|
|
47
|
+
//# sourceMappingURL=sibling-files.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sibling-files.d.ts","sourceRoot":"","sources":["../../src/smells/sibling-files.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,UAAU,CAAA;AAE1C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAA;AACrD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAA;AAO/D;;;;;;;GAOG;AACH,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,OAAO,EAAE,WAAW,CAAA;IAC7B,QAAQ,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAA;IACnC,QAAQ,CAAC,WAAW,EAAE,SAAS,MAAM,EAAE,CAAA;IACvC,QAAQ,CAAC,WAAW,EAAE,OAAO,CAAA;IAC7B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;IACzB,QAAQ,CAAC,OAAO,EAAE,iBAAiB,GAAG,SAAS,CAAA;CAChD;AAsBD,2EAA2E;AAC3E,wBAAgB,kBAAkB,CAChC,KAAK,EAAE,UAAU,EAAE,EACnB,OAAO,EAAE,iBAAiB,EAC1B,QAAQ,EAAE,MAAM,GACf;IAAE,QAAQ,EAAE,UAAU,EAAE,CAAC;IAAC,WAAW,EAAE,UAAU,EAAE,CAAA;CAAE,CAWvD;AAED,iEAAiE;AACjE,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,YAAY,GAAG,GAAG,CAAC,MAAM,EAAE,UAAU,EAAE,CAAC,CAoCjF;AAED,qFAAqF;AACrF,wBAAgB,YAAY,CAAC,CAAC,EAAE;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,EAAE,WAAW,EAAE,MAAM,GAAG,MAAM,CAWhG;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,YAAY,GAAG,MAAM,CAKxD"}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import picomatch from 'picomatch';
|
|
3
|
+
import { collectFunctions } from '../models/arch-function.js';
|
|
4
|
+
import { searchFunctionBody } from '../helpers/body-traversal.js';
|
|
5
|
+
/** Paths treated as tests when `.ignoreTests()` is on. */
|
|
6
|
+
const TEST_PATTERNS = ['**/*.test.ts', '**/*.spec.ts', '**/__tests__/**'];
|
|
7
|
+
/** Check if a source file contains any function matching the pattern. */
|
|
8
|
+
function fileMatchesPattern(sf, pattern, minLines) {
|
|
9
|
+
// Detectors scan for a property of the code, not a user-declared subject
|
|
10
|
+
// set, so they always include object-literal functions. `functions()`
|
|
11
|
+
// keeps that opt-in because widening a selector silently changes every
|
|
12
|
+
// existing rule; a detector has no such contract to break, and a
|
|
13
|
+
// duplicated arrow under an object key — a resolver, a route handler, a
|
|
14
|
+
// reducer case — is exactly the copy-paste rot this exists to find.
|
|
15
|
+
for (const fn of collectFunctions(sf, { includeObjectLiteralFunctions: true })) {
|
|
16
|
+
const body = fn.getBody();
|
|
17
|
+
if (!body)
|
|
18
|
+
continue;
|
|
19
|
+
const lineCount = body.getText().split('\n').length;
|
|
20
|
+
if (lineCount < minLines)
|
|
21
|
+
continue;
|
|
22
|
+
if (searchFunctionBody(fn, pattern).found)
|
|
23
|
+
return true;
|
|
24
|
+
}
|
|
25
|
+
return false;
|
|
26
|
+
}
|
|
27
|
+
/** Partition files into matching and non-matching based on the pattern. */
|
|
28
|
+
export function partitionByPattern(files, pattern, minLines) {
|
|
29
|
+
const matching = [];
|
|
30
|
+
const nonMatching = [];
|
|
31
|
+
for (const sf of files) {
|
|
32
|
+
if (fileMatchesPattern(sf, pattern, minLines)) {
|
|
33
|
+
matching.push(sf);
|
|
34
|
+
}
|
|
35
|
+
else {
|
|
36
|
+
nonMatching.push(sf);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
return { matching, nonMatching };
|
|
40
|
+
}
|
|
41
|
+
/** Group source files by parent folder, applying all filters. */
|
|
42
|
+
export function groupFilesByFolder(scope) {
|
|
43
|
+
const sourceFiles = scope.project.getSourceFiles();
|
|
44
|
+
const folderMatchers = scope.folders.map((g) => picomatch(g));
|
|
45
|
+
const ignoreMatchers = scope.ignorePaths.map((g) => picomatch(g));
|
|
46
|
+
const testMatchers = scope.ignoreTests ? TEST_PATTERNS.map((g) => picomatch(g)) : [];
|
|
47
|
+
const groups = new Map();
|
|
48
|
+
for (const sf of sourceFiles) {
|
|
49
|
+
const filePath = sf.getFilePath();
|
|
50
|
+
// Folder filter: if folders specified, file must match at least one
|
|
51
|
+
if (folderMatchers.length > 0 && !folderMatchers.some((m) => m(filePath))) {
|
|
52
|
+
continue;
|
|
53
|
+
}
|
|
54
|
+
// Ignore paths filter
|
|
55
|
+
if (ignoreMatchers.some((m) => m(filePath))) {
|
|
56
|
+
continue;
|
|
57
|
+
}
|
|
58
|
+
// Test file filter
|
|
59
|
+
if (testMatchers.some((m) => m(filePath))) {
|
|
60
|
+
continue;
|
|
61
|
+
}
|
|
62
|
+
const folder = path.dirname(filePath);
|
|
63
|
+
const existing = groups.get(folder);
|
|
64
|
+
if (existing) {
|
|
65
|
+
existing.push(sf);
|
|
66
|
+
}
|
|
67
|
+
else {
|
|
68
|
+
groups.set(folder, [sf]);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
return groups;
|
|
72
|
+
}
|
|
73
|
+
/** Single source of the inert message. Pure — takes the assessment, returns text. */
|
|
74
|
+
export function inertMessage(a, patternDesc) {
|
|
75
|
+
return (`This detector examined ${String(a.total)} sibling files, but only ${String(a.matching)} of them ` +
|
|
76
|
+
`hold the pattern '${patternDesc}', and no folder is within an edit of a majority — so as written it ` +
|
|
77
|
+
`cannot produce a finding today. It reports a file that diverges from what its siblings do; with no ` +
|
|
78
|
+
`majority reachable by adopting files, there is no divergence to report. ` +
|
|
79
|
+
`If this rule asserts a convention the codebase is still adopting, replace it with ` +
|
|
80
|
+
`crossProject().side(...).beComplete(), which fails the day a file falls short — until adoption is ` +
|
|
81
|
+
`complete, so expect that red. If the intent is to police divergence rather than the convention itself, ` +
|
|
82
|
+
`widen the folder so a majority forms, or choose a pattern the sibling files already share.`);
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Dedupe key for the inert finding. `dedupeConfigFindings` keys on
|
|
86
|
+
* `${file} ${ruleId ?? rule} ${element}`; `file` is always `''` here and
|
|
87
|
+
* `rule`/`ruleId` fall back to `describe()`, which is scope-blind (reads
|
|
88
|
+
* `_pattern` only). So without a scope-aware `element`, two same-pattern/
|
|
89
|
+
* different-scope inert detectors with no explicit `.rule({id})` would
|
|
90
|
+
* collapse into one finding under `checkAll`.
|
|
91
|
+
*
|
|
92
|
+
* Folds in every field `groupFilesByFolder()`/`fileMatchesPattern()` read to
|
|
93
|
+
* decide what's examined — `_folders`, `_ignorePaths`, `_ignoreTests`,
|
|
94
|
+
* `_minLines` — sorted so option order cannot split one semantic scope into
|
|
95
|
+
* two keys. Two rules with identical scope correctly collapse to one finding
|
|
96
|
+
* (one edit fixes both); two rules differing in ANY of these stay distinct.
|
|
97
|
+
*/
|
|
98
|
+
export function inertElement(scope) {
|
|
99
|
+
const patternDesc = scope.pattern?.description ?? 'unknown pattern';
|
|
100
|
+
const folders = [...scope.folders].sort().join('|');
|
|
101
|
+
const ignorePaths = [...scope.ignorePaths].sort().join('|');
|
|
102
|
+
return `inert:${patternDesc}:${folders}:${ignorePaths}:${String(scope.ignoreTests)}:${String(scope.minLines)}`;
|
|
103
|
+
}
|
|
104
|
+
//# sourceMappingURL=sibling-files.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sibling-files.js","sourceRoot":"","sources":["../../src/smells/sibling-files.ts"],"names":[],"mappings":"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"}
|