fix(hooks): recognize Prettier's TypeScript and JSON5 config files

Prettier's config searcher also reads .prettierrc.json5, .prettierrc.ts/.mts/.cts and prettier.config.ts/.mts/.cts. resolve-formatter knew none of them, so a project configured only through prettier.config.ts was never formatted by post-edit-format, quality-gate or the Stop batch. config-protection's rc pattern stopped at .yaml/.toml, so an existing .prettierrc.ts or .prettierrc.json5 could be edited freely.
This commit is contained in:
L4XB committed 2026-10-22 23:51:32 +02:00
1 parent 90606c8315
commit 10e7c5800f
4 files changed
+47 -3

No files matched your search

+3 -2
View File
@@ -117,8 +117,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)\.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,
// .eslintrc.base.json, .prettierrc.shared.yml ... Prettier also reads
// `.prettierrc.ts`, `.mts`, `.cts` and `.json5`.
/^\.(eslintrc|prettierrc|stylelintrc|markdownlintrc)(\.[A-Za-z0-9_-]+)*\.(js|cjs|mjs|ts|mts|cts|json|jsonc|json5|yml|yaml|toml)$/i,
];
function isProtectedName(basename) {
+10 -1
View File
@@ -32,7 +32,16 @@ const PRETTIER_CONFIGS = [
'.prettierrc.toml',
'prettier.config.js',
'prettier.config.cjs',
'prettier.config.mjs'
'prettier.config.mjs',
// The rest of the names Prettier's config searcher looks for
// (src/config/prettier-config/config-searcher.js).
'.prettierrc.json5',
'.prettierrc.ts',
'.prettierrc.mts',
'.prettierrc.cts',
'prettier.config.ts',
'prettier.config.mts',
'prettier.config.cts'
];
const PROJECT_ROOT_MARKERS = ['package.json', ...BIOME_CONFIGS, ...PRETTIER_CONFIGS];
+19
View File
@@ -447,6 +447,25 @@ function runTests() {
})
);
results.push(
test("blocks Prettier's TypeScript and JSON5 rc files", () => {
return withOwnedDirectory(fs.mkdtempSync(path.join(os.tmpdir(), 'ecc-config-protect-prettier-')), tmpDir => {
for (const name of ['.prettierrc.ts', '.prettierrc.mts', '.prettierrc.cts', '.prettierrc.json5', '.prettierrc.shared.ts']) {
const absPath = path.join(tmpDir, name);
fs.writeFileSync(absPath, 'export default {};\n');
const result = runHook({ tool_name: 'Edit', tool_input: { file_path: absPath } });
assert.strictEqual(result.code, 2, 'Expected ' + name + ' to be blocked');
assert.ok(
result.stderr.includes('BLOCKED: Modifying ' + name + ' is not allowed.'),
'Expected block message for ' + name + ', got: ' + result.stderr
);
}
});
})
);
results.push(
test('does not block build or test tooling configs', () => {
// Pins the boundary: this hook guards LINTER configs. A future widening
+15
View File
@@ -155,6 +155,21 @@ function runTests() {
assert.strictEqual(detectFormatter(root), 'prettier');
});
run("detectFormatter: detects Prettier's TypeScript and JSON5 config files", () => {
const names = ['.prettierrc.json5', '.prettierrc.ts', '.prettierrc.mts', '.prettierrc.cts',
'prettier.config.ts', 'prettier.config.mts', 'prettier.config.cts'];
for (const name of names) {
const root = makeTmpDir();
fs.writeFileSync(path.join(root, 'package.json'), JSON.stringify({ name: 'test' }));
fs.writeFileSync(path.join(root, name), 'export default {};\n');
assert.strictEqual(detectFormatter(root), 'prettier', name);
const nested = path.join(root, 'src');
fs.mkdirSync(nested);
fs.rmSync(path.join(root, 'package.json'));
assert.strictEqual(findProjectRoot(nested), root, `${name} marks the project root`);
}
});
run('detectFormatter: detects prettier key in package.json', () => {
const root = makeTmpDir();
fs.writeFileSync(path.join(root, 'package.json'), JSON.stringify({ name: 'test', prettier: { singleQuote: true } }));