@jmruthers/pace-core 0.6.6 → 0.6.8
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/{scripts/audit/audit-dependencies.cjs → audit-tool/00-dependencies.cjs} +227 -22
- package/audit-tool/audits/01-pace-core-compliance.cjs +556 -0
- package/audit-tool/audits/02-project-structure.cjs +240 -0
- package/audit-tool/audits/03-architecture.cjs +224 -0
- package/audit-tool/audits/04-code-quality.cjs +149 -0
- package/audit-tool/audits/05-styling.cjs +224 -0
- package/audit-tool/audits/06-security-rbac.cjs +554 -0
- package/audit-tool/audits/07-api-tech-stack.cjs +355 -0
- package/audit-tool/audits/08-testing-documentation.cjs +202 -0
- package/audit-tool/audits/09-operations.cjs +208 -0
- package/audit-tool/index.cjs +295 -0
- package/audit-tool/utils/code-utils.cjs +218 -0
- package/audit-tool/utils/file-utils.cjs +230 -0
- package/audit-tool/utils/report-utils.cjs +380 -0
- package/cursor-rules/00-standards-overview.mdc +156 -0
- package/cursor-rules/{00-pace-core-compliance.mdc → 01-pace-core-compliance.mdc} +187 -34
- package/cursor-rules/02-project-structure.mdc +37 -5
- package/cursor-rules/{03-solid-principles.mdc → 03-architecture.mdc} +125 -11
- package/cursor-rules/04-code-quality.mdc +419 -0
- package/cursor-rules/{08-markup-quality.mdc → 05-styling.mdc} +55 -10
- package/cursor-rules/{09-rbac-compliance.mdc → 06-security-rbac.mdc} +62 -6
- package/cursor-rules/07-api-tech-stack.mdc +377 -0
- package/cursor-rules/08-testing-documentation.mdc +324 -0
- package/cursor-rules/09-operations.mdc +365 -0
- package/dist/DataTable-6RMSCQJ6.js +15 -0
- package/dist/{DataTable-2N_tqbfq.d.ts → DataTable-DRUIgtUH.d.ts} +1 -1
- package/dist/{PublicPageProvider-BBH6Vqg7.d.ts → PublicPageProvider-CIGSujI2.d.ts} +40 -24
- package/dist/{UnifiedAuthProvider-ZT6TIGM7.js → UnifiedAuthProvider-7SNDOWYD.js} +2 -2
- package/dist/{api-Y4MQWOFW.js → api-7P7DI652.js} +1 -1
- package/dist/{chunk-MAGBIDNS.js → chunk-4DDCYDQ3.js} +8 -7
- package/dist/{chunk-BVP2BCJF.js → chunk-5W2A3DRC.js} +10 -9
- package/dist/{chunk-SD6WQY43.js → chunk-7ILTDCL2.js} +9 -1
- package/dist/{chunk-3QC3KRHK.js → chunk-A3W6LW53.js} +16 -1
- package/dist/{chunk-3O3WHILE.js → chunk-EF2UGZWY.js} +239 -63
- package/dist/{chunk-LAZMKTTF.js → chunk-EURB7QFZ.js} +341 -337
- package/dist/{chunk-2HGJFNAH.js → chunk-FEJLJNWA.js} +1 -15
- package/dist/{chunk-7TYHROIV.js → chunk-GS5672WG.js} +55 -13
- package/dist/{chunk-UIYSCEV7.js → chunk-IUBRCBSY.js} +1 -1
- package/dist/{chunk-ZFYPMX46.js → chunk-LX6U42O3.js} +1 -1
- package/dist/{chunk-FENMYN2U.js → chunk-MPBLMWVR.js} +3 -3
- package/dist/{chunk-ZS5VO5JB.js → chunk-NKHKXPI4.js} +408 -453
- package/dist/{chunk-A55DK444.js → chunk-OJ4SKRSV.js} +1 -7
- package/dist/{chunk-4T7OBVTU.js → chunk-S6ZQKDY6.js} +1 -1
- package/dist/{chunk-FTCRZOG2.js → chunk-T5CVK4R3.js} +5 -5
- package/dist/{chunk-OHIK3MIO.js → chunk-Z2FNRKF3.js} +13 -13
- package/dist/components.d.ts +5 -4
- package/dist/components.js +29 -34
- package/dist/eslint-rules/index.cjs +22 -9
- package/{src/eslint-rules/rules/compliance.cjs → dist/eslint-rules/rules/01-pace-core-compliance.cjs} +184 -23
- package/dist/eslint-rules/rules/04-code-quality.cjs +346 -0
- package/dist/eslint-rules/rules/05-styling.cjs +61 -0
- package/dist/eslint-rules/rules/{rbac.cjs → 06-security-rbac.cjs} +34 -13
- package/dist/eslint-rules/rules/07-api-tech-stack.cjs +385 -0
- package/dist/eslint-rules/rules/08-testing.cjs +94 -0
- package/dist/{functions-DHebl8-F.d.ts → functions-lBy5L2ry.d.ts} +1 -1
- package/dist/hooks.d.ts +5 -5
- package/dist/hooks.js +8 -8
- package/dist/index.d.ts +7 -7
- package/dist/index.js +21 -20
- package/dist/providers.js +2 -2
- package/dist/rbac/index.d.ts +1 -1
- package/dist/rbac/index.js +8 -8
- package/dist/theming/runtime.d.ts +61 -1
- package/dist/theming/runtime.js +1 -1
- package/dist/{types-B-K_5VnO.d.ts → types-DXstZpNI.d.ts} +0 -17
- package/dist/types.d.ts +2 -2
- package/dist/{usePublicRouteParams-COZ28Mvq.d.ts → usePublicRouteParams-MamNgwqe.d.ts} +19 -19
- package/dist/utils.d.ts +2 -2
- package/dist/utils.js +8 -8
- package/docs/README.md +1 -1
- package/docs/api/modules.md +106 -41
- package/docs/api-reference/components.md +18 -20
- package/docs/api-reference/hooks.md +80 -80
- package/docs/api-reference/types.md +1 -1
- package/docs/api-reference/utilities.md +1 -1
- package/docs/architecture/README.md +1 -1
- package/docs/core-concepts/events.md +3 -3
- package/docs/core-concepts/organisations.md +6 -6
- package/docs/core-concepts/permissions.md +6 -6
- package/docs/documentation-index.md +12 -18
- package/docs/getting-started/dependencies.md +23 -0
- package/docs/getting-started/documentation-index.md +1 -1
- package/docs/getting-started/examples/README.md +4 -4
- package/docs/getting-started/examples/full-featured-app.md +1 -1
- package/docs/getting-started/faq.md +2 -2
- package/docs/getting-started/quick-reference.md +4 -4
- package/docs/implementation-guides/app-layout.md +1 -1
- package/docs/implementation-guides/authentication.md +15 -15
- package/docs/implementation-guides/component-styling.md +1 -1
- package/docs/implementation-guides/data-tables.md +127 -34
- package/docs/implementation-guides/datatable-rbac-usage.md +1 -1
- package/docs/implementation-guides/dynamic-colors.md +3 -3
- package/docs/implementation-guides/file-upload-storage.md +2 -2
- package/docs/implementation-guides/hierarchical-datatable.md +40 -60
- package/docs/implementation-guides/inactivity-tracking.md +3 -3
- package/docs/implementation-guides/large-datasets.md +3 -2
- package/docs/implementation-guides/organisation-security.md +2 -2
- package/docs/implementation-guides/performance.md +2 -2
- package/docs/implementation-guides/permission-enforcement.md +1 -1
- package/docs/migration/V0.3.44_organisation-context-timing-fix.md +1 -1
- package/docs/migration/V0.4.0_rbac-migration.md +6 -6
- package/docs/rbac/README.md +5 -5
- package/docs/rbac/advanced-patterns.md +6 -6
- package/docs/rbac/api-reference.md +20 -20
- package/docs/rbac/event-based-apps.md +3 -3
- package/docs/rbac/examples.md +41 -41
- package/docs/rbac/getting-started.md +37 -37
- package/docs/rbac/performance.md +1 -1
- package/docs/rbac/quick-start.md +52 -52
- package/docs/rbac/secure-client-protection.md +1 -1
- package/docs/rbac/troubleshooting.md +1 -1
- package/docs/security/README.md +5 -5
- package/docs/standards/0-standards-overview.md +220 -0
- package/docs/standards/{00-pace-core-compliance.md → 1-pace-core-compliance-standards.md} +241 -185
- package/docs/standards/{02-project-structure.md → 2-project-structure-standards.md} +11 -47
- package/docs/standards/3-architecture-standards.md +606 -0
- package/docs/standards/4-code-quality-standards.md +728 -0
- package/docs/standards/{08-markup-quality.md → 5-styling-standards.md} +12 -9
- package/docs/standards/{09-rbac-compliance.md → 6-security-rbac-standards.md} +126 -18
- package/docs/standards/7-api-tech-stack-standards.md +662 -0
- package/docs/standards/8-testing-documentation-standards.md +401 -0
- package/docs/standards/9-operations-standards.md +1102 -0
- package/docs/standards/README.md +203 -104
- package/docs/troubleshooting/README.md +4 -4
- package/docs/troubleshooting/common-issues.md +2 -2
- package/docs/troubleshooting/debugging.md +9 -9
- package/docs/troubleshooting/migration.md +4 -4
- package/eslint-config-pace-core.cjs +50 -20
- package/package.json +50 -19
- package/scripts/eslint-audit.cjs +123 -0
- package/scripts/install-cursor-rules.cjs +11 -243
- package/scripts/install-eslint-config.cjs +349 -0
- package/scripts/validate-dependencies.cjs +248 -0
- package/src/__tests__/helpers/__tests__/component-test-utils.test.tsx +2 -2
- package/src/__tests__/helpers/__tests__/test-providers.test.tsx +2 -2
- package/src/__tests__/helpers/__tests__/test-utils.test.tsx +30 -18
- package/src/__tests__/integration/UserProfile.test.tsx +14 -14
- package/src/__tests__/rbac/PagePermissionGuard.test.tsx +6 -6
- package/src/__tests__/templates/accessibility.test.template.tsx +10 -9
- package/src/__tests__/templates/component.test.template.tsx +18 -15
- package/src/components/AddressField/AddressField.tsx +26 -1
- package/src/components/Alert/Alert.test.tsx +86 -22
- package/src/components/Alert/Alert.tsx +19 -11
- package/src/components/Badge/Badge.tsx +1 -1
- package/src/components/Calendar/Calendar.tsx +201 -47
- package/src/components/Checkbox/Checkbox.test.tsx +2 -1
- package/src/components/ContextSelector/ContextSelector.tsx +108 -126
- package/src/components/DataTable/AUDIT_REPORT.md +293 -0
- package/src/components/DataTable/DataTable.tsx +1 -19
- package/src/components/DataTable/__tests__/DataTableCore.test.tsx +6 -2
- package/src/components/DataTable/__tests__/a11y.basic.test.tsx +21 -6
- package/src/components/DataTable/__tests__/pagination.modes.test.tsx +3 -2
- package/src/components/DataTable/__tests__/test-utils/sharedTestUtils.tsx +9 -9
- package/src/components/DataTable/components/ColumnFilter.tsx +63 -74
- package/src/components/DataTable/components/ColumnVisibilityDropdown.tsx +43 -41
- package/src/components/DataTable/components/DataTableErrorBoundary.tsx +9 -11
- package/src/components/DataTable/components/DataTableLayout.tsx +5 -16
- package/src/components/DataTable/components/EditableRow.tsx +5 -7
- package/src/components/DataTable/components/EmptyState.tsx +11 -10
- package/src/components/DataTable/components/FilterRow.tsx +2 -4
- package/src/components/DataTable/components/ImportModal.tsx +124 -126
- package/src/components/DataTable/components/LoadingState.tsx +5 -6
- package/src/components/DataTable/components/SortIndicator.tsx +50 -0
- package/src/components/DataTable/components/__tests__/COVERAGE_NOTE.md +4 -4
- package/src/components/DataTable/components/__tests__/ColumnFilter.test.tsx +23 -82
- package/src/components/DataTable/components/__tests__/DataTableErrorBoundary.test.tsx +37 -9
- package/src/components/DataTable/components/__tests__/EmptyState.test.tsx +7 -4
- package/src/components/DataTable/components/__tests__/FilterRow.test.tsx +12 -4
- package/src/components/DataTable/components/__tests__/LoadingState.test.tsx +45 -27
- package/src/components/DataTable/components/index.ts +2 -1
- package/src/components/DataTable/types.ts +0 -18
- package/src/components/DataTable/utils/a11yUtils.ts +17 -0
- package/src/components/DatePickerWithTimezone/DatePickerWithTimezone.test.tsx +1 -1
- package/src/components/DatePickerWithTimezone/DatePickerWithTimezone.tsx +11 -15
- package/src/components/DateTimeField/DateTimeField.tsx +7 -8
- package/src/components/Dialog/Dialog.test.tsx +1 -0
- package/src/components/Dialog/Dialog.tsx +25 -8
- package/src/components/ErrorBoundary/ErrorBoundary.tsx +77 -79
- package/src/components/FileUpload/FileUpload.test.tsx +45 -16
- package/src/components/FileUpload/FileUpload.tsx +141 -130
- package/src/components/NavigationMenu/NavigationMenu.test.tsx +48 -12
- package/src/components/PaceAppLayout/PaceAppLayout.performance.test.tsx +9 -9
- package/src/components/PaceAppLayout/PaceAppLayout.security.test.tsx +30 -30
- package/src/components/PaceAppLayout/PaceAppLayout.test.tsx +4 -4
- package/src/components/PaceLoginPage/PaceLoginPage.test.tsx +7 -1
- package/src/components/Progress/Progress.tsx +2 -4
- package/src/components/ProtectedRoute/ProtectedRoute.tsx +8 -8
- package/src/components/Select/Select.tsx +86 -77
- package/src/components/Select/types.ts +3 -0
- package/src/hooks/__tests__/ServiceHooks.test.tsx +16 -16
- package/src/hooks/__tests__/hooks.integration.test.tsx +49 -49
- package/src/hooks/__tests__/useDataTablePerformance.unit.test.ts +8 -5
- package/src/hooks/__tests__/useFileUrl.unit.test.ts +4 -0
- package/src/hooks/__tests__/useFocusTrap.unit.test.tsx +99 -99
- package/src/hooks/__tests__/useInactivityTracker.unit.test.ts +45 -8
- package/src/hooks/__tests__/usePerformanceMonitor.unit.test.ts +22 -2
- package/src/hooks/public/usePublicEvent.ts +5 -5
- package/src/hooks/public/usePublicEventLogo.ts +5 -5
- package/src/hooks/public/usePublicFileDisplay.ts +2 -2
- package/src/hooks/public/usePublicRouteParams.ts +13 -9
- package/src/hooks/useAddressAutocomplete.test.ts +18 -18
- package/src/hooks/useAppConfig.ts +2 -2
- package/src/hooks/useEventTheme.test.ts +7 -7
- package/src/hooks/useEventTheme.ts +2 -1
- package/src/hooks/useFileDisplay.ts +2 -2
- package/src/hooks/useFileUrl.ts +52 -8
- package/src/hooks/useOrganisationSecurity.test.ts +2 -1
- package/src/providers/UnifiedAuthProvider.smoke.test.tsx +21 -21
- package/src/providers/__tests__/AuthProvider.test.tsx +21 -21
- package/src/providers/__tests__/EventProvider.test.tsx +61 -61
- package/src/providers/__tests__/InactivityProvider.test.tsx +56 -56
- package/src/providers/__tests__/OrganisationProvider.test.tsx +75 -75
- package/src/providers/__tests__/ProviderLifecycle.test.tsx +38 -38
- package/src/providers/__tests__/UnifiedAuthProvider.test.tsx +103 -103
- package/src/providers/services/__tests__/AuthServiceProvider.integration.test.tsx +7 -7
- package/src/providers/services/__tests__/UnifiedAuthProvider.integration.test.tsx +10 -10
- package/src/rbac/__tests__/auth-rbac.e2e.test.tsx +15 -6
- package/src/rbac/__tests__/rbac-functions.test.ts +3 -3
- package/src/rbac/api.test.ts +104 -0
- package/src/rbac/engine.ts +1 -1
- package/src/rbac/hooks/useCan.test.ts +2 -2
- package/src/rbac/secureClient.ts +1 -1
- package/src/rbac/types/functions.ts +1 -1
- package/src/styles/core.css +7 -0
- package/src/theming/__tests__/parseEventColours.test.ts +118 -3
- package/src/theming/parseEventColours.ts +77 -11
- package/src/types/supabase.ts +2 -3
- package/src/utils/__tests__/bundleAnalysis.unit.test.ts +9 -9
- package/src/utils/__tests__/lazyLoad.unit.test.tsx +42 -39
- package/src/utils/file-reference/__tests__/file-reference.test.ts +4 -0
- package/src/utils/formatting/formatDate.test.ts +3 -2
- package/src/utils/formatting/formatDateTime.test.ts +2 -2
- package/src/utils/google-places/googlePlacesUtils.test.ts +36 -24
- package/src/utils/storage/README.md +1 -1
- package/src/utils/storage/__tests__/helpers.unit.test.ts +19 -12
- package/src/utils/storage/helpers.test.ts +69 -3
- package/cursor-rules/01-standards-compliance.mdc +0 -285
- package/cursor-rules/04-testing-standards.mdc +0 -270
- package/cursor-rules/05-bug-reports-and-features.mdc +0 -248
- package/cursor-rules/06-code-quality.mdc +0 -311
- package/cursor-rules/07-tech-stack-compliance.mdc +0 -216
- package/cursor-rules/10-error-handling-patterns.mdc +0 -179
- package/cursor-rules/11-performance-optimization.mdc +0 -169
- package/cursor-rules/12-ci-cd-integration.mdc +0 -150
- package/dist/DataTable-LRJL4IRV.js +0 -15
- package/dist/eslint-rules/rules/compliance.cjs +0 -348
- package/dist/eslint-rules/rules/components.cjs +0 -113
- package/dist/eslint-rules/rules/imports.cjs +0 -102
- package/docs/best-practices/README.md +0 -472
- package/docs/best-practices/accessibility.md +0 -604
- package/docs/best-practices/common-patterns.md +0 -516
- package/docs/best-practices/deployment.md +0 -1103
- package/docs/best-practices/performance.md +0 -1328
- package/docs/best-practices/security.md +0 -940
- package/docs/best-practices/testing.md +0 -1034
- package/docs/rbac/compliance/compliance-guide.md +0 -544
- package/docs/standards/01-standards-compliance.md +0 -188
- package/docs/standards/03-solid-principles.md +0 -39
- package/docs/standards/04-testing-standards.md +0 -36
- package/docs/standards/05-bug-reports-and-features.md +0 -27
- package/docs/standards/06-code-quality.md +0 -34
- package/docs/standards/07-tech-stack-compliance.md +0 -30
- package/docs/standards/10-error-handling-patterns.md +0 -401
- package/docs/standards/11-performance-optimization.md +0 -348
- package/docs/standards/12-ci-cd-integration.md +0 -370
- package/docs/standards/ALIGNMENT_REVIEW_SUMMARY.md +0 -192
- package/scripts/audit/audit-compliance.cjs +0 -1295
- package/scripts/audit/audit-components.cjs +0 -260
- package/scripts/audit/audit-rbac.cjs +0 -954
- package/scripts/audit/audit-standards.cjs +0 -1268
- package/scripts/audit/index.cjs +0 -1927
- package/src/components/DataTable/components/DataTableBody.tsx +0 -478
- package/src/components/DataTable/components/DraggableColumnHeader.tsx +0 -156
- package/src/components/DataTable/components/ExpandButton.tsx +0 -113
- package/src/components/DataTable/components/GroupHeader.tsx +0 -54
- package/src/components/DataTable/components/ViewRowModal.tsx +0 -68
- package/src/components/DataTable/components/VirtualizedDataTable.tsx +0 -525
- package/src/components/DataTable/components/__tests__/ExpandButton.test.tsx +0 -462
- package/src/components/DataTable/components/__tests__/GroupHeader.test.tsx +0 -393
- package/src/components/DataTable/components/__tests__/ViewRowModal.test.tsx +0 -476
- package/src/components/DataTable/components/__tests__/VirtualizedDataTable.test.tsx +0 -128
- package/src/components/DataTable/core/DataTableContext.tsx +0 -216
- package/src/components/DataTable/core/__tests__/DataTableContext.test.tsx +0 -136
- package/src/components/DataTable/hooks/__tests__/useColumnReordering.test.ts +0 -570
- package/src/components/DataTable/hooks/useColumnReordering.ts +0 -123
- package/src/components/DataTable/utils/debugTools.ts +0 -514
- package/src/eslint-rules/index.cjs +0 -22
- package/src/eslint-rules/rules/components.cjs +0 -113
- package/src/eslint-rules/rules/imports.cjs +0 -102
- package/src/eslint-rules/rules/rbac.cjs +0 -790
- package/src/eslint-rules/utils/helpers.cjs +0 -42
- package/src/eslint-rules/utils/manifest-loader.cjs +0 -75
|
@@ -0,0 +1,349 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Installation Script for pace-core ESLint Config
|
|
5
|
+
* @package @jmruthers/pace-core
|
|
6
|
+
* @module Scripts/install-eslint-config
|
|
7
|
+
*
|
|
8
|
+
* Sets up pace-core ESLint configuration in consuming apps.
|
|
9
|
+
* Adds pace-core ESLint config to consuming app's ESLint configuration.
|
|
10
|
+
*
|
|
11
|
+
* This is an opt-in script - it does NOT run automatically via postinstall.
|
|
12
|
+
*
|
|
13
|
+
* Usage:
|
|
14
|
+
* node install-eslint-config.cjs # Setup ESLint config
|
|
15
|
+
* node install-eslint-config.cjs --force # Force update even if already configured
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
const fs = require('fs');
|
|
19
|
+
const path = require('path');
|
|
20
|
+
|
|
21
|
+
// Check for safety guards
|
|
22
|
+
function checkSafetyGuards() {
|
|
23
|
+
// Check environment variable
|
|
24
|
+
if (process.env.PACE_ESLINT_CONFIG_DISABLED === '1') {
|
|
25
|
+
console.log(`${colors.yellow}Skipping ESLint config installation: PACE_ESLINT_CONFIG_DISABLED=1${colors.reset}`);
|
|
26
|
+
process.exit(0);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Check for .git folder (safety guard for CI)
|
|
30
|
+
const cwd = process.cwd();
|
|
31
|
+
if (!fs.existsSync(path.join(cwd, '.git'))) {
|
|
32
|
+
console.log(`${colors.yellow}Skipping ESLint config installation: No .git folder found${colors.reset}`);
|
|
33
|
+
console.log(`${colors.yellow}This is a safety guard. If you want to install anyway, run with --force${colors.reset}`);
|
|
34
|
+
if (!process.argv.includes('--force')) {
|
|
35
|
+
process.exit(0);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// ANSI color codes for terminal output
|
|
41
|
+
const colors = {
|
|
42
|
+
reset: '\x1b[0m',
|
|
43
|
+
green: '\x1b[32m',
|
|
44
|
+
yellow: '\x1b[33m',
|
|
45
|
+
blue: '\x1b[34m',
|
|
46
|
+
cyan: '\x1b[36m',
|
|
47
|
+
bold: '\x1b[1m',
|
|
48
|
+
red: '\x1b[31m'
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
// Find existing ESLint config file
|
|
52
|
+
function findESLintConfig() {
|
|
53
|
+
const cwd = process.cwd();
|
|
54
|
+
const possibleConfigs = [
|
|
55
|
+
'eslint.config.js',
|
|
56
|
+
'eslint.config.cjs',
|
|
57
|
+
'eslint.config.mjs',
|
|
58
|
+
'.eslintrc.js',
|
|
59
|
+
'.eslintrc.cjs',
|
|
60
|
+
'.eslintrc.json',
|
|
61
|
+
'.eslintrc.yaml',
|
|
62
|
+
'.eslintrc.yml',
|
|
63
|
+
];
|
|
64
|
+
|
|
65
|
+
for (const configFile of possibleConfigs) {
|
|
66
|
+
const configPath = path.join(cwd, configFile);
|
|
67
|
+
if (fs.existsSync(configPath)) {
|
|
68
|
+
return { path: configPath, name: configFile, isESM: configFile.endsWith('.js') || configFile.endsWith('.mjs') };
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
return null;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Check if pace-core config is already included
|
|
76
|
+
function hasPaceCoreConfig(configContent) {
|
|
77
|
+
// Check for pace-core config import/require
|
|
78
|
+
const paceCorePatterns = [
|
|
79
|
+
/@jmruthers\/pace-core\/eslint-config/,
|
|
80
|
+
/pace-core\/eslint-config/,
|
|
81
|
+
/paceCoreConfig/,
|
|
82
|
+
/pace-core-compliance/,
|
|
83
|
+
];
|
|
84
|
+
|
|
85
|
+
return paceCorePatterns.some(pattern => pattern.test(configContent));
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Detect if file is ES module
|
|
89
|
+
function isESModule(filePath, content) {
|
|
90
|
+
if (filePath.endsWith('.mjs')) return true;
|
|
91
|
+
if (filePath.endsWith('.cjs')) return false;
|
|
92
|
+
if (filePath.endsWith('.js')) {
|
|
93
|
+
// Check for ES module indicators
|
|
94
|
+
return /^import\s+.*from|^export\s+default/.test(content.trim());
|
|
95
|
+
}
|
|
96
|
+
return false;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Detect if config uses tseslint.config() wrapper
|
|
100
|
+
function usesTseslintConfig(content) {
|
|
101
|
+
return /tseslint\.config\s*\(/.test(content);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Validate ESLint config structure
|
|
105
|
+
function validateConfig(configPath, content) {
|
|
106
|
+
try {
|
|
107
|
+
// Basic validation - check for common syntax errors
|
|
108
|
+
// Check for balanced brackets
|
|
109
|
+
const openBrackets = (content.match(/\[/g) || []).length;
|
|
110
|
+
const closeBrackets = (content.match(/\]/g) || []).length;
|
|
111
|
+
if (openBrackets !== closeBrackets) {
|
|
112
|
+
return { valid: false, error: `Unbalanced brackets: ${openBrackets} open, ${closeBrackets} close` };
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// Check for balanced parentheses
|
|
116
|
+
const openParens = (content.match(/\(/g) || []).length;
|
|
117
|
+
const closeParens = (content.match(/\)/g) || []).length;
|
|
118
|
+
if (openParens !== closeParens) {
|
|
119
|
+
return { valid: false, error: `Unbalanced parentheses: ${openParens} open, ${closeParens} close` };
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// Check that paceCoreConfig is spread correctly
|
|
123
|
+
if (content.includes('...paceCoreConfig')) {
|
|
124
|
+
// Should be spread in an array or tseslint.config()
|
|
125
|
+
if (!content.includes('[...paceCoreConfig') && !content.includes('tseslint.config(...paceCoreConfig')) {
|
|
126
|
+
return { valid: false, error: 'paceCoreConfig must be spread in an array or tseslint.config()' };
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
return { valid: true };
|
|
131
|
+
} catch (error) {
|
|
132
|
+
return { valid: false, error: error.message };
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// Backup file before modification
|
|
137
|
+
function backupFile(filePath) {
|
|
138
|
+
const backupPath = `${filePath}.backup.${Date.now()}`;
|
|
139
|
+
fs.copyFileSync(filePath, backupPath);
|
|
140
|
+
return backupPath;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// Setup ESLint configuration
|
|
144
|
+
function setupESLintConfig(force = false) {
|
|
145
|
+
const cwd = process.cwd();
|
|
146
|
+
const existingConfig = findESLintConfig();
|
|
147
|
+
|
|
148
|
+
if (existingConfig) {
|
|
149
|
+
// Read existing config
|
|
150
|
+
let content = fs.readFileSync(existingConfig.path, 'utf8');
|
|
151
|
+
const isESM = isESModule(existingConfig.path, content);
|
|
152
|
+
const format = isESM ? 'ES Module' : 'CommonJS';
|
|
153
|
+
|
|
154
|
+
console.log(`${colors.cyan}Found ESLint config:${colors.reset} ${existingConfig.name} (${format})`);
|
|
155
|
+
|
|
156
|
+
// Check if already configured
|
|
157
|
+
if (hasPaceCoreConfig(content)) {
|
|
158
|
+
if (!force) {
|
|
159
|
+
console.log(`${colors.blue}○${colors.reset} ${existingConfig.name} already includes pace-core rules`);
|
|
160
|
+
console.log(`${colors.blue} Use --force to update.${colors.reset}`);
|
|
161
|
+
return { action: 'skipped', file: existingConfig.name, format };
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
console.log(`${colors.yellow}Updating existing ESLint config...${colors.reset}`);
|
|
165
|
+
} else {
|
|
166
|
+
console.log(`${colors.cyan}Adding pace-core config to ${existingConfig.name}...${colors.reset}`);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// Backup before modification
|
|
170
|
+
const backupPath = backupFile(existingConfig.path);
|
|
171
|
+
console.log(`${colors.cyan} Backed up to ${path.basename(backupPath)}${colors.reset}`);
|
|
172
|
+
|
|
173
|
+
// Add pace-core config
|
|
174
|
+
if (isESM) {
|
|
175
|
+
// ES Module format
|
|
176
|
+
if (!content.includes('import paceCoreConfig')) {
|
|
177
|
+
// Add import at top (after other imports if they exist)
|
|
178
|
+
const importLines = content.match(/^(import\s+[^;]+;?\s*\n)+/m);
|
|
179
|
+
if (importLines) {
|
|
180
|
+
// Add after existing imports
|
|
181
|
+
content = content.replace(
|
|
182
|
+
importLines[0],
|
|
183
|
+
`${importLines[0]}import paceCoreConfig from '@jmruthers/pace-core/eslint-config';\n`
|
|
184
|
+
);
|
|
185
|
+
} else {
|
|
186
|
+
// Add at the beginning
|
|
187
|
+
content = `import paceCoreConfig from '@jmruthers/pace-core/eslint-config';\n${content}`;
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// Handle tseslint.config() wrapper
|
|
192
|
+
if (usesTseslintConfig(content)) {
|
|
193
|
+
// Config uses tseslint.config() - spread paceCoreConfig as first argument
|
|
194
|
+
if (!content.includes('...paceCoreConfig')) {
|
|
195
|
+
// Find tseslint.config( and add paceCoreConfig as first argument
|
|
196
|
+
// Handle both cases: tseslint.config(...) and tseslint.config(\n...)
|
|
197
|
+
if (content.match(/tseslint\.config\s*\(\s*[^\n]/)) {
|
|
198
|
+
// Has content on same line, add after opening paren
|
|
199
|
+
content = content.replace(
|
|
200
|
+
/(tseslint\.config\s*\()\s*/,
|
|
201
|
+
'$1\n ...paceCoreConfig,\n '
|
|
202
|
+
);
|
|
203
|
+
} else {
|
|
204
|
+
// Has content on new line, add as first line
|
|
205
|
+
content = content.replace(
|
|
206
|
+
/(tseslint\.config\s*\(\s*\n)\s*/,
|
|
207
|
+
'$1 ...paceCoreConfig,\n '
|
|
208
|
+
);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
} else if (content.includes('export default [')) {
|
|
212
|
+
// Already an array, add paceCoreConfig at the beginning
|
|
213
|
+
if (!content.includes('...paceCoreConfig')) {
|
|
214
|
+
content = content.replace(
|
|
215
|
+
/(export\s+default\s*\[)\s*/,
|
|
216
|
+
'$1\n ...paceCoreConfig,'
|
|
217
|
+
);
|
|
218
|
+
}
|
|
219
|
+
} else if (content.includes('export default')) {
|
|
220
|
+
// Not an array, wrap it
|
|
221
|
+
const exportMatch = content.match(/(export\s+default\s+)(.+?)(;?\s*$)/s);
|
|
222
|
+
if (exportMatch) {
|
|
223
|
+
content = content.replace(
|
|
224
|
+
exportMatch[0],
|
|
225
|
+
`${exportMatch[1]}[\n ...paceCoreConfig,\n ${exportMatch[2]}\n];`
|
|
226
|
+
);
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
} else {
|
|
230
|
+
// CommonJS format
|
|
231
|
+
if (!content.includes('require(\'@jmruthers/pace-core/eslint-config\')')) {
|
|
232
|
+
// Add require at top (after other requires if they exist)
|
|
233
|
+
const requireLines = content.match(/^(const\s+\w+\s*=\s*require\([^)]+\);\s*\n)+/m);
|
|
234
|
+
if (requireLines) {
|
|
235
|
+
content = content.replace(
|
|
236
|
+
requireLines[0],
|
|
237
|
+
`${requireLines[0]}const paceCoreConfig = require('@jmruthers/pace-core/eslint-config');\n`
|
|
238
|
+
);
|
|
239
|
+
} else {
|
|
240
|
+
content = `const paceCoreConfig = require('@jmruthers/pace-core/eslint-config');\n${content}`;
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
// Add to module.exports
|
|
245
|
+
if (content.includes('module.exports = [')) {
|
|
246
|
+
// Already an array, add paceCoreConfig at the beginning
|
|
247
|
+
if (!content.includes('...paceCoreConfig')) {
|
|
248
|
+
content = content.replace(
|
|
249
|
+
/(module\.exports\s*=\s*\[)\s*/,
|
|
250
|
+
'$1\n ...paceCoreConfig,'
|
|
251
|
+
);
|
|
252
|
+
}
|
|
253
|
+
} else if (content.includes('module.exports =')) {
|
|
254
|
+
// Not an array, wrap it
|
|
255
|
+
const exportMatch = content.match(/(module\.exports\s*=\s*)(.+?)(;?\s*$)/s);
|
|
256
|
+
if (exportMatch) {
|
|
257
|
+
content = content.replace(
|
|
258
|
+
exportMatch[0],
|
|
259
|
+
`${exportMatch[1]}[\n ...paceCoreConfig,\n ${exportMatch[2]}\n];`
|
|
260
|
+
);
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
// Validate config before writing
|
|
266
|
+
const validation = validateConfig(existingConfig.path, content);
|
|
267
|
+
if (!validation.valid) {
|
|
268
|
+
console.error(`${colors.red}✗${colors.reset} Config validation failed: ${validation.error}`);
|
|
269
|
+
console.error(`${colors.yellow} Restoring backup...${colors.reset}`);
|
|
270
|
+
fs.copyFileSync(backupPath, existingConfig.path);
|
|
271
|
+
throw new Error(`Invalid ESLint config structure: ${validation.error}`);
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
// Write updated config
|
|
275
|
+
fs.writeFileSync(existingConfig.path, content, 'utf8');
|
|
276
|
+
console.log(`${colors.green}✓${colors.reset} Updated ${existingConfig.name}`);
|
|
277
|
+
return { action: 'updated', file: existingConfig.name, backup: backupPath, format };
|
|
278
|
+
} else {
|
|
279
|
+
// Create new ESLint config (default to ES modules)
|
|
280
|
+
const configPath = path.join(cwd, 'eslint.config.js');
|
|
281
|
+
const configContent = `import paceCoreConfig from '@jmruthers/pace-core/eslint-config';
|
|
282
|
+
|
|
283
|
+
export default [
|
|
284
|
+
...paceCoreConfig,
|
|
285
|
+
// Your app-specific ESLint configuration
|
|
286
|
+
{
|
|
287
|
+
// Add your rules here
|
|
288
|
+
},
|
|
289
|
+
];
|
|
290
|
+
`;
|
|
291
|
+
|
|
292
|
+
console.log(`${colors.cyan}No ESLint config found. Creating eslint.config.js...${colors.reset}`);
|
|
293
|
+
fs.writeFileSync(configPath, configContent, 'utf8');
|
|
294
|
+
console.log(`${colors.green}✓${colors.reset} Created eslint.config.js`);
|
|
295
|
+
console.log(`${colors.cyan} Edit eslint.config.js to add your app-specific ESLint rules.${colors.reset}`);
|
|
296
|
+
return { action: 'created', file: 'eslint.config.js', format: 'ES Module' };
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
// Main execution
|
|
301
|
+
function main() {
|
|
302
|
+
checkSafetyGuards();
|
|
303
|
+
|
|
304
|
+
const force = process.argv.includes('--force');
|
|
305
|
+
|
|
306
|
+
if (force) {
|
|
307
|
+
console.log(`${colors.yellow}Warning: --force flag is set. This will overwrite existing configurations.${colors.reset}\n`);
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
try {
|
|
311
|
+
console.log(`${colors.cyan}Setting up ESLint configuration from pace-core...${colors.reset}\n`);
|
|
312
|
+
const result = setupESLintConfig(force);
|
|
313
|
+
|
|
314
|
+
// Summary
|
|
315
|
+
console.log(`\n${colors.bold}Setup Summary:${colors.reset}`);
|
|
316
|
+
if (result.action === 'created') {
|
|
317
|
+
console.log(` ${colors.green}ESLint Config:${colors.reset} Created ${result.file} (${result.format})`);
|
|
318
|
+
} else if (result.action === 'updated') {
|
|
319
|
+
console.log(` ${colors.green}ESLint Config:${colors.reset} Updated ${result.file} (${result.format})`);
|
|
320
|
+
console.log(` ${colors.yellow}Backup:${colors.reset} ${path.basename(result.backup)}`);
|
|
321
|
+
} else {
|
|
322
|
+
console.log(` ${colors.blue}ESLint Config:${colors.reset} Already configured (${result.file}, ${result.format})`);
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
console.log(`\n${colors.cyan}ESLint configuration is now available${colors.reset}`);
|
|
326
|
+
console.log(`\n${colors.cyan}Next steps:${colors.reset}`);
|
|
327
|
+
console.log(` • Run ${colors.bold}npm run lint${colors.reset} to verify ESLint is working`);
|
|
328
|
+
console.log(` • Edit your ESLint config to add app-specific rules`);
|
|
329
|
+
console.log(` • See ${colors.bold}packages/core/docs/standards/${colors.reset} for complete standards documentation`);
|
|
330
|
+
|
|
331
|
+
} catch (error) {
|
|
332
|
+
console.error(`${colors.red}Error during setup:${colors.reset}`);
|
|
333
|
+
console.error(error.message);
|
|
334
|
+
if (error.stack) {
|
|
335
|
+
console.error(error.stack);
|
|
336
|
+
}
|
|
337
|
+
process.exit(1);
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
// Run if called directly
|
|
342
|
+
if (require.main === module) {
|
|
343
|
+
main();
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
module.exports = {
|
|
347
|
+
setupESLintConfig,
|
|
348
|
+
findESLintConfig
|
|
349
|
+
};
|
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Dependency Validation Script
|
|
5
|
+
* @package @jmruthers/pace-core
|
|
6
|
+
* @module Scripts/ValidateDependencies
|
|
7
|
+
*
|
|
8
|
+
* Validates consistency between package.json, audit tool, and documentation.
|
|
9
|
+
* Ensures package.json is the single source of truth for dependencies.
|
|
10
|
+
*
|
|
11
|
+
* Checks:
|
|
12
|
+
* - All peerDependencies have corresponding entries in peerDependenciesMeta
|
|
13
|
+
* - No peer dependency is also in dependencies
|
|
14
|
+
* - Required peers (not optional) are correctly marked
|
|
15
|
+
* - Audit tool can read package.json correctly
|
|
16
|
+
* - No hardcoded dependency lists exist in audit tool
|
|
17
|
+
*
|
|
18
|
+
* Usage:
|
|
19
|
+
* node scripts/validate-dependencies.cjs
|
|
20
|
+
* npm run validate:dependencies
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
const fs = require('fs');
|
|
24
|
+
const path = require('path');
|
|
25
|
+
|
|
26
|
+
// Colors for terminal output
|
|
27
|
+
const colors = {
|
|
28
|
+
reset: '\x1b[0m',
|
|
29
|
+
red: '\x1b[31m',
|
|
30
|
+
green: '\x1b[32m',
|
|
31
|
+
yellow: '\x1b[33m',
|
|
32
|
+
blue: '\x1b[34m',
|
|
33
|
+
cyan: '\x1b[36m',
|
|
34
|
+
bold: '\x1b[1m',
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
// Get package.json path
|
|
38
|
+
const packageJsonPath = path.resolve(__dirname, '../package.json');
|
|
39
|
+
const auditToolPath = path.resolve(__dirname, '../audit-tool/00-dependencies.cjs');
|
|
40
|
+
|
|
41
|
+
let hasErrors = false;
|
|
42
|
+
let hasWarnings = false;
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Check package.json structure
|
|
46
|
+
*/
|
|
47
|
+
function validatePackageJson() {
|
|
48
|
+
console.log(`${colors.blue}Validating package.json structure...${colors.reset}`);
|
|
49
|
+
|
|
50
|
+
if (!fs.existsSync(packageJsonPath)) {
|
|
51
|
+
console.error(`${colors.red}Error: package.json not found at ${packageJsonPath}${colors.reset}`);
|
|
52
|
+
return false;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const pkg = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
|
|
56
|
+
const peerDeps = pkg.peerDependencies || {};
|
|
57
|
+
const peerMeta = pkg.peerDependenciesMeta || {};
|
|
58
|
+
const dependencies = pkg.dependencies || {};
|
|
59
|
+
|
|
60
|
+
let valid = true;
|
|
61
|
+
|
|
62
|
+
// Check 1: All OPTIONAL peerDependencies must have entries in peerDependenciesMeta
|
|
63
|
+
// Required peers (not optional) should NOT be in peerDependenciesMeta
|
|
64
|
+
// Only optional peers need to be explicitly marked
|
|
65
|
+
const optionalPeersInMeta = Object.keys(peerMeta).filter(dep => peerMeta[dep]?.optional);
|
|
66
|
+
const optionalPeersNotInMeta = Object.keys(peerDeps).filter(dep => {
|
|
67
|
+
// If it's in dependencies, it shouldn't be a peer at all (handled by check 2)
|
|
68
|
+
if (dependencies[dep]) return false;
|
|
69
|
+
// If it's marked as optional in meta, it's fine
|
|
70
|
+
if (peerMeta[dep]?.optional) return false;
|
|
71
|
+
// If it's not in meta and not in dependencies, we need to check if it should be optional
|
|
72
|
+
// For now, we'll only warn if a peer is in meta but not marked as optional
|
|
73
|
+
return false;
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
// Check for peers that are marked as optional in meta but should be required
|
|
77
|
+
// This is a warning, not an error, as the audit tool will treat them correctly
|
|
78
|
+
const requiredPeersInMeta = Object.keys(peerMeta).filter(
|
|
79
|
+
dep => peerDeps[dep] && !peerMeta[dep]?.optional
|
|
80
|
+
);
|
|
81
|
+
if (requiredPeersInMeta.length > 0) {
|
|
82
|
+
console.warn(`${colors.yellow}⚠️ Warning: The following required peers are in peerDependenciesMeta (they don't need to be):${colors.reset}`);
|
|
83
|
+
requiredPeersInMeta.forEach(dep => {
|
|
84
|
+
console.warn(` - ${colors.yellow}${dep}${colors.reset}`);
|
|
85
|
+
});
|
|
86
|
+
console.warn(`${colors.yellow} → Required peers don't need to be in peerDependenciesMeta (only optional ones do)${colors.reset}`);
|
|
87
|
+
hasWarnings = true;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Check 2: No peer dependency is also in dependencies
|
|
91
|
+
const duplicateDeps = Object.keys(peerDeps).filter(dep => dependencies[dep]);
|
|
92
|
+
if (duplicateDeps.length > 0) {
|
|
93
|
+
console.error(`${colors.red}❌ Error: The following packages appear in both peerDependencies and dependencies:${colors.reset}`);
|
|
94
|
+
duplicateDeps.forEach(dep => {
|
|
95
|
+
console.error(` - ${colors.red}${dep}${colors.reset}`);
|
|
96
|
+
});
|
|
97
|
+
console.error(`${colors.yellow} → Remove them from peerDependencies (they're already included)${colors.reset}`);
|
|
98
|
+
valid = false;
|
|
99
|
+
hasErrors = true;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// Check 3: Verify no required peers are marked as optional
|
|
103
|
+
// Required peers are those NOT in peerDependenciesMeta (or marked as optional: false)
|
|
104
|
+
const requiredPeers = Object.keys(peerDeps).filter(dep => {
|
|
105
|
+
// If it's in dependencies, it's not a peer (handled by check 2)
|
|
106
|
+
if (dependencies[dep]) return false;
|
|
107
|
+
// If it's not in meta, it's required by default
|
|
108
|
+
if (!peerMeta[dep]) return true;
|
|
109
|
+
// If it's in meta and marked as optional: false, it's required
|
|
110
|
+
if (peerMeta[dep]?.optional === false) return true;
|
|
111
|
+
// If it's in meta and marked as optional: true, it's optional
|
|
112
|
+
return false;
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
const incorrectlyOptional = requiredPeers.filter(dep => peerMeta[dep]?.optional === true);
|
|
116
|
+
if (incorrectlyOptional.length > 0) {
|
|
117
|
+
console.error(`${colors.red}❌ Error: The following required peers are incorrectly marked as optional:${colors.reset}`);
|
|
118
|
+
incorrectlyOptional.forEach(dep => {
|
|
119
|
+
console.error(` - ${colors.red}${dep}${colors.reset}`);
|
|
120
|
+
});
|
|
121
|
+
console.error(`${colors.yellow} → Remove "optional": true from peerDependenciesMeta for these packages (or remove them from meta entirely)${colors.reset}`);
|
|
122
|
+
valid = false;
|
|
123
|
+
hasErrors = true;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// Check 4: Warn about extra entries in peerDependenciesMeta that don't exist in peerDependencies
|
|
127
|
+
const extraMeta = Object.keys(peerMeta).filter(dep => !peerDeps[dep]);
|
|
128
|
+
if (extraMeta.length > 0) {
|
|
129
|
+
console.warn(`${colors.yellow}⚠️ Warning: The following entries in peerDependenciesMeta don't exist in peerDependencies:${colors.reset}`);
|
|
130
|
+
extraMeta.forEach(dep => {
|
|
131
|
+
console.warn(` - ${colors.yellow}${dep}${colors.reset}`);
|
|
132
|
+
});
|
|
133
|
+
console.warn(`${colors.yellow} → Remove them from peerDependenciesMeta or add them to peerDependencies${colors.reset}`);
|
|
134
|
+
hasWarnings = true;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
if (valid && duplicateDeps.length === 0) {
|
|
138
|
+
console.log(`${colors.green}✅ package.json structure is valid${colors.reset}`);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
return valid;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Check audit tool consistency
|
|
146
|
+
*/
|
|
147
|
+
function validateAuditTool() {
|
|
148
|
+
console.log(`${colors.blue}Validating audit tool consistency...${colors.reset}`);
|
|
149
|
+
|
|
150
|
+
if (!fs.existsSync(auditToolPath)) {
|
|
151
|
+
console.error(`${colors.red}Error: Audit tool not found at ${auditToolPath}${colors.reset}`);
|
|
152
|
+
return false;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
const auditToolContent = fs.readFileSync(auditToolPath, 'utf8');
|
|
156
|
+
let valid = true;
|
|
157
|
+
|
|
158
|
+
// Check 1: Verify no hardcoded REQUIRED_PEERS array
|
|
159
|
+
const hardcodedPattern = /const\s+REQUIRED_PEERS\s*=\s*\[['"][^'"]+['"]/;
|
|
160
|
+
if (hardcodedPattern.test(auditToolContent)) {
|
|
161
|
+
console.error(`${colors.red}❌ Error: Found hardcoded REQUIRED_PEERS array in audit tool${colors.reset}`);
|
|
162
|
+
console.error(`${colors.yellow} → The audit tool should read required/optional peers from peerDependenciesMeta${colors.reset}`);
|
|
163
|
+
valid = false;
|
|
164
|
+
hasErrors = true;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// Check 2: Verify audit tool uses peerDependenciesMeta
|
|
168
|
+
if (!auditToolContent.includes('peerDependenciesMeta')) {
|
|
169
|
+
console.error(`${colors.red}❌ Error: Audit tool does not reference peerDependenciesMeta${colors.reset}`);
|
|
170
|
+
console.error(`${colors.yellow} → The audit tool should read from peerDependenciesMeta to determine required vs optional peers${colors.reset}`);
|
|
171
|
+
valid = false;
|
|
172
|
+
hasErrors = true;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// Check 3: Verify audit tool can read package.json
|
|
176
|
+
if (!auditToolContent.includes('findPaceCorePackageJson')) {
|
|
177
|
+
console.warn(`${colors.yellow}⚠️ Warning: Audit tool may not be able to find pace-core package.json${colors.reset}`);
|
|
178
|
+
hasWarnings = true;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// Check 4: Try to load and test the audit tool
|
|
182
|
+
try {
|
|
183
|
+
const { runDependencyAudit } = require(auditToolPath);
|
|
184
|
+
if (typeof runDependencyAudit !== 'function') {
|
|
185
|
+
console.error(`${colors.red}❌ Error: Audit tool does not export runDependencyAudit function${colors.reset}`);
|
|
186
|
+
valid = false;
|
|
187
|
+
hasErrors = true;
|
|
188
|
+
} else {
|
|
189
|
+
// Test that it can read package.json (use current directory as test)
|
|
190
|
+
const testResult = runDependencyAudit(path.resolve(__dirname, '../..'));
|
|
191
|
+
if (testResult.error && testResult.error.includes('package.json')) {
|
|
192
|
+
console.warn(`${colors.yellow}⚠️ Warning: Audit tool had issues reading package.json: ${testResult.error}${colors.reset}`);
|
|
193
|
+
hasWarnings = true;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
} catch (error) {
|
|
197
|
+
console.error(`${colors.red}❌ Error: Could not load audit tool: ${error.message}${colors.reset}`);
|
|
198
|
+
valid = false;
|
|
199
|
+
hasErrors = true;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
if (valid) {
|
|
203
|
+
console.log(`${colors.green}✅ Audit tool is consistent with package.json${colors.reset}`);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
return valid;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* Main validation function
|
|
211
|
+
*/
|
|
212
|
+
function main() {
|
|
213
|
+
console.log(`${colors.bold}${colors.cyan}Dependency Validation${colors.reset}`);
|
|
214
|
+
console.log(`${colors.cyan}${'='.repeat(50)}${colors.reset}\n`);
|
|
215
|
+
|
|
216
|
+
const packageJsonValid = validatePackageJson();
|
|
217
|
+
console.log();
|
|
218
|
+
const auditToolValid = validateAuditTool();
|
|
219
|
+
console.log();
|
|
220
|
+
|
|
221
|
+
// Summary
|
|
222
|
+
console.log(`${colors.bold}Summary:${colors.reset}\n`);
|
|
223
|
+
|
|
224
|
+
if (hasErrors) {
|
|
225
|
+
console.log(`${colors.red}❌ Validation failed with errors${colors.reset}`);
|
|
226
|
+
console.log(`${colors.yellow}Fix the errors above before publishing${colors.reset}\n`);
|
|
227
|
+
process.exit(1);
|
|
228
|
+
} else if (hasWarnings) {
|
|
229
|
+
console.log(`${colors.yellow}⚠️ Validation passed with warnings${colors.reset}`);
|
|
230
|
+
console.log(`${colors.yellow}Review the warnings above${colors.reset}\n`);
|
|
231
|
+
process.exit(0);
|
|
232
|
+
} else {
|
|
233
|
+
console.log(`${colors.green}✅ All dependency validations passed!${colors.reset}\n`);
|
|
234
|
+
process.exit(0);
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
// Run if called directly
|
|
239
|
+
if (require.main === module) {
|
|
240
|
+
main();
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
// Export for use by other scripts
|
|
244
|
+
module.exports = {
|
|
245
|
+
validatePackageJson,
|
|
246
|
+
validateAuditTool,
|
|
247
|
+
main,
|
|
248
|
+
};
|
|
@@ -23,7 +23,7 @@ vi.mock('../test-utils', () => ({
|
|
|
23
23
|
describe('[helpers] componentTestPatterns', () => {
|
|
24
24
|
describe('testRenders', () => {
|
|
25
25
|
it('creates test that renders component without crashing', () => {
|
|
26
|
-
const TestComponent = () => <
|
|
26
|
+
const TestComponent = () => <p data-testid="test-component">Test</p>;
|
|
27
27
|
const testFn = componentTestPatterns.testRenders(TestComponent);
|
|
28
28
|
|
|
29
29
|
expect(typeof testFn).toBe('function');
|
|
@@ -33,7 +33,7 @@ describe('[helpers] componentTestPatterns', () => {
|
|
|
33
33
|
});
|
|
34
34
|
|
|
35
35
|
it('creates test that renders component with props', () => {
|
|
36
|
-
const TestComponent = ({ name }: { name: string }) => <
|
|
36
|
+
const TestComponent = ({ name }: { name: string }) => <p data-testid="test-component">{name}</p>;
|
|
37
37
|
const testFn = componentTestPatterns.testRenders(TestComponent, { name: 'Test Name' });
|
|
38
38
|
|
|
39
39
|
expect(typeof testFn).toBe('function');
|
|
@@ -14,11 +14,11 @@ import { TestProviderWrapper, renderWithProviders, createMockUseUnifiedAuth, use
|
|
|
14
14
|
function TestComponent() {
|
|
15
15
|
const auth = useUnifiedAuth();
|
|
16
16
|
return (
|
|
17
|
-
<
|
|
17
|
+
<p>
|
|
18
18
|
<span data-testid="user-email">{auth.user?.email}</span>
|
|
19
19
|
<span data-testid="is-authenticated">{auth.isAuthenticated ? 'true' : 'false'}</span>
|
|
20
20
|
<span data-testid="app-name">{auth.appName}</span>
|
|
21
|
-
</
|
|
21
|
+
</p>
|
|
22
22
|
);
|
|
23
23
|
}
|
|
24
24
|
|