driftdetect-detectors 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/dist/accessibility/alt-text.d.ts +63 -0
- package/dist/accessibility/alt-text.d.ts.map +1 -0
- package/dist/accessibility/alt-text.js +100 -0
- package/dist/accessibility/alt-text.js.map +1 -0
- package/dist/accessibility/aria-roles.d.ts +65 -0
- package/dist/accessibility/aria-roles.d.ts.map +1 -0
- package/dist/accessibility/aria-roles.js +87 -0
- package/dist/accessibility/aria-roles.js.map +1 -0
- package/dist/accessibility/focus-management.d.ts +62 -0
- package/dist/accessibility/focus-management.d.ts.map +1 -0
- package/dist/accessibility/focus-management.js +88 -0
- package/dist/accessibility/focus-management.js.map +1 -0
- package/dist/accessibility/heading-hierarchy.d.ts +66 -0
- package/dist/accessibility/heading-hierarchy.d.ts.map +1 -0
- package/dist/accessibility/heading-hierarchy.js +94 -0
- package/dist/accessibility/heading-hierarchy.js.map +1 -0
- package/dist/accessibility/index.d.ts +25 -0
- package/dist/accessibility/index.d.ts.map +1 -0
- package/dist/accessibility/index.js +21 -0
- package/dist/accessibility/index.js.map +1 -0
- package/dist/accessibility/keyboard-nav.d.ts +63 -0
- package/dist/accessibility/keyboard-nav.d.ts.map +1 -0
- package/dist/accessibility/keyboard-nav.js +86 -0
- package/dist/accessibility/keyboard-nav.js.map +1 -0
- package/dist/accessibility/semantic-html.d.ts +76 -0
- package/dist/accessibility/semantic-html.d.ts.map +1 -0
- package/dist/accessibility/semantic-html.js +204 -0
- package/dist/accessibility/semantic-html.js.map +1 -0
- package/dist/api/client-patterns.d.ts +121 -0
- package/dist/api/client-patterns.d.ts.map +1 -0
- package/dist/api/client-patterns.js +478 -0
- package/dist/api/client-patterns.js.map +1 -0
- package/dist/api/error-format.d.ts +140 -0
- package/dist/api/error-format.d.ts.map +1 -0
- package/dist/api/error-format.js +614 -0
- package/dist/api/error-format.js.map +1 -0
- package/dist/api/http-methods.d.ts +255 -0
- package/dist/api/http-methods.d.ts.map +1 -0
- package/dist/api/http-methods.js +890 -0
- package/dist/api/http-methods.js.map +1 -0
- package/dist/api/index.d.ts +16 -0
- package/dist/api/index.d.ts.map +1 -0
- package/dist/api/index.js +37 -0
- package/dist/api/index.js.map +1 -0
- package/dist/api/pagination.d.ts +133 -0
- package/dist/api/pagination.d.ts.map +1 -0
- package/dist/api/pagination.js +521 -0
- package/dist/api/pagination.js.map +1 -0
- package/dist/api/response-envelope.d.ts +261 -0
- package/dist/api/response-envelope.d.ts.map +1 -0
- package/dist/api/response-envelope.js +1050 -0
- package/dist/api/response-envelope.js.map +1 -0
- package/dist/api/retry-patterns.d.ts +117 -0
- package/dist/api/retry-patterns.d.ts.map +1 -0
- package/dist/api/retry-patterns.js +480 -0
- package/dist/api/retry-patterns.js.map +1 -0
- package/dist/api/route-structure.d.ts +128 -0
- package/dist/api/route-structure.d.ts.map +1 -0
- package/dist/api/route-structure.js +738 -0
- package/dist/api/route-structure.js.map +1 -0
- package/dist/auth/audit-logging.d.ts +80 -0
- package/dist/auth/audit-logging.d.ts.map +1 -0
- package/dist/auth/audit-logging.js +370 -0
- package/dist/auth/audit-logging.js.map +1 -0
- package/dist/auth/index.d.ts +33 -0
- package/dist/auth/index.d.ts.map +1 -0
- package/dist/auth/index.js +49 -0
- package/dist/auth/index.js.map +1 -0
- package/dist/auth/middleware-usage.d.ts +65 -0
- package/dist/auth/middleware-usage.d.ts.map +1 -0
- package/dist/auth/middleware-usage.js +192 -0
- package/dist/auth/middleware-usage.js.map +1 -0
- package/dist/auth/permission-checks.d.ts +60 -0
- package/dist/auth/permission-checks.d.ts.map +1 -0
- package/dist/auth/permission-checks.js +159 -0
- package/dist/auth/permission-checks.js.map +1 -0
- package/dist/auth/rbac-patterns.d.ts +68 -0
- package/dist/auth/rbac-patterns.d.ts.map +1 -0
- package/dist/auth/rbac-patterns.js +143 -0
- package/dist/auth/rbac-patterns.js.map +1 -0
- package/dist/auth/resource-ownership.d.ts +77 -0
- package/dist/auth/resource-ownership.d.ts.map +1 -0
- package/dist/auth/resource-ownership.js +324 -0
- package/dist/auth/resource-ownership.js.map +1 -0
- package/dist/auth/token-handling.d.ts +64 -0
- package/dist/auth/token-handling.d.ts.map +1 -0
- package/dist/auth/token-handling.js +151 -0
- package/dist/auth/token-handling.js.map +1 -0
- package/dist/base/ast-detector.d.ts +421 -0
- package/dist/base/ast-detector.d.ts.map +1 -0
- package/dist/base/ast-detector.js +699 -0
- package/dist/base/ast-detector.js.map +1 -0
- package/dist/base/base-detector.d.ts +366 -0
- package/dist/base/base-detector.d.ts.map +1 -0
- package/dist/base/base-detector.js +170 -0
- package/dist/base/base-detector.js.map +1 -0
- package/dist/base/index.d.ts +12 -0
- package/dist/base/index.d.ts.map +1 -0
- package/dist/base/index.js +17 -0
- package/dist/base/index.js.map +1 -0
- package/dist/base/regex-detector.d.ts +421 -0
- package/dist/base/regex-detector.d.ts.map +1 -0
- package/dist/base/regex-detector.js +537 -0
- package/dist/base/regex-detector.js.map +1 -0
- package/dist/base/structural-detector.d.ts +424 -0
- package/dist/base/structural-detector.d.ts.map +1 -0
- package/dist/base/structural-detector.js +731 -0
- package/dist/base/structural-detector.js.map +1 -0
- package/dist/base/types.d.ts +53 -0
- package/dist/base/types.d.ts.map +1 -0
- package/dist/base/types.js +5 -0
- package/dist/base/types.js.map +1 -0
- package/dist/components/component-structure.d.ts +163 -0
- package/dist/components/component-structure.d.ts.map +1 -0
- package/dist/components/component-structure.js +500 -0
- package/dist/components/component-structure.js.map +1 -0
- package/dist/components/composition.d.ts +287 -0
- package/dist/components/composition.d.ts.map +1 -0
- package/dist/components/composition.js +1123 -0
- package/dist/components/composition.js.map +1 -0
- package/dist/components/duplicate-detection.d.ts +251 -0
- package/dist/components/duplicate-detection.d.ts.map +1 -0
- package/dist/components/duplicate-detection.js +804 -0
- package/dist/components/duplicate-detection.js.map +1 -0
- package/dist/components/index.d.ts +16 -0
- package/dist/components/index.d.ts.map +1 -0
- package/dist/components/index.js +51 -0
- package/dist/components/index.js.map +1 -0
- package/dist/components/near-duplicate.d.ts +402 -0
- package/dist/components/near-duplicate.d.ts.map +1 -0
- package/dist/components/near-duplicate.js +1090 -0
- package/dist/components/near-duplicate.js.map +1 -0
- package/dist/components/props-patterns.d.ts +194 -0
- package/dist/components/props-patterns.d.ts.map +1 -0
- package/dist/components/props-patterns.js +795 -0
- package/dist/components/props-patterns.js.map +1 -0
- package/dist/components/ref-forwarding.d.ts +250 -0
- package/dist/components/ref-forwarding.d.ts.map +1 -0
- package/dist/components/ref-forwarding.js +832 -0
- package/dist/components/ref-forwarding.js.map +1 -0
- package/dist/components/state-patterns.d.ts +291 -0
- package/dist/components/state-patterns.d.ts.map +1 -0
- package/dist/components/state-patterns.js +970 -0
- package/dist/components/state-patterns.js.map +1 -0
- package/dist/config/config-validation.d.ts +74 -0
- package/dist/config/config-validation.d.ts.map +1 -0
- package/dist/config/config-validation.js +446 -0
- package/dist/config/config-validation.js.map +1 -0
- package/dist/config/default-values.d.ts +72 -0
- package/dist/config/default-values.d.ts.map +1 -0
- package/dist/config/default-values.js +386 -0
- package/dist/config/default-values.js.map +1 -0
- package/dist/config/env-naming.d.ts +73 -0
- package/dist/config/env-naming.d.ts.map +1 -0
- package/dist/config/env-naming.js +429 -0
- package/dist/config/env-naming.js.map +1 -0
- package/dist/config/environment-detection.d.ts +72 -0
- package/dist/config/environment-detection.d.ts.map +1 -0
- package/dist/config/environment-detection.js +400 -0
- package/dist/config/environment-detection.js.map +1 -0
- package/dist/config/feature-flags.d.ts +72 -0
- package/dist/config/feature-flags.d.ts.map +1 -0
- package/dist/config/feature-flags.js +384 -0
- package/dist/config/feature-flags.js.map +1 -0
- package/dist/config/index.d.ts +27 -0
- package/dist/config/index.d.ts.map +1 -0
- package/dist/config/index.js +43 -0
- package/dist/config/index.js.map +1 -0
- package/dist/config/required-optional.d.ts +71 -0
- package/dist/config/required-optional.d.ts.map +1 -0
- package/dist/config/required-optional.js +344 -0
- package/dist/config/required-optional.js.map +1 -0
- package/dist/data-access/connection-pooling.d.ts +63 -0
- package/dist/data-access/connection-pooling.d.ts.map +1 -0
- package/dist/data-access/connection-pooling.js +297 -0
- package/dist/data-access/connection-pooling.js.map +1 -0
- package/dist/data-access/dto-patterns.d.ts +64 -0
- package/dist/data-access/dto-patterns.d.ts.map +1 -0
- package/dist/data-access/dto-patterns.js +291 -0
- package/dist/data-access/dto-patterns.js.map +1 -0
- package/dist/data-access/index.d.ts +31 -0
- package/dist/data-access/index.d.ts.map +1 -0
- package/dist/data-access/index.js +49 -0
- package/dist/data-access/index.js.map +1 -0
- package/dist/data-access/n-plus-one.d.ts +60 -0
- package/dist/data-access/n-plus-one.d.ts.map +1 -0
- package/dist/data-access/n-plus-one.js +264 -0
- package/dist/data-access/n-plus-one.js.map +1 -0
- package/dist/data-access/query-patterns.d.ts +64 -0
- package/dist/data-access/query-patterns.d.ts.map +1 -0
- package/dist/data-access/query-patterns.js +314 -0
- package/dist/data-access/query-patterns.js.map +1 -0
- package/dist/data-access/repository-pattern.d.ts +62 -0
- package/dist/data-access/repository-pattern.d.ts.map +1 -0
- package/dist/data-access/repository-pattern.js +257 -0
- package/dist/data-access/repository-pattern.js.map +1 -0
- package/dist/data-access/transaction-patterns.d.ts +61 -0
- package/dist/data-access/transaction-patterns.d.ts.map +1 -0
- package/dist/data-access/transaction-patterns.js +277 -0
- package/dist/data-access/transaction-patterns.js.map +1 -0
- package/dist/data-access/validation-patterns.d.ts +62 -0
- package/dist/data-access/validation-patterns.d.ts.map +1 -0
- package/dist/data-access/validation-patterns.js +301 -0
- package/dist/data-access/validation-patterns.js.map +1 -0
- package/dist/documentation/deprecation.d.ts +62 -0
- package/dist/documentation/deprecation.d.ts.map +1 -0
- package/dist/documentation/deprecation.js +83 -0
- package/dist/documentation/deprecation.js.map +1 -0
- package/dist/documentation/example-code.d.ts +64 -0
- package/dist/documentation/example-code.d.ts.map +1 -0
- package/dist/documentation/example-code.js +79 -0
- package/dist/documentation/example-code.js.map +1 -0
- package/dist/documentation/index.d.ts +22 -0
- package/dist/documentation/index.d.ts.map +1 -0
- package/dist/documentation/index.js +19 -0
- package/dist/documentation/index.js.map +1 -0
- package/dist/documentation/jsdoc-patterns.d.ts +72 -0
- package/dist/documentation/jsdoc-patterns.d.ts.map +1 -0
- package/dist/documentation/jsdoc-patterns.js +92 -0
- package/dist/documentation/jsdoc-patterns.js.map +1 -0
- package/dist/documentation/readme-structure.d.ts +67 -0
- package/dist/documentation/readme-structure.d.ts.map +1 -0
- package/dist/documentation/readme-structure.js +76 -0
- package/dist/documentation/readme-structure.js.map +1 -0
- package/dist/documentation/todo-patterns.d.ts +67 -0
- package/dist/documentation/todo-patterns.d.ts.map +1 -0
- package/dist/documentation/todo-patterns.js +73 -0
- package/dist/documentation/todo-patterns.js.map +1 -0
- package/dist/errors/async-errors.d.ts +72 -0
- package/dist/errors/async-errors.d.ts.map +1 -0
- package/dist/errors/async-errors.js +214 -0
- package/dist/errors/async-errors.js.map +1 -0
- package/dist/errors/circuit-breaker.d.ts +53 -0
- package/dist/errors/circuit-breaker.d.ts.map +1 -0
- package/dist/errors/circuit-breaker.js +241 -0
- package/dist/errors/circuit-breaker.js.map +1 -0
- package/dist/errors/error-codes.d.ts +73 -0
- package/dist/errors/error-codes.d.ts.map +1 -0
- package/dist/errors/error-codes.js +211 -0
- package/dist/errors/error-codes.js.map +1 -0
- package/dist/errors/error-logging.d.ts +73 -0
- package/dist/errors/error-logging.d.ts.map +1 -0
- package/dist/errors/error-logging.js +256 -0
- package/dist/errors/error-logging.js.map +1 -0
- package/dist/errors/error-propagation.d.ts +73 -0
- package/dist/errors/error-propagation.d.ts.map +1 -0
- package/dist/errors/error-propagation.js +244 -0
- package/dist/errors/error-propagation.js.map +1 -0
- package/dist/errors/exception-hierarchy.d.ts +75 -0
- package/dist/errors/exception-hierarchy.d.ts.map +1 -0
- package/dist/errors/exception-hierarchy.js +259 -0
- package/dist/errors/exception-hierarchy.js.map +1 -0
- package/dist/errors/index.d.ts +31 -0
- package/dist/errors/index.d.ts.map +1 -0
- package/dist/errors/index.js +49 -0
- package/dist/errors/index.js.map +1 -0
- package/dist/errors/try-catch-placement.d.ts +73 -0
- package/dist/errors/try-catch-placement.d.ts.map +1 -0
- package/dist/errors/try-catch-placement.js +214 -0
- package/dist/errors/try-catch-placement.js.map +1 -0
- package/dist/index.d.ts +221 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +245 -0
- package/dist/index.js.map +1 -0
- package/dist/logging/context-fields.d.ts +48 -0
- package/dist/logging/context-fields.d.ts.map +1 -0
- package/dist/logging/context-fields.js +160 -0
- package/dist/logging/context-fields.js.map +1 -0
- package/dist/logging/correlation-ids.d.ts +44 -0
- package/dist/logging/correlation-ids.d.ts.map +1 -0
- package/dist/logging/correlation-ids.js +144 -0
- package/dist/logging/correlation-ids.js.map +1 -0
- package/dist/logging/health-checks.d.ts +45 -0
- package/dist/logging/health-checks.d.ts.map +1 -0
- package/dist/logging/health-checks.js +165 -0
- package/dist/logging/health-checks.js.map +1 -0
- package/dist/logging/index.d.ts +31 -0
- package/dist/logging/index.d.ts.map +1 -0
- package/dist/logging/index.js +49 -0
- package/dist/logging/index.js.map +1 -0
- package/dist/logging/log-levels.d.ts +46 -0
- package/dist/logging/log-levels.d.ts.map +1 -0
- package/dist/logging/log-levels.js +178 -0
- package/dist/logging/log-levels.js.map +1 -0
- package/dist/logging/metric-naming.d.ts +46 -0
- package/dist/logging/metric-naming.d.ts.map +1 -0
- package/dist/logging/metric-naming.js +157 -0
- package/dist/logging/metric-naming.js.map +1 -0
- package/dist/logging/pii-redaction.d.ts +44 -0
- package/dist/logging/pii-redaction.d.ts.map +1 -0
- package/dist/logging/pii-redaction.js +166 -0
- package/dist/logging/pii-redaction.js.map +1 -0
- package/dist/logging/structured-format.d.ts +53 -0
- package/dist/logging/structured-format.d.ts.map +1 -0
- package/dist/logging/structured-format.js +235 -0
- package/dist/logging/structured-format.js.map +1 -0
- package/dist/performance/bundle-size.d.ts +79 -0
- package/dist/performance/bundle-size.d.ts.map +1 -0
- package/dist/performance/bundle-size.js +276 -0
- package/dist/performance/bundle-size.js.map +1 -0
- package/dist/performance/caching-patterns.d.ts +78 -0
- package/dist/performance/caching-patterns.d.ts.map +1 -0
- package/dist/performance/caching-patterns.js +257 -0
- package/dist/performance/caching-patterns.js.map +1 -0
- package/dist/performance/code-splitting.d.ts +86 -0
- package/dist/performance/code-splitting.d.ts.map +1 -0
- package/dist/performance/code-splitting.js +447 -0
- package/dist/performance/code-splitting.js.map +1 -0
- package/dist/performance/debounce-throttle.d.ts +75 -0
- package/dist/performance/debounce-throttle.d.ts.map +1 -0
- package/dist/performance/debounce-throttle.js +232 -0
- package/dist/performance/debounce-throttle.js.map +1 -0
- package/dist/performance/index.d.ts +28 -0
- package/dist/performance/index.d.ts.map +1 -0
- package/dist/performance/index.js +39 -0
- package/dist/performance/index.js.map +1 -0
- package/dist/performance/lazy-loading.d.ts +75 -0
- package/dist/performance/lazy-loading.d.ts.map +1 -0
- package/dist/performance/lazy-loading.js +233 -0
- package/dist/performance/lazy-loading.js.map +1 -0
- package/dist/performance/memoization.d.ts +75 -0
- package/dist/performance/memoization.d.ts.map +1 -0
- package/dist/performance/memoization.js +251 -0
- package/dist/performance/memoization.js.map +1 -0
- package/dist/registry/detector-registry.d.ts +266 -0
- package/dist/registry/detector-registry.d.ts.map +1 -0
- package/dist/registry/detector-registry.js +526 -0
- package/dist/registry/detector-registry.js.map +1 -0
- package/dist/registry/index.d.ts +10 -0
- package/dist/registry/index.d.ts.map +1 -0
- package/dist/registry/index.js +10 -0
- package/dist/registry/index.js.map +1 -0
- package/dist/registry/loader.d.ts +232 -0
- package/dist/registry/loader.d.ts.map +1 -0
- package/dist/registry/loader.js +419 -0
- package/dist/registry/loader.js.map +1 -0
- package/dist/registry/types.d.ts +111 -0
- package/dist/registry/types.d.ts.map +1 -0
- package/dist/registry/types.js +19 -0
- package/dist/registry/types.js.map +1 -0
- package/dist/security/csp-headers.d.ts +78 -0
- package/dist/security/csp-headers.d.ts.map +1 -0
- package/dist/security/csp-headers.js +401 -0
- package/dist/security/csp-headers.js.map +1 -0
- package/dist/security/csrf-protection.d.ts +72 -0
- package/dist/security/csrf-protection.d.ts.map +1 -0
- package/dist/security/csrf-protection.js +344 -0
- package/dist/security/csrf-protection.js.map +1 -0
- package/dist/security/index.d.ts +30 -0
- package/dist/security/index.d.ts.map +1 -0
- package/dist/security/index.js +48 -0
- package/dist/security/index.js.map +1 -0
- package/dist/security/input-sanitization.d.ts +74 -0
- package/dist/security/input-sanitization.d.ts.map +1 -0
- package/dist/security/input-sanitization.js +373 -0
- package/dist/security/input-sanitization.js.map +1 -0
- package/dist/security/rate-limiting.d.ts +81 -0
- package/dist/security/rate-limiting.d.ts.map +1 -0
- package/dist/security/rate-limiting.js +535 -0
- package/dist/security/rate-limiting.js.map +1 -0
- package/dist/security/secret-management.d.ts +83 -0
- package/dist/security/secret-management.d.ts.map +1 -0
- package/dist/security/secret-management.js +547 -0
- package/dist/security/secret-management.js.map +1 -0
- package/dist/security/sql-injection.d.ts +76 -0
- package/dist/security/sql-injection.d.ts.map +1 -0
- package/dist/security/sql-injection.js +383 -0
- package/dist/security/sql-injection.js.map +1 -0
- package/dist/security/xss-prevention.d.ts +80 -0
- package/dist/security/xss-prevention.d.ts.map +1 -0
- package/dist/security/xss-prevention.js +416 -0
- package/dist/security/xss-prevention.js.map +1 -0
- package/dist/structural/barrel-exports.d.ts +178 -0
- package/dist/structural/barrel-exports.d.ts.map +1 -0
- package/dist/structural/barrel-exports.js +553 -0
- package/dist/structural/barrel-exports.js.map +1 -0
- package/dist/structural/circular-deps.d.ts +140 -0
- package/dist/structural/circular-deps.d.ts.map +1 -0
- package/dist/structural/circular-deps.js +422 -0
- package/dist/structural/circular-deps.js.map +1 -0
- package/dist/structural/co-location.d.ts +202 -0
- package/dist/structural/co-location.d.ts.map +1 -0
- package/dist/structural/co-location.js +640 -0
- package/dist/structural/co-location.js.map +1 -0
- package/dist/structural/directory-structure.d.ts +151 -0
- package/dist/structural/directory-structure.d.ts.map +1 -0
- package/dist/structural/directory-structure.js +457 -0
- package/dist/structural/directory-structure.js.map +1 -0
- package/dist/structural/file-naming.d.ts +61 -0
- package/dist/structural/file-naming.d.ts.map +1 -0
- package/dist/structural/file-naming.js +231 -0
- package/dist/structural/file-naming.js.map +1 -0
- package/dist/structural/import-ordering.d.ts +212 -0
- package/dist/structural/import-ordering.d.ts.map +1 -0
- package/dist/structural/import-ordering.js +821 -0
- package/dist/structural/import-ordering.js.map +1 -0
- package/dist/structural/index.d.ts +23 -0
- package/dist/structural/index.d.ts.map +1 -0
- package/dist/structural/index.js +26 -0
- package/dist/structural/index.js.map +1 -0
- package/dist/structural/module-boundaries.d.ts +164 -0
- package/dist/structural/module-boundaries.d.ts.map +1 -0
- package/dist/structural/module-boundaries.js +616 -0
- package/dist/structural/module-boundaries.js.map +1 -0
- package/dist/structural/package-boundaries.d.ts +182 -0
- package/dist/structural/package-boundaries.d.ts.map +1 -0
- package/dist/structural/package-boundaries.js +602 -0
- package/dist/structural/package-boundaries.js.map +1 -0
- package/dist/styling/class-naming.d.ts +263 -0
- package/dist/styling/class-naming.d.ts.map +1 -0
- package/dist/styling/class-naming.js +892 -0
- package/dist/styling/class-naming.js.map +1 -0
- package/dist/styling/color-usage.d.ts +213 -0
- package/dist/styling/color-usage.d.ts.map +1 -0
- package/dist/styling/color-usage.js +732 -0
- package/dist/styling/color-usage.js.map +1 -0
- package/dist/styling/design-tokens.d.ts +212 -0
- package/dist/styling/design-tokens.d.ts.map +1 -0
- package/dist/styling/design-tokens.js +748 -0
- package/dist/styling/design-tokens.js.map +1 -0
- package/dist/styling/index.d.ts +16 -0
- package/dist/styling/index.d.ts.map +1 -0
- package/dist/styling/index.js +56 -0
- package/dist/styling/index.js.map +1 -0
- package/dist/styling/responsive.d.ts +304 -0
- package/dist/styling/responsive.d.ts.map +1 -0
- package/dist/styling/responsive.js +888 -0
- package/dist/styling/responsive.js.map +1 -0
- package/dist/styling/spacing-scale.d.ts +248 -0
- package/dist/styling/spacing-scale.d.ts.map +1 -0
- package/dist/styling/spacing-scale.js +865 -0
- package/dist/styling/spacing-scale.js.map +1 -0
- package/dist/styling/tailwind-patterns.d.ts +305 -0
- package/dist/styling/tailwind-patterns.d.ts.map +1 -0
- package/dist/styling/tailwind-patterns.js +1181 -0
- package/dist/styling/tailwind-patterns.js.map +1 -0
- package/dist/styling/typography.d.ts +281 -0
- package/dist/styling/typography.d.ts.map +1 -0
- package/dist/styling/typography.js +1004 -0
- package/dist/styling/typography.js.map +1 -0
- package/dist/styling/z-index-scale.d.ts +270 -0
- package/dist/styling/z-index-scale.d.ts.map +1 -0
- package/dist/styling/z-index-scale.js +714 -0
- package/dist/styling/z-index-scale.js.map +1 -0
- package/dist/testing/co-location.d.ts +42 -0
- package/dist/testing/co-location.d.ts.map +1 -0
- package/dist/testing/co-location.js +134 -0
- package/dist/testing/co-location.js.map +1 -0
- package/dist/testing/describe-naming.d.ts +47 -0
- package/dist/testing/describe-naming.d.ts.map +1 -0
- package/dist/testing/describe-naming.js +150 -0
- package/dist/testing/describe-naming.js.map +1 -0
- package/dist/testing/file-naming.d.ts +44 -0
- package/dist/testing/file-naming.d.ts.map +1 -0
- package/dist/testing/file-naming.js +131 -0
- package/dist/testing/file-naming.js.map +1 -0
- package/dist/testing/fixture-patterns.d.ts +52 -0
- package/dist/testing/fixture-patterns.d.ts.map +1 -0
- package/dist/testing/fixture-patterns.js +228 -0
- package/dist/testing/fixture-patterns.js.map +1 -0
- package/dist/testing/index.d.ts +31 -0
- package/dist/testing/index.d.ts.map +1 -0
- package/dist/testing/index.js +49 -0
- package/dist/testing/index.js.map +1 -0
- package/dist/testing/mock-patterns.d.ts +53 -0
- package/dist/testing/mock-patterns.d.ts.map +1 -0
- package/dist/testing/mock-patterns.js +264 -0
- package/dist/testing/mock-patterns.js.map +1 -0
- package/dist/testing/setup-teardown.d.ts +55 -0
- package/dist/testing/setup-teardown.d.ts.map +1 -0
- package/dist/testing/setup-teardown.js +262 -0
- package/dist/testing/setup-teardown.js.map +1 -0
- package/dist/testing/test-structure.d.ts +51 -0
- package/dist/testing/test-structure.d.ts.map +1 -0
- package/dist/testing/test-structure.js +225 -0
- package/dist/testing/test-structure.js.map +1 -0
- package/dist/types/any-usage.d.ts +99 -0
- package/dist/types/any-usage.d.ts.map +1 -0
- package/dist/types/any-usage.js +641 -0
- package/dist/types/any-usage.js.map +1 -0
- package/dist/types/file-location.d.ts +76 -0
- package/dist/types/file-location.d.ts.map +1 -0
- package/dist/types/file-location.js +395 -0
- package/dist/types/file-location.js.map +1 -0
- package/dist/types/generic-patterns.d.ts +97 -0
- package/dist/types/generic-patterns.d.ts.map +1 -0
- package/dist/types/generic-patterns.js +615 -0
- package/dist/types/generic-patterns.js.map +1 -0
- package/dist/types/index.d.ts +31 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +43 -0
- package/dist/types/index.js.map +1 -0
- package/dist/types/interface-vs-type.d.ts +81 -0
- package/dist/types/interface-vs-type.d.ts.map +1 -0
- package/dist/types/interface-vs-type.js +440 -0
- package/dist/types/interface-vs-type.js.map +1 -0
- package/dist/types/naming-conventions.d.ts +84 -0
- package/dist/types/naming-conventions.d.ts.map +1 -0
- package/dist/types/naming-conventions.js +455 -0
- package/dist/types/naming-conventions.js.map +1 -0
- package/dist/types/type-assertions.d.ts +98 -0
- package/dist/types/type-assertions.d.ts.map +1 -0
- package/dist/types/type-assertions.js +639 -0
- package/dist/types/type-assertions.js.map +1 -0
- package/dist/types/utility-types.d.ts +110 -0
- package/dist/types/utility-types.d.ts.map +1 -0
- package/dist/types/utility-types.js +547 -0
- package/dist/types/utility-types.js.map +1 -0
- package/package.json +44 -0
|
@@ -0,0 +1,795 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Props Patterns Detector - Component props handling pattern detection
|
|
3
|
+
*
|
|
4
|
+
* Detects props patterns including destructuring, defaults, spreading,
|
|
5
|
+
* and type definitions. Identifies inconsistencies and reports violations.
|
|
6
|
+
*
|
|
7
|
+
* @requirements 8.2 - THE Component_Detector SHALL detect props patterns
|
|
8
|
+
* (destructuring, defaults, required vs optional)
|
|
9
|
+
*/
|
|
10
|
+
import { ASTDetector } from '../base/index.js';
|
|
11
|
+
// ============================================================================
|
|
12
|
+
// Constants
|
|
13
|
+
// ============================================================================
|
|
14
|
+
/**
|
|
15
|
+
* React FC type patterns
|
|
16
|
+
*/
|
|
17
|
+
export const FC_TYPE_PATTERNS = [
|
|
18
|
+
'FC',
|
|
19
|
+
'FunctionComponent',
|
|
20
|
+
'React.FC',
|
|
21
|
+
'React.FunctionComponent',
|
|
22
|
+
'VFC',
|
|
23
|
+
'VoidFunctionComponent',
|
|
24
|
+
'React.VFC',
|
|
25
|
+
'React.VoidFunctionComponent',
|
|
26
|
+
];
|
|
27
|
+
/**
|
|
28
|
+
* Common props type name patterns
|
|
29
|
+
*/
|
|
30
|
+
export const PROPS_TYPE_NAME_PATTERNS = [
|
|
31
|
+
/Props$/,
|
|
32
|
+
/^I[A-Z].*Props$/,
|
|
33
|
+
/^T[A-Z].*Props$/,
|
|
34
|
+
];
|
|
35
|
+
// ============================================================================
|
|
36
|
+
// Helper Functions
|
|
37
|
+
// ============================================================================
|
|
38
|
+
/**
|
|
39
|
+
* Check if a node represents a React component
|
|
40
|
+
*/
|
|
41
|
+
export function isReactComponent(node, content) {
|
|
42
|
+
// Check for function/arrow function that returns JSX
|
|
43
|
+
if (node.type === 'function_declaration' ||
|
|
44
|
+
node.type === 'arrow_function' ||
|
|
45
|
+
node.type === 'function_expression') {
|
|
46
|
+
// Component names should be PascalCase
|
|
47
|
+
const name = getComponentName(node, content);
|
|
48
|
+
if (!name || !/^[A-Z]/.test(name)) {
|
|
49
|
+
return false;
|
|
50
|
+
}
|
|
51
|
+
// Check if it returns JSX (simplified check)
|
|
52
|
+
const nodeText = node.text;
|
|
53
|
+
return nodeText.includes('<') && (nodeText.includes('/>') || nodeText.includes('</'));
|
|
54
|
+
}
|
|
55
|
+
return false;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Get the component name from a node
|
|
59
|
+
*/
|
|
60
|
+
export function getComponentName(node, content) {
|
|
61
|
+
// For function declarations, get the name directly
|
|
62
|
+
if (node.type === 'function_declaration') {
|
|
63
|
+
const nameNode = node.children.find(c => c.type === 'identifier');
|
|
64
|
+
return nameNode?.text;
|
|
65
|
+
}
|
|
66
|
+
// For arrow functions and function expressions, look for variable declaration
|
|
67
|
+
// This is a simplified approach - in real implementation would need parent context
|
|
68
|
+
const lines = content.split('\n');
|
|
69
|
+
const line = lines[node.startPosition.row];
|
|
70
|
+
if (line) {
|
|
71
|
+
// Match patterns like: const ComponentName = or export const ComponentName =
|
|
72
|
+
const match = line.match(/(?:const|let|var|export\s+(?:const|let|var)?)\s+([A-Z][a-zA-Z0-9]*)\s*[=:]/);
|
|
73
|
+
if (match && match[1]) {
|
|
74
|
+
return match[1];
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
return undefined;
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Detect the destructuring pattern used in a component
|
|
81
|
+
*/
|
|
82
|
+
export function detectDestructuringPattern(node, _content) {
|
|
83
|
+
const nodeText = node.text;
|
|
84
|
+
// Check for signature destructuring: ({ prop1, prop2 }) =>
|
|
85
|
+
// or function Component({ prop1, prop2 })
|
|
86
|
+
if (node.type === 'arrow_function' || node.type === 'function_declaration' || node.type === 'function_expression') {
|
|
87
|
+
// Look for object pattern in parameters
|
|
88
|
+
const hasObjectPatternParam = nodeText.match(/\(\s*\{[^}]*\}\s*(?::[^)]+)?\s*\)/);
|
|
89
|
+
if (hasObjectPatternParam) {
|
|
90
|
+
return 'signature';
|
|
91
|
+
}
|
|
92
|
+
// Check for body destructuring: const { prop1, prop2 } = props
|
|
93
|
+
const hasBodyDestructuring = nodeText.match(/(?:const|let|var)\s+\{[^}]+\}\s*=\s*props/);
|
|
94
|
+
if (hasBodyDestructuring) {
|
|
95
|
+
return 'body';
|
|
96
|
+
}
|
|
97
|
+
// Check for direct props access: props.propName
|
|
98
|
+
const hasDirectAccess = nodeText.match(/props\.[a-zA-Z_$][a-zA-Z0-9_$]*/);
|
|
99
|
+
if (hasDirectAccess) {
|
|
100
|
+
return 'direct-access';
|
|
101
|
+
}
|
|
102
|
+
// Check if component has any props parameter
|
|
103
|
+
const hasPropsParam = nodeText.match(/\(\s*props\s*(?::[^)]+)?\s*\)/) ||
|
|
104
|
+
nodeText.match(/function\s+\w+\s*\(\s*props\s*(?::[^)]+)?\s*\)/);
|
|
105
|
+
if (!hasPropsParam && !hasObjectPatternParam) {
|
|
106
|
+
return 'none';
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
return 'unknown';
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Detect the default props pattern used in a component
|
|
113
|
+
*/
|
|
114
|
+
export function detectDefaultPropsPattern(node, content, componentName) {
|
|
115
|
+
const nodeText = node.text;
|
|
116
|
+
// Check for default parameters in signature: ({ name = 'default' })
|
|
117
|
+
const hasDefaultParams = nodeText.match(/\(\s*\{[^}]*=\s*[^,}]+[^}]*\}/);
|
|
118
|
+
if (hasDefaultParams) {
|
|
119
|
+
return 'default-parameters';
|
|
120
|
+
}
|
|
121
|
+
// Check for destructuring defaults in body: const { name = 'default' } = props
|
|
122
|
+
const hasDestructuringDefaults = nodeText.match(/(?:const|let|var)\s+\{[^}]*=\s*[^,}]+[^}]*\}\s*=\s*props/);
|
|
123
|
+
if (hasDestructuringDefaults) {
|
|
124
|
+
return 'destructuring-defaults';
|
|
125
|
+
}
|
|
126
|
+
// Check for nullish coalescing: props.name ?? 'default'
|
|
127
|
+
const hasNullishCoalescing = nodeText.match(/props\.[a-zA-Z_$][a-zA-Z0-9_$]*\s*\?\?/);
|
|
128
|
+
if (hasNullishCoalescing) {
|
|
129
|
+
return 'nullish-coalescing';
|
|
130
|
+
}
|
|
131
|
+
// Check for logical OR: props.name || 'default'
|
|
132
|
+
const hasLogicalOr = nodeText.match(/props\.[a-zA-Z_$][a-zA-Z0-9_$]*\s*\|\|/);
|
|
133
|
+
if (hasLogicalOr) {
|
|
134
|
+
return 'logical-or';
|
|
135
|
+
}
|
|
136
|
+
// Check for static defaultProps (need to look in surrounding content)
|
|
137
|
+
if (componentName) {
|
|
138
|
+
const defaultPropsPattern = new RegExp(`${componentName}\\.defaultProps\\s*=`);
|
|
139
|
+
if (defaultPropsPattern.test(content)) {
|
|
140
|
+
return 'static-defaultProps';
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
return 'none';
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Detect the props spreading pattern used in a component
|
|
147
|
+
*/
|
|
148
|
+
export function detectSpreadingPattern(node, _content) {
|
|
149
|
+
const nodeText = node.text;
|
|
150
|
+
// Check for rest spread: ({ name, ...rest }) and {...rest}
|
|
151
|
+
const hasRestParam = nodeText.match(/\(\s*\{[^}]*,\s*\.\.\.([a-zA-Z_$][a-zA-Z0-9_$]*)\s*\}/);
|
|
152
|
+
const hasRestSpread = nodeText.match(/\{\s*\.\.\.([a-zA-Z_$][a-zA-Z0-9_$]*)\s*\}/);
|
|
153
|
+
if (hasRestParam && hasRestSpread) {
|
|
154
|
+
return 'rest-spread';
|
|
155
|
+
}
|
|
156
|
+
// Check for full props spread: {...props}
|
|
157
|
+
const hasFullSpread = nodeText.match(/\{\s*\.\.\.props\s*\}/);
|
|
158
|
+
if (hasFullSpread) {
|
|
159
|
+
return 'full-spread';
|
|
160
|
+
}
|
|
161
|
+
// Check for selective spreading (passing individual props)
|
|
162
|
+
const hasSelectiveSpread = nodeText.match(/<[A-Z][a-zA-Z0-9]*[^>]*\s+[a-zA-Z_$][a-zA-Z0-9_$]*\s*=\s*\{/);
|
|
163
|
+
if (hasSelectiveSpread && !hasFullSpread && !hasRestSpread) {
|
|
164
|
+
return 'selective-spread';
|
|
165
|
+
}
|
|
166
|
+
return 'none';
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* Detect the props type definition pattern used in a component
|
|
170
|
+
*/
|
|
171
|
+
export function detectTypePattern(node, content, componentName) {
|
|
172
|
+
const nodeText = node.text;
|
|
173
|
+
// Check for inline type: ({ name }: { name: string })
|
|
174
|
+
const hasInlineType = nodeText.match(/\(\s*\{[^}]*\}\s*:\s*\{[^}]+\}\s*\)/);
|
|
175
|
+
if (hasInlineType) {
|
|
176
|
+
return 'inline';
|
|
177
|
+
}
|
|
178
|
+
// Check for FC generic type: FC<Props>, React.FC<Props>
|
|
179
|
+
const hasFCType = FC_TYPE_PATTERNS.some(pattern => {
|
|
180
|
+
const regex = new RegExp(`:\\s*${pattern.replace('.', '\\.')}\\s*<`);
|
|
181
|
+
return regex.test(nodeText) || regex.test(content.split('\n')[node.startPosition.row] || '');
|
|
182
|
+
});
|
|
183
|
+
if (hasFCType) {
|
|
184
|
+
return 'generic';
|
|
185
|
+
}
|
|
186
|
+
// Check for type annotation on props parameter
|
|
187
|
+
const hasTypeAnnotation = nodeText.match(/\(\s*(?:\{[^}]*\}|props)\s*:\s*([A-Z][a-zA-Z0-9]*(?:Props)?)\s*\)/);
|
|
188
|
+
if (hasTypeAnnotation) {
|
|
189
|
+
// Determine if it's interface or type alias by looking in content
|
|
190
|
+
const typeName = hasTypeAnnotation[1];
|
|
191
|
+
if (typeName) {
|
|
192
|
+
const interfacePattern = new RegExp(`interface\\s+${typeName}\\s*\\{`);
|
|
193
|
+
const typePattern = new RegExp(`type\\s+${typeName}\\s*=`);
|
|
194
|
+
if (interfacePattern.test(content)) {
|
|
195
|
+
return 'interface';
|
|
196
|
+
}
|
|
197
|
+
if (typePattern.test(content)) {
|
|
198
|
+
return 'type-alias';
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
// Check for propTypes
|
|
203
|
+
if (componentName) {
|
|
204
|
+
const propTypesPattern = new RegExp(`${componentName}\\.propTypes\\s*=`);
|
|
205
|
+
if (propTypesPattern.test(content)) {
|
|
206
|
+
return 'prop-types';
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
return 'none';
|
|
210
|
+
}
|
|
211
|
+
/**
|
|
212
|
+
* Extract prop names from a component
|
|
213
|
+
*/
|
|
214
|
+
export function extractPropNames(node, _content) {
|
|
215
|
+
const nodeText = node.text;
|
|
216
|
+
const propNames = [];
|
|
217
|
+
// Extract from signature destructuring: ({ prop1, prop2, prop3 = 'default' })
|
|
218
|
+
const signatureMatch = nodeText.match(/\(\s*\{\s*([^}]+)\s*\}/);
|
|
219
|
+
if (signatureMatch && signatureMatch[1]) {
|
|
220
|
+
const propsStr = signatureMatch[1];
|
|
221
|
+
// Split by comma and extract prop names
|
|
222
|
+
const props = propsStr.split(',').map(p => {
|
|
223
|
+
// Handle: prop, prop = default, prop: alias, ...rest
|
|
224
|
+
const trimmed = p.trim();
|
|
225
|
+
if (trimmed.startsWith('...'))
|
|
226
|
+
return null; // Skip rest spread
|
|
227
|
+
const nameMatch = trimmed.match(/^([a-zA-Z_$][a-zA-Z0-9_$]*)/);
|
|
228
|
+
return nameMatch ? nameMatch[1] : null;
|
|
229
|
+
}).filter((p) => p !== null);
|
|
230
|
+
propNames.push(...props);
|
|
231
|
+
}
|
|
232
|
+
// Extract from body destructuring: const { prop1, prop2 } = props
|
|
233
|
+
const bodyMatch = nodeText.match(/(?:const|let|var)\s+\{\s*([^}]+)\s*\}\s*=\s*props/);
|
|
234
|
+
if (bodyMatch && bodyMatch[1]) {
|
|
235
|
+
const propsStr = bodyMatch[1];
|
|
236
|
+
const props = propsStr.split(',').map(p => {
|
|
237
|
+
const trimmed = p.trim();
|
|
238
|
+
if (trimmed.startsWith('...'))
|
|
239
|
+
return null;
|
|
240
|
+
const nameMatch = trimmed.match(/^([a-zA-Z_$][a-zA-Z0-9_$]*)/);
|
|
241
|
+
return nameMatch ? nameMatch[1] : null;
|
|
242
|
+
}).filter((p) => p !== null);
|
|
243
|
+
propNames.push(...props);
|
|
244
|
+
}
|
|
245
|
+
// Extract from direct access: props.propName
|
|
246
|
+
const directAccessMatches = nodeText.matchAll(/props\.([a-zA-Z_$][a-zA-Z0-9_$]*)/g);
|
|
247
|
+
for (const match of directAccessMatches) {
|
|
248
|
+
if (match[1] && !propNames.includes(match[1])) {
|
|
249
|
+
propNames.push(match[1]);
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
return [...new Set(propNames)]; // Remove duplicates
|
|
253
|
+
}
|
|
254
|
+
/**
|
|
255
|
+
* Extract prop names that have default values
|
|
256
|
+
*/
|
|
257
|
+
export function extractPropsWithDefaults(node, _content) {
|
|
258
|
+
const nodeText = node.text;
|
|
259
|
+
const propsWithDefaults = [];
|
|
260
|
+
// Extract from signature defaults: ({ prop1 = 'default', prop2 = 42 })
|
|
261
|
+
const signatureMatch = nodeText.match(/\(\s*\{\s*([^}]+)\s*\}/);
|
|
262
|
+
if (signatureMatch && signatureMatch[1]) {
|
|
263
|
+
const propsStr = signatureMatch[1];
|
|
264
|
+
const props = propsStr.split(',').map(p => {
|
|
265
|
+
const trimmed = p.trim();
|
|
266
|
+
// Match: propName = defaultValue
|
|
267
|
+
const defaultMatch = trimmed.match(/^([a-zA-Z_$][a-zA-Z0-9_$]*)\s*=/);
|
|
268
|
+
return defaultMatch ? defaultMatch[1] : null;
|
|
269
|
+
}).filter((p) => p !== null);
|
|
270
|
+
propsWithDefaults.push(...props);
|
|
271
|
+
}
|
|
272
|
+
// Extract from body destructuring defaults
|
|
273
|
+
const bodyMatch = nodeText.match(/(?:const|let|var)\s+\{\s*([^}]+)\s*\}\s*=\s*props/);
|
|
274
|
+
if (bodyMatch && bodyMatch[1]) {
|
|
275
|
+
const propsStr = bodyMatch[1];
|
|
276
|
+
const props = propsStr.split(',').map(p => {
|
|
277
|
+
const trimmed = p.trim();
|
|
278
|
+
const defaultMatch = trimmed.match(/^([a-zA-Z_$][a-zA-Z0-9_$]*)\s*=/);
|
|
279
|
+
return defaultMatch ? defaultMatch[1] : null;
|
|
280
|
+
}).filter((p) => p !== null);
|
|
281
|
+
propsWithDefaults.push(...props);
|
|
282
|
+
}
|
|
283
|
+
return [...new Set(propsWithDefaults)];
|
|
284
|
+
}
|
|
285
|
+
/**
|
|
286
|
+
* Get props type name from a component
|
|
287
|
+
*/
|
|
288
|
+
export function getPropsTypeName(node, content) {
|
|
289
|
+
const nodeText = node.text;
|
|
290
|
+
const line = content.split('\n')[node.startPosition.row] || '';
|
|
291
|
+
// Check for FC<PropsType>
|
|
292
|
+
for (const fcPattern of FC_TYPE_PATTERNS) {
|
|
293
|
+
const regex = new RegExp(`${fcPattern.replace('.', '\\.')}\\s*<\\s*([A-Z][a-zA-Z0-9]*)\\s*>`);
|
|
294
|
+
const match = line.match(regex) || nodeText.match(regex);
|
|
295
|
+
if (match && match[1]) {
|
|
296
|
+
return match[1];
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
// Check for type annotation: (props: PropsType) or ({ ... }: PropsType)
|
|
300
|
+
const typeAnnotationMatch = nodeText.match(/\)\s*:\s*([A-Z][a-zA-Z0-9]*(?:Props)?)\s*(?:=>|\{)/);
|
|
301
|
+
if (typeAnnotationMatch && typeAnnotationMatch[1]) {
|
|
302
|
+
return typeAnnotationMatch[1];
|
|
303
|
+
}
|
|
304
|
+
// Check for parameter type: (props: PropsType)
|
|
305
|
+
const paramTypeMatch = nodeText.match(/\(\s*(?:\{[^}]*\}|props)\s*:\s*([A-Z][a-zA-Z0-9]*(?:Props)?)\s*\)/);
|
|
306
|
+
if (paramTypeMatch && paramTypeMatch[1]) {
|
|
307
|
+
return paramTypeMatch[1];
|
|
308
|
+
}
|
|
309
|
+
return undefined;
|
|
310
|
+
}
|
|
311
|
+
/**
|
|
312
|
+
* Check if component uses FC type
|
|
313
|
+
*/
|
|
314
|
+
export function usesFCType(node, content) {
|
|
315
|
+
const line = content.split('\n')[node.startPosition.row] || '';
|
|
316
|
+
return FC_TYPE_PATTERNS.some(pattern => {
|
|
317
|
+
const regex = new RegExp(`:\\s*${pattern.replace('.', '\\.')}\\s*<`);
|
|
318
|
+
return regex.test(line);
|
|
319
|
+
});
|
|
320
|
+
}
|
|
321
|
+
/**
|
|
322
|
+
* Analyze a single component's props patterns
|
|
323
|
+
*/
|
|
324
|
+
export function analyzeComponentProps(node, content, filePath) {
|
|
325
|
+
const componentName = getComponentName(node, content);
|
|
326
|
+
if (!componentName) {
|
|
327
|
+
return null;
|
|
328
|
+
}
|
|
329
|
+
return {
|
|
330
|
+
componentName,
|
|
331
|
+
filePath,
|
|
332
|
+
line: node.startPosition.row + 1,
|
|
333
|
+
column: node.startPosition.column + 1,
|
|
334
|
+
destructuringPattern: detectDestructuringPattern(node, content),
|
|
335
|
+
defaultPropsPattern: detectDefaultPropsPattern(node, content, componentName),
|
|
336
|
+
spreadingPattern: detectSpreadingPattern(node, content),
|
|
337
|
+
typePattern: detectTypePattern(node, content, componentName),
|
|
338
|
+
propsTypeName: getPropsTypeName(node, content),
|
|
339
|
+
propsWithDefaults: extractPropsWithDefaults(node, content),
|
|
340
|
+
allPropNames: extractPropNames(node, content),
|
|
341
|
+
usesFCType: usesFCType(node, content),
|
|
342
|
+
};
|
|
343
|
+
}
|
|
344
|
+
/**
|
|
345
|
+
* Find the dominant pattern from a list of patterns
|
|
346
|
+
*/
|
|
347
|
+
function findDominantPattern(patterns, excludeValues = []) {
|
|
348
|
+
const counts = new Map();
|
|
349
|
+
for (const pattern of patterns) {
|
|
350
|
+
if (!excludeValues.includes(pattern)) {
|
|
351
|
+
counts.set(pattern, (counts.get(pattern) || 0) + 1);
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
let dominant = patterns[0] || 'unknown';
|
|
355
|
+
let maxCount = 0;
|
|
356
|
+
for (const [pattern, count] of counts) {
|
|
357
|
+
if (count > maxCount) {
|
|
358
|
+
maxCount = count;
|
|
359
|
+
dominant = pattern;
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
const total = patterns.filter(p => !excludeValues.includes(p)).length;
|
|
363
|
+
const confidence = total > 0 ? maxCount / total : 0;
|
|
364
|
+
return { dominant, confidence };
|
|
365
|
+
}
|
|
366
|
+
/**
|
|
367
|
+
* Analyze props patterns across multiple components
|
|
368
|
+
*/
|
|
369
|
+
export function analyzePropsPatterns(components) {
|
|
370
|
+
if (components.length === 0) {
|
|
371
|
+
return {
|
|
372
|
+
components: [],
|
|
373
|
+
dominantDestructuringPattern: 'unknown',
|
|
374
|
+
dominantDefaultPropsPattern: 'none',
|
|
375
|
+
dominantSpreadingPattern: 'none',
|
|
376
|
+
dominantTypePattern: 'none',
|
|
377
|
+
confidence: {
|
|
378
|
+
destructuring: 0,
|
|
379
|
+
defaults: 0,
|
|
380
|
+
spreading: 0,
|
|
381
|
+
types: 0,
|
|
382
|
+
},
|
|
383
|
+
inconsistentComponents: [],
|
|
384
|
+
};
|
|
385
|
+
}
|
|
386
|
+
// Find dominant patterns
|
|
387
|
+
const destructuringResult = findDominantPattern(components.map(c => c.destructuringPattern), ['unknown', 'none']);
|
|
388
|
+
const defaultsResult = findDominantPattern(components.map(c => c.defaultPropsPattern), ['unknown', 'none']);
|
|
389
|
+
const spreadingResult = findDominantPattern(components.map(c => c.spreadingPattern), ['unknown', 'none']);
|
|
390
|
+
const typesResult = findDominantPattern(components.map(c => c.typePattern), ['unknown', 'none']);
|
|
391
|
+
// Find inconsistent components
|
|
392
|
+
const inconsistentComponents = components.filter(c => {
|
|
393
|
+
// A component is inconsistent if it uses a different pattern than dominant
|
|
394
|
+
// and the pattern is not 'none' or 'unknown'
|
|
395
|
+
const destructuringInconsistent = c.destructuringPattern !== destructuringResult.dominant &&
|
|
396
|
+
c.destructuringPattern !== 'none' &&
|
|
397
|
+
c.destructuringPattern !== 'unknown' &&
|
|
398
|
+
destructuringResult.confidence > 0.5;
|
|
399
|
+
const defaultsInconsistent = c.defaultPropsPattern !== defaultsResult.dominant &&
|
|
400
|
+
c.defaultPropsPattern !== 'none' &&
|
|
401
|
+
c.defaultPropsPattern !== 'unknown' &&
|
|
402
|
+
defaultsResult.confidence > 0.5;
|
|
403
|
+
const typesInconsistent = c.typePattern !== typesResult.dominant &&
|
|
404
|
+
c.typePattern !== 'none' &&
|
|
405
|
+
c.typePattern !== 'unknown' &&
|
|
406
|
+
typesResult.confidence > 0.5;
|
|
407
|
+
return destructuringInconsistent || defaultsInconsistent || typesInconsistent;
|
|
408
|
+
});
|
|
409
|
+
return {
|
|
410
|
+
components,
|
|
411
|
+
dominantDestructuringPattern: destructuringResult.dominant,
|
|
412
|
+
dominantDefaultPropsPattern: defaultsResult.dominant,
|
|
413
|
+
dominantSpreadingPattern: spreadingResult.dominant,
|
|
414
|
+
dominantTypePattern: typesResult.dominant,
|
|
415
|
+
confidence: {
|
|
416
|
+
destructuring: destructuringResult.confidence,
|
|
417
|
+
defaults: defaultsResult.confidence,
|
|
418
|
+
spreading: spreadingResult.confidence,
|
|
419
|
+
types: typesResult.confidence,
|
|
420
|
+
},
|
|
421
|
+
inconsistentComponents,
|
|
422
|
+
};
|
|
423
|
+
}
|
|
424
|
+
/**
|
|
425
|
+
* Generate a suggestion for refactoring props handling
|
|
426
|
+
*/
|
|
427
|
+
export function generatePropsSuggestion(component, targetDestructuring, targetDefaults, targetTypes) {
|
|
428
|
+
const suggestions = [];
|
|
429
|
+
if (component.destructuringPattern !== targetDestructuring &&
|
|
430
|
+
component.destructuringPattern !== 'none' &&
|
|
431
|
+
component.destructuringPattern !== 'unknown') {
|
|
432
|
+
const destructuringDescriptions = {
|
|
433
|
+
'signature': 'destructure props in function signature',
|
|
434
|
+
'body': 'destructure props in function body',
|
|
435
|
+
'direct-access': 'access props directly',
|
|
436
|
+
'none': 'no props',
|
|
437
|
+
'unknown': 'unknown pattern',
|
|
438
|
+
};
|
|
439
|
+
suggestions.push(`Consider ${destructuringDescriptions[targetDestructuring]}`);
|
|
440
|
+
}
|
|
441
|
+
if (component.defaultPropsPattern !== targetDefaults &&
|
|
442
|
+
component.defaultPropsPattern !== 'none' &&
|
|
443
|
+
component.defaultPropsPattern !== 'unknown') {
|
|
444
|
+
const defaultsDescriptions = {
|
|
445
|
+
'static-defaultProps': 'using static defaultProps',
|
|
446
|
+
'default-parameters': 'using default parameters in signature',
|
|
447
|
+
'destructuring-defaults': 'using defaults in destructuring',
|
|
448
|
+
'logical-or': 'using logical OR for defaults',
|
|
449
|
+
'nullish-coalescing': 'using nullish coalescing for defaults',
|
|
450
|
+
'none': 'no defaults',
|
|
451
|
+
'unknown': 'unknown pattern',
|
|
452
|
+
};
|
|
453
|
+
suggestions.push(`Consider ${defaultsDescriptions[targetDefaults]}`);
|
|
454
|
+
}
|
|
455
|
+
if (component.typePattern !== targetTypes &&
|
|
456
|
+
component.typePattern !== 'none' &&
|
|
457
|
+
component.typePattern !== 'unknown') {
|
|
458
|
+
const typeDescriptions = {
|
|
459
|
+
'interface': 'using interface for props type',
|
|
460
|
+
'type-alias': 'using type alias for props type',
|
|
461
|
+
'inline': 'using inline type annotation',
|
|
462
|
+
'generic': 'using FC<Props> generic type',
|
|
463
|
+
'prop-types': 'using PropTypes',
|
|
464
|
+
'none': 'no type definition',
|
|
465
|
+
'unknown': 'unknown pattern',
|
|
466
|
+
};
|
|
467
|
+
suggestions.push(`Consider ${typeDescriptions[targetTypes]}`);
|
|
468
|
+
}
|
|
469
|
+
return suggestions.join('. ');
|
|
470
|
+
}
|
|
471
|
+
// ============================================================================
|
|
472
|
+
// Pattern Description Helpers
|
|
473
|
+
// ============================================================================
|
|
474
|
+
const DESTRUCTURING_DESCRIPTIONS = {
|
|
475
|
+
'signature': 'props destructured in function signature',
|
|
476
|
+
'body': 'props destructured in function body',
|
|
477
|
+
'direct-access': 'props accessed directly',
|
|
478
|
+
'none': 'no props',
|
|
479
|
+
'unknown': 'unknown pattern',
|
|
480
|
+
};
|
|
481
|
+
const DEFAULTS_DESCRIPTIONS = {
|
|
482
|
+
'static-defaultProps': 'static defaultProps',
|
|
483
|
+
'default-parameters': 'default parameters in signature',
|
|
484
|
+
'destructuring-defaults': 'defaults in destructuring',
|
|
485
|
+
'logical-or': 'logical OR for defaults',
|
|
486
|
+
'nullish-coalescing': 'nullish coalescing for defaults',
|
|
487
|
+
'none': 'no defaults',
|
|
488
|
+
'unknown': 'unknown pattern',
|
|
489
|
+
};
|
|
490
|
+
const TYPE_DESCRIPTIONS = {
|
|
491
|
+
'interface': 'interface for props type',
|
|
492
|
+
'type-alias': 'type alias for props type',
|
|
493
|
+
'inline': 'inline type annotation',
|
|
494
|
+
'generic': 'FC<Props> generic type',
|
|
495
|
+
'prop-types': 'PropTypes',
|
|
496
|
+
'none': 'no type definition',
|
|
497
|
+
'unknown': 'unknown pattern',
|
|
498
|
+
};
|
|
499
|
+
// ============================================================================
|
|
500
|
+
// Props Patterns Detector Class
|
|
501
|
+
// ============================================================================
|
|
502
|
+
/**
|
|
503
|
+
* Detector for component props patterns
|
|
504
|
+
*
|
|
505
|
+
* Identifies props handling patterns including destructuring, defaults,
|
|
506
|
+
* spreading, and type definitions. Reports violations when components
|
|
507
|
+
* don't follow the dominant pattern.
|
|
508
|
+
*
|
|
509
|
+
* @requirements 8.2 - THE Component_Detector SHALL detect props patterns
|
|
510
|
+
*/
|
|
511
|
+
export class PropsPatternDetector extends ASTDetector {
|
|
512
|
+
id = 'components/props-patterns';
|
|
513
|
+
category = 'components';
|
|
514
|
+
subcategory = 'props-handling';
|
|
515
|
+
name = 'Props Pattern Detector';
|
|
516
|
+
description = 'Detects props handling patterns (destructuring, defaults, types) and identifies inconsistencies';
|
|
517
|
+
supportedLanguages = ['typescript', 'javascript'];
|
|
518
|
+
/**
|
|
519
|
+
* Detect props patterns in the project
|
|
520
|
+
*/
|
|
521
|
+
async detect(context) {
|
|
522
|
+
const patterns = [];
|
|
523
|
+
const violations = [];
|
|
524
|
+
// If no AST, try to detect patterns from content using regex
|
|
525
|
+
const componentInfos = this.findComponentsInFile(context);
|
|
526
|
+
if (componentInfos.length === 0) {
|
|
527
|
+
return this.createEmptyResult();
|
|
528
|
+
}
|
|
529
|
+
// Analyze patterns across all components
|
|
530
|
+
const analysis = analyzePropsPatterns(componentInfos);
|
|
531
|
+
// Create pattern matches for detected patterns
|
|
532
|
+
if (analysis.dominantDestructuringPattern !== 'unknown' && analysis.confidence.destructuring > 0.3) {
|
|
533
|
+
patterns.push(this.createDestructuringPattern(context.file, analysis));
|
|
534
|
+
}
|
|
535
|
+
if (analysis.dominantDefaultPropsPattern !== 'none' && analysis.confidence.defaults > 0.3) {
|
|
536
|
+
patterns.push(this.createDefaultsPattern(context.file, analysis));
|
|
537
|
+
}
|
|
538
|
+
if (analysis.dominantTypePattern !== 'none' && analysis.confidence.types > 0.3) {
|
|
539
|
+
patterns.push(this.createTypePattern(context.file, analysis));
|
|
540
|
+
}
|
|
541
|
+
// Generate violations for inconsistent components in current file
|
|
542
|
+
for (const inconsistent of analysis.inconsistentComponents) {
|
|
543
|
+
if (inconsistent.filePath === context.file) {
|
|
544
|
+
const violation = this.createInconsistencyViolation(inconsistent, analysis);
|
|
545
|
+
if (violation) {
|
|
546
|
+
violations.push(violation);
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
const overallConfidence = Math.max(analysis.confidence.destructuring, analysis.confidence.defaults, analysis.confidence.types);
|
|
551
|
+
return this.createResult(patterns, violations, overallConfidence);
|
|
552
|
+
}
|
|
553
|
+
/**
|
|
554
|
+
* Find all React components in a file
|
|
555
|
+
*/
|
|
556
|
+
findComponentsInFile(context) {
|
|
557
|
+
const components = [];
|
|
558
|
+
if (context.ast) {
|
|
559
|
+
// Use AST to find components
|
|
560
|
+
const functionNodes = this.findNodesByTypes(context.ast, [
|
|
561
|
+
'function_declaration',
|
|
562
|
+
'arrow_function',
|
|
563
|
+
'function_expression',
|
|
564
|
+
]);
|
|
565
|
+
for (const node of functionNodes) {
|
|
566
|
+
if (isReactComponent(node, context.content)) {
|
|
567
|
+
const info = analyzeComponentProps(node, context.content, context.file);
|
|
568
|
+
if (info) {
|
|
569
|
+
components.push(info);
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
else {
|
|
575
|
+
// Fallback: use regex-based detection
|
|
576
|
+
const componentMatches = this.findComponentsWithRegex(context.content, context.file);
|
|
577
|
+
components.push(...componentMatches);
|
|
578
|
+
}
|
|
579
|
+
return components;
|
|
580
|
+
}
|
|
581
|
+
/**
|
|
582
|
+
* Find components using regex (fallback when AST is not available)
|
|
583
|
+
*/
|
|
584
|
+
findComponentsWithRegex(content, filePath) {
|
|
585
|
+
const components = [];
|
|
586
|
+
const seenComponents = new Set();
|
|
587
|
+
// Multiple patterns for different component styles
|
|
588
|
+
// Arrow function: const Button = ({ name }) => ...
|
|
589
|
+
const arrowPattern = /(?:export\s+)?const\s+([A-Z][a-zA-Z0-9]*)\s*(?::\s*(?:React\.)?(?:FC|FunctionComponent|VFC)\s*<[^>]*>\s*)?=\s*\(/g;
|
|
590
|
+
// Function declaration: function Button({ name }) { ... }
|
|
591
|
+
const functionPattern = /(?:export\s+)?function\s+([A-Z][a-zA-Z0-9]*)\s*\(/g;
|
|
592
|
+
const processPattern = (pattern) => {
|
|
593
|
+
let match;
|
|
594
|
+
while ((match = pattern.exec(content)) !== null) {
|
|
595
|
+
const componentName = match[1];
|
|
596
|
+
if (!componentName || seenComponents.has(componentName))
|
|
597
|
+
continue;
|
|
598
|
+
// Find the line number
|
|
599
|
+
const beforeMatch = content.slice(0, match.index);
|
|
600
|
+
const lineNumber = beforeMatch.split('\n').length;
|
|
601
|
+
// Get the component body - find the matching closing brace/paren
|
|
602
|
+
const startIndex = match.index;
|
|
603
|
+
let braceCount = 0;
|
|
604
|
+
let parenCount = 0;
|
|
605
|
+
let endIndex = startIndex;
|
|
606
|
+
let foundArrow = false;
|
|
607
|
+
let inFunctionBody = false;
|
|
608
|
+
for (let i = startIndex; i < content.length && i < startIndex + 10000; i++) {
|
|
609
|
+
const char = content[i];
|
|
610
|
+
if (char === '(') {
|
|
611
|
+
parenCount++;
|
|
612
|
+
}
|
|
613
|
+
else if (char === ')') {
|
|
614
|
+
parenCount--;
|
|
615
|
+
}
|
|
616
|
+
else if (char === '=' && content[i + 1] === '>') {
|
|
617
|
+
foundArrow = true;
|
|
618
|
+
// Check if next non-whitespace char is { (block body) or something else (expression body)
|
|
619
|
+
let j = i + 2;
|
|
620
|
+
while (j < content.length && /\s/.test(content[j] || ''))
|
|
621
|
+
j++;
|
|
622
|
+
if (content[j] === '{') {
|
|
623
|
+
inFunctionBody = true;
|
|
624
|
+
}
|
|
625
|
+
}
|
|
626
|
+
else if (char === '{') {
|
|
627
|
+
braceCount++;
|
|
628
|
+
}
|
|
629
|
+
else if (char === '}') {
|
|
630
|
+
braceCount--;
|
|
631
|
+
if (inFunctionBody && braceCount === 0 && parenCount <= 0) {
|
|
632
|
+
endIndex = i + 1;
|
|
633
|
+
break;
|
|
634
|
+
}
|
|
635
|
+
}
|
|
636
|
+
else if ((char === ';' || (char === '\n' && i > startIndex + 20)) && foundArrow && !inFunctionBody && parenCount <= 0) {
|
|
637
|
+
// End of arrow function with expression body
|
|
638
|
+
endIndex = i + 1;
|
|
639
|
+
break;
|
|
640
|
+
}
|
|
641
|
+
}
|
|
642
|
+
// If we didn't find a proper end, try to get a reasonable chunk
|
|
643
|
+
if (endIndex === startIndex) {
|
|
644
|
+
endIndex = Math.min(startIndex + 2000, content.length);
|
|
645
|
+
}
|
|
646
|
+
const componentBody = content.slice(startIndex, endIndex);
|
|
647
|
+
// Check if it returns JSX - look for < followed by a tag name
|
|
648
|
+
const hasJSX = /<[A-Za-z]/.test(componentBody);
|
|
649
|
+
if (!hasJSX) {
|
|
650
|
+
continue;
|
|
651
|
+
}
|
|
652
|
+
seenComponents.add(componentName);
|
|
653
|
+
// Create a mock node for analysis
|
|
654
|
+
const mockNode = {
|
|
655
|
+
type: 'function_declaration',
|
|
656
|
+
text: componentBody,
|
|
657
|
+
startPosition: { row: lineNumber - 1, column: 0 },
|
|
658
|
+
endPosition: { row: lineNumber - 1 + componentBody.split('\n').length, column: 0 },
|
|
659
|
+
children: [],
|
|
660
|
+
};
|
|
661
|
+
const info = analyzeComponentProps(mockNode, content, filePath);
|
|
662
|
+
if (info) {
|
|
663
|
+
components.push(info);
|
|
664
|
+
}
|
|
665
|
+
}
|
|
666
|
+
};
|
|
667
|
+
processPattern(arrowPattern);
|
|
668
|
+
processPattern(functionPattern);
|
|
669
|
+
return components;
|
|
670
|
+
}
|
|
671
|
+
/**
|
|
672
|
+
* Create a pattern match for destructuring pattern
|
|
673
|
+
*/
|
|
674
|
+
createDestructuringPattern(file, analysis) {
|
|
675
|
+
return {
|
|
676
|
+
patternId: `props-destructuring-${analysis.dominantDestructuringPattern}`,
|
|
677
|
+
location: { file, line: 1, column: 1 },
|
|
678
|
+
confidence: analysis.confidence.destructuring,
|
|
679
|
+
isOutlier: false,
|
|
680
|
+
};
|
|
681
|
+
}
|
|
682
|
+
/**
|
|
683
|
+
* Create a pattern match for defaults pattern
|
|
684
|
+
*/
|
|
685
|
+
createDefaultsPattern(file, analysis) {
|
|
686
|
+
return {
|
|
687
|
+
patternId: `props-defaults-${analysis.dominantDefaultPropsPattern}`,
|
|
688
|
+
location: { file, line: 1, column: 1 },
|
|
689
|
+
confidence: analysis.confidence.defaults,
|
|
690
|
+
isOutlier: false,
|
|
691
|
+
};
|
|
692
|
+
}
|
|
693
|
+
/**
|
|
694
|
+
* Create a pattern match for type pattern
|
|
695
|
+
*/
|
|
696
|
+
createTypePattern(file, analysis) {
|
|
697
|
+
return {
|
|
698
|
+
patternId: `props-types-${analysis.dominantTypePattern}`,
|
|
699
|
+
location: { file, line: 1, column: 1 },
|
|
700
|
+
confidence: analysis.confidence.types,
|
|
701
|
+
isOutlier: false,
|
|
702
|
+
};
|
|
703
|
+
}
|
|
704
|
+
/**
|
|
705
|
+
* Create a violation for an inconsistent component
|
|
706
|
+
*/
|
|
707
|
+
createInconsistencyViolation(component, analysis) {
|
|
708
|
+
const inconsistencies = [];
|
|
709
|
+
// Check destructuring inconsistency
|
|
710
|
+
if (component.destructuringPattern !== analysis.dominantDestructuringPattern &&
|
|
711
|
+
component.destructuringPattern !== 'none' &&
|
|
712
|
+
component.destructuringPattern !== 'unknown' &&
|
|
713
|
+
analysis.confidence.destructuring > 0.5) {
|
|
714
|
+
inconsistencies.push(`uses ${DESTRUCTURING_DESCRIPTIONS[component.destructuringPattern]} but project uses ${DESTRUCTURING_DESCRIPTIONS[analysis.dominantDestructuringPattern]}`);
|
|
715
|
+
}
|
|
716
|
+
// Check defaults inconsistency
|
|
717
|
+
if (component.defaultPropsPattern !== analysis.dominantDefaultPropsPattern &&
|
|
718
|
+
component.defaultPropsPattern !== 'none' &&
|
|
719
|
+
component.defaultPropsPattern !== 'unknown' &&
|
|
720
|
+
analysis.confidence.defaults > 0.5) {
|
|
721
|
+
inconsistencies.push(`uses ${DEFAULTS_DESCRIPTIONS[component.defaultPropsPattern]} but project uses ${DEFAULTS_DESCRIPTIONS[analysis.dominantDefaultPropsPattern]}`);
|
|
722
|
+
}
|
|
723
|
+
// Check type inconsistency
|
|
724
|
+
if (component.typePattern !== analysis.dominantTypePattern &&
|
|
725
|
+
component.typePattern !== 'none' &&
|
|
726
|
+
component.typePattern !== 'unknown' &&
|
|
727
|
+
analysis.confidence.types > 0.5) {
|
|
728
|
+
inconsistencies.push(`uses ${TYPE_DESCRIPTIONS[component.typePattern]} but project uses ${TYPE_DESCRIPTIONS[analysis.dominantTypePattern]}`);
|
|
729
|
+
}
|
|
730
|
+
if (inconsistencies.length === 0) {
|
|
731
|
+
return null;
|
|
732
|
+
}
|
|
733
|
+
const range = {
|
|
734
|
+
start: { line: component.line, character: component.column },
|
|
735
|
+
end: { line: component.line, character: component.column },
|
|
736
|
+
};
|
|
737
|
+
const suggestion = generatePropsSuggestion(component, analysis.dominantDestructuringPattern, analysis.dominantDefaultPropsPattern, analysis.dominantTypePattern);
|
|
738
|
+
return {
|
|
739
|
+
id: `props-pattern-${component.componentName}-${component.filePath.replace(/[^a-zA-Z0-9]/g, '-')}`,
|
|
740
|
+
patternId: 'components/props-patterns',
|
|
741
|
+
severity: 'warning',
|
|
742
|
+
file: component.filePath,
|
|
743
|
+
range,
|
|
744
|
+
message: `Component '${component.componentName}' ${inconsistencies.join('; ')}. ${suggestion}`,
|
|
745
|
+
expected: `Consistent props handling following project patterns`,
|
|
746
|
+
actual: `Inconsistent props handling in ${component.componentName}`,
|
|
747
|
+
aiExplainAvailable: true,
|
|
748
|
+
aiFixAvailable: true,
|
|
749
|
+
firstSeen: new Date(),
|
|
750
|
+
occurrences: 1,
|
|
751
|
+
};
|
|
752
|
+
}
|
|
753
|
+
/**
|
|
754
|
+
* Generate a quick fix for props pattern violations
|
|
755
|
+
*/
|
|
756
|
+
generateQuickFix(violation) {
|
|
757
|
+
// Extract the target pattern from the violation message
|
|
758
|
+
const destructuringMatch = violation.message.match(/project uses (props destructured in function signature|props destructured in function body|props accessed directly)/);
|
|
759
|
+
const defaultsMatch = violation.message.match(/project uses (default parameters in signature|defaults in destructuring|static defaultProps)/);
|
|
760
|
+
const typesMatch = violation.message.match(/project uses (interface for props type|type alias for props type|FC<Props> generic type)/);
|
|
761
|
+
if (!destructuringMatch && !defaultsMatch && !typesMatch) {
|
|
762
|
+
return null;
|
|
763
|
+
}
|
|
764
|
+
const suggestions = [];
|
|
765
|
+
if (destructuringMatch)
|
|
766
|
+
suggestions.push(destructuringMatch[1]);
|
|
767
|
+
if (defaultsMatch)
|
|
768
|
+
suggestions.push(defaultsMatch[1]);
|
|
769
|
+
if (typesMatch)
|
|
770
|
+
suggestions.push(typesMatch[1]);
|
|
771
|
+
return {
|
|
772
|
+
title: `Refactor to use ${suggestions.join(', ')}`,
|
|
773
|
+
kind: 'refactor',
|
|
774
|
+
edit: {
|
|
775
|
+
changes: {},
|
|
776
|
+
documentChanges: [
|
|
777
|
+
{ uri: violation.file, edits: [] },
|
|
778
|
+
],
|
|
779
|
+
},
|
|
780
|
+
isPreferred: true,
|
|
781
|
+
confidence: 0.6,
|
|
782
|
+
preview: `Refactor props handling to follow project patterns`,
|
|
783
|
+
};
|
|
784
|
+
}
|
|
785
|
+
}
|
|
786
|
+
// ============================================================================
|
|
787
|
+
// Factory Function
|
|
788
|
+
// ============================================================================
|
|
789
|
+
/**
|
|
790
|
+
* Create a new PropsPatternDetector instance
|
|
791
|
+
*/
|
|
792
|
+
export function createPropsPatternDetector() {
|
|
793
|
+
return new PropsPatternDetector();
|
|
794
|
+
}
|
|
795
|
+
//# sourceMappingURL=props-patterns.js.map
|