@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,188 @@
|
|
|
1
|
+
import { TerminalBuilder } from '@nielspeter/eess';
|
|
2
|
+
import { queries as queriesPredicate, mutations as mutationsPredicate, typesNamed as typesNamedPredicate, returnListOf as returnListOfPredicate, } from './schema-predicates.js';
|
|
3
|
+
import { haveFields as haveFieldsCondition, acceptArgs as acceptArgsCondition, haveMatchingResolver as haveMatchingResolverCondition, } from './schema-conditions.js';
|
|
4
|
+
/**
|
|
5
|
+
* Structural type guard: check if a GraphQL type has `getFields()`.
|
|
6
|
+
* Only GraphQLObjectType, GraphQLInterfaceType, and GraphQLInputObjectType have getFields.
|
|
7
|
+
* Scalars, enums, and unions do not.
|
|
8
|
+
*/
|
|
9
|
+
function isObjectType(type) {
|
|
10
|
+
if (typeof type !== 'object' || type === null)
|
|
11
|
+
return false;
|
|
12
|
+
if (!('getFields' in type))
|
|
13
|
+
return false;
|
|
14
|
+
// At this point TypeScript knows type has 'getFields', verify it's a function
|
|
15
|
+
const candidate = type;
|
|
16
|
+
return typeof candidate.getFields === 'function';
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Fluent rule builder for GraphQL schema architecture rules.
|
|
20
|
+
*
|
|
21
|
+
* Operates on SchemaElements extracted from .graphql files.
|
|
22
|
+
* Follows the same builder pattern as SliceRuleBuilder (standalone, not extending RuleBuilder).
|
|
23
|
+
*
|
|
24
|
+
* @example
|
|
25
|
+
* ```typescript
|
|
26
|
+
* schema(p, 'src/schema/*.graphql')
|
|
27
|
+
* .typesNamed(/Collection$/)
|
|
28
|
+
* .should()
|
|
29
|
+
* .haveFields('total', 'skip', 'limit', 'items')
|
|
30
|
+
* .check()
|
|
31
|
+
* ```
|
|
32
|
+
*/
|
|
33
|
+
export class SchemaRuleBuilder extends TerminalBuilder {
|
|
34
|
+
loaded;
|
|
35
|
+
_predicates = [];
|
|
36
|
+
_conditions = [];
|
|
37
|
+
constructor(loaded) {
|
|
38
|
+
super();
|
|
39
|
+
this.loaded = loaded;
|
|
40
|
+
}
|
|
41
|
+
// --- Predicate methods ---
|
|
42
|
+
/**
|
|
43
|
+
* Filter to only Query root type fields.
|
|
44
|
+
*/
|
|
45
|
+
queries() {
|
|
46
|
+
this._predicates.push(queriesPredicate());
|
|
47
|
+
return this;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Filter to only Mutation root type fields.
|
|
51
|
+
*/
|
|
52
|
+
mutations() {
|
|
53
|
+
this._predicates.push(mutationsPredicate());
|
|
54
|
+
return this;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Filter to object types matching the given name pattern.
|
|
58
|
+
*/
|
|
59
|
+
typesNamed(pattern) {
|
|
60
|
+
this._predicates.push(typesNamedPredicate(pattern));
|
|
61
|
+
return this;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Filter to fields returning a list of the given type.
|
|
65
|
+
*/
|
|
66
|
+
returnListOf(typeName) {
|
|
67
|
+
this._predicates.push(returnListOfPredicate(typeName));
|
|
68
|
+
return this;
|
|
69
|
+
}
|
|
70
|
+
// --- Chain methods ---
|
|
71
|
+
/**
|
|
72
|
+
* Begin the predicate phase. Purely a readability marker.
|
|
73
|
+
*/
|
|
74
|
+
that() {
|
|
75
|
+
return this;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Add another predicate (AND).
|
|
79
|
+
*/
|
|
80
|
+
and() {
|
|
81
|
+
return this;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Begin the condition phase.
|
|
85
|
+
*/
|
|
86
|
+
should() {
|
|
87
|
+
return this;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Add another condition (AND).
|
|
91
|
+
*/
|
|
92
|
+
andShould() {
|
|
93
|
+
return this;
|
|
94
|
+
}
|
|
95
|
+
// --- Condition methods ---
|
|
96
|
+
/**
|
|
97
|
+
* Assert that types have all listed fields.
|
|
98
|
+
*/
|
|
99
|
+
haveFields(...names) {
|
|
100
|
+
this._conditions.push(haveFieldsCondition(...names));
|
|
101
|
+
return this;
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Assert that fields accept all listed arguments.
|
|
105
|
+
*/
|
|
106
|
+
acceptArgs(...names) {
|
|
107
|
+
this._conditions.push(acceptArgsCondition(...names));
|
|
108
|
+
return this;
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Assert that schema fields have matching resolver implementations.
|
|
112
|
+
*
|
|
113
|
+
* @param resolverFileTexts - Map of file paths to source text
|
|
114
|
+
*/
|
|
115
|
+
haveMatchingResolver(resolverFileTexts) {
|
|
116
|
+
this._conditions.push(haveMatchingResolverCondition(resolverFileTexts));
|
|
117
|
+
return this;
|
|
118
|
+
}
|
|
119
|
+
// --- Evaluation ---
|
|
120
|
+
collectViolations() {
|
|
121
|
+
const allElements = this.getElements();
|
|
122
|
+
const filtered = allElements.filter((element) => this._predicates.every((predicate) => predicate.test(element)));
|
|
123
|
+
if (filtered.length === 0) {
|
|
124
|
+
return [];
|
|
125
|
+
}
|
|
126
|
+
if (this._conditions.length === 0) {
|
|
127
|
+
const ruleId = this._metadata?.id ?? 'unnamed';
|
|
128
|
+
console.warn(`[eess] Schema rule '${ruleId}' has predicates but no conditions. ` +
|
|
129
|
+
`Did you forget to add a condition after .should()?`);
|
|
130
|
+
return [];
|
|
131
|
+
}
|
|
132
|
+
const context = {
|
|
133
|
+
rule: this.buildRuleDescription(),
|
|
134
|
+
because: this._reason,
|
|
135
|
+
ruleId: this._metadata?.id,
|
|
136
|
+
suggestion: this._metadata?.suggestion,
|
|
137
|
+
docs: this._metadata?.docs,
|
|
138
|
+
};
|
|
139
|
+
const violations = [];
|
|
140
|
+
for (const condition of this._conditions) {
|
|
141
|
+
violations.push(...condition.evaluate(filtered, context));
|
|
142
|
+
}
|
|
143
|
+
return violations;
|
|
144
|
+
}
|
|
145
|
+
getElements() {
|
|
146
|
+
const elements = [];
|
|
147
|
+
const typeMap = this.loaded.schema.getTypeMap();
|
|
148
|
+
const firstFile = this.loaded.documents[0]?.filePath;
|
|
149
|
+
for (const [typeName, typeObj] of Object.entries(typeMap)) {
|
|
150
|
+
// Skip introspection types (start with __)
|
|
151
|
+
if (typeName.startsWith('__'))
|
|
152
|
+
continue;
|
|
153
|
+
// Skip scalar types that don't have getFields — use structural type guard
|
|
154
|
+
if (!isObjectType(typeObj))
|
|
155
|
+
continue;
|
|
156
|
+
const objectType = typeObj;
|
|
157
|
+
// Add type-level element
|
|
158
|
+
elements.push({
|
|
159
|
+
typeName,
|
|
160
|
+
objectType,
|
|
161
|
+
filePath: firstFile,
|
|
162
|
+
});
|
|
163
|
+
// Add field-level elements
|
|
164
|
+
const fields = objectType.getFields();
|
|
165
|
+
for (const [fieldName, field] of Object.entries(fields)) {
|
|
166
|
+
elements.push({
|
|
167
|
+
typeName,
|
|
168
|
+
fieldName,
|
|
169
|
+
objectType,
|
|
170
|
+
field,
|
|
171
|
+
filePath: firstFile,
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
return elements;
|
|
176
|
+
}
|
|
177
|
+
buildRuleDescription() {
|
|
178
|
+
const predicateDesc = this._predicates.map((p) => p.description).join(' and ');
|
|
179
|
+
const conditionDesc = this._conditions.map((c) => c.description).join(' and ');
|
|
180
|
+
const parts = ['schema'];
|
|
181
|
+
if (predicateDesc)
|
|
182
|
+
parts.push(`that ${predicateDesc}`);
|
|
183
|
+
if (conditionDesc)
|
|
184
|
+
parts.push(`should ${conditionDesc}`);
|
|
185
|
+
return parts.join(' ');
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
//# sourceMappingURL=schema-rule-builder.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema-rule-builder.js","sourceRoot":"","sources":["../../src/graphql/schema-rule-builder.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { type Node, type ClassDeclaration, type SourceFile } from 'ts-morph';
|
|
2
|
+
import type { ExpressionMatcher } from './matchers.js';
|
|
3
|
+
import type { ArchFunction } from '../models/arch-function.js';
|
|
4
|
+
/**
|
|
5
|
+
* Options for module body analysis.
|
|
6
|
+
*/
|
|
7
|
+
export interface ModuleBodyOptions {
|
|
8
|
+
/**
|
|
9
|
+
* When true, only traverse top-level (module-scope) statements.
|
|
10
|
+
* Skips class bodies, function bodies, and arrow function bodies.
|
|
11
|
+
* Default: false (full file traversal).
|
|
12
|
+
*/
|
|
13
|
+
scopeToModule?: boolean;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Result of searching a body for matcher hits.
|
|
17
|
+
*/
|
|
18
|
+
export interface MatchResult {
|
|
19
|
+
/** Whether at least one match was found */
|
|
20
|
+
found: boolean;
|
|
21
|
+
/** The matching nodes (for violation reporting: file, line, text) */
|
|
22
|
+
matchingNodes: Node[];
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Find all nodes in a subtree that match the given matcher.
|
|
26
|
+
*
|
|
27
|
+
* Uses getDescendantsOfKind when the matcher specifies syntaxKinds
|
|
28
|
+
* (efficient — only walks nodes of that kind). Falls back to
|
|
29
|
+
* getDescendants() for matchers without syntaxKinds (expression()).
|
|
30
|
+
*/
|
|
31
|
+
export declare function findMatchesInNode(node: Node, matcher: ExpressionMatcher): Node[];
|
|
32
|
+
/**
|
|
33
|
+
* Search all method bodies in a class for matches.
|
|
34
|
+
*
|
|
35
|
+
* Iterates over every method (instance and static), gets the body,
|
|
36
|
+
* and tests each body against the matcher. Returns aggregated results.
|
|
37
|
+
*/
|
|
38
|
+
export declare function searchClassBody(cls: ClassDeclaration, matcher: ExpressionMatcher): MatchResult;
|
|
39
|
+
/**
|
|
40
|
+
* Search a function body for matches.
|
|
41
|
+
*
|
|
42
|
+
* Uses ArchFunction.getBody() which returns the function/arrow body.
|
|
43
|
+
* For expression-bodied arrows (`() => expr`), getDescendantsOfKind
|
|
44
|
+
* still works — it walks the expression subtree.
|
|
45
|
+
*/
|
|
46
|
+
export declare function searchFunctionBody(fn: ArchFunction, matcher: ExpressionMatcher): MatchResult;
|
|
47
|
+
/**
|
|
48
|
+
* Extract the body from a function-like argument node.
|
|
49
|
+
*
|
|
50
|
+
* Handles:
|
|
51
|
+
* - ArrowFunction: () => { ... } or () => expr
|
|
52
|
+
* - FunctionExpression: function() { ... }
|
|
53
|
+
*
|
|
54
|
+
* Returns undefined if the node is not a function-like expression.
|
|
55
|
+
*/
|
|
56
|
+
export declare function getFunctionBody(node: Node): Node | undefined;
|
|
57
|
+
export declare function searchModuleBody(sourceFile: SourceFile, matcher: ExpressionMatcher, options?: ModuleBodyOptions): MatchResult;
|
|
58
|
+
//# sourceMappingURL=body-traversal.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"body-traversal.d.ts","sourceRoot":"","sources":["../../src/helpers/body-traversal.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,IAAI,EAAE,KAAK,gBAAgB,EAAE,KAAK,UAAU,EAAqB,MAAM,UAAU,CAAA;AAC/F,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAA;AACtD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAA;AAE9D;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC;;;;OAIG;IACH,aAAa,CAAC,EAAE,OAAO,CAAA;CACxB;AAED;;GAEG;AAEH,MAAM,WAAW,WAAW;IAC1B,2CAA2C;IAC3C,KAAK,EAAE,OAAO,CAAA;IACd,qEAAqE;IACrE,aAAa,EAAE,IAAI,EAAE,CAAA;CACtB;AAuCD;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,iBAAiB,GAAG,IAAI,EAAE,CAKhF;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,gBAAgB,EAAE,OAAO,EAAE,iBAAiB,GAAG,WAAW,CAqC9F;AAED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,EAAE,EAAE,YAAY,EAAE,OAAO,EAAE,iBAAiB,GAAG,WAAW,CAW5F;AAED;;;;;;;;GAQG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,IAAI,GAAG,IAAI,GAAG,SAAS,CAQ5D;AAiCD,wBAAgB,gBAAgB,CAC9B,UAAU,EAAE,UAAU,EACtB,OAAO,EAAE,iBAAiB,EAC1B,OAAO,CAAC,EAAE,iBAAiB,GAC1B,WAAW,CA4Bb"}
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
import { Node as NodeUtils } from 'ts-morph';
|
|
2
|
+
/**
|
|
3
|
+
* Targeted traversal: only check nodes of the specified syntax kinds.
|
|
4
|
+
*/
|
|
5
|
+
function findMatchesByKind(node, matcher) {
|
|
6
|
+
const matches = [];
|
|
7
|
+
for (const kind of matcher.syntaxKinds ?? []) {
|
|
8
|
+
for (const descendant of node.getDescendantsOfKind(kind)) {
|
|
9
|
+
if (matcher.matches(descendant)) {
|
|
10
|
+
matches.push(descendant);
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
return matches;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Broad traversal: check every descendant, then deduplicate.
|
|
18
|
+
*
|
|
19
|
+
* Parent nodes' getText() includes children's text, so regex-based
|
|
20
|
+
* matchers (expression()) match at multiple ancestor levels.
|
|
21
|
+
* Keep only the deepest (most specific) matching nodes.
|
|
22
|
+
*/
|
|
23
|
+
function findMatchesBroad(node, matcher) {
|
|
24
|
+
const matches = [];
|
|
25
|
+
for (const descendant of node.getDescendants()) {
|
|
26
|
+
if (matcher.matches(descendant)) {
|
|
27
|
+
matches.push(descendant);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
return matches.filter((m) => !matches.some((other) => other !== m && other.getStart() >= m.getStart() && other.getEnd() <= m.getEnd()));
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Find all nodes in a subtree that match the given matcher.
|
|
34
|
+
*
|
|
35
|
+
* Uses getDescendantsOfKind when the matcher specifies syntaxKinds
|
|
36
|
+
* (efficient — only walks nodes of that kind). Falls back to
|
|
37
|
+
* getDescendants() for matchers without syntaxKinds (expression()).
|
|
38
|
+
*/
|
|
39
|
+
export function findMatchesInNode(node, matcher) {
|
|
40
|
+
if (matcher.syntaxKinds && matcher.syntaxKinds.length > 0) {
|
|
41
|
+
return findMatchesByKind(node, matcher);
|
|
42
|
+
}
|
|
43
|
+
return findMatchesBroad(node, matcher);
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Search all method bodies in a class for matches.
|
|
47
|
+
*
|
|
48
|
+
* Iterates over every method (instance and static), gets the body,
|
|
49
|
+
* and tests each body against the matcher. Returns aggregated results.
|
|
50
|
+
*/
|
|
51
|
+
export function searchClassBody(cls, matcher) {
|
|
52
|
+
const matchingNodes = [];
|
|
53
|
+
for (const method of cls.getMethods()) {
|
|
54
|
+
const body = method.getBody();
|
|
55
|
+
if (!body)
|
|
56
|
+
continue;
|
|
57
|
+
matchingNodes.push(...findMatchesInNode(body, matcher));
|
|
58
|
+
}
|
|
59
|
+
// Also check constructor body (use last constructor — earlier ones are overload signatures without bodies)
|
|
60
|
+
const ctors = cls.getConstructors();
|
|
61
|
+
const ctor = ctors[ctors.length - 1];
|
|
62
|
+
if (ctor) {
|
|
63
|
+
const body = ctor.getBody();
|
|
64
|
+
if (body) {
|
|
65
|
+
matchingNodes.push(...findMatchesInNode(body, matcher));
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
// Also check getters and setters
|
|
69
|
+
for (const accessor of cls.getGetAccessors()) {
|
|
70
|
+
const body = accessor.getBody();
|
|
71
|
+
if (body) {
|
|
72
|
+
matchingNodes.push(...findMatchesInNode(body, matcher));
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
for (const accessor of cls.getSetAccessors()) {
|
|
76
|
+
const body = accessor.getBody();
|
|
77
|
+
if (body) {
|
|
78
|
+
matchingNodes.push(...findMatchesInNode(body, matcher));
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
return {
|
|
82
|
+
found: matchingNodes.length > 0,
|
|
83
|
+
matchingNodes,
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Search a function body for matches.
|
|
88
|
+
*
|
|
89
|
+
* Uses ArchFunction.getBody() which returns the function/arrow body.
|
|
90
|
+
* For expression-bodied arrows (`() => expr`), getDescendantsOfKind
|
|
91
|
+
* still works — it walks the expression subtree.
|
|
92
|
+
*/
|
|
93
|
+
export function searchFunctionBody(fn, matcher) {
|
|
94
|
+
const body = fn.getBody();
|
|
95
|
+
if (!body) {
|
|
96
|
+
return { found: false, matchingNodes: [] };
|
|
97
|
+
}
|
|
98
|
+
const matchingNodes = findMatchesInNode(body, matcher);
|
|
99
|
+
return {
|
|
100
|
+
found: matchingNodes.length > 0,
|
|
101
|
+
matchingNodes,
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Extract the body from a function-like argument node.
|
|
106
|
+
*
|
|
107
|
+
* Handles:
|
|
108
|
+
* - ArrowFunction: () => { ... } or () => expr
|
|
109
|
+
* - FunctionExpression: function() { ... }
|
|
110
|
+
*
|
|
111
|
+
* Returns undefined if the node is not a function-like expression.
|
|
112
|
+
*/
|
|
113
|
+
export function getFunctionBody(node) {
|
|
114
|
+
if (NodeUtils.isArrowFunction(node)) {
|
|
115
|
+
return node.getBody();
|
|
116
|
+
}
|
|
117
|
+
if (NodeUtils.isFunctionExpression(node)) {
|
|
118
|
+
return node.getBody();
|
|
119
|
+
}
|
|
120
|
+
return undefined;
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Search a module (SourceFile) for matches.
|
|
124
|
+
*
|
|
125
|
+
* Default: walks the entire file (all descendants), including inside
|
|
126
|
+
* class methods and function bodies. This makes `modules().notContain()`
|
|
127
|
+
* a file-level policy check.
|
|
128
|
+
*
|
|
129
|
+
* With `scopeToModule: true`: walks only top-level statements,
|
|
130
|
+
* skipping class bodies, function declaration bodies, and arrow/function
|
|
131
|
+
* expression bodies. Use when you already have class/function rules and
|
|
132
|
+
* want to avoid duplicate violations.
|
|
133
|
+
*/
|
|
134
|
+
/**
|
|
135
|
+
* Collect matches from top-level variable statement initializers,
|
|
136
|
+
* skipping arrow/function expressions (covered by function rules).
|
|
137
|
+
*/
|
|
138
|
+
function collectVariableStatementMatches(statement, matcher) {
|
|
139
|
+
if (!NodeUtils.isVariableStatement(statement))
|
|
140
|
+
return [];
|
|
141
|
+
const matches = [];
|
|
142
|
+
for (const decl of statement.getDeclarationList().getDeclarations()) {
|
|
143
|
+
const initializer = decl.getInitializer();
|
|
144
|
+
if (!initializer)
|
|
145
|
+
continue;
|
|
146
|
+
// Skip arrow/function expressions entirely — function rules cover them
|
|
147
|
+
if (NodeUtils.isArrowFunction(initializer) || NodeUtils.isFunctionExpression(initializer)) {
|
|
148
|
+
continue;
|
|
149
|
+
}
|
|
150
|
+
matches.push(...findMatchesInNode(initializer, matcher));
|
|
151
|
+
}
|
|
152
|
+
return matches;
|
|
153
|
+
}
|
|
154
|
+
export function searchModuleBody(sourceFile, matcher, options) {
|
|
155
|
+
if (!options?.scopeToModule) {
|
|
156
|
+
// Full file traversal — walk all descendants
|
|
157
|
+
const matchingNodes = findMatchesInNode(sourceFile, matcher);
|
|
158
|
+
return { found: matchingNodes.length > 0, matchingNodes };
|
|
159
|
+
}
|
|
160
|
+
// Module-scope only — walk each top-level statement but skip class/function internals
|
|
161
|
+
const matchingNodes = [];
|
|
162
|
+
for (const statement of sourceFile.getStatements()) {
|
|
163
|
+
// Skip class declarations entirely (their bodies are covered by class rules)
|
|
164
|
+
if (NodeUtils.isClassDeclaration(statement))
|
|
165
|
+
continue;
|
|
166
|
+
// Skip function declarations entirely (their bodies are covered by function rules)
|
|
167
|
+
if (NodeUtils.isFunctionDeclaration(statement))
|
|
168
|
+
continue;
|
|
169
|
+
// For variable statements (const/let/var), check the initializer but skip
|
|
170
|
+
// arrow function and function expression bodies within it
|
|
171
|
+
if (NodeUtils.isVariableStatement(statement)) {
|
|
172
|
+
matchingNodes.push(...collectVariableStatementMatches(statement, matcher));
|
|
173
|
+
continue;
|
|
174
|
+
}
|
|
175
|
+
// All other statements: walk their descendants
|
|
176
|
+
matchingNodes.push(...findMatchesInNode(statement, matcher));
|
|
177
|
+
}
|
|
178
|
+
return { found: matchingNodes.length > 0, matchingNodes };
|
|
179
|
+
}
|
|
180
|
+
//# sourceMappingURL=body-traversal.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"body-traversal.js","sourceRoot":"","sources":["../../src/helpers/body-traversal.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { type CallExpression } from 'ts-morph';
|
|
2
|
+
import type { ArchFunction } from '../models/arch-function.js';
|
|
3
|
+
/**
|
|
4
|
+
* Represents a callback function extracted from a call expression argument.
|
|
5
|
+
* Wraps the arrow function or function expression as an ArchFunction.
|
|
6
|
+
*/
|
|
7
|
+
export interface ExtractedCallback {
|
|
8
|
+
/** The ArchFunction wrapping the callback. */
|
|
9
|
+
fn: ArchFunction;
|
|
10
|
+
/** The call expression this callback was extracted from. */
|
|
11
|
+
callSite: CallExpression;
|
|
12
|
+
/** Argument index within the call expression (0-based). */
|
|
13
|
+
argIndex: number;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Extract all inline function arguments from a call expression.
|
|
17
|
+
*
|
|
18
|
+
* Handles:
|
|
19
|
+
* - Arrow functions: `app.get('/path', (req, res) => { ... })`
|
|
20
|
+
* - Function expressions: `app.get('/path', function(req, res) { ... })`
|
|
21
|
+
*
|
|
22
|
+
* Does NOT resolve named references (e.g., `app.get('/path', myHandler)`).
|
|
23
|
+
* Reference resolution requires type-checker lookups and is deferred.
|
|
24
|
+
*
|
|
25
|
+
* @returns Array of extracted callbacks with their source metadata
|
|
26
|
+
*/
|
|
27
|
+
export declare function extractCallbacks(callExpr: CallExpression): ExtractedCallback[];
|
|
28
|
+
//# sourceMappingURL=callback-extractor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"callback-extractor.d.ts","sourceRoot":"","sources":["../../src/helpers/callback-extractor.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,cAAc,EAA4C,MAAM,UAAU,CAAA;AACxF,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAA;AAE9D;;;GAGG;AACH,MAAM,WAAW,iBAAiB;IAChC,8CAA8C;IAC9C,EAAE,EAAE,YAAY,CAAA;IAChB,4DAA4D;IAC5D,QAAQ,EAAE,cAAc,CAAA;IACxB,2DAA2D;IAC3D,QAAQ,EAAE,MAAM,CAAA;CACjB;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,cAAc,GAAG,iBAAiB,EAAE,CAiB9E"}
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
import { Node as NodeUtils, SyntaxKind } from 'ts-morph';
|
|
2
|
+
/**
|
|
3
|
+
* Extract all inline function arguments from a call expression.
|
|
4
|
+
*
|
|
5
|
+
* Handles:
|
|
6
|
+
* - Arrow functions: `app.get('/path', (req, res) => { ... })`
|
|
7
|
+
* - Function expressions: `app.get('/path', function(req, res) { ... })`
|
|
8
|
+
*
|
|
9
|
+
* Does NOT resolve named references (e.g., `app.get('/path', myHandler)`).
|
|
10
|
+
* Reference resolution requires type-checker lookups and is deferred.
|
|
11
|
+
*
|
|
12
|
+
* @returns Array of extracted callbacks with their source metadata
|
|
13
|
+
*/
|
|
14
|
+
export function extractCallbacks(callExpr) {
|
|
15
|
+
const callbacks = [];
|
|
16
|
+
const args = callExpr.getArguments();
|
|
17
|
+
for (let i = 0; i < args.length; i++) {
|
|
18
|
+
const arg = args[i];
|
|
19
|
+
if (!arg)
|
|
20
|
+
continue;
|
|
21
|
+
const fn = extractInlineFunction(arg, callExpr, i);
|
|
22
|
+
if (fn) {
|
|
23
|
+
callbacks.push(fn);
|
|
24
|
+
}
|
|
25
|
+
else {
|
|
26
|
+
// Search object literal arguments for function-valued properties
|
|
27
|
+
callbacks.push(...extractFromObjectLiteral(arg, callExpr, i, 0));
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
return callbacks;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Maximum depth to recurse into nested object literals.
|
|
34
|
+
* Prevents extracting unintended callbacks from deep config/schema structures.
|
|
35
|
+
*/
|
|
36
|
+
const MAX_OBJECT_DEPTH = 3;
|
|
37
|
+
/**
|
|
38
|
+
* Recursively search an ObjectLiteralExpression for function-like property values.
|
|
39
|
+
*
|
|
40
|
+
* Handles:
|
|
41
|
+
* - Arrow function properties: `{ handler: (req) => { ... } }`
|
|
42
|
+
* - Function expression properties: `{ handler: function(req) { ... } }`
|
|
43
|
+
* - Method shorthand: `{ handler(req) { ... } }`
|
|
44
|
+
* - Nested object literals: `{ hooks: { onRequest: (req) => { ... } } }`
|
|
45
|
+
*
|
|
46
|
+
* Stops at MAX_OBJECT_DEPTH to avoid false positives from schema defaults.
|
|
47
|
+
*/
|
|
48
|
+
function extractFromObjectLiteral(arg, callSite, argIndex, depth) {
|
|
49
|
+
if (!NodeUtils.isObjectLiteralExpression(arg))
|
|
50
|
+
return [];
|
|
51
|
+
if (depth >= MAX_OBJECT_DEPTH)
|
|
52
|
+
return [];
|
|
53
|
+
const results = [];
|
|
54
|
+
for (const prop of arg.getProperties()) {
|
|
55
|
+
// Method shorthand: { handler(req, res) { ... } }
|
|
56
|
+
if (NodeUtils.isMethodDeclaration(prop)) {
|
|
57
|
+
results.push({
|
|
58
|
+
fn: fromMethodDeclaration(prop),
|
|
59
|
+
callSite,
|
|
60
|
+
argIndex,
|
|
61
|
+
});
|
|
62
|
+
continue;
|
|
63
|
+
}
|
|
64
|
+
if (!NodeUtils.isPropertyAssignment(prop))
|
|
65
|
+
continue;
|
|
66
|
+
const init = prop.getInitializer();
|
|
67
|
+
if (!init)
|
|
68
|
+
continue;
|
|
69
|
+
// Direct function property
|
|
70
|
+
const direct = extractInlineFunction(init, callSite, argIndex);
|
|
71
|
+
if (direct) {
|
|
72
|
+
results.push(direct);
|
|
73
|
+
continue;
|
|
74
|
+
}
|
|
75
|
+
// Recurse into nested object literals (depth-limited)
|
|
76
|
+
results.push(...extractFromObjectLiteral(init, callSite, argIndex, depth + 1));
|
|
77
|
+
}
|
|
78
|
+
return results;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Try to extract an ArchFunction from a single argument node.
|
|
82
|
+
*/
|
|
83
|
+
function extractInlineFunction(arg, callSite, argIndex) {
|
|
84
|
+
// Arrow function: (req, res) => { ... }
|
|
85
|
+
if (arg.getKind() === SyntaxKind.ArrowFunction) {
|
|
86
|
+
return {
|
|
87
|
+
fn: fromArrowExpression(arg),
|
|
88
|
+
callSite,
|
|
89
|
+
argIndex,
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
// Function expression: function(req, res) { ... }
|
|
93
|
+
if (arg.getKind() === SyntaxKind.FunctionExpression) {
|
|
94
|
+
return {
|
|
95
|
+
fn: fromFunctionExpression(arg),
|
|
96
|
+
callSite,
|
|
97
|
+
argIndex,
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
return null;
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Wrap an arrow function argument as an ArchFunction.
|
|
104
|
+
* Unlike fromArrowVariableDeclaration (plan 0009), this has no variable name.
|
|
105
|
+
* The name is synthesized from the call site context.
|
|
106
|
+
*/
|
|
107
|
+
function fromArrowExpression(node) {
|
|
108
|
+
const arrow = node.asKindOrThrow(SyntaxKind.ArrowFunction);
|
|
109
|
+
return {
|
|
110
|
+
getName: () => undefined, // anonymous --- name derived from context
|
|
111
|
+
getSourceFile: () => arrow.getSourceFile(),
|
|
112
|
+
isExported: () => false, // callbacks are never exported
|
|
113
|
+
isAsync: () => arrow.isAsync(),
|
|
114
|
+
getParameters: () => arrow.getParameters(),
|
|
115
|
+
getReturnType: () => arrow.getReturnType(),
|
|
116
|
+
getBody: () => arrow.getBody(),
|
|
117
|
+
getNode: () => arrow,
|
|
118
|
+
getStartLineNumber: () => arrow.getStartLineNumber(),
|
|
119
|
+
getScope: () => 'public',
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Wrap a function expression argument as an ArchFunction.
|
|
124
|
+
*/
|
|
125
|
+
function fromFunctionExpression(node) {
|
|
126
|
+
const funcExpr = node.asKindOrThrow(SyntaxKind.FunctionExpression);
|
|
127
|
+
return {
|
|
128
|
+
getName: () => funcExpr.getName(), // may have a name: `function handler() {}`
|
|
129
|
+
getSourceFile: () => funcExpr.getSourceFile(),
|
|
130
|
+
isExported: () => false,
|
|
131
|
+
isAsync: () => funcExpr.isAsync(),
|
|
132
|
+
getParameters: () => funcExpr.getParameters(),
|
|
133
|
+
getReturnType: () => funcExpr.getReturnType(),
|
|
134
|
+
getBody: () => funcExpr.getBody(),
|
|
135
|
+
getNode: () => funcExpr,
|
|
136
|
+
getStartLineNumber: () => funcExpr.getStartLineNumber(),
|
|
137
|
+
getScope: () => 'public',
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Wrap an object literal method declaration as an ArchFunction.
|
|
142
|
+
* Handles: `{ handler(req, res) { ... } }`
|
|
143
|
+
*/
|
|
144
|
+
function fromMethodDeclaration(node) {
|
|
145
|
+
const method = node.asKindOrThrow(SyntaxKind.MethodDeclaration);
|
|
146
|
+
return {
|
|
147
|
+
getName: () => method.getName(),
|
|
148
|
+
getSourceFile: () => method.getSourceFile(),
|
|
149
|
+
isExported: () => false,
|
|
150
|
+
isAsync: () => method.isAsync(),
|
|
151
|
+
getParameters: () => method.getParameters(),
|
|
152
|
+
getReturnType: () => method.getReturnType(),
|
|
153
|
+
getBody: () => method.getBody(),
|
|
154
|
+
getNode: () => method,
|
|
155
|
+
getStartLineNumber: () => method.getStartLineNumber(),
|
|
156
|
+
getScope: () => 'public',
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
//# sourceMappingURL=callback-extractor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"callback-extractor.js","sourceRoot":"","sources":["../../src/helpers/callback-extractor.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { Node } from 'ts-morph';
|
|
2
|
+
import type { ClassDeclaration } from 'ts-morph';
|
|
3
|
+
/**
|
|
4
|
+
* Calculate cyclomatic complexity (McCabe) for a function body.
|
|
5
|
+
*
|
|
6
|
+
* Accepts the body Node directly (from ArchFunction.getBody(),
|
|
7
|
+
* MethodDeclaration.getBody(), etc.).
|
|
8
|
+
*
|
|
9
|
+
* Complexity = 1 + number of decision points.
|
|
10
|
+
* Returns 1 for an undefined/empty body (one path through).
|
|
11
|
+
*/
|
|
12
|
+
export declare function cyclomaticComplexity(body: Node | undefined): number;
|
|
13
|
+
/**
|
|
14
|
+
* Count lines spanned by a node (from first line to last line, inclusive).
|
|
15
|
+
*
|
|
16
|
+
* This is a "span lines" count — it includes blank lines and comments
|
|
17
|
+
* within the node's range. This is consistent with how editors report
|
|
18
|
+
* function/class length, and avoids the fragility of text-based
|
|
19
|
+
* comment stripping.
|
|
20
|
+
*/
|
|
21
|
+
export declare function linesOfCode(node: Node): number;
|
|
22
|
+
/**
|
|
23
|
+
* Count the number of methods on a class.
|
|
24
|
+
*/
|
|
25
|
+
export declare function methodCount(cls: ClassDeclaration): number;
|
|
26
|
+
//# sourceMappingURL=complexity.d.ts.map
|