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,832 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ref Forwarding Detector - Ref forwarding pattern detection
|
|
3
|
+
*
|
|
4
|
+
* Detects ref forwarding patterns including React.forwardRef usage,
|
|
5
|
+
* useImperativeHandle, ref prop forwarding, callback refs, and ref merging.
|
|
6
|
+
* Identifies issues and suggests improvements.
|
|
7
|
+
*
|
|
8
|
+
* @requirements 8.7 - THE Component_Detector SHALL detect ref forwarding patterns
|
|
9
|
+
*/
|
|
10
|
+
import { ASTDetector } from '../base/index.js';
|
|
11
|
+
// ============================================================================
|
|
12
|
+
// Constants
|
|
13
|
+
// ============================================================================
|
|
14
|
+
/**
|
|
15
|
+
* Default configuration for ref forwarding detection
|
|
16
|
+
*/
|
|
17
|
+
export const DEFAULT_REF_FORWARDING_CONFIG = {
|
|
18
|
+
maxImperativeHandleMethods: 5,
|
|
19
|
+
flagUnusedRefs: true,
|
|
20
|
+
flagMissingForwardRef: true,
|
|
21
|
+
};
|
|
22
|
+
/**
|
|
23
|
+
* Common ref-related hook names
|
|
24
|
+
*/
|
|
25
|
+
export const REF_HOOKS = ['useRef', 'useImperativeHandle', 'useCallback'];
|
|
26
|
+
/**
|
|
27
|
+
* DOM element names that commonly receive refs
|
|
28
|
+
*/
|
|
29
|
+
export const DOM_ELEMENTS_WITH_REFS = [
|
|
30
|
+
'input', 'textarea', 'select', 'button', 'form',
|
|
31
|
+
'div', 'span', 'section', 'article', 'nav', 'header', 'footer',
|
|
32
|
+
'canvas', 'video', 'audio', 'img', 'iframe',
|
|
33
|
+
'table', 'tbody', 'thead', 'tr', 'td', 'th',
|
|
34
|
+
'ul', 'ol', 'li', 'a', 'p', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6',
|
|
35
|
+
];
|
|
36
|
+
// ============================================================================
|
|
37
|
+
// Helper Functions - Component Detection
|
|
38
|
+
// ============================================================================
|
|
39
|
+
/**
|
|
40
|
+
* Check if a node represents a React component
|
|
41
|
+
*/
|
|
42
|
+
export function isReactComponent(node, content) {
|
|
43
|
+
if (node.type === 'function_declaration' ||
|
|
44
|
+
node.type === 'arrow_function' ||
|
|
45
|
+
node.type === 'function_expression') {
|
|
46
|
+
const name = getComponentName(node, content);
|
|
47
|
+
if (!name || !/^[A-Z]/.test(name)) {
|
|
48
|
+
return false;
|
|
49
|
+
}
|
|
50
|
+
const nodeText = node.text;
|
|
51
|
+
return nodeText.includes('<') && (nodeText.includes('/>') || nodeText.includes('</'));
|
|
52
|
+
}
|
|
53
|
+
return false;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Get the component name from a node
|
|
57
|
+
*/
|
|
58
|
+
export function getComponentName(node, content) {
|
|
59
|
+
if (node.type === 'function_declaration') {
|
|
60
|
+
const nameNode = node.children.find(c => c.type === 'identifier');
|
|
61
|
+
return nameNode?.text;
|
|
62
|
+
}
|
|
63
|
+
const lines = content.split('\n');
|
|
64
|
+
const line = lines[node.startPosition.row];
|
|
65
|
+
if (line) {
|
|
66
|
+
const match = line.match(/(?:const|let|var|export\s+(?:const|let|var)?)\s+([A-Z][a-zA-Z0-9]*)\s*[=:]/);
|
|
67
|
+
if (match && match[1]) {
|
|
68
|
+
return match[1];
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
return undefined;
|
|
72
|
+
}
|
|
73
|
+
// ============================================================================
|
|
74
|
+
// Helper Functions - forwardRef Detection
|
|
75
|
+
// ============================================================================
|
|
76
|
+
/**
|
|
77
|
+
* Detect React.forwardRef usage in content
|
|
78
|
+
*/
|
|
79
|
+
export function detectForwardRef(content) {
|
|
80
|
+
const results = [];
|
|
81
|
+
// Pattern 1: const Component = forwardRef((props, ref) => ...)
|
|
82
|
+
const forwardRefPattern = /(?:const|let|var)\s+([A-Z][a-zA-Z0-9]*)\s*=\s*(?:React\.)?forwardRef\s*(?:<[^>]*>)?\s*\(\s*(?:\([^)]*,\s*([a-zA-Z_$][a-zA-Z0-9_$]*)\s*\)|function\s*\([^)]*,\s*([a-zA-Z_$][a-zA-Z0-9_$]*)\s*\))/g;
|
|
83
|
+
let match;
|
|
84
|
+
while ((match = forwardRefPattern.exec(content)) !== null) {
|
|
85
|
+
const beforeMatch = content.slice(0, match.index);
|
|
86
|
+
const line = beforeMatch.split('\n').length;
|
|
87
|
+
const refName = match[2] || match[3] || 'ref';
|
|
88
|
+
results.push({
|
|
89
|
+
pattern: 'forwardRef',
|
|
90
|
+
variableName: refName,
|
|
91
|
+
line,
|
|
92
|
+
column: 1,
|
|
93
|
+
forwardedToDOM: false,
|
|
94
|
+
forwardedToChild: false,
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
// Pattern 2: export default forwardRef(...)
|
|
98
|
+
const exportForwardRefPattern = /export\s+default\s+(?:React\.)?forwardRef\s*(?:<[^>]*>)?\s*\(/g;
|
|
99
|
+
while ((match = exportForwardRefPattern.exec(content)) !== null) {
|
|
100
|
+
const beforeMatch = content.slice(0, match.index);
|
|
101
|
+
const line = beforeMatch.split('\n').length;
|
|
102
|
+
results.push({
|
|
103
|
+
pattern: 'forwardRef',
|
|
104
|
+
variableName: 'ref',
|
|
105
|
+
line,
|
|
106
|
+
column: 1,
|
|
107
|
+
forwardedToDOM: false,
|
|
108
|
+
forwardedToChild: false,
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
return results;
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Check if component is wrapped with forwardRef
|
|
115
|
+
*/
|
|
116
|
+
export function isWrappedWithForwardRef(content, componentName) {
|
|
117
|
+
// Check for: const Component = forwardRef(...)
|
|
118
|
+
const pattern1 = new RegExp(`const\\s+${componentName}\\s*=\\s*(?:React\\.)?forwardRef`);
|
|
119
|
+
// Check for: export default forwardRef(Component)
|
|
120
|
+
const pattern2 = new RegExp(`export\\s+default\\s+(?:React\\.)?forwardRef\\s*\\(\\s*${componentName}`);
|
|
121
|
+
// Check for: forwardRef<...>((props, ref) => ...) assigned to component
|
|
122
|
+
const pattern3 = new RegExp(`${componentName}\\s*=\\s*(?:React\\.)?forwardRef`);
|
|
123
|
+
return pattern1.test(content) || pattern2.test(content) || pattern3.test(content);
|
|
124
|
+
}
|
|
125
|
+
// ============================================================================
|
|
126
|
+
// Helper Functions - useImperativeHandle Detection
|
|
127
|
+
// ============================================================================
|
|
128
|
+
/**
|
|
129
|
+
* Detect useImperativeHandle usage in content
|
|
130
|
+
*/
|
|
131
|
+
export function detectUseImperativeHandle(content) {
|
|
132
|
+
const results = [];
|
|
133
|
+
// Pattern: useImperativeHandle(ref, () => ({ method1, method2 }), [deps])
|
|
134
|
+
const imperativePattern = /useImperativeHandle\s*\(\s*([a-zA-Z_$][a-zA-Z0-9_$]*)\s*,\s*\(\)\s*=>\s*\(\s*\{([^}]*)\}/g;
|
|
135
|
+
let match;
|
|
136
|
+
while ((match = imperativePattern.exec(content)) !== null) {
|
|
137
|
+
const beforeMatch = content.slice(0, match.index);
|
|
138
|
+
const line = beforeMatch.split('\n').length;
|
|
139
|
+
const refName = match[1] || 'ref';
|
|
140
|
+
const methodsStr = match[2] || '';
|
|
141
|
+
// Extract method names
|
|
142
|
+
const methods = methodsStr
|
|
143
|
+
.split(',')
|
|
144
|
+
.map(m => m.trim().split(':')[0]?.trim() || '')
|
|
145
|
+
.filter(m => m && /^[a-zA-Z_$]/.test(m));
|
|
146
|
+
results.push({
|
|
147
|
+
pattern: 'useImperativeHandle',
|
|
148
|
+
variableName: refName,
|
|
149
|
+
line,
|
|
150
|
+
column: 1,
|
|
151
|
+
forwardedToDOM: false,
|
|
152
|
+
forwardedToChild: false,
|
|
153
|
+
exposedMethods: methods,
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
// Simpler pattern without extracting methods
|
|
157
|
+
const simplePattern = /useImperativeHandle\s*\(\s*([a-zA-Z_$][a-zA-Z0-9_$]*)/g;
|
|
158
|
+
while ((match = simplePattern.exec(content)) !== null) {
|
|
159
|
+
const beforeMatch = content.slice(0, match.index);
|
|
160
|
+
const line = beforeMatch.split('\n').length;
|
|
161
|
+
const refName = match[1] || 'ref';
|
|
162
|
+
// Check if we already have this one
|
|
163
|
+
if (!results.some(r => r.line === line && r.pattern === 'useImperativeHandle')) {
|
|
164
|
+
results.push({
|
|
165
|
+
pattern: 'useImperativeHandle',
|
|
166
|
+
variableName: refName,
|
|
167
|
+
line,
|
|
168
|
+
column: 1,
|
|
169
|
+
forwardedToDOM: false,
|
|
170
|
+
forwardedToChild: false,
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
return results;
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* Check if useImperativeHandle is used without forwardRef
|
|
178
|
+
*/
|
|
179
|
+
export function hasImperativeWithoutForwardRef(content) {
|
|
180
|
+
const hasImperative = /useImperativeHandle\s*\(/.test(content);
|
|
181
|
+
const hasForwardRef = /(?:React\.)?forwardRef\s*[(<]/.test(content);
|
|
182
|
+
return hasImperative && !hasForwardRef;
|
|
183
|
+
}
|
|
184
|
+
// ============================================================================
|
|
185
|
+
// Helper Functions - useRef Detection
|
|
186
|
+
// ============================================================================
|
|
187
|
+
/**
|
|
188
|
+
* Detect useRef usage in content
|
|
189
|
+
*/
|
|
190
|
+
export function detectUseRef(content) {
|
|
191
|
+
const results = [];
|
|
192
|
+
// Pattern: const ref = useRef<Type>(initial)
|
|
193
|
+
const useRefPattern = /const\s+([a-zA-Z_$][a-zA-Z0-9_$]*)\s*=\s*useRef\s*(?:<([^>]*)>)?\s*\(([^)]*)\)/g;
|
|
194
|
+
let match;
|
|
195
|
+
while ((match = useRefPattern.exec(content)) !== null) {
|
|
196
|
+
const beforeMatch = content.slice(0, match.index);
|
|
197
|
+
const line = beforeMatch.split('\n').length;
|
|
198
|
+
const refName = match[1] || 'ref';
|
|
199
|
+
const typeAnnotation = match[2] || '';
|
|
200
|
+
const initialValue = match[3] || '';
|
|
201
|
+
// Determine if this is a DOM ref or mutable ref
|
|
202
|
+
const isDOMRef = initialValue.trim() === 'null' ||
|
|
203
|
+
typeAnnotation.includes('Element') ||
|
|
204
|
+
typeAnnotation.includes('HTML');
|
|
205
|
+
// Check if ref is used on a DOM element
|
|
206
|
+
const refUsagePattern = new RegExp(`ref\\s*=\\s*\\{\\s*${refName}\\s*\\}`);
|
|
207
|
+
const forwardedToDOM = refUsagePattern.test(content);
|
|
208
|
+
results.push({
|
|
209
|
+
pattern: isDOMRef ? 'useRef-dom' : 'useRef-mutable',
|
|
210
|
+
variableName: refName,
|
|
211
|
+
line,
|
|
212
|
+
column: 1,
|
|
213
|
+
forwardedToDOM,
|
|
214
|
+
forwardedToChild: false,
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
return results;
|
|
218
|
+
}
|
|
219
|
+
/**
|
|
220
|
+
* Check if a ref is used in the component
|
|
221
|
+
*/
|
|
222
|
+
export function isRefUsed(content, refName) {
|
|
223
|
+
// Check for ref={refName} or ref.current
|
|
224
|
+
const usagePattern = new RegExp(`ref\\s*=\\s*\\{\\s*${refName}\\s*\\}|${refName}\\.current`, 'g');
|
|
225
|
+
return usagePattern.test(content);
|
|
226
|
+
}
|
|
227
|
+
// ============================================================================
|
|
228
|
+
// Helper Functions - Ref Prop Forwarding Detection
|
|
229
|
+
// ============================================================================
|
|
230
|
+
/**
|
|
231
|
+
* Detect ref prop forwarding to DOM elements
|
|
232
|
+
*/
|
|
233
|
+
export function detectRefPropToDom(content) {
|
|
234
|
+
const results = [];
|
|
235
|
+
// Pattern: <element ref={ref} /> or <element ref={props.ref} />
|
|
236
|
+
const domRefPattern = /<([a-z][a-zA-Z0-9]*)[^>]*\s+ref\s*=\s*\{([^}]+)\}/g;
|
|
237
|
+
let match;
|
|
238
|
+
while ((match = domRefPattern.exec(content)) !== null) {
|
|
239
|
+
const beforeMatch = content.slice(0, match.index);
|
|
240
|
+
const line = beforeMatch.split('\n').length;
|
|
241
|
+
const elementName = match[1] || '';
|
|
242
|
+
const refValue = match[2]?.trim() || '';
|
|
243
|
+
// Only count if it's a DOM element (lowercase)
|
|
244
|
+
if (DOM_ELEMENTS_WITH_REFS.includes(elementName.toLowerCase())) {
|
|
245
|
+
results.push({
|
|
246
|
+
pattern: 'ref-prop-to-dom',
|
|
247
|
+
variableName: refValue,
|
|
248
|
+
line,
|
|
249
|
+
column: 1,
|
|
250
|
+
forwardedToDOM: true,
|
|
251
|
+
forwardedToChild: false,
|
|
252
|
+
targetName: elementName,
|
|
253
|
+
});
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
return results;
|
|
257
|
+
}
|
|
258
|
+
/**
|
|
259
|
+
* Detect ref prop forwarding to child components
|
|
260
|
+
*/
|
|
261
|
+
export function detectRefPropToChild(content) {
|
|
262
|
+
const results = [];
|
|
263
|
+
// Pattern: <Component ref={ref} /> (PascalCase component)
|
|
264
|
+
const childRefPattern = /<([A-Z][a-zA-Z0-9]*)[^>]*\s+ref\s*=\s*\{([^}]+)\}/g;
|
|
265
|
+
let match;
|
|
266
|
+
while ((match = childRefPattern.exec(content)) !== null) {
|
|
267
|
+
const beforeMatch = content.slice(0, match.index);
|
|
268
|
+
const line = beforeMatch.split('\n').length;
|
|
269
|
+
const componentName = match[1] || '';
|
|
270
|
+
const refValue = match[2]?.trim() || '';
|
|
271
|
+
results.push({
|
|
272
|
+
pattern: 'ref-prop-to-child',
|
|
273
|
+
variableName: refValue,
|
|
274
|
+
line,
|
|
275
|
+
column: 1,
|
|
276
|
+
forwardedToDOM: false,
|
|
277
|
+
forwardedToChild: true,
|
|
278
|
+
targetName: componentName,
|
|
279
|
+
});
|
|
280
|
+
}
|
|
281
|
+
return results;
|
|
282
|
+
}
|
|
283
|
+
// ============================================================================
|
|
284
|
+
// Helper Functions - Callback Ref Detection
|
|
285
|
+
// ============================================================================
|
|
286
|
+
/**
|
|
287
|
+
* Detect callback ref patterns
|
|
288
|
+
*/
|
|
289
|
+
export function detectCallbackRef(content) {
|
|
290
|
+
const results = [];
|
|
291
|
+
// Pattern 1: ref={(el) => ...} or ref={el => ...}
|
|
292
|
+
const inlineCallbackPattern = /ref\s*=\s*\{\s*\(?([a-zA-Z_$][a-zA-Z0-9_$]*)\)?\s*=>/g;
|
|
293
|
+
let match;
|
|
294
|
+
while ((match = inlineCallbackPattern.exec(content)) !== null) {
|
|
295
|
+
const beforeMatch = content.slice(0, match.index);
|
|
296
|
+
const line = beforeMatch.split('\n').length;
|
|
297
|
+
const paramName = match[1] || 'el';
|
|
298
|
+
results.push({
|
|
299
|
+
pattern: 'callback-ref',
|
|
300
|
+
variableName: paramName,
|
|
301
|
+
line,
|
|
302
|
+
column: 1,
|
|
303
|
+
forwardedToDOM: false,
|
|
304
|
+
forwardedToChild: false,
|
|
305
|
+
});
|
|
306
|
+
}
|
|
307
|
+
// Pattern 2: ref={callbackRef} where callbackRef is a function
|
|
308
|
+
// This is harder to detect without type info, so we look for common patterns
|
|
309
|
+
const callbackRefPattern = /const\s+([a-zA-Z_$][a-zA-Z0-9_$]*)\s*=\s*(?:useCallback\s*\(\s*)?\(?([a-zA-Z_$][a-zA-Z0-9_$]*)\)?\s*=>\s*\{[^}]*\.\s*current\s*=/g;
|
|
310
|
+
while ((match = callbackRefPattern.exec(content)) !== null) {
|
|
311
|
+
const beforeMatch = content.slice(0, match.index);
|
|
312
|
+
const line = beforeMatch.split('\n').length;
|
|
313
|
+
const refName = match[1] || 'callbackRef';
|
|
314
|
+
results.push({
|
|
315
|
+
pattern: 'callback-ref',
|
|
316
|
+
variableName: refName,
|
|
317
|
+
line,
|
|
318
|
+
column: 1,
|
|
319
|
+
forwardedToDOM: false,
|
|
320
|
+
forwardedToChild: false,
|
|
321
|
+
});
|
|
322
|
+
}
|
|
323
|
+
return results;
|
|
324
|
+
}
|
|
325
|
+
// ============================================================================
|
|
326
|
+
// Helper Functions - Ref Merging Detection
|
|
327
|
+
// ============================================================================
|
|
328
|
+
/**
|
|
329
|
+
* Detect ref merging patterns (multiple refs combined)
|
|
330
|
+
*/
|
|
331
|
+
export function detectRefMerging(content) {
|
|
332
|
+
const results = [];
|
|
333
|
+
// Pattern 1: useMergeRefs or mergeRefs utility
|
|
334
|
+
const mergeRefsPattern = /(?:useMergeRefs|mergeRefs)\s*\(\s*\[([^\]]+)\]/g;
|
|
335
|
+
let match;
|
|
336
|
+
while ((match = mergeRefsPattern.exec(content)) !== null) {
|
|
337
|
+
const beforeMatch = content.slice(0, match.index);
|
|
338
|
+
const line = beforeMatch.split('\n').length;
|
|
339
|
+
const refsStr = match[1] || '';
|
|
340
|
+
const refs = refsStr.split(',').map(r => r.trim()).filter(r => r);
|
|
341
|
+
results.push({
|
|
342
|
+
pattern: 'ref-merging',
|
|
343
|
+
variableName: refs.join(', '),
|
|
344
|
+
line,
|
|
345
|
+
column: 1,
|
|
346
|
+
forwardedToDOM: false,
|
|
347
|
+
forwardedToChild: false,
|
|
348
|
+
});
|
|
349
|
+
}
|
|
350
|
+
// Pattern 2: Callback ref that assigns to multiple refs
|
|
351
|
+
const multiAssignPattern = /ref\s*=\s*\{\s*\(?([a-zA-Z_$][a-zA-Z0-9_$]*)\)?\s*=>\s*\{[^}]*\.current\s*=[^}]*\.current\s*=/g;
|
|
352
|
+
while ((match = multiAssignPattern.exec(content)) !== null) {
|
|
353
|
+
const beforeMatch = content.slice(0, match.index);
|
|
354
|
+
const line = beforeMatch.split('\n').length;
|
|
355
|
+
results.push({
|
|
356
|
+
pattern: 'ref-merging',
|
|
357
|
+
variableName: 'merged',
|
|
358
|
+
line,
|
|
359
|
+
column: 1,
|
|
360
|
+
forwardedToDOM: false,
|
|
361
|
+
forwardedToChild: false,
|
|
362
|
+
});
|
|
363
|
+
}
|
|
364
|
+
// Pattern 3: composeRefs utility
|
|
365
|
+
const composeRefsPattern = /composeRefs\s*\(/g;
|
|
366
|
+
while ((match = composeRefsPattern.exec(content)) !== null) {
|
|
367
|
+
const beforeMatch = content.slice(0, match.index);
|
|
368
|
+
const line = beforeMatch.split('\n').length;
|
|
369
|
+
results.push({
|
|
370
|
+
pattern: 'ref-merging',
|
|
371
|
+
variableName: 'composed',
|
|
372
|
+
line,
|
|
373
|
+
column: 1,
|
|
374
|
+
forwardedToDOM: false,
|
|
375
|
+
forwardedToChild: false,
|
|
376
|
+
});
|
|
377
|
+
}
|
|
378
|
+
return results;
|
|
379
|
+
}
|
|
380
|
+
// ============================================================================
|
|
381
|
+
// Helper Functions - Issue Detection
|
|
382
|
+
// ============================================================================
|
|
383
|
+
/**
|
|
384
|
+
* Check if component accepts ref prop but doesn't use forwardRef
|
|
385
|
+
*/
|
|
386
|
+
export function detectMissingForwardRef(content) {
|
|
387
|
+
// Check if component has ref in props
|
|
388
|
+
const hasRefProp = /\(\s*\{[^}]*\bref\b[^}]*\}/.test(content) ||
|
|
389
|
+
/props\.ref/.test(content);
|
|
390
|
+
// Check if wrapped with forwardRef
|
|
391
|
+
const hasForwardRef = /(?:React\.)?forwardRef\s*[(<]/.test(content);
|
|
392
|
+
return hasRefProp && !hasForwardRef;
|
|
393
|
+
}
|
|
394
|
+
/**
|
|
395
|
+
* Detect ref forwarding issues in a component
|
|
396
|
+
*/
|
|
397
|
+
export function detectRefIssues(content, refUsages, config) {
|
|
398
|
+
const issues = [];
|
|
399
|
+
// Check for useImperativeHandle without forwardRef
|
|
400
|
+
if (hasImperativeWithoutForwardRef(content)) {
|
|
401
|
+
const imperativeUsage = refUsages.find(r => r.pattern === 'useImperativeHandle');
|
|
402
|
+
issues.push({
|
|
403
|
+
type: 'imperative-without-forwardRef',
|
|
404
|
+
description: 'useImperativeHandle is used without forwardRef wrapper',
|
|
405
|
+
severity: 'error',
|
|
406
|
+
suggestion: 'Wrap the component with React.forwardRef to properly expose imperative methods',
|
|
407
|
+
line: imperativeUsage?.line || 1,
|
|
408
|
+
column: 1,
|
|
409
|
+
});
|
|
410
|
+
}
|
|
411
|
+
// Check for excessive imperative handle methods
|
|
412
|
+
for (const usage of refUsages) {
|
|
413
|
+
if (usage.pattern === 'useImperativeHandle' && usage.exposedMethods) {
|
|
414
|
+
if (usage.exposedMethods.length > config.maxImperativeHandleMethods) {
|
|
415
|
+
issues.push({
|
|
416
|
+
type: 'excessive-imperative-handle',
|
|
417
|
+
description: `useImperativeHandle exposes ${usage.exposedMethods.length} methods (max: ${config.maxImperativeHandleMethods})`,
|
|
418
|
+
severity: 'warning',
|
|
419
|
+
suggestion: 'Consider reducing the number of exposed methods or using a different pattern',
|
|
420
|
+
line: usage.line,
|
|
421
|
+
column: 1,
|
|
422
|
+
});
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
// Check for missing forwardRef
|
|
427
|
+
if (config.flagMissingForwardRef && detectMissingForwardRef(content)) {
|
|
428
|
+
issues.push({
|
|
429
|
+
type: 'missing-forwardRef',
|
|
430
|
+
description: 'Component accepts ref prop but is not wrapped with forwardRef',
|
|
431
|
+
severity: 'warning',
|
|
432
|
+
suggestion: 'Wrap the component with React.forwardRef to properly forward refs',
|
|
433
|
+
line: 1,
|
|
434
|
+
column: 1,
|
|
435
|
+
});
|
|
436
|
+
}
|
|
437
|
+
// Check for unused refs
|
|
438
|
+
if (config.flagUnusedRefs) {
|
|
439
|
+
for (const usage of refUsages) {
|
|
440
|
+
if ((usage.pattern === 'useRef-dom' || usage.pattern === 'useRef-mutable') &&
|
|
441
|
+
!isRefUsed(content, usage.variableName)) {
|
|
442
|
+
issues.push({
|
|
443
|
+
type: 'unused-ref',
|
|
444
|
+
description: `Ref '${usage.variableName}' is declared but never used`,
|
|
445
|
+
severity: 'info',
|
|
446
|
+
suggestion: 'Remove the unused ref or use it in the component',
|
|
447
|
+
line: usage.line,
|
|
448
|
+
column: 1,
|
|
449
|
+
});
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
return issues;
|
|
454
|
+
}
|
|
455
|
+
// ============================================================================
|
|
456
|
+
// Helper Functions - Analysis
|
|
457
|
+
// ============================================================================
|
|
458
|
+
/**
|
|
459
|
+
* Collect all ref usages from content
|
|
460
|
+
*/
|
|
461
|
+
export function collectRefUsages(content) {
|
|
462
|
+
return [
|
|
463
|
+
...detectForwardRef(content),
|
|
464
|
+
...detectUseImperativeHandle(content),
|
|
465
|
+
...detectUseRef(content),
|
|
466
|
+
...detectRefPropToDom(content),
|
|
467
|
+
...detectRefPropToChild(content),
|
|
468
|
+
...detectCallbackRef(content),
|
|
469
|
+
...detectRefMerging(content),
|
|
470
|
+
];
|
|
471
|
+
}
|
|
472
|
+
/**
|
|
473
|
+
* Analyze a single component's ref patterns
|
|
474
|
+
*/
|
|
475
|
+
export function analyzeComponentRefs(nodeText, content, filePath, componentName, line, config) {
|
|
476
|
+
const refUsages = collectRefUsages(nodeText);
|
|
477
|
+
const issues = detectRefIssues(nodeText, refUsages, config);
|
|
478
|
+
const usesForwardRef = refUsages.some(r => r.pattern === 'forwardRef');
|
|
479
|
+
const usesImperativeHandle = refUsages.some(r => r.pattern === 'useImperativeHandle');
|
|
480
|
+
const acceptsRefProp = /\(\s*\{[^}]*\bref\b[^}]*\}/.test(nodeText) ||
|
|
481
|
+
/props\.ref/.test(nodeText) ||
|
|
482
|
+
/,\s*ref\s*\)/.test(nodeText);
|
|
483
|
+
return {
|
|
484
|
+
componentName,
|
|
485
|
+
filePath,
|
|
486
|
+
line,
|
|
487
|
+
column: 1,
|
|
488
|
+
usesForwardRef,
|
|
489
|
+
usesImperativeHandle,
|
|
490
|
+
refUsages,
|
|
491
|
+
issues,
|
|
492
|
+
acceptsRefProp,
|
|
493
|
+
isWrappedWithForwardRef: isWrappedWithForwardRef(content, componentName),
|
|
494
|
+
};
|
|
495
|
+
}
|
|
496
|
+
/**
|
|
497
|
+
* Find dominant pattern from a list
|
|
498
|
+
*/
|
|
499
|
+
function findDominantPattern(patterns) {
|
|
500
|
+
const counts = new Map();
|
|
501
|
+
for (const pattern of patterns) {
|
|
502
|
+
if (pattern !== 'none') {
|
|
503
|
+
counts.set(pattern, (counts.get(pattern) || 0) + 1);
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
let dominant = 'none';
|
|
507
|
+
let maxCount = 0;
|
|
508
|
+
for (const [pattern, count] of counts) {
|
|
509
|
+
if (count > maxCount) {
|
|
510
|
+
maxCount = count;
|
|
511
|
+
dominant = pattern;
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
const total = patterns.filter(p => p !== 'none').length;
|
|
515
|
+
const confidence = total > 0 ? maxCount / total : 0;
|
|
516
|
+
return { dominant, confidence };
|
|
517
|
+
}
|
|
518
|
+
/**
|
|
519
|
+
* Analyze ref forwarding patterns across multiple components
|
|
520
|
+
*/
|
|
521
|
+
export function analyzeRefForwardingPatterns(components) {
|
|
522
|
+
if (components.length === 0) {
|
|
523
|
+
return {
|
|
524
|
+
components: [],
|
|
525
|
+
dominantPattern: 'none',
|
|
526
|
+
patternCounts: {
|
|
527
|
+
'forwardRef': 0,
|
|
528
|
+
'useImperativeHandle': 0,
|
|
529
|
+
'ref-prop-to-dom': 0,
|
|
530
|
+
'ref-prop-to-child': 0,
|
|
531
|
+
'callback-ref': 0,
|
|
532
|
+
'useRef-dom': 0,
|
|
533
|
+
'useRef-mutable': 0,
|
|
534
|
+
'ref-merging': 0,
|
|
535
|
+
'none': 0,
|
|
536
|
+
},
|
|
537
|
+
confidence: 0,
|
|
538
|
+
componentsWithIssues: [],
|
|
539
|
+
healthScore: 1.0,
|
|
540
|
+
};
|
|
541
|
+
}
|
|
542
|
+
// Collect all patterns
|
|
543
|
+
const allPatterns = [];
|
|
544
|
+
const patternCounts = {
|
|
545
|
+
'forwardRef': 0,
|
|
546
|
+
'useImperativeHandle': 0,
|
|
547
|
+
'ref-prop-to-dom': 0,
|
|
548
|
+
'ref-prop-to-child': 0,
|
|
549
|
+
'callback-ref': 0,
|
|
550
|
+
'useRef-dom': 0,
|
|
551
|
+
'useRef-mutable': 0,
|
|
552
|
+
'ref-merging': 0,
|
|
553
|
+
'none': 0,
|
|
554
|
+
};
|
|
555
|
+
for (const comp of components) {
|
|
556
|
+
for (const usage of comp.refUsages) {
|
|
557
|
+
allPatterns.push(usage.pattern);
|
|
558
|
+
patternCounts[usage.pattern]++;
|
|
559
|
+
}
|
|
560
|
+
if (comp.refUsages.length === 0) {
|
|
561
|
+
patternCounts['none']++;
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
// Find dominant pattern
|
|
565
|
+
const { dominant, confidence } = findDominantPattern(allPatterns);
|
|
566
|
+
// Find components with issues
|
|
567
|
+
const componentsWithIssues = components.filter(c => c.issues.length > 0);
|
|
568
|
+
// Calculate health score
|
|
569
|
+
let healthScore = 1.0;
|
|
570
|
+
const totalIssues = components.reduce((sum, c) => sum + c.issues.length, 0);
|
|
571
|
+
if (totalIssues > 0) {
|
|
572
|
+
healthScore = Math.max(0, 1 - (totalIssues * 0.1));
|
|
573
|
+
}
|
|
574
|
+
return {
|
|
575
|
+
components,
|
|
576
|
+
dominantPattern: dominant,
|
|
577
|
+
patternCounts,
|
|
578
|
+
confidence,
|
|
579
|
+
componentsWithIssues,
|
|
580
|
+
healthScore,
|
|
581
|
+
};
|
|
582
|
+
}
|
|
583
|
+
// ============================================================================
|
|
584
|
+
// Pattern Description Helpers
|
|
585
|
+
// ============================================================================
|
|
586
|
+
const PATTERN_DESCRIPTIONS = {
|
|
587
|
+
'forwardRef': 'React.forwardRef wrapper',
|
|
588
|
+
'useImperativeHandle': 'useImperativeHandle hook',
|
|
589
|
+
'ref-prop-to-dom': 'ref forwarded to DOM element',
|
|
590
|
+
'ref-prop-to-child': 'ref forwarded to child component',
|
|
591
|
+
'callback-ref': 'callback ref pattern',
|
|
592
|
+
'useRef-dom': 'useRef for DOM access',
|
|
593
|
+
'useRef-mutable': 'useRef for mutable values',
|
|
594
|
+
'ref-merging': 'multiple refs merged',
|
|
595
|
+
'none': 'no ref pattern',
|
|
596
|
+
};
|
|
597
|
+
const ISSUE_DESCRIPTIONS = {
|
|
598
|
+
'missing-forwardRef': 'missing forwardRef wrapper',
|
|
599
|
+
'incorrect-ref-typing': 'incorrect ref typing',
|
|
600
|
+
'ref-not-forwarded': 'ref not forwarded to element',
|
|
601
|
+
'imperative-without-forwardRef': 'useImperativeHandle without forwardRef',
|
|
602
|
+
'excessive-imperative-handle': 'too many methods exposed via useImperativeHandle',
|
|
603
|
+
'unused-ref': 'unused ref declaration',
|
|
604
|
+
'ref-in-render': 'ref accessed during render',
|
|
605
|
+
};
|
|
606
|
+
/**
|
|
607
|
+
* Get human-readable description for a pattern
|
|
608
|
+
*/
|
|
609
|
+
export function getPatternDescription(pattern) {
|
|
610
|
+
return PATTERN_DESCRIPTIONS[pattern] || pattern;
|
|
611
|
+
}
|
|
612
|
+
/**
|
|
613
|
+
* Get human-readable description for an issue
|
|
614
|
+
*/
|
|
615
|
+
export function getIssueDescription(issue) {
|
|
616
|
+
return ISSUE_DESCRIPTIONS[issue] || issue;
|
|
617
|
+
}
|
|
618
|
+
// ============================================================================
|
|
619
|
+
// Ref Forwarding Detector Class
|
|
620
|
+
// ============================================================================
|
|
621
|
+
/**
|
|
622
|
+
* Detector for ref forwarding patterns
|
|
623
|
+
*
|
|
624
|
+
* Identifies ref forwarding patterns including forwardRef, useImperativeHandle,
|
|
625
|
+
* ref prop forwarding, callback refs, and ref merging. Reports issues when
|
|
626
|
+
* components don't follow best practices.
|
|
627
|
+
*
|
|
628
|
+
* @requirements 8.7 - THE Component_Detector SHALL detect ref forwarding patterns
|
|
629
|
+
*/
|
|
630
|
+
export class RefForwardingDetector extends ASTDetector {
|
|
631
|
+
id = 'components/ref-forwarding';
|
|
632
|
+
category = 'components';
|
|
633
|
+
subcategory = 'ref-forwarding';
|
|
634
|
+
name = 'Ref Forwarding Detector';
|
|
635
|
+
description = 'Detects ref forwarding patterns and identifies issues with ref handling';
|
|
636
|
+
supportedLanguages = ['typescript', 'javascript'];
|
|
637
|
+
config;
|
|
638
|
+
constructor(config = {}) {
|
|
639
|
+
super();
|
|
640
|
+
this.config = { ...DEFAULT_REF_FORWARDING_CONFIG, ...config };
|
|
641
|
+
}
|
|
642
|
+
/**
|
|
643
|
+
* Detect ref forwarding patterns in the project
|
|
644
|
+
*/
|
|
645
|
+
async detect(context) {
|
|
646
|
+
const patterns = [];
|
|
647
|
+
const violations = [];
|
|
648
|
+
// Find all components in the file
|
|
649
|
+
const componentInfos = this.findComponentsInFile(context);
|
|
650
|
+
if (componentInfos.length === 0) {
|
|
651
|
+
return this.createEmptyResult();
|
|
652
|
+
}
|
|
653
|
+
// Analyze patterns across all components
|
|
654
|
+
const analysis = analyzeRefForwardingPatterns(componentInfos);
|
|
655
|
+
// Create pattern matches for detected patterns
|
|
656
|
+
for (const [pattern, count] of Object.entries(analysis.patternCounts)) {
|
|
657
|
+
if (count > 0 && pattern !== 'none') {
|
|
658
|
+
patterns.push(this.createPatternMatch(context.file, pattern, count, analysis));
|
|
659
|
+
}
|
|
660
|
+
}
|
|
661
|
+
// Generate violations for components with issues in current file
|
|
662
|
+
for (const comp of analysis.componentsWithIssues) {
|
|
663
|
+
if (comp.filePath === context.file) {
|
|
664
|
+
for (const issue of comp.issues) {
|
|
665
|
+
violations.push(this.createViolation(context.file, comp, issue));
|
|
666
|
+
}
|
|
667
|
+
}
|
|
668
|
+
}
|
|
669
|
+
return this.createResult(patterns, violations, analysis.confidence);
|
|
670
|
+
}
|
|
671
|
+
/**
|
|
672
|
+
* Find all React components in a file
|
|
673
|
+
*/
|
|
674
|
+
findComponentsInFile(context) {
|
|
675
|
+
const components = [];
|
|
676
|
+
if (context.ast) {
|
|
677
|
+
// Use AST to find components
|
|
678
|
+
const functionNodes = this.findNodesByTypes(context.ast, [
|
|
679
|
+
'function_declaration',
|
|
680
|
+
'arrow_function',
|
|
681
|
+
'function_expression',
|
|
682
|
+
]);
|
|
683
|
+
for (const node of functionNodes) {
|
|
684
|
+
if (isReactComponent(node, context.content)) {
|
|
685
|
+
const componentName = getComponentName(node, context.content);
|
|
686
|
+
if (componentName) {
|
|
687
|
+
const info = analyzeComponentRefs(node.text, context.content, context.file, componentName, node.startPosition.row + 1, this.config);
|
|
688
|
+
components.push(info);
|
|
689
|
+
}
|
|
690
|
+
}
|
|
691
|
+
}
|
|
692
|
+
}
|
|
693
|
+
else {
|
|
694
|
+
// Fallback: use regex-based detection
|
|
695
|
+
const componentMatches = this.findComponentsWithRegex(context.content, context.file);
|
|
696
|
+
components.push(...componentMatches);
|
|
697
|
+
}
|
|
698
|
+
return components;
|
|
699
|
+
}
|
|
700
|
+
/**
|
|
701
|
+
* Find components using regex (fallback when AST is not available)
|
|
702
|
+
*/
|
|
703
|
+
findComponentsWithRegex(content, filePath) {
|
|
704
|
+
const components = [];
|
|
705
|
+
const seenComponents = new Set();
|
|
706
|
+
// Arrow function: const Button = (...) => ...
|
|
707
|
+
const arrowPattern = /(?:export\s+)?const\s+([A-Z][a-zA-Z0-9]*)\s*(?::\s*[^=]+)?\s*=\s*(?:React\.)?(?:forwardRef\s*(?:<[^>]*>)?\s*\()?\s*\(/g;
|
|
708
|
+
// Function declaration: function Button(...) { ... }
|
|
709
|
+
const functionPattern = /(?:export\s+)?function\s+([A-Z][a-zA-Z0-9]*)\s*\(/g;
|
|
710
|
+
const processMatch = (match, _pattern) => {
|
|
711
|
+
const componentName = match[1];
|
|
712
|
+
if (!componentName || seenComponents.has(componentName))
|
|
713
|
+
return;
|
|
714
|
+
const beforeMatch = content.slice(0, match.index);
|
|
715
|
+
const lineNumber = beforeMatch.split('\n').length;
|
|
716
|
+
// Get component body (simplified)
|
|
717
|
+
const startIndex = match.index;
|
|
718
|
+
const endIndex = Math.min(startIndex + 5000, content.length);
|
|
719
|
+
const componentText = content.slice(startIndex, endIndex);
|
|
720
|
+
// Check if it looks like a React component
|
|
721
|
+
if (componentText.includes('<') && (componentText.includes('/>') || componentText.includes('</'))) {
|
|
722
|
+
seenComponents.add(componentName);
|
|
723
|
+
const info = analyzeComponentRefs(componentText, content, filePath, componentName, lineNumber, this.config);
|
|
724
|
+
components.push(info);
|
|
725
|
+
}
|
|
726
|
+
};
|
|
727
|
+
let match;
|
|
728
|
+
while ((match = arrowPattern.exec(content)) !== null) {
|
|
729
|
+
processMatch(match, arrowPattern);
|
|
730
|
+
}
|
|
731
|
+
while ((match = functionPattern.exec(content)) !== null) {
|
|
732
|
+
processMatch(match, functionPattern);
|
|
733
|
+
}
|
|
734
|
+
return components;
|
|
735
|
+
}
|
|
736
|
+
/**
|
|
737
|
+
* Create a pattern match for a ref pattern
|
|
738
|
+
*/
|
|
739
|
+
createPatternMatch(file, pattern, count, analysis) {
|
|
740
|
+
const total = Object.values(analysis.patternCounts).reduce((a, b) => a + b, 0) - analysis.patternCounts['none'];
|
|
741
|
+
const confidence = total > 0 ? count / total : 0;
|
|
742
|
+
return {
|
|
743
|
+
patternId: `ref-forwarding-${pattern}`,
|
|
744
|
+
location: { file, line: 1, column: 1 },
|
|
745
|
+
confidence,
|
|
746
|
+
isOutlier: confidence < 0.2,
|
|
747
|
+
};
|
|
748
|
+
}
|
|
749
|
+
/**
|
|
750
|
+
* Create a violation for a ref issue
|
|
751
|
+
*/
|
|
752
|
+
createViolation(file, component, issue) {
|
|
753
|
+
const range = {
|
|
754
|
+
start: { line: issue.line, character: issue.column },
|
|
755
|
+
end: { line: issue.line, character: issue.column + 10 },
|
|
756
|
+
};
|
|
757
|
+
const quickFix = this.generateQuickFixForIssue(issue, component);
|
|
758
|
+
const violation = {
|
|
759
|
+
id: `ref-forwarding-${component.componentName}-${issue.type}-${file.replace(/[^a-zA-Z0-9]/g, '-')}`,
|
|
760
|
+
patternId: 'components/ref-forwarding',
|
|
761
|
+
severity: issue.severity,
|
|
762
|
+
file,
|
|
763
|
+
range,
|
|
764
|
+
message: `${component.componentName}: ${issue.description}`,
|
|
765
|
+
expected: 'Proper ref forwarding pattern',
|
|
766
|
+
actual: getIssueDescription(issue.type),
|
|
767
|
+
aiExplainAvailable: true,
|
|
768
|
+
aiFixAvailable: true,
|
|
769
|
+
firstSeen: new Date(),
|
|
770
|
+
occurrences: 1,
|
|
771
|
+
};
|
|
772
|
+
if (quickFix) {
|
|
773
|
+
violation.quickFix = quickFix;
|
|
774
|
+
}
|
|
775
|
+
return violation;
|
|
776
|
+
}
|
|
777
|
+
/**
|
|
778
|
+
* Generate a quick fix for a ref issue
|
|
779
|
+
*/
|
|
780
|
+
generateQuickFixForIssue(issue, component) {
|
|
781
|
+
switch (issue.type) {
|
|
782
|
+
case 'missing-forwardRef':
|
|
783
|
+
return {
|
|
784
|
+
title: `Wrap ${component.componentName} with forwardRef`,
|
|
785
|
+
kind: 'quickfix',
|
|
786
|
+
edit: { changes: {} },
|
|
787
|
+
isPreferred: true,
|
|
788
|
+
confidence: 0.8,
|
|
789
|
+
preview: `const ${component.componentName} = forwardRef((props, ref) => { ... })`,
|
|
790
|
+
};
|
|
791
|
+
case 'imperative-without-forwardRef':
|
|
792
|
+
return {
|
|
793
|
+
title: `Add forwardRef wrapper to ${component.componentName}`,
|
|
794
|
+
kind: 'quickfix',
|
|
795
|
+
edit: { changes: {} },
|
|
796
|
+
isPreferred: true,
|
|
797
|
+
confidence: 0.9,
|
|
798
|
+
preview: `const ${component.componentName} = forwardRef((props, ref) => { useImperativeHandle(ref, ...) })`,
|
|
799
|
+
};
|
|
800
|
+
case 'unused-ref':
|
|
801
|
+
return {
|
|
802
|
+
title: 'Remove unused ref',
|
|
803
|
+
kind: 'quickfix',
|
|
804
|
+
edit: { changes: {} },
|
|
805
|
+
isPreferred: false,
|
|
806
|
+
confidence: 0.7,
|
|
807
|
+
preview: 'Remove the unused useRef declaration',
|
|
808
|
+
};
|
|
809
|
+
default:
|
|
810
|
+
return undefined;
|
|
811
|
+
}
|
|
812
|
+
}
|
|
813
|
+
/**
|
|
814
|
+
* Generate a quick fix for a violation
|
|
815
|
+
*/
|
|
816
|
+
generateQuickFix(violation) {
|
|
817
|
+
if (violation.quickFix) {
|
|
818
|
+
return violation.quickFix;
|
|
819
|
+
}
|
|
820
|
+
return null;
|
|
821
|
+
}
|
|
822
|
+
}
|
|
823
|
+
// ============================================================================
|
|
824
|
+
// Factory Function
|
|
825
|
+
// ============================================================================
|
|
826
|
+
/**
|
|
827
|
+
* Create a new RefForwardingDetector instance
|
|
828
|
+
*/
|
|
829
|
+
export function createRefForwardingDetector(config) {
|
|
830
|
+
return new RefForwardingDetector(config);
|
|
831
|
+
}
|
|
832
|
+
//# sourceMappingURL=ref-forwarding.js.map
|