@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,470 @@
|
|
|
1
|
+
import { Node, SyntaxKind } from 'ts-morph';
|
|
2
|
+
/**
|
|
3
|
+
* Normalize expression text for matching.
|
|
4
|
+
* Replaces optional chaining `?.` with `.` so users don't need to
|
|
5
|
+
* account for both forms.
|
|
6
|
+
*
|
|
7
|
+
* PoC finding: `this?.normalizeCount` getText() includes the `?`.
|
|
8
|
+
*/
|
|
9
|
+
function normalizeText(text) {
|
|
10
|
+
return text.replace(/\?\./g, '.');
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Match a CallExpression by function/method name.
|
|
14
|
+
*
|
|
15
|
+
* Matches against `CallExpression.getExpression().getText()` after
|
|
16
|
+
* normalizing optional chaining.
|
|
17
|
+
*
|
|
18
|
+
* @param nameOrRegex - Exact name (e.g. 'parseInt', 'this.normalizeCount')
|
|
19
|
+
* or regex for flexible matching.
|
|
20
|
+
*
|
|
21
|
+
* @example
|
|
22
|
+
* call('parseInt') // matches parseInt(x)
|
|
23
|
+
* call('this.normalizeCount') // matches this.normalizeCount(x) AND this?.normalizeCount(x)
|
|
24
|
+
* call(/^console\./) // matches console.log, console.warn, etc.
|
|
25
|
+
*/
|
|
26
|
+
export function call(nameOrRegex) {
|
|
27
|
+
if (typeof nameOrRegex === 'string') {
|
|
28
|
+
return {
|
|
29
|
+
description: `call to '${nameOrRegex}'`,
|
|
30
|
+
syntaxKinds: [SyntaxKind.CallExpression],
|
|
31
|
+
matches(node) {
|
|
32
|
+
if (!Node.isCallExpression(node))
|
|
33
|
+
return false;
|
|
34
|
+
const text = normalizeText(node.getExpression().getText());
|
|
35
|
+
return text === nameOrRegex;
|
|
36
|
+
},
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
return {
|
|
40
|
+
description: `call matching ${String(nameOrRegex)}`,
|
|
41
|
+
syntaxKinds: [SyntaxKind.CallExpression],
|
|
42
|
+
matches(node) {
|
|
43
|
+
if (!Node.isCallExpression(node))
|
|
44
|
+
return false;
|
|
45
|
+
const text = normalizeText(node.getExpression().getText());
|
|
46
|
+
return nameOrRegex.test(text);
|
|
47
|
+
},
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Match a PropertyAccessExpression by the dotted chain.
|
|
52
|
+
*
|
|
53
|
+
* Matches against `PropertyAccessExpression.getText()` after normalizing
|
|
54
|
+
* optional chaining. Useful for detecting direct property access patterns
|
|
55
|
+
* like `process.env`, `this.db`, `window.location`.
|
|
56
|
+
*
|
|
57
|
+
* @param chain - Exact dotted chain (e.g. 'process.env') or regex.
|
|
58
|
+
*
|
|
59
|
+
* @example
|
|
60
|
+
* access('process.env') // matches process.env.FOO (the inner access)
|
|
61
|
+
* access(/^this\.db/) // matches this.db, this.db.query, etc.
|
|
62
|
+
*/
|
|
63
|
+
export function access(chain) {
|
|
64
|
+
if (typeof chain === 'string') {
|
|
65
|
+
return {
|
|
66
|
+
description: `access to '${chain}'`,
|
|
67
|
+
syntaxKinds: [SyntaxKind.PropertyAccessExpression],
|
|
68
|
+
matches(node) {
|
|
69
|
+
if (!Node.isPropertyAccessExpression(node))
|
|
70
|
+
return false;
|
|
71
|
+
const text = normalizeText(node.getText());
|
|
72
|
+
return text === chain;
|
|
73
|
+
},
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
return {
|
|
77
|
+
description: `access matching ${String(chain)}`,
|
|
78
|
+
syntaxKinds: [SyntaxKind.PropertyAccessExpression],
|
|
79
|
+
matches(node) {
|
|
80
|
+
if (!Node.isPropertyAccessExpression(node))
|
|
81
|
+
return false;
|
|
82
|
+
const text = normalizeText(node.getText());
|
|
83
|
+
return chain.test(text);
|
|
84
|
+
},
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Match a NewExpression by constructor name.
|
|
89
|
+
*
|
|
90
|
+
* Matches against `NewExpression.getExpression().getText()`.
|
|
91
|
+
* The PoC confirmed this correctly distinguishes 'Error' from 'DomainError'.
|
|
92
|
+
*
|
|
93
|
+
* @param nameOrRegex - Exact constructor name or regex.
|
|
94
|
+
*
|
|
95
|
+
* @example
|
|
96
|
+
* newExpr('Error') // matches new Error(...) but NOT new DomainError(...)
|
|
97
|
+
* newExpr('DomainError') // matches new DomainError(...)
|
|
98
|
+
* newExpr(/Error$/) // matches new Error, new DomainError, new TypeError, etc.
|
|
99
|
+
*/
|
|
100
|
+
export function newExpr(nameOrRegex) {
|
|
101
|
+
if (typeof nameOrRegex === 'string') {
|
|
102
|
+
return {
|
|
103
|
+
description: `new '${nameOrRegex}'`,
|
|
104
|
+
syntaxKinds: [SyntaxKind.NewExpression],
|
|
105
|
+
matches(node) {
|
|
106
|
+
if (!Node.isNewExpression(node))
|
|
107
|
+
return false;
|
|
108
|
+
const text = node.getExpression().getText();
|
|
109
|
+
return text === nameOrRegex;
|
|
110
|
+
},
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
return {
|
|
114
|
+
description: `new matching ${String(nameOrRegex)}`,
|
|
115
|
+
syntaxKinds: [SyntaxKind.NewExpression],
|
|
116
|
+
matches(node) {
|
|
117
|
+
if (!Node.isNewExpression(node))
|
|
118
|
+
return false;
|
|
119
|
+
const text = node.getExpression().getText();
|
|
120
|
+
return nameOrRegex.test(text);
|
|
121
|
+
},
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Escape hatch: match any node whose getText() contains or matches the given pattern.
|
|
126
|
+
*
|
|
127
|
+
* This is intentionally broad and should be used sparingly. It walks ALL
|
|
128
|
+
* descendant nodes and checks getText() against the pattern. A runtime
|
|
129
|
+
* console.warn is emitted the first time expression() is used, encouraging
|
|
130
|
+
* users to prefer call/access/newExpr where possible.
|
|
131
|
+
*
|
|
132
|
+
* @param textOrRegex - Substring to search for (string) or regex pattern.
|
|
133
|
+
*
|
|
134
|
+
* @example
|
|
135
|
+
* expression('eval') // matches any node containing 'eval'
|
|
136
|
+
* expression(/document\.write/) // matches document.write calls
|
|
137
|
+
*/
|
|
138
|
+
export function expression(textOrRegex) {
|
|
139
|
+
let warned = false;
|
|
140
|
+
if (typeof textOrRegex === 'string') {
|
|
141
|
+
return {
|
|
142
|
+
description: `expression containing '${textOrRegex}'`,
|
|
143
|
+
// No syntaxKinds — walks all descendants
|
|
144
|
+
matches(node) {
|
|
145
|
+
if (!warned) {
|
|
146
|
+
console.warn(`[eess] expression('${textOrRegex}') is a broad matcher. ` +
|
|
147
|
+
`Prefer call(), access(), or newExpr() for precise matching.`);
|
|
148
|
+
warned = true;
|
|
149
|
+
}
|
|
150
|
+
return node.getText().includes(textOrRegex);
|
|
151
|
+
},
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
return {
|
|
155
|
+
description: `expression matching ${String(textOrRegex)}`,
|
|
156
|
+
// No syntaxKinds — walks all descendants
|
|
157
|
+
matches(node) {
|
|
158
|
+
if (!warned) {
|
|
159
|
+
console.warn(`[eess] expression(${String(textOrRegex)}) is a broad matcher. ` +
|
|
160
|
+
`Prefer call(), access(), or newExpr() for precise matching.`);
|
|
161
|
+
warned = true;
|
|
162
|
+
}
|
|
163
|
+
return textOrRegex.test(node.getText());
|
|
164
|
+
},
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* Match a PropertyAssignment by name and optional value.
|
|
169
|
+
*
|
|
170
|
+
* Targets `SyntaxKind.PropertyAssignment` for efficient traversal via
|
|
171
|
+
* `getDescendantsOfKind`. Does NOT match ShorthandPropertyAssignment
|
|
172
|
+
* (`{ schema }`) — those have no initializer.
|
|
173
|
+
*
|
|
174
|
+
* Value matching uses semantic comparison for primitives:
|
|
175
|
+
* - `boolean` — matches TrueKeyword / FalseKeyword
|
|
176
|
+
* - `number` — matches NumericLiteral via getLiteralValue()
|
|
177
|
+
* - `string` — matches StringLiteral via getLiteralValue() (no quotes needed)
|
|
178
|
+
* - `RegExp` — matches initializer getText() (raw text including quotes)
|
|
179
|
+
*
|
|
180
|
+
* @param name - Exact property name or regex for flexible matching.
|
|
181
|
+
* @param value - Optional value constraint. Omit for name-only matching.
|
|
182
|
+
*
|
|
183
|
+
* @example
|
|
184
|
+
* property('additionalProperties', true) // matches additionalProperties: true
|
|
185
|
+
* property('type', 'object') // matches type: 'object'
|
|
186
|
+
* property('maximum', 100) // matches maximum: 100
|
|
187
|
+
* property(/^additional/) // matches any property starting with 'additional'
|
|
188
|
+
* property('mode', /^'(strict|loose)'$/) // matches mode: 'strict' or 'loose' (getText() includes quotes)
|
|
189
|
+
*/
|
|
190
|
+
/**
|
|
191
|
+
* Test whether an initializer matches the expected value.
|
|
192
|
+
*/
|
|
193
|
+
function matchPropertyValue(initializer, value) {
|
|
194
|
+
if (typeof value === 'boolean') {
|
|
195
|
+
const kind = initializer.getKind();
|
|
196
|
+
return value ? kind === SyntaxKind.TrueKeyword : kind === SyntaxKind.FalseKeyword;
|
|
197
|
+
}
|
|
198
|
+
if (typeof value === 'number') {
|
|
199
|
+
return Node.isNumericLiteral(initializer) && initializer.getLiteralValue() === value;
|
|
200
|
+
}
|
|
201
|
+
if (typeof value === 'string') {
|
|
202
|
+
return Node.isStringLiteral(initializer) && initializer.getLiteralValue() === value;
|
|
203
|
+
}
|
|
204
|
+
return value.test(initializer.getText());
|
|
205
|
+
}
|
|
206
|
+
/**
|
|
207
|
+
* Test whether a property assignment's name matches the expected pattern.
|
|
208
|
+
*/
|
|
209
|
+
function matchPropertyName(node, name) {
|
|
210
|
+
if (!Node.isPropertyAssignment(node))
|
|
211
|
+
return false;
|
|
212
|
+
if (node.getNameNode().getKind() === SyntaxKind.ComputedPropertyName)
|
|
213
|
+
return false;
|
|
214
|
+
const nameNode = node.getNameNode();
|
|
215
|
+
const propName = Node.isStringLiteral(nameNode) ? nameNode.getLiteralValue() : node.getName();
|
|
216
|
+
if (typeof name === 'string')
|
|
217
|
+
return propName === name;
|
|
218
|
+
return name.test(propName);
|
|
219
|
+
}
|
|
220
|
+
export function property(name, value) {
|
|
221
|
+
const nameDesc = typeof name === 'string' ? `'${name}'` : String(name);
|
|
222
|
+
const valueDesc = value === undefined ? '' : ` = ${String(value)}`;
|
|
223
|
+
const description = `property ${nameDesc}${valueDesc}`;
|
|
224
|
+
return {
|
|
225
|
+
description,
|
|
226
|
+
syntaxKinds: [SyntaxKind.PropertyAssignment],
|
|
227
|
+
matches(node) {
|
|
228
|
+
if (!matchPropertyName(node, name))
|
|
229
|
+
return false;
|
|
230
|
+
if (value === undefined)
|
|
231
|
+
return true;
|
|
232
|
+
const initializer = Node.isPropertyAssignment(node) ? node.getInitializer() : undefined;
|
|
233
|
+
if (!initializer)
|
|
234
|
+
return false;
|
|
235
|
+
return matchPropertyValue(initializer, value);
|
|
236
|
+
},
|
|
237
|
+
};
|
|
238
|
+
}
|
|
239
|
+
/**
|
|
240
|
+
* Default stub/deferred-work patterns found in comments.
|
|
241
|
+
* Matches common markers (see regex) and phrases like
|
|
242
|
+
* "not implemented" or "coming soon".
|
|
243
|
+
*
|
|
244
|
+
* Exported as a constant for use with `comment()`. Users can pass
|
|
245
|
+
* their own RegExp to `comment()` for narrower matching.
|
|
246
|
+
*/
|
|
247
|
+
export const STUB_PATTERNS = /\b(TODO|FIXME|HACK|XXX|STUB|DEFERRED|PLACEHOLDER)\b|\bnot\s+implemented\b|\bcoming\s+soon\b/i;
|
|
248
|
+
/**
|
|
249
|
+
* Match comments attached to AST nodes.
|
|
250
|
+
*
|
|
251
|
+
* Unlike other matchers that test AST nodes, this matcher tests the
|
|
252
|
+
* leading and trailing comment ranges of each node. It uses
|
|
253
|
+
* `syntaxKinds: undefined` so the broad traversal path visits every node,
|
|
254
|
+
* and `matches(node)` checks that node's comment ranges.
|
|
255
|
+
*
|
|
256
|
+
* A Set tracks matched comment positions to avoid duplicates (the same
|
|
257
|
+
* comment may be visited as leading trivia of multiple nested nodes).
|
|
258
|
+
*
|
|
259
|
+
* @param pattern - String substring or RegExp to test against comment text.
|
|
260
|
+
*
|
|
261
|
+
* @example
|
|
262
|
+
* comment(/HACK/) // matches hack marker comments
|
|
263
|
+
* comment(STUB_PATTERNS) // matches all common stub markers
|
|
264
|
+
* comment('HACK') // matches hack comments
|
|
265
|
+
*/
|
|
266
|
+
export function comment(pattern) {
|
|
267
|
+
// Dedup by (filePath, pos) — prevents the same comment from matching
|
|
268
|
+
// multiple times when visited as leading trivia of nested nodes.
|
|
269
|
+
// Using a composite string key avoids cross-file collisions.
|
|
270
|
+
const matchedComments = new Set();
|
|
271
|
+
function testComment(range) {
|
|
272
|
+
const text = range.getText();
|
|
273
|
+
if (typeof pattern === 'string') {
|
|
274
|
+
return text.includes(pattern);
|
|
275
|
+
}
|
|
276
|
+
// Reset lastIndex for stateful (g-flag) regexes
|
|
277
|
+
pattern.lastIndex = 0;
|
|
278
|
+
return pattern.test(text);
|
|
279
|
+
}
|
|
280
|
+
return {
|
|
281
|
+
description: typeof pattern === 'string'
|
|
282
|
+
? `comment containing '${pattern}'`
|
|
283
|
+
: `comment matching ${String(pattern)}`,
|
|
284
|
+
// No syntaxKinds — broad traversal to visit all nodes and their comments
|
|
285
|
+
matches(node) {
|
|
286
|
+
const filePath = node.getSourceFile().getFilePath();
|
|
287
|
+
const ranges = [...node.getLeadingCommentRanges(), ...node.getTrailingCommentRanges()];
|
|
288
|
+
for (const range of ranges) {
|
|
289
|
+
const key = `${filePath}:${String(range.getPos())}`;
|
|
290
|
+
if (matchedComments.has(key))
|
|
291
|
+
continue;
|
|
292
|
+
if (testComment(range)) {
|
|
293
|
+
matchedComments.add(key);
|
|
294
|
+
return true;
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
return false;
|
|
298
|
+
},
|
|
299
|
+
};
|
|
300
|
+
}
|
|
301
|
+
/**
|
|
302
|
+
* Extract the tag name from a JsxElement or JsxSelfClosingElement node.
|
|
303
|
+
*/
|
|
304
|
+
function getJsxTagName(node) {
|
|
305
|
+
if (Node.isJsxElement(node)) {
|
|
306
|
+
return node.getOpeningElement().getTagNameNode().getText();
|
|
307
|
+
}
|
|
308
|
+
if (Node.isJsxSelfClosingElement(node)) {
|
|
309
|
+
return node.getTagNameNode().getText();
|
|
310
|
+
}
|
|
311
|
+
return undefined;
|
|
312
|
+
}
|
|
313
|
+
/**
|
|
314
|
+
* Match a JSX element by tag name.
|
|
315
|
+
*
|
|
316
|
+
* Targets `JsxElement` (has children) and `JsxSelfClosingElement` (no children).
|
|
317
|
+
* This is a **tag-only** matcher — it cannot check attributes. Use the
|
|
318
|
+
* `jsxElements()` entry point for attribute-level rules.
|
|
319
|
+
*
|
|
320
|
+
* @param tagOrRegex - Exact tag name (e.g. 'div', 'Button') or regex.
|
|
321
|
+
*
|
|
322
|
+
* @example
|
|
323
|
+
* jsxElement('div') // matches <div>...</div> and <div />
|
|
324
|
+
* jsxElement('Button') // matches <Button>...</Button>
|
|
325
|
+
* jsxElement(/^motion\./) // matches <motion.div>, <motion.span>, etc.
|
|
326
|
+
*/
|
|
327
|
+
export function jsxElement(tagOrRegex) {
|
|
328
|
+
if (typeof tagOrRegex === 'string') {
|
|
329
|
+
return {
|
|
330
|
+
description: `JSX element <${tagOrRegex}>`,
|
|
331
|
+
syntaxKinds: [SyntaxKind.JsxElement, SyntaxKind.JsxSelfClosingElement],
|
|
332
|
+
matches(node) {
|
|
333
|
+
const tag = getJsxTagName(node);
|
|
334
|
+
return tag === tagOrRegex;
|
|
335
|
+
},
|
|
336
|
+
};
|
|
337
|
+
}
|
|
338
|
+
return {
|
|
339
|
+
description: `JSX element matching ${String(tagOrRegex)}`,
|
|
340
|
+
syntaxKinds: [SyntaxKind.JsxElement, SyntaxKind.JsxSelfClosingElement],
|
|
341
|
+
matches(node) {
|
|
342
|
+
const tag = getJsxTagName(node);
|
|
343
|
+
if (tag === undefined)
|
|
344
|
+
return false;
|
|
345
|
+
return tagOrRegex.test(tag);
|
|
346
|
+
},
|
|
347
|
+
};
|
|
348
|
+
}
|
|
349
|
+
/**
|
|
350
|
+
* Match hardcoded JSX text content — the children of JSX elements.
|
|
351
|
+
*
|
|
352
|
+
* Where {@link jsxElement} matches elements by tag and the `jsxElements()`
|
|
353
|
+
* entry point handles attribute values, this matcher covers the remaining
|
|
354
|
+
* gap: literal text rendered as element children. Composes with
|
|
355
|
+
* `notContain()` for i18n-style rules ("no hardcoded user-facing text").
|
|
356
|
+
*
|
|
357
|
+
* Matches:
|
|
358
|
+
* - `JsxText` nodes with non-whitespace content (`<button>Save</button>`).
|
|
359
|
+
* - `JsxExpression` wrapping a bare string literal (`<div>{"Save"}</div>`)
|
|
360
|
+
* or a no-substitution template (`` <div>{`Save`}</div> ``) — these render
|
|
361
|
+
* identically to plain text and would otherwise be a trivial bypass.
|
|
362
|
+
*
|
|
363
|
+
* Does NOT match: inter-element whitespace, `{expr}` wrapping an identifier
|
|
364
|
+
* or call (`<div>{t("save")}</div>`), templates with substitution
|
|
365
|
+
* (`` <div>{`Hi ${name}`}</div> ``), or attribute values whether braced or
|
|
366
|
+
* quoted (`title={"Save"}`, `title="Save"`) — those belong to the
|
|
367
|
+
* `jsxElements()` entry point. No letter-presence filter is baked in —
|
|
368
|
+
* `<div>123</div>` matches; the matcher takes no options, so to narrow which
|
|
369
|
+
* text is flagged, scope the rule with folder/file predicates or
|
|
370
|
+
* `.excluding(...)`. Note: text inside translation wrappers like `<Trans>`
|
|
371
|
+
* also matches — scope those out the same way if your i18n setup uses them.
|
|
372
|
+
*
|
|
373
|
+
* @example
|
|
374
|
+
* modules(p)
|
|
375
|
+
* .that()
|
|
376
|
+
* .resideInFolder('src/components/**')
|
|
377
|
+
* .should()
|
|
378
|
+
* .notContain(jsxText())
|
|
379
|
+
* .because('User-facing text must go through t()')
|
|
380
|
+
* .check()
|
|
381
|
+
*/
|
|
382
|
+
export function jsxText() {
|
|
383
|
+
return {
|
|
384
|
+
description: 'JSX text content',
|
|
385
|
+
syntaxKinds: [SyntaxKind.JsxText, SyntaxKind.JsxExpression],
|
|
386
|
+
matches(node) {
|
|
387
|
+
if (Node.isJsxText(node)) {
|
|
388
|
+
return node.getLiteralText().trim().length > 0;
|
|
389
|
+
}
|
|
390
|
+
if (Node.isJsxExpression(node)) {
|
|
391
|
+
// A JsxExpression appears in two positions: as an element child
|
|
392
|
+
// (`<div>{"x"}</div>`) or as an attribute value (`<div title={"x"} />`).
|
|
393
|
+
// Attribute values are the domain of the `jsxElements()` entry point —
|
|
394
|
+
// only match child position (parent is a JsxElement or JsxFragment).
|
|
395
|
+
const parent = node.getParent();
|
|
396
|
+
if (!Node.isJsxElement(parent) && !Node.isJsxFragment(parent))
|
|
397
|
+
return false;
|
|
398
|
+
const expr = node.getExpression();
|
|
399
|
+
if (!expr)
|
|
400
|
+
return false;
|
|
401
|
+
if (Node.isStringLiteral(expr))
|
|
402
|
+
return true;
|
|
403
|
+
if (Node.isNoSubstitutionTemplateLiteral(expr))
|
|
404
|
+
return true;
|
|
405
|
+
return false;
|
|
406
|
+
}
|
|
407
|
+
return false;
|
|
408
|
+
},
|
|
409
|
+
};
|
|
410
|
+
}
|
|
411
|
+
/**
|
|
412
|
+
* Match type assertion expressions — both `as Type` and the legacy
|
|
413
|
+
* angle-bracket `<Type>value` form.
|
|
414
|
+
*
|
|
415
|
+
* By default, `as const` is excluded from matches (it narrows types rather
|
|
416
|
+
* than widening them, so it's idiomatic for literal preservation). The
|
|
417
|
+
* `allowConst` option does not affect angle-bracket assertions — those
|
|
418
|
+
* can't express `as const` and are always matched.
|
|
419
|
+
*
|
|
420
|
+
* Does NOT match `satisfies` expressions — those are validation, not
|
|
421
|
+
* assertions, and should not be banned.
|
|
422
|
+
*
|
|
423
|
+
* @example
|
|
424
|
+
* // Allow `as const` in user code (default)
|
|
425
|
+
* classes(p).should().notContain(typeAssertion()).check()
|
|
426
|
+
*
|
|
427
|
+
* @example
|
|
428
|
+
* // Ban `as const` too — no `as` expressions of any kind
|
|
429
|
+
* modules(p).should().notContain(typeAssertion({ allowConst: false })).check()
|
|
430
|
+
*/
|
|
431
|
+
export function typeAssertion(options) {
|
|
432
|
+
const allowConst = options?.allowConst ?? true;
|
|
433
|
+
return {
|
|
434
|
+
description: 'type assertion',
|
|
435
|
+
syntaxKinds: [SyntaxKind.AsExpression, SyntaxKind.TypeAssertionExpression],
|
|
436
|
+
matches(node) {
|
|
437
|
+
// Angle-bracket `<Type>value` — can't be `as const`, always matches
|
|
438
|
+
if (Node.isTypeAssertion(node))
|
|
439
|
+
return true;
|
|
440
|
+
if (!Node.isAsExpression(node))
|
|
441
|
+
return false;
|
|
442
|
+
if (!allowConst)
|
|
443
|
+
return true;
|
|
444
|
+
const typeNode = node.getTypeNode();
|
|
445
|
+
if (typeNode && Node.isTypeReference(typeNode)) {
|
|
446
|
+
const typeName = typeNode.getTypeName();
|
|
447
|
+
if (Node.isIdentifier(typeName) && typeName.getText() === 'const') {
|
|
448
|
+
return false;
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
return true;
|
|
452
|
+
},
|
|
453
|
+
};
|
|
454
|
+
}
|
|
455
|
+
/**
|
|
456
|
+
* Match `!` non-null assertion expressions.
|
|
457
|
+
*
|
|
458
|
+
* @example
|
|
459
|
+
* functions(p).should().notContain(nonNullAssertion()).check()
|
|
460
|
+
*/
|
|
461
|
+
export function nonNullAssertion() {
|
|
462
|
+
return {
|
|
463
|
+
description: 'non-null assertion',
|
|
464
|
+
syntaxKinds: [SyntaxKind.NonNullExpression],
|
|
465
|
+
matches(node) {
|
|
466
|
+
return Node.isNonNullExpression(node);
|
|
467
|
+
},
|
|
468
|
+
};
|
|
469
|
+
}
|
|
470
|
+
//# sourceMappingURL=matchers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"matchers.js","sourceRoot":"","sources":["../../src/helpers/matchers.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { TypeMatcher } from '../helpers/type-matchers.js';
|
|
2
|
+
/**
|
|
3
|
+
* A shape constraint for a single property in a return type.
|
|
4
|
+
*
|
|
5
|
+
* - `string` — matched as regex against `Type.getText()` (e.g. `'number'`, `'string'`)
|
|
6
|
+
* - `'T[]'` — matches any array type regardless of element type
|
|
7
|
+
* - `TypeMatcher` — full programmatic control (reuse existing matchers)
|
|
8
|
+
*/
|
|
9
|
+
export type PropertyConstraint = string | TypeMatcher;
|
|
10
|
+
/**
|
|
11
|
+
* A named architectural pattern that describes expected return type shape.
|
|
12
|
+
*/
|
|
13
|
+
export interface ArchPattern {
|
|
14
|
+
/** Human-readable pattern name, e.g. 'paginated-collection' */
|
|
15
|
+
readonly name: string;
|
|
16
|
+
/** Required properties and their type constraints on the return type */
|
|
17
|
+
readonly returnShape: Record<string, PropertyConstraint>;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Define a reusable architectural pattern.
|
|
21
|
+
*
|
|
22
|
+
* @example
|
|
23
|
+
* ```ts
|
|
24
|
+
* const paginatedCollection = definePattern('paginated-collection', {
|
|
25
|
+
* returnShape: {
|
|
26
|
+
* total: 'number',
|
|
27
|
+
* skip: 'number',
|
|
28
|
+
* limit: 'number',
|
|
29
|
+
* items: 'T[]',
|
|
30
|
+
* },
|
|
31
|
+
* })
|
|
32
|
+
* ```
|
|
33
|
+
*/
|
|
34
|
+
export declare function definePattern(name: string, options: {
|
|
35
|
+
returnShape: Record<string, PropertyConstraint>;
|
|
36
|
+
}): ArchPattern;
|
|
37
|
+
//# sourceMappingURL=pattern.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pattern.d.ts","sourceRoot":"","sources":["../../src/helpers/pattern.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,6BAA6B,CAAA;AAE9D;;;;;;GAMG;AACH,MAAM,MAAM,kBAAkB,GAAG,MAAM,GAAG,WAAW,CAAA;AAErD;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,+DAA+D;IAC/D,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,wEAAwE;IACxE,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,kBAAkB,CAAC,CAAA;CACzD;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,aAAa,CAC3B,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE;IAAE,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,kBAAkB,CAAC,CAAA;CAAE,GAC3D,WAAW,CAKb"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Define a reusable architectural pattern.
|
|
3
|
+
*
|
|
4
|
+
* @example
|
|
5
|
+
* ```ts
|
|
6
|
+
* const paginatedCollection = definePattern('paginated-collection', {
|
|
7
|
+
* returnShape: {
|
|
8
|
+
* total: 'number',
|
|
9
|
+
* skip: 'number',
|
|
10
|
+
* limit: 'number',
|
|
11
|
+
* items: 'T[]',
|
|
12
|
+
* },
|
|
13
|
+
* })
|
|
14
|
+
* ```
|
|
15
|
+
*/
|
|
16
|
+
export function definePattern(name, options) {
|
|
17
|
+
return {
|
|
18
|
+
name,
|
|
19
|
+
returnShape: options.returnShape,
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
//# sourceMappingURL=pattern.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pattern.js","sourceRoot":"","sources":["../../src/helpers/pattern.ts"],"names":[],"mappings":"AAqBA;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,aAAa,CAC3B,IAAY,EACZ,OAA4D;IAE5D,OAAO;QACL,IAAI;QACJ,WAAW,EAAE,OAAO,CAAC,WAAW;KACjC,CAAA;AACH,CAAC"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { SourceFile } from 'ts-morph';
|
|
2
|
+
import type { Slice } from '../models/slice.js';
|
|
3
|
+
/**
|
|
4
|
+
* An edge in the slice dependency graph.
|
|
5
|
+
* Represents: a file in `from` imports a file in `to`.
|
|
6
|
+
*/
|
|
7
|
+
export interface SliceEdge {
|
|
8
|
+
from: string;
|
|
9
|
+
to: string;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Build a reverse lookup map: file path -> slice name.
|
|
13
|
+
* Shared by buildSliceDependencyGraph and findSliceDependencyDetails.
|
|
14
|
+
*/
|
|
15
|
+
export declare function buildFileToSliceMap(slices: Slice[]): Map<string, string>;
|
|
16
|
+
export declare function buildSliceDependencyGraph(slices: Slice[], fileToSlice?: Map<string, string>): SliceEdge[];
|
|
17
|
+
/**
|
|
18
|
+
* Find which specific files cause a dependency from one slice to another.
|
|
19
|
+
* Used for detailed violation messages.
|
|
20
|
+
*
|
|
21
|
+
* @param slices - The resolved slices
|
|
22
|
+
* @param fromSliceName - Source slice name
|
|
23
|
+
* @param toSliceName - Target slice name
|
|
24
|
+
* @param fileToSlice - Pre-built file-to-slice map (optional, built internally if not provided)
|
|
25
|
+
* @returns Array of { sourceFile, importPath, importLine }
|
|
26
|
+
*/
|
|
27
|
+
export declare function findSliceDependencyDetails(slices: Slice[], fromSliceName: string, toSliceName: string, fileToSlice?: Map<string, string>): Array<{
|
|
28
|
+
sourceFile: SourceFile;
|
|
29
|
+
importPath: string;
|
|
30
|
+
importLine: number;
|
|
31
|
+
}>;
|
|
32
|
+
//# sourceMappingURL=slice-graph.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"slice-graph.d.ts","sourceRoot":"","sources":["../../src/helpers/slice-graph.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,UAAU,CAAA;AAC1C,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAA;AAG/C;;;GAGG;AAEH,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,MAAM,CAAA;IACZ,EAAE,EAAE,MAAM,CAAA;CACX;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,KAAK,EAAE,GAAG,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAQxE;AAsCD,wBAAgB,yBAAyB,CACvC,MAAM,EAAE,KAAK,EAAE,EACf,WAAW,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,GAChC,SAAS,EAAE,CAcb;AAED;;;;;;;;;GASG;AACH,wBAAgB,0BAA0B,CACxC,MAAM,EAAE,KAAK,EAAE,EACf,aAAa,EAAE,MAAM,EACrB,WAAW,EAAE,MAAM,EACnB,WAAW,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,GAChC,KAAK,CAAC;IAAE,UAAU,EAAE,UAAU,CAAC;IAAC,UAAU,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,CAAC,CAwB3E"}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { getDependencyDecls } from '../core/module-dependencies.js';
|
|
2
|
+
/**
|
|
3
|
+
* Build a reverse lookup map: file path -> slice name.
|
|
4
|
+
* Shared by buildSliceDependencyGraph and findSliceDependencyDetails.
|
|
5
|
+
*/
|
|
6
|
+
export function buildFileToSliceMap(slices) {
|
|
7
|
+
const fileToSlice = new Map();
|
|
8
|
+
for (const slice of slices) {
|
|
9
|
+
for (const file of slice.files) {
|
|
10
|
+
fileToSlice.set(file.getFilePath(), slice.name);
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
return fileToSlice;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Build a directed dependency graph between slices.
|
|
17
|
+
*
|
|
18
|
+
* For each file in each slice, resolve its imports. If an imported file
|
|
19
|
+
* belongs to a different slice, add a directed edge from the importing
|
|
20
|
+
* slice to the imported slice.
|
|
21
|
+
*
|
|
22
|
+
* @param slices - The resolved slices
|
|
23
|
+
* @param fileToSlice - Pre-built file-to-slice map (optional, built internally if not provided)
|
|
24
|
+
* @returns Unique directed edges between slices
|
|
25
|
+
*/
|
|
26
|
+
/**
|
|
27
|
+
* Collect unique slice edges from a single file's imports.
|
|
28
|
+
*/
|
|
29
|
+
function collectEdgesFromFile(file, sliceName, fileToSlice, edgeSet, edges) {
|
|
30
|
+
for (const importDecl of getDependencyDecls(file)) {
|
|
31
|
+
const resolved = importDecl.getModuleSpecifierSourceFile();
|
|
32
|
+
if (!resolved)
|
|
33
|
+
continue;
|
|
34
|
+
const targetSlice = fileToSlice.get(resolved.getFilePath());
|
|
35
|
+
if (targetSlice && targetSlice !== sliceName) {
|
|
36
|
+
const edgeKey = `${sliceName}->${targetSlice}`;
|
|
37
|
+
if (!edgeSet.has(edgeKey)) {
|
|
38
|
+
edgeSet.add(edgeKey);
|
|
39
|
+
edges.push({ from: sliceName, to: targetSlice });
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
export function buildSliceDependencyGraph(slices, fileToSlice) {
|
|
45
|
+
const map = fileToSlice ?? buildFileToSliceMap(slices);
|
|
46
|
+
// Collect unique edges
|
|
47
|
+
const edgeSet = new Set();
|
|
48
|
+
const edges = [];
|
|
49
|
+
for (const slice of slices) {
|
|
50
|
+
for (const file of slice.files) {
|
|
51
|
+
collectEdgesFromFile(file, slice.name, map, edgeSet, edges);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
return edges;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Find which specific files cause a dependency from one slice to another.
|
|
58
|
+
* Used for detailed violation messages.
|
|
59
|
+
*
|
|
60
|
+
* @param slices - The resolved slices
|
|
61
|
+
* @param fromSliceName - Source slice name
|
|
62
|
+
* @param toSliceName - Target slice name
|
|
63
|
+
* @param fileToSlice - Pre-built file-to-slice map (optional, built internally if not provided)
|
|
64
|
+
* @returns Array of { sourceFile, importPath, importLine }
|
|
65
|
+
*/
|
|
66
|
+
export function findSliceDependencyDetails(slices, fromSliceName, toSliceName, fileToSlice) {
|
|
67
|
+
const map = fileToSlice ?? buildFileToSliceMap(slices);
|
|
68
|
+
const fromSlice = slices.find((s) => s.name === fromSliceName);
|
|
69
|
+
if (!fromSlice)
|
|
70
|
+
return [];
|
|
71
|
+
const details = [];
|
|
72
|
+
for (const file of fromSlice.files) {
|
|
73
|
+
for (const importDecl of getDependencyDecls(file)) {
|
|
74
|
+
const resolved = importDecl.getModuleSpecifierSourceFile();
|
|
75
|
+
if (!resolved)
|
|
76
|
+
continue;
|
|
77
|
+
const targetSlice = map.get(resolved.getFilePath());
|
|
78
|
+
if (targetSlice === toSliceName) {
|
|
79
|
+
details.push({
|
|
80
|
+
sourceFile: file,
|
|
81
|
+
importPath: resolved.getFilePath(),
|
|
82
|
+
importLine: importDecl.getStartLineNumber(),
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
return details;
|
|
88
|
+
}
|
|
89
|
+
//# sourceMappingURL=slice-graph.js.map
|