@jmruthers/pace-core 0.5.193 → 0.6.1
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 +29 -0
- package/README.md +7 -1
- package/cursor-rules/00-pace-core-compliance.mdc +372 -0
- package/cursor-rules/01-standards-compliance.mdc +275 -0
- package/cursor-rules/02-project-structure.mdc +200 -0
- package/cursor-rules/03-solid-principles.mdc +341 -0
- package/cursor-rules/04-testing-standards.mdc +315 -0
- package/cursor-rules/05-bug-reports-and-features.mdc +246 -0
- package/cursor-rules/06-code-quality.mdc +392 -0
- package/cursor-rules/07-tech-stack-compliance.mdc +309 -0
- package/cursor-rules/CHANGELOG.md +101 -0
- package/cursor-rules/README.md +191 -0
- package/dist/{DataTable-Be6dH_dR.d.ts → DataTable-CH1U5Tpy.d.ts} +1 -1
- package/dist/{DataTable-5FU7IESH.js → DataTable-DQ7RSOHE.js} +6 -6
- package/dist/{PublicPageProvider-C0Sm_e5k.d.ts → PublicPageProvider-ce4xlHYA.d.ts} +34 -155
- package/dist/{UnifiedAuthProvider-RGJTDE2C.js → UnifiedAuthProvider-ATAP5UTR.js} +2 -2
- package/dist/{chunk-6C4YBBJM 5.js → chunk-3QRJFVBR.js} +1 -1
- package/dist/chunk-3QRJFVBR.js.map +1 -0
- package/dist/{chunk-IIELH4DL.js → chunk-3XTALGJF.js} +2 -2
- package/dist/{chunk-IIELH4DL.js.map → chunk-3XTALGJF.js.map} +1 -1
- package/dist/{chunk-HWIIPPNI.js → chunk-4N5C5XZU.js} +20 -20
- package/dist/chunk-4N5C5XZU.js.map +1 -0
- package/dist/{chunk-7EQTDTTJ.js → chunk-4ZC4GX36.js} +5 -5
- package/dist/{chunk-7EQTDTTJ.js 2.map → chunk-4ZC4GX36.js.map} +1 -1
- package/dist/{chunk-7FLMSG37.js → chunk-BYFSK72L.js} +22 -22
- package/dist/chunk-BYFSK72L.js.map +1 -0
- package/dist/{chunk-LFNCN2SP.js → chunk-EXUD6RNJ.js} +46 -7
- package/dist/chunk-EXUD6RNJ.js.map +1 -0
- package/dist/{chunk-NOAYCWCX 5.js → chunk-GLK6VM3F.js} +167 -169
- package/dist/chunk-GLK6VM3F.js.map +1 -0
- package/dist/{chunk-HW3OVDUF.js → chunk-J36DSWQK.js} +1 -1
- package/dist/{chunk-HW3OVDUF.js.map → chunk-J36DSWQK.js.map} +1 -1
- package/dist/{chunk-BC4IJKSL.js → chunk-JBKQ3SAO.js} +2 -2
- package/dist/{chunk-QWWZ5CAQ.js → chunk-LXQLPRQ2.js} +2 -2
- package/dist/{chunk-E3SPN4VZ 5.js → chunk-T33XF5ZC.js} +119 -114
- package/dist/chunk-T33XF5ZC.js.map +1 -0
- package/dist/{chunk-XNXXZ43G.js → chunk-XM25TVIE.js} +27 -4
- package/dist/chunk-XM25TVIE.js.map +1 -0
- package/dist/components.d.ts +3 -3
- package/dist/components.js +8 -8
- package/dist/hooks.d.ts +6 -6
- package/dist/hooks.js +17 -22
- package/dist/hooks.js.map +1 -1
- package/dist/index.d.ts +7 -7
- package/dist/index.js +15 -16
- package/dist/index.js.map +1 -1
- package/dist/providers.js +1 -1
- package/dist/rbac/index.d.ts +1 -1
- package/dist/rbac/index.js +5 -5
- package/dist/{usePublicRouteParams-TZe0gy-4.d.ts → usePublicRouteParams-BJAlWfuJ.d.ts} +3 -3
- package/dist/{useToast-C8gR5ir4.d.ts → useToast-AyaT-x7p.d.ts} +2 -2
- package/dist/utils.d.ts +1 -1
- package/dist/utils.js +3 -3
- package/docs/getting-started/cursor-rules.md +262 -0
- package/docs/getting-started/installation-guide.md +6 -1
- package/docs/getting-started/quick-start.md +6 -1
- package/docs/migration/MIGRATION_GUIDE.md +4 -4
- package/docs/migration/REACT_19_MIGRATION.md +227 -0
- package/docs/standards/README.md +39 -0
- package/docs/troubleshooting/migration.md +4 -4
- package/examples/PublicPages/PublicEventPage.tsx +1 -1
- package/package.json +11 -6
- package/scripts/audit-consuming-app.cjs +961 -0
- package/scripts/check-pace-core-compliance.cjs +34 -15
- package/scripts/install-cursor-rules.cjs +236 -0
- package/src/__tests__/helpers/test-providers.tsx +1 -1
- package/src/__tests__/helpers/test-utils.tsx +1 -1
- package/src/components/Badge/Badge.tsx +2 -4
- package/src/components/Button/Button.tsx +5 -4
- package/src/components/Calendar/Calendar.tsx +1 -1
- package/src/components/DataTable/DataTable.test.tsx +57 -93
- package/src/components/DataTable/DataTable.tsx +2 -2
- package/src/components/DataTable/__tests__/pagination.modes.test.tsx +13 -5
- package/src/components/DataTable/__tests__/ssr.strict-mode.test.tsx +12 -12
- package/src/components/DataTable/components/AccessDeniedPage.tsx +1 -1
- package/src/components/DataTable/components/BulkOperationsDropdown.tsx +1 -1
- package/src/components/DataTable/components/DataTableCore.tsx +4 -7
- package/src/components/DataTable/components/DataTableModals.tsx +1 -1
- package/src/components/DataTable/components/EditableRow.tsx +1 -1
- package/src/components/DataTable/components/UnifiedTableBody.tsx +6 -8
- package/src/components/DataTable/components/__tests__/DataTableModals.test.tsx +23 -23
- package/src/components/DataTable/components/__tests__/EditableRow.test.tsx +11 -11
- package/src/components/DataTable/components/__tests__/ExpandButton.test.tsx +36 -36
- package/src/components/DataTable/components/__tests__/GroupHeader.test.tsx +27 -27
- package/src/components/DataTable/components/__tests__/ImportModal.test.tsx +39 -39
- package/src/components/DataTable/components/__tests__/UnifiedTableBody.test.tsx +33 -33
- package/src/components/DataTable/components/__tests__/ViewRowModal.test.tsx +29 -29
- package/src/components/DataTable/hooks/useColumnReordering.ts +2 -2
- package/src/components/DataTable/hooks/useKeyboardNavigation.ts +2 -2
- package/src/components/DatePickerWithTimezone/DatePickerWithTimezone.test.tsx +8 -14
- package/src/components/Dialog/Dialog.tsx +6 -5
- package/src/components/ErrorBoundary/ErrorBoundary.tsx +1 -1
- package/src/components/EventSelector/EventSelector.tsx +1 -1
- package/src/components/FileDisplay/FileDisplay.test.tsx +2 -2
- package/src/components/Footer/Footer.tsx +1 -1
- package/src/components/Form/Form.test.tsx +36 -15
- package/src/components/Form/Form.tsx +30 -26
- package/src/components/Header/Header.tsx +1 -1
- package/src/components/InactivityWarningModal/InactivityWarningModal.test.tsx +40 -40
- package/src/components/InactivityWarningModal/InactivityWarningModal.tsx +1 -1
- package/src/components/Input/Input.tsx +28 -30
- package/src/components/Label/Label.tsx +1 -1
- package/src/components/LoadingSpinner/LoadingSpinner.tsx +1 -1
- package/src/components/LoginForm/LoginForm.test.tsx +42 -42
- package/src/components/LoginForm/LoginForm.tsx +8 -8
- package/src/components/NavigationMenu/NavigationMenu.tsx +1 -1
- package/src/components/PaceAppLayout/PaceAppLayout.performance.test.tsx +1 -1
- package/src/components/PaceAppLayout/PaceAppLayout.test.tsx +50 -50
- package/src/components/PaceAppLayout/PaceAppLayout.tsx +1 -1
- package/src/components/PaceAppLayout/README.md +1 -1
- package/src/components/PaceLoginPage/PaceLoginPage.tsx +1 -1
- package/src/components/PasswordChange/PasswordChangeForm.test.tsx +33 -33
- package/src/components/PasswordChange/PasswordChangeForm.tsx +1 -1
- package/src/components/Progress/Progress.tsx +1 -1
- package/src/components/PublicLayout/PublicPageLayout.tsx +1 -1
- package/src/components/Select/Select.tsx +33 -22
- package/src/components/SessionRestorationLoader/SessionRestorationLoader.tsx +1 -1
- package/src/components/Table/Table.tsx +1 -1
- package/src/components/Textarea/Textarea.tsx +27 -29
- package/src/components/Toast/Toast.tsx +1 -1
- package/src/components/Tooltip/Tooltip.tsx +1 -1
- package/src/components/UserMenu/UserMenu.tsx +1 -1
- package/src/hooks/__tests__/hooks.integration.test.tsx +80 -55
- package/src/hooks/__tests__/useStorage.unit.test.ts +36 -36
- package/src/hooks/public/usePublicEvent.ts +1 -1
- package/src/hooks/public/usePublicEventLogo.ts +1 -1
- package/src/hooks/public/usePublicRouteParams.ts +1 -1
- package/src/hooks/useDataTableState.ts +8 -18
- package/src/hooks/useFocusManagement.ts +2 -2
- package/src/hooks/useFocusTrap.ts +4 -4
- package/src/hooks/useFormDialog.ts +8 -7
- package/src/hooks/useInactivityTracker.ts +1 -1
- package/src/hooks/usePermissionCache.ts +1 -1
- package/src/hooks/useSecureDataAccess.ts +19 -4
- package/src/hooks/useToast.ts +2 -2
- package/src/providers/__tests__/OrganisationProvider.test.tsx +57 -13
- package/src/providers/__tests__/ProviderLifecycle.test.tsx +21 -6
- package/src/providers/__tests__/UnifiedAuthProvider.test.tsx +10 -10
- package/src/providers/services/UnifiedAuthProvider.tsx +22 -22
- package/src/providers/services/__tests__/AuthServiceProvider.integration.test.tsx +13 -3
- package/src/rbac/__tests__/adapters.comprehensive.test.tsx +24 -24
- package/src/rbac/components/EnhancedNavigationMenu.tsx +1 -1
- package/src/rbac/components/NavigationGuard.tsx +1 -1
- package/src/rbac/components/NavigationProvider.tsx +1 -1
- package/src/rbac/components/PagePermissionGuard.tsx +1 -1
- package/src/rbac/components/PagePermissionProvider.tsx +1 -1
- package/src/rbac/components/PermissionEnforcer.tsx +1 -1
- package/src/rbac/components/RoleBasedRouter.tsx +1 -1
- package/src/rbac/components/SecureDataProvider.tsx +1 -1
- package/src/rbac/secureClient.ts +12 -0
- package/src/utils/security/secureDataAccess.test.ts +31 -20
- package/src/utils/security/secureDataAccess.ts +4 -3
- package/dist/chunk-6C4YBBJM.js +0 -628
- package/dist/chunk-6C4YBBJM.js.map +0 -1
- package/dist/chunk-7D4SUZUM.js 2.map +0 -1
- package/dist/chunk-7EQTDTTJ.js.map +0 -1
- package/dist/chunk-7FLMSG37.js 2.map +0 -1
- package/dist/chunk-7FLMSG37.js.map +0 -1
- package/dist/chunk-E3SPN4VZ.js +0 -12917
- package/dist/chunk-E3SPN4VZ.js.map +0 -1
- package/dist/chunk-E66EQZE6 5.js +0 -37
- package/dist/chunk-E66EQZE6.js 2.map +0 -1
- package/dist/chunk-HWIIPPNI.js.map +0 -1
- package/dist/chunk-I7PSE6JW 5.js +0 -191
- package/dist/chunk-I7PSE6JW.js 2.map +0 -1
- package/dist/chunk-KNC55RTG.js 5.map +0 -1
- package/dist/chunk-KQCRWDSA.js 5.map +0 -1
- package/dist/chunk-LFNCN2SP.js 2.map +0 -1
- package/dist/chunk-LFNCN2SP.js.map +0 -1
- package/dist/chunk-LMC26NLJ 2.js +0 -84
- package/dist/chunk-NOAYCWCX.js +0 -4993
- package/dist/chunk-NOAYCWCX.js.map +0 -1
- package/dist/chunk-QWWZ5CAQ.js.map +0 -1
- package/dist/chunk-QXHPKYJV 3.js +0 -113
- package/dist/chunk-R77UEZ4E 3.js +0 -68
- package/dist/chunk-VBXEHIUJ.js 6.map +0 -1
- package/dist/chunk-XNXXZ43G.js.map +0 -1
- package/dist/chunk-ZSAAAMVR 6.js +0 -25
- package/dist/components.js 5.map +0 -1
- package/dist/styles/index 2.js +0 -12
- package/dist/styles/index.js 5.map +0 -1
- package/dist/theming/runtime 5.js +0 -19
- package/dist/theming/runtime.js 5.map +0 -1
- /package/dist/{DataTable-5FU7IESH.js.map → DataTable-DQ7RSOHE.js.map} +0 -0
- /package/dist/{UnifiedAuthProvider-RGJTDE2C.js.map → UnifiedAuthProvider-ATAP5UTR.js.map} +0 -0
- /package/dist/{chunk-BC4IJKSL.js.map → chunk-JBKQ3SAO.js.map} +0 -0
- /package/dist/{chunk-QWWZ5CAQ.js 3.map → chunk-LXQLPRQ2.js.map} +0 -0
- /package/examples/{rbac → RBAC}/CompleteRBACExample.tsx +0 -0
- /package/examples/{rbac → RBAC}/EventBasedApp.tsx +0 -0
- /package/examples/{rbac → RBAC}/PermissionExample.tsx +0 -0
- /package/examples/{rbac → RBAC}/index.ts +0 -0
|
@@ -385,6 +385,13 @@ function scanFile(filePath, manifest) {
|
|
|
385
385
|
const content = fs.readFileSync(filePath, 'utf8');
|
|
386
386
|
const relativePath = path.relative(process.cwd(), filePath);
|
|
387
387
|
|
|
388
|
+
// Normalize path for cross-platform compatibility (handle both forward and backslash paths)
|
|
389
|
+
const normalizedPath = relativePath.replace(/\\/g, '/');
|
|
390
|
+
|
|
391
|
+
// Skip Edge Functions - they run in Deno, not React, so React hooks aren't available
|
|
392
|
+
// Direct Supabase auth calls are the correct approach in Edge Functions
|
|
393
|
+
const isEdgeFunction = normalizedPath.includes('supabase/functions/');
|
|
394
|
+
|
|
388
395
|
// Check for restricted imports
|
|
389
396
|
manifest.restrictedImports.forEach(({ module, reason }) => {
|
|
390
397
|
const importPattern = new RegExp(`from\\s+['"]${module.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}['"]`, 'g');
|
|
@@ -660,9 +667,15 @@ function scanFile(filePath, manifest) {
|
|
|
660
667
|
|
|
661
668
|
// Skip Edge Functions - they run in Deno, not React, so React hooks are not available
|
|
662
669
|
// Edge Functions MUST use direct supabase.auth.getUser() calls - this is correct and required
|
|
663
|
-
//
|
|
664
|
-
|
|
665
|
-
|
|
670
|
+
// isEdgeFunction is already defined above
|
|
671
|
+
|
|
672
|
+
// Other auth patterns - defined here so it's accessible in all scopes
|
|
673
|
+
const otherAuthPatterns = [
|
|
674
|
+
{ pattern: /\.auth\.signIn\s*\(/g, method: 'signIn' },
|
|
675
|
+
{ pattern: /\.auth\.signUp\s*\(/g, method: 'signUp' },
|
|
676
|
+
{ pattern: /\.auth\.signOut\s*\(/g, method: 'signOut' },
|
|
677
|
+
{ pattern: /\.auth\.onAuthStateChange\s*\(/g, method: 'onAuthStateChange' }
|
|
678
|
+
];
|
|
666
679
|
|
|
667
680
|
// Skip all auth checks for Edge Functions - they cannot use React hooks
|
|
668
681
|
if (isEdgeFunction) {
|
|
@@ -684,14 +697,6 @@ function scanFile(filePath, manifest) {
|
|
|
684
697
|
{ pattern: /\w+\.auth\.getSession\s*\(/g, method: 'getSession', specific: false }
|
|
685
698
|
];
|
|
686
699
|
|
|
687
|
-
// Other auth patterns
|
|
688
|
-
const otherAuthPatterns = [
|
|
689
|
-
{ pattern: /\.auth\.signIn\s*\(/g, method: 'signIn' },
|
|
690
|
-
{ pattern: /\.auth\.signUp\s*\(/g, method: 'signUp' },
|
|
691
|
-
{ pattern: /\.auth\.signOut\s*\(/g, method: 'signOut' },
|
|
692
|
-
{ pattern: /\.auth\.onAuthStateChange\s*\(/g, method: 'onAuthStateChange' }
|
|
693
|
-
];
|
|
694
|
-
|
|
695
700
|
// Check if file actually uses useUnifiedAuth hook (not just imports it)
|
|
696
701
|
const usesUnifiedAuthHook = /useUnifiedAuth\s*\(/.test(content);
|
|
697
702
|
const hasUnifiedAuthImport = /UnifiedAuthProvider/.test(content) ||
|
|
@@ -760,7 +765,8 @@ function scanFile(filePath, manifest) {
|
|
|
760
765
|
const isInLineComment = /\/\/[^\n]*$/.test(lineUpToMatch);
|
|
761
766
|
|
|
762
767
|
// Only skip if clearly in a comment - be conservative
|
|
763
|
-
|
|
768
|
+
// Also skip Edge Functions - they run in Deno, not React, so React hooks aren't available
|
|
769
|
+
if (!isInLineComment && !isEdgeFunction) {
|
|
764
770
|
violations.directSupabaseAuth.push({
|
|
765
771
|
file: relativePath,
|
|
766
772
|
line: getLineNumber(content, matchText),
|
|
@@ -794,7 +800,8 @@ function scanFile(filePath, manifest) {
|
|
|
794
800
|
const lineUpToMatch = content.substring(lineStart, searchIndex);
|
|
795
801
|
const isInLineComment = /\/\/[^\n]*$/.test(lineUpToMatch);
|
|
796
802
|
|
|
797
|
-
|
|
803
|
+
// Skip Edge Functions - they run in Deno, not React, so React hooks aren't available
|
|
804
|
+
if (!isInLineComment && !isEdgeFunction) {
|
|
798
805
|
// Calculate line number from index
|
|
799
806
|
const lineNum = content.substring(0, searchIndex).split('\n').length;
|
|
800
807
|
|
|
@@ -855,7 +862,8 @@ function scanFile(filePath, manifest) {
|
|
|
855
862
|
(doubleQuotes % 2 === 1 && beforeMatch.endsWith('"')) ||
|
|
856
863
|
(backticks % 2 === 1 && beforeMatch.endsWith('`'));
|
|
857
864
|
|
|
858
|
-
|
|
865
|
+
// Skip Edge Functions - they run in Deno, not React, so React hooks aren't available
|
|
866
|
+
if (!isInLineComment && !isInBlockComment && !isInString && !usesUnifiedAuthHook && !isEdgeFunction) {
|
|
859
867
|
violations.directSupabaseAuth.push({
|
|
860
868
|
file: relativePath,
|
|
861
869
|
line: getLineNumber(content, matchText),
|
|
@@ -2500,6 +2508,10 @@ function findSourceFiles(dir, fileList = []) {
|
|
|
2500
2508
|
const stat = fs.statSync(fullPath);
|
|
2501
2509
|
|
|
2502
2510
|
if (stat.isDirectory()) {
|
|
2511
|
+
// Skip Edge Functions directory - they run in Deno, not React, so React hooks aren't available
|
|
2512
|
+
if (item === 'functions' && dir.includes('supabase')) {
|
|
2513
|
+
return; // Skip supabase/functions directory
|
|
2514
|
+
}
|
|
2503
2515
|
if (!ignoreDirs.includes(item) && !item.startsWith('.')) {
|
|
2504
2516
|
findSourceFiles(fullPath, fileList);
|
|
2505
2517
|
}
|
|
@@ -2584,5 +2596,12 @@ if (require.main === module) {
|
|
|
2584
2596
|
}
|
|
2585
2597
|
}
|
|
2586
2598
|
|
|
2587
|
-
module.exports = {
|
|
2599
|
+
module.exports = {
|
|
2600
|
+
main,
|
|
2601
|
+
scanFile,
|
|
2602
|
+
generateReport,
|
|
2603
|
+
loadManifest,
|
|
2604
|
+
findProjectRoot,
|
|
2605
|
+
findSourceFiles
|
|
2606
|
+
};
|
|
2588
2607
|
|
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Installation Script for pace-core Cursor Rules
|
|
5
|
+
* @package @jmruthers/pace-core
|
|
6
|
+
* @module Scripts/install-cursor-rules
|
|
7
|
+
*
|
|
8
|
+
* Copies cursor rules from pace-core to consuming app's .cursor/rules/ directory.
|
|
9
|
+
* This is an opt-in script - it does NOT run automatically via postinstall.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
const fs = require('fs');
|
|
13
|
+
const path = require('path');
|
|
14
|
+
|
|
15
|
+
// ANSI color codes for terminal output
|
|
16
|
+
const colors = {
|
|
17
|
+
reset: '\x1b[0m',
|
|
18
|
+
green: '\x1b[32m',
|
|
19
|
+
yellow: '\x1b[33m',
|
|
20
|
+
blue: '\x1b[34m',
|
|
21
|
+
cyan: '\x1b[36m',
|
|
22
|
+
bold: '\x1b[1m',
|
|
23
|
+
red: '\x1b[31m'
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
// Check for safety guards
|
|
27
|
+
function checkSafetyGuards() {
|
|
28
|
+
// Check environment variable
|
|
29
|
+
if (process.env.PACE_CURSOR_RULES_DISABLED === '1') {
|
|
30
|
+
console.log(`${colors.yellow}Skipping cursor rules installation: PACE_CURSOR_RULES_DISABLED=1${colors.reset}`);
|
|
31
|
+
process.exit(0);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// Check for .git folder (safety guard for CI)
|
|
35
|
+
const cwd = process.cwd();
|
|
36
|
+
if (!fs.existsSync(path.join(cwd, '.git'))) {
|
|
37
|
+
console.log(`${colors.yellow}Skipping cursor rules installation: No .git folder found${colors.reset}`);
|
|
38
|
+
console.log(`${colors.yellow}This is a safety guard. If you want to install anyway, run with --force${colors.reset}`);
|
|
39
|
+
if (!process.argv.includes('--force')) {
|
|
40
|
+
process.exit(0);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Get pace-core package path
|
|
46
|
+
function getPaceCorePath() {
|
|
47
|
+
// Try to find pace-core in node_modules
|
|
48
|
+
const cwd = process.cwd();
|
|
49
|
+
const paceCorePath = path.join(cwd, 'node_modules', '@jmruthers', 'pace-core');
|
|
50
|
+
|
|
51
|
+
if (fs.existsSync(paceCorePath)) {
|
|
52
|
+
return paceCorePath;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// If not found, try relative to script location (for development)
|
|
56
|
+
const scriptDir = __dirname;
|
|
57
|
+
const relativePath = path.join(scriptDir, '..');
|
|
58
|
+
if (fs.existsSync(path.join(relativePath, 'cursor-rules'))) {
|
|
59
|
+
return relativePath;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
console.error(`${colors.red}Error: Could not find @jmruthers/pace-core package${colors.reset}`);
|
|
63
|
+
console.error(`${colors.red}Make sure pace-core is installed: npm install @jmruthers/pace-core${colors.reset}`);
|
|
64
|
+
process.exit(1);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Get cursor rules source directory
|
|
68
|
+
function getCursorRulesSource(paceCorePath) {
|
|
69
|
+
const rulesPath = path.join(paceCorePath, 'cursor-rules');
|
|
70
|
+
if (!fs.existsSync(rulesPath)) {
|
|
71
|
+
console.error(`${colors.red}Error: cursor-rules directory not found in pace-core${colors.reset}`);
|
|
72
|
+
process.exit(1);
|
|
73
|
+
}
|
|
74
|
+
return rulesPath;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Get target directory (.cursor/rules in app root)
|
|
78
|
+
function getCursorRulesTarget() {
|
|
79
|
+
const cwd = process.cwd();
|
|
80
|
+
const targetDir = path.join(cwd, '.cursor', 'rules');
|
|
81
|
+
return targetDir;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// Read rule file and extract version metadata
|
|
85
|
+
function getRuleVersion(filePath) {
|
|
86
|
+
try {
|
|
87
|
+
const content = fs.readFileSync(filePath, 'utf8');
|
|
88
|
+
const frontmatterMatch = content.match(/^---\n([\s\S]*?)\n---/);
|
|
89
|
+
if (frontmatterMatch) {
|
|
90
|
+
const frontmatter = frontmatterMatch[1];
|
|
91
|
+
const versionMatch = frontmatter.match(/paceCoreVersion:\s*["']?([^"'\n]+)["']?/);
|
|
92
|
+
const rulesVersionMatch = frontmatter.match(/rulesVersion:\s*["']?([^"'\n]+)["']?/);
|
|
93
|
+
return {
|
|
94
|
+
paceCoreVersion: versionMatch ? versionMatch[1] : null,
|
|
95
|
+
rulesVersion: rulesVersionMatch ? rulesVersionMatch[1] : null
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
} catch (error) {
|
|
99
|
+
// Ignore errors
|
|
100
|
+
}
|
|
101
|
+
return { paceCoreVersion: null, rulesVersion: null };
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Check if file needs updating
|
|
105
|
+
function needsUpdate(sourcePath, targetPath, force) {
|
|
106
|
+
if (force) {
|
|
107
|
+
return true;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
if (!fs.existsSync(targetPath)) {
|
|
111
|
+
return true;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// Compare versions first (fast check)
|
|
115
|
+
const sourceVersion = getRuleVersion(sourcePath);
|
|
116
|
+
const targetVersion = getRuleVersion(targetPath);
|
|
117
|
+
|
|
118
|
+
// If versions differ, needs update
|
|
119
|
+
if (sourceVersion.paceCoreVersion !== targetVersion.paceCoreVersion ||
|
|
120
|
+
sourceVersion.rulesVersion !== targetVersion.rulesVersion) {
|
|
121
|
+
return true;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// If versions match but content differs, also needs update
|
|
125
|
+
// (handles cases where version metadata wasn't updated)
|
|
126
|
+
try {
|
|
127
|
+
const sourceContent = fs.readFileSync(sourcePath, 'utf8');
|
|
128
|
+
const targetContent = fs.readFileSync(targetPath, 'utf8');
|
|
129
|
+
if (sourceContent !== targetContent) {
|
|
130
|
+
return true;
|
|
131
|
+
}
|
|
132
|
+
} catch (error) {
|
|
133
|
+
// If we can't read files, assume update needed
|
|
134
|
+
return true;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
return false;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// Install cursor rules
|
|
141
|
+
function installCursorRules(force = false) {
|
|
142
|
+
checkSafetyGuards();
|
|
143
|
+
|
|
144
|
+
const paceCorePath = getPaceCorePath();
|
|
145
|
+
const sourceDir = getCursorRulesSource(paceCorePath);
|
|
146
|
+
const targetDir = getCursorRulesTarget();
|
|
147
|
+
|
|
148
|
+
// Create target directory if it doesn't exist
|
|
149
|
+
if (!fs.existsSync(targetDir)) {
|
|
150
|
+
fs.mkdirSync(targetDir, { recursive: true });
|
|
151
|
+
console.log(`${colors.green}Created .cursor/rules/ directory${colors.reset}`);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// Read all .mdc files from source
|
|
155
|
+
const files = fs.readdirSync(sourceDir)
|
|
156
|
+
.filter(file => file.endsWith('.mdc'))
|
|
157
|
+
.sort(); // Sort for consistent output
|
|
158
|
+
|
|
159
|
+
if (files.length === 0) {
|
|
160
|
+
console.log(`${colors.yellow}No cursor rules found in pace-core${colors.reset}`);
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
console.log(`${colors.cyan}Installing cursor rules from pace-core...${colors.reset}\n`);
|
|
165
|
+
|
|
166
|
+
let installed = 0;
|
|
167
|
+
let skipped = 0;
|
|
168
|
+
let updated = 0;
|
|
169
|
+
|
|
170
|
+
files.forEach(file => {
|
|
171
|
+
const sourcePath = path.join(sourceDir, file);
|
|
172
|
+
const targetPath = path.join(targetDir, file);
|
|
173
|
+
|
|
174
|
+
// Check if this is a pace-core rule (00-09) or app rule (50+)
|
|
175
|
+
const ruleNumber = parseInt(file.match(/^(\d+)-/)?.[1] || '99');
|
|
176
|
+
const isPaceCoreRule = ruleNumber >= 0 && ruleNumber <= 9;
|
|
177
|
+
|
|
178
|
+
if (fs.existsSync(targetPath)) {
|
|
179
|
+
if (isPaceCoreRule) {
|
|
180
|
+
// pace-core rules should be automatically updated when they change
|
|
181
|
+
if (needsUpdate(sourcePath, targetPath, force)) {
|
|
182
|
+
// Automatically update pace-core rules when versions differ or content changed
|
|
183
|
+
fs.copyFileSync(sourcePath, targetPath);
|
|
184
|
+
console.log(`${colors.green}✓${colors.reset} Updated ${file}${force ? ' (forced)' : ''}`);
|
|
185
|
+
updated++;
|
|
186
|
+
} else {
|
|
187
|
+
console.log(`${colors.blue}○${colors.reset} ${file} is up to date`);
|
|
188
|
+
skipped++;
|
|
189
|
+
}
|
|
190
|
+
} else {
|
|
191
|
+
// App rules (50+) are never overwritten
|
|
192
|
+
console.log(`${colors.blue}○${colors.reset} ${file} is a custom rule, skipping`);
|
|
193
|
+
skipped++;
|
|
194
|
+
}
|
|
195
|
+
} else {
|
|
196
|
+
// File doesn't exist, install it
|
|
197
|
+
fs.copyFileSync(sourcePath, targetPath);
|
|
198
|
+
console.log(`${colors.green}✓${colors.reset} Installed ${file}`);
|
|
199
|
+
installed++;
|
|
200
|
+
}
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
console.log(`\n${colors.bold}Installation Summary:${colors.reset}`);
|
|
204
|
+
console.log(` ${colors.green}Installed:${colors.reset} ${installed}`);
|
|
205
|
+
console.log(` ${colors.yellow}Updated:${colors.reset} ${updated}`);
|
|
206
|
+
console.log(` ${colors.blue}Skipped:${colors.reset} ${skipped}`);
|
|
207
|
+
console.log(`\n${colors.cyan}Cursor rules are now available in .cursor/rules/${colors.reset}`);
|
|
208
|
+
console.log(`${colors.cyan}Restart Cursor to load the new rules.${colors.reset}`);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
// Main execution
|
|
212
|
+
function main() {
|
|
213
|
+
const force = process.argv.includes('--force');
|
|
214
|
+
|
|
215
|
+
if (force) {
|
|
216
|
+
console.log(`${colors.yellow}Warning: --force flag is set. This will overwrite existing pace-core rules.${colors.reset}\n`);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
try {
|
|
220
|
+
installCursorRules(force);
|
|
221
|
+
} catch (error) {
|
|
222
|
+
console.error(`${colors.red}Error installing cursor rules:${colors.reset}`);
|
|
223
|
+
console.error(error.message);
|
|
224
|
+
if (error.stack) {
|
|
225
|
+
console.error(error.stack);
|
|
226
|
+
}
|
|
227
|
+
process.exit(1);
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
// Run if called directly
|
|
232
|
+
if (require.main === module) {
|
|
233
|
+
main();
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
module.exports = { installCursorRules, getCursorRulesTarget };
|
|
@@ -251,8 +251,7 @@ function getBadgeClasses(variant: BadgeVariant = 'solid-main-normal'): string {
|
|
|
251
251
|
* <Badge variant="soft-acc-strong">Featured</Badge>
|
|
252
252
|
* ```
|
|
253
253
|
*/
|
|
254
|
-
|
|
255
|
-
({ className, variant = 'solid-main-normal', ...props }, ref) => {
|
|
254
|
+
function Badge({ className, variant = 'solid-main-normal', ref, ...props }: BadgeProps & { ref?: React.Ref<HTMLSpanElement> }) {
|
|
256
255
|
const isSoftVariant = variant.startsWith('soft-');
|
|
257
256
|
|
|
258
257
|
if (isSoftVariant) {
|
|
@@ -291,8 +290,7 @@ const Badge = React.forwardRef<HTMLSpanElement, BadgeProps>(
|
|
|
291
290
|
{...props}
|
|
292
291
|
/>
|
|
293
292
|
);
|
|
294
|
-
|
|
295
|
-
);
|
|
293
|
+
}
|
|
296
294
|
|
|
297
295
|
Badge.displayName = 'Badge';
|
|
298
296
|
|
|
@@ -198,11 +198,12 @@ export function ButtonGroup({
|
|
|
198
198
|
// Type assertion needed because React.ComponentType doesn't guarantee displayName
|
|
199
199
|
const componentType = child.type as React.ComponentType<unknown> & { displayName?: string };
|
|
200
200
|
if (componentType.displayName === 'Button') {
|
|
201
|
+
const childProps = child.props as { variant?: string; size?: string; [key: string]: unknown };
|
|
201
202
|
return React.cloneElement(child, {
|
|
202
|
-
variant:
|
|
203
|
-
size:
|
|
204
|
-
...
|
|
205
|
-
});
|
|
203
|
+
variant: childProps.variant || variant,
|
|
204
|
+
size: childProps.size || size,
|
|
205
|
+
...childProps
|
|
206
|
+
} as typeof childProps);
|
|
206
207
|
}
|
|
207
208
|
}
|
|
208
209
|
return child;
|
|
@@ -350,7 +350,7 @@ const Calendar = React.forwardRef<HTMLTableElement, CalendarProps>(
|
|
|
350
350
|
goToMonth(nextMonth);
|
|
351
351
|
};
|
|
352
352
|
|
|
353
|
-
const monthGridElement = monthGridChild as React.ReactElement & {
|
|
353
|
+
const monthGridElement = monthGridChild as React.ReactElement<any> & {
|
|
354
354
|
ref?: React.Ref<HTMLTableElement | null>;
|
|
355
355
|
};
|
|
356
356
|
const mergedRef =
|