@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,47 @@
|
|
|
1
|
+
import type { SourceFile } from 'ts-morph';
|
|
2
|
+
import type { ArchProject } from '../core/project.js';
|
|
3
|
+
import type { ExpressionMatcher } from '../helpers/matchers.js';
|
|
4
|
+
/**
|
|
5
|
+
* Which files the sibling detector looks at, and how they are grouped.
|
|
6
|
+
*
|
|
7
|
+
* Separated from the builder because selecting and grouping files is not
|
|
8
|
+
* detecting inconsistency — the builder collects a fluent configuration and
|
|
9
|
+
* hands it here. The scope arrives as a value rather than being read off a
|
|
10
|
+
* builder, so the grouping is callable and testable without one.
|
|
11
|
+
*/
|
|
12
|
+
export interface SiblingScope {
|
|
13
|
+
readonly project: ArchProject;
|
|
14
|
+
readonly folders: readonly string[];
|
|
15
|
+
readonly ignorePaths: readonly string[];
|
|
16
|
+
readonly ignoreTests: boolean;
|
|
17
|
+
readonly minLines: number;
|
|
18
|
+
readonly pattern: ExpressionMatcher | undefined;
|
|
19
|
+
}
|
|
20
|
+
/** Partition files into matching and non-matching based on the pattern. */
|
|
21
|
+
export declare function partitionByPattern(files: SourceFile[], pattern: ExpressionMatcher, minLines: number): {
|
|
22
|
+
matching: SourceFile[];
|
|
23
|
+
nonMatching: SourceFile[];
|
|
24
|
+
};
|
|
25
|
+
/** Group source files by parent folder, applying all filters. */
|
|
26
|
+
export declare function groupFilesByFolder(scope: SiblingScope): Map<string, SourceFile[]>;
|
|
27
|
+
/** Single source of the inert message. Pure — takes the assessment, returns text. */
|
|
28
|
+
export declare function inertMessage(a: {
|
|
29
|
+
matching: number;
|
|
30
|
+
total: number;
|
|
31
|
+
}, patternDesc: string): string;
|
|
32
|
+
/**
|
|
33
|
+
* Dedupe key for the inert finding. `dedupeConfigFindings` keys on
|
|
34
|
+
* `${file} ${ruleId ?? rule} ${element}`; `file` is always `''` here and
|
|
35
|
+
* `rule`/`ruleId` fall back to `describe()`, which is scope-blind (reads
|
|
36
|
+
* `_pattern` only). So without a scope-aware `element`, two same-pattern/
|
|
37
|
+
* different-scope inert detectors with no explicit `.rule({id})` would
|
|
38
|
+
* collapse into one finding under `checkAll`.
|
|
39
|
+
*
|
|
40
|
+
* Folds in every field `groupFilesByFolder()`/`fileMatchesPattern()` read to
|
|
41
|
+
* decide what's examined — `_folders`, `_ignorePaths`, `_ignoreTests`,
|
|
42
|
+
* `_minLines` — sorted so option order cannot split one semantic scope into
|
|
43
|
+
* two keys. Two rules with identical scope correctly collapse to one finding
|
|
44
|
+
* (one edit fixes both); two rules differing in ANY of these stay distinct.
|
|
45
|
+
*/
|
|
46
|
+
export declare function inertElement(scope: SiblingScope): string;
|
|
47
|
+
//# sourceMappingURL=sibling-files.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sibling-files.d.ts","sourceRoot":"","sources":["../../src/smells/sibling-files.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,UAAU,CAAA;AAE1C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAA;AACrD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAA;AAO/D;;;;;;;GAOG;AACH,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,OAAO,EAAE,WAAW,CAAA;IAC7B,QAAQ,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAA;IACnC,QAAQ,CAAC,WAAW,EAAE,SAAS,MAAM,EAAE,CAAA;IACvC,QAAQ,CAAC,WAAW,EAAE,OAAO,CAAA;IAC7B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;IACzB,QAAQ,CAAC,OAAO,EAAE,iBAAiB,GAAG,SAAS,CAAA;CAChD;AAsBD,2EAA2E;AAC3E,wBAAgB,kBAAkB,CAChC,KAAK,EAAE,UAAU,EAAE,EACnB,OAAO,EAAE,iBAAiB,EAC1B,QAAQ,EAAE,MAAM,GACf;IAAE,QAAQ,EAAE,UAAU,EAAE,CAAC;IAAC,WAAW,EAAE,UAAU,EAAE,CAAA;CAAE,CAWvD;AAED,iEAAiE;AACjE,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,YAAY,GAAG,GAAG,CAAC,MAAM,EAAE,UAAU,EAAE,CAAC,CAoCjF;AAED,qFAAqF;AACrF,wBAAgB,YAAY,CAAC,CAAC,EAAE;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,EAAE,WAAW,EAAE,MAAM,GAAG,MAAM,CAWhG;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,YAAY,GAAG,MAAM,CAKxD"}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import picomatch from 'picomatch';
|
|
3
|
+
import { collectFunctions } from '../models/arch-function.js';
|
|
4
|
+
import { searchFunctionBody } from '../helpers/body-traversal.js';
|
|
5
|
+
/** Paths treated as tests when `.ignoreTests()` is on. */
|
|
6
|
+
const TEST_PATTERNS = ['**/*.test.ts', '**/*.spec.ts', '**/__tests__/**'];
|
|
7
|
+
/** Check if a source file contains any function matching the pattern. */
|
|
8
|
+
function fileMatchesPattern(sf, pattern, minLines) {
|
|
9
|
+
// Detectors scan for a property of the code, not a user-declared subject
|
|
10
|
+
// set, so they always include object-literal functions. `functions()`
|
|
11
|
+
// keeps that opt-in because widening a selector silently changes every
|
|
12
|
+
// existing rule; a detector has no such contract to break, and a
|
|
13
|
+
// duplicated arrow under an object key — a resolver, a route handler, a
|
|
14
|
+
// reducer case — is exactly the copy-paste rot this exists to find.
|
|
15
|
+
for (const fn of collectFunctions(sf, { includeObjectLiteralFunctions: true })) {
|
|
16
|
+
const body = fn.getBody();
|
|
17
|
+
if (!body)
|
|
18
|
+
continue;
|
|
19
|
+
const lineCount = body.getText().split('\n').length;
|
|
20
|
+
if (lineCount < minLines)
|
|
21
|
+
continue;
|
|
22
|
+
if (searchFunctionBody(fn, pattern).found)
|
|
23
|
+
return true;
|
|
24
|
+
}
|
|
25
|
+
return false;
|
|
26
|
+
}
|
|
27
|
+
/** Partition files into matching and non-matching based on the pattern. */
|
|
28
|
+
export function partitionByPattern(files, pattern, minLines) {
|
|
29
|
+
const matching = [];
|
|
30
|
+
const nonMatching = [];
|
|
31
|
+
for (const sf of files) {
|
|
32
|
+
if (fileMatchesPattern(sf, pattern, minLines)) {
|
|
33
|
+
matching.push(sf);
|
|
34
|
+
}
|
|
35
|
+
else {
|
|
36
|
+
nonMatching.push(sf);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
return { matching, nonMatching };
|
|
40
|
+
}
|
|
41
|
+
/** Group source files by parent folder, applying all filters. */
|
|
42
|
+
export function groupFilesByFolder(scope) {
|
|
43
|
+
const sourceFiles = scope.project.getSourceFiles();
|
|
44
|
+
const folderMatchers = scope.folders.map((g) => picomatch(g));
|
|
45
|
+
const ignoreMatchers = scope.ignorePaths.map((g) => picomatch(g));
|
|
46
|
+
const testMatchers = scope.ignoreTests ? TEST_PATTERNS.map((g) => picomatch(g)) : [];
|
|
47
|
+
const groups = new Map();
|
|
48
|
+
for (const sf of sourceFiles) {
|
|
49
|
+
const filePath = sf.getFilePath();
|
|
50
|
+
// Folder filter: if folders specified, file must match at least one
|
|
51
|
+
if (folderMatchers.length > 0 && !folderMatchers.some((m) => m(filePath))) {
|
|
52
|
+
continue;
|
|
53
|
+
}
|
|
54
|
+
// Ignore paths filter
|
|
55
|
+
if (ignoreMatchers.some((m) => m(filePath))) {
|
|
56
|
+
continue;
|
|
57
|
+
}
|
|
58
|
+
// Test file filter
|
|
59
|
+
if (testMatchers.some((m) => m(filePath))) {
|
|
60
|
+
continue;
|
|
61
|
+
}
|
|
62
|
+
const folder = path.dirname(filePath);
|
|
63
|
+
const existing = groups.get(folder);
|
|
64
|
+
if (existing) {
|
|
65
|
+
existing.push(sf);
|
|
66
|
+
}
|
|
67
|
+
else {
|
|
68
|
+
groups.set(folder, [sf]);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
return groups;
|
|
72
|
+
}
|
|
73
|
+
/** Single source of the inert message. Pure — takes the assessment, returns text. */
|
|
74
|
+
export function inertMessage(a, patternDesc) {
|
|
75
|
+
return (`This detector examined ${String(a.total)} sibling files, but only ${String(a.matching)} of them ` +
|
|
76
|
+
`hold the pattern '${patternDesc}', and no folder is within an edit of a majority — so as written it ` +
|
|
77
|
+
`cannot produce a finding today. It reports a file that diverges from what its siblings do; with no ` +
|
|
78
|
+
`majority reachable by adopting files, there is no divergence to report. ` +
|
|
79
|
+
`If this rule asserts a convention the codebase is still adopting, replace it with ` +
|
|
80
|
+
`crossProject().side(...).beComplete(), which fails the day a file falls short — until adoption is ` +
|
|
81
|
+
`complete, so expect that red. If the intent is to police divergence rather than the convention itself, ` +
|
|
82
|
+
`widen the folder so a majority forms, or choose a pattern the sibling files already share.`);
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Dedupe key for the inert finding. `dedupeConfigFindings` keys on
|
|
86
|
+
* `${file} ${ruleId ?? rule} ${element}`; `file` is always `''` here and
|
|
87
|
+
* `rule`/`ruleId` fall back to `describe()`, which is scope-blind (reads
|
|
88
|
+
* `_pattern` only). So without a scope-aware `element`, two same-pattern/
|
|
89
|
+
* different-scope inert detectors with no explicit `.rule({id})` would
|
|
90
|
+
* collapse into one finding under `checkAll`.
|
|
91
|
+
*
|
|
92
|
+
* Folds in every field `groupFilesByFolder()`/`fileMatchesPattern()` read to
|
|
93
|
+
* decide what's examined — `_folders`, `_ignorePaths`, `_ignoreTests`,
|
|
94
|
+
* `_minLines` — sorted so option order cannot split one semantic scope into
|
|
95
|
+
* two keys. Two rules with identical scope correctly collapse to one finding
|
|
96
|
+
* (one edit fixes both); two rules differing in ANY of these stay distinct.
|
|
97
|
+
*/
|
|
98
|
+
export function inertElement(scope) {
|
|
99
|
+
const patternDesc = scope.pattern?.description ?? 'unknown pattern';
|
|
100
|
+
const folders = [...scope.folders].sort().join('|');
|
|
101
|
+
const ignorePaths = [...scope.ignorePaths].sort().join('|');
|
|
102
|
+
return `inert:${patternDesc}:${folders}:${ignorePaths}:${String(scope.ignoreTests)}:${String(scope.minLines)}`;
|
|
103
|
+
}
|
|
104
|
+
//# sourceMappingURL=sibling-files.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sibling-files.js","sourceRoot":"","sources":["../../src/smells/sibling-files.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { ArchFunction } from '../models/arch-function.js';
|
|
2
|
+
import type { Fingerprint } from './fingerprint.js';
|
|
3
|
+
/** A function paired with its structural fingerprint. */
|
|
4
|
+
interface FingerprintedFunction {
|
|
5
|
+
fn: ArchFunction;
|
|
6
|
+
fingerprint: Fingerprint;
|
|
7
|
+
}
|
|
8
|
+
/** Two bodies the detector considers similar enough to report, and by how much. */
|
|
9
|
+
export interface SimilarPair {
|
|
10
|
+
a: ArchFunction;
|
|
11
|
+
b: ArchFunction;
|
|
12
|
+
similarity: number;
|
|
13
|
+
/**
|
|
14
|
+
* The fingerprints the score came from, carried so a REPORTED pair can be
|
|
15
|
+
* asked what varies between the two bodies without a second AST pass.
|
|
16
|
+
*
|
|
17
|
+
* Optional because a caller may construct a pair by hand — the message
|
|
18
|
+
* builder degrades to the bare percentage rather than assuming.
|
|
19
|
+
*/
|
|
20
|
+
fingerprintA?: Fingerprint;
|
|
21
|
+
fingerprintB?: Fingerprint;
|
|
22
|
+
}
|
|
23
|
+
/** Build fingerprints for all collected functions. */
|
|
24
|
+
export declare function fingerprintAll(functions: ArchFunction[]): FingerprintedFunction[];
|
|
25
|
+
/** Compare all pairs of fingerprints, collect those above threshold. */
|
|
26
|
+
export declare function findSimilarPairs(items: FingerprintedFunction[], minSimilarity: number, minDistinctVocabulary: number): SimilarPair[];
|
|
27
|
+
export {};
|
|
28
|
+
//# sourceMappingURL=similar-pairs.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"similar-pairs.d.ts","sourceRoot":"","sources":["../../src/smells/similar-pairs.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAA;AAC9D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAA;AAGnD,yDAAyD;AACzD,UAAU,qBAAqB;IAC7B,EAAE,EAAE,YAAY,CAAA;IAChB,WAAW,EAAE,WAAW,CAAA;CACzB;AAED,mFAAmF;AACnF,MAAM,WAAW,WAAW;IAC1B,CAAC,EAAE,YAAY,CAAA;IACf,CAAC,EAAE,YAAY,CAAA;IACf,UAAU,EAAE,MAAM,CAAA;IAClB;;;;;;OAMG;IACH,YAAY,CAAC,EAAE,WAAW,CAAA;IAC1B,YAAY,CAAC,EAAE,WAAW,CAAA;CAC3B;AA4BD,sDAAsD;AACtD,wBAAgB,cAAc,CAAC,SAAS,EAAE,YAAY,EAAE,GAAG,qBAAqB,EAAE,CAQjF;AAED,wEAAwE;AACxE,wBAAgB,gBAAgB,CAC9B,KAAK,EAAE,qBAAqB,EAAE,EAC9B,aAAa,EAAE,MAAM,EACrB,qBAAqB,EAAE,MAAM,GAC5B,WAAW,EAAE,CAyEf"}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import { buildFingerprint, computeSimilarity } from './fingerprint.js';
|
|
2
|
+
/**
|
|
3
|
+
* The pairwise comparison behind `smells.duplicateBodies()`.
|
|
4
|
+
*
|
|
5
|
+
* Separated from the builder because it is the algorithm, not the DSL: the
|
|
6
|
+
* builder's job is to collect a fluent configuration and hand it here. The
|
|
7
|
+
* thresholds arrive as arguments rather than being read off a builder, so the
|
|
8
|
+
* comparison is callable and testable without one.
|
|
9
|
+
*/
|
|
10
|
+
/**
|
|
11
|
+
* Does one function's body enclose the other's?
|
|
12
|
+
*
|
|
13
|
+
* Only meaningful within one source file; two bodies in different files can
|
|
14
|
+
* never nest. Compared on the BODY spans rather than the declarations, because
|
|
15
|
+
* that is what was fingerprinted.
|
|
16
|
+
*/
|
|
17
|
+
function containsOther(a, b) {
|
|
18
|
+
const bodyA = a.getBody();
|
|
19
|
+
const bodyB = b.getBody();
|
|
20
|
+
if (!bodyA || !bodyB)
|
|
21
|
+
return false;
|
|
22
|
+
if (bodyA.getSourceFile() !== bodyB.getSourceFile())
|
|
23
|
+
return false;
|
|
24
|
+
const [startA, endA] = [bodyA.getStart(), bodyA.getEnd()];
|
|
25
|
+
const [startB, endB] = [bodyB.getStart(), bodyB.getEnd()];
|
|
26
|
+
return (startA <= startB && endA >= endB) || (startB <= startA && endB >= endA);
|
|
27
|
+
}
|
|
28
|
+
/** Build fingerprints for all collected functions. */
|
|
29
|
+
export function fingerprintAll(functions) {
|
|
30
|
+
const result = [];
|
|
31
|
+
for (const fn of functions) {
|
|
32
|
+
const body = fn.getBody();
|
|
33
|
+
if (!body)
|
|
34
|
+
continue;
|
|
35
|
+
result.push({ fn, fingerprint: buildFingerprint(body) });
|
|
36
|
+
}
|
|
37
|
+
return result;
|
|
38
|
+
}
|
|
39
|
+
/** Compare all pairs of fingerprints, collect those above threshold. */
|
|
40
|
+
export function findSimilarPairs(items, minSimilarity, minDistinctVocabulary) {
|
|
41
|
+
const pairs = [];
|
|
42
|
+
for (let i = 0; i < items.length; i++) {
|
|
43
|
+
for (let j = i + 1; j < items.length; j++) {
|
|
44
|
+
const a = items[i];
|
|
45
|
+
const b = items[j];
|
|
46
|
+
if (!a || !b)
|
|
47
|
+
continue;
|
|
48
|
+
// Fast rejection 1: if node counts differ too much, similarity cannot reach threshold
|
|
49
|
+
const maxCount = Math.max(a.fingerprint.nodeCount, b.fingerprint.nodeCount);
|
|
50
|
+
const minCount = Math.min(a.fingerprint.nodeCount, b.fingerprint.nodeCount);
|
|
51
|
+
if (maxCount > 0 && minCount / maxCount < minSimilarity) {
|
|
52
|
+
continue;
|
|
53
|
+
}
|
|
54
|
+
// Fast rejection 2 (plan 0103): neither body has enough distinct vocabulary
|
|
55
|
+
// for a match to be evidence of anything. `Math.min`, not sum or average —
|
|
56
|
+
// ONE small-vocabulary side is enough to make the pair uninformative
|
|
57
|
+
// regardless of the other side's size.
|
|
58
|
+
const minDistinct = Math.min(a.fingerprint.distinctVocabulary, b.fingerprint.distinctVocabulary);
|
|
59
|
+
if (minDistinct < minDistinctVocabulary) {
|
|
60
|
+
continue;
|
|
61
|
+
}
|
|
62
|
+
// A body compared against a body NESTED INSIDE IT can never be
|
|
63
|
+
// actionable: "extract the shared logic into one function" is impossible
|
|
64
|
+
// when one function already contains the other. The detector generates
|
|
65
|
+
// these itself, because it collects object-literal functions by design —
|
|
66
|
+
// so `const rule = () => ({ evaluate })` yields both the outer arrow and
|
|
67
|
+
// the `evaluate` inside it, and they are similar by construction.
|
|
68
|
+
//
|
|
69
|
+
// Measured on this repo: 10 of 173 pairs, every one of them noise.
|
|
70
|
+
if (containsOther(a.fn, b.fn))
|
|
71
|
+
continue;
|
|
72
|
+
// Bug 0230 — the PAIRWISE floor, where the two above are per-body.
|
|
73
|
+
// Plan 0103 asks "does this body carry enough vocabulary to be
|
|
74
|
+
// evidence?"; nothing asked "do these two carry any of the SAME
|
|
75
|
+
// vocabulary?". `computeSimilarity` cannot answer it and must not — it
|
|
76
|
+
// scores syntax kinds only, which is what makes it a type-2 clone score
|
|
77
|
+
// — so a pair could reach 1.00 on shape with an empty intersection.
|
|
78
|
+
// Shipped, on this repo: `asDeclared` against `InconsistentSiblings-
|
|
79
|
+
// Builder.scope`, two functions that each gather six of their own fields
|
|
80
|
+
// into a record and have not one identifier in common. "Extract the
|
|
81
|
+
// shared logic into one function" names something that does not exist.
|
|
82
|
+
//
|
|
83
|
+
// `=== 0`, never a threshold. Measured over all 89 pairs this repo
|
|
84
|
+
// produces: two share nothing, none share one or two, and the nearest
|
|
85
|
+
// real finding shares four. The gap is empty, so no tuned number is
|
|
86
|
+
// needed — and a non-zero floor would start suppressing copy-paste.
|
|
87
|
+
//
|
|
88
|
+
// Guarded by `minDistinct > 0`, and that is not a special case for a
|
|
89
|
+
// test. "These share no vocabulary" is evidence of unrelatedness only if
|
|
90
|
+
// there was vocabulary to share: two bodies that are pure control flow
|
|
91
|
+
// (`return true`) share their entire content, because the shape IS the
|
|
92
|
+
// content. Whether such a pair is worth reporting is the CALLER's
|
|
93
|
+
// decision, spelled `minDistinctVocabulary(0)`, and rejection 2 above
|
|
94
|
+
// has already applied it. Overriding it here would make that option
|
|
95
|
+
// silently mean something else.
|
|
96
|
+
if (minDistinct > 0 && sharedVocabulary(a.fingerprint, b.fingerprint) === 0)
|
|
97
|
+
continue;
|
|
98
|
+
const similarity = computeSimilarity(a.fingerprint, b.fingerprint);
|
|
99
|
+
if (similarity >= minSimilarity) {
|
|
100
|
+
pairs.push({
|
|
101
|
+
a: a.fn,
|
|
102
|
+
b: b.fn,
|
|
103
|
+
similarity,
|
|
104
|
+
fingerprintA: a.fingerprint,
|
|
105
|
+
fingerprintB: b.fingerprint,
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
return pairs;
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* How many distinct identifiers/literals both bodies use.
|
|
114
|
+
*
|
|
115
|
+
* Reads `Fingerprint.texts`, which `computeSimilarity` deliberately ignores:
|
|
116
|
+
* the score is over syntax kinds so that a renamed copy still scores as a
|
|
117
|
+
* clone. That is right for scoring and wrong for deciding whether two bodies
|
|
118
|
+
* are related at all, which is what this answers.
|
|
119
|
+
*/
|
|
120
|
+
function sharedVocabulary(a, b) {
|
|
121
|
+
const theirs = new Set();
|
|
122
|
+
for (const text of b.texts)
|
|
123
|
+
if (text !== undefined)
|
|
124
|
+
theirs.add(text);
|
|
125
|
+
const counted = new Set();
|
|
126
|
+
for (const text of a.texts) {
|
|
127
|
+
if (text === undefined || counted.has(text))
|
|
128
|
+
continue;
|
|
129
|
+
if (theirs.has(text))
|
|
130
|
+
counted.add(text);
|
|
131
|
+
}
|
|
132
|
+
return counted.size;
|
|
133
|
+
}
|
|
134
|
+
//# sourceMappingURL=similar-pairs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"similar-pairs.js","sourceRoot":"","sources":["../../src/smells/similar-pairs.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,14 +1,8 @@
|
|
|
1
1
|
import type { ArchProject } from '../core/project.js';
|
|
2
|
-
import type {
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
* Extends TerminalBuilder for shared terminal methods (check/warn/excluding/because/rule).
|
|
7
|
-
*
|
|
8
|
-
* SmellBuilder does NOT extend RuleBuilder — smell detectors have a
|
|
9
|
-
* different chain grammar (no .that()/.should()) and execution model
|
|
10
|
-
* (pairwise comparison rather than individual element evaluation).
|
|
11
|
-
*/
|
|
2
|
+
import type { CollectResult } from '../core/terminal-builder.js';
|
|
3
|
+
import type { ArchViolation } from '@nielspeter/eess';
|
|
4
|
+
import type { GlobNode } from '@nielspeter/eess';
|
|
5
|
+
import { TerminalBuilder } from '../core/terminal-builder.js';
|
|
12
6
|
export declare abstract class SmellBuilder extends TerminalBuilder {
|
|
13
7
|
protected readonly project: ArchProject;
|
|
14
8
|
protected _folders: string[];
|
|
@@ -16,8 +10,27 @@ export declare abstract class SmellBuilder extends TerminalBuilder {
|
|
|
16
10
|
protected _ignoreTests: boolean;
|
|
17
11
|
protected _ignorePaths: string[];
|
|
18
12
|
protected _groupByFolder: boolean;
|
|
13
|
+
/**
|
|
14
|
+
* This family's own narrowing, named — plan 0099 / ADR-010 part 4.
|
|
15
|
+
*
|
|
16
|
+
* `_minLines` defaults to 5 and neither `agentGuardrails` nor `strictBoundaries`
|
|
17
|
+
* exposes a knob for it, so a user whose bodies are all shorter is told their
|
|
18
|
+
* rule enforces nothing by a filter they never wrote and cannot reach. Naming it
|
|
19
|
+
* is the difference between an actionable finding and one that sends an agent
|
|
20
|
+
* hunting for filters that do not exist.
|
|
21
|
+
*/
|
|
22
|
+
/** This family counts function bodies, not subjects — plan 0099. */
|
|
23
|
+
protected examinedUnitNoun(): string;
|
|
24
|
+
protected narrowingHint(): string | undefined;
|
|
19
25
|
constructor(project: ArchProject);
|
|
20
|
-
/**
|
|
26
|
+
/**
|
|
27
|
+
* Scope detection to files matching the glob pattern.
|
|
28
|
+
*
|
|
29
|
+
* Note the name: this matches the **whole file path**, not the directory
|
|
30
|
+
* portion (`passesFileFilters` applies the matcher to `sf.getFilePath()`).
|
|
31
|
+
* So its declared kind is `file-path`, which is why plan 0069 derives `kind`
|
|
32
|
+
* from the string a matcher is applied to rather than from the method name.
|
|
33
|
+
*/
|
|
21
34
|
inFolder(glob: string): this;
|
|
22
35
|
/** Ignore functions/files shorter than N lines. Default: 5. */
|
|
23
36
|
minLines(n: number): this;
|
|
@@ -25,12 +38,44 @@ export declare abstract class SmellBuilder extends TerminalBuilder {
|
|
|
25
38
|
ignoreTests(): this;
|
|
26
39
|
/** Exclude files matching the given glob patterns. */
|
|
27
40
|
ignorePaths(...globs: string[]): this;
|
|
41
|
+
/** The project this detector was built against. See `RuleBuilder.getProject`. */
|
|
42
|
+
getProject(): ArchProject;
|
|
43
|
+
/**
|
|
44
|
+
* The globs this detector was scoped with.
|
|
45
|
+
*
|
|
46
|
+
* `inFolder` is `discovery`: it defines the population to scan, so a glob
|
|
47
|
+
* matching nothing means the detector scans nothing. `ignorePaths` is
|
|
48
|
+
* `exclusion` and is never a fault — an exclusion matching zero files is
|
|
49
|
+
* remedy-optional (proposal 006).
|
|
50
|
+
*
|
|
51
|
+
* Repeated `inFolder()` calls OR together (`folderMatchers.some`), so they
|
|
52
|
+
* form one `any` node rather than one node each.
|
|
53
|
+
*/
|
|
54
|
+
globs(): readonly GlobNode[];
|
|
28
55
|
/** Group violation output by directory. */
|
|
29
56
|
groupByFolder(): this;
|
|
57
|
+
/**
|
|
58
|
+
* An independent copy, carrying both scope lists.
|
|
59
|
+
*
|
|
60
|
+
* Without this, `ignorePaths()` on a copy would push into the array the
|
|
61
|
+
* original still points at — and a leaked *ignore* is a false green: the
|
|
62
|
+
* next detector off the same held builder would silently skip those files.
|
|
63
|
+
*/
|
|
64
|
+
protected copy(): this;
|
|
30
65
|
/** Delegate to detect() for the terminal builder pipeline. */
|
|
31
|
-
protected collectViolations():
|
|
66
|
+
protected collectViolations(): CollectResult;
|
|
32
67
|
/** Subclasses implement: run detection, return violations. */
|
|
33
68
|
protected abstract detect(): ArchViolation[];
|
|
69
|
+
/**
|
|
70
|
+
* Subclasses implement: units this detector examined — plan 0098.
|
|
71
|
+
*
|
|
72
|
+
* Declared here rather than left to the subclasses because
|
|
73
|
+
* `collectViolations()` is the seam and it cannot see a hook the subclass
|
|
74
|
+
* merely happens to have. Plan 0096 added `examinedUnits()` to both detectors
|
|
75
|
+
* and this class could not reach either; a new detector would have compiled
|
|
76
|
+
* with no evidence at all.
|
|
77
|
+
*/
|
|
78
|
+
abstract examinedUnits(): number;
|
|
34
79
|
/** Subclasses implement: human-readable rule description. */
|
|
35
80
|
protected abstract describe(): string;
|
|
36
81
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"smell-builder.d.ts","sourceRoot":"","sources":["../../src/smells/smell-builder.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAA;AACrD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"smell-builder.d.ts","sourceRoot":"","sources":["../../src/smells/smell-builder.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAA;AACrD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAA;AAChE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAA;AACrD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAA;AAGhD,OAAO,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAA;AAc7D,8BAAsB,YAAa,SAAQ,eAAe;IAiD5C,SAAS,CAAC,QAAQ,CAAC,OAAO,EAAE,WAAW;IAhDnD,SAAS,CAAC,QAAQ,EAAE,MAAM,EAAE,CAAK;IACjC,SAAS,CAAC,SAAS,EAAE,MAAM,CAAoB;IAC/C,SAAS,CAAC,YAAY,UAAQ;IAC9B,SAAS,CAAC,YAAY,EAAE,MAAM,EAAE,CAAK;IACrC,SAAS,CAAC,cAAc,UAAQ;IAEhC;;;;;;;;OAQG;IACH,oEAAoE;cACjD,gBAAgB,IAAI,MAAM;cAI1B,aAAa,IAAI,MAAM,GAAG,SAAS;gBA4BvB,OAAO,EAAE,WAAW;IAInD;;;;;;;OAOG;IACH,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAM5B,+DAA+D;IAC/D,QAAQ,CAAC,CAAC,EAAE,MAAM,GAAG,IAAI;IAMzB,+DAA+D;IAC/D,WAAW,IAAI,IAAI;IAMnB,sDAAsD;IACtD,WAAW,CAAC,GAAG,KAAK,EAAE,MAAM,EAAE,GAAG,IAAI;IAMrC,iFAAiF;IACjF,UAAU,IAAI,WAAW;IAIzB;;;;;;;;;;OAUG;IACM,KAAK,IAAI,SAAS,QAAQ,EAAE;IAuBrC,2CAA2C;IAC3C,aAAa,IAAI,IAAI;IAMrB;;;;;;OAMG;cACgB,IAAI,IAAI,IAAI;IAO/B,8DAA8D;IAC9D,SAAS,CAAC,iBAAiB,IAAI,aAAa;IAI5C,8DAA8D;IAC9D,SAAS,CAAC,QAAQ,CAAC,MAAM,IAAI,aAAa,EAAE;IAE5C;;;;;;;;OAQG;IACH,QAAQ,CAAC,aAAa,IAAI,MAAM;IAEhC,6DAA6D;IAC7D,SAAS,CAAC,QAAQ,CAAC,QAAQ,IAAI,MAAM;CACtC"}
|
|
@@ -1,4 +1,7 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { stampGlobs } from '@nielspeter/eess/internal';
|
|
2
|
+
import { globAnyOf, collectResult } from '@nielspeter/eess';
|
|
3
|
+
import { TerminalBuilder } from '../core/terminal-builder.js';
|
|
4
|
+
import { isProjectRelative } from '../core/project-relative.js';
|
|
2
5
|
/**
|
|
3
6
|
* Base class for smell detector builders.
|
|
4
7
|
* Extends TerminalBuilder for shared terminal methods (check/warn/excluding/because/rule).
|
|
@@ -7,45 +10,140 @@ import { TerminalBuilder } from '@nielspeter/eess';
|
|
|
7
10
|
* different chain grammar (no .that()/.should()) and execution model
|
|
8
11
|
* (pairwise comparison rather than individual element evaluation).
|
|
9
12
|
*/
|
|
13
|
+
/** The `minLines` threshold applied when the author sets none. */
|
|
14
|
+
const DEFAULT_MIN_LINES = 5;
|
|
10
15
|
export class SmellBuilder extends TerminalBuilder {
|
|
11
16
|
project;
|
|
12
17
|
_folders = [];
|
|
13
|
-
_minLines =
|
|
18
|
+
_minLines = DEFAULT_MIN_LINES;
|
|
14
19
|
_ignoreTests = false;
|
|
15
20
|
_ignorePaths = [];
|
|
16
21
|
_groupByFolder = false;
|
|
22
|
+
/**
|
|
23
|
+
* This family's own narrowing, named — plan 0099 / ADR-010 part 4.
|
|
24
|
+
*
|
|
25
|
+
* `_minLines` defaults to 5 and neither `agentGuardrails` nor `strictBoundaries`
|
|
26
|
+
* exposes a knob for it, so a user whose bodies are all shorter is told their
|
|
27
|
+
* rule enforces nothing by a filter they never wrote and cannot reach. Naming it
|
|
28
|
+
* is the difference between an actionable finding and one that sends an agent
|
|
29
|
+
* hunting for filters that do not exist.
|
|
30
|
+
*/
|
|
31
|
+
/** This family counts function bodies, not subjects — plan 0099. */
|
|
32
|
+
examinedUnitNoun() {
|
|
33
|
+
return 'function bodies';
|
|
34
|
+
}
|
|
35
|
+
narrowingHint() {
|
|
36
|
+
const applied = [`minLines(${String(this._minLines)})`];
|
|
37
|
+
if (this._folders.length > 0)
|
|
38
|
+
applied.push(`inFolder(${this._folders.join(', ')})`);
|
|
39
|
+
if (this._ignorePaths.length > 0)
|
|
40
|
+
applied.push(`ignorePaths(${this._ignorePaths.join(', ')})`);
|
|
41
|
+
if (this._ignoreTests)
|
|
42
|
+
applied.push('ignoreTests()');
|
|
43
|
+
// The "you did not write it" clause is TRUE only while the value is still the
|
|
44
|
+
// default. Rendered with an author-written `.minLines(500)` it said
|
|
45
|
+
// "minLines(500) — minLines defaults to 5, a default you did not write",
|
|
46
|
+
// which is false on the path that produced it: an agent goes hunting for
|
|
47
|
+
// where 5 comes from and never touches the 500 that is the actual fault.
|
|
48
|
+
// ADR-009 rule 2, on an unsuppressable hard failure.
|
|
49
|
+
// States the filters as FACT, and does not claim they caused the emptiness.
|
|
50
|
+
//
|
|
51
|
+
// "Its own narrowing removed them" asserts removal. Measured on a types-only
|
|
52
|
+
// corpus with the default threshold: there were zero function bodies, so
|
|
53
|
+
// `minLines` removed nothing — the sentence named a false cause and pointed at
|
|
54
|
+
// a knob that `agentGuardrails` and `strictBoundaries` expose no setter for,
|
|
55
|
+
// on an unsuppressable hard failure. `narrowingHint()`'s own docstring
|
|
56
|
+
// promises the caller "names the possibility rather than asserting a cause it
|
|
57
|
+
// cannot verify"; this is the family honouring that rather than the base
|
|
58
|
+
// softening it afterwards.
|
|
59
|
+
const wroteItThemselves = this._minLines !== DEFAULT_MIN_LINES;
|
|
60
|
+
return wroteItThemselves
|
|
61
|
+
? `Its narrowing was: ${applied.join(', ')}.`
|
|
62
|
+
: `Its narrowing was: ${applied.join(', ')}` +
|
|
63
|
+
` — minLines defaults to ${String(DEFAULT_MIN_LINES)}, a default you did not write.`;
|
|
64
|
+
}
|
|
17
65
|
constructor(project) {
|
|
18
66
|
super();
|
|
19
67
|
this.project = project;
|
|
20
68
|
}
|
|
21
|
-
/**
|
|
69
|
+
/**
|
|
70
|
+
* Scope detection to files matching the glob pattern.
|
|
71
|
+
*
|
|
72
|
+
* Note the name: this matches the **whole file path**, not the directory
|
|
73
|
+
* portion (`passesFileFilters` applies the matcher to `sf.getFilePath()`).
|
|
74
|
+
* So its declared kind is `file-path`, which is why plan 0069 derives `kind`
|
|
75
|
+
* from the string a matcher is applied to rather than from the method name.
|
|
76
|
+
*/
|
|
22
77
|
inFolder(glob) {
|
|
23
|
-
this.
|
|
24
|
-
|
|
78
|
+
const next = this.copy();
|
|
79
|
+
next._folders.push(glob);
|
|
80
|
+
return next;
|
|
25
81
|
}
|
|
26
82
|
/** Ignore functions/files shorter than N lines. Default: 5. */
|
|
27
83
|
minLines(n) {
|
|
28
|
-
|
|
29
|
-
|
|
84
|
+
const next = this.copy();
|
|
85
|
+
next._minLines = n;
|
|
86
|
+
return next;
|
|
30
87
|
}
|
|
31
88
|
/** Exclude test files (*.test.ts, *.spec.ts, __tests__/**). */
|
|
32
89
|
ignoreTests() {
|
|
33
|
-
|
|
34
|
-
|
|
90
|
+
const next = this.copy();
|
|
91
|
+
next._ignoreTests = true;
|
|
92
|
+
return next;
|
|
35
93
|
}
|
|
36
94
|
/** Exclude files matching the given glob patterns. */
|
|
37
95
|
ignorePaths(...globs) {
|
|
38
|
-
this.
|
|
39
|
-
|
|
96
|
+
const next = this.copy();
|
|
97
|
+
next._ignorePaths.push(...globs);
|
|
98
|
+
return next;
|
|
99
|
+
}
|
|
100
|
+
/** The project this detector was built against. See `RuleBuilder.getProject`. */
|
|
101
|
+
getProject() {
|
|
102
|
+
return this.project;
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* The globs this detector was scoped with.
|
|
106
|
+
*
|
|
107
|
+
* `inFolder` is `discovery`: it defines the population to scan, so a glob
|
|
108
|
+
* matching nothing means the detector scans nothing. `ignorePaths` is
|
|
109
|
+
* `exclusion` and is never a fault — an exclusion matching zero files is
|
|
110
|
+
* remedy-optional (proposal 006).
|
|
111
|
+
*
|
|
112
|
+
* Repeated `inFolder()` calls OR together (`folderMatchers.some`), so they
|
|
113
|
+
* form one `any` node rather than one node each.
|
|
114
|
+
*/
|
|
115
|
+
globs() {
|
|
116
|
+
const trees = [];
|
|
117
|
+
if (this._folders.length > 0) {
|
|
118
|
+
trees.push(stampGlobs(globAnyOf(this._folders, 'file-path', this._folders.every((g) => isProjectRelative(g)) ? 'normalized' : 'absolute'), 'discovery', (g) => `inFolder("${g.glob}")`));
|
|
119
|
+
}
|
|
120
|
+
for (const glob of this._ignorePaths) {
|
|
121
|
+
trees.push(stampGlobs(globAnyOf([glob], 'file-path'), 'exclusion', (g) => `ignorePaths("${g.glob}")`));
|
|
122
|
+
}
|
|
123
|
+
return trees;
|
|
40
124
|
}
|
|
41
125
|
/** Group violation output by directory. */
|
|
42
126
|
groupByFolder() {
|
|
43
|
-
|
|
44
|
-
|
|
127
|
+
const next = this.copy();
|
|
128
|
+
next._groupByFolder = true;
|
|
129
|
+
return next;
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* An independent copy, carrying both scope lists.
|
|
133
|
+
*
|
|
134
|
+
* Without this, `ignorePaths()` on a copy would push into the array the
|
|
135
|
+
* original still points at — and a leaked *ignore* is a false green: the
|
|
136
|
+
* next detector off the same held builder would silently skip those files.
|
|
137
|
+
*/
|
|
138
|
+
copy() {
|
|
139
|
+
const clone = super.copy();
|
|
140
|
+
clone._folders = [...this._folders];
|
|
141
|
+
clone._ignorePaths = [...this._ignorePaths];
|
|
142
|
+
return clone;
|
|
45
143
|
}
|
|
46
144
|
/** Delegate to detect() for the terminal builder pipeline. */
|
|
47
145
|
collectViolations() {
|
|
48
|
-
return this.detect();
|
|
146
|
+
return collectResult(this.detect(), { examined: this.examinedUnits() });
|
|
49
147
|
}
|
|
50
148
|
}
|
|
51
149
|
//# sourceMappingURL=smell-builder.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"smell-builder.js","sourceRoot":"","sources":["../../src/smells/smell-builder.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"smell-builder.js","sourceRoot":"","sources":["../../src/smells/smell-builder.ts"],"names":[],"mappings":"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"}
|