Skip to content

Commit f2c7703

Browse files
committed
feat(husky): add Flatten JSON tool and use npx zx
- Add support for 'includePatterns' in tool configuration - Add 'Flatten JSON' tool configuration - Update workflow to respect 'includePatterns' - Switch git hooks to use 'npx zx' for better portability - Add tests for new functionality - Update documentation
1 parent 9488b2c commit f2c7703

File tree

8 files changed

+101
-9
lines changed

8 files changed

+101
-9
lines changed

booster/.husky/commit-msg

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,4 @@ cd "$ROOT_DIR"
1212
HOOK="$CWD/commit-msg.ts"
1313

1414
# Use the generic runner script to execute the TypeScript hook
15-
exec "$CWD/shared/runner.sh" pnpm zx $HOOK "$@"
15+
exec "$CWD/shared/runner.sh" npx zx "$HOOK" "$@"

booster/.husky/pre-commit

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,4 @@ cd "$ROOT_DIR"
1212
HOOK="$CWD/pre-commit.ts"
1313

1414
# Use the generic runner script to execute the TypeScript hook
15-
exec "$CWD/shared/runner.sh" pnpm zx $HOOK "$@"
15+
exec "$CWD/shared/runner.sh" npx zx "$HOOK" "$@"

booster/.husky/pre-push

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,4 @@ cd "$ROOT_DIR"
1212
HOOK="$CWD/pre-push.ts"
1313

1414
# Use the generic runner script to execute the TypeScript hook
15-
exec "$CWD/shared/runner.sh" pnpm zx $HOOK "$@"
15+
exec "$CWD/shared/runner.sh" npx zx "$HOOK" "$@"

