From 438a27236675d072dcf03363d5aa19e56b7779f9 Mon Sep 17 00:00:00 2001 From: 0xObsidian <131651958+0xObsidian@users.noreply.github.com> Date: Sat, 4 Oct 2025 11:19:34 +0200 Subject: [PATCH 1/2] fix(test): error handling in search-and-replace test Description ----------- The "should handle errors gracefully" test was intentionally triggering errors to verify error handling, but `console.error` was not mocked to suppress output, causing noisy `stderr` logs during test despite all tests passing. Testing the introduced fix -------------------------- ``` npm run test ``` All tests will now pass cleanly without `stderr` error output. --- test/search-and-replace.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/search-and-replace.test.ts b/test/search-and-replace.test.ts index bbf34e9..de4a663 100644 --- a/test/search-and-replace.test.ts +++ b/test/search-and-replace.test.ts @@ -98,7 +98,7 @@ describe('searchAndReplace', () => { }) it('should handle errors gracefully', async () => { - const consoleErrorSpy = vi.spyOn(console, 'error') + const consoleErrorSpy = vi.spyOn(console, 'error').mockImplementation(() => {}) // Mock the file system and simulate an error for readFile mockFs({ From a8b44ac15c4898aae01d7b4c7941bfac5ae420c1 Mon Sep 17 00:00:00 2001 From: 0xObsidian <131651958+0xObsidian@users.noreply.github.com> Date: Sat, 4 Oct 2025 12:38:11 +0200 Subject: [PATCH 2/2] feat: support `in` alias for rename entries in commit `813773d4`, @beeman mentioned that `paths` should be renamed to `in`. This PR basically adds support for it + maintain backward compatibility. Feat added ---------- - Accepts `in` alias alongside `paths` in rename entries - Normalizes `in` to `paths` in `InitScriptSchemaRename` - Added tests for alias and existing `paths` behavior Implementation test ------------------- pnpm vitest run test/init-script-schema.test.ts pnpm test --- src/utils/init-script-schema.ts | 24 ++++++++++++----- test/init-script-schema.test.ts | 46 +++++++++++++++++++++++++++++++++ 2 files changed, 63 insertions(+), 7 deletions(-) create mode 100644 test/init-script-schema.test.ts diff --git a/src/utils/init-script-schema.ts b/src/utils/init-script-schema.ts index 66e0b0a..434bc6f 100644 --- a/src/utils/init-script-schema.ts +++ b/src/utils/init-script-schema.ts @@ -11,13 +11,23 @@ export const InitScriptSchemaVersions = z.object({ solana: z.string().optional(), }) -export const InitScriptSchemaRename = z.record( - z.object({ - // TODO: Rename 'paths' to 'in' (breaking change) - paths: z.array(z.string()), - to: z.string(), - }), -) +const InitScriptSchemaRenameEntryBase = z.object({ + // Accept alias `in` for backward/forward compatibility and normalize to `paths` + in: z.array(z.string()).optional(), + paths: z.array(z.string()).optional(), + to: z.string(), +}) + +export const InitScriptSchemaRename = z.record(InitScriptSchemaRenameEntryBase).transform((input) => { + // Normalize entries: if `in` is provided, move to `paths` + const normalized: Record = {} + for (const key of Object.keys(input)) { + const entry = input[key] as { in?: string[]; paths?: string[]; to: string } + const paths = entry.paths ?? entry.in ?? [] + normalized[key] = { paths, to: entry.to } + } + return normalized +}) export const InitScriptSchema = z.object({ instructions: InitScriptSchemaInstructions.optional(), diff --git a/test/init-script-schema.test.ts b/test/init-script-schema.test.ts new file mode 100644 index 0000000..a2a35b1 --- /dev/null +++ b/test/init-script-schema.test.ts @@ -0,0 +1,46 @@ +import { describe, expect, it } from 'vitest' +import { InitScriptSchema } from '../src/utils/init-script-schema' + +describe('InitScriptSchema - rename alias', () => { + const parseRename = (rename: unknown) => InitScriptSchema.parse({ rename }).rename + + it('should accept `in` alias and normalize to `paths`', () => { + const parsed = parseRename({ example: { in: ['some/path/to/file'], to: '{{name}}Example' } } as unknown) + + // @ts-expect-error normalized by schema transform + expect(parsed.example.in).toBeUndefined() + expect(parsed?.example.paths).toEqual(['some/path/to/file']) + expect(parsed?.example.to).toBe('{{name}}Example') + }) + + it('should accept `paths` field without changes', () => { + const parsed = parseRename({ example: { paths: ['some/path/to/file'], to: '{{name}}Example' } }) + + expect(parsed?.example.paths).toEqual(['some/path/to/file']) + expect(parsed?.example.to).toBe('{{name}}Example') + }) + + it('should prioritize `paths` over `in` when both provided', () => { + const entry = { in: ['path/from/in'], paths: ['path/from/paths'], to: '{{name}}Example' } as unknown + const parsed = parseRename({ example: entry }) + + expect(parsed?.example.paths).toEqual(['path/from/paths']) + }) + + it('should handle empty arrays', () => { + const parsed = parseRename({ example: { in: [], to: '{{name}}Example' } } as unknown) + + expect(parsed?.example.paths).toEqual([]) + }) + + it('should handle mixed `in` and `paths` usage', () => { + const rename = { + example1: { in: ['some/path/to/file1'], to: '{{name}}Example1' }, + example2: { paths: ['some/path/to/file2'], to: '{{name}}Example2' }, + } as unknown + const parsed = parseRename(rename) + + expect(parsed?.example1.paths).toEqual(['some/path/to/file1']) + expect(parsed?.example2.paths).toEqual(['some/path/to/file2']) + }) +})