Last active
January 14, 2026 12:36
-
-
Save drikusroor/d891d53f953be009a1ffd7a0cebcd7f8 to your computer and use it in GitHub Desktop.
React / Next.js CamelCase to kebab-case migration audit script
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /** | |
| bun run audit-kebab.ts | |
| π Scanning ./src for CamelCase/PascalCase violations (Files, Imports, Mocks)... | |
| π **File Naming Violations** (1) | |
| ----------------------------------- | |
| β stores/UserStore.ts | |
| π **Reference Violations** (4) | |
| ----------------------------------- | |
| β [Import] in hooks/use-auth.ts: "@/stores/UserStore" | |
| β [Import] in hooks/use-user.test.tsx: "@/stores/UserStore" | |
| β [Import] in hooks/use-user.tsx: "@/stores/UserStore" | |
| β [Import] in stores/user-store.test.ts: "./UserStore" | |
| Found 5 issues. | |
| or | |
| β― bun run audit-kebab.ts | |
| π Scanning ./src for CamelCase/PascalCase violations (Files, Imports, Mocks)... | |
| β All clear! No CamelCase detected. | |
| Made with Google Gemini. | |
| **/ | |
| import { Glob } from 'bun'; | |
| import { resolve } from 'path'; | |
| // --- Configuration --- | |
| const SRC_DIR = './src'; | |
| const EXTENSIONS = ['ts', 'tsx', 'js', 'jsx']; | |
| // --- Logic --- | |
| console.log( | |
| `π Scanning ${SRC_DIR} for CamelCase/PascalCase violations (Files, Imports, Mocks)...\n`, | |
| ); | |
| const glob = new Glob(`**/*.{${EXTENSIONS.join(',')}}`); | |
| const violations = { | |
| fileNames: [] as string[], | |
| references: [] as { file: string; type: string; path: string }[], | |
| }; | |
| // Regex explanation: | |
| // (?: ... ) -> Non-capturing group for the keyword prefixes | |
| // from -> standard 'import x from "y"' | |
| // import\(? -> dynamic 'import("y")' | |
| // require\( -> CommonJS 'require("y")' | |
| // \.mock\( -> Vitest/Jest 'vi.mock("y")' | |
| // \s* -> Allow whitespace | |
| // ['"]([^'"]+)['"] -> Capture the string inside the quotes | |
| const importPattern = /(?:from|import\(?|require\(|\.mock\()\s*['"]([^'"]+)['"]/g; | |
| const hasUpperCase = (str: string) => /[A-Z]/.test(str); | |
| const isLocalImport = (path: string) => path.startsWith('.') || path.startsWith('@/'); | |
| for await (const filePath of glob.scan({ cwd: SRC_DIR })) { | |
| const fullPath = resolve(SRC_DIR, filePath); | |
| const fileName = filePath.split('/').pop() || ''; | |
| // 1. Check File Name | |
| if (hasUpperCase(fileName)) { | |
| violations.fileNames.push(filePath); | |
| } | |
| // 2. Check Content (Imports & Mocks) | |
| const content = await Bun.file(fullPath).text(); | |
| let match; | |
| importPattern.lastIndex = 0; | |
| while ((match = importPattern.exec(content)) !== null) { | |
| const matchedString = match[0]; // The full match (e.g., 'vi.mock("./HistorySearch")') | |
| const importPath = match[1]; // The capture group (e.g., './HistorySearch') | |
| if (isLocalImport(importPath)) { | |
| if (hasUpperCase(importPath)) { | |
| // Determine type for better logging | |
| let type = 'Import'; | |
| if (matchedString.includes('.mock')) type = 'Mock'; | |
| else if (matchedString.includes('require')) type = 'Require'; | |
| violations.references.push({ file: filePath, type, path: importPath }); | |
| } | |
| } | |
| } | |
| } | |
| // --- Report --- | |
| if (violations.fileNames.length > 0) { | |
| console.log(`\nπ **File Naming Violations** (${violations.fileNames.length})`); | |
| console.log('-----------------------------------'); | |
| violations.fileNames.forEach(f => console.log(` β ${f}`)); | |
| } | |
| if (violations.references.length > 0) { | |
| console.log(`\nπ **Reference Violations** (${violations.references.length})`); | |
| console.log('-----------------------------------'); | |
| violations.references.forEach(v => console.log(` β [${v.type}] in ${v.file}: \t"${v.path}"`)); | |
| } | |
| if (violations.fileNames.length === 0 && violations.references.length === 0) { | |
| console.log('β All clear! No CamelCase detected.'); | |
| } else { | |
| console.log(`\nFound ${violations.fileNames.length + violations.references.length} issues.`); | |
| process.exit(1); | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment