tsslint icon indicating copy to clipboard operation
tsslint copied to clipboard

Implementing more efficient cache invalidation in CLI

Open johnsoncodehk opened this issue 6 months ago • 0 comments

Below is a custom rule that checks if the imported module is correctly added to package.json.

'missing-dependency'({ typescript: ts, sourceFile, reportError, languageServiceHost }) {
	const { noEmit } = languageServiceHost.getCompilationSettings();
	if (noEmit) {
		return;
	}
	const packageJsonPath = ts.findConfigFile(sourceFile.fileName, ts.sys.fileExists, 'package.json');
	if (!packageJsonPath) {
		return;
	}
	const packageJson = JSON.parse(ts.sys.readFile(packageJsonPath) ?? '');
	if (packageJson.private) {
		return;
	}
	const parentPackageJsonPath = ts.findConfigFile(path.dirname(path.dirname(packageJsonPath)), ts.sys.fileExists, 'package.json');
	const parentPackageJson = !!parentPackageJsonPath && parentPackageJsonPath !== packageJsonPath
		? JSON.parse(ts.sys.readFile(parentPackageJsonPath) ?? '')
		: {};
	ts.forEachChild(sourceFile, function visit(node) {
		if (
			ts.isImportDeclaration(node)
			&& !node.importClause?.isTypeOnly
			&& ts.isStringLiteral(node.moduleSpecifier)
			&& !node.moduleSpecifier.text.startsWith('./')
			&& !node.moduleSpecifier.text.startsWith('../')
		) {
			let moduleName = node.moduleSpecifier.text.split('/')[0];
			if (moduleName.startsWith('@')) {
				moduleName += '/' + node.moduleSpecifier.text.split('/')[1];
			}
			if (
				(
					packageJson.devDependencies?.[moduleName]
					|| parentPackageJson.dependencies?.[moduleName]
					|| parentPackageJson.devDependencies?.[moduleName]
					|| parentPackageJson.peerDependencies?.[moduleName]
				)
				&& !packageJson.dependencies?.[moduleName]
				&& !packageJson.peerDependencies?.[moduleName]
			) {
				reportError(
					`Module '${moduleName}' should be in the dependencies.`,
					node.getStart(sourceFile),
					node.getEnd()
				);
			}
		}
		ts.forEachChild(node, visit);
	});
},

This rule is not re-run when package.json changes, because cache invalidation only checks if the tsslint.config.ts and tsconfig include files have changed, but not package.json.

As a solution, we should probably proxy fs.readFileSync in the CLI to record the files that the rule depends on when it runs.

johnsoncodehk avatar Jun 11 '25 08:06 johnsoncodehk