@ontrails/warden 1.0.0-beta.13 → 1.0.0-beta.15
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/.turbo/turbo-lint.log +1 -1
- package/CHANGELOG.md +30 -0
- package/README.md +31 -20
- package/dist/cli.d.ts +19 -2
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +261 -64
- package/dist/cli.js.map +1 -1
- package/dist/draft.d.ts +5 -0
- package/dist/draft.d.ts.map +1 -0
- package/dist/draft.js +16 -0
- package/dist/draft.js.map +1 -0
- package/dist/drift.d.ts +10 -7
- package/dist/drift.d.ts.map +1 -1
- package/dist/drift.js +50 -16
- package/dist/drift.js.map +1 -1
- package/dist/formatters.d.ts +2 -1
- package/dist/formatters.d.ts.map +1 -1
- package/dist/formatters.js +15 -4
- package/dist/formatters.js.map +1 -1
- package/dist/index.d.ts +9 -17
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +10 -17
- package/dist/index.js.map +1 -1
- package/dist/rules/ast.d.ts +412 -7
- package/dist/rules/ast.d.ts.map +1 -1
- package/dist/rules/ast.js +1847 -102
- package/dist/rules/ast.js.map +1 -1
- package/dist/rules/circular-refs.d.ts +6 -0
- package/dist/rules/circular-refs.d.ts.map +1 -0
- package/dist/rules/circular-refs.js +83 -0
- package/dist/rules/circular-refs.js.map +1 -0
- package/dist/rules/context-no-surface-types.d.ts.map +1 -1
- package/dist/rules/context-no-surface-types.js +59 -3
- package/dist/rules/context-no-surface-types.js.map +1 -1
- package/dist/rules/contour-exists.d.ts +7 -0
- package/dist/rules/contour-exists.d.ts.map +1 -0
- package/dist/rules/contour-exists.js +113 -0
- package/dist/rules/contour-exists.js.map +1 -0
- package/dist/rules/contour-ids.d.ts +10 -0
- package/dist/rules/contour-ids.d.ts.map +1 -0
- package/dist/rules/contour-ids.js +12 -0
- package/dist/rules/contour-ids.js.map +1 -0
- package/dist/rules/cross-declarations.d.ts.map +1 -1
- package/dist/rules/cross-declarations.js +171 -57
- package/dist/rules/cross-declarations.js.map +1 -1
- package/dist/rules/dead-internal-trail.d.ts +3 -0
- package/dist/rules/dead-internal-trail.d.ts.map +1 -0
- package/dist/rules/dead-internal-trail.js +80 -0
- package/dist/rules/dead-internal-trail.js.map +1 -0
- package/dist/rules/draft-file-marking.d.ts +6 -0
- package/dist/rules/draft-file-marking.d.ts.map +1 -0
- package/dist/rules/draft-file-marking.js +87 -0
- package/dist/rules/draft-file-marking.js.map +1 -0
- package/dist/rules/draft-visible-debt.d.ts +12 -0
- package/dist/rules/draft-visible-debt.d.ts.map +1 -0
- package/dist/rules/draft-visible-debt.js +50 -0
- package/dist/rules/draft-visible-debt.js.map +1 -0
- package/dist/rules/error-mapping-completeness.d.ts +13 -0
- package/dist/rules/error-mapping-completeness.d.ts.map +1 -0
- package/dist/rules/error-mapping-completeness.js +160 -0
- package/dist/rules/error-mapping-completeness.js.map +1 -0
- package/dist/rules/example-valid.d.ts +6 -0
- package/dist/rules/example-valid.d.ts.map +1 -0
- package/dist/rules/example-valid.js +203 -0
- package/dist/rules/example-valid.js.map +1 -0
- package/dist/rules/fires-declarations.d.ts +16 -0
- package/dist/rules/fires-declarations.d.ts.map +1 -0
- package/dist/rules/fires-declarations.js +444 -0
- package/dist/rules/fires-declarations.js.map +1 -0
- package/dist/rules/implementation-returns-result.d.ts +9 -0
- package/dist/rules/implementation-returns-result.d.ts.map +1 -1
- package/dist/rules/implementation-returns-result.js +638 -76
- package/dist/rules/implementation-returns-result.js.map +1 -1
- package/dist/rules/incomplete-accessor-for-standard-op.d.ts +30 -0
- package/dist/rules/incomplete-accessor-for-standard-op.d.ts.map +1 -0
- package/dist/rules/incomplete-accessor-for-standard-op.js +226 -0
- package/dist/rules/incomplete-accessor-for-standard-op.js.map +1 -0
- package/dist/rules/incomplete-crud.d.ts +21 -0
- package/dist/rules/incomplete-crud.d.ts.map +1 -0
- package/dist/rules/incomplete-crud.js +368 -0
- package/dist/rules/incomplete-crud.js.map +1 -0
- package/dist/rules/index.d.ts +40 -7
- package/dist/rules/index.d.ts.map +1 -1
- package/dist/rules/index.js +91 -15
- package/dist/rules/index.js.map +1 -1
- package/dist/rules/intent-propagation.d.ts +3 -0
- package/dist/rules/intent-propagation.d.ts.map +1 -0
- package/dist/rules/intent-propagation.js +57 -0
- package/dist/rules/intent-propagation.js.map +1 -0
- package/dist/rules/missing-reconcile.d.ts +3 -0
- package/dist/rules/missing-reconcile.d.ts.map +1 -0
- package/dist/rules/missing-reconcile.js +44 -0
- package/dist/rules/missing-reconcile.js.map +1 -0
- package/dist/rules/missing-visibility.d.ts +3 -0
- package/dist/rules/missing-visibility.d.ts.map +1 -0
- package/dist/rules/missing-visibility.js +63 -0
- package/dist/rules/missing-visibility.js.map +1 -0
- package/dist/rules/no-direct-impl-in-route.d.ts.map +1 -1
- package/dist/rules/no-direct-impl-in-route.js +0 -3
- package/dist/rules/no-direct-impl-in-route.js.map +1 -1
- package/dist/rules/no-direct-implementation-call.js +1 -1
- package/dist/rules/no-direct-implementation-call.js.map +1 -1
- package/dist/rules/no-sync-result-assumption.d.ts.map +1 -1
- package/dist/rules/no-sync-result-assumption.js +870 -61
- package/dist/rules/no-sync-result-assumption.js.map +1 -1
- package/dist/rules/no-throw-in-detour-recover.d.ts +3 -0
- package/dist/rules/no-throw-in-detour-recover.d.ts.map +1 -0
- package/dist/rules/no-throw-in-detour-recover.js +147 -0
- package/dist/rules/no-throw-in-detour-recover.js.map +1 -0
- package/dist/rules/no-throw-in-detour-target.d.ts +4 -1
- package/dist/rules/no-throw-in-detour-target.d.ts.map +1 -1
- package/dist/rules/no-throw-in-detour-target.js +6 -3
- package/dist/rules/no-throw-in-detour-target.js.map +1 -1
- package/dist/rules/no-throw-in-implementation.d.ts +4 -2
- package/dist/rules/no-throw-in-implementation.d.ts.map +1 -1
- package/dist/rules/no-throw-in-implementation.js +6 -4
- package/dist/rules/no-throw-in-implementation.js.map +1 -1
- package/dist/rules/on-references-exist.d.ts +14 -0
- package/dist/rules/on-references-exist.d.ts.map +1 -0
- package/dist/rules/on-references-exist.js +109 -0
- package/dist/rules/on-references-exist.js.map +1 -0
- package/dist/rules/orphaned-signal.d.ts +3 -0
- package/dist/rules/orphaned-signal.d.ts.map +1 -0
- package/dist/rules/orphaned-signal.js +67 -0
- package/dist/rules/orphaned-signal.js.map +1 -0
- package/dist/rules/permit-governance.d.ts +3 -0
- package/dist/rules/permit-governance.d.ts.map +1 -0
- package/dist/rules/permit-governance.js +15 -0
- package/dist/rules/permit-governance.js.map +1 -0
- package/dist/rules/reference-exists.d.ts +6 -0
- package/dist/rules/reference-exists.d.ts.map +1 -0
- package/dist/rules/reference-exists.js +47 -0
- package/dist/rules/reference-exists.js.map +1 -0
- package/dist/rules/registry-names.d.ts +8 -0
- package/dist/rules/registry-names.d.ts.map +1 -0
- package/dist/rules/registry-names.js +83 -0
- package/dist/rules/registry-names.js.map +1 -0
- package/dist/rules/resource-declarations.d.ts +14 -0
- package/dist/rules/resource-declarations.d.ts.map +1 -0
- package/dist/rules/resource-declarations.js +413 -0
- package/dist/rules/resource-declarations.js.map +1 -0
- package/dist/rules/resource-exists.d.ts +6 -0
- package/dist/rules/resource-exists.d.ts.map +1 -0
- package/dist/rules/resource-exists.js +90 -0
- package/dist/rules/resource-exists.js.map +1 -0
- package/dist/rules/resource-id-grammar.d.ts +3 -0
- package/dist/rules/resource-id-grammar.d.ts.map +1 -0
- package/dist/rules/resource-id-grammar.js +39 -0
- package/dist/rules/resource-id-grammar.js.map +1 -0
- package/dist/rules/specs.d.ts.map +1 -1
- package/dist/rules/specs.js +5 -1
- package/dist/rules/specs.js.map +1 -1
- package/dist/rules/types.d.ts +53 -4
- package/dist/rules/types.d.ts.map +1 -1
- package/dist/rules/unreachable-detour-shadowing.d.ts +3 -0
- package/dist/rules/unreachable-detour-shadowing.d.ts.map +1 -0
- package/dist/rules/unreachable-detour-shadowing.js +202 -0
- package/dist/rules/unreachable-detour-shadowing.js.map +1 -0
- package/dist/rules/valid-describe-refs.d.ts.map +1 -1
- package/dist/rules/valid-describe-refs.js +132 -16
- package/dist/rules/valid-describe-refs.js.map +1 -1
- package/dist/rules/valid-detour-contract.d.ts +3 -0
- package/dist/rules/valid-detour-contract.d.ts.map +1 -0
- package/dist/rules/valid-detour-contract.js +47 -0
- package/dist/rules/valid-detour-contract.js.map +1 -0
- package/dist/rules/valid-detour-refs.d.ts.map +1 -1
- package/dist/rules/valid-detour-refs.js +73 -82
- package/dist/rules/valid-detour-refs.js.map +1 -1
- package/dist/rules/warden-export-symmetry.d.ts +7 -0
- package/dist/rules/warden-export-symmetry.d.ts.map +1 -0
- package/dist/rules/warden-export-symmetry.js +352 -0
- package/dist/rules/warden-export-symmetry.js.map +1 -0
- package/dist/rules/warden-rules-use-ast.d.ts +17 -0
- package/dist/rules/warden-rules-use-ast.d.ts.map +1 -0
- package/dist/rules/warden-rules-use-ast.js +778 -0
- package/dist/rules/warden-rules-use-ast.js.map +1 -0
- package/dist/trails/circular-refs.trail.d.ts +24 -0
- package/dist/trails/circular-refs.trail.d.ts.map +1 -0
- package/dist/trails/circular-refs.trail.js +29 -0
- package/dist/trails/circular-refs.trail.js.map +1 -0
- package/dist/trails/context-no-surface-types.trail.d.ts +2 -2
- package/dist/trails/context-no-surface-types.trail.d.ts.map +1 -1
- package/dist/trails/context-no-trailhead-types.trail.d.ts +2 -2
- package/dist/trails/context-no-trailhead-types.trail.d.ts.map +1 -1
- package/dist/trails/contour-exists.trail.d.ts +24 -0
- package/dist/trails/contour-exists.trail.d.ts.map +1 -0
- package/dist/trails/contour-exists.trail.js +21 -0
- package/dist/trails/contour-exists.trail.js.map +1 -0
- package/dist/trails/cross-declarations.trail.d.ts +2 -2
- package/dist/trails/cross-declarations.trail.d.ts.map +1 -1
- package/dist/trails/dead-internal-trail.trail.d.ts +24 -0
- package/dist/trails/dead-internal-trail.trail.d.ts.map +1 -0
- package/dist/trails/dead-internal-trail.trail.js +26 -0
- package/dist/trails/dead-internal-trail.trail.js.map +1 -0
- package/dist/trails/{provision-declarations.trail.d.ts → draft-file-marking.trail.d.ts} +3 -3
- package/dist/trails/draft-file-marking.trail.d.ts.map +1 -0
- package/dist/trails/draft-file-marking.trail.js +16 -0
- package/dist/trails/draft-file-marking.trail.js.map +1 -0
- package/dist/trails/draft-visible-debt.trail.d.ts +13 -0
- package/dist/trails/draft-visible-debt.trail.d.ts.map +1 -0
- package/dist/trails/draft-visible-debt.trail.js +16 -0
- package/dist/trails/draft-visible-debt.trail.js.map +1 -0
- package/dist/trails/error-mapping-completeness.trail.d.ts +13 -0
- package/dist/trails/error-mapping-completeness.trail.d.ts.map +1 -0
- package/dist/trails/error-mapping-completeness.trail.js +29 -0
- package/dist/trails/error-mapping-completeness.trail.js.map +1 -0
- package/dist/trails/{follow-declarations.trail.d.ts → example-valid.trail.d.ts} +3 -3
- package/dist/trails/example-valid.trail.d.ts.map +1 -0
- package/dist/trails/example-valid.trail.js +25 -0
- package/dist/trails/example-valid.trail.js.map +1 -0
- package/dist/trails/fires-declarations.trail.d.ts +13 -0
- package/dist/trails/fires-declarations.trail.d.ts.map +1 -0
- package/dist/trails/fires-declarations.trail.js +22 -0
- package/dist/trails/fires-declarations.trail.js.map +1 -0
- package/dist/trails/implementation-returns-result.trail.d.ts +2 -2
- package/dist/trails/implementation-returns-result.trail.d.ts.map +1 -1
- package/dist/trails/incomplete-accessor-for-standard-op.trail.d.ts +12 -0
- package/dist/trails/incomplete-accessor-for-standard-op.trail.d.ts.map +1 -0
- package/dist/trails/incomplete-accessor-for-standard-op.trail.js +60 -0
- package/dist/trails/incomplete-accessor-for-standard-op.trail.js.map +1 -0
- package/dist/trails/incomplete-crud.trail.d.ts +24 -0
- package/dist/trails/incomplete-crud.trail.d.ts.map +1 -0
- package/dist/trails/incomplete-crud.trail.js +39 -0
- package/dist/trails/incomplete-crud.trail.js.map +1 -0
- package/dist/trails/index.d.ts +29 -7
- package/dist/trails/index.d.ts.map +1 -1
- package/dist/trails/index.js +28 -6
- package/dist/trails/index.js.map +1 -1
- package/dist/trails/intent-propagation.trail.d.ts +24 -0
- package/dist/trails/intent-propagation.trail.d.ts.map +1 -0
- package/dist/trails/intent-propagation.trail.js +30 -0
- package/dist/trails/intent-propagation.trail.js.map +1 -0
- package/dist/trails/missing-reconcile.trail.d.ts +24 -0
- package/dist/trails/missing-reconcile.trail.d.ts.map +1 -0
- package/dist/trails/missing-reconcile.trail.js +33 -0
- package/dist/trails/missing-reconcile.trail.js.map +1 -0
- package/dist/trails/missing-visibility.trail.d.ts +24 -0
- package/dist/trails/missing-visibility.trail.d.ts.map +1 -0
- package/dist/trails/missing-visibility.trail.js +22 -0
- package/dist/trails/missing-visibility.trail.js.map +1 -0
- package/dist/trails/no-direct-impl-in-route.trail.d.ts +2 -2
- package/dist/trails/no-direct-impl-in-route.trail.d.ts.map +1 -1
- package/dist/trails/no-direct-implementation-call.trail.d.ts +2 -2
- package/dist/trails/no-direct-implementation-call.trail.d.ts.map +1 -1
- package/dist/trails/no-sync-result-assumption.trail.d.ts +2 -2
- package/dist/trails/no-sync-result-assumption.trail.d.ts.map +1 -1
- package/dist/trails/no-throw-in-detour-recover.trail.d.ts +13 -0
- package/dist/trails/no-throw-in-detour-recover.trail.d.ts.map +1 -0
- package/dist/trails/no-throw-in-detour-recover.trail.js +24 -0
- package/dist/trails/no-throw-in-detour-recover.trail.js.map +1 -0
- package/dist/trails/no-throw-in-detour-target.trail.d.ts +13 -3
- package/dist/trails/no-throw-in-detour-target.trail.d.ts.map +1 -1
- package/dist/trails/no-throw-in-implementation.trail.d.ts +2 -2
- package/dist/trails/no-throw-in-implementation.trail.d.ts.map +1 -1
- package/dist/trails/on-references-exist.trail.d.ts +24 -0
- package/dist/trails/on-references-exist.trail.d.ts.map +1 -0
- package/dist/trails/on-references-exist.trail.js +21 -0
- package/dist/trails/on-references-exist.trail.js.map +1 -0
- package/dist/trails/orphaned-signal.trail.d.ts +24 -0
- package/dist/trails/orphaned-signal.trail.d.ts.map +1 -0
- package/dist/trails/orphaned-signal.trail.js +36 -0
- package/dist/trails/orphaned-signal.trail.js.map +1 -0
- package/dist/trails/permit-governance.trail.d.ts +12 -0
- package/dist/trails/permit-governance.trail.d.ts.map +1 -0
- package/dist/trails/permit-governance.trail.js +47 -0
- package/dist/trails/permit-governance.trail.js.map +1 -0
- package/dist/trails/prefer-schema-inference.trail.d.ts +2 -2
- package/dist/trails/prefer-schema-inference.trail.d.ts.map +1 -1
- package/dist/trails/reference-exists.trail.d.ts +24 -0
- package/dist/trails/reference-exists.trail.d.ts.map +1 -0
- package/dist/trails/reference-exists.trail.js +25 -0
- package/dist/trails/reference-exists.trail.js.map +1 -0
- package/dist/trails/resource-declarations.trail.d.ts +13 -0
- package/dist/trails/resource-declarations.trail.d.ts.map +1 -0
- package/dist/trails/{provision-declarations.trail.js → resource-declarations.trail.js} +7 -7
- package/dist/trails/resource-declarations.trail.js.map +1 -0
- package/dist/trails/resource-exists.trail.d.ts +24 -0
- package/dist/trails/resource-exists.trail.d.ts.map +1 -0
- package/dist/trails/{provision-exists.trail.js → resource-exists.trail.js} +8 -8
- package/dist/trails/resource-exists.trail.js.map +1 -0
- package/dist/trails/resource-id-grammar.trail.d.ts +13 -0
- package/dist/trails/resource-id-grammar.trail.d.ts.map +1 -0
- package/dist/trails/resource-id-grammar.trail.js +38 -0
- package/dist/trails/resource-id-grammar.trail.js.map +1 -0
- package/dist/trails/run.d.ts +25 -9
- package/dist/trails/run.d.ts.map +1 -1
- package/dist/trails/run.js +63 -19
- package/dist/trails/run.js.map +1 -1
- package/dist/trails/schema.d.ts +28 -3
- package/dist/trails/schema.d.ts.map +1 -1
- package/dist/trails/schema.js +57 -4
- package/dist/trails/schema.js.map +1 -1
- package/dist/trails/unreachable-detour-shadowing.trail.d.ts +13 -0
- package/dist/trails/unreachable-detour-shadowing.trail.d.ts.map +1 -0
- package/dist/trails/unreachable-detour-shadowing.trail.js +44 -0
- package/dist/trails/unreachable-detour-shadowing.trail.js.map +1 -0
- package/dist/trails/valid-describe-refs.trail.d.ts +12 -3
- package/dist/trails/valid-describe-refs.trail.d.ts.map +1 -1
- package/dist/trails/valid-detour-contract.trail.d.ts +12 -0
- package/dist/trails/valid-detour-contract.trail.d.ts.map +1 -0
- package/dist/trails/valid-detour-contract.trail.js +66 -0
- package/dist/trails/valid-detour-contract.trail.js.map +1 -0
- package/dist/trails/valid-detour-refs.trail.d.ts +13 -3
- package/dist/trails/valid-detour-refs.trail.d.ts.map +1 -1
- package/dist/trails/warden-export-symmetry.trail.d.ts +13 -0
- package/dist/trails/warden-export-symmetry.trail.d.ts.map +1 -0
- package/dist/trails/warden-export-symmetry.trail.js +16 -0
- package/dist/trails/warden-export-symmetry.trail.js.map +1 -0
- package/dist/trails/warden-rules-use-ast.trail.d.ts +13 -0
- package/dist/trails/warden-rules-use-ast.trail.d.ts.map +1 -0
- package/dist/trails/warden-rules-use-ast.trail.js +41 -0
- package/dist/trails/warden-rules-use-ast.trail.js.map +1 -0
- package/dist/trails/wrap-rule.d.ts +16 -2
- package/dist/trails/wrap-rule.d.ts.map +1 -1
- package/dist/trails/wrap-rule.js +71 -11
- package/dist/trails/wrap-rule.js.map +1 -1
- package/package.json +7 -4
- package/src/__tests__/ast.test.ts +613 -0
- package/src/__tests__/circular-refs.test.ts +121 -0
- package/src/__tests__/cli.test.ts +360 -32
- package/src/__tests__/contour-exists.test.ts +203 -0
- package/src/__tests__/cross-declarations.test.ts +245 -0
- package/src/__tests__/dead-internal-trail.test.ts +81 -0
- package/src/__tests__/draft-rules-context.test.ts +150 -0
- package/src/__tests__/drift.test.ts +75 -5
- package/src/__tests__/error-mapping-completeness.test.ts +56 -0
- package/src/__tests__/example-valid.test.ts +101 -0
- package/src/__tests__/fires-declarations-param-destructure.test.ts +54 -0
- package/src/__tests__/fires-declarations.test.ts +652 -0
- package/src/__tests__/formatters.test.ts +2 -2
- package/src/__tests__/implementation-returns-result.test.ts +1016 -2
- package/src/__tests__/incomplete-accessor-for-standard-op.test.ts +337 -0
- package/src/__tests__/incomplete-crud.test.ts +498 -0
- package/src/__tests__/intent-propagation.test.ts +116 -0
- package/src/__tests__/missing-reconcile.test.ts +154 -0
- package/src/__tests__/missing-visibility.test.ts +108 -0
- package/src/__tests__/no-sync-result-assumption.test.ts +870 -39
- package/src/__tests__/no-throw-in-detour-recover.test.ts +93 -0
- package/src/__tests__/no-throw-in-implementation.test.ts +88 -0
- package/src/__tests__/on-references-exist.test.ts +151 -0
- package/src/__tests__/orphaned-signal.test.ts +137 -0
- package/src/__tests__/permit-governance.test.ts +66 -0
- package/src/__tests__/reference-exists.test.ts +281 -0
- package/src/__tests__/resource-declarations.test.ts +448 -0
- package/src/__tests__/resource-exists.test.ts +122 -0
- package/src/__tests__/resource-id-grammar.test.ts +50 -0
- package/src/__tests__/rules.test.ts +17 -77
- package/src/__tests__/topo-aware-rule.test.ts +257 -0
- package/src/__tests__/trails.test.ts +2 -2
- package/src/__tests__/unreachable-detour-shadowing.test.ts +128 -0
- package/src/__tests__/valid-describe-refs.test.ts +183 -0
- package/src/__tests__/valid-detour-contract.test.ts +86 -0
- package/src/__tests__/warden-export-symmetry.test.ts +251 -0
- package/src/__tests__/warden-rules-use-ast.test.ts +468 -0
- package/src/__tests__/wrap-rule.test.ts +3 -3
- package/src/cli.ts +458 -91
- package/src/draft.ts +22 -0
- package/src/drift.ts +63 -21
- package/src/formatters.ts +15 -4
- package/src/index.ts +62 -23
- package/src/rules/ast.ts +2715 -119
- package/src/rules/circular-refs.ts +154 -0
- package/src/rules/{context-no-trailhead-types.ts → context-no-surface-types.ts} +72 -12
- package/src/rules/contour-exists.ts +251 -0
- package/src/rules/contour-ids.ts +15 -0
- package/src/rules/cross-declarations.ts +277 -69
- package/src/rules/dead-internal-trail.ts +141 -0
- package/src/rules/draft-file-marking.ts +160 -0
- package/src/rules/draft-visible-debt.ts +87 -0
- package/src/rules/error-mapping-completeness.ts +273 -0
- package/src/rules/example-valid.ts +401 -0
- package/src/rules/fires-declarations.ts +609 -0
- package/src/rules/implementation-returns-result.ts +1042 -122
- package/src/rules/incomplete-accessor-for-standard-op.ts +315 -0
- package/src/rules/incomplete-crud.ts +579 -0
- package/src/rules/index.ts +95 -16
- package/src/rules/intent-propagation.ts +142 -0
- package/src/rules/missing-reconcile.ts +98 -0
- package/src/rules/missing-visibility.ts +110 -0
- package/src/rules/no-direct-impl-in-route.ts +0 -4
- package/src/rules/no-direct-implementation-call.ts +1 -1
- package/src/rules/no-sync-result-assumption.ts +1134 -96
- package/src/rules/no-throw-in-detour-recover.ts +225 -0
- package/src/rules/no-throw-in-implementation.ts +6 -4
- package/src/rules/on-references-exist.ts +194 -0
- package/src/rules/orphaned-signal.ts +150 -0
- package/src/rules/permit-governance.ts +25 -0
- package/src/rules/reference-exists.ts +98 -0
- package/src/rules/registry-names.ts +83 -0
- package/src/rules/{provision-declarations.ts → resource-declarations.ts} +208 -138
- package/src/rules/{provision-exists.ts → resource-exists.ts} +48 -51
- package/src/rules/resource-id-grammar.ts +65 -0
- package/src/rules/specs.ts +5 -1
- package/src/rules/types.ts +57 -4
- package/src/rules/unreachable-detour-shadowing.ts +375 -0
- package/src/rules/valid-describe-refs.ts +160 -32
- package/src/rules/valid-detour-contract.ts +78 -0
- package/src/rules/warden-export-symmetry.ts +533 -0
- package/src/rules/warden-rules-use-ast.ts +996 -0
- package/src/trails/circular-refs.trail.ts +29 -0
- package/src/trails/{context-no-trailhead-types.trail.ts → context-no-surface-types.trail.ts} +4 -4
- package/src/trails/contour-exists.trail.ts +21 -0
- package/src/trails/dead-internal-trail.trail.ts +26 -0
- package/src/trails/draft-file-marking.trail.ts +16 -0
- package/src/trails/draft-visible-debt.trail.ts +16 -0
- package/src/trails/error-mapping-completeness.trail.ts +29 -0
- package/src/trails/example-valid.trail.ts +25 -0
- package/src/trails/fires-declarations.trail.ts +22 -0
- package/src/trails/incomplete-accessor-for-standard-op.trail.ts +76 -0
- package/src/trails/incomplete-crud.trail.ts +39 -0
- package/src/trails/index.ts +40 -7
- package/src/trails/intent-propagation.trail.ts +30 -0
- package/src/trails/missing-reconcile.trail.ts +33 -0
- package/src/trails/missing-visibility.trail.ts +22 -0
- package/src/trails/no-throw-in-detour-recover.trail.ts +24 -0
- package/src/trails/on-references-exist.trail.ts +21 -0
- package/src/trails/orphaned-signal.trail.ts +36 -0
- package/src/trails/permit-governance.trail.ts +51 -0
- package/src/trails/reference-exists.trail.ts +25 -0
- package/src/trails/{provision-declarations.trail.ts → resource-declarations.trail.ts} +6 -6
- package/src/trails/{provision-exists.trail.ts → resource-exists.trail.ts} +7 -7
- package/src/trails/resource-id-grammar.trail.ts +39 -0
- package/src/trails/run.ts +121 -24
- package/src/trails/schema.ts +66 -4
- package/src/trails/unreachable-detour-shadowing.trail.ts +45 -0
- package/src/trails/valid-detour-contract.trail.ts +71 -0
- package/src/trails/warden-export-symmetry.trail.ts +16 -0
- package/src/trails/warden-rules-use-ast.trail.ts +45 -0
- package/src/trails/wrap-rule.ts +104 -12
- package/tsconfig.tests.json +10 -0
- package/tsconfig.tsbuildinfo +1 -1
- package/dist/rules/follow-declarations.d.ts +0 -13
- package/dist/rules/follow-declarations.d.ts.map +0 -1
- package/dist/rules/follow-declarations.js +0 -264
- package/dist/rules/follow-declarations.js.map +0 -1
- package/dist/rules/provision-declarations.d.ts +0 -14
- package/dist/rules/provision-declarations.d.ts.map +0 -1
- package/dist/rules/provision-declarations.js +0 -344
- package/dist/rules/provision-declarations.js.map +0 -1
- package/dist/rules/provision-exists.d.ts +0 -6
- package/dist/rules/provision-exists.d.ts.map +0 -1
- package/dist/rules/provision-exists.js +0 -89
- package/dist/rules/provision-exists.js.map +0 -1
- package/dist/rules/service-declarations.d.ts +0 -16
- package/dist/rules/service-declarations.d.ts.map +0 -1
- package/dist/rules/service-declarations.js +0 -346
- package/dist/rules/service-declarations.js.map +0 -1
- package/dist/rules/service-exists.d.ts +0 -8
- package/dist/rules/service-exists.d.ts.map +0 -1
- package/dist/rules/service-exists.js +0 -91
- package/dist/rules/service-exists.js.map +0 -1
- package/dist/trails/follow-declarations.trail.d.ts.map +0 -1
- package/dist/trails/follow-declarations.trail.js +0 -22
- package/dist/trails/follow-declarations.trail.js.map +0 -1
- package/dist/trails/provision-declarations.trail.d.ts.map +0 -1
- package/dist/trails/provision-declarations.trail.js.map +0 -1
- package/dist/trails/provision-exists.trail.d.ts +0 -15
- package/dist/trails/provision-exists.trail.d.ts.map +0 -1
- package/dist/trails/provision-exists.trail.js.map +0 -1
- package/dist/trails/service-declarations.trail.d.ts +0 -26
- package/dist/trails/service-declarations.trail.d.ts.map +0 -1
- package/dist/trails/service-declarations.trail.js +0 -27
- package/dist/trails/service-declarations.trail.js.map +0 -1
- package/dist/trails/service-exists.trail.d.ts +0 -32
- package/dist/trails/service-exists.trail.d.ts.map +0 -1
- package/dist/trails/service-exists.trail.js +0 -29
- package/dist/trails/service-exists.trail.js.map +0 -1
- package/src/__tests__/no-throw-in-detour-target.test.ts +0 -78
- package/src/__tests__/provision-declarations.test.ts +0 -318
- package/src/__tests__/provision-exists.test.ts +0 -122
- package/src/rules/no-throw-in-detour-target.ts +0 -150
- package/src/rules/valid-detour-refs.ts +0 -187
- package/src/trails/no-throw-in-detour-target.trail.ts +0 -20
- package/src/trails/valid-detour-refs.trail.ts +0 -24
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import {
|
|
2
|
+
collectContourReferenceTargetsByName,
|
|
3
|
+
findContourDefinitions,
|
|
4
|
+
offsetToLine,
|
|
5
|
+
parse,
|
|
6
|
+
} from './ast.js';
|
|
7
|
+
import type { AstNode } from './ast.js';
|
|
8
|
+
import { isTestFile } from './scan.js';
|
|
9
|
+
import type {
|
|
10
|
+
ProjectAwareWardenRule,
|
|
11
|
+
ProjectContext,
|
|
12
|
+
WardenDiagnostic,
|
|
13
|
+
} from './types.js';
|
|
14
|
+
|
|
15
|
+
const mergeReferenceGraphs = (
|
|
16
|
+
localGraph: ReadonlyMap<string, readonly string[]>,
|
|
17
|
+
contextGraph?: ReadonlyMap<string, readonly string[]>
|
|
18
|
+
): ReadonlyMap<string, readonly string[]> => {
|
|
19
|
+
const merged = new Map<string, Set<string>>();
|
|
20
|
+
|
|
21
|
+
const addTargets = (source: string, targets: readonly string[]): void => {
|
|
22
|
+
const existing = merged.get(source);
|
|
23
|
+
if (existing) {
|
|
24
|
+
for (const target of targets) {
|
|
25
|
+
existing.add(target);
|
|
26
|
+
}
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
merged.set(source, new Set(targets));
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
for (const [source, targets] of contextGraph ?? []) {
|
|
34
|
+
addTargets(source, targets);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
for (const [source, targets] of localGraph) {
|
|
38
|
+
addTargets(source, targets);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return new Map(
|
|
42
|
+
[...merged.entries()].map(([source, targets]) => [source, [...targets]])
|
|
43
|
+
);
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
const findCyclePath = (
|
|
47
|
+
start: string,
|
|
48
|
+
graph: ReadonlyMap<string, readonly string[]>,
|
|
49
|
+
current = start,
|
|
50
|
+
path: readonly string[] = [start],
|
|
51
|
+
active: ReadonlySet<string> = new Set([start])
|
|
52
|
+
): readonly string[] | null => {
|
|
53
|
+
for (const target of graph.get(current) ?? []) {
|
|
54
|
+
if (target === start) {
|
|
55
|
+
return [...path, target];
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
if (active.has(target)) {
|
|
59
|
+
continue;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const cycle = findCyclePath(
|
|
63
|
+
start,
|
|
64
|
+
graph,
|
|
65
|
+
target,
|
|
66
|
+
[...path, target],
|
|
67
|
+
new Set([...active, target])
|
|
68
|
+
);
|
|
69
|
+
if (cycle) {
|
|
70
|
+
return cycle;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
return null;
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
const buildCircularReferenceDiagnostic = (
|
|
78
|
+
contourName: string,
|
|
79
|
+
cyclePath: readonly string[],
|
|
80
|
+
filePath: string,
|
|
81
|
+
line: number
|
|
82
|
+
): WardenDiagnostic => ({
|
|
83
|
+
filePath,
|
|
84
|
+
line,
|
|
85
|
+
message: `Contour "${contourName}" participates in circular contour references: ${cyclePath.join(' -> ')}.`,
|
|
86
|
+
rule: 'circular-refs',
|
|
87
|
+
severity: 'warn',
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
const checkCircularReferences = (
|
|
91
|
+
ast: AstNode,
|
|
92
|
+
sourceCode: string,
|
|
93
|
+
filePath: string,
|
|
94
|
+
graph: ReadonlyMap<string, readonly string[]>
|
|
95
|
+
): readonly WardenDiagnostic[] => {
|
|
96
|
+
if (isTestFile(filePath)) {
|
|
97
|
+
return [];
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
return findContourDefinitions(ast).flatMap((definition) => {
|
|
101
|
+
const cyclePath = findCyclePath(definition.name, graph);
|
|
102
|
+
if (!cyclePath) {
|
|
103
|
+
return [];
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
return [
|
|
107
|
+
buildCircularReferenceDiagnostic(
|
|
108
|
+
definition.name,
|
|
109
|
+
cyclePath,
|
|
110
|
+
filePath,
|
|
111
|
+
offsetToLine(sourceCode, definition.start)
|
|
112
|
+
),
|
|
113
|
+
];
|
|
114
|
+
});
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Warns when contour references form a direct or transitive cycle.
|
|
119
|
+
*/
|
|
120
|
+
export const circularRefs: ProjectAwareWardenRule = {
|
|
121
|
+
check(sourceCode: string, filePath: string): readonly WardenDiagnostic[] {
|
|
122
|
+
const ast = parse(filePath, sourceCode);
|
|
123
|
+
if (!ast) {
|
|
124
|
+
return [];
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const graph = collectContourReferenceTargetsByName(ast);
|
|
128
|
+
return checkCircularReferences(ast, sourceCode, filePath, graph);
|
|
129
|
+
},
|
|
130
|
+
checkWithContext(
|
|
131
|
+
sourceCode: string,
|
|
132
|
+
filePath: string,
|
|
133
|
+
context: ProjectContext
|
|
134
|
+
): readonly WardenDiagnostic[] {
|
|
135
|
+
const ast = parse(filePath, sourceCode);
|
|
136
|
+
if (!ast) {
|
|
137
|
+
return [];
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
const localGraph = collectContourReferenceTargetsByName(
|
|
141
|
+
ast,
|
|
142
|
+
context.knownContourIds
|
|
143
|
+
);
|
|
144
|
+
return checkCircularReferences(
|
|
145
|
+
ast,
|
|
146
|
+
sourceCode,
|
|
147
|
+
filePath,
|
|
148
|
+
mergeReferenceGraphs(localGraph, context.contourReferencesByName)
|
|
149
|
+
);
|
|
150
|
+
},
|
|
151
|
+
description: 'Warn when contour references form direct or transitive cycles.',
|
|
152
|
+
name: 'circular-refs',
|
|
153
|
+
severity: 'warn',
|
|
154
|
+
};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Detects imports of
|
|
2
|
+
* Detects imports of surface-specific modules and types in trail files.
|
|
3
3
|
*
|
|
4
4
|
* Uses AST parsing for accurate detection — no false positives from
|
|
5
5
|
* imports in comments or strings.
|
|
@@ -40,6 +40,67 @@ interface ImportSpecifier {
|
|
|
40
40
|
readonly imported?: { readonly name?: string };
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
+
const isBareTrailCallee = (callee: AstNode): boolean => {
|
|
44
|
+
if (callee.type !== 'Identifier') {
|
|
45
|
+
return false;
|
|
46
|
+
}
|
|
47
|
+
return (callee as unknown as { name?: string }).name === 'trail';
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
const isNamespacedTrailCallee = (callee: AstNode): boolean => {
|
|
51
|
+
if (
|
|
52
|
+
callee.type !== 'MemberExpression' &&
|
|
53
|
+
callee.type !== 'StaticMemberExpression'
|
|
54
|
+
) {
|
|
55
|
+
return false;
|
|
56
|
+
}
|
|
57
|
+
// Skip computed access like `ns[trail]()` — the bracketed expression may
|
|
58
|
+
// resolve to any runtime value, not the `trail` primitive, even when it
|
|
59
|
+
// happens to be an identifier literally named `trail`.
|
|
60
|
+
if ((callee as unknown as { computed?: boolean }).computed === true) {
|
|
61
|
+
return false;
|
|
62
|
+
}
|
|
63
|
+
const prop = (callee as unknown as { property?: AstNode }).property;
|
|
64
|
+
if (prop?.type !== 'Identifier') {
|
|
65
|
+
return false;
|
|
66
|
+
}
|
|
67
|
+
return (prop as unknown as { name?: string }).name === 'trail';
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* True when `ast` contains a `trail(...)` call expression — i.e. this file
|
|
72
|
+
* looks like a trail definition. AST-based replacement for the legacy
|
|
73
|
+
* `/\btrail\s*\(/.test(sourceCode)` gate, which fired on string literals,
|
|
74
|
+
* comments, and docstrings.
|
|
75
|
+
*
|
|
76
|
+
* @remarks
|
|
77
|
+
* Both bare-identifier `trail(...)` and namespaced `ns.trail(...)` callees
|
|
78
|
+
* are recognized, so files using either `import { trail }` or
|
|
79
|
+
* `import * as ns from '@ontrails/core'` are detected as trail definitions.
|
|
80
|
+
*
|
|
81
|
+
* The inner `if (found)` guard skips further work in each callback invocation,
|
|
82
|
+
* but the shared `walk` helper in `./ast.ts` exposes no abort mechanism, so
|
|
83
|
+
* the full tree is still traversed once a match is seen. Acceptable: `walk`
|
|
84
|
+
* is cheap and this rule only runs on files that already matched a path
|
|
85
|
+
* filter upstream.
|
|
86
|
+
*/
|
|
87
|
+
const hasTrailCall = (ast: AstNode): boolean => {
|
|
88
|
+
let found = false;
|
|
89
|
+
walk(ast, (node) => {
|
|
90
|
+
if (found || node.type !== 'CallExpression') {
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
const { callee } = node as unknown as { callee?: AstNode };
|
|
94
|
+
if (!callee) {
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
if (isBareTrailCallee(callee) || isNamespacedTrailCallee(callee)) {
|
|
98
|
+
found = true;
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
return found;
|
|
102
|
+
};
|
|
103
|
+
|
|
43
104
|
const makeDiag = (
|
|
44
105
|
filePath: string,
|
|
45
106
|
sourceCode: string,
|
|
@@ -49,7 +110,7 @@ const makeDiag = (
|
|
|
49
110
|
filePath,
|
|
50
111
|
line: offsetToLine(sourceCode, node.start),
|
|
51
112
|
message,
|
|
52
|
-
rule: 'context-no-
|
|
113
|
+
rule: 'context-no-surface-types',
|
|
53
114
|
severity: 'error',
|
|
54
115
|
});
|
|
55
116
|
|
|
@@ -92,7 +153,7 @@ const checkSpecifiersForSurfaceTypes = (
|
|
|
92
153
|
filePath,
|
|
93
154
|
sourceCode,
|
|
94
155
|
node,
|
|
95
|
-
`Do not import
|
|
156
|
+
`Do not import surface type "${typeName}" in trail implementation files.`
|
|
96
157
|
);
|
|
97
158
|
};
|
|
98
159
|
|
|
@@ -111,7 +172,7 @@ const classifyImport = (
|
|
|
111
172
|
filePath,
|
|
112
173
|
sourceCode,
|
|
113
174
|
node,
|
|
114
|
-
`Do not import from
|
|
175
|
+
`Do not import from surface module "${moduleName}" in trail implementation files.`
|
|
115
176
|
);
|
|
116
177
|
}
|
|
117
178
|
|
|
@@ -119,18 +180,17 @@ const classifyImport = (
|
|
|
119
180
|
};
|
|
120
181
|
|
|
121
182
|
/**
|
|
122
|
-
* Detects imports of
|
|
183
|
+
* Detects imports of surface-specific types in trail implementation files.
|
|
123
184
|
*/
|
|
124
|
-
export const
|
|
185
|
+
export const contextNoSurfaceTypes: WardenRule = {
|
|
125
186
|
check(sourceCode: string, filePath: string): readonly WardenDiagnostic[] {
|
|
126
|
-
if (!/\btrail\s*\(/.test(sourceCode)) {
|
|
127
|
-
return [];
|
|
128
|
-
}
|
|
129
|
-
|
|
130
187
|
const ast = parse(filePath, sourceCode);
|
|
131
188
|
if (!ast) {
|
|
132
189
|
return [];
|
|
133
190
|
}
|
|
191
|
+
if (!hasTrailCall(ast)) {
|
|
192
|
+
return [];
|
|
193
|
+
}
|
|
134
194
|
|
|
135
195
|
const diagnostics: WardenDiagnostic[] = [];
|
|
136
196
|
walk(ast, (node) => {
|
|
@@ -143,8 +203,8 @@ export const contextNoTrailheadTypes: WardenRule = {
|
|
|
143
203
|
return diagnostics;
|
|
144
204
|
},
|
|
145
205
|
description:
|
|
146
|
-
'Disallow
|
|
147
|
-
name: 'context-no-
|
|
206
|
+
'Disallow surface-specific type imports (Request, Response, McpSession, etc.) in trail implementation files.',
|
|
207
|
+
name: 'context-no-surface-types',
|
|
148
208
|
|
|
149
209
|
severity: 'error',
|
|
150
210
|
};
|
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
import {
|
|
2
|
+
buildUserNamespaceContext,
|
|
3
|
+
collectContourDefinitionIds,
|
|
4
|
+
collectImportAliasMap,
|
|
5
|
+
collectNamedContourIds,
|
|
6
|
+
extractFirstStringArg,
|
|
7
|
+
findConfigProperty,
|
|
8
|
+
findTrailDefinitions,
|
|
9
|
+
identifierName,
|
|
10
|
+
isMemberAccessNonComputed,
|
|
11
|
+
isUserNamespaceReceiverAllowed,
|
|
12
|
+
offsetToLine,
|
|
13
|
+
parse,
|
|
14
|
+
deriveContourIdentifierName,
|
|
15
|
+
} from './ast.js';
|
|
16
|
+
import type { AstNode, TrailDefinition, UserNamespaceContext } from './ast.js';
|
|
17
|
+
import { mergeKnownContourIds } from './contour-ids.js';
|
|
18
|
+
import { isTestFile } from './scan.js';
|
|
19
|
+
import type {
|
|
20
|
+
ProjectAwareWardenRule,
|
|
21
|
+
ProjectContext,
|
|
22
|
+
WardenDiagnostic,
|
|
23
|
+
} from './types.js';
|
|
24
|
+
|
|
25
|
+
const isContourCall = (node: AstNode): boolean =>
|
|
26
|
+
node.type === 'CallExpression' &&
|
|
27
|
+
identifierName((node as unknown as { callee?: AstNode }).callee) ===
|
|
28
|
+
'contour';
|
|
29
|
+
|
|
30
|
+
const getContourElements = (config: AstNode): readonly AstNode[] => {
|
|
31
|
+
const contoursProp = findConfigProperty(config, 'contours');
|
|
32
|
+
if (!contoursProp) {
|
|
33
|
+
return [];
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const arrayNode = contoursProp.value;
|
|
37
|
+
if (!arrayNode || (arrayNode as AstNode).type !== 'ArrayExpression') {
|
|
38
|
+
return [];
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const elements = (arrayNode as AstNode)['elements'] as
|
|
42
|
+
| readonly AstNode[]
|
|
43
|
+
| undefined;
|
|
44
|
+
return elements ?? [];
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Resolve `contours.user` to its contour name. When `userNamespace` carries a
|
|
49
|
+
* scope-aware `safeMemberStarts` set, the member access must appear in it —
|
|
50
|
+
* rejecting cases where `contours` is shadowed by a local binding such as a
|
|
51
|
+
* function parameter or `const contours = ...`. Without the set, falls back
|
|
52
|
+
* to the bare name check for backward compatibility.
|
|
53
|
+
*/
|
|
54
|
+
const resolveNamespaceMemberContourName = (
|
|
55
|
+
element: AstNode,
|
|
56
|
+
userNamespace: UserNamespaceContext
|
|
57
|
+
): string | null => {
|
|
58
|
+
if (!isMemberAccessNonComputed(element)) {
|
|
59
|
+
return null;
|
|
60
|
+
}
|
|
61
|
+
const { object, property } = element as unknown as {
|
|
62
|
+
readonly object?: AstNode;
|
|
63
|
+
readonly property?: AstNode;
|
|
64
|
+
};
|
|
65
|
+
const receiver = object ? identifierName(object) : null;
|
|
66
|
+
if (
|
|
67
|
+
!receiver ||
|
|
68
|
+
!isUserNamespaceReceiverAllowed(receiver, element.start, userNamespace)
|
|
69
|
+
) {
|
|
70
|
+
return null;
|
|
71
|
+
}
|
|
72
|
+
return property ? identifierName(property) : null;
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
const resolveDeclaredContourName = (
|
|
76
|
+
element: AstNode,
|
|
77
|
+
contourIdsByName: ReadonlyMap<string, string>,
|
|
78
|
+
knownContourIds?: ReadonlySet<string>,
|
|
79
|
+
importAliases?: ReadonlyMap<string, string>,
|
|
80
|
+
userNamespace?: UserNamespaceContext
|
|
81
|
+
): string | null => {
|
|
82
|
+
if (element.type === 'Identifier') {
|
|
83
|
+
const name = identifierName(element);
|
|
84
|
+
return name
|
|
85
|
+
? deriveContourIdentifierName(
|
|
86
|
+
name,
|
|
87
|
+
contourIdsByName,
|
|
88
|
+
knownContourIds,
|
|
89
|
+
importAliases
|
|
90
|
+
)
|
|
91
|
+
: null;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
if (userNamespace && userNamespace.bindings.size > 0) {
|
|
95
|
+
const namespaceTarget = resolveNamespaceMemberContourName(
|
|
96
|
+
element,
|
|
97
|
+
userNamespace
|
|
98
|
+
);
|
|
99
|
+
if (namespaceTarget) {
|
|
100
|
+
return namespaceTarget;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
return isContourCall(element) ? extractFirstStringArg(element) : null;
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
const extractDeclaredContourNames = (
|
|
108
|
+
config: AstNode,
|
|
109
|
+
contourIdsByName: ReadonlyMap<string, string>,
|
|
110
|
+
knownContourIds?: ReadonlySet<string>,
|
|
111
|
+
importAliases?: ReadonlyMap<string, string>,
|
|
112
|
+
userNamespace?: UserNamespaceContext
|
|
113
|
+
): readonly string[] => [
|
|
114
|
+
...new Set(
|
|
115
|
+
getContourElements(config).flatMap((element) => {
|
|
116
|
+
const contourName = resolveDeclaredContourName(
|
|
117
|
+
element,
|
|
118
|
+
contourIdsByName,
|
|
119
|
+
knownContourIds,
|
|
120
|
+
importAliases,
|
|
121
|
+
userNamespace
|
|
122
|
+
);
|
|
123
|
+
return contourName ? [contourName] : [];
|
|
124
|
+
})
|
|
125
|
+
),
|
|
126
|
+
];
|
|
127
|
+
|
|
128
|
+
const buildMissingContourDiagnostic = (
|
|
129
|
+
trailId: string,
|
|
130
|
+
contourName: string,
|
|
131
|
+
filePath: string,
|
|
132
|
+
line: number
|
|
133
|
+
): WardenDiagnostic => ({
|
|
134
|
+
filePath,
|
|
135
|
+
line,
|
|
136
|
+
message: `Trail "${trailId}" declares contour "${contourName}" which is not defined in the project.`,
|
|
137
|
+
rule: 'contour-exists',
|
|
138
|
+
severity: 'error',
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
const buildDiagnosticsForDefinition = (
|
|
142
|
+
definition: TrailDefinition,
|
|
143
|
+
sourceCode: string,
|
|
144
|
+
filePath: string,
|
|
145
|
+
knownContourIds: ReadonlySet<string>,
|
|
146
|
+
contourIdsByName: ReadonlyMap<string, string>,
|
|
147
|
+
importAliases: ReadonlyMap<string, string>,
|
|
148
|
+
userNamespace: UserNamespaceContext
|
|
149
|
+
): readonly WardenDiagnostic[] => {
|
|
150
|
+
if (definition.kind !== 'trail') {
|
|
151
|
+
return [];
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
const line = offsetToLine(sourceCode, definition.start);
|
|
155
|
+
return extractDeclaredContourNames(
|
|
156
|
+
definition.config,
|
|
157
|
+
contourIdsByName,
|
|
158
|
+
knownContourIds,
|
|
159
|
+
importAliases,
|
|
160
|
+
userNamespace
|
|
161
|
+
).flatMap((contourName) =>
|
|
162
|
+
knownContourIds.has(contourName)
|
|
163
|
+
? []
|
|
164
|
+
: [
|
|
165
|
+
buildMissingContourDiagnostic(
|
|
166
|
+
definition.id,
|
|
167
|
+
contourName,
|
|
168
|
+
filePath,
|
|
169
|
+
line
|
|
170
|
+
),
|
|
171
|
+
]
|
|
172
|
+
);
|
|
173
|
+
};
|
|
174
|
+
|
|
175
|
+
const buildContourDiagnostics = (
|
|
176
|
+
ast: AstNode,
|
|
177
|
+
sourceCode: string,
|
|
178
|
+
filePath: string,
|
|
179
|
+
knownContourIds: ReadonlySet<string>
|
|
180
|
+
): readonly WardenDiagnostic[] => {
|
|
181
|
+
const contourIdsByName = collectNamedContourIds(ast);
|
|
182
|
+
const importAliases = collectImportAliasMap(ast);
|
|
183
|
+
const userNamespace = buildUserNamespaceContext(ast);
|
|
184
|
+
|
|
185
|
+
return findTrailDefinitions(ast).flatMap((definition) =>
|
|
186
|
+
buildDiagnosticsForDefinition(
|
|
187
|
+
definition,
|
|
188
|
+
sourceCode,
|
|
189
|
+
filePath,
|
|
190
|
+
knownContourIds,
|
|
191
|
+
contourIdsByName,
|
|
192
|
+
importAliases,
|
|
193
|
+
userNamespace
|
|
194
|
+
)
|
|
195
|
+
);
|
|
196
|
+
};
|
|
197
|
+
|
|
198
|
+
const checkContourDeclarations = (
|
|
199
|
+
ast: AstNode,
|
|
200
|
+
sourceCode: string,
|
|
201
|
+
filePath: string,
|
|
202
|
+
knownContourIds: ReadonlySet<string>
|
|
203
|
+
): readonly WardenDiagnostic[] => {
|
|
204
|
+
if (isTestFile(filePath)) {
|
|
205
|
+
return [];
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
return buildContourDiagnostics(ast, sourceCode, filePath, knownContourIds);
|
|
209
|
+
};
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* Checks that every contour declared in a trail `contours` array resolves to a
|
|
213
|
+
* known contour definition.
|
|
214
|
+
*/
|
|
215
|
+
export const contourExists: ProjectAwareWardenRule = {
|
|
216
|
+
check(sourceCode: string, filePath: string): readonly WardenDiagnostic[] {
|
|
217
|
+
const ast = parse(filePath, sourceCode);
|
|
218
|
+
if (!ast) {
|
|
219
|
+
return [];
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
return checkContourDeclarations(
|
|
223
|
+
ast,
|
|
224
|
+
sourceCode,
|
|
225
|
+
filePath,
|
|
226
|
+
collectContourDefinitionIds(ast)
|
|
227
|
+
);
|
|
228
|
+
},
|
|
229
|
+
checkWithContext(
|
|
230
|
+
sourceCode: string,
|
|
231
|
+
filePath: string,
|
|
232
|
+
context: ProjectContext
|
|
233
|
+
): readonly WardenDiagnostic[] {
|
|
234
|
+
const ast = parse(filePath, sourceCode);
|
|
235
|
+
if (!ast) {
|
|
236
|
+
return [];
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
const localContourIds = collectContourDefinitionIds(ast);
|
|
240
|
+
return checkContourDeclarations(
|
|
241
|
+
ast,
|
|
242
|
+
sourceCode,
|
|
243
|
+
filePath,
|
|
244
|
+
mergeKnownContourIds(localContourIds, context.knownContourIds)
|
|
245
|
+
);
|
|
246
|
+
},
|
|
247
|
+
description:
|
|
248
|
+
'Ensure every contour declared on a trail resolves to a known contour definition.',
|
|
249
|
+
name: 'contour-exists',
|
|
250
|
+
severity: 'error',
|
|
251
|
+
};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Merge a file's locally-defined contour IDs with the project-wide set.
|
|
3
|
+
*
|
|
4
|
+
* Rules that run with a `ProjectContext` need to treat both local and
|
|
5
|
+
* project-wide contour definitions as "known" so that declarations and
|
|
6
|
+
* references resolve correctly. When no project context is available — e.g.
|
|
7
|
+
* single-file lint runs via `check` — the local set is returned as-is.
|
|
8
|
+
*/
|
|
9
|
+
export const mergeKnownContourIds = (
|
|
10
|
+
localContourIds: ReadonlySet<string>,
|
|
11
|
+
projectContourIds?: ReadonlySet<string>
|
|
12
|
+
): ReadonlySet<string> =>
|
|
13
|
+
projectContourIds
|
|
14
|
+
? new Set([...projectContourIds, ...localContourIds])
|
|
15
|
+
: localContourIds;
|