-
Notifications
You must be signed in to change notification settings - Fork 13
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: add lint for naming design rules (#106)
Co-Authored-By: [email protected] <[email protected]>
- Loading branch information
1 parent
86b70f5
commit 3535789
Showing
5 changed files
with
138 additions
and
0 deletions.
There are no files selected for viewing
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
name: Test & Lint | ||
|
||
on: | ||
pull_request: | ||
types: [opened, synchronize, reopened, ready_for_review] | ||
|
||
jobs: | ||
test: | ||
name: Test and Lint | ||
runs-on: ubuntu-latest | ||
if: github.event.pull_request.draft == false | ||
|
||
steps: | ||
- name: Checkout code | ||
uses: actions/checkout@v3 | ||
|
||
- name: Setup Node.js | ||
uses: actions/setup-node@v3 | ||
with: | ||
node-version: '20' | ||
|
||
- name: Setup Bun | ||
uses: oven-sh/setup-bun@v1 | ||
with: | ||
bun-version: latest | ||
|
||
- name: Install dependencies | ||
run: bun install | ||
|
||
- name: Run Design Rule Lint | ||
run: bun run lint:design | ||
|
||
- name: Run Type Check | ||
run: bunx tsc --noEmit | ||
|
||
- name: Run Tests | ||
run: bun test |
This file contains 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
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
import fs from "fs" | ||
import path from "path" | ||
import { glob } from "glob" | ||
|
||
// Regex patterns for finding Zod objects and enums | ||
const zodObjectPattern = /z\.object\(\s*{([^}]*)}\s*\)/g | ||
const zodEnumPattern = /z\.enum\(\s*\[(([^[\]]*))]\s*\)/g | ||
const propertyPattern = /\s*(\w+)\s*:/g | ||
const enumValuePattern = /"([^"]+)"|'([^']+)'/g | ||
|
||
function isCamelCase(str: string): boolean { | ||
return /^[a-z][a-zA-Z0-9]*$/.test(str) && /[A-Z]/.test(str) | ||
} | ||
|
||
function isLowerSnakeCase(str: string): boolean { | ||
return /^[a-z0-9]+(_[a-z0-9]+)*$/.test(str) | ||
} | ||
|
||
function checkFile(filePath: string): boolean { | ||
let hasViolations = false | ||
const content = fs.readFileSync(filePath, "utf-8") | ||
|
||
// Check z.object() properties for camelCase | ||
let match: RegExpExecArray | null | ||
while ((match = zodObjectPattern.exec(content)) !== null) { | ||
const objectContent = match[1] | ||
if (!objectContent) continue | ||
|
||
let propMatch: RegExpExecArray | null | ||
while ((propMatch = propertyPattern.exec(objectContent)) !== null) { | ||
const propertyName = propMatch[1] | ||
if (propertyName && isCamelCase(propertyName)) { | ||
console.error( | ||
`Error: Found camelCase property "${propertyName}" in Zod object in file: ${filePath}` | ||
) | ||
hasViolations = true | ||
} | ||
} | ||
propertyPattern.lastIndex = 0 | ||
} | ||
|
||
// Reset regex lastIndex | ||
zodObjectPattern.lastIndex = 0 | ||
|
||
// Check z.enum() values for snake_case | ||
while ((match = zodEnumPattern.exec(content)) !== null) { | ||
const enumContent = match[1] | ||
if (!enumContent) continue | ||
|
||
let enumMatch: RegExpExecArray | null | ||
while ((enumMatch = enumValuePattern.exec(enumContent)) !== null) { | ||
const enumValue = enumMatch[1] || enumMatch[2] // Handle both single and double quotes | ||
if (enumValue && !isLowerSnakeCase(enumValue)) { | ||
console.error( | ||
`Error: Found non-snake_case enum value "${enumValue}" in file: ${filePath}` | ||
) | ||
hasViolations = true | ||
} | ||
} | ||
enumValuePattern.lastIndex = 0 | ||
} | ||
|
||
return hasViolations | ||
} | ||
|
||
function main() { | ||
const srcDir = path.join(__dirname, "..", "src") | ||
const files = glob.sync("**/*.ts", { cwd: srcDir }) | ||
|
||
let hasViolations = false | ||
|
||
for (const file of files) { | ||
const fullPath = path.join(srcDir, file) | ||
if (checkFile(fullPath)) { | ||
hasViolations = true | ||
} | ||
} | ||
|
||
if (hasViolations) { | ||
console.error("\nLinting failed! Please fix the above violations.") | ||
process.exit(1) | ||
} else { | ||
console.log("Design rules check passed!") | ||
} | ||
} | ||
|
||
main() |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
import { z } from "zod" | ||
|
||
// This file contains intentional violations for testing the lint script | ||
export const test_camel_case = z.object({ | ||
myProperty: z.string(), // Should fail: camelCase | ||
another_prop: z.string() // Should pass: snake_case | ||
}) | ||
|
||
export const test_enum_case = z.enum([ | ||
"goodCase", // Should fail: not snake_case | ||
"snake_case_ok" // Should pass: snake_case | ||
]) |