booster/.husky/shared/tools.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ export const TOOLS: ToolConfig[] = [
2323
{
2424
name: 'ESLint',
2525
command: 'eslint',
26-
args: ['--fix', '--cache'],
26+
args: ['--fix', '--cache', '--no-warn-ignored'],
2727
type: 'node',
2828
stagesFilesAfter: true,
2929
extensions: ['.js', '.jsx', '.ts', '.tsx', '.vue', '.mjs', '.cjs'],
@@ -52,6 +52,16 @@ export const TOOLS: ToolConfig[] = [
5252
],
5353
group: 'format',
5454
},
55+
{
56+
name: 'Flatten JSON',
57+
command: 'node',
58+
args: ['./tools/flatten-json.ts'],
59+
type: 'node',
60+
extensions: ['.json'],
61+
includePatterns: ['apps/*/src/locales/*.json'],
62+
stagesFilesAfter: true,
63+
group: 'format',
64+
},
5565
{
5666
name: 'Stylelint',
5767
command: 'stylelint',

booster/.husky/shared/types.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@ export interface ToolConfig {
4040
extensions?: string[]
4141
/** If true, re-stages files after execution (useful for fixers) */
4242
stagesFilesAfter?: boolean
43+
/** Only run on files matching any of these glob patterns (or regexes) */
44+
includePatterns?: (string | RegExp)[]
4345
/** If false, does not pass the list of staged files to the command. Default is true. */
4446
passFiles?: boolean
4547
/** If true, runs the command for each file individually. Default is false. */

booster/.husky/shared/workflow.ts

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { which, fs } from 'zx'
1+
import { which, fs, path } from 'zx'
22
import {
33
ensureMutagenSync,
44
exec,
@@ -222,7 +222,21 @@ async function prepareTool(tool: ToolConfig, files: string[]): Promise<PreparedT
222222
? files.filter((file) => tool.extensions!.some((ext) => file.endsWith(ext)))
223223
: files
224224

225-
if (filesToRun.length === 0) return null
225+
const filteredFiles = tool.includePatterns
226+
? filesToRun.filter((file) => {
227+
return tool.includePatterns!.some((pattern) => {
228+
if (pattern instanceof RegExp) {
229+
return pattern.test(file)
230+
}
231+
232+
// Use Node.js built-in globbing matching (requires Node.js 22+)
233+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
234+
return (path as any).match(pattern, file);
235+
});
236+
})
237+
: filesToRun;
238+
239+
if (filteredFiles.length === 0) return null
226240

227241
// Check binary existence
228242
if (!(await isToolAvailable(tool))) {
@@ -232,7 +246,7 @@ async function prepareTool(tool: ToolConfig, files: string[]): Promise<PreparedT
232246

233247
return {
234248
tool,
235-
files: filesToRun,
249+
files: filteredFiles,
236250
description: tool.description || `Running ${tool.name}...`,
237251
}
238252
}

booster/.husky/tests/shared/workflow.test.ts

Lines changed: 56 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import {
1111
initEnvironment
1212
} from '../../shared/core'
1313
import { shouldSkipDuringMerge, stageFiles } from '../../shared/git'
14-
import { fs, which, $ } from 'zx'
14+
import { fs, which, $, path } from 'zx'
1515

1616
// Mock dependencies
1717
vi.mock('../../shared/core', () => ({
@@ -52,6 +52,9 @@ vi.mock('zx', () => ({
5252
which: vi.fn(),
5353
$: {
5454
verbose: false
55+
},
56+
path: {
57+
match: vi.fn()
5558
}
5659
}))
5760

@@ -382,6 +385,58 @@ describe('workflow.ts', () => {
382385

383386
expect(exec).toHaveBeenCalledTimes(2)
384387
})
388+
389+
it('should filter files based on includePatterns (regex)', async () => {
390+
vi.mocked(isSkipped).mockReturnValue(false)
391+
vi.mocked(fs.pathExists).mockResolvedValue(true)
392+
vi.mocked(exec).mockResolvedValue({} as any)
393+
394+
const regexTool: ToolConfig = {
395+
...mockTool,
396+
type: 'node',
397+
command: 'check',
398+
extensions: ['.json'], // Add extension to match file extension check first
399+
includePatterns: [/src\/locales\/.*\.json$/]
400+
}
401+
402+
const files = ['apps/app1/src/locales/en.json', 'other/file.json']
403+
await runQualityChecks(files, [regexTool])
404+
405+
// Should run only for matched file
406+
expect(exec).toHaveBeenCalledTimes(1)
407+
const callArgs = vi.mocked(exec).mock.calls[0][0]
408+
expect(callArgs).toContain('apps/app1/src/locales/en.json')
409+
expect(callArgs).not.toContain('other/file.json')
410+
})
411+
412+
it('should filter files based on includePatterns (glob)', async () => {
413+
vi.mocked(isSkipped).mockReturnValue(false)
414+
vi.mocked(fs.pathExists).mockResolvedValue(true)
415+
vi.mocked(exec).mockResolvedValue({} as any)
416+
417+
// Mock path.match behavior
418+
// @ts-ignore
419+
vi.mocked(path.match).mockImplementation((pattern, file) => {
420+
// Simple mock: check if file ends with .json and path contains /locales/
421+
return file.endsWith('.json') && file.includes('/locales/')
422+
})
423+
424+
const globTool: ToolConfig = {
425+
...mockTool,
426+
type: 'node',
427+
command: 'check',
428+
extensions: ['.json'], // Add extension to match file extension check first
429+
includePatterns: ['apps/*/src/locales/*.json']
430+
}
431+
432+
const files = ['apps/app1/src/locales/en.json', 'other/file.json']
433+
await runQualityChecks(files, [globTool])
434+
435+
expect(exec).toHaveBeenCalledTimes(1)
436+
const callArgs = vi.mocked(exec).mock.calls[0][0]
437+
expect(callArgs).toContain('apps/app1/src/locales/en.json')
438+
expect(callArgs).not.toContain('other/file.json')
439+
})
385440
})
386441

387442
describe('runHook', () => {

docs/content/3.tools/4.git_hooks.md

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,16 @@ Disable, override, or add custom tools:
182182
}
183183
```
184184

185+
```json [Filter by Glob Pattern]
186+
{
187+
"tools": {
188+
"Flatten JSON": {
189+
"includePatterns": ["apps/*/src/locales/*.json"]
190+
}
191+
}
192+
}
193+
```
194+
185195
::tip
186196
Alternative config file names: `.githooks.json` or set `GIT_HOOKS_CONFIG` environment variable to a custom path.
187197
::
@@ -269,7 +279,7 @@ HOOKS_ONLY=lint,format git commit -m "..."
269279
| Group | Tools |
270280
|-------|-------|
271281
| `lint` | ESLint, Stylelint, PHP Syntax Check, TypeScript |
272-
| `format` | Prettier, ECS |
282+
| `format` | Prettier, ECS, Flatten JSON |
273283
| `analysis` | PHPStan, Psalm, Deptrac |
274284
| `refactor` | Rector |
275285

@@ -278,6 +288,7 @@ HOOKS_ONLY=lint,format git commit -m "..."
278288
|-----------|---------------------|
279289
| ESLint | `SKIP_ESLINT` |
280290
| Prettier | `SKIP_PRETTIER` |
291+
| Flatten JSON | `SKIP_FLATTEN_JSON` |
281292
| Stylelint | `SKIP_STYLELINT` |
282293
| TypeScript | `SKIP_TYPESCRIPT` |
283294
| PHP Syntax Check | `SKIP_PHP_SYNTAX_CHECK` |

0 commit comments

Comments
 (0)