@nielspeter/eess-ts 0.1.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/LICENSE +21 -0
- package/README.md +364 -0
- package/dist/builders/call-rule-builder.d.ts +199 -0
- package/dist/builders/call-rule-builder.d.ts.map +1 -0
- package/dist/builders/call-rule-builder.js +254 -0
- package/dist/builders/call-rule-builder.js.map +1 -0
- package/dist/builders/class-rule-builder.d.ts +136 -0
- package/dist/builders/class-rule-builder.d.ts.map +1 -0
- package/dist/builders/class-rule-builder.js +253 -0
- package/dist/builders/class-rule-builder.js.map +1 -0
- package/dist/builders/cross-layer-builder.d.ts +96 -0
- package/dist/builders/cross-layer-builder.d.ts.map +1 -0
- package/dist/builders/cross-layer-builder.js +172 -0
- package/dist/builders/cross-layer-builder.js.map +1 -0
- package/dist/builders/function-rule-builder.d.ts +198 -0
- package/dist/builders/function-rule-builder.d.ts.map +1 -0
- package/dist/builders/function-rule-builder.js +287 -0
- package/dist/builders/function-rule-builder.js.map +1 -0
- package/dist/builders/jsx-rule-builder.d.ts +113 -0
- package/dist/builders/jsx-rule-builder.d.ts.map +1 -0
- package/dist/builders/jsx-rule-builder.js +150 -0
- package/dist/builders/jsx-rule-builder.js.map +1 -0
- package/dist/builders/module-rule-builder.d.ts +159 -0
- package/dist/builders/module-rule-builder.d.ts.map +1 -0
- package/dist/builders/module-rule-builder.js +237 -0
- package/dist/builders/module-rule-builder.js.map +1 -0
- package/dist/builders/scoped-function-rule-builder.d.ts +21 -0
- package/dist/builders/scoped-function-rule-builder.d.ts.map +1 -0
- package/dist/builders/scoped-function-rule-builder.js +26 -0
- package/dist/builders/scoped-function-rule-builder.js.map +1 -0
- package/dist/builders/slice-rule-builder.d.ts +85 -0
- package/dist/builders/slice-rule-builder.d.ts.map +1 -0
- package/dist/builders/slice-rule-builder.js +134 -0
- package/dist/builders/slice-rule-builder.js.map +1 -0
- package/dist/builders/type-rule-builder.d.ts +103 -0
- package/dist/builders/type-rule-builder.d.ts.map +1 -0
- package/dist/builders/type-rule-builder.js +170 -0
- package/dist/builders/type-rule-builder.js.map +1 -0
- package/dist/cli/bin.d.ts +3 -0
- package/dist/cli/bin.d.ts.map +1 -0
- package/dist/cli/bin.js +4 -0
- package/dist/cli/bin.js.map +1 -0
- package/dist/cli/commands/baseline.d.ts +11 -0
- package/dist/cli/commands/baseline.d.ts.map +1 -0
- package/dist/cli/commands/baseline.js +16 -0
- package/dist/cli/commands/baseline.js.map +1 -0
- package/dist/cli/commands/check.d.ts +21 -0
- package/dist/cli/commands/check.d.ts.map +1 -0
- package/dist/cli/commands/check.js +94 -0
- package/dist/cli/commands/check.js.map +1 -0
- package/dist/cli/commands/explain.d.ts +9 -0
- package/dist/cli/commands/explain.d.ts.map +1 -0
- package/dist/cli/commands/explain.js +49 -0
- package/dist/cli/commands/explain.js.map +1 -0
- package/dist/cli/config.d.ts +31 -0
- package/dist/cli/config.d.ts.map +1 -0
- package/dist/cli/config.js +20 -0
- package/dist/cli/config.js.map +1 -0
- package/dist/cli/index.d.ts +21 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +168 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/load-rules.d.ts +20 -0
- package/dist/cli/load-rules.d.ts.map +1 -0
- package/dist/cli/load-rules.js +97 -0
- package/dist/cli/load-rules.js.map +1 -0
- package/dist/cli/resolve-config.d.ts +11 -0
- package/dist/cli/resolve-config.d.ts.map +1 -0
- package/dist/cli/resolve-config.js +61 -0
- package/dist/cli/resolve-config.js.map +1 -0
- package/dist/cli/watch.d.ts +49 -0
- package/dist/cli/watch.d.ts.map +1 -0
- package/dist/cli/watch.js +138 -0
- package/dist/cli/watch.js.map +1 -0
- package/dist/conditions/body-analysis-function.d.ts +23 -0
- package/dist/conditions/body-analysis-function.d.ts.map +1 -0
- package/dist/conditions/body-analysis-function.js +103 -0
- package/dist/conditions/body-analysis-function.js.map +1 -0
- package/dist/conditions/body-analysis-module.d.ts +27 -0
- package/dist/conditions/body-analysis-module.d.ts.map +1 -0
- package/dist/conditions/body-analysis-module.js +99 -0
- package/dist/conditions/body-analysis-module.js.map +1 -0
- package/dist/conditions/body-analysis.d.ts +34 -0
- package/dist/conditions/body-analysis.d.ts.map +1 -0
- package/dist/conditions/body-analysis.js +95 -0
- package/dist/conditions/body-analysis.js.map +1 -0
- package/dist/conditions/call.d.ts +62 -0
- package/dist/conditions/call.d.ts.map +1 -0
- package/dist/conditions/call.js +267 -0
- package/dist/conditions/call.js.map +1 -0
- package/dist/conditions/catch-analysis.d.ts +31 -0
- package/dist/conditions/catch-analysis.d.ts.map +1 -0
- package/dist/conditions/catch-analysis.js +78 -0
- package/dist/conditions/catch-analysis.js.map +1 -0
- package/dist/conditions/class.d.ts +33 -0
- package/dist/conditions/class.d.ts.map +1 -0
- package/dist/conditions/class.js +110 -0
- package/dist/conditions/class.js.map +1 -0
- package/dist/conditions/cross-layer.d.ts +28 -0
- package/dist/conditions/cross-layer.d.ts.map +1 -0
- package/dist/conditions/cross-layer.js +105 -0
- package/dist/conditions/cross-layer.js.map +1 -0
- package/dist/conditions/dependency.d.ts +73 -0
- package/dist/conditions/dependency.d.ts.map +1 -0
- package/dist/conditions/dependency.js +177 -0
- package/dist/conditions/dependency.js.map +1 -0
- package/dist/conditions/exports.d.ts +33 -0
- package/dist/conditions/exports.d.ts.map +1 -0
- package/dist/conditions/exports.js +101 -0
- package/dist/conditions/exports.js.map +1 -0
- package/dist/conditions/function.d.ts +79 -0
- package/dist/conditions/function.d.ts.map +1 -0
- package/dist/conditions/function.js +167 -0
- package/dist/conditions/function.js.map +1 -0
- package/dist/conditions/helpers.d.ts +14 -0
- package/dist/conditions/helpers.d.ts.map +1 -0
- package/dist/conditions/helpers.js +26 -0
- package/dist/conditions/helpers.js.map +1 -0
- package/dist/conditions/jsx.d.ts +40 -0
- package/dist/conditions/jsx.d.ts.map +1 -0
- package/dist/conditions/jsx.js +133 -0
- package/dist/conditions/jsx.js.map +1 -0
- package/dist/conditions/members.d.ts +74 -0
- package/dist/conditions/members.d.ts.map +1 -0
- package/dist/conditions/members.js +224 -0
- package/dist/conditions/members.js.map +1 -0
- package/dist/conditions/pattern.d.ts +20 -0
- package/dist/conditions/pattern.d.ts.map +1 -0
- package/dist/conditions/pattern.js +108 -0
- package/dist/conditions/pattern.js.map +1 -0
- package/dist/conditions/reverse-dependency.d.ts +28 -0
- package/dist/conditions/reverse-dependency.d.ts.map +1 -0
- package/dist/conditions/reverse-dependency.js +275 -0
- package/dist/conditions/reverse-dependency.js.map +1 -0
- package/dist/conditions/slice.d.ts +49 -0
- package/dist/conditions/slice.d.ts.map +1 -0
- package/dist/conditions/slice.js +153 -0
- package/dist/conditions/slice.js.map +1 -0
- package/dist/conditions/structural.d.ts +58 -0
- package/dist/conditions/structural.d.ts.map +1 -0
- package/dist/conditions/structural.js +102 -0
- package/dist/conditions/structural.js.map +1 -0
- package/dist/conditions/type-level.d.ts +29 -0
- package/dist/conditions/type-level.d.ts.map +1 -0
- package/dist/conditions/type-level.js +48 -0
- package/dist/conditions/type-level.js.map +1 -0
- package/dist/core/import-options.d.ts +32 -0
- package/dist/core/import-options.d.ts.map +1 -0
- package/dist/core/import-options.js +29 -0
- package/dist/core/import-options.js.map +1 -0
- package/dist/core/module-dependencies.d.ts +28 -0
- package/dist/core/module-dependencies.d.ts.map +1 -0
- package/dist/core/module-dependencies.js +35 -0
- package/dist/core/module-dependencies.js.map +1 -0
- package/dist/core/pair-condition.d.ts +10 -0
- package/dist/core/pair-condition.d.ts.map +1 -0
- package/dist/core/pair-condition.js +2 -0
- package/dist/core/pair-condition.js.map +1 -0
- package/dist/core/project.d.ts +68 -0
- package/dist/core/project.d.ts.map +1 -0
- package/dist/core/project.js +127 -0
- package/dist/core/project.js.map +1 -0
- package/dist/core/type-matcher.d.ts +10 -0
- package/dist/core/type-matcher.d.ts.map +1 -0
- package/dist/core/type-matcher.js +2 -0
- package/dist/core/type-matcher.js.map +1 -0
- package/dist/core/violation.d.ts +38 -0
- package/dist/core/violation.d.ts.map +1 -0
- package/dist/core/violation.js +130 -0
- package/dist/core/violation.js.map +1 -0
- package/dist/graphql/index.d.ts +76 -0
- package/dist/graphql/index.d.ts.map +1 -0
- package/dist/graphql/index.js +108 -0
- package/dist/graphql/index.js.map +1 -0
- package/dist/graphql/resolver-rule-builder.d.ts +75 -0
- package/dist/graphql/resolver-rule-builder.d.ts.map +1 -0
- package/dist/graphql/resolver-rule-builder.js +146 -0
- package/dist/graphql/resolver-rule-builder.js.map +1 -0
- package/dist/graphql/schema-conditions.d.ts +31 -0
- package/dist/graphql/schema-conditions.d.ts.map +1 -0
- package/dist/graphql/schema-conditions.js +119 -0
- package/dist/graphql/schema-conditions.js.map +1 -0
- package/dist/graphql/schema-loader.d.ts +75 -0
- package/dist/graphql/schema-loader.d.ts.map +1 -0
- package/dist/graphql/schema-loader.js +113 -0
- package/dist/graphql/schema-loader.js.map +1 -0
- package/dist/graphql/schema-predicates.d.ts +42 -0
- package/dist/graphql/schema-predicates.d.ts.map +1 -0
- package/dist/graphql/schema-predicates.js +74 -0
- package/dist/graphql/schema-predicates.js.map +1 -0
- package/dist/graphql/schema-rule-builder.d.ts +74 -0
- package/dist/graphql/schema-rule-builder.d.ts.map +1 -0
- package/dist/graphql/schema-rule-builder.js +188 -0
- package/dist/graphql/schema-rule-builder.js.map +1 -0
- package/dist/helpers/body-traversal.d.ts +58 -0
- package/dist/helpers/body-traversal.d.ts.map +1 -0
- package/dist/helpers/body-traversal.js +180 -0
- package/dist/helpers/body-traversal.js.map +1 -0
- package/dist/helpers/callback-extractor.d.ts +28 -0
- package/dist/helpers/callback-extractor.d.ts.map +1 -0
- package/dist/helpers/callback-extractor.js +159 -0
- package/dist/helpers/callback-extractor.js.map +1 -0
- package/dist/helpers/complexity.d.ts +26 -0
- package/dist/helpers/complexity.d.ts.map +1 -0
- package/dist/helpers/complexity.js +64 -0
- package/dist/helpers/complexity.js.map +1 -0
- package/dist/helpers/matchers.d.ts +206 -0
- package/dist/helpers/matchers.d.ts.map +1 -0
- package/dist/helpers/matchers.js +470 -0
- package/dist/helpers/matchers.js.map +1 -0
- package/dist/helpers/pattern.d.ts +37 -0
- package/dist/helpers/pattern.d.ts.map +1 -0
- package/dist/helpers/pattern.js +22 -0
- package/dist/helpers/pattern.js.map +1 -0
- package/dist/helpers/slice-graph.d.ts +32 -0
- package/dist/helpers/slice-graph.d.ts.map +1 -0
- package/dist/helpers/slice-graph.js +89 -0
- package/dist/helpers/slice-graph.js.map +1 -0
- package/dist/helpers/tarjan.d.ts +21 -0
- package/dist/helpers/tarjan.d.ts.map +1 -0
- package/dist/helpers/tarjan.js +71 -0
- package/dist/helpers/tarjan.js.map +1 -0
- package/dist/helpers/type-matchers.d.ts +67 -0
- package/dist/helpers/type-matchers.d.ts.map +1 -0
- package/dist/helpers/type-matchers.js +108 -0
- package/dist/helpers/type-matchers.js.map +1 -0
- package/dist/helpers/within.d.ts +41 -0
- package/dist/helpers/within.d.ts.map +1 -0
- package/dist/helpers/within.js +32 -0
- package/dist/helpers/within.js.map +1 -0
- package/dist/index.d.ts +101 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +116 -0
- package/dist/index.js.map +1 -0
- package/dist/models/arch-call.d.ts +83 -0
- package/dist/models/arch-call.d.ts.map +1 -0
- package/dist/models/arch-call.js +90 -0
- package/dist/models/arch-call.js.map +1 -0
- package/dist/models/arch-function.d.ts +76 -0
- package/dist/models/arch-function.d.ts.map +1 -0
- package/dist/models/arch-function.js +122 -0
- package/dist/models/arch-function.js.map +1 -0
- package/dist/models/arch-jsx-element.d.ts +75 -0
- package/dist/models/arch-jsx-element.d.ts.map +1 -0
- package/dist/models/arch-jsx-element.js +231 -0
- package/dist/models/arch-jsx-element.js.map +1 -0
- package/dist/models/cross-layer.d.ts +15 -0
- package/dist/models/cross-layer.d.ts.map +1 -0
- package/dist/models/cross-layer.js +2 -0
- package/dist/models/cross-layer.js.map +1 -0
- package/dist/models/slice.d.ts +60 -0
- package/dist/models/slice.d.ts.map +1 -0
- package/dist/models/slice.js +89 -0
- package/dist/models/slice.js.map +1 -0
- package/dist/predicates/call.d.ts +49 -0
- package/dist/predicates/call.d.ts.map +1 -0
- package/dist/predicates/call.js +115 -0
- package/dist/predicates/call.js.map +1 -0
- package/dist/predicates/class.d.ts +42 -0
- package/dist/predicates/class.d.ts.map +1 -0
- package/dist/predicates/class.js +83 -0
- package/dist/predicates/class.js.map +1 -0
- package/dist/predicates/function.d.ts +81 -0
- package/dist/predicates/function.d.ts.map +1 -0
- package/dist/predicates/function.js +157 -0
- package/dist/predicates/function.js.map +1 -0
- package/dist/predicates/identity.d.ts +55 -0
- package/dist/predicates/identity.d.ts.map +1 -0
- package/dist/predicates/identity.js +96 -0
- package/dist/predicates/identity.js.map +1 -0
- package/dist/predicates/index.d.ts +4 -0
- package/dist/predicates/index.d.ts.map +1 -0
- package/dist/predicates/index.js +3 -0
- package/dist/predicates/index.js.map +1 -0
- package/dist/predicates/jsx.d.ts +41 -0
- package/dist/predicates/jsx.d.ts.map +1 -0
- package/dist/predicates/jsx.js +84 -0
- package/dist/predicates/jsx.js.map +1 -0
- package/dist/predicates/metrics.d.ts +30 -0
- package/dist/predicates/metrics.d.ts.map +1 -0
- package/dist/predicates/metrics.js +65 -0
- package/dist/predicates/metrics.js.map +1 -0
- package/dist/predicates/module.d.ts +44 -0
- package/dist/predicates/module.d.ts.map +1 -0
- package/dist/predicates/module.js +70 -0
- package/dist/predicates/module.js.map +1 -0
- package/dist/predicates/type.d.ts +48 -0
- package/dist/predicates/type.d.ts.map +1 -0
- package/dist/predicates/type.js +107 -0
- package/dist/predicates/type.js.map +1 -0
- package/dist/presets/boundaries.d.ts +19 -0
- package/dist/presets/boundaries.d.ts.map +1 -0
- package/dist/presets/boundaries.js +93 -0
- package/dist/presets/boundaries.js.map +1 -0
- package/dist/presets/data-layer.d.ts +20 -0
- package/dist/presets/data-layer.d.ts.map +1 -0
- package/dist/presets/data-layer.js +30 -0
- package/dist/presets/data-layer.js.map +1 -0
- package/dist/presets/index.d.ts +9 -0
- package/dist/presets/index.d.ts.map +1 -0
- package/dist/presets/index.js +5 -0
- package/dist/presets/index.js.map +1 -0
- package/dist/presets/layered.d.ts +21 -0
- package/dist/presets/layered.d.ts.map +1 -0
- package/dist/presets/layered.js +103 -0
- package/dist/presets/layered.js.map +1 -0
- package/dist/presets/shared.d.ts +3 -0
- package/dist/presets/shared.d.ts.map +1 -0
- package/dist/presets/shared.js +5 -0
- package/dist/presets/shared.js.map +1 -0
- package/dist/rules/architecture.d.ts +10 -0
- package/dist/rules/architecture.d.ts.map +1 -0
- package/dist/rules/architecture.js +14 -0
- package/dist/rules/architecture.js.map +1 -0
- package/dist/rules/code-quality.d.ts +55 -0
- package/dist/rules/code-quality.d.ts.map +1 -0
- package/dist/rules/code-quality.js +116 -0
- package/dist/rules/code-quality.js.map +1 -0
- package/dist/rules/dependencies.d.ts +32 -0
- package/dist/rules/dependencies.d.ts.map +1 -0
- package/dist/rules/dependencies.js +37 -0
- package/dist/rules/dependencies.js.map +1 -0
- package/dist/rules/errors.d.ts +31 -0
- package/dist/rules/errors.d.ts.map +1 -0
- package/dist/rules/errors.js +97 -0
- package/dist/rules/errors.js.map +1 -0
- package/dist/rules/hygiene.d.ts +54 -0
- package/dist/rules/hygiene.d.ts.map +1 -0
- package/dist/rules/hygiene.js +62 -0
- package/dist/rules/hygiene.js.map +1 -0
- package/dist/rules/metrics-function.d.ts +45 -0
- package/dist/rules/metrics-function.d.ts.map +1 -0
- package/dist/rules/metrics-function.js +87 -0
- package/dist/rules/metrics-function.js.map +1 -0
- package/dist/rules/metrics.d.ts +64 -0
- package/dist/rules/metrics.d.ts.map +1 -0
- package/dist/rules/metrics.js +157 -0
- package/dist/rules/metrics.js.map +1 -0
- package/dist/rules/naming.d.ts +24 -0
- package/dist/rules/naming.d.ts.map +1 -0
- package/dist/rules/naming.js +51 -0
- package/dist/rules/naming.js.map +1 -0
- package/dist/rules/security.d.ts +59 -0
- package/dist/rules/security.d.ts.map +1 -0
- package/dist/rules/security.js +92 -0
- package/dist/rules/security.js.map +1 -0
- package/dist/rules/typescript.d.ts +73 -0
- package/dist/rules/typescript.d.ts.map +1 -0
- package/dist/rules/typescript.js +105 -0
- package/dist/rules/typescript.js.map +1 -0
- package/dist/smells/duplicate-bodies.d.ts +24 -0
- package/dist/smells/duplicate-bodies.d.ts.map +1 -0
- package/dist/smells/duplicate-bodies.js +131 -0
- package/dist/smells/duplicate-bodies.js.map +1 -0
- package/dist/smells/fingerprint.d.ts +27 -0
- package/dist/smells/fingerprint.d.ts.map +1 -0
- package/dist/smells/fingerprint.js +50 -0
- package/dist/smells/fingerprint.js.map +1 -0
- package/dist/smells/inconsistent-siblings.d.ts +21 -0
- package/dist/smells/inconsistent-siblings.d.ts.map +1 -0
- package/dist/smells/inconsistent-siblings.js +135 -0
- package/dist/smells/inconsistent-siblings.js.map +1 -0
- package/dist/smells/index.d.ts +12 -0
- package/dist/smells/index.d.ts.map +1 -0
- package/dist/smells/index.js +15 -0
- package/dist/smells/index.js.map +1 -0
- package/dist/smells/smell-builder.d.ts +37 -0
- package/dist/smells/smell-builder.d.ts.map +1 -0
- package/dist/smells/smell-builder.js +51 -0
- package/dist/smells/smell-builder.js.map +1 -0
- package/package.json +121 -0
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
import { Node, } from 'ts-morph';
|
|
2
|
+
import { createViolation, getElementName } from '../core/violation.js';
|
|
3
|
+
import { elementCondition } from './helpers.js';
|
|
4
|
+
/**
|
|
5
|
+
* Extract property symbols from a property-bearing node.
|
|
6
|
+
* Resolves through the type system (handles Partial<>, Pick<>, etc.).
|
|
7
|
+
*/
|
|
8
|
+
function getPropertySymbols(node) {
|
|
9
|
+
return node.getType().getProperties();
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Check if a property symbol is readonly.
|
|
13
|
+
*
|
|
14
|
+
* Strategy:
|
|
15
|
+
* 1. Check direct declarations — works for explicit `readonly` on
|
|
16
|
+
* interface properties and class properties.
|
|
17
|
+
* 2. For mapped types like Readonly<T>, declarations point to the
|
|
18
|
+
* source type where isReadonly() returns false. Fall back to the
|
|
19
|
+
* TypeScript compiler's internal CheckFlags.Readonly (bit 3 = 8)
|
|
20
|
+
* on the symbol's links.
|
|
21
|
+
*
|
|
22
|
+
* Spike test confirmed: for Readonly<T>, Symbol.getDeclarations()
|
|
23
|
+
* returns the inner type's declarations (not readonly), but
|
|
24
|
+
* compilerSymbol.links.checkFlags has the Readonly bit set (8).
|
|
25
|
+
*/
|
|
26
|
+
function isPropertyReadonly(prop) {
|
|
27
|
+
// Strategy 1: Check direct declarations for explicit `readonly` keyword.
|
|
28
|
+
// Works for interface properties and class properties that are directly
|
|
29
|
+
// declared with the readonly modifier.
|
|
30
|
+
const decls = prop.getDeclarations();
|
|
31
|
+
for (const d of decls) {
|
|
32
|
+
if (Node.isPropertySignature(d) || Node.isPropertyDeclaration(d)) {
|
|
33
|
+
if (d.isReadonly())
|
|
34
|
+
return true;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
// Strategy 2: Check TS compiler's CheckFlags.Readonly (bit 3 = 8).
|
|
38
|
+
// For mapped types like Readonly<T>, Symbol.getDeclarations() returns
|
|
39
|
+
// the *original* type's declarations where isReadonly() is false,
|
|
40
|
+
// but the compiler tracks the readonly modifier on the symbol itself.
|
|
41
|
+
//
|
|
42
|
+
// Spike results (ts-morph 27 / TS ~5.9):
|
|
43
|
+
// Readonly<{a: string}>.a → decl.isReadonly() = false, but
|
|
44
|
+
// compilerSymbol.links.checkFlags & 8 = 8 (readonly)
|
|
45
|
+
//
|
|
46
|
+
// eess-exclude eess/adr005-no-type-assertions: reaches TS compiler-internal Symbol.links (not on the public ts.Symbol type) to read CheckFlags for mapped-type readonly detection
|
|
47
|
+
const links = prop.compilerSymbol.links;
|
|
48
|
+
if (typeof links === 'object' && links !== null && 'checkFlags' in links) {
|
|
49
|
+
const flags = links.checkFlags;
|
|
50
|
+
if (typeof flags === 'number' && (flags & 8) !== 0) {
|
|
51
|
+
return true;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
// Conservative: treat as mutable if neither strategy detected readonly.
|
|
55
|
+
return false;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Assert that all named properties exist on the element.
|
|
59
|
+
* Every name must be present — violation per missing name.
|
|
60
|
+
*
|
|
61
|
+
* @param names At least one property name required. Throws if called with zero arguments.
|
|
62
|
+
*
|
|
63
|
+
* @example
|
|
64
|
+
* types(p).that().haveNameMatching(/Config$/)
|
|
65
|
+
* .should().havePropertyNamed('version', 'name')
|
|
66
|
+
* .check()
|
|
67
|
+
*/
|
|
68
|
+
export function havePropertyNamed(...names) {
|
|
69
|
+
if (names.length === 0) {
|
|
70
|
+
throw new Error('havePropertyNamed() requires at least one property name');
|
|
71
|
+
}
|
|
72
|
+
const quotedNames = names.map((n) => `"${n}"`).join(', ');
|
|
73
|
+
return {
|
|
74
|
+
description: `have properties named ${quotedNames}`,
|
|
75
|
+
evaluate(elements, context) {
|
|
76
|
+
const violations = [];
|
|
77
|
+
for (const element of elements) {
|
|
78
|
+
const props = getPropertySymbols(element);
|
|
79
|
+
const propNames = new Set(props.map((p) => p.getName()));
|
|
80
|
+
for (const name of names) {
|
|
81
|
+
if (!propNames.has(name)) {
|
|
82
|
+
violations.push(createViolation(element, `${getElementName(element)} is missing required property "${name}"`, context));
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
return violations;
|
|
87
|
+
},
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Assert that none of the named properties exist on the element.
|
|
92
|
+
* Violation per element per forbidden name found.
|
|
93
|
+
*
|
|
94
|
+
* @param names At least one property name required. Throws if called with zero arguments.
|
|
95
|
+
*
|
|
96
|
+
* @example
|
|
97
|
+
* types(p).that().resideInFolder('src/api')
|
|
98
|
+
* .should().notHavePropertyNamed('offset', 'pageSize', 'page', 'size')
|
|
99
|
+
* .because('use skip/limit for pagination')
|
|
100
|
+
* .check()
|
|
101
|
+
*/
|
|
102
|
+
export function notHavePropertyNamed(...names) {
|
|
103
|
+
if (names.length === 0) {
|
|
104
|
+
throw new Error('notHavePropertyNamed() requires at least one property name');
|
|
105
|
+
}
|
|
106
|
+
const nameSet = new Set(names);
|
|
107
|
+
const quotedNames = names.map((n) => `"${n}"`).join(', ');
|
|
108
|
+
return {
|
|
109
|
+
description: `not have properties named ${quotedNames}`,
|
|
110
|
+
evaluate(elements, context) {
|
|
111
|
+
const violations = [];
|
|
112
|
+
for (const element of elements) {
|
|
113
|
+
const props = getPropertySymbols(element);
|
|
114
|
+
for (const prop of props) {
|
|
115
|
+
if (nameSet.has(prop.getName())) {
|
|
116
|
+
violations.push(createViolation(element, `${getElementName(element)} has forbidden property "${prop.getName()}"`, context));
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
return violations;
|
|
121
|
+
},
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Assert at least one property name matches the regex.
|
|
126
|
+
* Semantics: EXISTS — violation if *no* property name matches.
|
|
127
|
+
*
|
|
128
|
+
* Note: this checks for existence of at least one match, unlike
|
|
129
|
+
* `havePropertyNamed(...names)` which requires ALL names to exist.
|
|
130
|
+
*
|
|
131
|
+
* @example
|
|
132
|
+
* types(p).that().haveNameMatching(/Entity$/)
|
|
133
|
+
* .should().havePropertyMatching(/^id$/)
|
|
134
|
+
* .check()
|
|
135
|
+
*/
|
|
136
|
+
export function havePropertyMatching(pattern) {
|
|
137
|
+
return elementCondition(`have a property matching ${String(pattern)}`, (element) => {
|
|
138
|
+
// Clone regex to avoid /g flag statefulness across calls
|
|
139
|
+
const re = new RegExp(pattern.source, pattern.flags);
|
|
140
|
+
const props = getPropertySymbols(element);
|
|
141
|
+
return props.some((p) => re.test(p.getName()));
|
|
142
|
+
}, (element) => `${getElementName(element)} has no property matching ${String(pattern)}`);
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Assert no property name matches the regex.
|
|
146
|
+
* Violation per matching property found.
|
|
147
|
+
*
|
|
148
|
+
* @example
|
|
149
|
+
* types(p).should().notHavePropertyMatching(/^(data|info|stuff|item)$/).check()
|
|
150
|
+
*/
|
|
151
|
+
export function notHavePropertyMatching(pattern) {
|
|
152
|
+
return {
|
|
153
|
+
description: `not have properties matching ${String(pattern)}`,
|
|
154
|
+
evaluate(elements, context) {
|
|
155
|
+
const violations = [];
|
|
156
|
+
for (const element of elements) {
|
|
157
|
+
// Clone regex to avoid /g flag statefulness across elements
|
|
158
|
+
const re = new RegExp(pattern.source, pattern.flags);
|
|
159
|
+
const props = getPropertySymbols(element);
|
|
160
|
+
for (const prop of props) {
|
|
161
|
+
if (re.test(prop.getName())) {
|
|
162
|
+
violations.push(createViolation(element, `${getElementName(element)} has property "${prop.getName()}" matching ${String(pattern)}`, context));
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
return violations;
|
|
167
|
+
},
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Assert all properties are readonly.
|
|
172
|
+
*
|
|
173
|
+
* Checks direct declarations for explicit `readonly` keyword, and
|
|
174
|
+
* falls back to TypeScript compiler's CheckFlags for mapped types
|
|
175
|
+
* like Readonly<T>.
|
|
176
|
+
*
|
|
177
|
+
* @example
|
|
178
|
+
* types(p).that().resideInFolder('src/state')
|
|
179
|
+
* .should().haveOnlyReadonlyProperties()
|
|
180
|
+
* .because('state objects must be immutable')
|
|
181
|
+
* .check()
|
|
182
|
+
*/
|
|
183
|
+
export function haveOnlyReadonlyProperties() {
|
|
184
|
+
return {
|
|
185
|
+
description: 'have only readonly properties',
|
|
186
|
+
evaluate(elements, context) {
|
|
187
|
+
const violations = [];
|
|
188
|
+
for (const element of elements) {
|
|
189
|
+
const props = getPropertySymbols(element);
|
|
190
|
+
for (const prop of props) {
|
|
191
|
+
if (!isPropertyReadonly(prop)) {
|
|
192
|
+
violations.push(createViolation(element, `${getElementName(element)} has mutable property "${prop.getName()}"`, context));
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
return violations;
|
|
197
|
+
},
|
|
198
|
+
};
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* Assert property count does not exceed the maximum.
|
|
202
|
+
* Detects god objects / oversized DTOs.
|
|
203
|
+
*
|
|
204
|
+
* @example
|
|
205
|
+
* types(p).should().maxProperties(15)
|
|
206
|
+
* .because('large interfaces indicate a missing abstraction')
|
|
207
|
+
* .check()
|
|
208
|
+
*/
|
|
209
|
+
export function maxProperties(max) {
|
|
210
|
+
return {
|
|
211
|
+
description: `have at most ${String(max)} properties`,
|
|
212
|
+
evaluate(elements, context) {
|
|
213
|
+
const violations = [];
|
|
214
|
+
for (const element of elements) {
|
|
215
|
+
const count = getPropertySymbols(element).length;
|
|
216
|
+
if (count > max) {
|
|
217
|
+
violations.push(createViolation(element, `${getElementName(element)} has ${String(count)} properties, max allowed is ${String(max)}`, context));
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
return violations;
|
|
221
|
+
},
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
//# sourceMappingURL=members.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"members.js","sourceRoot":"","sources":["../../src/conditions/members.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { Condition } from '@nielspeter/eess';
|
|
2
|
+
import type { ArchPattern } from '../helpers/pattern.js';
|
|
3
|
+
import type { ArchFunction } from '../models/arch-function.js';
|
|
4
|
+
/**
|
|
5
|
+
* Condition: functions must return a type matching the pattern's returnShape.
|
|
6
|
+
*
|
|
7
|
+
* For each property in `pattern.returnShape`:
|
|
8
|
+
* 1. The return type must have a property with that name.
|
|
9
|
+
* 2. The property's type must satisfy the constraint.
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* ```ts
|
|
13
|
+
* functions(p)
|
|
14
|
+
* .that().resideInFolder('src/routes/**')
|
|
15
|
+
* .should().followPattern(paginatedCollection)
|
|
16
|
+
* .check()
|
|
17
|
+
* ```
|
|
18
|
+
*/
|
|
19
|
+
export declare function followPattern(pattern: ArchPattern): Condition<ArchFunction>;
|
|
20
|
+
//# sourceMappingURL=pattern.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pattern.d.ts","sourceRoot":"","sources":["../../src/conditions/pattern.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAoB,MAAM,kBAAkB,CAAA;AAEnE,OAAO,KAAK,EAAE,WAAW,EAAsB,MAAM,uBAAuB,CAAA;AAC5E,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAA;AAG9D;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,WAAW,GAAG,SAAS,CAAC,YAAY,CAAC,CA6B3E"}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Condition: functions must return a type matching the pattern's returnShape.
|
|
3
|
+
*
|
|
4
|
+
* For each property in `pattern.returnShape`:
|
|
5
|
+
* 1. The return type must have a property with that name.
|
|
6
|
+
* 2. The property's type must satisfy the constraint.
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* ```ts
|
|
10
|
+
* functions(p)
|
|
11
|
+
* .that().resideInFolder('src/routes/**')
|
|
12
|
+
* .should().followPattern(paginatedCollection)
|
|
13
|
+
* .check()
|
|
14
|
+
* ```
|
|
15
|
+
*/
|
|
16
|
+
export function followPattern(pattern) {
|
|
17
|
+
return {
|
|
18
|
+
description: `follow pattern "${pattern.name}"`,
|
|
19
|
+
evaluate(elements, context) {
|
|
20
|
+
const violations = [];
|
|
21
|
+
for (const fn of elements) {
|
|
22
|
+
const returnType = fn.getReturnType();
|
|
23
|
+
// Unwrap Promise<T> to inspect the resolved type
|
|
24
|
+
const resolvedType = unwrapPromise(returnType);
|
|
25
|
+
const missing = getMissingProperties(resolvedType, pattern);
|
|
26
|
+
if (missing.length > 0) {
|
|
27
|
+
const fnName = fn.getName() ?? '<anonymous>';
|
|
28
|
+
const node = fn.getNode();
|
|
29
|
+
violations.push({
|
|
30
|
+
rule: context.rule,
|
|
31
|
+
element: fnName,
|
|
32
|
+
file: node.getSourceFile().getFilePath(),
|
|
33
|
+
line: fn.getStartLineNumber(),
|
|
34
|
+
message: `"${fnName}" does not follow pattern "${pattern.name}": ${missing.join('; ')}`,
|
|
35
|
+
because: context.because,
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
return violations;
|
|
40
|
+
},
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Unwrap Promise<T> -> T so async functions can be checked against
|
|
45
|
+
* the same pattern as sync ones.
|
|
46
|
+
*/
|
|
47
|
+
function unwrapPromise(type) {
|
|
48
|
+
const typeText = type.getText();
|
|
49
|
+
if (typeText.startsWith('Promise<')) {
|
|
50
|
+
const typeArgs = type.getTypeArguments();
|
|
51
|
+
if (typeArgs.length === 1 && typeArgs[0] !== undefined) {
|
|
52
|
+
return typeArgs[0];
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
return type;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Check each property constraint in the pattern against the resolved type.
|
|
59
|
+
* Returns human-readable descriptions of missing/mismatched properties.
|
|
60
|
+
*/
|
|
61
|
+
function getMissingProperties(type, pattern) {
|
|
62
|
+
const problems = [];
|
|
63
|
+
for (const [propName, constraint] of Object.entries(pattern.returnShape)) {
|
|
64
|
+
const prop = type.getProperty(propName);
|
|
65
|
+
if (prop === undefined) {
|
|
66
|
+
problems.push(`missing property "${propName}"`);
|
|
67
|
+
continue;
|
|
68
|
+
}
|
|
69
|
+
// Get a declaration node for resolving property type.
|
|
70
|
+
// Try the type's own symbol declarations first, then fall back to the property's declarations.
|
|
71
|
+
const typeDeclarations = type.getSymbol()?.getDeclarations();
|
|
72
|
+
const propDeclarations = prop.getDeclarations();
|
|
73
|
+
const declarationNode = (typeDeclarations !== undefined && typeDeclarations.length > 0
|
|
74
|
+
? typeDeclarations[0]
|
|
75
|
+
: undefined) ?? (propDeclarations.length > 0 ? propDeclarations[0] : undefined);
|
|
76
|
+
if (declarationNode === undefined) {
|
|
77
|
+
problems.push(`property "${propName}" has no resolvable declaration`);
|
|
78
|
+
continue;
|
|
79
|
+
}
|
|
80
|
+
const propType = prop.getTypeAtLocation(declarationNode);
|
|
81
|
+
if (!matchesConstraint(propType, constraint)) {
|
|
82
|
+
const actual = propType.getNonNullableType().getText();
|
|
83
|
+
const expected = typeof constraint === 'string' ? constraint : '<custom matcher>';
|
|
84
|
+
problems.push(`property "${propName}" has type '${actual}', expected '${expected}'`);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
return problems;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Test whether a resolved property type satisfies a PropertyConstraint.
|
|
91
|
+
*/
|
|
92
|
+
function matchesConstraint(propType, constraint) {
|
|
93
|
+
const stripped = propType.getNonNullableType();
|
|
94
|
+
if (typeof constraint === 'function') {
|
|
95
|
+
// TypeMatcher — delegate directly
|
|
96
|
+
return constraint(propType);
|
|
97
|
+
}
|
|
98
|
+
// String constraint
|
|
99
|
+
if (constraint === 'T[]') {
|
|
100
|
+
// Special: any array type
|
|
101
|
+
return stripped.isArray();
|
|
102
|
+
}
|
|
103
|
+
// Match constraint as literal string against type text
|
|
104
|
+
const escaped = constraint.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
105
|
+
const regex = new RegExp(`^${escaped}$`);
|
|
106
|
+
return regex.test(stripped.getText());
|
|
107
|
+
}
|
|
108
|
+
//# sourceMappingURL=pattern.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pattern.js","sourceRoot":"","sources":["../../src/conditions/pattern.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { type SourceFile } from 'ts-morph';
|
|
2
|
+
import type { Condition } from '@nielspeter/eess';
|
|
3
|
+
/**
|
|
4
|
+
* Every file that imports this module must match at least one of the globs.
|
|
5
|
+
*
|
|
6
|
+
* Enforces barrel/facade patterns: internal modules should only be
|
|
7
|
+
* imported through their public API (e.g., index.ts).
|
|
8
|
+
*
|
|
9
|
+
* Modules with zero importers pass vacuously. Chain `.andShould().beImported()`
|
|
10
|
+
* if you also want to catch orphaned files.
|
|
11
|
+
*
|
|
12
|
+
* Both static `import` declarations and dynamic `import()` expressions with
|
|
13
|
+
* string-literal specifiers are resolved. `require()` calls are not resolved.
|
|
14
|
+
*/
|
|
15
|
+
export declare function onlyBeImportedVia(...globs: string[]): Condition<SourceFile>;
|
|
16
|
+
/**
|
|
17
|
+
* Module must be imported by at least one other file in the project.
|
|
18
|
+
*
|
|
19
|
+
* Detects dead/orphaned modules that nobody references.
|
|
20
|
+
* Use `.excluding('index.ts', 'main.ts')` to skip entry points.
|
|
21
|
+
*
|
|
22
|
+
* Both static `import` declarations and dynamic `import()` expressions with
|
|
23
|
+
* string-literal specifiers are resolved. Modules loaded via `require()` or
|
|
24
|
+
* dynamic imports with computed specifiers will still be falsely reported.
|
|
25
|
+
*/
|
|
26
|
+
export declare function beImported(): Condition<SourceFile>;
|
|
27
|
+
export declare function haveNoUnusedExports(): Condition<SourceFile>;
|
|
28
|
+
//# sourceMappingURL=reverse-dependency.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reverse-dependency.d.ts","sourceRoot":"","sources":["../../src/conditions/reverse-dependency.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,UAAU,EAAkC,MAAM,UAAU,CAAA;AAC1E,OAAO,KAAK,EAAE,SAAS,EAAoB,MAAM,kBAAkB,CAAA;AAwInE;;;;;;;;;;;GAWG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,KAAK,EAAE,MAAM,EAAE,GAAG,SAAS,CAAC,UAAU,CAAC,CAgC3E;AAED;;;;;;;;;GASG;AACH,wBAAgB,UAAU,IAAI,SAAS,CAAC,UAAU,CAAC,CA0BlD;AA0CD,wBAAgB,mBAAmB,IAAI,SAAS,CAAC,UAAU,CAAC,CAW3D"}
|