From cf16d1678d5687ae79d369e56897b8032570047e Mon Sep 17 00:00:00 2001 From: Sandy Tao Date: Tue, 21 Oct 2025 13:08:33 -0700 Subject: [PATCH] fix(scripts): add tsconfig linter to prevent adding files to the exclude list (#11602) --- scripts/lint.js | 77 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 77 insertions(+) diff --git a/scripts/lint.js b/scripts/lint.js index c6904cf7da..008f7cb209 100644 --- a/scripts/lint.js +++ b/scripts/lint.js @@ -251,6 +251,79 @@ export function runSensitiveKeywordLinter() { } } +function stripJSONComments(json) { + return json.replace( + /\\"|"(?:\\"|[^"])*"|(\/\/.*|\/\*[\s\S]*?\*\/)/g, + (m, g) => (g ? '' : m), + ); +} + +export function runTSConfigLinter() { + console.log('\nRunning tsconfig linter...'); + + let files = []; + try { + // Find all tsconfig.json files under packages/ using a git pathspec + files = execSync("git ls-files 'packages/**/tsconfig.json'") + .toString() + .trim() + .split('\n') + .filter(Boolean); + } catch (e) { + console.error('Error finding tsconfig.json files:', e.message); + process.exit(1); + } + + let hasError = false; + + for (const file of files) { + const tsconfigPath = join(process.cwd(), file); + if (!existsSync(tsconfigPath)) { + console.error(`Error: ${tsconfigPath} does not exist.`); + hasError = true; + continue; + } + + try { + const content = readFileSync(tsconfigPath, 'utf-8'); + const config = JSON.parse(stripJSONComments(content)); + + // Check if exclude exists and matches exactly + if (config.exclude) { + if (!Array.isArray(config.exclude)) { + console.error( + `Error: ${file} "exclude" must be an array. Found: ${JSON.stringify( + config.exclude, + )}`, + ); + hasError = true; + } else { + const allowedExclude = new Set(['node_modules', 'dist']); + const invalidExcludes = config.exclude.filter( + (item) => !allowedExclude.has(item), + ); + + if (invalidExcludes.length > 0) { + console.error( + `Error: ${file} "exclude" contains invalid items: ${JSON.stringify( + invalidExcludes, + )}. Only "node_modules" and "dist" are allowed.`, + ); + hasError = true; + } + } + } + } catch (error) { + console.error(`Error parsing ${tsconfigPath}: ${error.message}`); + hasError = true; + } + } + + if (hasError) { + process.exit(1); + } +} + function main() { const args = process.argv.slice(2); @@ -275,6 +348,9 @@ function main() { if (args.includes('--sensitive-keywords')) { runSensitiveKeywordLinter(); } + if (args.includes('--tsconfig')) { + runTSConfigLinter(); + } if (args.length === 0) { setupLinters(); @@ -284,6 +360,7 @@ function main() { runYamllint(); runPrettier(); runSensitiveKeywordLinter(); + runTSConfigLinter(); console.log('\nAll linting checks passed!'); } }