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,888 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Responsive Detector - Breakpoint usage pattern detection
|
|
3
|
+
*
|
|
4
|
+
* Detects responsive breakpoint patterns including:
|
|
5
|
+
* - Tailwind responsive prefixes (sm:, md:, lg:, xl:, 2xl:)
|
|
6
|
+
* - CSS media queries (@media (min-width: ...), @media (max-width: ...))
|
|
7
|
+
* - CSS container queries (@container)
|
|
8
|
+
* - Theme breakpoint usage (theme.breakpoints.*, theme.screens.*)
|
|
9
|
+
* - CSS custom property breakpoints (var(--breakpoint-*))
|
|
10
|
+
* - Mobile-first vs desktop-first approaches
|
|
11
|
+
*
|
|
12
|
+
* Flags inconsistent responsive usage:
|
|
13
|
+
* - Inconsistent breakpoint ordering (e.g., lg: before md:)
|
|
14
|
+
* - Mixing mobile-first and desktop-first approaches
|
|
15
|
+
* - Arbitrary breakpoint values not matching the design system
|
|
16
|
+
* - Missing responsive variants for key breakpoints
|
|
17
|
+
* - Hardcoded pixel values in media queries instead of using design tokens
|
|
18
|
+
*
|
|
19
|
+
* @requirements 9.8 - THE Styling_Detector SHALL detect responsive breakpoint usage patterns
|
|
20
|
+
*/
|
|
21
|
+
import { RegexDetector } from '../base/index.js';
|
|
22
|
+
// ============================================================================
|
|
23
|
+
// Constants
|
|
24
|
+
// ============================================================================
|
|
25
|
+
/**
|
|
26
|
+
* Standard Tailwind breakpoints (in pixels)
|
|
27
|
+
*/
|
|
28
|
+
export const TAILWIND_BREAKPOINTS = {
|
|
29
|
+
sm: 640,
|
|
30
|
+
md: 768,
|
|
31
|
+
lg: 1024,
|
|
32
|
+
xl: 1280,
|
|
33
|
+
'2xl': 1536,
|
|
34
|
+
};
|
|
35
|
+
/**
|
|
36
|
+
* Tailwind breakpoint order (mobile-first)
|
|
37
|
+
*/
|
|
38
|
+
export const TAILWIND_BREAKPOINT_ORDER = ['sm', 'md', 'lg', 'xl', '2xl'];
|
|
39
|
+
/**
|
|
40
|
+
* Common design system breakpoints (in pixels)
|
|
41
|
+
*/
|
|
42
|
+
export const COMMON_BREAKPOINTS = {
|
|
43
|
+
xs: 320,
|
|
44
|
+
sm: 640,
|
|
45
|
+
md: 768,
|
|
46
|
+
lg: 1024,
|
|
47
|
+
xl: 1280,
|
|
48
|
+
'2xl': 1536,
|
|
49
|
+
'3xl': 1920,
|
|
50
|
+
};
|
|
51
|
+
/**
|
|
52
|
+
* Tailwind responsive prefix pattern
|
|
53
|
+
* Matches: sm:flex, md:hidden, lg:grid-cols-3, xl:p-4, 2xl:text-lg
|
|
54
|
+
*/
|
|
55
|
+
export const TAILWIND_RESPONSIVE_PATTERN = /\b(sm|md|lg|xl|2xl):([a-z][a-z0-9-]*(?:-[a-z0-9]+)*(?:\[[^\]]+\])?)/gi;
|
|
56
|
+
/**
|
|
57
|
+
* CSS media query pattern (min-width - mobile-first)
|
|
58
|
+
* Matches: @media (min-width: 768px), @media screen and (min-width: 1024px)
|
|
59
|
+
*/
|
|
60
|
+
export const CSS_MEDIA_QUERY_MIN_WIDTH_PATTERN = /@media\s+(?:screen\s+and\s+)?\(\s*min-width\s*:\s*(\d+(?:\.\d+)?)(px|em|rem)\s*\)/gi;
|
|
61
|
+
/**
|
|
62
|
+
* CSS media query pattern (max-width - desktop-first)
|
|
63
|
+
* Matches: @media (max-width: 768px), @media screen and (max-width: 1024px)
|
|
64
|
+
*/
|
|
65
|
+
export const CSS_MEDIA_QUERY_MAX_WIDTH_PATTERN = /@media\s+(?:screen\s+and\s+)?\(\s*max-width\s*:\s*(\d+(?:\.\d+)?)(px|em|rem)\s*\)/gi;
|
|
66
|
+
/**
|
|
67
|
+
* CSS container query pattern
|
|
68
|
+
* Matches: @container (min-width: 400px), @container sidebar (min-width: 300px)
|
|
69
|
+
*/
|
|
70
|
+
export const CSS_CONTAINER_QUERY_PATTERN = /@container\s*(?:\w+\s*)?\(\s*(?:min|max)-width\s*:\s*(\d+(?:\.\d+)?)(px|em|rem)\s*\)/gi;
|
|
71
|
+
/**
|
|
72
|
+
* Theme breakpoint usage patterns
|
|
73
|
+
*/
|
|
74
|
+
export const THEME_BREAKPOINT_PATTERNS = [
|
|
75
|
+
// theme.breakpoints.*, theme.screens.*
|
|
76
|
+
/theme\.(?:breakpoints|screens)\.([a-zA-Z0-9_]+)/g,
|
|
77
|
+
// ${theme.breakpoints.*} in template literals
|
|
78
|
+
/\$\{theme\.(?:breakpoints|screens)\.([a-zA-Z0-9_]+)\}/g,
|
|
79
|
+
// props.theme.breakpoints.*
|
|
80
|
+
/props\.theme\.(?:breakpoints|screens)\.([a-zA-Z0-9_]+)/g,
|
|
81
|
+
];
|
|
82
|
+
/**
|
|
83
|
+
* CSS custom property breakpoint patterns
|
|
84
|
+
* Matches: var(--breakpoint-sm), var(--screen-md)
|
|
85
|
+
*/
|
|
86
|
+
export const CSS_BREAKPOINT_PROPERTY_PATTERN = /var\(\s*--(?:breakpoint|screen|bp)[-_]?([a-zA-Z0-9_-]*)\s*(?:,\s*[^)]+)?\)/gi;
|
|
87
|
+
/**
|
|
88
|
+
* Hardcoded media query pixel values (arbitrary values)
|
|
89
|
+
*/
|
|
90
|
+
export const HARDCODED_MEDIA_QUERY_PATTERN = /@media\s+(?:screen\s+and\s+)?\(\s*(?:min|max)-width\s*:\s*(\d+(?:\.\d+)?)(px)\s*\)/gi;
|
|
91
|
+
/**
|
|
92
|
+
* File patterns to exclude from responsive detection
|
|
93
|
+
*/
|
|
94
|
+
export const EXCLUDED_FILE_PATTERNS = [
|
|
95
|
+
/\.test\.[jt]sx?$/,
|
|
96
|
+
/\.spec\.[jt]sx?$/,
|
|
97
|
+
/\.stories\.[jt]sx?$/,
|
|
98
|
+
/tailwind\.config\.[jt]s$/,
|
|
99
|
+
/postcss\.config\.[jt]s$/,
|
|
100
|
+
];
|
|
101
|
+
/**
|
|
102
|
+
* Allowed arbitrary breakpoint values (common exceptions)
|
|
103
|
+
*/
|
|
104
|
+
export const ALLOWED_BREAKPOINT_VALUES = new Set([
|
|
105
|
+
320, 375, 414, 480, // Common mobile sizes
|
|
106
|
+
640, 768, 1024, 1280, 1536, 1920, // Standard breakpoints
|
|
107
|
+
]);
|
|
108
|
+
// ============================================================================
|
|
109
|
+
// Helper Functions
|
|
110
|
+
// ============================================================================
|
|
111
|
+
/**
|
|
112
|
+
* Check if a file should be excluded from responsive detection
|
|
113
|
+
*/
|
|
114
|
+
export function shouldExcludeFile(filePath) {
|
|
115
|
+
return EXCLUDED_FILE_PATTERNS.some(pattern => pattern.test(filePath));
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Check if a position is inside a comment
|
|
119
|
+
*/
|
|
120
|
+
function isInsideComment(content, index) {
|
|
121
|
+
const beforeIndex = content.slice(0, index);
|
|
122
|
+
// Check for single-line comment
|
|
123
|
+
const lastNewline = beforeIndex.lastIndexOf('\n');
|
|
124
|
+
const currentLine = beforeIndex.slice(lastNewline + 1);
|
|
125
|
+
if (currentLine.includes('//')) {
|
|
126
|
+
const commentStart = currentLine.indexOf('//');
|
|
127
|
+
const positionInLine = index - lastNewline - 1;
|
|
128
|
+
if (positionInLine > commentStart) {
|
|
129
|
+
return true;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
// Check for multi-line comment
|
|
133
|
+
const lastBlockCommentStart = beforeIndex.lastIndexOf('/*');
|
|
134
|
+
const lastBlockCommentEnd = beforeIndex.lastIndexOf('*/');
|
|
135
|
+
if (lastBlockCommentStart > lastBlockCommentEnd) {
|
|
136
|
+
return true;
|
|
137
|
+
}
|
|
138
|
+
return false;
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Check if a breakpoint value is in the standard set
|
|
142
|
+
*/
|
|
143
|
+
export function isStandardBreakpoint(value) {
|
|
144
|
+
return Object.values(TAILWIND_BREAKPOINTS).includes(value);
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Check if a breakpoint value is allowed (standard or common exception)
|
|
148
|
+
*/
|
|
149
|
+
export function isAllowedBreakpointValue(value) {
|
|
150
|
+
return ALLOWED_BREAKPOINT_VALUES.has(value) || isStandardBreakpoint(value);
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Get the breakpoint order index for a Tailwind breakpoint
|
|
154
|
+
*/
|
|
155
|
+
export function getBreakpointOrderIndex(breakpoint) {
|
|
156
|
+
return TAILWIND_BREAKPOINT_ORDER.indexOf(breakpoint);
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* Find the nearest standard breakpoint value
|
|
160
|
+
*/
|
|
161
|
+
export function findNearestBreakpoint(value) {
|
|
162
|
+
const breakpoints = Object.entries(TAILWIND_BREAKPOINTS);
|
|
163
|
+
let nearest = breakpoints[0];
|
|
164
|
+
let minDiff = Math.abs(value - nearest[1]);
|
|
165
|
+
for (const [name, bpValue] of breakpoints) {
|
|
166
|
+
const diff = Math.abs(value - bpValue);
|
|
167
|
+
if (diff < minDiff) {
|
|
168
|
+
minDiff = diff;
|
|
169
|
+
nearest = [name, bpValue];
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
return { name: nearest[0], value: nearest[1] };
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* Suggest a standard breakpoint for an arbitrary value
|
|
176
|
+
*/
|
|
177
|
+
export function suggestBreakpoint(value) {
|
|
178
|
+
const nearest = findNearestBreakpoint(value);
|
|
179
|
+
return `Use standard breakpoint '${nearest.name}' (${nearest.value}px) instead of ${value}px`;
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* Convert em/rem to pixels (assuming 16px base)
|
|
183
|
+
*/
|
|
184
|
+
export function convertToPixels(value, unit) {
|
|
185
|
+
if (unit === 'em' || unit === 'rem') {
|
|
186
|
+
return value * 16;
|
|
187
|
+
}
|
|
188
|
+
return value;
|
|
189
|
+
}
|
|
190
|
+
/**
|
|
191
|
+
* Detect Tailwind responsive prefixes in content
|
|
192
|
+
*/
|
|
193
|
+
export function detectTailwindResponsive(content, file) {
|
|
194
|
+
const results = [];
|
|
195
|
+
const lines = content.split('\n');
|
|
196
|
+
const regex = new RegExp(TAILWIND_RESPONSIVE_PATTERN.source, TAILWIND_RESPONSIVE_PATTERN.flags);
|
|
197
|
+
let match;
|
|
198
|
+
while ((match = regex.exec(content)) !== null) {
|
|
199
|
+
// Skip if inside a comment
|
|
200
|
+
if (isInsideComment(content, match.index)) {
|
|
201
|
+
continue;
|
|
202
|
+
}
|
|
203
|
+
const beforeMatch = content.slice(0, match.index);
|
|
204
|
+
const lineNumber = beforeMatch.split('\n').length;
|
|
205
|
+
const lastNewline = beforeMatch.lastIndexOf('\n');
|
|
206
|
+
const column = match.index - lastNewline;
|
|
207
|
+
const breakpoint = match[1] || '';
|
|
208
|
+
const breakpointValue = TAILWIND_BREAKPOINTS[breakpoint];
|
|
209
|
+
results.push({
|
|
210
|
+
type: 'tailwind-responsive',
|
|
211
|
+
file,
|
|
212
|
+
line: lineNumber,
|
|
213
|
+
column,
|
|
214
|
+
matchedText: match[0],
|
|
215
|
+
breakpoint,
|
|
216
|
+
breakpointValue,
|
|
217
|
+
context: lines[lineNumber - 1] || '',
|
|
218
|
+
});
|
|
219
|
+
}
|
|
220
|
+
return results;
|
|
221
|
+
}
|
|
222
|
+
/**
|
|
223
|
+
* Detect CSS media queries (min-width - mobile-first)
|
|
224
|
+
*/
|
|
225
|
+
export function detectCSSMediaQueriesMinWidth(content, file) {
|
|
226
|
+
const results = [];
|
|
227
|
+
const lines = content.split('\n');
|
|
228
|
+
const regex = new RegExp(CSS_MEDIA_QUERY_MIN_WIDTH_PATTERN.source, CSS_MEDIA_QUERY_MIN_WIDTH_PATTERN.flags);
|
|
229
|
+
let match;
|
|
230
|
+
while ((match = regex.exec(content)) !== null) {
|
|
231
|
+
// Skip if inside a comment
|
|
232
|
+
if (isInsideComment(content, match.index)) {
|
|
233
|
+
continue;
|
|
234
|
+
}
|
|
235
|
+
const beforeMatch = content.slice(0, match.index);
|
|
236
|
+
const lineNumber = beforeMatch.split('\n').length;
|
|
237
|
+
const lastNewline = beforeMatch.lastIndexOf('\n');
|
|
238
|
+
const column = match.index - lastNewline;
|
|
239
|
+
const value = parseFloat(match[1] || '0');
|
|
240
|
+
const unit = match[2] || 'px';
|
|
241
|
+
const breakpointValue = convertToPixels(value, unit);
|
|
242
|
+
results.push({
|
|
243
|
+
type: 'mobile-first',
|
|
244
|
+
file,
|
|
245
|
+
line: lineNumber,
|
|
246
|
+
column,
|
|
247
|
+
matchedText: match[0],
|
|
248
|
+
breakpoint: `${value}${unit}`,
|
|
249
|
+
breakpointValue,
|
|
250
|
+
context: lines[lineNumber - 1] || '',
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
return results;
|
|
254
|
+
}
|
|
255
|
+
/**
|
|
256
|
+
* Detect CSS media queries (max-width - desktop-first)
|
|
257
|
+
*/
|
|
258
|
+
export function detectCSSMediaQueriesMaxWidth(content, file) {
|
|
259
|
+
const results = [];
|
|
260
|
+
const lines = content.split('\n');
|
|
261
|
+
const regex = new RegExp(CSS_MEDIA_QUERY_MAX_WIDTH_PATTERN.source, CSS_MEDIA_QUERY_MAX_WIDTH_PATTERN.flags);
|
|
262
|
+
let match;
|
|
263
|
+
while ((match = regex.exec(content)) !== null) {
|
|
264
|
+
// Skip if inside a comment
|
|
265
|
+
if (isInsideComment(content, match.index)) {
|
|
266
|
+
continue;
|
|
267
|
+
}
|
|
268
|
+
const beforeMatch = content.slice(0, match.index);
|
|
269
|
+
const lineNumber = beforeMatch.split('\n').length;
|
|
270
|
+
const lastNewline = beforeMatch.lastIndexOf('\n');
|
|
271
|
+
const column = match.index - lastNewline;
|
|
272
|
+
const value = parseFloat(match[1] || '0');
|
|
273
|
+
const unit = match[2] || 'px';
|
|
274
|
+
const breakpointValue = convertToPixels(value, unit);
|
|
275
|
+
results.push({
|
|
276
|
+
type: 'desktop-first',
|
|
277
|
+
file,
|
|
278
|
+
line: lineNumber,
|
|
279
|
+
column,
|
|
280
|
+
matchedText: match[0],
|
|
281
|
+
breakpoint: `${value}${unit}`,
|
|
282
|
+
breakpointValue,
|
|
283
|
+
context: lines[lineNumber - 1] || '',
|
|
284
|
+
});
|
|
285
|
+
}
|
|
286
|
+
return results;
|
|
287
|
+
}
|
|
288
|
+
/**
|
|
289
|
+
* Detect CSS container queries
|
|
290
|
+
*/
|
|
291
|
+
export function detectCSSContainerQueries(content, file) {
|
|
292
|
+
const results = [];
|
|
293
|
+
const lines = content.split('\n');
|
|
294
|
+
const regex = new RegExp(CSS_CONTAINER_QUERY_PATTERN.source, CSS_CONTAINER_QUERY_PATTERN.flags);
|
|
295
|
+
let match;
|
|
296
|
+
while ((match = regex.exec(content)) !== null) {
|
|
297
|
+
// Skip if inside a comment
|
|
298
|
+
if (isInsideComment(content, match.index)) {
|
|
299
|
+
continue;
|
|
300
|
+
}
|
|
301
|
+
const beforeMatch = content.slice(0, match.index);
|
|
302
|
+
const lineNumber = beforeMatch.split('\n').length;
|
|
303
|
+
const lastNewline = beforeMatch.lastIndexOf('\n');
|
|
304
|
+
const column = match.index - lastNewline;
|
|
305
|
+
const value = parseFloat(match[1] || '0');
|
|
306
|
+
const unit = match[2] || 'px';
|
|
307
|
+
const breakpointValue = convertToPixels(value, unit);
|
|
308
|
+
results.push({
|
|
309
|
+
type: 'css-container-query',
|
|
310
|
+
file,
|
|
311
|
+
line: lineNumber,
|
|
312
|
+
column,
|
|
313
|
+
matchedText: match[0],
|
|
314
|
+
breakpoint: `${value}${unit}`,
|
|
315
|
+
breakpointValue,
|
|
316
|
+
context: lines[lineNumber - 1] || '',
|
|
317
|
+
});
|
|
318
|
+
}
|
|
319
|
+
return results;
|
|
320
|
+
}
|
|
321
|
+
/**
|
|
322
|
+
* Detect theme breakpoint usage
|
|
323
|
+
*/
|
|
324
|
+
export function detectThemeBreakpoints(content, file) {
|
|
325
|
+
const results = [];
|
|
326
|
+
const lines = content.split('\n');
|
|
327
|
+
for (const pattern of THEME_BREAKPOINT_PATTERNS) {
|
|
328
|
+
const regex = new RegExp(pattern.source, pattern.flags);
|
|
329
|
+
let match;
|
|
330
|
+
while ((match = regex.exec(content)) !== null) {
|
|
331
|
+
// Skip if inside a comment
|
|
332
|
+
if (isInsideComment(content, match.index)) {
|
|
333
|
+
continue;
|
|
334
|
+
}
|
|
335
|
+
const beforeMatch = content.slice(0, match.index);
|
|
336
|
+
const lineNumber = beforeMatch.split('\n').length;
|
|
337
|
+
const lastNewline = beforeMatch.lastIndexOf('\n');
|
|
338
|
+
const column = match.index - lastNewline;
|
|
339
|
+
const breakpoint = match[1] || '';
|
|
340
|
+
const breakpointValue = TAILWIND_BREAKPOINTS[breakpoint];
|
|
341
|
+
results.push({
|
|
342
|
+
type: 'theme-breakpoint',
|
|
343
|
+
file,
|
|
344
|
+
line: lineNumber,
|
|
345
|
+
column,
|
|
346
|
+
matchedText: match[0],
|
|
347
|
+
breakpoint,
|
|
348
|
+
breakpointValue,
|
|
349
|
+
context: lines[lineNumber - 1] || '',
|
|
350
|
+
});
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
return results;
|
|
354
|
+
}
|
|
355
|
+
/**
|
|
356
|
+
* Detect CSS custom property breakpoints
|
|
357
|
+
*/
|
|
358
|
+
export function detectCSSBreakpointProperties(content, file) {
|
|
359
|
+
const results = [];
|
|
360
|
+
const lines = content.split('\n');
|
|
361
|
+
const regex = new RegExp(CSS_BREAKPOINT_PROPERTY_PATTERN.source, CSS_BREAKPOINT_PROPERTY_PATTERN.flags);
|
|
362
|
+
let match;
|
|
363
|
+
while ((match = regex.exec(content)) !== null) {
|
|
364
|
+
// Skip if inside a comment
|
|
365
|
+
if (isInsideComment(content, match.index)) {
|
|
366
|
+
continue;
|
|
367
|
+
}
|
|
368
|
+
const beforeMatch = content.slice(0, match.index);
|
|
369
|
+
const lineNumber = beforeMatch.split('\n').length;
|
|
370
|
+
const lastNewline = beforeMatch.lastIndexOf('\n');
|
|
371
|
+
const column = match.index - lastNewline;
|
|
372
|
+
const breakpoint = match[1] || '';
|
|
373
|
+
const breakpointValue = TAILWIND_BREAKPOINTS[breakpoint];
|
|
374
|
+
results.push({
|
|
375
|
+
type: 'css-breakpoint-property',
|
|
376
|
+
file,
|
|
377
|
+
line: lineNumber,
|
|
378
|
+
column,
|
|
379
|
+
matchedText: match[0],
|
|
380
|
+
breakpoint,
|
|
381
|
+
breakpointValue,
|
|
382
|
+
context: lines[lineNumber - 1] || '',
|
|
383
|
+
});
|
|
384
|
+
}
|
|
385
|
+
return results;
|
|
386
|
+
}
|
|
387
|
+
/**
|
|
388
|
+
* Extract all class names from a className attribute
|
|
389
|
+
*/
|
|
390
|
+
function extractClassNamesFromLine(line) {
|
|
391
|
+
const classNames = [];
|
|
392
|
+
// Match className="..." or class="..."
|
|
393
|
+
const classAttrMatch = line.match(/(?:className|class)=["']([^"']+)["']/);
|
|
394
|
+
if (classAttrMatch && classAttrMatch[1]) {
|
|
395
|
+
classNames.push(...classAttrMatch[1].split(/\s+/).filter(Boolean));
|
|
396
|
+
}
|
|
397
|
+
// Match className={`...`} template literals
|
|
398
|
+
const templateMatch = line.match(/(?:className|class)=\{`([^`]+)`\}/);
|
|
399
|
+
if (templateMatch && templateMatch[1]) {
|
|
400
|
+
// Extract static parts (ignore ${...} expressions)
|
|
401
|
+
const staticParts = templateMatch[1].replace(/\$\{[^}]+\}/g, ' ');
|
|
402
|
+
classNames.push(...staticParts.split(/\s+/).filter(Boolean));
|
|
403
|
+
}
|
|
404
|
+
return classNames;
|
|
405
|
+
}
|
|
406
|
+
/**
|
|
407
|
+
* Detect inconsistent breakpoint ordering in Tailwind classes
|
|
408
|
+
*/
|
|
409
|
+
export function detectInconsistentBreakpointOrder(content, file) {
|
|
410
|
+
const results = [];
|
|
411
|
+
const lines = content.split('\n');
|
|
412
|
+
for (let lineIndex = 0; lineIndex < lines.length; lineIndex++) {
|
|
413
|
+
const line = lines[lineIndex];
|
|
414
|
+
const classNames = extractClassNamesFromLine(line);
|
|
415
|
+
if (classNames.length === 0)
|
|
416
|
+
continue;
|
|
417
|
+
// Group responsive classes by their base class
|
|
418
|
+
const responsiveGroups = new Map();
|
|
419
|
+
classNames.forEach((className, index) => {
|
|
420
|
+
const match = className.match(/^(sm|md|lg|xl|2xl):(.+)$/);
|
|
421
|
+
if (match) {
|
|
422
|
+
const breakpoint = match[1];
|
|
423
|
+
const baseClass = match[2];
|
|
424
|
+
// Extract the property prefix (e.g., 'flex' from 'flex', 'p' from 'p-4')
|
|
425
|
+
const propertyMatch = baseClass.match(/^([a-z]+(?:-[a-z]+)*?)(?:-\d|$|\[)/i);
|
|
426
|
+
const propertyPrefix = propertyMatch ? propertyMatch[1] : baseClass;
|
|
427
|
+
if (!responsiveGroups.has(propertyPrefix)) {
|
|
428
|
+
responsiveGroups.set(propertyPrefix, []);
|
|
429
|
+
}
|
|
430
|
+
responsiveGroups.get(propertyPrefix).push({ breakpoint, className, index });
|
|
431
|
+
}
|
|
432
|
+
});
|
|
433
|
+
// Check each group for ordering issues
|
|
434
|
+
for (const [_propertyPrefix, group] of responsiveGroups) {
|
|
435
|
+
if (group.length < 2)
|
|
436
|
+
continue;
|
|
437
|
+
// Check if breakpoints are in correct order
|
|
438
|
+
for (let i = 0; i < group.length - 1; i++) {
|
|
439
|
+
const current = group[i];
|
|
440
|
+
const next = group[i + 1];
|
|
441
|
+
const currentOrder = getBreakpointOrderIndex(current.breakpoint);
|
|
442
|
+
const nextOrder = getBreakpointOrderIndex(next.breakpoint);
|
|
443
|
+
// If current breakpoint should come after next (wrong order)
|
|
444
|
+
if (currentOrder > nextOrder && current.index < next.index) {
|
|
445
|
+
const classIndex = line.indexOf(current.className);
|
|
446
|
+
const column = classIndex >= 0 ? classIndex + 1 : 1;
|
|
447
|
+
results.push({
|
|
448
|
+
type: 'inconsistent-breakpoint-order',
|
|
449
|
+
file,
|
|
450
|
+
line: lineIndex + 1,
|
|
451
|
+
column,
|
|
452
|
+
endLine: lineIndex + 1,
|
|
453
|
+
endColumn: column + current.className.length,
|
|
454
|
+
breakpoints: [current.breakpoint, next.breakpoint],
|
|
455
|
+
issue: `Breakpoint '${current.breakpoint}:' appears before '${next.breakpoint}:' but should come after (mobile-first order: sm → md → lg → xl → 2xl)`,
|
|
456
|
+
suggestedFix: `Reorder classes to follow mobile-first breakpoint order: ${TAILWIND_BREAKPOINT_ORDER.join(' → ')}`,
|
|
457
|
+
lineContent: line,
|
|
458
|
+
});
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
return results;
|
|
464
|
+
}
|
|
465
|
+
/**
|
|
466
|
+
* Detect mixed mobile-first and desktop-first approaches
|
|
467
|
+
*/
|
|
468
|
+
export function detectMixedApproach(mobileFirstPatterns, desktopFirstPatterns, file) {
|
|
469
|
+
const results = [];
|
|
470
|
+
// Only flag if both approaches are used significantly
|
|
471
|
+
if (mobileFirstPatterns.length > 0 && desktopFirstPatterns.length > 0) {
|
|
472
|
+
const totalPatterns = mobileFirstPatterns.length + desktopFirstPatterns.length;
|
|
473
|
+
const mobileFirstRatio = mobileFirstPatterns.length / totalPatterns;
|
|
474
|
+
// If neither approach is dominant (both > 20%), flag as mixed
|
|
475
|
+
if (mobileFirstRatio > 0.2 && mobileFirstRatio < 0.8) {
|
|
476
|
+
// Flag the minority approach patterns
|
|
477
|
+
const minorityPatterns = mobileFirstRatio > 0.5 ? desktopFirstPatterns : mobileFirstPatterns;
|
|
478
|
+
const dominantApproach = mobileFirstRatio > 0.5 ? 'mobile-first (min-width)' : 'desktop-first (max-width)';
|
|
479
|
+
for (const pattern of minorityPatterns) {
|
|
480
|
+
results.push({
|
|
481
|
+
type: 'mixed-approach',
|
|
482
|
+
file,
|
|
483
|
+
line: pattern.line,
|
|
484
|
+
column: pattern.column,
|
|
485
|
+
endLine: pattern.line,
|
|
486
|
+
endColumn: pattern.column + pattern.matchedText.length,
|
|
487
|
+
breakpoints: [pattern.breakpoint || ''],
|
|
488
|
+
issue: `File mixes mobile-first and desktop-first approaches. This media query uses ${pattern.type === 'mobile-first' ? 'min-width' : 'max-width'}, but the file predominantly uses ${dominantApproach}`,
|
|
489
|
+
suggestedFix: `Consider standardizing on ${dominantApproach} approach for consistency`,
|
|
490
|
+
lineContent: pattern.context || '',
|
|
491
|
+
});
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
return results;
|
|
496
|
+
}
|
|
497
|
+
/**
|
|
498
|
+
* Detect arbitrary/hardcoded breakpoint values in media queries
|
|
499
|
+
*/
|
|
500
|
+
export function detectArbitraryBreakpoints(content, file) {
|
|
501
|
+
const results = [];
|
|
502
|
+
const lines = content.split('\n');
|
|
503
|
+
const regex = new RegExp(HARDCODED_MEDIA_QUERY_PATTERN.source, HARDCODED_MEDIA_QUERY_PATTERN.flags);
|
|
504
|
+
let match;
|
|
505
|
+
while ((match = regex.exec(content)) !== null) {
|
|
506
|
+
// Skip if inside a comment
|
|
507
|
+
if (isInsideComment(content, match.index)) {
|
|
508
|
+
continue;
|
|
509
|
+
}
|
|
510
|
+
const value = parseFloat(match[1] || '0');
|
|
511
|
+
// Skip if it's a standard breakpoint value
|
|
512
|
+
if (isAllowedBreakpointValue(value)) {
|
|
513
|
+
continue;
|
|
514
|
+
}
|
|
515
|
+
const beforeMatch = content.slice(0, match.index);
|
|
516
|
+
const lineNumber = beforeMatch.split('\n').length;
|
|
517
|
+
const lastNewline = beforeMatch.lastIndexOf('\n');
|
|
518
|
+
const column = match.index - lastNewline;
|
|
519
|
+
const endColumn = column + match[0].length;
|
|
520
|
+
results.push({
|
|
521
|
+
type: 'arbitrary-breakpoint',
|
|
522
|
+
file,
|
|
523
|
+
line: lineNumber,
|
|
524
|
+
column,
|
|
525
|
+
endLine: lineNumber,
|
|
526
|
+
endColumn,
|
|
527
|
+
breakpoints: [`${value}px`],
|
|
528
|
+
issue: `Arbitrary breakpoint value '${value}px' doesn't match standard design system breakpoints`,
|
|
529
|
+
suggestedFix: suggestBreakpoint(value),
|
|
530
|
+
lineContent: lines[lineNumber - 1] || '',
|
|
531
|
+
});
|
|
532
|
+
}
|
|
533
|
+
return results;
|
|
534
|
+
}
|
|
535
|
+
/**
|
|
536
|
+
* Analyze responsive patterns in a file
|
|
537
|
+
*/
|
|
538
|
+
export function analyzeResponsive(content, file) {
|
|
539
|
+
// Skip excluded files
|
|
540
|
+
if (shouldExcludeFile(file)) {
|
|
541
|
+
return {
|
|
542
|
+
patterns: [],
|
|
543
|
+
violations: [],
|
|
544
|
+
usesTailwindResponsive: false,
|
|
545
|
+
usesCSSMediaQueries: false,
|
|
546
|
+
usesCSSContainerQueries: false,
|
|
547
|
+
usesThemeBreakpoints: false,
|
|
548
|
+
usesCSSBreakpointProperties: false,
|
|
549
|
+
usesMobileFirst: false,
|
|
550
|
+
usesDesktopFirst: false,
|
|
551
|
+
responsiveConsistencyConfidence: 1.0,
|
|
552
|
+
};
|
|
553
|
+
}
|
|
554
|
+
// Detect all patterns
|
|
555
|
+
const tailwindResponsive = detectTailwindResponsive(content, file);
|
|
556
|
+
const mobileFirstQueries = detectCSSMediaQueriesMinWidth(content, file);
|
|
557
|
+
const desktopFirstQueries = detectCSSMediaQueriesMaxWidth(content, file);
|
|
558
|
+
const containerQueries = detectCSSContainerQueries(content, file);
|
|
559
|
+
const themeBreakpoints = detectThemeBreakpoints(content, file);
|
|
560
|
+
const cssBreakpointProperties = detectCSSBreakpointProperties(content, file);
|
|
561
|
+
const allPatterns = [
|
|
562
|
+
...tailwindResponsive,
|
|
563
|
+
...mobileFirstQueries,
|
|
564
|
+
...desktopFirstQueries,
|
|
565
|
+
...containerQueries,
|
|
566
|
+
...themeBreakpoints,
|
|
567
|
+
...cssBreakpointProperties,
|
|
568
|
+
];
|
|
569
|
+
// Detect violations
|
|
570
|
+
const breakpointOrderViolations = detectInconsistentBreakpointOrder(content, file);
|
|
571
|
+
const mixedApproachViolations = detectMixedApproach(mobileFirstQueries, desktopFirstQueries, file);
|
|
572
|
+
const arbitraryBreakpointViolations = detectArbitraryBreakpoints(content, file);
|
|
573
|
+
const allViolations = [
|
|
574
|
+
...breakpointOrderViolations,
|
|
575
|
+
...mixedApproachViolations,
|
|
576
|
+
...arbitraryBreakpointViolations,
|
|
577
|
+
];
|
|
578
|
+
// Calculate confidence
|
|
579
|
+
const hasPatterns = allPatterns.length > 0;
|
|
580
|
+
const hasViolations = allViolations.length > 0;
|
|
581
|
+
let responsiveConsistencyConfidence = 0;
|
|
582
|
+
if (hasPatterns && !hasViolations) {
|
|
583
|
+
responsiveConsistencyConfidence = 1.0;
|
|
584
|
+
}
|
|
585
|
+
else if (hasPatterns && hasViolations) {
|
|
586
|
+
const ratio = allPatterns.length / (allPatterns.length + allViolations.length);
|
|
587
|
+
responsiveConsistencyConfidence = ratio;
|
|
588
|
+
}
|
|
589
|
+
else if (!hasPatterns && hasViolations) {
|
|
590
|
+
responsiveConsistencyConfidence = 0;
|
|
591
|
+
}
|
|
592
|
+
else {
|
|
593
|
+
responsiveConsistencyConfidence = 0.5; // No responsive patterns detected
|
|
594
|
+
}
|
|
595
|
+
return {
|
|
596
|
+
patterns: allPatterns,
|
|
597
|
+
violations: allViolations,
|
|
598
|
+
usesTailwindResponsive: tailwindResponsive.length > 0,
|
|
599
|
+
usesCSSMediaQueries: mobileFirstQueries.length > 0 || desktopFirstQueries.length > 0,
|
|
600
|
+
usesCSSContainerQueries: containerQueries.length > 0,
|
|
601
|
+
usesThemeBreakpoints: themeBreakpoints.length > 0,
|
|
602
|
+
usesCSSBreakpointProperties: cssBreakpointProperties.length > 0,
|
|
603
|
+
usesMobileFirst: mobileFirstQueries.length > 0,
|
|
604
|
+
usesDesktopFirst: desktopFirstQueries.length > 0,
|
|
605
|
+
responsiveConsistencyConfidence,
|
|
606
|
+
};
|
|
607
|
+
}
|
|
608
|
+
// ============================================================================
|
|
609
|
+
// Responsive Detector Class
|
|
610
|
+
// ============================================================================
|
|
611
|
+
/**
|
|
612
|
+
* Detector for responsive breakpoint usage patterns
|
|
613
|
+
*
|
|
614
|
+
* Identifies responsive patterns (Tailwind prefixes, CSS media queries, container queries)
|
|
615
|
+
* and flags inconsistent breakpoint usage.
|
|
616
|
+
*
|
|
617
|
+
* @requirements 9.8 - THE Styling_Detector SHALL detect responsive breakpoint usage patterns
|
|
618
|
+
*/
|
|
619
|
+
export class ResponsiveDetector extends RegexDetector {
|
|
620
|
+
id = 'styling/responsive';
|
|
621
|
+
category = 'styling';
|
|
622
|
+
subcategory = 'responsive';
|
|
623
|
+
name = 'Responsive Detector';
|
|
624
|
+
description = 'Detects responsive breakpoint usage patterns and flags inconsistent breakpoint ordering, mixed approaches, and arbitrary values';
|
|
625
|
+
supportedLanguages = ['typescript', 'javascript', 'css'];
|
|
626
|
+
/**
|
|
627
|
+
* Detect responsive patterns and violations
|
|
628
|
+
*/
|
|
629
|
+
async detect(context) {
|
|
630
|
+
const patterns = [];
|
|
631
|
+
const violations = [];
|
|
632
|
+
// Analyze the file
|
|
633
|
+
const analysis = analyzeResponsive(context.content, context.file);
|
|
634
|
+
// Create pattern matches for responsive patterns
|
|
635
|
+
if (analysis.usesTailwindResponsive) {
|
|
636
|
+
patterns.push(this.createTailwindResponsivePattern(context.file, analysis));
|
|
637
|
+
}
|
|
638
|
+
if (analysis.usesCSSMediaQueries) {
|
|
639
|
+
patterns.push(this.createCSSMediaQueryPattern(context.file, analysis));
|
|
640
|
+
}
|
|
641
|
+
if (analysis.usesCSSContainerQueries) {
|
|
642
|
+
patterns.push(this.createCSSContainerQueryPattern(context.file, analysis));
|
|
643
|
+
}
|
|
644
|
+
if (analysis.usesThemeBreakpoints) {
|
|
645
|
+
patterns.push(this.createThemeBreakpointPattern(context.file, analysis));
|
|
646
|
+
}
|
|
647
|
+
if (analysis.usesCSSBreakpointProperties) {
|
|
648
|
+
patterns.push(this.createCSSBreakpointPropertyPattern(context.file, analysis));
|
|
649
|
+
}
|
|
650
|
+
// Create violations
|
|
651
|
+
for (const violation of analysis.violations) {
|
|
652
|
+
violations.push(this.createResponsiveViolation(violation));
|
|
653
|
+
}
|
|
654
|
+
return this.createResult(patterns, violations, analysis.responsiveConsistencyConfidence);
|
|
655
|
+
}
|
|
656
|
+
/**
|
|
657
|
+
* Create a pattern match for Tailwind responsive usage
|
|
658
|
+
*/
|
|
659
|
+
createTailwindResponsivePattern(file, analysis) {
|
|
660
|
+
const tailwindPatterns = analysis.patterns.filter(p => p.type === 'tailwind-responsive');
|
|
661
|
+
const firstPattern = tailwindPatterns[0];
|
|
662
|
+
return {
|
|
663
|
+
patternId: `${this.id}/tailwind`,
|
|
664
|
+
location: {
|
|
665
|
+
file,
|
|
666
|
+
line: firstPattern?.line || 1,
|
|
667
|
+
column: firstPattern?.column || 1,
|
|
668
|
+
},
|
|
669
|
+
confidence: 1.0,
|
|
670
|
+
isOutlier: false,
|
|
671
|
+
};
|
|
672
|
+
}
|
|
673
|
+
/**
|
|
674
|
+
* Create a pattern match for CSS media query usage
|
|
675
|
+
*/
|
|
676
|
+
createCSSMediaQueryPattern(file, analysis) {
|
|
677
|
+
const mediaQueryPatterns = analysis.patterns.filter(p => p.type === 'mobile-first' || p.type === 'desktop-first');
|
|
678
|
+
const firstPattern = mediaQueryPatterns[0];
|
|
679
|
+
return {
|
|
680
|
+
patternId: `${this.id}/media-query`,
|
|
681
|
+
location: {
|
|
682
|
+
file,
|
|
683
|
+
line: firstPattern?.line || 1,
|
|
684
|
+
column: firstPattern?.column || 1,
|
|
685
|
+
},
|
|
686
|
+
confidence: 1.0,
|
|
687
|
+
isOutlier: false,
|
|
688
|
+
};
|
|
689
|
+
}
|
|
690
|
+
/**
|
|
691
|
+
* Create a pattern match for CSS container query usage
|
|
692
|
+
*/
|
|
693
|
+
createCSSContainerQueryPattern(file, analysis) {
|
|
694
|
+
const containerPatterns = analysis.patterns.filter(p => p.type === 'css-container-query');
|
|
695
|
+
const firstPattern = containerPatterns[0];
|
|
696
|
+
return {
|
|
697
|
+
patternId: `${this.id}/container-query`,
|
|
698
|
+
location: {
|
|
699
|
+
file,
|
|
700
|
+
line: firstPattern?.line || 1,
|
|
701
|
+
column: firstPattern?.column || 1,
|
|
702
|
+
},
|
|
703
|
+
confidence: 1.0,
|
|
704
|
+
isOutlier: false,
|
|
705
|
+
};
|
|
706
|
+
}
|
|
707
|
+
/**
|
|
708
|
+
* Create a pattern match for theme breakpoint usage
|
|
709
|
+
*/
|
|
710
|
+
createThemeBreakpointPattern(file, analysis) {
|
|
711
|
+
const themePatterns = analysis.patterns.filter(p => p.type === 'theme-breakpoint');
|
|
712
|
+
const firstPattern = themePatterns[0];
|
|
713
|
+
return {
|
|
714
|
+
patternId: `${this.id}/theme`,
|
|
715
|
+
location: {
|
|
716
|
+
file,
|
|
717
|
+
line: firstPattern?.line || 1,
|
|
718
|
+
column: firstPattern?.column || 1,
|
|
719
|
+
},
|
|
720
|
+
confidence: 1.0,
|
|
721
|
+
isOutlier: false,
|
|
722
|
+
};
|
|
723
|
+
}
|
|
724
|
+
/**
|
|
725
|
+
* Create a pattern match for CSS breakpoint property usage
|
|
726
|
+
*/
|
|
727
|
+
createCSSBreakpointPropertyPattern(file, analysis) {
|
|
728
|
+
const cssPatterns = analysis.patterns.filter(p => p.type === 'css-breakpoint-property');
|
|
729
|
+
const firstPattern = cssPatterns[0];
|
|
730
|
+
return {
|
|
731
|
+
patternId: `${this.id}/css-property`,
|
|
732
|
+
location: {
|
|
733
|
+
file,
|
|
734
|
+
line: firstPattern?.line || 1,
|
|
735
|
+
column: firstPattern?.column || 1,
|
|
736
|
+
},
|
|
737
|
+
confidence: 1.0,
|
|
738
|
+
isOutlier: false,
|
|
739
|
+
};
|
|
740
|
+
}
|
|
741
|
+
/**
|
|
742
|
+
* Create a violation for a responsive issue
|
|
743
|
+
*/
|
|
744
|
+
createResponsiveViolation(violationInfo) {
|
|
745
|
+
const severity = violationInfo.type === 'arbitrary-breakpoint' ? 'warning' : 'info';
|
|
746
|
+
const violation = {
|
|
747
|
+
id: `${this.id}-${violationInfo.file}-${violationInfo.line}-${violationInfo.column}`,
|
|
748
|
+
patternId: this.id,
|
|
749
|
+
severity,
|
|
750
|
+
file: violationInfo.file,
|
|
751
|
+
range: {
|
|
752
|
+
start: { line: violationInfo.line - 1, character: violationInfo.column - 1 },
|
|
753
|
+
end: { line: violationInfo.endLine - 1, character: violationInfo.endColumn - 1 },
|
|
754
|
+
},
|
|
755
|
+
message: violationInfo.issue,
|
|
756
|
+
explanation: this.getExplanation(violationInfo),
|
|
757
|
+
expected: this.getExpectedValue(violationInfo),
|
|
758
|
+
actual: violationInfo.breakpoints.join(', '),
|
|
759
|
+
aiExplainAvailable: true,
|
|
760
|
+
aiFixAvailable: violationInfo.type === 'arbitrary-breakpoint',
|
|
761
|
+
firstSeen: new Date(),
|
|
762
|
+
occurrences: 1,
|
|
763
|
+
};
|
|
764
|
+
const quickFix = this.createQuickFixForViolation(violationInfo);
|
|
765
|
+
if (quickFix !== undefined) {
|
|
766
|
+
violation.quickFix = quickFix;
|
|
767
|
+
}
|
|
768
|
+
return violation;
|
|
769
|
+
}
|
|
770
|
+
/**
|
|
771
|
+
* Get explanation for a responsive violation
|
|
772
|
+
*/
|
|
773
|
+
getExplanation(violationInfo) {
|
|
774
|
+
switch (violationInfo.type) {
|
|
775
|
+
case 'inconsistent-breakpoint-order':
|
|
776
|
+
return `Tailwind responsive classes should follow mobile-first ordering (sm → md → lg → xl → 2xl). This ensures styles cascade correctly from smaller to larger screens. Incorrect ordering can lead to unexpected style overrides.`;
|
|
777
|
+
case 'mixed-approach':
|
|
778
|
+
return `Mixing mobile-first (min-width) and desktop-first (max-width) media queries in the same file creates confusion and can lead to conflicting styles. Choose one approach and use it consistently throughout your codebase.`;
|
|
779
|
+
case 'arbitrary-breakpoint':
|
|
780
|
+
return `Using arbitrary breakpoint values instead of standard design system breakpoints makes it harder to maintain consistent responsive behavior. Standard breakpoints (640px, 768px, 1024px, 1280px, 1536px) ensure consistency across your application.`;
|
|
781
|
+
case 'hardcoded-media-query':
|
|
782
|
+
return `Hardcoded pixel values in media queries should be replaced with design tokens or CSS custom properties for maintainability. This allows breakpoints to be changed in one place.`;
|
|
783
|
+
case 'missing-responsive-variant':
|
|
784
|
+
return `Key responsive breakpoints should have corresponding style variants to ensure proper display across all device sizes.`;
|
|
785
|
+
default:
|
|
786
|
+
return `Responsive patterns should be consistent throughout the codebase for maintainability.`;
|
|
787
|
+
}
|
|
788
|
+
}
|
|
789
|
+
/**
|
|
790
|
+
* Get expected value for a responsive violation
|
|
791
|
+
*/
|
|
792
|
+
getExpectedValue(violationInfo) {
|
|
793
|
+
switch (violationInfo.type) {
|
|
794
|
+
case 'inconsistent-breakpoint-order':
|
|
795
|
+
return `Breakpoints in order: ${TAILWIND_BREAKPOINT_ORDER.join(' → ')}`;
|
|
796
|
+
case 'mixed-approach':
|
|
797
|
+
return `Consistent use of either mobile-first (min-width) or desktop-first (max-width)`;
|
|
798
|
+
case 'arbitrary-breakpoint':
|
|
799
|
+
return `Standard breakpoint value (${Object.entries(TAILWIND_BREAKPOINTS).map(([k, v]) => `${k}: ${v}px`).join(', ')})`;
|
|
800
|
+
case 'hardcoded-media-query':
|
|
801
|
+
return `CSS custom property or design token (e.g., var(--breakpoint-md))`;
|
|
802
|
+
case 'missing-responsive-variant':
|
|
803
|
+
return `Responsive variants for key breakpoints`;
|
|
804
|
+
default:
|
|
805
|
+
return `Consistent responsive pattern`;
|
|
806
|
+
}
|
|
807
|
+
}
|
|
808
|
+
/**
|
|
809
|
+
* Create a quick fix for a responsive violation
|
|
810
|
+
*/
|
|
811
|
+
createQuickFixForViolation(violationInfo) {
|
|
812
|
+
if (violationInfo.type !== 'arbitrary-breakpoint') {
|
|
813
|
+
return undefined;
|
|
814
|
+
}
|
|
815
|
+
// Extract the pixel value from the breakpoint
|
|
816
|
+
const pxMatch = violationInfo.breakpoints[0]?.match(/(\d+)px/);
|
|
817
|
+
if (!pxMatch) {
|
|
818
|
+
return undefined;
|
|
819
|
+
}
|
|
820
|
+
const value = parseInt(pxMatch[1], 10);
|
|
821
|
+
const nearest = findNearestBreakpoint(value);
|
|
822
|
+
// Create replacement text
|
|
823
|
+
const replacement = violationInfo.lineContent.replace(new RegExp(`${value}px`), `${nearest.value}px`);
|
|
824
|
+
return {
|
|
825
|
+
title: `Replace with standard breakpoint: ${nearest.name} (${nearest.value}px)`,
|
|
826
|
+
kind: 'quickfix',
|
|
827
|
+
edit: {
|
|
828
|
+
changes: {
|
|
829
|
+
[violationInfo.file]: [
|
|
830
|
+
{
|
|
831
|
+
range: {
|
|
832
|
+
start: { line: violationInfo.line - 1, character: 0 },
|
|
833
|
+
end: { line: violationInfo.line - 1, character: violationInfo.lineContent.length },
|
|
834
|
+
},
|
|
835
|
+
newText: replacement,
|
|
836
|
+
},
|
|
837
|
+
],
|
|
838
|
+
},
|
|
839
|
+
},
|
|
840
|
+
isPreferred: true,
|
|
841
|
+
confidence: 0.7,
|
|
842
|
+
preview: `Replace '${value}px' with '${nearest.value}px' (${nearest.name})`,
|
|
843
|
+
};
|
|
844
|
+
}
|
|
845
|
+
/**
|
|
846
|
+
* Generate a quick fix for a violation
|
|
847
|
+
*/
|
|
848
|
+
generateQuickFix(violation) {
|
|
849
|
+
// Check if this is a responsive violation with arbitrary breakpoint
|
|
850
|
+
if (!violation.message.includes('breakpoint') && !violation.message.includes('Breakpoint')) {
|
|
851
|
+
return null;
|
|
852
|
+
}
|
|
853
|
+
// Extract the pixel value from the message
|
|
854
|
+
const pxMatch = violation.actual.match(/(\d+)px/);
|
|
855
|
+
if (!pxMatch) {
|
|
856
|
+
return null;
|
|
857
|
+
}
|
|
858
|
+
const value = parseInt(pxMatch[1], 10);
|
|
859
|
+
const nearest = findNearestBreakpoint(value);
|
|
860
|
+
return {
|
|
861
|
+
title: `Replace with standard breakpoint: ${nearest.name} (${nearest.value}px)`,
|
|
862
|
+
kind: 'quickfix',
|
|
863
|
+
edit: {
|
|
864
|
+
changes: {
|
|
865
|
+
[violation.file]: [
|
|
866
|
+
{
|
|
867
|
+
range: violation.range,
|
|
868
|
+
newText: `${nearest.value}px`,
|
|
869
|
+
},
|
|
870
|
+
],
|
|
871
|
+
},
|
|
872
|
+
},
|
|
873
|
+
isPreferred: true,
|
|
874
|
+
confidence: 0.7,
|
|
875
|
+
preview: `Replace '${value}px' with '${nearest.value}px' (${nearest.name})`,
|
|
876
|
+
};
|
|
877
|
+
}
|
|
878
|
+
}
|
|
879
|
+
// ============================================================================
|
|
880
|
+
// Factory Function
|
|
881
|
+
// ============================================================================
|
|
882
|
+
/**
|
|
883
|
+
* Create a new ResponsiveDetector instance
|
|
884
|
+
*/
|
|
885
|
+
export function createResponsiveDetector() {
|
|
886
|
+
return new ResponsiveDetector();
|
|
887
|
+
}
|
|
888
|
+
//# sourceMappingURL=responsive.js.map
|