diff --git a/README.md b/README.md index f08569ab4..c8c143f25 100644 --- a/README.md +++ b/README.md @@ -590,6 +590,12 @@ Here's an explanation of the configuration options: | `security.enableSecurityCheck` | Whether to perform security checks on files | `true` | | `tokenCount.encoding` | Token count encoding for AI model context limits (e.g., `o200k_base`, `cl100k_base`) | `"o200k_base"` | +The configuration file supports [JSON5](https://json5.org/) syntax, which allows: +- Comments (both single-line and multi-line) +- Trailing commas in objects and arrays +- Unquoted property names +- More relaxed string syntax + Example configuration: ```json5 @@ -607,7 +613,7 @@ Example configuration: "showLineNumbers": false, "copyToClipboard": true, "topFilesLength": 5, - "includeEmptyDirectories": false + "includeEmptyDirectories": false, }, "include": [ "**/*" @@ -619,14 +625,14 @@ Example configuration: "customPatterns": [ "additional-folder", "**/*.log" - ] + ], }, "security": { "enableSecurityCheck": true }, "tokenCount": { "encoding": "o200k_base" - } + }, } ``` diff --git a/biome.json b/biome.json index c5cb7a9af..4b6ef7d7a 100644 --- a/biome.json +++ b/biome.json @@ -48,7 +48,11 @@ }, "json": { "parser": { - "allowComments": true + "allowComments": true, + "allowTrailingCommas": true + }, + "formatter": { + "enabled": false } } } diff --git a/package-lock.json b/package-lock.json index d09c63bd7..2ddfdc25b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -22,6 +22,7 @@ "iconv-lite": "^0.6.3", "istextorbinary": "^9.5.0", "jschardet": "^3.1.4", + "json5": "^2.2.3", "log-update": "^6.1.0", "minimatch": "^10.0.1", "picocolors": "^1.1.1", @@ -2954,7 +2955,6 @@ "version": "2.2.3", "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", - "dev": true, "license": "MIT", "bin": { "json5": "lib/cli.js" diff --git a/package.json b/package.json index 41d7ec1af..74f4a742e 100644 --- a/package.json +++ b/package.json @@ -76,6 +76,7 @@ "iconv-lite": "^0.6.3", "istextorbinary": "^9.5.0", "jschardet": "^3.1.4", + "json5": "^2.2.3", "log-update": "^6.1.0", "minimatch": "^10.0.1", "picocolors": "^1.1.1", diff --git a/repomix.config.json b/repomix.config.json index d02dd0d32..b9ff43ce0 100644 --- a/repomix.config.json +++ b/repomix.config.json @@ -11,19 +11,19 @@ "removeEmptyLines": false, "topFilesLength": 5, "showLineNumbers": false, - "includeEmptyDirectories": true + "includeEmptyDirectories": true, }, "include": [], "ignore": { "useGitignore": true, "useDefaultPatterns": true, // ignore is specified in .repomixignore - "customPatterns": [] + "customPatterns": [], }, "security": { - "enableSecurityCheck": true + "enableSecurityCheck": true, }, "tokenCount": { - "encoding": "o200k_base" + "encoding": "o200k_base", } } diff --git a/src/config/configLoad.ts b/src/config/configLoad.ts index 6c94a8b70..314884911 100644 --- a/src/config/configLoad.ts +++ b/src/config/configLoad.ts @@ -1,7 +1,7 @@ import * as fs from 'node:fs/promises'; import path from 'node:path'; +import JSON5 from 'json5'; import pc from 'picocolors'; -import stripJsonComments from 'strip-json-comments'; import { RepomixError, rethrowValidationErrorIfZodError } from '../shared/errorHandle.js'; import { logger } from '../shared/logger.js'; import { @@ -70,12 +70,12 @@ export const loadFileConfig = async (rootDir: string, argConfigPath: string | nu const loadAndValidateConfig = async (filePath: string): Promise => { try { const fileContent = await fs.readFile(filePath, 'utf-8'); - const config = JSON.parse(stripJsonComments(fileContent)); + const config = JSON5.parse(fileContent); return repomixConfigFileSchema.parse(config); } catch (error) { rethrowValidationErrorIfZodError(error, 'Invalid config schema'); if (error instanceof SyntaxError) { - throw new RepomixError(`Invalid JSON in config file ${filePath}: ${error.message}`); + throw new RepomixError(`Invalid JSON5 in config file ${filePath}: ${error.message}`); } if (error instanceof Error) { throw new RepomixError(`Error loading config from ${filePath}: ${error.message}`); diff --git a/tests/config/configLoad.test.ts b/tests/config/configLoad.test.ts index e7db286e7..765d2f495 100644 --- a/tests/config/configLoad.test.ts +++ b/tests/config/configLoad.test.ts @@ -107,6 +107,37 @@ describe('configLoad', () => { ignore: { useGitignore: true }, }); }); + + test('should parse config file with JSON5 features', async () => { + const configWithJSON5Features = `{ + // Output configuration + output: { + filePath: 'test-output.txt', + style: 'plain', + }, + /* Ignore configuration */ + ignore: { + useGitignore: true, // Use .gitignore file + customPatterns: [ + '*.log', + '*.tmp', + '*.temp', // Trailing comma + ], + }, + }`; + + vi.mocked(fs.readFile).mockResolvedValue(configWithJSON5Features); + vi.mocked(fs.stat).mockResolvedValue({ isFile: () => true } as Stats); + + const result = await loadFileConfig(process.cwd(), 'test-config.json'); + expect(result).toEqual({ + output: { filePath: 'test-output.txt', style: 'plain' }, + ignore: { + useGitignore: true, + customPatterns: ['*.log', '*.tmp', '*.temp'], + }, + }); + }); }); describe('mergeConfigs', () => {