From 7ffea7091a8323e8eaa65a76183c0196479c20ec Mon Sep 17 00:00:00 2001 From: affaan-m <124439313+affaan-m@users.noreply.github.com> Date: Mon, 28 Sep 2026 07:07:07 -0400 Subject: [PATCH] fix(hooks): distinguish Biome configs from results files Preserve the original contributor histories and apply the exact independently reviewed repair. Source-Parent: 74023e568908f528c9d9ae1a9bae501f4c1f2e38 Review-Manifest-SHA256: 208a6350119857df7c1e5b83a46cdba0b7f7399f58ed60167397920c7a21b454 --- scripts/hooks/config-protection.js | 9 ++++---- tests/hooks/config-protection.test.js | 32 +++++++++++++++++++++++++-- 2 files changed, 35 insertions(+), 6 deletions(-) diff --git a/scripts/hooks/config-protection.js b/scripts/hooks/config-protection.js index e82076575..88ddcddfa 100644 --- a/scripts/hooks/config-protection.js +++ b/scripts/hooks/config-protection.js @@ -43,9 +43,12 @@ const PROTECTED_FILES = new Set([ 'prettier.config.js', 'prettier.config.cjs', 'prettier.config.mjs', - // Biome + // Biome's discovered filenames. Custom --config-path/extends targets need + // reference context; an arbitrary biome.* basename is not sufficient. 'biome.json', 'biome.jsonc', + '.biome.json', + '.biome.jsonc', // Ruff (Python) '.ruff.toml', 'ruff.toml', @@ -82,11 +85,9 @@ const PROTECTED_FILES = new Set([ */ const PROTECTED_PATTERNS = [ // eslint.config.base.mjs, prettier.config.shared.cjs, stylelint.config.local.js ... - /^(eslint|prettier|stylelint|commitlint|oxlint|biome)\.config(\.[A-Za-z0-9_-]+)*\.(js|mjs|cjs|ts|mts|cts)$/i, + /^(eslint|prettier|stylelint|commitlint|oxlint)\.config(\.[A-Za-z0-9_-]+)*\.(js|mjs|cjs|ts|mts|cts)$/i, // .eslintrc.base.json, .prettierrc.shared.yml ... /^\.(eslintrc|prettierrc|stylelintrc|markdownlintrc)(\.[A-Za-z0-9_-]+)*\.(js|cjs|mjs|json|jsonc|yml|yaml|toml)$/i, - // biome.base.json, biome.shared.jsonc - /^biome(\.[A-Za-z0-9_-]+)*\.jsonc?$/i, ]; function isProtectedName(basename) { diff --git a/tests/hooks/config-protection.test.js b/tests/hooks/config-protection.test.js index 2ba65638a..c7ce3c26e 100644 --- a/tests/hooks/config-protection.test.js +++ b/tests/hooks/config-protection.test.js @@ -332,8 +332,8 @@ function runTests() { const names = [ 'eslint.config.base.mjs', 'prettier.config.shared.cjs', '.eslintrc.base.json', 'ESLint.Config.Base.MJS', 'stylelint.config.local.ts', 'commitlint.config.shared.cts', 'oxlint.config.base.mts', - 'biome.config.shared.js', '.prettierrc.shared.yml', '.stylelintrc.team.toml', - '.markdownlintrc.team.jsonc', 'biome.shared.jsonc', 'BIOME.Team.Base.JSON' + '.prettierrc.shared.yml', '.stylelintrc.team.toml', + '.markdownlintrc.team.jsonc' ]; for (const name of names) { const absPath = path.join(tmpDir, name); @@ -380,6 +380,34 @@ function runTests() { }) ); + results.push( + test('Biome filenames protect discovered configs without blocking ordinary result files', () => { + return withOwnedDirectory(fs.mkdtempSync(path.join(os.tmpdir(), 'ecc-config-protect-biome-')), tmpDir => { + // Arbitrary --config-path/extends targets need reference context; their + // basename alone does not prove that a file is Biome configuration. + const cases = [ + ['biome.results.json', 0], ['biome.report.jsonc', 0], + ['biome.json', 2], ['biome.jsonc', 2], ['.biome.json', 2], ['.biome.jsonc', 2], + ['BIOME.JSON', 2], ['.BIOME.JSONC', 2], + ['biome.shared.jsonc', 0], + ['BIOME.Team.Base.JSON', 0], ['biome.config.shared.js', 0], ['biome.json.bak', 0], + ]; + for (const [name, expected] of cases) { + const absPath = path.join(tmpDir, name); + const input = { tool_name: 'Write', tool_input: { file_path: absPath, content: '{}' } }; + // Start each spelling independently on case-insensitive filesystems. + fs.rmSync(absPath, { force: true }); + assert.strictEqual(runHook(input).code, 0, 'First creation should be allowed: ' + name); + fs.writeFileSync(absPath, '{}'); + const result = runHook(input); + assert.strictEqual(result.code, expected, 'Unexpected filename classification: ' + name); + assert.strictEqual(result.stdout, '', 'No raw input should be echoed: ' + name); + assert.strictEqual(fs.readFileSync(absPath, 'utf8'), '{}', 'Hook must not modify the fixture'); + } + }); + }) + ); + const passed = results.filter(result => result === 'passed').length; const failed = results.filter(result => result === 'failed').length; const skipped = results.filter(result => result === 'skipped').length;