@nielspeter/eess-ts 0.2.1 → 0.5.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 +61 -35
- 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 +75 -32
- 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 +141 -55
- 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 +385 -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 +182 -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 +177 -39
- 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 +16 -19
- 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 +243 -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 +109 -42
- 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 +8 -3
- package/dist/cli/commands/baseline.d.ts.map +1 -1
- package/dist/cli/commands/baseline.js +111 -7
- 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 +385 -47
- 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 +47 -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 +101 -43
- 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 +25 -32
- package/dist/cli/load-rules.js.map +1 -1
- package/dist/cli/resolve-config.d.ts +0 -8
- package/dist/cli/resolve-config.d.ts.map +1 -1
- package/dist/cli/resolve-config.js +25 -55
- 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 +351 -0
- package/dist/cli/rule-file-findings.js.map +1 -0
- package/dist/cli/watch.d.ts +17 -34
- package/dist/cli/watch.d.ts.map +1 -1
- package/dist/cli/watch.js +14 -129
- 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 +41 -19
- 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 +41 -37
- 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 +20 -6
- 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 +155 -93
- 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 +150 -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 +49 -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 +117 -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 +390 -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 +19 -0
- package/dist/core/exclusion-comments.d.ts.map +1 -0
- package/dist/core/exclusion-comments.js +7 -0
- package/dist/core/exclusion-comments.js.map +1 -0
- package/dist/core/execute-rule.d.ts +158 -0
- package/dist/core/execute-rule.d.ts.map +1 -0
- package/dist/core/execute-rule.js +666 -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/invalidate-on-modify.d.ts +20 -0
- package/dist/core/invalidate-on-modify.d.ts.map +1 -0
- package/dist/core/invalidate-on-modify.js +26 -0
- package/dist/core/invalidate-on-modify.js.map +1 -0
- 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 +227 -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 +593 -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 +224 -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 +22 -0
- package/dist/core/path-universe.d.ts.map +1 -0
- package/dist/core/path-universe.js +45 -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 +29 -4
- package/dist/core/project.js.map +1 -1
- package/dist/core/rule-builder.d.ts +303 -0
- package/dist/core/rule-builder.d.ts.map +1 -0
- package/dist/core/rule-builder.js +463 -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 +88 -0
- package/dist/core/rule-declaration.js.map +1 -0
- package/dist/core/terminal-builder.d.ts +629 -0
- package/dist/core/terminal-builder.d.ts.map +1 -0
- package/dist/core/terminal-builder.js +807 -0
- package/dist/core/terminal-builder.js.map +1 -0
- package/dist/core/terminal-execution.d.ts +39 -0
- package/dist/core/terminal-execution.d.ts.map +1 -0
- package/dist/core/terminal-execution.js +103 -0
- package/dist/core/terminal-execution.js.map +1 -0
- package/dist/core/ts-comment-mask.d.ts +2 -0
- package/dist/core/ts-comment-mask.d.ts.map +1 -0
- package/dist/core/ts-comment-mask.js +102 -0
- package/dist/core/ts-comment-mask.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/graphql-rule-builder.d.ts +95 -0
- package/dist/graphql/graphql-rule-builder.d.ts.map +1 -0
- package/dist/graphql/graphql-rule-builder.js +127 -0
- package/dist/graphql/graphql-rule-builder.js.map +1 -0
- package/dist/graphql/index.d.ts +1 -0
- package/dist/graphql/index.d.ts.map +1 -1
- package/dist/graphql/index.js +2 -1
- package/dist/graphql/index.js.map +1 -1
- package/dist/graphql/resolver-rule-builder.d.ts +44 -23
- package/dist/graphql/resolver-rule-builder.d.ts.map +1 -1
- package/dist/graphql/resolver-rule-builder.js +80 -72
- 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 +82 -12
- package/dist/graphql/schema-loader.js.map +1 -1
- package/dist/graphql/schema-predicates.d.ts.map +1 -1
- package/dist/graphql/schema-predicates.js +17 -12
- package/dist/graphql/schema-predicates.js.map +1 -1
- package/dist/graphql/schema-rule-builder.d.ts +15 -24
- package/dist/graphql/schema-rule-builder.d.ts.map +1 -1
- package/dist/graphql/schema-rule-builder.js +39 -77
- 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 +28 -0
- package/dist/helpers/baseline-generator.d.ts.map +1 -0
- package/dist/helpers/baseline-generator.js +27 -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 +135 -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 +53 -84
- 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 +16 -0
- package/dist/helpers/diff-aware.d.ts.map +1 -0
- package/dist/helpers/diff-aware.js +16 -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 +164 -86
- 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 +55 -19
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +67 -15
- 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 +54 -8
- package/dist/models/arch-function.d.ts.map +1 -1
- package/dist/models/arch-function.js +145 -8
- package/dist/models/arch-function.js.map +1 -1
- package/dist/models/arch-jsx-element.d.ts.map +1 -1
- package/dist/models/arch-jsx-element.js +18 -19
- 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/jsx.d.ts.map +1 -1
- package/dist/predicates/jsx.js +2 -1
- package/dist/predicates/jsx.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 +49 -16
- package/dist/presets/agent-guardrails.d.ts.map +1 -1
- package/dist/presets/agent-guardrails.js +308 -19
- package/dist/presets/agent-guardrails.js.map +1 -1
- package/dist/presets/boundaries.d.ts +46 -3
- 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 +23 -3
- 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 +10 -5
- package/dist/presets/index.d.ts.map +1 -1
- package/dist/presets/index.js +19 -3
- package/dist/presets/index.js.map +1 -1
- package/dist/presets/layered.d.ts +47 -3
- 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 +81 -10
- 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 +267 -2
- package/dist/presets/shared.d.ts.map +1 -1
- package/dist/presets/shared.js +367 -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 +63 -39
- 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 +80 -47
- package/dist/rules/metrics.js.map +1 -1
- package/dist/smells/clusters.d.ts +78 -0
- package/dist/smells/clusters.d.ts.map +1 -0
- package/dist/smells/clusters.js +124 -0
- package/dist/smells/clusters.js.map +1 -0
- package/dist/smells/duplicate-bodies.d.ts +92 -5
- package/dist/smells/duplicate-bodies.d.ts.map +1 -1
- package/dist/smells/duplicate-bodies.js +203 -73
- package/dist/smells/duplicate-bodies.js.map +1 -1
- package/dist/smells/duplicate-report.d.ts +90 -0
- package/dist/smells/duplicate-report.d.ts.map +1 -0
- package/dist/smells/duplicate-report.js +344 -0
- package/dist/smells/duplicate-report.js.map +1 -0
- package/dist/smells/fingerprint.d.ts +20 -1
- package/dist/smells/fingerprint.d.ts.map +1 -1
- package/dist/smells/fingerprint.js +27 -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 +28 -0
- package/dist/smells/similar-pairs.d.ts.map +1 -0
- package/dist/smells/similar-pairs.js +134 -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 +112 -14
- package/dist/smells/smell-builder.js.map +1 -1
- package/dist/smells/variation.d.ts +47 -0
- package/dist/smells/variation.d.ts.map +1 -0
- package/dist/smells/variation.js +90 -0
- package/dist/smells/variation.js.map +1 -0
- 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 +65 -16
- package/dist/tsconfig/tsconfig-builder.js.map +1 -1
- package/package.json +4 -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
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import { ArchConfigError } from '@nielspeter/eess';
|
|
2
|
+
/**
|
|
3
|
+
* Group reported pairs into connected components.
|
|
4
|
+
*
|
|
5
|
+
* A pair is the wrong unit of report and it is not a close call. Measured on a
|
|
6
|
+
* ~5,600-file production monorepo: 407 clusters produced **4,770** pair
|
|
7
|
+
* findings, the eight largest clusters alone produced 49% of them, one cluster
|
|
8
|
+
* of 89 members produced 398, and the worst single function was named 29 times.
|
|
9
|
+
*
|
|
10
|
+
* Every one of those findings can be true and the output is still unreadable,
|
|
11
|
+
* because N mutually-similar bodies carry ONE observation and emit N^2/2 lines
|
|
12
|
+
* of it. The observation is "these 89 share a shape", which is also the unit a
|
|
13
|
+
* reader can act on. That is what this returns.
|
|
14
|
+
*/
|
|
15
|
+
export function clusterPairs(pairs) {
|
|
16
|
+
const parent = new Map();
|
|
17
|
+
const find = (x) => {
|
|
18
|
+
let root = parent.get(x) ?? x;
|
|
19
|
+
if (root === x) {
|
|
20
|
+
parent.set(x, x);
|
|
21
|
+
return x;
|
|
22
|
+
}
|
|
23
|
+
root = find(root);
|
|
24
|
+
parent.set(x, root);
|
|
25
|
+
return root;
|
|
26
|
+
};
|
|
27
|
+
const union = (a, b) => {
|
|
28
|
+
const ra = find(a);
|
|
29
|
+
const rb = find(b);
|
|
30
|
+
if (ra !== rb)
|
|
31
|
+
parent.set(ra, rb);
|
|
32
|
+
};
|
|
33
|
+
for (const pair of pairs)
|
|
34
|
+
union(pair.a, pair.b);
|
|
35
|
+
// First-seen order throughout, so the report is stable across runs. Map
|
|
36
|
+
// iteration order is insertion order, and `pairs` arrives in the order the
|
|
37
|
+
// comparison found them, which is the source-file walk order.
|
|
38
|
+
const byRoot = new Map();
|
|
39
|
+
const seen = new Set();
|
|
40
|
+
for (const pair of pairs) {
|
|
41
|
+
const root = find(pair.a);
|
|
42
|
+
let entry = byRoot.get(root);
|
|
43
|
+
if (!entry) {
|
|
44
|
+
entry = { members: [], pairs: [] };
|
|
45
|
+
byRoot.set(root, entry);
|
|
46
|
+
}
|
|
47
|
+
entry.pairs.push(pair);
|
|
48
|
+
for (const fn of [pair.a, pair.b]) {
|
|
49
|
+
if (seen.has(fn))
|
|
50
|
+
continue;
|
|
51
|
+
seen.add(fn);
|
|
52
|
+
entry.members.push(fn);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
// Narrowed here, at the one place clusters are constructed, rather than at
|
|
56
|
+
// every place they are read. An entry exists only because a pair created it,
|
|
57
|
+
// and both of that pair's ends are pushed, so `members` always holds at least
|
|
58
|
+
// two — but the compiler cannot see that through `push`. A cluster that
|
|
59
|
+
// somehow held fewer is a broken invariant in THIS function, and dropping it
|
|
60
|
+
// silently at emission is what the type change exists to prevent, so it is
|
|
61
|
+
// reported as a configuration finding rather than filtered away.
|
|
62
|
+
return [...byRoot.values()].map((entry) => {
|
|
63
|
+
const [first, second, ...rest] = entry.members;
|
|
64
|
+
if (!first || !second) {
|
|
65
|
+
throw new ArchConfigError('duplicateBodies', 'a similarity cluster was built with fewer than two members, which cannot happen: ' +
|
|
66
|
+
'every cluster is created from a pair and both of its ends are recorded. ' +
|
|
67
|
+
'This is a defect in clusterPairs, not in the code being analysed.');
|
|
68
|
+
}
|
|
69
|
+
// Annotated, never cast (ADR-005): the annotation makes the compiler CHECK
|
|
70
|
+
// the literal is a valid `NonEmptyMembers`, where a cast would only assert
|
|
71
|
+
// it.
|
|
72
|
+
const members = [first, second, ...rest];
|
|
73
|
+
return {
|
|
74
|
+
members,
|
|
75
|
+
pairs: entry.pairs,
|
|
76
|
+
peakSimilarity: entry.pairs.reduce((best, p) => Math.max(best, p.similarity), 0),
|
|
77
|
+
};
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* How likely a cluster is to be worth acting on, highest first.
|
|
82
|
+
*
|
|
83
|
+
* Not a filter and deliberately not a score change — the similarity number is
|
|
84
|
+
* left exactly as it was. This decides only what a reader sees first, because at
|
|
85
|
+
* 4,770 findings the order IS the product.
|
|
86
|
+
*
|
|
87
|
+
* The signal is the one the fingerprint throws away: a body's identifiers are
|
|
88
|
+
* ignored by design (that is what makes the score a type-2 clone score), and so
|
|
89
|
+
* is the DECLARATION's own name, which is where the evidence was. Measured over
|
|
90
|
+
* that same corpus, bucketed by how the two names relate:
|
|
91
|
+
*
|
|
92
|
+
* | bucket | share | reading |
|
|
93
|
+
* | ----------------------------- | ----- | -------------------------- |
|
|
94
|
+
* | different file, different name | 56% | convergent idiom |
|
|
95
|
+
* | same class | 20% | siblings, often extractable |
|
|
96
|
+
* | different file, SAME name | 14% | a COPY of one function |
|
|
97
|
+
* | same file, different class | 10% | co-located |
|
|
98
|
+
*
|
|
99
|
+
* Two pairs from that run, both scored 100%: `timestampPrefix` in two files (a
|
|
100
|
+
* literal copy) and two unrelated SDK wrapper methods (a shared idiom). Same
|
|
101
|
+
* number on screen, opposite verdicts, and the thing that separates them costs
|
|
102
|
+
* nothing to compute.
|
|
103
|
+
*
|
|
104
|
+
* **It is a ranking and not a filter on purpose.** Dropping the different-name
|
|
105
|
+
* bucket would discard real same-class duplication — one repository in that
|
|
106
|
+
* corpus has six `exists*` methods differing only in a table name. Dropping
|
|
107
|
+
* cross-file would have discarded the finding that led to bug 0227.
|
|
108
|
+
*/
|
|
109
|
+
export function clusterRank(cluster) {
|
|
110
|
+
const bare = (fn) => (fn.getName() ?? '<anonymous>').split('.').pop() ?? '';
|
|
111
|
+
const file = (fn) => fn.getSourceFile().getFilePath();
|
|
112
|
+
let best = 0;
|
|
113
|
+
for (const pair of cluster.pairs) {
|
|
114
|
+
const sameFile = file(pair.a) === file(pair.b);
|
|
115
|
+
const sameName = bare(pair.a) === bare(pair.b);
|
|
116
|
+
// A copy of one function into another file is the most actionable thing
|
|
117
|
+
// this detector can find, and the cheapest to verify.
|
|
118
|
+
const score = !sameFile && sameName ? 3 : sameFile ? 2 : 1;
|
|
119
|
+
if (score > best)
|
|
120
|
+
best = score;
|
|
121
|
+
}
|
|
122
|
+
return best;
|
|
123
|
+
}
|
|
124
|
+
//# sourceMappingURL=clusters.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"clusters.js","sourceRoot":"","sources":["../../src/smells/clusters.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,24 +1,111 @@
|
|
|
1
|
+
import type { RuleDescription } from '@nielspeter/eess';
|
|
1
2
|
import { SmellBuilder } from './smell-builder.js';
|
|
2
|
-
import type { ArchViolation } from '
|
|
3
|
+
import type { ArchViolation } from '@nielspeter/eess';
|
|
3
4
|
import type { ArchProject } from '../core/project.js';
|
|
4
5
|
export declare class DuplicateBodiesBuilder extends SmellBuilder {
|
|
5
6
|
private _minSimilarity;
|
|
7
|
+
private _minDistinctVocabulary;
|
|
6
8
|
constructor(project: ArchProject);
|
|
7
9
|
/** Set the AST similarity threshold. Default: 0.85. */
|
|
8
10
|
withMinSimilarity(threshold: number): this;
|
|
11
|
+
/**
|
|
12
|
+
* Minimum count of distinct identifier/literal text either body must carry
|
|
13
|
+
* before a pair is even compared — not raw line count, and not similarity.
|
|
14
|
+
* Two bodies can share a syntactic shape for no reason other than the shape
|
|
15
|
+
* being mandated (a wither, a getter, a boilerplate skeleton); below this
|
|
16
|
+
* threshold a "match" carries no information about what the code actually
|
|
17
|
+
* does. Default: 8 — see plan 0103's Phase 0 triage for how it was chosen.
|
|
18
|
+
* Tune down for a codebase with terser naming than this default assumes;
|
|
19
|
+
* tune up if short, low-vocabulary bodies keep surfacing as noise.
|
|
20
|
+
*/
|
|
21
|
+
minDistinctVocabulary(n: number): this;
|
|
22
|
+
/**
|
|
23
|
+
* Named by id or by what the detector checks — the inherited version returns
|
|
24
|
+
* the sentinel `'unnamed'`, and `inconsistent-siblings.ts` already carries this
|
|
25
|
+
* override for that reason.
|
|
26
|
+
*
|
|
27
|
+
* It became load-bearing in plan 0099: the floor makes an over-filtered
|
|
28
|
+
* detector FAIL, and `dedupeConfigFindings` keys on `(file, ruleId ?? rule,
|
|
29
|
+
* element)`. With `file: ''` and `'unnamed'` in both other slots, three
|
|
30
|
+
* genuinely different detectors — different similarity, different
|
|
31
|
+
* `ignorePaths` — collapsed into one finding claiming they were "one option"
|
|
32
|
+
* and "one edit". Two were silently discarded and the survivor said
|
|
33
|
+
* `Rule: unnamed`.
|
|
34
|
+
*/
|
|
35
|
+
describeRule(): RuleDescription;
|
|
9
36
|
protected detect(): ArchViolation[];
|
|
10
37
|
protected describe(): string;
|
|
11
38
|
/** Check if a file path passes all glob-based filters. */
|
|
39
|
+
/**
|
|
40
|
+
* The three matcher sets travel together as one `filters` — they are one
|
|
41
|
+
* configuration, and four positional `picomatch.Matcher[]` arguments in a row
|
|
42
|
+
* are exactly the transposition risk the parameter cap exists to prevent.
|
|
43
|
+
*/
|
|
12
44
|
private passesFileFilters;
|
|
13
45
|
/** Check if a function body meets the minimum line count. */
|
|
14
46
|
private meetsMinLines;
|
|
15
47
|
/** Collect all functions matching folder/path/test filters. */
|
|
48
|
+
/**
|
|
49
|
+
* Bodies entering pairwise comparison — plan 0096, and the ONE method both
|
|
50
|
+
* readers call.
|
|
51
|
+
*
|
|
52
|
+
* The seam is AFTER `minLines` and the path filters: a project can load 300
|
|
53
|
+
* files while every body sits under the threshold, and the count that notices
|
|
54
|
+
* has to be taken where the comparison receives its input, not where the files
|
|
55
|
+
* were read.
|
|
56
|
+
*
|
|
57
|
+
* No fingerprinting to count. `meetsMinLines` already returns false for a
|
|
58
|
+
* bodyless function, so `fingerprintAll(xs).length === xs.length` **always** —
|
|
59
|
+
* fingerprinting to answer "is it zero" is a full AST pass of dead work on
|
|
60
|
+
* what becomes the hot path of every consumer's `diagnose()` call.
|
|
61
|
+
*/
|
|
62
|
+
private selected;
|
|
63
|
+
/** Units this detector examined — plan 0096. */
|
|
64
|
+
examinedUnits(): number;
|
|
16
65
|
private collectFilteredFunctions;
|
|
17
|
-
/** Build fingerprints for all collected functions. */
|
|
18
|
-
private fingerprintAll;
|
|
19
|
-
/** Compare all pairs of fingerprints, collect those above threshold. */
|
|
20
|
-
private findSimilarPairs;
|
|
21
66
|
/** Build violations from similar pairs. */
|
|
67
|
+
/**
|
|
68
|
+
* Report order for pairs — which, measured, never has anything to order.
|
|
69
|
+
*
|
|
70
|
+
* Extracted from {@link buildViolations} so that method is the violation shape
|
|
71
|
+
* and nothing else. Enforcement review of bug 0242 read this as a live defect
|
|
72
|
+
* (keyed on `a`, i.e. walk order, while the finding reports at the anchor).
|
|
73
|
+
* Probing it says otherwise: {@link buildViolations} is reached only from
|
|
74
|
+
* {@link buildClusterViolations} for a cluster of two or fewer members, and
|
|
75
|
+
* such a cluster carries exactly one pair. Instrumented to throw on more than
|
|
76
|
+
* one, the whole `packages/ts` suite (3604 tests) produced **zero** hits.
|
|
77
|
+
*
|
|
78
|
+
* So this cannot reorder anything today, and {@link orderedClusters} is what
|
|
79
|
+
* actually orders the output. The key is corrected anyway — being right if
|
|
80
|
+
* this ever becomes reachable costs one expression — but the correction fixes
|
|
81
|
+
* no observable behaviour, and saying otherwise would be the kind of unmeasured
|
|
82
|
+
* claim this repo keeps catching.
|
|
83
|
+
*/
|
|
84
|
+
private orderedPairs;
|
|
85
|
+
/**
|
|
86
|
+
* One violation per CLUSTER of mutually-similar bodies, not per pair.
|
|
87
|
+
*
|
|
88
|
+
* A pair is the wrong unit and it is not a close call. Measured on a
|
|
89
|
+
* ~5,600-file production monorepo: 407 clusters emitted **4,770** pair
|
|
90
|
+
* findings — more findings than the 3,810 bodies that produced them. The
|
|
91
|
+
* eight largest clusters were 49% of the output, one cluster of 89 members
|
|
92
|
+
* emitted 398 lines, and the worst single function was named 29 times. Every
|
|
93
|
+
* one of those can be TRUE and the report is still unreadable, because N
|
|
94
|
+
* mutually-similar bodies carry one observation and emit N^2/2 lines of it.
|
|
95
|
+
*
|
|
96
|
+
* Two-member clusters — the common case — keep the exact message and identity
|
|
97
|
+
* they had before, so existing baselines for them are untouched. Only a group
|
|
98
|
+
* of three or more collapses, which is where the inflation lives.
|
|
99
|
+
*/
|
|
100
|
+
/**
|
|
101
|
+
* Report order.
|
|
102
|
+
*
|
|
103
|
+
* Rank is presentation only — no pair is dropped and no score changes. At four
|
|
104
|
+
* thousand findings the ORDER is the product: what a reader sees first decides
|
|
105
|
+
* whether they read at all. `.groupByFolder()` still wins when asked for.
|
|
106
|
+
*/
|
|
107
|
+
private orderedClusters;
|
|
108
|
+
private buildClusterViolations;
|
|
22
109
|
private buildViolations;
|
|
23
110
|
}
|
|
24
111
|
//# sourceMappingURL=duplicate-bodies.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"duplicate-bodies.d.ts","sourceRoot":"","sources":["../../src/smells/duplicate-bodies.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"duplicate-bodies.d.ts","sourceRoot":"","sources":["../../src/smells/duplicate-bodies.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAA;AAIvD,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAA;AAOjD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAA;AACrD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAA;AASrD,qBAAa,sBAAuB,SAAQ,YAAY;IACtD,OAAO,CAAC,cAAc,CAAO;IAC7B,OAAO,CAAC,sBAAsB,CAAI;gBAEtB,OAAO,EAAE,WAAW;IAIhC,uDAAuD;IACvD,iBAAiB,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI;IAM1C;;;;;;;;;OASG;IACH,qBAAqB,CAAC,CAAC,EAAE,MAAM,GAAG,IAAI;IAMtC;;;;;;;;;;;;OAYG;IACM,YAAY,IAAI,eAAe;IAOxC,SAAS,CAAC,MAAM,IAAI,aAAa,EAAE;IAOnC,SAAS,CAAC,QAAQ,IAAI,MAAM;IAqB5B,0DAA0D;IAC1D;;;;OAIG;IACH,OAAO,CAAC,iBAAiB;IAoBzB,6DAA6D;IAC7D,OAAO,CAAC,aAAa;IAOrB,+DAA+D;IAC/D;;;;;;;;;;;;;OAaG;IACH,OAAO,CAAC,QAAQ;IAIhB,gDAAgD;IAChD,aAAa,IAAI,MAAM;IAIvB,OAAO,CAAC,wBAAwB;IAmChC,2CAA2C;IAC3C;;;;;;;;;;;;;;;;OAgBG;IACH,OAAO,CAAC,YAAY;IAepB;;;;;;;;;;;;;;OAcG;IACH;;;;;;OAMG;IACH,OAAO,CAAC,eAAe;IA8BvB,OAAO,CAAC,sBAAsB;IAe9B,OAAO,CAAC,eAAe;CAIxB"}
|
|
@@ -1,37 +1,103 @@
|
|
|
1
1
|
import picomatch from 'picomatch';
|
|
2
2
|
import path from 'node:path';
|
|
3
|
+
import { selectionMemo } from '@nielspeter/eess/internal';
|
|
3
4
|
import { SmellBuilder } from './smell-builder.js';
|
|
4
5
|
import { collectFunctions } from '../models/arch-function.js';
|
|
5
|
-
import {
|
|
6
|
+
import { fingerprintAll, findSimilarPairs } from './similar-pairs.js';
|
|
7
|
+
import { anchorFile, clusterViolation, pairViolation } from './duplicate-report.js';
|
|
8
|
+
import { clusterPairs, clusterRank } from './clusters.js';
|
|
9
|
+
import { relativeToRoot } from '../core/project-relative.js';
|
|
6
10
|
/** Test file patterns for ignoreTests(). */
|
|
7
11
|
const TEST_PATTERNS = ['**/*.test.ts', '**/*.spec.ts', '**/__tests__/**'];
|
|
12
|
+
const selectionOf = selectionMemo();
|
|
8
13
|
export class DuplicateBodiesBuilder extends SmellBuilder {
|
|
9
14
|
_minSimilarity = 0.85;
|
|
15
|
+
_minDistinctVocabulary = 8;
|
|
10
16
|
constructor(project) {
|
|
11
17
|
super(project);
|
|
12
18
|
}
|
|
13
19
|
/** Set the AST similarity threshold. Default: 0.85. */
|
|
14
20
|
withMinSimilarity(threshold) {
|
|
15
|
-
|
|
16
|
-
|
|
21
|
+
const next = this.copy();
|
|
22
|
+
next._minSimilarity = threshold;
|
|
23
|
+
return next;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Minimum count of distinct identifier/literal text either body must carry
|
|
27
|
+
* before a pair is even compared — not raw line count, and not similarity.
|
|
28
|
+
* Two bodies can share a syntactic shape for no reason other than the shape
|
|
29
|
+
* being mandated (a wither, a getter, a boilerplate skeleton); below this
|
|
30
|
+
* threshold a "match" carries no information about what the code actually
|
|
31
|
+
* does. Default: 8 — see plan 0103's Phase 0 triage for how it was chosen.
|
|
32
|
+
* Tune down for a codebase with terser naming than this default assumes;
|
|
33
|
+
* tune up if short, low-vocabulary bodies keep surfacing as noise.
|
|
34
|
+
*/
|
|
35
|
+
minDistinctVocabulary(n) {
|
|
36
|
+
const next = this.copy();
|
|
37
|
+
next._minDistinctVocabulary = n;
|
|
38
|
+
return next;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Named by id or by what the detector checks — the inherited version returns
|
|
42
|
+
* the sentinel `'unnamed'`, and `inconsistent-siblings.ts` already carries this
|
|
43
|
+
* override for that reason.
|
|
44
|
+
*
|
|
45
|
+
* It became load-bearing in plan 0099: the floor makes an over-filtered
|
|
46
|
+
* detector FAIL, and `dedupeConfigFindings` keys on `(file, ruleId ?? rule,
|
|
47
|
+
* element)`. With `file: ''` and `'unnamed'` in both other slots, three
|
|
48
|
+
* genuinely different detectors — different similarity, different
|
|
49
|
+
* `ignorePaths` — collapsed into one finding claiming they were "one option"
|
|
50
|
+
* and "one edit". Two were silently discarded and the survivor said
|
|
51
|
+
* `Rule: unnamed`.
|
|
52
|
+
*/
|
|
53
|
+
describeRule() {
|
|
54
|
+
return {
|
|
55
|
+
...super.describeRule(),
|
|
56
|
+
rule: this._metadata?.id ?? this.describe(),
|
|
57
|
+
};
|
|
17
58
|
}
|
|
18
59
|
detect() {
|
|
19
|
-
const functions = this.
|
|
20
|
-
const fingerprinted =
|
|
21
|
-
const pairs =
|
|
22
|
-
return this.
|
|
60
|
+
const functions = this.selected();
|
|
61
|
+
const fingerprinted = fingerprintAll(functions);
|
|
62
|
+
const pairs = findSimilarPairs(fingerprinted, this._minSimilarity, this._minDistinctVocabulary);
|
|
63
|
+
return this.buildClusterViolations(pairs);
|
|
23
64
|
}
|
|
24
65
|
describe() {
|
|
25
66
|
const scope = this._folders.length > 0 ? this._folders.join(', ') : 'all files';
|
|
26
|
-
|
|
67
|
+
// EVERY narrowing that distinguishes two instances, not just similarity.
|
|
68
|
+
//
|
|
69
|
+
// This is the `Rule:` line a reader sees, and since plan 0099 it is also the
|
|
70
|
+
// dedupe identity for a detector with no `.rule({ id })`. Rendering only
|
|
71
|
+
// folders and similarity meant three detectors differing in `minLines` shared
|
|
72
|
+
// one key: measured, 3 findings collapsed to 1 claiming they were "one edit",
|
|
73
|
+
// and fixing the surviving threshold left the other two dead and green.
|
|
74
|
+
const filters = [
|
|
75
|
+
`minLines >= ${String(this._minLines)}`,
|
|
76
|
+
`minDistinctVocabulary >= ${String(this._minDistinctVocabulary)}`,
|
|
77
|
+
];
|
|
78
|
+
if (this._ignorePaths.length > 0)
|
|
79
|
+
filters.push(`ignoring ${this._ignorePaths.join(', ')}`);
|
|
80
|
+
if (this._ignoreTests)
|
|
81
|
+
filters.push('ignoring tests');
|
|
82
|
+
return (`No duplicate function bodies in ${scope} ` +
|
|
83
|
+
`(similarity >= ${String(this._minSimilarity)}, ${filters.join(', ')})`);
|
|
27
84
|
}
|
|
28
85
|
/** Check if a file path passes all glob-based filters. */
|
|
29
|
-
|
|
30
|
-
|
|
86
|
+
/**
|
|
87
|
+
* The three matcher sets travel together as one `filters` — they are one
|
|
88
|
+
* configuration, and four positional `picomatch.Matcher[]` arguments in a row
|
|
89
|
+
* are exactly the transposition risk the parameter cap exists to prevent.
|
|
90
|
+
*/
|
|
91
|
+
passesFileFilters(filePath, filters, fromRoot) {
|
|
92
|
+
const { folderMatchers, ignoreMatchers, testMatchers } = filters;
|
|
93
|
+
// Both forms, for every set — bug 0036. A project-relative `inFolder()` or
|
|
94
|
+
// `ignorePaths()` glob could never match an absolute path.
|
|
95
|
+
const hits = (ms) => ms.some((m) => m(filePath)) || (fromRoot !== undefined && ms.some((m) => m(fromRoot)));
|
|
96
|
+
if (folderMatchers.length > 0 && !hits(folderMatchers))
|
|
31
97
|
return false;
|
|
32
|
-
if (ignoreMatchers
|
|
98
|
+
if (hits(ignoreMatchers))
|
|
33
99
|
return false;
|
|
34
|
-
if (testMatchers
|
|
100
|
+
if (hits(testMatchers))
|
|
35
101
|
return false;
|
|
36
102
|
return true;
|
|
37
103
|
}
|
|
@@ -44,6 +110,27 @@ export class DuplicateBodiesBuilder extends SmellBuilder {
|
|
|
44
110
|
return lineCount >= this._minLines;
|
|
45
111
|
}
|
|
46
112
|
/** Collect all functions matching folder/path/test filters. */
|
|
113
|
+
/**
|
|
114
|
+
* Bodies entering pairwise comparison — plan 0096, and the ONE method both
|
|
115
|
+
* readers call.
|
|
116
|
+
*
|
|
117
|
+
* The seam is AFTER `minLines` and the path filters: a project can load 300
|
|
118
|
+
* files while every body sits under the threshold, and the count that notices
|
|
119
|
+
* has to be taken where the comparison receives its input, not where the files
|
|
120
|
+
* were read.
|
|
121
|
+
*
|
|
122
|
+
* No fingerprinting to count. `meetsMinLines` already returns false for a
|
|
123
|
+
* bodyless function, so `fingerprintAll(xs).length === xs.length` **always** —
|
|
124
|
+
* fingerprinting to answer "is it zero" is a full AST pass of dead work on
|
|
125
|
+
* what becomes the hot path of every consumer's `diagnose()` call.
|
|
126
|
+
*/
|
|
127
|
+
selected() {
|
|
128
|
+
return selectionOf(this, () => this.collectFilteredFunctions());
|
|
129
|
+
}
|
|
130
|
+
/** Units this detector examined — plan 0096. */
|
|
131
|
+
examinedUnits() {
|
|
132
|
+
return this.selected().length;
|
|
133
|
+
}
|
|
47
134
|
collectFilteredFunctions() {
|
|
48
135
|
const sourceFiles = this.project.getSourceFiles();
|
|
49
136
|
const folderMatchers = this._folders.map((g) => picomatch(g));
|
|
@@ -51,10 +138,16 @@ export class DuplicateBodiesBuilder extends SmellBuilder {
|
|
|
51
138
|
const testMatchers = this._ignoreTests ? TEST_PATTERNS.map((g) => picomatch(g)) : [];
|
|
52
139
|
const allFunctions = [];
|
|
53
140
|
for (const sf of sourceFiles) {
|
|
54
|
-
if (!this.passesFileFilters(sf.getFilePath(), folderMatchers, ignoreMatchers, testMatchers)) {
|
|
141
|
+
if (!this.passesFileFilters(sf.getFilePath(), { folderMatchers, ignoreMatchers, testMatchers }, relativeToRoot(sf, sf.getFilePath(), this.project.tsConfigPath))) {
|
|
55
142
|
continue;
|
|
56
143
|
}
|
|
57
|
-
for
|
|
144
|
+
// Detectors scan for a property of the code, not a user-declared subject
|
|
145
|
+
// set, so they always include object-literal functions. `functions()`
|
|
146
|
+
// keeps that opt-in because widening a selector silently changes every
|
|
147
|
+
// existing rule; a detector has no such contract to break, and a
|
|
148
|
+
// duplicated arrow under an object key — a resolver, a route handler, a
|
|
149
|
+
// reducer case — is exactly the copy-paste rot this exists to find.
|
|
150
|
+
for (const fn of collectFunctions(sf, { includeObjectLiteralFunctions: true })) {
|
|
58
151
|
if (this.meetsMinLines(fn)) {
|
|
59
152
|
allFunctions.push(fn);
|
|
60
153
|
}
|
|
@@ -62,70 +155,107 @@ export class DuplicateBodiesBuilder extends SmellBuilder {
|
|
|
62
155
|
}
|
|
63
156
|
return allFunctions;
|
|
64
157
|
}
|
|
65
|
-
/** Build
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
158
|
+
/** Build violations from similar pairs. */
|
|
159
|
+
/**
|
|
160
|
+
* Report order for pairs — which, measured, never has anything to order.
|
|
161
|
+
*
|
|
162
|
+
* Extracted from {@link buildViolations} so that method is the violation shape
|
|
163
|
+
* and nothing else. Enforcement review of bug 0242 read this as a live defect
|
|
164
|
+
* (keyed on `a`, i.e. walk order, while the finding reports at the anchor).
|
|
165
|
+
* Probing it says otherwise: {@link buildViolations} is reached only from
|
|
166
|
+
* {@link buildClusterViolations} for a cluster of two or fewer members, and
|
|
167
|
+
* such a cluster carries exactly one pair. Instrumented to throw on more than
|
|
168
|
+
* one, the whole `packages/ts` suite (3604 tests) produced **zero** hits.
|
|
169
|
+
*
|
|
170
|
+
* So this cannot reorder anything today, and {@link orderedClusters} is what
|
|
171
|
+
* actually orders the output. The key is corrected anyway — being right if
|
|
172
|
+
* this ever becomes reachable costs one expression — but the correction fixes
|
|
173
|
+
* no observable behaviour, and saying otherwise would be the kind of unmeasured
|
|
174
|
+
* claim this repo keeps catching.
|
|
175
|
+
*/
|
|
176
|
+
orderedPairs(pairs) {
|
|
177
|
+
if (!this._groupByFolder)
|
|
178
|
+
return pairs;
|
|
179
|
+
// Keyed on the ANCHOR's folder, not on `a`'s. Which endpoint is `a` is the
|
|
180
|
+
// source walk; the anchor is where the finding says it is (bug 0242). While
|
|
181
|
+
// those agreed this was invisible, and after the anchor moved a finding
|
|
182
|
+
// reported in one folder would sort into another folder's group — the one
|
|
183
|
+
// thing this option exists to prevent.
|
|
184
|
+
const keyed = pairs.map((pair) => ({
|
|
185
|
+
pair,
|
|
186
|
+
folder: path.dirname(anchorFile([pair.a, pair.b])),
|
|
187
|
+
}));
|
|
188
|
+
keyed.sort((x, y) => (x.folder < y.folder ? -1 : x.folder > y.folder ? 1 : 0));
|
|
189
|
+
return keyed.map((k) => k.pair);
|
|
75
190
|
}
|
|
76
|
-
/**
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
191
|
+
/**
|
|
192
|
+
* One violation per CLUSTER of mutually-similar bodies, not per pair.
|
|
193
|
+
*
|
|
194
|
+
* A pair is the wrong unit and it is not a close call. Measured on a
|
|
195
|
+
* ~5,600-file production monorepo: 407 clusters emitted **4,770** pair
|
|
196
|
+
* findings — more findings than the 3,810 bodies that produced them. The
|
|
197
|
+
* eight largest clusters were 49% of the output, one cluster of 89 members
|
|
198
|
+
* emitted 398 lines, and the worst single function was named 29 times. Every
|
|
199
|
+
* one of those can be TRUE and the report is still unreadable, because N
|
|
200
|
+
* mutually-similar bodies carry one observation and emit N^2/2 lines of it.
|
|
201
|
+
*
|
|
202
|
+
* Two-member clusters — the common case — keep the exact message and identity
|
|
203
|
+
* they had before, so existing baselines for them are untouched. Only a group
|
|
204
|
+
* of three or more collapses, which is where the inflation lives.
|
|
205
|
+
*/
|
|
206
|
+
/**
|
|
207
|
+
* Report order.
|
|
208
|
+
*
|
|
209
|
+
* Rank is presentation only — no pair is dropped and no score changes. At four
|
|
210
|
+
* thousand findings the ORDER is the product: what a reader sees first decides
|
|
211
|
+
* whether they read at all. `.groupByFolder()` still wins when asked for.
|
|
212
|
+
*/
|
|
213
|
+
orderedClusters(clusters) {
|
|
214
|
+
// Decorated once, then sorted. `anchorFile` asks ts-morph for a path and a
|
|
215
|
+
// line per member, and a comparator runs O(n log n) times — on the 407
|
|
216
|
+
// clusters this module's own comments measure, computing it inside the
|
|
217
|
+
// comparator is tens of thousands of avoidable calls.
|
|
218
|
+
const keyed = clusters.map((cluster) => {
|
|
219
|
+
// The ANCHOR's folder, not `members[0]`'s: `members[0]` is the source walk
|
|
220
|
+
// and the finding is reported at the path-then-line minimum (bug 0242), so
|
|
221
|
+
// grouping on the walk would file a finding under a folder it is not
|
|
222
|
+
// reported in — the one thing `.groupByFolder()` exists to prevent.
|
|
223
|
+
const anchor = anchorFile(cluster.members);
|
|
224
|
+
return { cluster, anchor, folder: path.dirname(anchor), rank: clusterRank(cluster) };
|
|
225
|
+
});
|
|
226
|
+
// Plain `<`, not `localeCompare`: the latter's result depends on the
|
|
227
|
+
// runtime's ICU build and default locale, which is this record's own failure
|
|
228
|
+
// class — a report that reads differently on two machines — in the
|
|
229
|
+
// expression fixing it. `comparePositions` next door uses `<` too.
|
|
230
|
+
const text = (x, y) => (x < y ? -1 : x > y ? 1 : 0);
|
|
231
|
+
keyed.sort((x, y) => {
|
|
232
|
+
const primary = this._groupByFolder ? text(x.folder, y.folder) : y.rank - x.rank;
|
|
233
|
+
// The tie-break is what makes the SEQUENCE deterministic, and without it
|
|
234
|
+
// the default path had none: `clusterRank` returns one of four values over
|
|
235
|
+
// hundreds of clusters, and `Array#sort` is stable, so equal ranks kept
|
|
236
|
+
// walk order. Found by architecture review of bug 0242's fix; the branch
|
|
237
|
+
// had disclosed it in a test comment and nowhere else.
|
|
238
|
+
return primary !== 0 ? primary : text(x.anchor, y.anchor);
|
|
239
|
+
});
|
|
240
|
+
return keyed.map((k) => k.cluster);
|
|
98
241
|
}
|
|
99
|
-
|
|
100
|
-
buildViolations(pairs) {
|
|
101
|
-
const ruleDescription = this.describe();
|
|
242
|
+
buildClusterViolations(pairs) {
|
|
102
243
|
const violations = [];
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
const nameA = pair.a.getName() ?? '<anonymous>';
|
|
113
|
-
const fileA = pair.a.getSourceFile().getFilePath();
|
|
114
|
-
const lineA = pair.a.getStartLineNumber();
|
|
115
|
-
const nameB = pair.b.getName() ?? '<anonymous>';
|
|
116
|
-
const fileB = pair.b.getSourceFile().getFilePath();
|
|
117
|
-
const lineB = pair.b.getStartLineNumber();
|
|
118
|
-
const pct = Math.round(pair.similarity * 100);
|
|
119
|
-
violations.push({
|
|
120
|
-
rule: ruleDescription,
|
|
121
|
-
element: nameA,
|
|
122
|
-
file: fileA,
|
|
123
|
-
line: lineA,
|
|
124
|
-
message: `${nameA} (${fileA}:${String(lineA)}) is ${String(pct)}% similar to ${nameB} (${fileB}:${String(lineB)})`,
|
|
125
|
-
because: this._reason,
|
|
126
|
-
});
|
|
244
|
+
for (const cluster of this.orderedClusters(clusterPairs(pairs))) {
|
|
245
|
+
// A two-member cluster IS the pair, so it keeps the message and the
|
|
246
|
+
// baseline identity that already shipped. Only three-or-more collapses,
|
|
247
|
+
// which is where the inflation lives.
|
|
248
|
+
if (cluster.members.length <= 2) {
|
|
249
|
+
violations.push(...this.buildViolations(cluster.pairs));
|
|
250
|
+
continue;
|
|
251
|
+
}
|
|
252
|
+
violations.push(clusterViolation(cluster, { rule: this.describe(), because: this._reason }));
|
|
127
253
|
}
|
|
128
254
|
return violations;
|
|
129
255
|
}
|
|
256
|
+
buildViolations(pairs) {
|
|
257
|
+
const context = { rule: this.describe(), because: this._reason };
|
|
258
|
+
return this.orderedPairs([...pairs]).map((pair) => pairViolation(pair, context));
|
|
259
|
+
}
|
|
130
260
|
}
|
|
131
261
|
//# sourceMappingURL=duplicate-bodies.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"duplicate-bodies.js","sourceRoot":"","sources":["../../src/smells/duplicate-bodies.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"duplicate-bodies.js","sourceRoot":"","sources":["../../src/smells/duplicate-bodies.ts"],"names":[],"mappings":"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"}
|