@jmruthers/pace-core 0.6.4 → 0.6.6
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/CHANGELOG.md +104 -0
- package/README.md +5 -403
- package/core-usage-manifest.json +93 -0
- package/cursor-rules/00-pace-core-compliance.mdc +128 -26
- package/cursor-rules/01-standards-compliance.mdc +49 -8
- package/cursor-rules/02-project-structure.mdc +6 -0
- package/cursor-rules/03-solid-principles.mdc +2 -0
- package/cursor-rules/04-testing-standards.mdc +2 -0
- package/cursor-rules/05-bug-reports-and-features.mdc +2 -0
- package/cursor-rules/06-code-quality.mdc +2 -0
- package/cursor-rules/07-tech-stack-compliance.mdc +2 -0
- package/cursor-rules/08-markup-quality.mdc +52 -27
- package/cursor-rules/09-rbac-compliance.mdc +462 -0
- package/cursor-rules/10-error-handling-patterns.mdc +179 -0
- package/cursor-rules/11-performance-optimization.mdc +169 -0
- package/cursor-rules/12-ci-cd-integration.mdc +150 -0
- package/dist/{AuthService-Cb34EQs3.d.ts → AuthService-DmfO5rGS.d.ts} +10 -0
- package/dist/{DataTable-BMRU8a1j.d.ts → DataTable-2N_tqbfq.d.ts} +1 -1
- package/dist/DataTable-LRJL4IRV.js +15 -0
- package/dist/{PublicPageProvider-DEMpysFR.d.ts → PublicPageProvider-BBH6Vqg7.d.ts} +72 -139
- package/dist/UnifiedAuthProvider-ZT6TIGM7.js +7 -0
- package/dist/api-Y4MQWOFW.js +4 -0
- package/dist/audit-MYQXYZFU.js +3 -0
- package/dist/{chunk-J36DSWQK.js → chunk-2HGJFNAH.js} +8 -28
- package/dist/{chunk-OEWDTMG7.js → chunk-3O3WHILE.js} +38 -121
- package/dist/{chunk-M43Y4SSO.js → chunk-3QC3KRHK.js} +1 -14
- package/dist/{chunk-DGUM43GV.js → chunk-3RG5ZIWI.js} +1 -4
- package/dist/{chunk-QXHPKYJV.js → chunk-4SXLQIZO.js} +1 -26
- package/dist/chunk-4T7OBVTU.js +62 -0
- package/dist/{chunk-E66EQZE6.js → chunk-6GLLNA6U.js} +3 -9
- package/dist/{chunk-ZSAAAMVR.js → chunk-6QYDGKQY.js} +1 -4
- package/dist/{chunk-NN6WWZ5U.js → chunk-7TYHROIV.js} +579 -563
- package/dist/{chunk-M7MPQISP.js → chunk-A55DK444.js} +9 -16
- package/dist/{chunk-63FOKYGO.js → chunk-AHU7G2R5.js} +2 -11
- package/dist/{chunk-L4OXEN46.js → chunk-BVP2BCJF.js} +2 -16
- package/dist/chunk-C7NSAPTL.js +1 -0
- package/dist/{chunk-YKRAFF5K.js → chunk-FENMYN2U.js} +73 -149
- package/dist/{chunk-AVMLPIM7.js → chunk-FTCRZOG2.js} +284 -432
- package/dist/{chunk-G37KK66H.js → chunk-FYHN4DD5.js} +60 -19
- package/dist/{chunk-VBXEHIUJ.js → chunk-HF6O3O37.js} +6 -88
- package/dist/{chunk-I6DAQMWX.js → chunk-LAZMKTTF.js} +930 -891
- package/dist/{chunk-5EC5MEWX.js → chunk-MAGBIDNS.js} +77 -222
- package/dist/chunk-MBADTM7L.js +64 -0
- package/dist/chunk-OHIK3MIO.js +994 -0
- package/dist/{chunk-6SOIHG6Z.js → chunk-S7DKJPLT.js} +115 -44
- package/dist/{chunk-FMUCXFII.js → chunk-SD6WQY43.js} +1 -5
- package/dist/{chunk-PWLANIRT.js → chunk-TTRFSOKR.js} +1 -7
- package/dist/{chunk-5DRSZLL2.js → chunk-UH3NTO3F.js} +1 -6
- package/dist/{chunk-FFQEQTNW.js → chunk-UIYSCEV7.js} +134 -45
- package/dist/{chunk-3LPHPB62.js → chunk-ZFYPMX46.js} +271 -87
- package/dist/{chunk-7JPAB3T5.js → chunk-ZS5VO5JB.js} +1989 -1283
- package/dist/components.d.ts +6 -6
- package/dist/components.js +57 -267
- package/dist/{database.generated-CzIvgcPu.d.ts → database.generated-CcnC_DRc.d.ts} +4795 -3691
- package/dist/eslint-rules/index.cjs +22 -0
- package/dist/eslint-rules/rules/compliance.cjs +348 -0
- package/dist/eslint-rules/rules/components.cjs +113 -0
- package/dist/eslint-rules/rules/imports.cjs +102 -0
- package/dist/eslint-rules/rules/rbac.cjs +790 -0
- package/dist/eslint-rules/utils/helpers.cjs +42 -0
- package/dist/eslint-rules/utils/manifest-loader.cjs +75 -0
- package/dist/hooks.d.ts +5 -5
- package/dist/hooks.js +62 -270
- package/dist/icons/index.d.ts +1 -0
- package/dist/icons/index.js +1 -0
- package/dist/index.d.ts +36 -26
- package/dist/index.js +87 -690
- package/dist/providers.d.ts +2 -2
- package/dist/providers.js +8 -35
- package/dist/rbac/eslint-rules.d.ts +46 -44
- package/dist/rbac/eslint-rules.js +7 -4
- package/dist/rbac/index.d.ts +124 -594
- package/dist/rbac/index.js +14 -207
- package/dist/styles/index.js +2 -12
- package/dist/theming/runtime.js +3 -19
- package/dist/{timezone-CHhWg6b4.d.ts → timezone-BZe_eUxx.d.ts} +175 -1
- package/dist/{types-CkbwOr4Y.d.ts → types-B-K_5VnO.d.ts} +4 -0
- package/dist/types-t9H8qKRw.d.ts +55 -0
- package/dist/types.d.ts +1 -1
- package/dist/types.js +7 -94
- package/dist/{usePublicRouteParams-i3qtoBgg.d.ts → usePublicRouteParams-COZ28Mvq.d.ts} +9 -9
- package/dist/utils.d.ts +24 -117
- package/dist/utils.js +54 -392
- package/docs/README.md +16 -6
- package/docs/api/README.md +4 -402
- package/docs/api/modules.md +454 -930
- package/docs/api-reference/components.md +3 -1
- package/docs/api-reference/deprecated.md +31 -6
- package/docs/api-reference/rpc-functions.md +78 -3
- package/docs/best-practices/accessibility.md +6 -3
- package/docs/getting-started/cursor-rules.md +3 -23
- package/docs/getting-started/dependencies.md +650 -0
- package/docs/getting-started/installation-guide.md +20 -7
- package/docs/getting-started/quick-start.md +23 -12
- package/docs/implementation-guides/permission-enforcement.md +4 -0
- package/docs/rbac/MIGRATION_GUIDE.md +819 -0
- package/docs/rbac/RBAC_CONTRACT.md +724 -0
- package/docs/rbac/README.md +12 -3
- package/docs/rbac/edge-functions-guide.md +376 -0
- package/docs/rbac/secure-client-protection.md +0 -34
- package/docs/standards/00-pace-core-compliance.md +967 -0
- package/docs/standards/01-standards-compliance.md +188 -0
- package/docs/standards/02-project-structure.md +985 -0
- package/docs/standards/03-solid-principles.md +39 -0
- package/docs/standards/04-testing-standards.md +36 -0
- package/docs/standards/05-bug-reports-and-features.md +27 -0
- package/docs/standards/{04-code-style-standard.md → 06-code-quality.md} +2 -0
- package/docs/standards/07-tech-stack-compliance.md +30 -0
- package/docs/standards/08-markup-quality.md +345 -0
- package/docs/standards/{07-rbac-and-rls-standard.md → 09-rbac-compliance.md} +149 -54
- package/docs/standards/10-error-handling-patterns.md +401 -0
- package/docs/standards/11-performance-optimization.md +348 -0
- package/docs/standards/12-ci-cd-integration.md +370 -0
- package/docs/standards/ALIGNMENT_REVIEW_SUMMARY.md +192 -0
- package/docs/standards/README.md +62 -33
- package/docs/troubleshooting/organisation-context-setup.md +42 -19
- package/eslint-config-pace-core.cjs +20 -4
- package/package.json +31 -21
- package/scripts/audit/audit-compliance.cjs +1295 -0
- package/scripts/audit/audit-components.cjs +260 -0
- package/scripts/audit/audit-dependencies.cjs +395 -0
- package/scripts/audit/audit-rbac.cjs +954 -0
- package/scripts/audit/audit-standards.cjs +1268 -0
- package/scripts/audit/index.cjs +1898 -194
- package/scripts/install-cursor-rules.cjs +259 -8
- package/scripts/validate-master.js +1 -1
- package/src/__tests__/fixtures/supabase.ts +1 -1
- package/src/__tests__/helpers/__tests__/component-test-utils.test.tsx +1 -1
- package/src/__tests__/helpers/__tests__/optimized-test-setup.test.ts +1 -1
- package/src/__tests__/helpers/__tests__/supabaseMock.test.ts +1 -1
- package/src/__tests__/helpers/__tests__/test-utils.test.tsx +3 -3
- package/src/__tests__/helpers/component-test-utils.tsx +1 -1
- package/src/__tests__/helpers/supabaseMock.ts +2 -2
- package/src/__tests__/public-recipe-view.test.ts +38 -9
- package/src/components/Button/Button.tsx +5 -1
- package/src/components/ContextSelector/ContextSelector.tsx +42 -39
- package/src/components/DataTable/__tests__/keyboard.test.tsx +15 -2
- package/src/components/DataTable/components/DataTableBody.tsx +55 -31
- package/src/components/DataTable/components/DataTableCore.tsx +186 -13
- package/src/components/DataTable/components/DataTableLayout.tsx +30 -5
- package/src/components/DataTable/components/EditFields.tsx +23 -3
- package/src/components/DataTable/components/EditableRow.tsx +7 -2
- package/src/components/DataTable/components/ImportModal.tsx +4 -6
- package/src/components/DataTable/components/RowComponent.tsx +12 -0
- package/src/components/DataTable/components/ViewRowModal.tsx +4 -4
- package/src/components/DataTable/components/__tests__/ImportModal.test.tsx +455 -96
- package/src/components/DataTable/components/__tests__/ViewRowModal.test.tsx +122 -58
- package/src/components/DataTable/components/hooks/usePermissionTracking.ts +0 -4
- package/src/components/DataTable/core/DataTableContext.tsx +1 -1
- package/src/components/DataTable/hooks/__tests__/useDataTableState.test.ts +51 -47
- package/src/components/DataTable/hooks/useDataTablePermissions.ts +24 -21
- package/src/components/DataTable/hooks/useDataTableState.ts +125 -9
- package/src/components/DataTable/hooks/useTableColumns.ts +40 -2
- package/src/components/DataTable/hooks/useTableHandlers.ts +11 -0
- package/src/components/DataTable/types.ts +5 -0
- package/src/components/DateTimeField/DateTimeField.tsx +20 -20
- package/src/components/DateTimeField/README.md +5 -2
- package/src/components/Dialog/Dialog.test.tsx +361 -318
- package/src/components/Dialog/Dialog.tsx +1154 -323
- package/src/components/Dialog/index.ts +3 -3
- package/src/components/FileDisplay/FileDisplay.test.tsx +45 -2
- package/src/components/FileDisplay/FileDisplay.tsx +28 -22
- package/src/components/Form/Form.test.tsx +9 -10
- package/src/components/Form/Form.tsx +369 -9
- package/src/components/InactivityWarningModal/InactivityWarningModal.test.tsx +28 -28
- package/src/components/InactivityWarningModal/InactivityWarningModal.tsx +40 -54
- package/src/components/LoginForm/LoginForm.tsx +2 -2
- package/src/components/NavigationMenu/NavigationMenu.test.tsx +14 -13
- package/src/components/NavigationMenu/NavigationMenu.tsx +2 -2
- package/src/components/NavigationMenu/useNavigationFiltering.ts +11 -21
- package/src/components/PaceAppLayout/PaceAppLayout.test.tsx +6 -4
- package/src/components/PaceAppLayout/PaceAppLayout.tsx +30 -41
- package/src/components/PaceAppLayout/README.md +10 -9
- package/src/components/PaceAppLayout/test-setup.tsx +40 -31
- package/src/components/PaceLoginPage/PaceLoginPage.test.tsx +108 -61
- package/src/components/PaceLoginPage/PaceLoginPage.tsx +27 -3
- package/src/components/PasswordChange/PasswordChangeForm.test.tsx +61 -0
- package/src/components/PasswordChange/PasswordChangeForm.tsx +20 -13
- package/src/components/PublicLayout/PublicLayout.test.tsx +7 -3
- package/src/components/PublicLayout/PublicPageLayout.tsx +5 -8
- package/src/components/Select/Select.tsx +23 -21
- package/src/components/Select/types.ts +1 -1
- package/src/components/UserMenu/UserMenu.test.tsx +38 -6
- package/src/components/UserMenu/UserMenu.tsx +39 -34
- package/src/components/index.ts +3 -4
- package/src/eslint-rules/index.cjs +22 -0
- package/src/eslint-rules/rules/compliance.cjs +348 -0
- package/src/eslint-rules/rules/components.cjs +113 -0
- package/src/eslint-rules/rules/imports.cjs +102 -0
- package/src/eslint-rules/rules/rbac.cjs +790 -0
- package/src/eslint-rules/utils/helpers.cjs +42 -0
- package/src/eslint-rules/utils/manifest-loader.cjs +75 -0
- package/src/hooks/__tests__/hooks.integration.test.tsx +6 -8
- package/src/hooks/__tests__/useAppConfig.unit.test.ts +129 -67
- package/src/hooks/__tests__/usePublicEvent.simple.test.ts +149 -67
- package/src/hooks/__tests__/usePublicEvent.test.ts +149 -79
- package/src/hooks/__tests__/usePublicEvent.unit.test.ts +158 -109
- package/src/hooks/__tests__/useSessionDraft.test.ts +163 -0
- package/src/hooks/__tests__/useSessionRestoration.unit.test.tsx +10 -5
- package/src/hooks/public/usePublicEvent.ts +62 -190
- package/src/hooks/public/usePublicEventLogo.test.ts +70 -17
- package/src/hooks/public/usePublicEventLogo.ts +19 -9
- package/src/hooks/useAppConfig.ts +26 -24
- package/src/hooks/useEventTheme.test.ts +211 -233
- package/src/hooks/useEventTheme.ts +19 -28
- package/src/hooks/useEvents.ts +11 -7
- package/src/hooks/useKeyboardShortcuts.ts +1 -1
- package/src/hooks/useOrganisationPermissions.ts +9 -11
- package/src/hooks/useOrganisations.ts +13 -7
- package/src/hooks/useQueryCache.ts +0 -1
- package/src/hooks/useSessionDraft.ts +380 -0
- package/src/hooks/useSessionRestoration.ts +3 -1
- package/src/icons/index.ts +27 -0
- package/src/index.ts +16 -1
- package/src/providers/OrganisationProvider.tsx +23 -14
- package/src/providers/services/EventServiceProvider.tsx +1 -24
- package/src/providers/services/UnifiedAuthProvider.tsx +5 -48
- package/src/providers/services/__tests__/UnifiedAuthProvider.integration.test.tsx +3 -0
- package/src/rbac/README.md +20 -20
- package/src/rbac/__tests__/adapters.comprehensive.test.tsx +7 -457
- package/src/rbac/__tests__/auth-rbac.e2e.test.tsx +33 -7
- package/src/rbac/adapters.tsx +7 -295
- package/src/rbac/api.test.ts +44 -56
- package/src/rbac/api.ts +10 -17
- package/src/rbac/cache-invalidation.ts +0 -1
- package/src/rbac/compliance/index.ts +10 -0
- package/src/rbac/compliance/pattern-detector.ts +553 -0
- package/src/rbac/compliance/runtime-compliance.ts +22 -0
- package/src/rbac/components/AccessDenied.tsx +150 -0
- package/src/rbac/components/NavigationGuard.tsx +12 -20
- package/src/rbac/components/PagePermissionGuard.tsx +4 -24
- package/src/rbac/components/__tests__/NavigationGuard.test.tsx +21 -8
- package/src/rbac/components/index.ts +3 -41
- package/src/rbac/eslint-rules.js +1 -1
- package/src/rbac/hooks/index.ts +0 -3
- package/src/rbac/hooks/permissions/index.ts +0 -3
- package/src/rbac/hooks/permissions/useAccessLevel.ts +4 -8
- package/src/rbac/hooks/usePermissions.ts +0 -3
- package/src/rbac/hooks/useRBAC.test.ts +21 -3
- package/src/rbac/hooks/useRBAC.ts +4 -3
- package/src/rbac/hooks/useResolvedScope.test.ts +57 -47
- package/src/rbac/hooks/useResolvedScope.ts +58 -140
- package/src/rbac/hooks/useResourcePermissions.test.ts +241 -60
- package/src/rbac/hooks/useResourcePermissions.ts +182 -63
- package/src/rbac/hooks/useRoleManagement.test.ts +65 -22
- package/src/rbac/hooks/useRoleManagement.ts +147 -19
- package/src/rbac/hooks/useSecureSupabase.ts +4 -8
- package/src/rbac/index.ts +7 -9
- package/src/rbac/permissions.ts +17 -17
- package/src/rbac/utils/contextValidator.ts +45 -7
- package/src/services/AuthService.ts +132 -23
- package/src/services/EventService.ts +4 -97
- package/src/services/InactivityService.ts +155 -58
- package/src/services/OrganisationService.ts +7 -44
- package/src/services/__tests__/OrganisationService.test.ts +26 -8
- package/src/services/base/BaseService.ts +0 -3
- package/src/styles/core.css +4 -0
- package/src/types/database.generated.ts +4733 -3809
- package/src/utils/__tests__/organisationContext.unit.test.ts +9 -10
- package/src/utils/context/organisationContext.test.ts +13 -28
- package/src/utils/context/organisationContext.ts +21 -52
- package/src/utils/dynamic/dynamicUtils.ts +1 -1
- package/src/utils/file-reference/index.ts +39 -15
- package/src/utils/formatting/formatDateTime.test.ts +3 -2
- package/src/utils/formatting/formatTime.test.ts +3 -2
- package/src/utils/google-places/loadGoogleMapsScript.ts +29 -4
- package/src/utils/index.ts +4 -1
- package/src/utils/persistence/__tests__/keyDerivation.test.ts +135 -0
- package/src/utils/persistence/__tests__/sensitiveFieldDetection.test.ts +123 -0
- package/src/utils/persistence/keyDerivation.ts +304 -0
- package/src/utils/persistence/sensitiveFieldDetection.ts +212 -0
- package/src/utils/security/secureStorage.ts +5 -5
- package/src/utils/storage/helpers.ts +3 -3
- package/src/utils/supabase/createBaseClient.ts +147 -0
- package/src/utils/timezone/timezone.test.ts +1 -2
- package/src/utils/timezone/timezone.ts +1 -1
- package/src/utils/validation/csrf.ts +4 -4
- package/cursor-rules/CHANGELOG.md +0 -119
- package/cursor-rules/README.md +0 -192
- package/dist/DataTable-E7YQZD7D.js +0 -175
- package/dist/DataTable-E7YQZD7D.js.map +0 -1
- package/dist/UnifiedAuthProvider-QPXO24B4.js +0 -18
- package/dist/UnifiedAuthProvider-QPXO24B4.js.map +0 -1
- package/dist/api-6LVZTHDS.js +0 -52
- package/dist/api-6LVZTHDS.js.map +0 -1
- package/dist/audit-V53FV5AG.js +0 -17
- package/dist/audit-V53FV5AG.js.map +0 -1
- package/dist/chunk-36LVWXB2.js +0 -227
- package/dist/chunk-36LVWXB2.js.map +0 -1
- package/dist/chunk-3LPHPB62.js.map +0 -1
- package/dist/chunk-5DRSZLL2.js.map +0 -1
- package/dist/chunk-5EC5MEWX.js.map +0 -1
- package/dist/chunk-63FOKYGO.js.map +0 -1
- package/dist/chunk-6SOIHG6Z.js.map +0 -1
- package/dist/chunk-7JPAB3T5.js.map +0 -1
- package/dist/chunk-ATKZM7RX.js +0 -2053
- package/dist/chunk-ATKZM7RX.js.map +0 -1
- package/dist/chunk-AVMLPIM7.js.map +0 -1
- package/dist/chunk-DGUM43GV.js.map +0 -1
- package/dist/chunk-E66EQZE6.js.map +0 -1
- package/dist/chunk-FFQEQTNW.js.map +0 -1
- package/dist/chunk-FMUCXFII.js.map +0 -1
- package/dist/chunk-G37KK66H.js.map +0 -1
- package/dist/chunk-I6DAQMWX.js.map +0 -1
- package/dist/chunk-J36DSWQK.js.map +0 -1
- package/dist/chunk-KQCRWDSA.js +0 -1
- package/dist/chunk-KQCRWDSA.js.map +0 -1
- package/dist/chunk-L4OXEN46.js.map +0 -1
- package/dist/chunk-LMC26NLJ.js +0 -84
- package/dist/chunk-LMC26NLJ.js.map +0 -1
- package/dist/chunk-M43Y4SSO.js.map +0 -1
- package/dist/chunk-M7MPQISP.js.map +0 -1
- package/dist/chunk-NN6WWZ5U.js.map +0 -1
- package/dist/chunk-OEWDTMG7.js.map +0 -1
- package/dist/chunk-PWLANIRT.js.map +0 -1
- package/dist/chunk-QXHPKYJV.js.map +0 -1
- package/dist/chunk-VBXEHIUJ.js.map +0 -1
- package/dist/chunk-YKRAFF5K.js.map +0 -1
- package/dist/chunk-ZSAAAMVR.js.map +0 -1
- package/dist/components.js.map +0 -1
- package/dist/contextValidator-OOPCLPZW.js +0 -9
- package/dist/contextValidator-OOPCLPZW.js.map +0 -1
- package/dist/eslint-rules/pace-core-compliance.cjs +0 -510
- package/dist/hooks.js.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/providers.js.map +0 -1
- package/dist/rbac/eslint-rules.js.map +0 -1
- package/dist/rbac/index.js.map +0 -1
- package/dist/styles/index.js.map +0 -1
- package/dist/theming/runtime.js.map +0 -1
- package/dist/types.js.map +0 -1
- package/dist/utils.js.map +0 -1
- package/docs/standards/01-architecture-standard.md +0 -44
- package/docs/standards/02-api-and-rpc-standard.md +0 -39
- package/docs/standards/03-component-standard.md +0 -32
- package/docs/standards/05-security-standard.md +0 -44
- package/docs/standards/06-testing-and-docs-standard.md +0 -29
- package/docs/standards/pace-core-compliance.md +0 -432
- package/scripts/audit/core/checks/accessibility.cjs +0 -197
- package/scripts/audit/core/checks/api-usage.cjs +0 -191
- package/scripts/audit/core/checks/bundle.cjs +0 -142
- package/scripts/audit/core/checks/compliance.cjs +0 -2706
- package/scripts/audit/core/checks/config.cjs +0 -54
- package/scripts/audit/core/checks/coverage.cjs +0 -84
- package/scripts/audit/core/checks/dependencies.cjs +0 -994
- package/scripts/audit/core/checks/documentation.cjs +0 -268
- package/scripts/audit/core/checks/environment.cjs +0 -116
- package/scripts/audit/core/checks/error-handling.cjs +0 -340
- package/scripts/audit/core/checks/forms.cjs +0 -172
- package/scripts/audit/core/checks/heuristics.cjs +0 -68
- package/scripts/audit/core/checks/hooks.cjs +0 -334
- package/scripts/audit/core/checks/imports.cjs +0 -244
- package/scripts/audit/core/checks/performance.cjs +0 -325
- package/scripts/audit/core/checks/routes.cjs +0 -117
- package/scripts/audit/core/checks/state.cjs +0 -130
- package/scripts/audit/core/checks/structure.cjs +0 -65
- package/scripts/audit/core/checks/style.cjs +0 -584
- package/scripts/audit/core/checks/testing.cjs +0 -122
- package/scripts/audit/core/checks/typescript.cjs +0 -61
- package/scripts/audit/core/scanner.cjs +0 -199
- package/scripts/audit/core/utils.cjs +0 -137
- package/scripts/audit/reporters/console.cjs +0 -151
- package/scripts/audit/reporters/json.cjs +0 -54
- package/scripts/audit/reporters/markdown.cjs +0 -124
- package/scripts/audit-consuming-app.cjs +0 -86
- package/src/eslint-rules/pace-core-compliance.cjs +0 -510
- package/src/eslint-rules/pace-core-compliance.js +0 -638
- package/src/rbac/components/EnhancedNavigationMenu.test.tsx +0 -555
- package/src/rbac/components/EnhancedNavigationMenu.tsx +0 -293
- package/src/rbac/components/NavigationProvider.test.tsx +0 -481
- package/src/rbac/components/NavigationProvider.tsx +0 -345
- package/src/rbac/components/PagePermissionProvider.test.tsx +0 -476
- package/src/rbac/components/PagePermissionProvider.tsx +0 -279
- package/src/rbac/components/PermissionEnforcer.tsx +0 -312
- package/src/rbac/components/RoleBasedRouter.tsx +0 -440
- package/src/rbac/components/SecureDataProvider.test.tsx +0 -543
- package/src/rbac/components/SecureDataProvider.tsx +0 -339
- package/src/rbac/components/__tests__/EnhancedNavigationMenu.test.tsx +0 -620
- package/src/rbac/components/__tests__/NavigationProvider.test.tsx +0 -726
- package/src/rbac/components/__tests__/PagePermissionProvider.test.tsx +0 -661
- package/src/rbac/components/__tests__/PermissionEnforcer.test.tsx +0 -881
- package/src/rbac/components/__tests__/RoleBasedRouter.test.tsx +0 -783
- package/src/rbac/components/__tests__/SecureDataProvider.fixed.test.tsx +0 -645
- package/src/rbac/components/__tests__/SecureDataProvider.test.tsx +0 -659
- package/src/rbac/hooks/permissions/useCachedPermissions.ts +0 -79
- package/src/rbac/hooks/permissions/useHasAllPermissions.ts +0 -90
- package/src/rbac/hooks/permissions/useHasAnyPermission.ts +0 -90
|
@@ -0,0 +1,348 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* General compliance rules
|
|
3
|
+
* @package @jmruthers/pace-core
|
|
4
|
+
* @module ESLintRules/rules/compliance
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const path = require('path');
|
|
8
|
+
const { getPaceCoreComponents, getPaceCoreHooks, getPaceCoreUtils } = require('../utils/manifest-loader.cjs');
|
|
9
|
+
const { isPaceCoreSourceFile } = require('../utils/helpers.cjs');
|
|
10
|
+
|
|
11
|
+
module.exports = {
|
|
12
|
+
rules: {
|
|
13
|
+
/**
|
|
14
|
+
* Prefer pace-core components over native HTML elements or custom implementations
|
|
15
|
+
*/
|
|
16
|
+
'prefer-pace-core-components': {
|
|
17
|
+
meta: {
|
|
18
|
+
type: 'suggestion',
|
|
19
|
+
docs: {
|
|
20
|
+
description: 'Suggest using pace-core components instead of native HTML elements',
|
|
21
|
+
category: 'Best Practices',
|
|
22
|
+
recommended: true
|
|
23
|
+
},
|
|
24
|
+
hasSuggestions: true,
|
|
25
|
+
messages: {
|
|
26
|
+
preferButton: "Use 'Button' component from '@jmruthers/pace-core' instead of <button>",
|
|
27
|
+
preferInput: "Use 'Input' component from '@jmruthers/pace-core' instead of <input>",
|
|
28
|
+
preferTextarea: "Use 'Textarea' component from '@jmruthers/pace-core' instead of <textarea>",
|
|
29
|
+
preferLabel: "Use 'Label' component from '@jmruthers/pace-core' instead of <label>",
|
|
30
|
+
preferForm: "Use 'Form' component from '@jmruthers/pace-core' instead of custom form implementation"
|
|
31
|
+
}
|
|
32
|
+
},
|
|
33
|
+
create(context) {
|
|
34
|
+
const filename = context.getFilename();
|
|
35
|
+
|
|
36
|
+
// Exclude pace-core source files - these rules are for consuming apps
|
|
37
|
+
if (isPaceCoreSourceFile(filename)) {
|
|
38
|
+
return {};
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const paceCoreComponents = getPaceCoreComponents();
|
|
42
|
+
|
|
43
|
+
return {
|
|
44
|
+
JSXOpeningElement(node) {
|
|
45
|
+
const elementName = node.name.name;
|
|
46
|
+
|
|
47
|
+
if (!elementName) return;
|
|
48
|
+
|
|
49
|
+
// Only flag lowercase native HTML elements, not capitalized components
|
|
50
|
+
// Native HTML: <button>, <input>, <label>, <textarea>
|
|
51
|
+
// Components: <Button>, <Input>, <Label>, <Textarea>
|
|
52
|
+
const isNativeHTMLElement = elementName === elementName.toLowerCase();
|
|
53
|
+
|
|
54
|
+
if (!isNativeHTMLElement) {
|
|
55
|
+
// This is a capitalized component, not a native HTML element
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Check for native HTML elements that have pace-core alternatives
|
|
60
|
+
const nativeToPaceCore = {
|
|
61
|
+
'button': 'Button',
|
|
62
|
+
'input': 'Input',
|
|
63
|
+
'textarea': 'Textarea',
|
|
64
|
+
'label': 'Label'
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
if (nativeToPaceCore[elementName]) {
|
|
68
|
+
const paceCoreComponent = nativeToPaceCore[elementName];
|
|
69
|
+
if (paceCoreComponents.includes(paceCoreComponent)) {
|
|
70
|
+
context.report({
|
|
71
|
+
node,
|
|
72
|
+
messageId: `prefer${paceCoreComponent}`,
|
|
73
|
+
suggest: [{
|
|
74
|
+
desc: `Import and use ${paceCoreComponent} from pace-core`,
|
|
75
|
+
fix(fixer) {
|
|
76
|
+
// This is a complex fix, so we'll just report
|
|
77
|
+
return null;
|
|
78
|
+
}
|
|
79
|
+
}]
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
},
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Detect custom hooks that duplicate pace-core functionality
|
|
90
|
+
*/
|
|
91
|
+
'prefer-pace-core-hooks': {
|
|
92
|
+
meta: {
|
|
93
|
+
type: 'suggestion',
|
|
94
|
+
docs: {
|
|
95
|
+
description: 'Suggest using pace-core hooks instead of custom implementations',
|
|
96
|
+
category: 'Best Practices',
|
|
97
|
+
recommended: true
|
|
98
|
+
},
|
|
99
|
+
messages: {
|
|
100
|
+
preferPaceCoreHook: "Consider using '{{hook}}' from '@jmruthers/pace-core' instead of custom hook '{{customHook}}'"
|
|
101
|
+
}
|
|
102
|
+
},
|
|
103
|
+
create(context) {
|
|
104
|
+
const filename = context.getFilename();
|
|
105
|
+
|
|
106
|
+
// Exclude pace-core source files - these rules are for consuming apps
|
|
107
|
+
if (isPaceCoreSourceFile(filename)) {
|
|
108
|
+
return {};
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
const paceCoreHooks = getPaceCoreHooks();
|
|
112
|
+
const hookPatterns = {
|
|
113
|
+
'useToast': ['useToast', 'useNotification', 'useSnackbar'],
|
|
114
|
+
'useDebounce': ['useDebounce', 'useDebounced'],
|
|
115
|
+
'useAuth': ['useAuth', 'useAuthentication', 'useUser'],
|
|
116
|
+
'useFile': ['useFile', 'useFileUpload', 'useFileReference'],
|
|
117
|
+
'useForm': ['useForm', 'useZodForm'],
|
|
118
|
+
'useTable': ['useTable', 'useDataTable']
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
return {
|
|
122
|
+
FunctionDeclaration(node) {
|
|
123
|
+
const functionName = node.id?.name;
|
|
124
|
+
if (!functionName || !functionName.startsWith('use')) return;
|
|
125
|
+
|
|
126
|
+
// Check if this looks like a hook that pace-core provides
|
|
127
|
+
for (const [paceCoreHook, patterns] of Object.entries(hookPatterns)) {
|
|
128
|
+
if (paceCoreHooks.includes(paceCoreHook)) {
|
|
129
|
+
for (const pattern of patterns) {
|
|
130
|
+
if (functionName.toLowerCase().includes(pattern.toLowerCase().replace('use', ''))) {
|
|
131
|
+
context.report({
|
|
132
|
+
node: node.id,
|
|
133
|
+
messageId: 'preferPaceCoreHook',
|
|
134
|
+
data: {
|
|
135
|
+
hook: paceCoreHook,
|
|
136
|
+
customHook: functionName
|
|
137
|
+
}
|
|
138
|
+
});
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
},
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Detect utility functions that duplicate pace-core functionality
|
|
151
|
+
*/
|
|
152
|
+
'prefer-pace-core-utils': {
|
|
153
|
+
meta: {
|
|
154
|
+
type: 'suggestion',
|
|
155
|
+
docs: {
|
|
156
|
+
description: 'Suggest using pace-core utilities instead of custom implementations',
|
|
157
|
+
category: 'Best Practices',
|
|
158
|
+
recommended: true
|
|
159
|
+
},
|
|
160
|
+
messages: {
|
|
161
|
+
preferPaceCoreUtil: "Consider using '{{util}}' from '@jmruthers/pace-core' instead of custom function '{{customUtil}}'"
|
|
162
|
+
}
|
|
163
|
+
},
|
|
164
|
+
create(context) {
|
|
165
|
+
const filename = context.getFilename();
|
|
166
|
+
|
|
167
|
+
// Exclude pace-core source files - these rules are for consuming apps
|
|
168
|
+
if (isPaceCoreSourceFile(filename)) {
|
|
169
|
+
return {};
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
const paceCoreUtils = getPaceCoreUtils();
|
|
173
|
+
const utilPatterns = {
|
|
174
|
+
'formatDate': ['formatDate', 'formatDateTime', 'dateFormat'],
|
|
175
|
+
'formatCurrency': ['formatCurrency', 'formatMoney', 'currencyFormat'],
|
|
176
|
+
'formatNumber': ['formatNumber', 'numberFormat'],
|
|
177
|
+
'cn': ['cn', 'classNames', 'clsx', 'mergeClasses'],
|
|
178
|
+
'validateUserInput': ['validateInput', 'validateUser', 'validateUserInput'],
|
|
179
|
+
'sanitizeUserInput': ['sanitize', 'sanitizeInput', 'sanitizeUser']
|
|
180
|
+
};
|
|
181
|
+
|
|
182
|
+
// Patterns that should NOT trigger validateUserInput warnings
|
|
183
|
+
// These are for API/config validation, not user input validation
|
|
184
|
+
const excludePatterns = [
|
|
185
|
+
'validateApi',
|
|
186
|
+
'validateRequest',
|
|
187
|
+
'validateConfig',
|
|
188
|
+
'validateKey',
|
|
189
|
+
'validateToken',
|
|
190
|
+
'validateAuth',
|
|
191
|
+
'validateRateLimit',
|
|
192
|
+
'validatePermission',
|
|
193
|
+
'validateAccess'
|
|
194
|
+
];
|
|
195
|
+
|
|
196
|
+
return {
|
|
197
|
+
FunctionDeclaration(node) {
|
|
198
|
+
const functionName = node.id?.name;
|
|
199
|
+
if (!functionName) return;
|
|
200
|
+
|
|
201
|
+
// Skip if function name matches exclude patterns (API/config validation, not user input)
|
|
202
|
+
const lowerName = functionName.toLowerCase();
|
|
203
|
+
if (excludePatterns.some(pattern => lowerName.includes(pattern.toLowerCase()))) {
|
|
204
|
+
return;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// Check if this looks like a util that pace-core provides
|
|
208
|
+
for (const [paceCoreUtil, patterns] of Object.entries(utilPatterns)) {
|
|
209
|
+
if (paceCoreUtils.includes(paceCoreUtil)) {
|
|
210
|
+
for (const pattern of patterns) {
|
|
211
|
+
// Only match if the pattern is a significant part of the function name
|
|
212
|
+
// Don't match generic "validate" - it's too broad
|
|
213
|
+
if (paceCoreUtil === 'validateUserInput' && pattern === 'validate') {
|
|
214
|
+
// Skip generic "validate" - too many false positives
|
|
215
|
+
continue;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
if (functionName.toLowerCase().includes(pattern.toLowerCase())) {
|
|
219
|
+
context.report({
|
|
220
|
+
node: node.id,
|
|
221
|
+
messageId: 'preferPaceCoreUtil',
|
|
222
|
+
data: {
|
|
223
|
+
util: paceCoreUtil,
|
|
224
|
+
customUtil: functionName
|
|
225
|
+
}
|
|
226
|
+
});
|
|
227
|
+
return;
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
},
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* Detect component files with names matching pace-core components
|
|
239
|
+
*/
|
|
240
|
+
'no-local-component-duplication': {
|
|
241
|
+
meta: {
|
|
242
|
+
type: 'problem',
|
|
243
|
+
docs: {
|
|
244
|
+
description: 'Disallow local components with names matching pace-core components',
|
|
245
|
+
category: 'Best Practices',
|
|
246
|
+
recommended: true
|
|
247
|
+
},
|
|
248
|
+
messages: {
|
|
249
|
+
duplicateComponent: "Component '{{componentName}}' conflicts with pace-core component. Use '@jmruthers/pace-core' instead of creating a local version."
|
|
250
|
+
}
|
|
251
|
+
},
|
|
252
|
+
create(context) {
|
|
253
|
+
const filename = context.getFilename();
|
|
254
|
+
|
|
255
|
+
// Exclude pace-core source files - these rules are for consuming apps
|
|
256
|
+
if (isPaceCoreSourceFile(filename)) {
|
|
257
|
+
return {};
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
const paceCoreComponents = getPaceCoreComponents();
|
|
261
|
+
|
|
262
|
+
// Only check component files (components/, src/components/, etc.)
|
|
263
|
+
if (!filename.match(/(components|Components)\//)) {
|
|
264
|
+
return {};
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
// Extract component name from filename
|
|
268
|
+
const basename = path.basename(filename, path.extname(filename));
|
|
269
|
+
const componentName = basename.replace(/\.(test|spec)$/, '');
|
|
270
|
+
|
|
271
|
+
return {
|
|
272
|
+
Program(node) {
|
|
273
|
+
// Check if this file exports a component with a name matching pace-core
|
|
274
|
+
if (paceCoreComponents.includes(componentName)) {
|
|
275
|
+
// Check if file exports this component
|
|
276
|
+
const hasExport = node.body.some(stmt => {
|
|
277
|
+
if (stmt.type === 'ExportNamedDeclaration') {
|
|
278
|
+
return stmt.declaration?.id?.name === componentName ||
|
|
279
|
+
stmt.specifiers?.some(spec => spec.exported.name === componentName);
|
|
280
|
+
}
|
|
281
|
+
if (stmt.type === 'ExportDefaultDeclaration') {
|
|
282
|
+
return stmt.declaration?.id?.name === componentName ||
|
|
283
|
+
stmt.declaration?.name === componentName;
|
|
284
|
+
}
|
|
285
|
+
return false;
|
|
286
|
+
});
|
|
287
|
+
|
|
288
|
+
if (hasExport) {
|
|
289
|
+
context.report({
|
|
290
|
+
node,
|
|
291
|
+
messageId: 'duplicateComponent',
|
|
292
|
+
data: {
|
|
293
|
+
componentName
|
|
294
|
+
}
|
|
295
|
+
});
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
};
|
|
300
|
+
}
|
|
301
|
+
},
|
|
302
|
+
|
|
303
|
+
/**
|
|
304
|
+
* Disallow inline styles
|
|
305
|
+
*/
|
|
306
|
+
'no-inline-styles': {
|
|
307
|
+
meta: {
|
|
308
|
+
type: 'problem',
|
|
309
|
+
docs: {
|
|
310
|
+
description: 'Disallow inline styles. Use pace-core components or Tailwind classes instead.',
|
|
311
|
+
category: 'Best Practices',
|
|
312
|
+
recommended: true
|
|
313
|
+
},
|
|
314
|
+
messages: {
|
|
315
|
+
inlineStyle: 'Inline style detected. Use pace-core components or Tailwind classes instead.'
|
|
316
|
+
},
|
|
317
|
+
hasSuggestions: true
|
|
318
|
+
},
|
|
319
|
+
create(context) {
|
|
320
|
+
const filename = context.getFilename();
|
|
321
|
+
|
|
322
|
+
// Exclude pace-core source files - these rules are for consuming apps
|
|
323
|
+
if (isPaceCoreSourceFile(filename)) {
|
|
324
|
+
return {};
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
return {
|
|
328
|
+
JSXAttribute(node) {
|
|
329
|
+
if (node.name && node.name.name === 'style') {
|
|
330
|
+
context.report({
|
|
331
|
+
node,
|
|
332
|
+
messageId: 'inlineStyle',
|
|
333
|
+
suggest: [{
|
|
334
|
+
desc: 'Remove inline style and use pace-core component styling or Tailwind utility classes',
|
|
335
|
+
fix(fixer) {
|
|
336
|
+
// Remove the style attribute
|
|
337
|
+
return fixer.remove(node);
|
|
338
|
+
}
|
|
339
|
+
}]
|
|
340
|
+
});
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
};
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
};
|
|
348
|
+
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Component usage rules
|
|
3
|
+
* @package @jmruthers/pace-core
|
|
4
|
+
* @module ESLintRules/rules/components
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const { isPaceCoreSourceFile } = require('../utils/helpers.cjs');
|
|
8
|
+
|
|
9
|
+
module.exports = {
|
|
10
|
+
rules: {
|
|
11
|
+
/**
|
|
12
|
+
* Prefer pace-core Form component over plain form tags and direct react-hook-form usage
|
|
13
|
+
*/
|
|
14
|
+
'prefer-pace-core-form': {
|
|
15
|
+
meta: {
|
|
16
|
+
type: 'problem',
|
|
17
|
+
docs: {
|
|
18
|
+
description: 'Disallow plain <form> tags and direct react-hook-form imports. Use pace-core Form component instead.',
|
|
19
|
+
category: 'Best Practices',
|
|
20
|
+
recommended: true
|
|
21
|
+
},
|
|
22
|
+
messages: {
|
|
23
|
+
plainFormTag: "Plain <form> tag detected. Use pace-core Form component instead.",
|
|
24
|
+
reactHookFormImport: "Direct import from 'react-hook-form' detected: {{imports}}. Use pace-core Form component instead. useFormContext is allowed when Form is imported from pace-core."
|
|
25
|
+
},
|
|
26
|
+
hasSuggestions: true
|
|
27
|
+
},
|
|
28
|
+
create(context) {
|
|
29
|
+
const filename = context.getFilename();
|
|
30
|
+
|
|
31
|
+
// Exclude pace-core source files - these rules are for consuming apps
|
|
32
|
+
if (isPaceCoreSourceFile(filename)) {
|
|
33
|
+
return {};
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
let hasPaceCoreForm = false;
|
|
37
|
+
const problematicImports = ['useForm', 'FormProvider', 'Controller', 'useWatch', 'useFieldArray', 'useController'];
|
|
38
|
+
|
|
39
|
+
return {
|
|
40
|
+
ImportDeclaration(node) {
|
|
41
|
+
const importSource = node.source.value;
|
|
42
|
+
|
|
43
|
+
// Check if Form is imported from pace-core
|
|
44
|
+
if (importSource === '@jmruthers/pace-core' ||
|
|
45
|
+
importSource === '@jmruthers/pace-core/components') {
|
|
46
|
+
const hasForm = node.specifiers.some(spec => {
|
|
47
|
+
if (spec.type === 'ImportSpecifier') {
|
|
48
|
+
return spec.imported.name === 'Form';
|
|
49
|
+
}
|
|
50
|
+
return false;
|
|
51
|
+
});
|
|
52
|
+
if (hasForm) {
|
|
53
|
+
hasPaceCoreForm = true;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Check for react-hook-form imports
|
|
58
|
+
if (importSource === 'react-hook-form') {
|
|
59
|
+
const importedItems = node.specifiers
|
|
60
|
+
.filter(spec => spec.type === 'ImportSpecifier')
|
|
61
|
+
.map(spec => spec.imported.name);
|
|
62
|
+
|
|
63
|
+
const foundProblematic = importedItems.filter(item => problematicImports.includes(item));
|
|
64
|
+
|
|
65
|
+
// Allow useFormContext when Form is imported from pace-core
|
|
66
|
+
const isAcceptable = hasPaceCoreForm &&
|
|
67
|
+
foundProblematic.length === 1 &&
|
|
68
|
+
foundProblematic[0] === 'useFormContext';
|
|
69
|
+
|
|
70
|
+
if (foundProblematic.length > 0 && !isAcceptable) {
|
|
71
|
+
context.report({
|
|
72
|
+
node: node.source,
|
|
73
|
+
messageId: 'reactHookFormImport',
|
|
74
|
+
data: {
|
|
75
|
+
imports: foundProblematic.join(', ')
|
|
76
|
+
},
|
|
77
|
+
suggest: [{
|
|
78
|
+
desc: 'Use pace-core Form component instead',
|
|
79
|
+
fix(fixer) {
|
|
80
|
+
return fixer.remove(node);
|
|
81
|
+
}
|
|
82
|
+
}]
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
},
|
|
87
|
+
|
|
88
|
+
JSXOpeningElement(node) {
|
|
89
|
+
// Check for plain <form> tags (lowercase, not Form component)
|
|
90
|
+
if (node.name.type === 'JSXIdentifier') {
|
|
91
|
+
const elementName = node.name.name;
|
|
92
|
+
// Check if it's lowercase 'form' (not 'Form' component)
|
|
93
|
+
if (elementName === 'form') {
|
|
94
|
+
context.report({
|
|
95
|
+
node,
|
|
96
|
+
messageId: 'plainFormTag',
|
|
97
|
+
suggest: [{
|
|
98
|
+
desc: 'Replace with pace-core Form component',
|
|
99
|
+
fix(fixer) {
|
|
100
|
+
// This is complex to auto-fix, so we'll just report
|
|
101
|
+
return null;
|
|
102
|
+
}
|
|
103
|
+
}]
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
};
|
|
113
|
+
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Import restriction rules
|
|
3
|
+
* @package @jmruthers/pace-core
|
|
4
|
+
* @module ESLintRules/rules/imports
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const { getRestrictedImports } = require('../utils/manifest-loader.cjs');
|
|
8
|
+
const { getPaceCoreAlternative, isPaceCoreSourceFile } = require('../utils/helpers.cjs');
|
|
9
|
+
|
|
10
|
+
module.exports = {
|
|
11
|
+
rules: {
|
|
12
|
+
/**
|
|
13
|
+
* Block direct imports of libraries wrapped by pace-core
|
|
14
|
+
*/
|
|
15
|
+
'no-restricted-imports': {
|
|
16
|
+
meta: {
|
|
17
|
+
type: 'problem',
|
|
18
|
+
docs: {
|
|
19
|
+
description: 'Disallow direct imports of libraries that pace-core wraps',
|
|
20
|
+
category: 'Best Practices',
|
|
21
|
+
recommended: true
|
|
22
|
+
},
|
|
23
|
+
fixable: 'code',
|
|
24
|
+
hasSuggestions: true,
|
|
25
|
+
messages: {
|
|
26
|
+
restrictedImport: '{{message}} Import from {{alternative}} instead.',
|
|
27
|
+
restrictedImportWithReason: '{{message}} {{reason}}'
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
create(context) {
|
|
31
|
+
const filename = context.getFilename();
|
|
32
|
+
|
|
33
|
+
// Exclude pace-core source files - these rules are for consuming apps
|
|
34
|
+
if (isPaceCoreSourceFile(filename)) {
|
|
35
|
+
return {};
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const restrictedImports = getRestrictedImports();
|
|
39
|
+
const restrictedModules = restrictedImports.map(imp => imp.module);
|
|
40
|
+
|
|
41
|
+
// Also catch @radix-ui/* patterns
|
|
42
|
+
const radixPattern = /^@radix-ui\//;
|
|
43
|
+
|
|
44
|
+
return {
|
|
45
|
+
ImportDeclaration(node) {
|
|
46
|
+
const importSource = node.source.value;
|
|
47
|
+
|
|
48
|
+
// Check exact matches
|
|
49
|
+
const restricted = restrictedImports.find(imp => imp.module === importSource);
|
|
50
|
+
if (restricted) {
|
|
51
|
+
context.report({
|
|
52
|
+
node: node.source,
|
|
53
|
+
messageId: 'restrictedImportWithReason',
|
|
54
|
+
data: {
|
|
55
|
+
message: `Direct import of '${importSource}' is not allowed.`,
|
|
56
|
+
reason: restricted.reason
|
|
57
|
+
},
|
|
58
|
+
suggest: [{
|
|
59
|
+
desc: `Use pace-core alternative: ${restricted.reason}`,
|
|
60
|
+
fix(fixer) {
|
|
61
|
+
// Suggest importing from pace-core instead
|
|
62
|
+
const paceCoreAlternative = getPaceCoreAlternative(importSource);
|
|
63
|
+
if (paceCoreAlternative) {
|
|
64
|
+
return fixer.replaceText(
|
|
65
|
+
node.source,
|
|
66
|
+
`'@jmruthers/pace-core${paceCoreAlternative}'`
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
return null;
|
|
70
|
+
}
|
|
71
|
+
}]
|
|
72
|
+
});
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Check @radix-ui/* pattern
|
|
77
|
+
if (radixPattern.test(importSource) && !restrictedModules.includes(importSource)) {
|
|
78
|
+
context.report({
|
|
79
|
+
node: node.source,
|
|
80
|
+
messageId: 'restrictedImport',
|
|
81
|
+
data: {
|
|
82
|
+
message: `Direct import of '${importSource}' is not allowed.`,
|
|
83
|
+
alternative: '@jmruthers/pace-core'
|
|
84
|
+
},
|
|
85
|
+
suggest: [{
|
|
86
|
+
desc: 'Use pace-core component instead',
|
|
87
|
+
fix(fixer) {
|
|
88
|
+
return fixer.replaceText(
|
|
89
|
+
node.source,
|
|
90
|
+
"'@jmruthers/pace-core'"
|
|
91
|
+
);
|
|
92
|
+
}
|
|
93
|
+
}]
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
};
|
|
102
|
+
|