fix(linter): remove custom eslint hasher

The custom eslint hasher was an optimization from the Node.js hashing
era that stripped dependency file hashes for non-type-aware lint rules.
With the native Rust hasher this optimization is no longer needed.

Deprecate `hasTypeAwareRules` option (removal in v23).
This commit is contained in:
Leosvel Pérez Espinosa
2026-04-09 11:55:13 +02:00
committed by Craigory Coppola
parent fd0dff1078
commit d64aeef5df
10 changed files with 7 additions and 178 deletions
@@ -6,11 +6,9 @@ import type {
import {
joinPathFragments,
offsetFromRoot,
readJson,
updateJson,
updateProjectConfiguration,
} from '@nx/devkit';
import { hasRulesRequiringTypeChecking } from '@nx/eslint';
import { dirname } from 'path';
import type { Logger, ProjectMigrationInfo } from '../../utilities';
import { BuilderMigrator } from './builder.migrator';
@@ -115,13 +113,6 @@ export class AngularEslintLintMigrator extends BuilderMigrator {
return pattern;
});
if (existEsLintConfigPath) {
const eslintConfig = readJson(this.tree, this.newEsLintConfigPath);
if (hasRulesRequiringTypeChecking(eslintConfig)) {
target.options.hasTypeAwareRules = true;
}
}
updateProjectConfiguration(this.tree, this.project.name, {
...this.projectConfig,
});
@@ -1043,46 +1043,6 @@ describe('app migrator', () => {
});
});
it('should set hasTypeAwareRules when there are rules requiring type checking', async () => {
writeJson(tree, '.eslintrc.json', {
root: true,
ignorePatterns: ['projects/**/*'],
overrides: [
{
files: ['*.ts', '*.tsx'],
extends: ['plugin:@nx/typescript'],
rules: { '@typescript-eslint/await-thenable': 'error' },
},
],
});
const project = addProject('app1', {
root: '',
sourceRoot: 'src',
architect: {
lint: {
builder: '@angular-eslint/builder:lint',
options: {
eslintConfig: '.eslintrc.json',
lintFilePatterns: ['src/**/*.ts', 'src/**/*.html'],
},
},
},
});
const migrator = new AppMigrator(tree, {}, project);
await migrator.migrate();
const { targets } = readProjectConfiguration(tree, 'app1');
expect(targets.lint).toStrictEqual({
executor: '@nx/eslint:lint',
options: {
eslintConfig: 'apps/app1/.eslintrc.json',
hasTypeAwareRules: true,
lintFilePatterns: ['apps/app1/**/*.ts', 'apps/app1/**/*.html'],
},
});
});
it('should update server target', async () => {
const project = addProject('app1', {
root: '',
@@ -789,49 +789,6 @@ describe('lib migrator', () => {
});
});
it('should set hasTypeAwareRules when there are rules requiring type checking', async () => {
writeJson(tree, 'projects/lib1/.eslintrc.json', {
extends: '../../.eslintrc.json',
ignorePatterns: ['!**/*'],
overrides: [
{
files: ['*.ts', '*.tsx'],
extends: ['plugin:@nx/typescript'],
rules: { '@typescript-eslint/await-thenable': 'error' },
},
],
});
const project = addProject('lib1', {
root: 'projects/lib1',
sourceRoot: 'projects/lib1/src',
architect: {
lint: {
builder: '@angular-eslint/builder:lint',
options: {
eslintConfig: 'projects/lib1/.eslintrc.json',
lintFilePatterns: [
'projects/lib1/**/*.ts',
'projects/lib1/**/*.html',
],
},
},
},
});
const migrator = new LibMigrator(tree, {}, project);
await migrator.migrate();
const { targets } = readProjectConfiguration(tree, 'lib1');
expect(targets.lint).toStrictEqual({
executor: '@nx/eslint:lint',
options: {
eslintConfig: 'libs/lib1/.eslintrc.json',
hasTypeAwareRules: true,
lintFilePatterns: ['libs/lib1/**/*.ts', 'libs/lib1/**/*.html'],
},
});
});
it('should update test target', async () => {
const project = addProject('lib1', {
root: 'projects/lib1',
-1
View File
@@ -3,7 +3,6 @@
"lint": {
"implementation": "./src/executors/lint/lint.impl",
"schema": "./src/executors/lint/schema.json",
"hasher": "./src/executors/lint/hasher",
"description": "Run ESLint on a project."
}
}
-5
View File
@@ -1,8 +1,3 @@
export { lintProjectGenerator } from './src/generators/lint-project/lint-project';
export { lintInitGenerator } from './src/generators/init/init';
export { Linter, LinterType } from './src/generators/utils/linter';
// @nx/angular needs it for the Angular CLI workspace migration to Nx to
// infer whether a config is using type aware rules and set the
// `hasTypeAwareRules` option of the `@nx/eslint:lint` executor.
export { hasRulesRequiringTypeChecking } from './src/utils/rules-requiring-type-checking';
@@ -1,76 +0,0 @@
import {
Hash,
ProjectGraph,
ProjectsConfigurations,
Task,
TaskGraph,
TaskHasher,
hashArray,
} from '@nx/devkit';
export default async function run(
task: Task,
context: {
hasher: TaskHasher;
projectGraph: ProjectGraph;
taskGraph: TaskGraph;
projectsConfigurations: ProjectsConfigurations;
env: NodeJS.ProcessEnv;
}
): Promise<Hash> {
const res = await context.hasher.hashTask(
task,
context.taskGraph,
context.env
);
if (task.overrides['hasTypeAwareRules'] === true) {
return res;
}
const deps = allDeps(task.id, context.taskGraph, context.projectGraph);
const tags = hashArray(
deps.map((d) =>
(context.projectsConfigurations.projects[d].tags || []).join('|')
)
);
const command = res.details['command'];
let selfSource = '';
for (let n of Object.keys(res.details)) {
if (n.startsWith(`${task.target.project}:`)) {
selfSource = res.details.nodes[n];
}
}
const nodes = {};
const hashes = [] as string[];
for (const d of Object.keys(res.details.nodes).sort()) {
if (d.indexOf('$fileset') === -1) {
nodes[d] = res.details.nodes[d];
hashes.push(res.details.nodes[d]);
}
}
const hashResult = {
value: hashArray([command, selfSource, ...hashes, tags]),
details: {
command,
nodes: { [task.target.project]: selfSource, tags, ...nodes },
},
};
hashResult['name'] = 'eslint-hasher';
return hashResult;
}
function allDeps(
taskId: string,
taskGraph: TaskGraph,
projectGraph: ProjectGraph
): string[] {
if (!taskGraph.tasks) {
return [];
}
const project = taskGraph.tasks[taskId].target.project;
return projectGraph.dependencies[project]
.filter((d) => !!projectGraph.nodes[d.target])
.map((d) => d.target);
}
@@ -11,7 +11,7 @@ export default async function run(
options: Schema,
context: ExecutorContext
): Promise<{ success: boolean }> {
// this is only used for the hasher
// hasTypeAwareRules is deprecated and no longer used, delete it so it's not passed to ESLint
delete options.hasTypeAwareRules;
const systemRoot = context.root;
+3 -1
View File
@@ -15,7 +15,9 @@ export interface Schema extends JsonObject {
maxWarnings: number;
quiet: boolean;
ignorePath: string | null;
hasTypeAwareRules: boolean;
/** @deprecated No longer has any effect. Will be removed in Nx v23. */
hasTypeAwareRules?: boolean;
cacheStrategy: 'content' | 'metadata' | null;
rulesdir: string[];
resolvePluginsRelativeTo: string | null;
@@ -106,7 +106,8 @@
},
"hasTypeAwareRules": {
"type": "boolean",
"description": "When set to `true`, the linter will invalidate its cache when any of its dependencies changes."
"description": "Deprecated. No longer has any effect.",
"x-deprecated": "No longer has any effect. Will be removed in Nx v23."
},
"cacheStrategy": {
"type": "string",
+1 -1
View File
@@ -41,7 +41,7 @@ export async function hashTasksThatDoNotDependOnOutputsOfOtherTasks(
const tasksToHash = tasksWithHashers
.filter(({ task, customHasher }) => {
// If a task has a custom hasher, it might depend on the outputs of other tasks
if (customHasher && customHasher.name !== 'eslint-hasher') {
if (customHasher) {
return false;
}