@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,90 @@
|
|
|
1
|
+
import type { ArchViolation } from '@nielspeter/eess';
|
|
2
|
+
import type { ArchFunction } from '../models/arch-function.js';
|
|
3
|
+
import type { SimilarPair } from './similar-pairs.js';
|
|
4
|
+
import type { SimilarCluster } from './clusters.js';
|
|
5
|
+
/**
|
|
6
|
+
* What differs between two reported bodies, in one clause.
|
|
7
|
+
*
|
|
8
|
+
* The percentage answers "how alike are these shapes" and cannot answer the
|
|
9
|
+
* question a reader actually has, which is "should I consolidate them". That
|
|
10
|
+
* turns on how many things vary: one call target is a parameter extraction,
|
|
11
|
+
* nine property names is a shared idiom, and those two score identically today.
|
|
12
|
+
*
|
|
13
|
+
* Measured on a ~5,600-file production monorepo — the bucket that is mostly
|
|
14
|
+
* convergent idiom carries a median of 6 varying axes against 4 for the rest.
|
|
15
|
+
* Real information, and NOT a classifier: it is reported, never filtered on.
|
|
16
|
+
*/
|
|
17
|
+
export declare function varianceSummary(pair: SimilarPair | undefined): string;
|
|
18
|
+
/**
|
|
19
|
+
* One body's place in the total order every part of a finding is derived from.
|
|
20
|
+
*
|
|
21
|
+
* `name` is the third key and is optional only so the choice stays testable as
|
|
22
|
+
* pure data. Two anonymous functions can share a file AND a line —
|
|
23
|
+
* `{ handler: () => {}, fallback: () => {} }` on one line, which
|
|
24
|
+
* `collectFunctions({ includeObjectLiteralFunctions: true })` does collect — and
|
|
25
|
+
* without a third component such a pair falls back to walk order, which is the
|
|
26
|
+
* whole defect.
|
|
27
|
+
*/
|
|
28
|
+
interface BodyPosition {
|
|
29
|
+
readonly file: string;
|
|
30
|
+
readonly line: number;
|
|
31
|
+
readonly name?: string;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Which member a finding is reported at — by the total order, never walk order.
|
|
35
|
+
*
|
|
36
|
+
* Bug 0242. The identity is sorted precisely so it "survives a filesystem
|
|
37
|
+
* walking the members in a different order"; the ANCHOR decides `file` and
|
|
38
|
+
* `line`, which is where an author must put `// eess-exclude`, and it was
|
|
39
|
+
* `members[0]` — that same walk order. Durable identity beside a non-durable
|
|
40
|
+
* location, in one finding: a waiver committed against the member one machine
|
|
41
|
+
* reported would stop suppressing on a machine that reported the other.
|
|
42
|
+
*
|
|
43
|
+
* Precondition: at least one member. On an empty list it returns 0, which is not
|
|
44
|
+
* a valid index — the caller guards on the lookup, and returning a sentinel
|
|
45
|
+
* would move that guard rather than remove it. Stated because a total signature
|
|
46
|
+
* over a partial function is worth saying out loud.
|
|
47
|
+
*
|
|
48
|
+
* Takes plain positions rather than the nodes, so the choice is testable without
|
|
49
|
+
* a ts-morph project. An end-to-end test that reads whatever order the
|
|
50
|
+
* filesystem happens to give cannot tell "sorted" from "walk order" when that
|
|
51
|
+
* walk is already alphabetical — it would pass against the bug it was written
|
|
52
|
+
* for. One that *reverses* the walk deliberately can, and both
|
|
53
|
+
* `tests/smells/anchor-determinism.test.ts` and
|
|
54
|
+
* `tests/integration/baseline-portability.test.ts` do: together they are a
|
|
55
|
+
* differently-derived pair (ADR-009 rule 5), not a duplicate.
|
|
56
|
+
*/
|
|
57
|
+
export declare function anchorIndex(members: readonly BodyPosition[]): number;
|
|
58
|
+
/**
|
|
59
|
+
* The file a finding over these bodies will be reported at.
|
|
60
|
+
*
|
|
61
|
+
* Exported so report ORDER can be keyed on the member the finding actually
|
|
62
|
+
* names. `.groupByFolder()` sorted on the walk-order endpoint, which agreed with
|
|
63
|
+
* the reported location only until bug 0242 moved the anchor — after which a
|
|
64
|
+
* finding reported in one folder could sort into a different folder's group,
|
|
65
|
+
* which is the one thing that option exists to prevent.
|
|
66
|
+
*
|
|
67
|
+
* Returns '' for an empty list, matching what the callers already did with a
|
|
68
|
+
* missing member rather than inventing a second convention for it.
|
|
69
|
+
*/
|
|
70
|
+
export declare function anchorFile(members: readonly ArchFunction[]): string;
|
|
71
|
+
/**
|
|
72
|
+
* The finding for a duplicated PAIR — two bodies, the common case.
|
|
73
|
+
*
|
|
74
|
+
* Lives here beside `clusterViolation` for the reason this module's own
|
|
75
|
+
* docstring gives: the builder collects a fluent configuration, and this is what
|
|
76
|
+
* the result says. Keeping it inline pushed `DuplicateBodiesBuilder` past the
|
|
77
|
+
* repo's 150-line class rule and its own method past 30 — the rules noticing
|
|
78
|
+
* correctly, twice, exactly as they did when this module was first split out.
|
|
79
|
+
*/
|
|
80
|
+
export declare function pairViolation(pair: SimilarPair, context: {
|
|
81
|
+
rule: string;
|
|
82
|
+
because?: string;
|
|
83
|
+
}): ArchViolation;
|
|
84
|
+
/** The single finding standing for a group of three or more similar bodies. */
|
|
85
|
+
export declare function clusterViolation(cluster: SimilarCluster, context: {
|
|
86
|
+
rule: string;
|
|
87
|
+
because?: string;
|
|
88
|
+
}): ArchViolation;
|
|
89
|
+
export {};
|
|
90
|
+
//# sourceMappingURL=duplicate-report.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"duplicate-report.d.ts","sourceRoot":"","sources":["../../src/smells/duplicate-report.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAA;AACrD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAA;AAC9D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAA;AACrD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,eAAe,CAAA;AAiDnD;;;;;;;;;;;GAWG;AAEH,wBAAgB,eAAe,CAAC,IAAI,EAAE,WAAW,GAAG,SAAS,GAAG,MAAM,CAiBrE;AAiBD;;;;;;;;;GASG;AACH,UAAU,YAAY;IACpB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CACvB;AA6BD;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,wBAAgB,WAAW,CAAC,OAAO,EAAE,SAAS,YAAY,EAAE,GAAG,MAAM,CASpE;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,UAAU,CAAC,OAAO,EAAE,SAAS,YAAY,EAAE,GAAG,MAAM,CAMnE;AAsED;;;;;;;;GAQG;AACH,wBAAgB,aAAa,CAC3B,IAAI,EAAE,WAAW,EACjB,OAAO,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,GAC1C,aAAa,CAgDf;AAED,+EAA+E;AAC/E,wBAAgB,gBAAgB,CAC9B,OAAO,EAAE,cAAc,EACvB,OAAO,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,GAC1C,aAAa,CAmDf"}
|
|
@@ -0,0 +1,344 @@
|
|
|
1
|
+
import { variationBetween } from './variation.js';
|
|
2
|
+
/**
|
|
3
|
+
* How a duplicate-body finding READS.
|
|
4
|
+
*
|
|
5
|
+
* Separated from `DuplicateBodiesBuilder` for the same reason `similar-pairs.ts`
|
|
6
|
+
* is: the builder's job is to collect a fluent configuration, and this is what
|
|
7
|
+
* the result says. Keeping them together pushed the class past the repo's own
|
|
8
|
+
* 150-line class rule, which is the rule noticing correctly.
|
|
9
|
+
*/
|
|
10
|
+
/**
|
|
11
|
+
* Cluster members and variation axes shown before the message elides.
|
|
12
|
+
*
|
|
13
|
+
* Three is enough to recognise the group and short enough to stay one line in a
|
|
14
|
+
* terminal. The remainder is always counted, never dropped silently.
|
|
15
|
+
*/
|
|
16
|
+
const MAX_SHOWN = 3;
|
|
17
|
+
/**
|
|
18
|
+
* Longest an axis text is shown before it is elided.
|
|
19
|
+
*
|
|
20
|
+
* A varying "identifier" can be a string literal, and a string literal can be a
|
|
21
|
+
* forty-line SQL query in a template. Measured on a ~5,600-file monorepo: 527
|
|
22
|
+
* findings rendered as **658 lines**, because axis texts carried their own
|
|
23
|
+
* newlines straight into the message. A finding that spills over many lines
|
|
24
|
+
* defeats the entire point of reporting axes, which is that a reader can triage
|
|
25
|
+
* one at a glance.
|
|
26
|
+
*/
|
|
27
|
+
const MAX_AXIS_TEXT = 32;
|
|
28
|
+
/**
|
|
29
|
+
* One axis text, guaranteed to be one short line.
|
|
30
|
+
*
|
|
31
|
+
* Whitespace is collapsed BEFORE truncating, so a multi-line literal is not
|
|
32
|
+
* merely cut at its first newline — that would silently present the first line
|
|
33
|
+
* as if it were the whole text.
|
|
34
|
+
*/
|
|
35
|
+
function oneLine(text) {
|
|
36
|
+
const flat = text.replace(/\s+/g, ' ').trim();
|
|
37
|
+
return flat.length <= MAX_AXIS_TEXT ? flat : `${flat.slice(0, MAX_AXIS_TEXT - 1)}…`;
|
|
38
|
+
}
|
|
39
|
+
/** How one body reads inside a message. */
|
|
40
|
+
function locate(fn) {
|
|
41
|
+
return `${fn.getName() ?? '<anonymous>'} (${fn.getSourceFile().getFilePath()}:${String(fn.getStartLineNumber())})`;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* What differs between two reported bodies, in one clause.
|
|
45
|
+
*
|
|
46
|
+
* The percentage answers "how alike are these shapes" and cannot answer the
|
|
47
|
+
* question a reader actually has, which is "should I consolidate them". That
|
|
48
|
+
* turns on how many things vary: one call target is a parameter extraction,
|
|
49
|
+
* nine property names is a shared idiom, and those two score identically today.
|
|
50
|
+
*
|
|
51
|
+
* Measured on a ~5,600-file production monorepo — the bucket that is mostly
|
|
52
|
+
* convergent idiom carries a median of 6 varying axes against 4 for the rest.
|
|
53
|
+
* Real information, and NOT a classifier: it is reported, never filtered on.
|
|
54
|
+
*/
|
|
55
|
+
// eess-exclude eess/no-unused-exports: consumed by the test suite; the build tsconfig this gate reads excludes tests. Its last in-`src` caller moved into this module when `pairViolation` was extracted here (bug 0242), so the export now exists for `variation.test.ts` alone — two tests there, both feeding it a hand-built fingerprint pair: one pins the one-line elision measured at 527 findings rendering as 658 lines, the other pins the `too large to diff` wording. Writing this reason down found that its `skipped` branch was rendered by nobody's test — asserted on `variationBetween` alone — so `variation.test.ts` now pins that wording too.
|
|
56
|
+
export function varianceSummary(pair) {
|
|
57
|
+
const a = pair?.fingerprintA;
|
|
58
|
+
const b = pair?.fingerprintB;
|
|
59
|
+
if (!a || !b)
|
|
60
|
+
return '';
|
|
61
|
+
const variation = variationBetween(a, b);
|
|
62
|
+
// A declared non-answer, not a computed zero (ADR-010) — saying "differs in
|
|
63
|
+
// nothing" about a pair too large to align would be a lie in the direction
|
|
64
|
+
// that costs a reader most.
|
|
65
|
+
if (variation.skipped)
|
|
66
|
+
return ' — too large to diff';
|
|
67
|
+
if (variation.axes.length === 0)
|
|
68
|
+
return ' — identical text: a literal copy';
|
|
69
|
+
const shown = variation.axes
|
|
70
|
+
.slice(0, MAX_SHOWN)
|
|
71
|
+
.map((axis) => `${oneLine(axis.from)} -> ${oneLine(axis.to)}`)
|
|
72
|
+
.join(', ');
|
|
73
|
+
const rest = variation.axes.length - Math.min(MAX_SHOWN, variation.axes.length);
|
|
74
|
+
const noun = variation.axes.length === 1 ? 'axis' : 'axes';
|
|
75
|
+
return ` — ${String(variation.axes.length)} varying ${noun}: ${shown}${rest > 0 ? `, +${String(rest)} more` : ''}`;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* The files a finding concerns besides the one it is reported at.
|
|
79
|
+
*
|
|
80
|
+
* `relatedFiles` means the OTHERS: `file` is already the finding's own, and
|
|
81
|
+
* repeating it would make the field's own meaning ambiguous to any consumer
|
|
82
|
+
* that reads the two together. Sorted so the value does not depend on source
|
|
83
|
+
* walk order, and `undefined` rather than `[]` when a duplicate sits entirely
|
|
84
|
+
* within one file — an empty array reads as "checked, none", which is true, but
|
|
85
|
+
* omitting it keeps a single-file finding byte-identical to what shipped.
|
|
86
|
+
*/
|
|
87
|
+
function otherFiles(own, all) {
|
|
88
|
+
const rest = [...new Set(all)].filter((f) => f !== own).sort();
|
|
89
|
+
return rest.length > 0 ? rest : undefined;
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* The total order: path, then line, then name.
|
|
93
|
+
*
|
|
94
|
+
* ONE comparator, because this is one decision. It was briefly two — a `<` on
|
|
95
|
+
* `{ file, line }` here and a zero-padded string key beside `orientPair` — which
|
|
96
|
+
* review called out as the module whose subject is duplicated bodies duplicating
|
|
97
|
+
* a body. They agreed, but only by accident of padding width: unpadded, `":10"`
|
|
98
|
+
* sorts before `":9"` lexicographically, so two same-file bodies at lines 9 and
|
|
99
|
+
* 10 would have oriented a finding's axes against the anchor it reports at.
|
|
100
|
+
*/
|
|
101
|
+
function comparePositions(a, b) {
|
|
102
|
+
if (a.file !== b.file)
|
|
103
|
+
return a.file < b.file ? -1 : 1;
|
|
104
|
+
if (a.line !== b.line)
|
|
105
|
+
return a.line - b.line;
|
|
106
|
+
const an = a.name ?? '';
|
|
107
|
+
const bn = b.name ?? '';
|
|
108
|
+
return an < bn ? -1 : an > bn ? 1 : 0;
|
|
109
|
+
}
|
|
110
|
+
/** Where a body sits, as the order above wants it. */
|
|
111
|
+
function positionOf(fn) {
|
|
112
|
+
return {
|
|
113
|
+
file: fn.getSourceFile().getFilePath(),
|
|
114
|
+
line: fn.getStartLineNumber(),
|
|
115
|
+
name: fn.getName() ?? '<anonymous>',
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Which member a finding is reported at — by the total order, never walk order.
|
|
120
|
+
*
|
|
121
|
+
* Bug 0242. The identity is sorted precisely so it "survives a filesystem
|
|
122
|
+
* walking the members in a different order"; the ANCHOR decides `file` and
|
|
123
|
+
* `line`, which is where an author must put `// eess-exclude`, and it was
|
|
124
|
+
* `members[0]` — that same walk order. Durable identity beside a non-durable
|
|
125
|
+
* location, in one finding: a waiver committed against the member one machine
|
|
126
|
+
* reported would stop suppressing on a machine that reported the other.
|
|
127
|
+
*
|
|
128
|
+
* Precondition: at least one member. On an empty list it returns 0, which is not
|
|
129
|
+
* a valid index — the caller guards on the lookup, and returning a sentinel
|
|
130
|
+
* would move that guard rather than remove it. Stated because a total signature
|
|
131
|
+
* over a partial function is worth saying out loud.
|
|
132
|
+
*
|
|
133
|
+
* Takes plain positions rather than the nodes, so the choice is testable without
|
|
134
|
+
* a ts-morph project. An end-to-end test that reads whatever order the
|
|
135
|
+
* filesystem happens to give cannot tell "sorted" from "walk order" when that
|
|
136
|
+
* walk is already alphabetical — it would pass against the bug it was written
|
|
137
|
+
* for. One that *reverses* the walk deliberately can, and both
|
|
138
|
+
* `tests/smells/anchor-determinism.test.ts` and
|
|
139
|
+
* `tests/integration/baseline-portability.test.ts` do: together they are a
|
|
140
|
+
* differently-derived pair (ADR-009 rule 5), not a duplicate.
|
|
141
|
+
*/
|
|
142
|
+
// eess-exclude eess/no-unused-exports: consumed by the test suite; the build tsconfig this gate reads excludes tests. Exported ON PURPOSE — `anchor-determinism.test.ts` asserts this choice as pure data, which is the derivation an end-to-end test reading the filesystem's own order cannot supply when that order is already alphabetical.
|
|
143
|
+
export function anchorIndex(members) {
|
|
144
|
+
let best = 0;
|
|
145
|
+
for (let i = 1; i < members.length; i++) {
|
|
146
|
+
const m = members[i];
|
|
147
|
+
const b = members[best];
|
|
148
|
+
if (!m || !b)
|
|
149
|
+
continue;
|
|
150
|
+
if (comparePositions(m, b) < 0)
|
|
151
|
+
best = i;
|
|
152
|
+
}
|
|
153
|
+
return best;
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* The file a finding over these bodies will be reported at.
|
|
157
|
+
*
|
|
158
|
+
* Exported so report ORDER can be keyed on the member the finding actually
|
|
159
|
+
* names. `.groupByFolder()` sorted on the walk-order endpoint, which agreed with
|
|
160
|
+
* the reported location only until bug 0242 moved the anchor — after which a
|
|
161
|
+
* finding reported in one folder could sort into a different folder's group,
|
|
162
|
+
* which is the one thing that option exists to prevent.
|
|
163
|
+
*
|
|
164
|
+
* Returns '' for an empty list, matching what the callers already did with a
|
|
165
|
+
* missing member rather than inventing a second convention for it.
|
|
166
|
+
*/
|
|
167
|
+
export function anchorFile(members) {
|
|
168
|
+
const positions = members.map((fn) => ({
|
|
169
|
+
file: fn.getSourceFile().getFilePath(),
|
|
170
|
+
line: fn.getStartLineNumber(),
|
|
171
|
+
}));
|
|
172
|
+
return positions[anchorIndex(positions)]?.file ?? '';
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* Which pair's varying axes stand as the cluster's evidence.
|
|
176
|
+
*
|
|
177
|
+
* `pairs[0]` was the first pair the search happened to find, so the axes quoted
|
|
178
|
+
* for one finding differed between machines (bug 0242, second pass). Chosen by
|
|
179
|
+
* the same total order as the anchor, applied to the pair's own endpoints, so
|
|
180
|
+
* the evidence is a function of the members and not of discovery.
|
|
181
|
+
*/
|
|
182
|
+
function representativePair(pairs) {
|
|
183
|
+
// Oriented BEFORE comparing, which is what lets `comparePairs` be a plain
|
|
184
|
+
// two-key comparison. The first version sorted each pair's ends inside the
|
|
185
|
+
// comparator instead; that sort was redundant with this orientation and no
|
|
186
|
+
// sabotage of it went red, so it was removed rather than given a test. An
|
|
187
|
+
// unfalsifiable branch is worth deleting when the alternative is a fixture
|
|
188
|
+
// written to justify keeping it.
|
|
189
|
+
let best;
|
|
190
|
+
for (const p of pairs) {
|
|
191
|
+
const oriented = orientPair(p);
|
|
192
|
+
if (best === undefined || comparePairs(oriented, best) < 0)
|
|
193
|
+
best = oriented;
|
|
194
|
+
}
|
|
195
|
+
return best;
|
|
196
|
+
}
|
|
197
|
+
/**
|
|
198
|
+
* Two ORIENTED pairs, by their lower end and then their upper.
|
|
199
|
+
*
|
|
200
|
+
* Requires both arguments to have been through {@link orientPair}, which is why
|
|
201
|
+
* it is not exported: on an un-oriented pair `a` is the source walk and this
|
|
202
|
+
* comparison would inherit exactly the non-determinism it exists to remove.
|
|
203
|
+
*/
|
|
204
|
+
function comparePairs(x, y) {
|
|
205
|
+
const first = comparePositions(positionOf(x.a), positionOf(y.a));
|
|
206
|
+
return first !== 0 ? first : comparePositions(positionOf(x.b), positionOf(y.b));
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* A pair with its two ends in the total order — `a` is the end a finding over
|
|
210
|
+
* this pair is reported at.
|
|
211
|
+
*
|
|
212
|
+
* `varianceSummary` renders `from -> to` off the pair's own `a`/`b`, and which
|
|
213
|
+
* end is `a` is the source walk — so the SAME finding read `'x' -> 'y'` on one
|
|
214
|
+
* machine and `'y' -> 'x'` on another (bug 0242, second pass). Ordering the ends
|
|
215
|
+
* makes the direction a function of the members.
|
|
216
|
+
*
|
|
217
|
+
* **Requires a pair with real nodes.** `SimilarPair` documents a hand-built pair
|
|
218
|
+
* as a supported shape, and an earlier version of this comment claimed such a
|
|
219
|
+
* pair was "never touched" because the in-order branch returned it unchanged.
|
|
220
|
+
* That was false, and review measured it: `positionOf(pair.a)` is the FIRST
|
|
221
|
+
* thing evaluated, so a pair with no nodes throws before any branch is taken. A
|
|
222
|
+
* caller that only wants a variance summary must call `varianceSummary`
|
|
223
|
+
* directly, as `variation.test.ts` does.
|
|
224
|
+
*
|
|
225
|
+
* Always returns a new object, in both branches. Returning the caller's own
|
|
226
|
+
* object when it was already in order gave the two branches different ownership
|
|
227
|
+
* semantics — a later mutation of the result would write through to the input on
|
|
228
|
+
* one path and not the other.
|
|
229
|
+
*/
|
|
230
|
+
function orientPair(pair) {
|
|
231
|
+
if (comparePositions(positionOf(pair.a), positionOf(pair.b)) <= 0)
|
|
232
|
+
return { ...pair };
|
|
233
|
+
return {
|
|
234
|
+
...pair,
|
|
235
|
+
a: pair.b,
|
|
236
|
+
b: pair.a,
|
|
237
|
+
fingerprintA: pair.fingerprintB,
|
|
238
|
+
fingerprintB: pair.fingerprintA,
|
|
239
|
+
};
|
|
240
|
+
}
|
|
241
|
+
/**
|
|
242
|
+
* The finding for a duplicated PAIR — two bodies, the common case.
|
|
243
|
+
*
|
|
244
|
+
* Lives here beside `clusterViolation` for the reason this module's own
|
|
245
|
+
* docstring gives: the builder collects a fluent configuration, and this is what
|
|
246
|
+
* the result says. Keeping it inline pushed `DuplicateBodiesBuilder` past the
|
|
247
|
+
* repo's 150-line class rule and its own method past 30 — the rules noticing
|
|
248
|
+
* correctly, twice, exactly as they did when this module was first split out.
|
|
249
|
+
*/
|
|
250
|
+
export function pairViolation(pair, context) {
|
|
251
|
+
// Bug 0242: which endpoint is `a` is the source walk, so the REPORTED
|
|
252
|
+
// location — where an author must put `// eess-exclude` — moved with the
|
|
253
|
+
// filesystem, while the identity below was sorted precisely so it would not.
|
|
254
|
+
//
|
|
255
|
+
// Oriented ONCE, and everything below derived from that one decision. It was
|
|
256
|
+
// briefly derived twice — `anchorIndex` for the coordinates, `orientPair` for
|
|
257
|
+
// the axis direction — which review called out: a divergence between the two
|
|
258
|
+
// would print a finding whose prose leads with one body while its axes read in
|
|
259
|
+
// the other direction, the exact contradiction `anchor-determinism.test.ts`
|
|
260
|
+
// was added to catch.
|
|
261
|
+
//
|
|
262
|
+
// Total, not `| undefined`. The earlier shape returned `undefined` on a branch
|
|
263
|
+
// that could not be reached and the caller dropped that with `?? []` — a
|
|
264
|
+
// finding vanishing with no diagnostic, in a repo whose whole thesis is that
|
|
265
|
+
// silent drops are the disqualifying failure. Unreachable today is not a
|
|
266
|
+
// defence: the type is what stops it becoming reachable.
|
|
267
|
+
const oriented = orientPair(pair);
|
|
268
|
+
const first = positionOf(oriented.a);
|
|
269
|
+
const second = positionOf(oriented.b);
|
|
270
|
+
const nameA = first.name ?? '<anonymous>';
|
|
271
|
+
const nameB = second.name ?? '<anonymous>';
|
|
272
|
+
const pct = Math.round(pair.similarity * 100);
|
|
273
|
+
return {
|
|
274
|
+
rule: context.rule,
|
|
275
|
+
element: nameA,
|
|
276
|
+
file: first.file,
|
|
277
|
+
line: first.line,
|
|
278
|
+
// Both endpoints (bug 0239). A two-body duplicate is the COMMON case — copy
|
|
279
|
+
// a function into one new file — and it had the same defect as a cluster:
|
|
280
|
+
// anchored on one end, so the other could not see it under `--changed`.
|
|
281
|
+
relatedFiles: otherFiles(first.file, [first.file, second.file]),
|
|
282
|
+
message: `${nameA} (${first.file}:${String(first.line)}) is ${String(pct)}% similar to ` +
|
|
283
|
+
`${nameB} (${second.file}:${String(second.line)})${varianceSummary(oriented)}`,
|
|
284
|
+
// Sorted, path-qualified and without the percentage, so the pair reads the
|
|
285
|
+
// same whichever way a filesystem walks it and does not drift as a body is
|
|
286
|
+
// edited. The anchor above uses the same ordering.
|
|
287
|
+
//
|
|
288
|
+
// Limitation: two anonymous functions in one file share an endpoint
|
|
289
|
+
// (`<file>#<anonymous>`) and so share an identity. Measured at 0 collisions
|
|
290
|
+
// over 1006 findings on a real codebase; the guard in
|
|
291
|
+
// `tests/integration/baseline-portability.test.ts` is what would catch it
|
|
292
|
+
// becoming common.
|
|
293
|
+
identity: `duplicate-pair::${[`${first.file}#${nameA}`, `${second.file}#${nameB}`].sort().join('::')}`,
|
|
294
|
+
because: context.because,
|
|
295
|
+
};
|
|
296
|
+
}
|
|
297
|
+
/** The single finding standing for a group of three or more similar bodies. */
|
|
298
|
+
export function clusterViolation(cluster, context) {
|
|
299
|
+
const at = anchorIndex(cluster.members.map((fn) => ({
|
|
300
|
+
file: fn.getSourceFile().getFilePath(),
|
|
301
|
+
line: fn.getStartLineNumber(),
|
|
302
|
+
})));
|
|
303
|
+
// Total, like `pairViolation`. `members` is typed non-empty, so the anchor
|
|
304
|
+
// lookup cannot miss and there is no `undefined` for the caller to drop.
|
|
305
|
+
const anchor = cluster.members[at] ?? cluster.members[0];
|
|
306
|
+
// Sorted by the same total order the anchor uses (bug 0242, second pass).
|
|
307
|
+
// `MAX_SHOWN` elides all but the first few, so first-seen order decided WHICH
|
|
308
|
+
// member a reader never sees — the filesystem's choice, printed as evidence.
|
|
309
|
+
// Nothing here moves the identity or `relatedFiles`, so no waiver breaks; what
|
|
310
|
+
// breaks is a reader comparing a local run against CI.
|
|
311
|
+
const others = cluster.members
|
|
312
|
+
.filter((_, i) => i !== at)
|
|
313
|
+
.map((fn) => ({ fn, at: positionOf(fn) }))
|
|
314
|
+
.sort((x, y) => comparePositions(x.at, y.at))
|
|
315
|
+
.map((m) => m.fn);
|
|
316
|
+
const shown = others.slice(0, MAX_SHOWN).map(locate).join(', ');
|
|
317
|
+
const rest = others.length - Math.min(MAX_SHOWN, others.length);
|
|
318
|
+
const pct = Math.round(cluster.peakSimilarity * 100);
|
|
319
|
+
const keys = cluster.members
|
|
320
|
+
.map((fn) => `${fn.getSourceFile().getFilePath()}#${fn.getName() ?? '<anonymous>'}`)
|
|
321
|
+
.sort();
|
|
322
|
+
return {
|
|
323
|
+
rule: context.rule,
|
|
324
|
+
element: anchor.getName() ?? '<anonymous>',
|
|
325
|
+
file: anchor.getSourceFile().getFilePath(),
|
|
326
|
+
line: anchor.getStartLineNumber(),
|
|
327
|
+
// Every member's file, so `--changed` keeps this finding for whichever
|
|
328
|
+
// member a developer actually edited (bug 0239). Keyed on the ANCHOR — this
|
|
329
|
+
// comment used to say "the anchor is walk order", which bug 0242 made false
|
|
330
|
+
// in the same commit that left the sentence standing. De-duplicated: several
|
|
331
|
+
// members commonly share one file, and a repeated path would say nothing
|
|
332
|
+
// extra to a set-based filter.
|
|
333
|
+
relatedFiles: otherFiles(anchor.getSourceFile().getFilePath(), cluster.members.map((fn) => fn.getSourceFile().getFilePath())),
|
|
334
|
+
message: `${locate(anchor)} is up to ${String(pct)}% similar to ` +
|
|
335
|
+
`${String(others.length)} other bodies: ${shown}${rest > 0 ? `, +${String(rest)} more` : ''}` +
|
|
336
|
+
`${varianceSummary(representativePair(cluster.pairs))}`,
|
|
337
|
+
// Same construction as the pair identity — sorted, path-qualified, and
|
|
338
|
+
// without the percentage — so it survives a filesystem walking the members
|
|
339
|
+
// in a different order and does not drift as a body is edited.
|
|
340
|
+
identity: `duplicate-cluster::${keys.join('::')}`,
|
|
341
|
+
because: context.because,
|
|
342
|
+
};
|
|
343
|
+
}
|
|
344
|
+
//# sourceMappingURL=duplicate-report.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"duplicate-report.js","sourceRoot":"","sources":["../../src/smells/duplicate-report.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { SyntaxKind, type Node } from 'ts-morph';
|
|
2
2
|
/**
|
|
3
3
|
* Structural fingerprint of a function body.
|
|
4
4
|
* Captures the shape (node kinds, call targets) while ignoring
|
|
@@ -7,10 +7,29 @@ import { type SyntaxKind, type Node } from 'ts-morph';
|
|
|
7
7
|
export interface Fingerprint {
|
|
8
8
|
/** Ordered sequence of syntax node kinds in the body */
|
|
9
9
|
readonly kinds: readonly SyntaxKind[];
|
|
10
|
+
/**
|
|
11
|
+
* Ordered, index-parallel to {@link kinds}: the node's text where it carries
|
|
12
|
+
* one, `undefined` otherwise.
|
|
13
|
+
*
|
|
14
|
+
* `computeSimilarity` does not read this and must not — ignoring identifiers
|
|
15
|
+
* is what makes the score a type-2 clone score. It exists so a caller can ask
|
|
16
|
+
* the SEPARATE question, of a pair already reported, "at the positions these
|
|
17
|
+
* two bodies align, do they say the same words?" — which is what tells a
|
|
18
|
+
* reader whether one call target varies or nine property names do. See
|
|
19
|
+
* `variation.ts`.
|
|
20
|
+
*/
|
|
21
|
+
readonly texts: readonly (string | undefined)[];
|
|
10
22
|
/** Normalized call targets (e.g. ['parseInt', 'this.extractCount']) */
|
|
11
23
|
readonly calls: readonly string[];
|
|
12
24
|
/** Total AST node count (for line-count filtering) */
|
|
13
25
|
readonly nodeCount: number;
|
|
26
|
+
/**
|
|
27
|
+
* Count of DISTINCT identifier/literal texts in the body — the vocabulary
|
|
28
|
+
* a body actually carries, as opposed to its punctuation/keyword shape.
|
|
29
|
+
* Plan 0103's floor reads this; `computeSimilarity()` does not — see that
|
|
30
|
+
* function's own docs for why.
|
|
31
|
+
*/
|
|
32
|
+
readonly distinctVocabulary: number;
|
|
14
33
|
}
|
|
15
34
|
/**
|
|
16
35
|
* Build a structural fingerprint from a function body AST node.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fingerprint.d.ts","sourceRoot":"","sources":["../../src/smells/fingerprint.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"fingerprint.d.ts","sourceRoot":"","sources":["../../src/smells/fingerprint.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,KAAK,IAAI,EAAqB,MAAM,UAAU,CAAA;AAgBnE;;;;GAIG;AACH,MAAM,WAAW,WAAW;IAC1B,wDAAwD;IACxD,QAAQ,CAAC,KAAK,EAAE,SAAS,UAAU,EAAE,CAAA;IACrC;;;;;;;;;;OAUG;IACH,QAAQ,CAAC,KAAK,EAAE,SAAS,CAAC,MAAM,GAAG,SAAS,CAAC,EAAE,CAAA;IAC/C,uEAAuE;IACvE,QAAQ,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,CAAA;IACjC,sDAAsD;IACtD,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;IAC1B;;;;;OAKG;IACH,QAAQ,CAAC,kBAAkB,EAAE,MAAM,CAAA;CACpC;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,IAAI,GAAG,WAAW,CAsBxD;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,CAAC,EAAE,WAAW,EAAE,CAAC,EAAE,WAAW,GAAG,MAAM,CAMxE"}
|
|
@@ -1,4 +1,17 @@
|
|
|
1
|
-
import { Node as NodeClass } from 'ts-morph';
|
|
1
|
+
import { SyntaxKind, Node as NodeClass } from 'ts-morph';
|
|
2
|
+
// Deliberately not exhaustive over every text-bearing kind — `TemplateHead`/
|
|
3
|
+
// `TemplateMiddle`/`TemplateTail` (interpolated templates), `BigIntLiteral` and
|
|
4
|
+
// `RegularExpressionLiteral` are omitted. Every omission UNDERcounts vocabulary,
|
|
5
|
+
// which only makes the floor (plan 0103) more conservative — fewer pairs
|
|
6
|
+
// compared, never a false positive from a body that reads as emptier than it
|
|
7
|
+
// is. Widen this set if a real interpolated-template-heavy corpus needs it.
|
|
8
|
+
const TEXT_KINDS = new Set([
|
|
9
|
+
SyntaxKind.Identifier,
|
|
10
|
+
SyntaxKind.PrivateIdentifier,
|
|
11
|
+
SyntaxKind.StringLiteral,
|
|
12
|
+
SyntaxKind.NoSubstitutionTemplateLiteral,
|
|
13
|
+
SyntaxKind.NumericLiteral,
|
|
14
|
+
]);
|
|
2
15
|
/**
|
|
3
16
|
* Build a structural fingerprint from a function body AST node.
|
|
4
17
|
* Walks all descendants, records their SyntaxKind in order,
|
|
@@ -6,14 +19,25 @@ import { Node as NodeClass } from 'ts-morph';
|
|
|
6
19
|
*/
|
|
7
20
|
export function buildFingerprint(body) {
|
|
8
21
|
const kinds = [];
|
|
22
|
+
const texts = [];
|
|
9
23
|
const calls = [];
|
|
24
|
+
const distinct = new Set();
|
|
10
25
|
for (const node of body.getDescendants()) {
|
|
11
|
-
|
|
26
|
+
const kind = node.getKind();
|
|
27
|
+
kinds.push(kind);
|
|
12
28
|
if (NodeClass.isCallExpression(node)) {
|
|
13
29
|
calls.push(node.getExpression().getText().replace(/\?\./g, '.'));
|
|
14
30
|
}
|
|
31
|
+
if (TEXT_KINDS.has(kind)) {
|
|
32
|
+
const text = node.getText();
|
|
33
|
+
texts.push(text);
|
|
34
|
+
distinct.add(text);
|
|
35
|
+
}
|
|
36
|
+
else {
|
|
37
|
+
texts.push(undefined);
|
|
38
|
+
}
|
|
15
39
|
}
|
|
16
|
-
return { kinds, calls, nodeCount: kinds.length };
|
|
40
|
+
return { kinds, texts, calls, nodeCount: kinds.length, distinctVocabulary: distinct.size };
|
|
17
41
|
}
|
|
18
42
|
/**
|
|
19
43
|
* Compute similarity between two fingerprints.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fingerprint.js","sourceRoot":"","sources":["../../src/smells/fingerprint.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"fingerprint.js","sourceRoot":"","sources":["../../src/smells/fingerprint.ts"],"names":[],"mappings":"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"}
|