Skip to content

Instantly share code, notes, and snippets.

@rluvaton
Created January 31, 2024 16:44
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save rluvaton/a01089455037c937f2a6428bdc30aafa to your computer and use it in GitHub Desktop.
Save rluvaton/a01089455037c937f2a6428bdc30aafa to your computer and use it in GitHub Desktop.
TypeCheck only on typescript project files
import ts from 'typescript';
import path from 'node:path';
import Debug from 'debug';
const debug = Debug('type-check-only-project-files');
// ----------------------------------------------------------------
// This file is a helper file that can be used to run type check
// only for the files that in the project and not for any other file
// ----------------------------------------------------------------
const args = process.argv.slice(2);
if (!args.length || args.find(item => item === '--help' || item === '-h')) {
console.log(`Usage: node scripts/type-check.mjs <tsconfig path>`);
process.exit(0);
}
const tsconfigPath = args[0];
if (!tsconfigPath) {
console.error('No tsconfig path provided.');
process.exit(1);
}
console.log('Running type check with tsconfig:', tsconfigPath);
const cwd = process.cwd();
function typeCheck() {
const configFile = path.isAbsolute(tsconfigPath)
? tsconfigPath
: ts.findConfigFile(cwd, ts.sys.fileExists, tsconfigPath);
if (!configFile) {
console.error('No tsconfig file found for path:', tsconfigPath);
process.exit(1);
}
const config = ts.readConfigFile(configFile, ts.sys.readFile);
// File names are only the files that in the project (without files in the tsconfig paths when they outside the project)
const { options, fileNames, raw } = ts.parseJsonConfigFileContent(
config.config,
ts.sys,
// Resolve to the folder where the tsconfig file located
path.dirname(tsconfigPath)
);
if (!options.skipLibCheck) {
console.error('The tsconfig must have skipLibCheck set to true as it wont be respected in the type check');
process.exit(1);
}
const program = ts.createProgram({
rootNames: fileNames,
options
});
debug('fileNames', fileNames);
debug('parsed options raw', raw);
if (fileNames.length === 0) {
console.error('No files in the project.', {
fileNames,
options
});
process.exit(1);
}
// Contain all files that the project depends
// so, no modules libs are there and files in the tsconfig paths even when outside the project
const sourceFiles = program.getSourceFiles();
// To lower case as process.cwd() return in the actual case while the SourceFile.path returns in lower case
const absoluteFileNames = new Set(fileNames.map(item => path.join(cwd, item).toLowerCase()));
// Get the source files that only in the project
const projectSourceFiles = sourceFiles.filter(item => absoluteFileNames.has(item.path.toLowerCase()));
debug(`Number of files in project: ${projectSourceFiles.length}`);
if (projectSourceFiles.length === 0) {
console.error('No files found in project.', {
absoluteFileNames: Array.from(absoluteFileNames),
sourceFiles: sourceFiles.map(item => item.path)
});
process.exit(1);
}
let projectDiagnostics = [];
// For each project source file run type check
// this only do type-check in the passed files and not for any other file (so tsconfig files that outside the project are not type checked)
for (const sourceFile of projectSourceFiles) {
debug('Running type check for file:', sourceFile.path);
const numberOfResultsBefore = projectDiagnostics.length;
projectDiagnostics = projectDiagnostics.concat(
// This do type check for a single file which prevent type checking files that outside the project
ts.getPreEmitDiagnostics(program, sourceFile)
);
if (projectDiagnostics.length > numberOfResultsBefore) {
debug(
`Have ${projectDiagnostics.length - numberOfResultsBefore} type check results for file`,
sourceFile.path
);
} else {
debug('No type check results for file', sourceFile.path);
}
}
if (projectDiagnostics.length > 0) {
debug(`Got ${projectDiagnostics.length} type check results for project`);
console.error(
ts.formatDiagnosticsWithColorAndContext(projectDiagnostics, {
getCanonicalFileName: fileName => fileName,
getCurrentDirectory: () => cwd,
getNewLine: () => ts.sys.newLine
})
);
process.exit(1);
}
console.log(`TypeScript type checking successful for ${tsconfigPath} 🎉`);
}
// Call the function to perform type checking
typeCheck();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment