@angular-eslint/builder 21.4.1-alpha.7 → 22.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -14,8 +14,6 @@ You can profile rule execution times by enabling ESLint's stats output:
14
14
  ng lint --stats
15
15
  ```
16
16
 
17
- This option requires a flat ESLint configuration (`eslint.config.js/ts/mjs`). Using `--stats` with legacy `.eslintrc.*` files will cause an error.
18
-
19
17
  ## Bulk suppressions
20
18
 
21
19
  ESLint introduced bulk suppressions in v9.24. This allows you to enable new lint rules without having to fix every existing violation first. For details, see https://eslint.org/docs/latest/use/suppressions. In ESLint v10.1 a subset of the suppressions functionality was added to the Node.js API (https://eslint.org/blog/2026/03/eslint-v10.1.0-released/#api-support-for-bulk-suppressions).
@@ -1 +1 @@
1
- {"version":3,"file":"lint.impl.d.ts","sourceRoot":"","sources":["../src/lint.impl.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;;AAMvC,wBA6OE"}
1
+ {"version":3,"file":"lint.impl.d.ts","sourceRoot":"","sources":["../src/lint.impl.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;;AAMvC,wBA0NE"}
package/dist/lint.impl.js CHANGED
@@ -38,20 +38,7 @@ exports.default = (0, architect_1.createBuilder)(async (options, context) => {
38
38
  options.suppressionsLocation = options.suppressionsLocation
39
39
  ? (0, path_1.resolve)(systemRoot, options.suppressionsLocation)
40
40
  : null;
41
- /**
42
- * Until ESLint v9 is released and the new so called flat config is the default
43
- * we only want to support it if the user has explicitly opted into it by converting
44
- * their root ESLint config to use a supported flat config file name.
45
- */
46
- const useFlatConfig = eslint_utils_1.supportedFlatConfigNames.some((name) => (0, fs_1.existsSync)((0, path_1.join)(systemRoot, name)));
47
- const { eslint, ESLint } = await (0, eslint_utils_1.resolveAndInstantiateESLint)(eslintConfigPath, options, useFlatConfig);
48
- const version = ESLint?.version?.split('.');
49
- if (!version ||
50
- version.length < 2 ||
51
- Number(version[0]) < 7 ||
52
- (Number(version[0]) === 7 && Number(version[1]) < 6)) {
53
- throw new Error('ESLint must be version 7.6 or higher.');
54
- }
41
+ const { eslint, ESLint } = await (0, eslint_utils_1.resolveAndInstantiateESLint)(eslintConfigPath, options);
55
42
  let lintResults = [];
56
43
  try {
57
44
  lintResults = await eslint.lintFiles(options.lintFilePatterns);
@@ -65,9 +52,9 @@ exports.default = (0, architect_1.createBuilder)(async (options, context) => {
65
52
  resolveESLintConfigPath(projectRoot) ?? '';
66
53
  }
67
54
  console.error(`
68
- Error: You have attempted to use a lint rule which requires the full TypeScript type-checker to be available, but you do not have \`parserOptions.project\` configured to point at your project tsconfig.json files in the relevant TypeScript file "overrides" block of your project ESLint config ${eslintConfigPath || eslintConfigPathForError}
55
+ Error: You have attempted to use a lint rule which requires the full TypeScript type-checker to be available, but you have not configured type information for the TypeScript files in your project ESLint config ${eslintConfigPath || eslintConfigPathForError}
69
56
 
70
- For full guidance on how to resolve this issue, please see https://github.com/angular-eslint/angular-eslint/blob/main/docs/RULES_REQUIRING_TYPE_INFORMATION.md
57
+ The simplest way to enable type information is to set \`languageOptions.parserOptions.projectService\` to \`true\` for your TypeScript files. For full guidance on how to resolve this issue, please see https://github.com/angular-eslint/angular-eslint/blob/main/docs/RULES_REQUIRING_TYPE_INFORMATION.md
71
58
  `);
72
59
  return {
73
60
  success: false,
@@ -81,7 +68,7 @@ For full guidance on how to resolve this issue, please see https://github.com/an
81
68
  .filter((pattern) => !!pattern)
82
69
  .map((pattern) => `- '${pattern}'`);
83
70
  if (ignoredPatterns.length) {
84
- throw new Error(`All files matching the following patterns are ignored:\n${ignoredPatterns.join('\n')}\n\nPlease check your '.eslintignore' file.`);
71
+ throw new Error(`All files matching the following patterns are ignored:\n${ignoredPatterns.join('\n')}\n\nPlease check the 'ignores' configuration in your ESLint flat config.`);
85
72
  }
86
73
  throw new Error('Invalid lint configuration. Nothing to lint. Please check your lint target pattern(s).');
87
74
  }
@@ -173,21 +160,11 @@ For full guidance on how to resolve this issue, please see https://github.com/an
173
160
  }
174
161
  });
175
162
  function resolveESLintConfigPath(projectRoot) {
176
- const rcPath = (0, path_1.join)(projectRoot, '.eslintrc.json');
177
- if ((0, fs_1.existsSync)(rcPath)) {
178
- return rcPath;
179
- }
180
- const jsPath = (0, path_1.join)(projectRoot, 'eslint.config.js');
181
- if ((0, fs_1.existsSync)(jsPath)) {
182
- return jsPath;
183
- }
184
- const mjsPath = (0, path_1.join)(projectRoot, 'eslint.config.mjs');
185
- if ((0, fs_1.existsSync)(mjsPath)) {
186
- return mjsPath;
187
- }
188
- const cjsPath = (0, path_1.join)(projectRoot, 'eslint.config.cjs');
189
- if ((0, fs_1.existsSync)(cjsPath)) {
190
- return cjsPath;
163
+ for (const name of eslint_utils_1.defaultFlatConfigNames) {
164
+ const candidate = (0, path_1.join)(projectRoot, name);
165
+ if ((0, fs_1.existsSync)(candidate)) {
166
+ return candidate;
167
+ }
191
168
  }
192
169
  return null;
193
170
  }
package/dist/schema.json CHANGED
@@ -90,37 +90,11 @@
90
90
  { "minLength": 1 }
91
91
  ]
92
92
  },
93
- "ignorePath": {
94
- "type": "string",
95
- "description": "The path of the `.eslintignore` file."
96
- },
97
- "noEslintrc": {
98
- "type": "boolean",
99
- "description": "NOTE: eslintrc config only. The equivalent of the `--no-eslintrc` flag on the ESLint CLI, it is false by default",
100
- "default": false
101
- },
102
93
  "noConfigLookup": {
103
94
  "type": "boolean",
104
- "description": "NOTE: flat config only. The equivalent of the `--no-config-lookup` flag on the ESLint CLI, it is false by default",
95
+ "description": "The equivalent of the `--no-config-lookup` flag on the ESLint CLI, it is false by default",
105
96
  "default": false
106
97
  },
