diff --git a/package-lock.json b/package-lock.json index 7068f9e7a..d5c031163 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1727,7 +1727,6 @@ "integrity": "sha512-qzQZRBqkFsYyaSWXuEHc2WR9c0a0CXwiE5FWUvn7ZM+vdy1uZLfCunD38UzhuB7YN/J11ndbDBcTmOdxJo9Q7A==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "undici-types": "~7.16.0" } @@ -3575,7 +3574,6 @@ "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.6.1.tgz", "integrity": "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==", "license": "MIT", - "peer": true, "bin": { "jiti": "lib/jiti-cli.mjs" } @@ -5465,7 +5463,6 @@ "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=12" }, @@ -5529,7 +5526,6 @@ "integrity": "sha512-ytQKuwgmrrkDTFP4LjR0ToE2nqgy886GpvRSpU0JAnrdBYppuY5rLkRUYPU1yCryb24SsKBTL/hlDQAEFVwtZg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "esbuild": "~0.25.0", "get-tsconfig": "^4.7.5" @@ -5672,7 +5668,6 @@ "integrity": "sha512-ZWyE8YXEXqJrrSLvYgrRP7p62OziLW7xI5HYGWFzOvupfAlrLvURSzv/FyGyy0eidogEM3ujU+kUG1zuHgb6Ug==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.5.0", @@ -5789,7 +5784,6 @@ "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=12" }, @@ -5803,7 +5797,6 @@ "integrity": "sha512-LUCP5ev3GURDysTWiP47wRRUpLKMOfPh+yKTx3kVIEiu5KOMeqzpnYNsKyOoVrULivR8tLcks4+lga33Whn90A==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@types/chai": "^5.2.2", "@vitest/expect": "3.2.4", @@ -6059,7 +6052,6 @@ "resolved": "https://registry.npmjs.org/zod/-/zod-4.1.12.tgz", "integrity": "sha512-JInaHOamG8pt5+Ey8kGmdcAcg3OL9reK8ltczgHTAwNhMys/6ThXHityHxVV2p3fkw/c+MAvBHFVYHFZDmjMCQ==", "license": "MIT", - "peer": true, "funding": { "url": "https://github.com/sponsors/colinhacks" } diff --git a/src/config/configLoad.ts b/src/config/configLoad.ts index 1f6066ae1..2fa0d36b7 100644 --- a/src/config/configLoad.ts +++ b/src/config/configLoad.ts @@ -12,6 +12,7 @@ import { type RepomixConfigCli, type RepomixConfigFile, type RepomixConfigMerged, + type RepomixOutputStyle, repomixConfigFileSchema, repomixConfigMergedSchema, } from './configSchema.js'; @@ -167,6 +168,14 @@ const loadAndValidateConfig = async ( } }; +// Mapping of output styles to their file extensions +const styleToExtensionMap: Record = { + xml: '.xml', + markdown: '.md', + plain: '.txt', + json: '.json', +} as const; + export const mergeConfigs = ( cwd: string, fileConfig: RepomixConfigFile, @@ -205,6 +214,15 @@ export const mergeConfigs = ( mergedOutput.filePath = desiredPath; logger.trace('Adjusted output file path to match style:', mergedOutput.filePath); } + } else { + // If filePath is explicitly set, check if it has an extension + const currentExtension = path.extname(mergedOutput.filePath); + if (!currentExtension) { + // No extension found, add the appropriate extension based on style + const extensionToAdd = styleToExtensionMap[style]; + mergedOutput.filePath = `${mergedOutput.filePath}${extensionToAdd}`; + logger.trace('Added file extension to output path:', mergedOutput.filePath); + } } return mergedOutput; diff --git a/tests/config/configLoad.test.ts b/tests/config/configLoad.test.ts index d864ab096..14f04af6e 100644 --- a/tests/config/configLoad.test.ts +++ b/tests/config/configLoad.test.ts @@ -329,5 +329,65 @@ describe('configLoad', () => { expect(merged.output.filePath).toBe('repomix-output.txt'); expect(merged.output.style).toBe('plain'); }); + + test('should add extension when CLI filePath has no extension (default style)', () => { + const merged = mergeConfigs(process.cwd(), {}, { output: { filePath: 'myoutput' } }); + expect(merged.output.filePath).toBe('myoutput.xml'); + expect(merged.output.style).toBe('xml'); + }); + + test('should add extension when CLI filePath has no extension (markdown style)', () => { + const merged = mergeConfigs(process.cwd(), {}, { output: { filePath: 'myoutput', style: 'markdown' } }); + expect(merged.output.filePath).toBe('myoutput.md'); + expect(merged.output.style).toBe('markdown'); + }); + + test('should add extension when CLI filePath has no extension (plain style)', () => { + const merged = mergeConfigs(process.cwd(), {}, { output: { filePath: 'myoutput', style: 'plain' } }); + expect(merged.output.filePath).toBe('myoutput.txt'); + expect(merged.output.style).toBe('plain'); + }); + + test('should add extension when CLI filePath has no extension (json style)', () => { + const merged = mergeConfigs(process.cwd(), {}, { output: { filePath: 'myoutput', style: 'json' } }); + expect(merged.output.filePath).toBe('myoutput.json'); + expect(merged.output.style).toBe('json'); + }); + + test('should keep extension when CLI filePath already has extension', () => { + const merged = mergeConfigs(process.cwd(), {}, { output: { filePath: 'myoutput.txt', style: 'markdown' } }); + expect(merged.output.filePath).toBe('myoutput.txt'); + expect(merged.output.style).toBe('markdown'); + }); + + test('should add extension when file config filePath has no extension', () => { + const merged = mergeConfigs(process.cwd(), { output: { filePath: 'myoutput', style: 'markdown' } }, {}); + expect(merged.output.filePath).toBe('myoutput.md'); + expect(merged.output.style).toBe('markdown'); + }); + + test('should keep extension when file config filePath already has extension', () => { + const merged = mergeConfigs(process.cwd(), { output: { filePath: 'myoutput.custom', style: 'markdown' } }, {}); + expect(merged.output.filePath).toBe('myoutput.custom'); + expect(merged.output.style).toBe('markdown'); + }); + + test('should add extension for paths with directories when no extension', () => { + const merged = mergeConfigs(process.cwd(), {}, { output: { filePath: 'output/myfile', style: 'json' } }); + expect(merged.output.filePath).toBe('output/myfile.json'); + expect(merged.output.style).toBe('json'); + }); + + test('should keep extension for paths with directories when extension exists', () => { + const merged = mergeConfigs(process.cwd(), {}, { output: { filePath: 'output/myfile.txt', style: 'json' } }); + expect(merged.output.filePath).toBe('output/myfile.txt'); + expect(merged.output.style).toBe('json'); + }); + + test('should add extension when style is changed via CLI but filePath has no extension', () => { + const merged = mergeConfigs(process.cwd(), { output: { filePath: 'myfile' } }, { output: { style: 'markdown' } }); + expect(merged.output.filePath).toBe('myfile.md'); + expect(merged.output.style).toBe('markdown'); + }); }); });