107
- "rulesdir": {
108
- "type": "array",
109
- "description": "The equivalent of the `--rulesdir` flag on the ESLint CLI, it is an empty array by default",
110
- "default": [],
111
- "items": {
112
- "type": "string"
113
- }
114
- },
115
- "resolvePluginsRelativeTo": {
116
- "type": "string",
117
- "description": "The equivalent of the `--resolve-plugins-relative-to` flag on the ESLint CLI"
118
- },
119
- "reportUnusedDisableDirectives": {
120
- "type": "string",
121
- "enum": ["off", "warn", "error"],
122
- "description": "The equivalent of the `--report-unused-disable-directives` flag on the ESLint CLI."
123
- },
124
98
  "concurrency": {
125
99
  "oneOf": [
126
100
  { "type": "string", "enum": ["auto", "off"] },
@@ -1,7 +1,17 @@
1
1
  import type { ESLint } from 'eslint';
2
2
  import type { Schema } from '../schema';
3
- export declare const supportedFlatConfigNames: string[];
4
- export declare function resolveAndInstantiateESLint(eslintConfigPath: string | undefined, options: Schema, useFlatConfig?: boolean): Promise<{
3
+ /**
4
+ * The conventional flat config file names that ESLint resolves automatically.
5
+ *
6
+ * NOTE: this is NOT a restriction on what an explicitly provided config file may
7
+ * be named. ESLint's own `--config`/`overrideConfigFile` accepts a flat config
8
+ * file with any name, and so does this builder (see the legacy eslintrc denylist
9
+ * below). This list only mirrors the names ESLint discovers on its own when no
10
+ * explicit config is given, so we can reconstruct a likely config path for
11
+ * diagnostic messages.
12
+ */
13
+ export declare const defaultFlatConfigNames: string[];
14
+ export declare function resolveAndInstantiateESLint(eslintConfigPath: string | undefined, options: Schema): Promise<{
5
15
  ESLint: typeof import("eslint").ESLint;
6
16
  eslint: ESLint;
7
17
  }>;
@@ -1 +1 @@
1
- {"version":3,"file":"eslint-utils.d.ts","sourceRoot":"","sources":["../../src/utils/eslint-utils.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AACrC,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,WAAW,CAAC;AAExC,eAAO,MAAM,wBAAwB,UAOpC,CAAC;AAiDF,wBAAsB,2BAA2B,CAC/C,gBAAgB,EAAE,MAAM,GAAG,SAAS,EACpC,OAAO,EAAE,MAAM,EACf,aAAa,UAAQ;;;GAuHtB"}
1
+ {"version":3,"file":"eslint-utils.d.ts","sourceRoot":"","sources":["../../src/utils/eslint-utils.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AACrC,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,WAAW,CAAC;AAExC;;;;;;;;;GASG;AACH,eAAO,MAAM,sBAAsB,UAOlC,CAAC;AAyCF,wBAAsB,2BAA2B,CAC/C,gBAAgB,EAAE,MAAM,GAAG,SAAS,EACpC,OAAO,EAAE,MAAM;;;GAqEhB"}
@@ -1,8 +1,18 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.supportedFlatConfigNames = void 0;
3
+ exports.defaultFlatConfigNames = void 0;
4
4
  exports.resolveAndInstantiateESLint = resolveAndInstantiateESLint;
5
- exports.supportedFlatConfigNames = [
5
+ /**
6
+ * The conventional flat config file names that ESLint resolves automatically.
7
+ *
8
+ * NOTE: this is NOT a restriction on what an explicitly provided config file may
9
+ * be named. ESLint's own `--config`/`overrideConfigFile` accepts a flat config
10
+ * file with any name, and so does this builder (see the legacy eslintrc denylist
11
+ * below). This list only mirrors the names ESLint discovers on its own when no
12
+ * explicit config is given, so we can reconstruct a likely config path for
13
+ * diagnostic messages.
14
+ */
15
+ exports.defaultFlatConfigNames = [
6
16
  'eslint.config.js',
7
17
  'eslint.config.mjs',
8
18
  'eslint.config.cjs',
@@ -10,19 +20,22 @@ exports.supportedFlatConfigNames = [
10
20
  'eslint.config.mts',
11
21
  'eslint.config.cts',
12
22
  ];
13
- async function resolveESLintClass(useFlatConfig = false) {
23
+ /**
24
+ * Legacy "eslintrc" config file names (e.g. `.eslintrc`, `.eslintrc.json`,
25
+ * `.eslintrc.js`, `.eslintrc.yml`). ESLint removed support for this format in
26
+ * v10 and angular-eslint no longer supports it.
27
+ *
28
+ * We detect these names explicitly so we can surface a helpful error message.
29
+ * Any other file name is passed straight through to ESLint - exactly like
30
+ * ESLint's own `--config`/`overrideConfigFile`, a flat config file can have
31
+ * any name, so we deliberately do NOT require the `eslint.config.*` naming
32
+ * convention for an explicitly provided config file.
33
+ */
34
+ const legacyEslintrcConfigFilePattern = /(^|[\\/])\.eslintrc(\.(c?js|ya?ml|json))?$/;
35
+ async function resolveESLintClass() {
14
36
  try {
15
- // In eslint 8.57.0 (the final v8 minor version), a dedicated API was added for resolving the correct ESLint class.
16
37
  const eslint = await import('eslint');
17
- if (typeof eslint.loadESLint === 'function') {
18
- return await eslint.loadESLint({ useFlatConfig });
19
- }
20
- // If that API is not available (an older version of v8), we need to use the old way of resolving the ESLint class.
21
- if (!useFlatConfig) {
22
- return eslint.ESLint;
23
- }
24
- const { FlatESLint } = (await import('eslint/use-at-your-own-risk'));
25
- return FlatESLint;
38
+ return eslint.ESLint;
26
39
  }
27
40
  catch {
28
41
  throw new Error('Unable to find ESLint. Ensure ESLint is installed.');
@@ -39,22 +52,12 @@ function validateConcurrency(concurrency) {
39
52
  }
40
53
  throw new Error('The --concurrency option must be auto, off or a positive integer');
41
54
  }
42
- async function resolveAndInstantiateESLint(eslintConfigPath, options, useFlatConfig = false) {
43
- if (options.stats && !useFlatConfig) {
44
- throw new Error('The --stats option requires ESLint Flat Config');
45
- }
46
- if (options.applySuppressions && !useFlatConfig) {
47
- throw new Error('The --apply-suppressions option requires ESLint Flat Config');
48
- }
49
- if (options.suppressionsLocation && !useFlatConfig) {
50
- throw new Error('The --suppressions-location option requires ESLint Flat Config');
51
- }
52
- if (useFlatConfig &&
53
- eslintConfigPath &&
54
- !exports.supportedFlatConfigNames.some((name) => eslintConfigPath.endsWith(name))) {
55
- throw new Error(`When using the new Flat Config with ESLint, all configs must be named ${exports.supportedFlatConfigNames.join(' or ')}, and .eslintrc files may not be used. See https://eslint.org/docs/latest/use/configure/configuration-files`);
55
+ async function resolveAndInstantiateESLint(eslintConfigPath, options) {
56
+ if (eslintConfigPath &&
57
+ legacyEslintrcConfigFilePattern.test(eslintConfigPath)) {
58
+ throw new Error(`The ESLint config file "${eslintConfigPath}" uses the legacy "eslintrc" format, which is no longer supported. Please use an ESLint flat config file (it can have any name, e.g. eslint.config.js). See https://eslint.org/docs/latest/use/configure/configuration-files`);
56
59
  }
57
- const ESLint = await resolveESLintClass(useFlatConfig);
60
+ const ESLint = await resolveESLintClass();
58
61
  const eslintOptions = {
59
62
  fix: !!options.fix,
60
63
  cache: !!options.cache,
@@ -77,55 +80,26 @@ async function resolveAndInstantiateESLint(eslintConfigPath, options, useFlatCon
77
80
  validateConcurrency(concurrency);
78
81
  eslintOptions.concurrency = concurrency;
79
82
  }
80
- if (useFlatConfig) {
81
- eslintOptions.stats = !!options.stats;
82
- if (options.applySuppressions) {
83
- eslintOptions.applySuppressions = true;
84
- }
85
- if (options.suppressionsLocation) {
86
- eslintOptions.suppressionsLocation = options.suppressionsLocation;
87
- }
88
- if (typeof options.useEslintrc !== 'undefined') {
89
- throw new Error('For Flat Config, the `useEslintrc` option is not applicable. See https://eslint.org/docs/latest/use/configure/configuration-files-new');
90
- }
91
- if (options.resolvePluginsRelativeTo !== undefined) {
92
- throw new Error('For Flat Config, ESLint removed `resolvePluginsRelativeTo` and so it is not supported as an option. See https://eslint.org/docs/latest/use/configure/configuration-files-new');
93
- }
94
- if (options.ignorePath !== undefined) {
95
- throw new Error('For Flat Config, ESLint removed `ignorePath` and so it is not supported as an option. See https://eslint.org/docs/latest/use/configure/configuration-files-new');
96
- }
97
- if (options.reportUnusedDisableDirectives) {
98
- throw new Error('For Flat Config, ESLint removed `reportUnusedDisableDirectives` and so it is not supported as an option. See https://eslint.org/docs/latest/use/configure/configuration-files-new');
99
- }
100
- /**
101
- * Adapted from https://github.com/eslint/eslint/blob/50f03a119e6827c03b1d6c86d3aa1f4820b609e8/lib/cli.js#L144
102
- */
103
- if (typeof options.noConfigLookup !== 'undefined') {
104
- const configLookup = !options.noConfigLookup;
105
- let overrideConfigFile = typeof eslintConfigPath === 'string' ? eslintConfigPath : !configLookup;
106
- if (overrideConfigFile === false) {
107
- overrideConfigFile = undefined;
108
- }
109
- eslintOptions.overrideConfigFile = overrideConfigFile;
110
- }
111
- else {
112
- eslintOptions.overrideConfigFile = eslintConfigPath;
83
+ eslintOptions.stats = !!options.stats;
84
+ if (options.applySuppressions) {
85
+ eslintOptions.applySuppressions = true;
86
+ }
87
+ if (options.suppressionsLocation) {
88
+ eslintOptions.suppressionsLocation = options.suppressionsLocation;
89
+ }
90
+ /**
91
+ * Adapted from https://github.com/eslint/eslint/blob/50f03a119e6827c03b1d6c86d3aa1f4820b609e8/lib/cli.js#L144
92
+ */
93
+ if (typeof options.noConfigLookup !== 'undefined') {
94
+ const configLookup = !options.noConfigLookup;
95
+ let overrideConfigFile = typeof eslintConfigPath === 'string' ? eslintConfigPath : !configLookup;
96
+ if (overrideConfigFile === false) {
97
+ overrideConfigFile = undefined;
113
98
  }
99
+ eslintOptions.overrideConfigFile = overrideConfigFile;
114
100
  }
115
101
  else {
116
102
  eslintOptions.overrideConfigFile = eslintConfigPath;
117
- const legacyOptions = eslintOptions;
118
- legacyOptions.rulePaths = options.rulesdir || [];
119
- legacyOptions.resolvePluginsRelativeTo =
120
- options.resolvePluginsRelativeTo || undefined;
121
- legacyOptions.ignorePath = options.ignorePath || undefined;
122
- /**
123
- * If "noEslintrc" is set to `true` (and therefore here "useEslintrc" will be `false`), then ESLint will not
124
- * merge the provided config with others it finds automatically.
125
- */
126
- legacyOptions.useEslintrc = !options.noEslintrc;
127
- legacyOptions.reportUnusedDisableDirectives =
128
- options.reportUnusedDisableDirectives || undefined;
129
103
  }
130
104
  const eslint = new ESLint(eslintOptions);
131
105
  return {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@angular-eslint/builder",
3
- "version": "21.4.1-alpha.7",
3
+ "version": "22.0.0",
4
4
  "description": "Angular CLI builder for ESLint",
5
5
  "license": "MIT",
6
6
  "main": "dist/index.js",
@@ -19,12 +19,12 @@
19
19
  "builders.json"
20
20
  ],
21
21
  "dependencies": {
22
- "@angular-devkit/architect": ">= 0.2100.0 < 0.2200.0",
23
- "@angular-devkit/core": ">= 21.0.0 < 22.0.0"
22
+ "@angular-devkit/architect": ">= 0.2200.0 < 0.2300.0",
23
+ "@angular-devkit/core": ">= 22.0.0 < 23.0.0"
24
24
  },
25
25
  "peerDependencies": {
26
- "@angular/cli": ">= 21.0.0 < 22.0.0",
27
- "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
26
+ "@angular/cli": ">= 22.0.0 < 23.0.0",
27
+ "eslint": "^9.0.0 || ^10.0.0",
28
28
  "typescript": "*"
29
29
  },
30
30
  "builders": "./builders.json",