blob: 34f3ff5810f20dc2337f74db6e0fdc6750c57798 [file] [log] [blame]
Nikolay Vitkovde07efa2025-01-17 12:58:351// Copyright 2025 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
Nikolay Vitkov41c69112025-01-31 15:20:045import stylisticPlugin from '@stylistic/eslint-plugin';
Nikolay Vitkov55adf672025-01-02 12:07:336import typescriptPlugin from '@typescript-eslint/eslint-plugin';
Nikolay Vitkov41c69112025-01-31 15:20:047import tsParser from '@typescript-eslint/parser';
Nikolay Vitkov856215c2025-04-23 14:32:578import { defineConfig, globalIgnores } from 'eslint/config';
Nikolay Vitkovf1e3bd82025-02-13 11:55:229import eslintPlugin from 'eslint-plugin-eslint-plugin';
Nikolay Vitkov55adf672025-01-02 12:07:3310import importPlugin from 'eslint-plugin-import';
11import jsdocPlugin from 'eslint-plugin-jsdoc';
Nikolay Vitkov41c69112025-01-31 15:20:0412import mochaPlugin from 'eslint-plugin-mocha';
Nikolay Vitkov55adf672025-01-02 12:07:3313import globals from 'globals';
Nikolay Vitkova2ced3f2025-03-05 11:58:0314import { join } from 'path';
Nikolay Vitkov55adf672025-01-02 12:07:3315
Nikolay Vitkovc9178752025-04-02 13:37:0716import rulesdirPlugin from './scripts/eslint_rules/rules-dir.mjs';
Nikolay Vitkov55adf672025-01-02 12:07:3317
Nikolay Vitkov856215c2025-04-23 14:32:5718export default defineConfig([
19 globalIgnores([
20 // Git submodules that are not in third_party
21 'build/',
22 'buildtools/',
Nikolay Vitkov5b2bcff2025-01-29 19:21:1223
Nikolay Vitkov856215c2025-04-23 14:32:5724 // Don't include the common build directory
25 'out/',
26 // Don't include third party code
27 'third_party/',
Nikolay Vitkov4dc470b2025-02-20 12:44:3228
Nikolay Vitkov856215c2025-04-23 14:32:5729 'front_end/diff/diff_match_patch.jD',
30 'front_end/models/javascript_metadata/NativeFunctions.js',
31 // All of these scripts are auto-generated so don't lint them.
32 'front_end/generated/ARIAProperties.js',
33 'front_end/generated/Deprecation.ts',
34 'front_end/generated/InspectorBackendCommands.js',
35 'front_end/generated/protocol-mapping.d.ts',
36 'front_end/generated/protocol-proxy-api.d.ts',
37 'front_end/generated/protocol.ts',
38 // Any third_party addition has its source code checked out into
39 // third_party/X/package, so we ignore that code as it's not code we author or
40 // own.
41 'front_end/third_party/*/package/',
42 // Any JS files are also not authored by devtools-frontend, so we ignore those.
43 'front_end/third_party/**/*',
44 // Lighthouse doesn't have a package/ folder but has other nested folders, so
45 // we ignore any folders within the lighthouse directory.
46 'front_end/third_party/lighthouse/*/',
47 // The CodeMirror bundle file is auto-generated and rolled-up as part of the',
48 // install script, so we don't need to lint it.
49 'front_end/third_party/codemirror.next/bundle.ts',
50 // Lit lib files are auto-generated and rolled up as part of the install script.
51 'front_end/third_party/lit/src/*.ts',
52 // @puppeteer/replay is auto-generated.
53 'front_end/third_party/puppeteer-replay/**/*.ts',
54 // Third party code we did not author for extensions
55 'extensions/cxx_debugging/third_party/**/*',
Nikolay Vitkov55adf672025-01-02 12:07:3356
Nikolay Vitkov856215c2025-04-23 14:32:5757 '**/node_modules',
58 'scripts/build/typescript/tests',
59 'scripts/migration/**/*.js',
60 'scripts/protocol_typescript/*.js',
61 'scripts/deps/tests/fixtures',
62 'test/**/fixtures/',
63 'test/e2e/**/*.js',
64 'test/shared/**/*.js',
65 ]),
Nikolay Vitkov55adf672025-01-02 12:07:3366 {
Nikolay Vitkovb4e8dc72025-01-07 13:03:0267 name: 'JavaScript files',
Nikolay Vitkov55adf672025-01-02 12:07:3368 plugins: {
69 '@typescript-eslint': typescriptPlugin,
Nikolay Vitkovb4e8dc72025-01-07 13:03:0270 '@stylistic': stylisticPlugin,
Nikolay Vitkovf1e3bd82025-02-13 11:55:2271 '@eslint-plugin': eslintPlugin,
Nikolay Vitkov55adf672025-01-02 12:07:3372 mocha: mochaPlugin,
73 rulesdir: rulesdirPlugin,
74 import: importPlugin,
75 jsdoc: jsdocPlugin,
Nikolay Vitkov55adf672025-01-02 12:07:3376 },
77
78 languageOptions: {
Nikolay Vitkovb4e8dc72025-01-07 13:03:0279 ecmaVersion: 'latest',
80 sourceType: 'module',
Nikolay Vitkov55adf672025-01-02 12:07:3381 globals: {
82 ...globals.browser,
83 },
Nikolay Vitkov55adf672025-01-02 12:07:3384 },
85
Nikolay Vitkov5b2bcff2025-01-29 19:21:1286 linterOptions: {
87 reportUnusedDisableDirectives: 'error',
88 },
89
Nikolay Vitkov55adf672025-01-02 12:07:3390 rules: {
91 // syntax preferences
Nikolay Vitkovb4e8dc72025-01-07 13:03:0292 '@stylistic/quotes': [
Nikolay Vitkov55adf672025-01-02 12:07:3393 'error',
94 'single',
95 {
96 avoidEscape: true,
97 allowTemplateLiterals: false,
98 },
99 ],
100
Nikolay Vitkovb4e8dc72025-01-07 13:03:02101 '@stylistic/semi': 'error',
102 '@stylistic/no-extra-semi': 'error',
103 '@stylistic/comma-style': ['error', 'last'],
104 '@stylistic/wrap-iife': ['error', 'inside'],
Nikolay Vitkov55adf672025-01-02 12:07:33105
Nikolay Vitkovb4e8dc72025-01-07 13:03:02106 '@stylistic/spaced-comment': [
Nikolay Vitkov55adf672025-01-02 12:07:33107 'error',
108 'always',
109 {
110 markers: ['*'],
111 },
112 ],
113
114 eqeqeq: 'error',
115
116 'accessor-pairs': [
117 'error',
118 {
119 getWithoutSet: false,
120 setWithoutGet: false,
121 },
122 ],
123
124 curly: 'error',
Nikolay Vitkovb4e8dc72025-01-07 13:03:02125 '@stylistic/new-parens': 'error',
126 '@stylistic/func-call-spacing': 'error',
127 '@stylistic/arrow-parens': ['error', 'as-needed'],
128 '@stylistic/eol-last': 'error',
Nikolay Vitkov55adf672025-01-02 12:07:33129 'object-shorthand': ['error', 'properties'],
130 'no-useless-rename': 'error',
131
132 // anti-patterns
133 'no-caller': 'error',
134 'no-case-declarations': 'error',
135 'no-cond-assign': 'error',
136
137 'no-console': [
138 'error',
139 {
140 allow: [
141 'assert',
142 'context',
143 'error',
144 'timeStamp',
145 'time',
146 'timeEnd',
147 'warn',
148 ],
149 },
150 ],
151
152 'no-debugger': 'error',
153 'no-dupe-keys': 'error',
154 'no-duplicate-case': 'error',
155
156 'no-else-return': [
157 'error',
158 {
159 allowElseIf: false,
160 },
161 ],
162
Nikolay Vitkovd1ebd9e2025-02-26 10:19:45163 'no-empty': [
164 'error',
165 {
166 allowEmptyCatch: true,
167 },
168 ],
169 'no-lonely-if': 'error',
170
Nikolay Vitkov55adf672025-01-02 12:07:33171 'no-empty-character-class': 'error',
172 'no-global-assign': 'error',
173 'no-implied-eval': 'error',
174 'no-labels': 'error',
175 'no-multi-str': 'error',
Nikolay Vitkovb4e8dc72025-01-07 13:03:02176 'no-object-constructor': 'error',
Nikolay Vitkov55adf672025-01-02 12:07:33177 'no-octal-escape': 'error',
178 'no-self-compare': 'error',
179 'no-shadow-restricted-names': 'error',
180 'no-unreachable': 'error',
181 'no-unsafe-negation': 'error',
182
183 'no-unused-vars': [
184 'error',
185 {
186 args: 'none',
187 vars: 'local',
188 },
189 ],
190
191 'no-var': 'error',
192 'no-with': 'error',
193 'prefer-const': 'error',
194 radix: 'error',
195 'valid-typeof': 'error',
196 'no-return-assign': ['error', 'always'],
Benedikt Meurer07ad6092025-04-24 15:28:53197 'no-implicit-coercion': ['error', {allow: ['!!']}],
Nikolay Vitkov55adf672025-01-02 12:07:33198
Nikolay Vitkovc32e3bb2025-03-10 10:56:34199 'no-array-constructor': 'error',
200
Nikolay Vitkov55adf672025-01-02 12:07:33201 // es2015 features
202 'require-yield': 'error',
Nikolay Vitkovb4e8dc72025-01-07 13:03:02203 '@stylistic/template-curly-spacing': ['error', 'never'],
Nikolay Vitkov55adf672025-01-02 12:07:33204
205 // file whitespace
Nikolay Vitkovb4e8dc72025-01-07 13:03:02206 '@stylistic/no-multiple-empty-lines': [
Nikolay Vitkov55adf672025-01-02 12:07:33207 'error',
208 {
209 max: 1,
210 },
211 ],
Nikolay Vitkovb4e8dc72025-01-07 13:03:02212 '@stylistic/no-mixed-spaces-and-tabs': 'error',
213 '@stylistic/no-trailing-spaces': 'error',
214 '@stylistic/linebreak-style': ['error', 'unix'],
Nikolay Vitkov55adf672025-01-02 12:07:33215
216 /**
217 * Disabled, aspirational rules
218 */
Nikolay Vitkovb4e8dc72025-01-07 13:03:02219 '@stylistic/indent': [
Nikolay Vitkov55adf672025-01-02 12:07:33220 'off',
221 2,
222 {
223 SwitchCase: 1,
224 CallExpression: {
225 arguments: 2,
226 },
227 MemberExpression: 2,
228 },
229 ],
230
231 // brace-style is disabled, as eslint cannot enforce 1tbs as default, but allman for functions
Nikolay Vitkovb4e8dc72025-01-07 13:03:02232 '@stylistic/brace-style': [
Nikolay Vitkov55adf672025-01-02 12:07:33233 'off',
234 'allman',
235 {
236 allowSingleLine: true,
237 },
238 ],
239
240 // key-spacing is disabled, as some objects use value-aligned spacing, some not.
Nikolay Vitkovb4e8dc72025-01-07 13:03:02241 '@stylistic/key-spacing': [
Nikolay Vitkov55adf672025-01-02 12:07:33242 'off',
243 {
244 beforeColon: false,
245 afterColon: true,
246 align: 'value',
247 },
248 ],
249
Nikolay Vitkovb4e8dc72025-01-07 13:03:02250 '@stylistic/quote-props': ['error', 'as-needed'],
Nikolay Vitkov55adf672025-01-02 12:07:33251
252 // no-implicit-globals will prevent accidental globals
253 'no-implicit-globals': 'off',
254 'no-unused-private-class-members': 'error',
255
Benedikt Meurer00c39582025-01-31 09:26:27256 // Sort imports first
257 'import/first': 'error',
Nikolay Vitkov55adf672025-01-02 12:07:33258 // Closure does not properly typecheck default exports
259 'import/no-default-export': 'error',
260 /**
261 * Catch duplicate import paths. For example this would catch the following example:
262 * import {Foo} from './foo.js'
263 * import * as FooModule from './foo.js'
264 **/
265 'import/no-duplicates': 'error',
Nikolay Vitkov41c69112025-01-31 15:20:04266 /**
267 * Provides more consistency in the imports.
268 */
269 'import/order': [
270 'error',
271 {
272 // We need to group the builtin and external as clang-format
273 // can't differentiate the two
274 groups: [['builtin', 'external'], 'parent', 'sibling', 'index'],
275 'newlines-between': 'always',
276 // clang-format has it's own logic overriding this
277 named: false,
278 alphabetize: {
279 order: 'asc',
280 caseInsensitive: true,
281 },
282 },
283 ],
Nikolay Vitkov55adf672025-01-02 12:07:33284 // Try to spot '// console.log()' left over from debugging
285 'rulesdir/no-commented-out-console': 'error',
286 // Prevent imports being commented out rather than deleted.
287 'rulesdir/no-commented-out-import': 'error',
Nikolay Vitkov55adf672025-01-02 12:07:33288 'rulesdir/check-license-header': 'error',
Nikolay Vitkov55adf672025-01-02 12:07:33289 /**
290 * Ensures that JS Doc comments are properly aligned - all the starting
291 * `*` are in the right place.
292 */
293 'jsdoc/check-alignment': 'error',
294 },
295 },
296 {
297 name: 'TypeScript files',
298 files: ['**/*.ts'],
299
300 languageOptions: {
301 ecmaVersion: 'latest',
302 sourceType: 'module',
303
Nikolay Vitkovb4e8dc72025-01-07 13:03:02304 parser: tsParser,
Nikolay Vitkov55adf672025-01-02 12:07:33305 parserOptions: {
306 allowAutomaticSingleRunInference: true,
307 project: join(
Benedikt Meurer07ad6092025-04-24 15:28:53308 import.meta.dirname,
309 'config',
310 'typescript',
311 'tsconfig.eslint.json',
312 ),
Nikolay Vitkov55adf672025-01-02 12:07:33313 },
314 },
315
316 rules: {
Benedikt Meurer84b0fcc2025-02-12 13:57:23317 '@typescript-eslint/array-type': [
318 'error',
319 {
320 default: 'array-simple',
321 },
322 ],
Nikolay Vitkov55adf672025-01-02 12:07:33323 '@typescript-eslint/no-explicit-any': [
324 'error',
325 {
326 ignoreRestArgs: true,
327 },
328 ],
329
330 '@typescript-eslint/explicit-member-accessibility': [
331 'error',
332 {
333 accessibility: 'no-public',
334 },
335 ],
336
Nikolay Vitkov55adf672025-01-02 12:07:33337 // run just the TypeScript unused-vars rule, else we get duplicate errors
338 'no-unused-vars': 'off',
339 '@typescript-eslint/no-unused-vars': [
340 'error',
341 {
342 argsIgnorePattern: '^_',
343 },
344 ],
345
Nikolay Vitkovb4e8dc72025-01-07 13:03:02346 '@stylistic/member-delimiter-style': [
Nikolay Vitkov55adf672025-01-02 12:07:33347 'error',
348 {
349 multiline: {
350 delimiter: 'semi',
351 requireLast: true,
352 },
353
354 singleline: {
355 delimiter: 'comma',
356 requireLast: false,
357 },
358
359 overrides: {
360 interface: {
361 singleline: {
362 delimiter: 'semi',
363 requireLast: false,
364 },
365
366 multiline: {
367 delimiter: 'semi',
368 requireLast: true,
369 },
370 },
371
372 typeLiteral: {
373 singleline: {
374 delimiter: 'comma',
375 requireLast: false,
376 },
377
378 multiline: {
379 delimiter: 'comma',
380 requireLast: true,
381 },
382 },
383 },
384 },
385 ],
386
387 '@typescript-eslint/no-floating-promises': [
388 'error',
389 {
390 ignoreVoid: true,
391 },
392 ],
393
Nikolay Vitkov55adf672025-01-02 12:07:33394 /**
395 * Enforce that enum members are explicitly defined:
396 * const enum Foo { A = 'a' } rather than const enum Foo { A }
397 */
398 '@typescript-eslint/prefer-enum-initializers': 'error',
399 /**
400 * Ban non-null assertion operator, e.g.:
401 * this.foo!.toLowerCase()
402 */
403 '@typescript-eslint/no-non-null-assertion': 'error',
404 '@typescript-eslint/consistent-type-imports': 'error',
405
406 '@typescript-eslint/naming-convention': [
407 'error',
Nikolay Vitkov52a17972025-01-15 12:52:10408 // Forbids interfaces starting with an I prefix.
409 {
410 selector: 'interface',
411 format: ['PascalCase'],
412
413 custom: {
414 regex: '^I[A-Z]',
415 match: false,
416 },
417 },
Nikolay Vitkov55adf672025-01-02 12:07:33418 {
419 selector: [
420 'function',
421 'accessor',
422 'method',
423 'property',
424 'parameterProperty',
425 ],
426 format: ['camelCase'],
427 },
428 {
429 selector: 'variable',
430
431 filter: {
432 // Ignore localization variables.
433 regex: '^(UIStrings|str_)$',
434 match: false,
435 },
436
437 format: ['camelCase'],
438 },
439 {
440 // We are using camelCase, PascalCase and UPPER_CASE for top-level constants, allow the for now.
441 selector: 'variable',
442 modifiers: ['const'],
443 filter: {
444 // Ignore localization variables.
445 regex: '^(UIStrings|str_)$',
446 match: false,
447 },
448
449 format: ['camelCase', 'UPPER_CASE', 'PascalCase'],
450 },
451 {
452 selector: 'classProperty',
453 modifiers: ['static', 'readonly'],
454 format: ['UPPER_CASE', 'camelCase'],
455 },
456 {
457 selector: 'enumMember',
458 format: ['UPPER_CASE'],
459 },
460 {
461 selector: ['typeLike'],
462 format: ['PascalCase'],
463 },
464 {
465 selector: 'parameter',
466 format: ['camelCase'],
467 leadingUnderscore: 'allow',
468 },
469 {
470 // Public methods are currently in transition and may still have leading underscores.
471 selector: 'method',
472 modifiers: ['public'],
473 format: ['camelCase'],
474 leadingUnderscore: 'allow',
475 },
476 {
477 selector: 'property',
478 modifiers: ['public'],
479 format: ['camelCase'],
480 leadingUnderscore: 'allow',
481 },
482 {
483 // Object literals may be constructed as arguments to external libraries which follow different styles.
484 selector: ['objectLiteralMethod', 'objectLiteralProperty'],
485 modifiers: ['public'],
486 format: null,
487 },
488 {
489 // Ignore type properties that require quotes
490 selector: 'typeProperty',
491 format: null,
492 modifiers: ['requiresQuotes'],
493 },
494 ],
495
Nikolay Vitkov44e30062025-01-07 14:33:04496 '@typescript-eslint/consistent-type-definitions': ['error', 'interface'],
497
Nikolay Vitkov93062642025-02-18 09:49:30498 // Disable eslint base rule
499 'no-throw-literal': 'off',
500 '@typescript-eslint/only-throw-error': 'error',
501
Nikolay Vitkov65a5a912025-02-18 18:30:26502 // Disabled this rule while investigating why it creates
503 // certain TypeScript compilation errors after fixes
504 '@typescript-eslint/no-unnecessary-type-assertion': 'off',
505
Nikolay Vitkovd36860c2025-02-19 17:50:27506 '@typescript-eslint/no-inferrable-types': 'error',
507
Nikolay Vitkovd396b272025-02-19 08:37:28508 '@typescript-eslint/consistent-generic-constructors': [
509 'error',
510 'constructor',
511 ],
512
Nikolay Vitkovd36860c2025-02-19 17:50:27513 // This is more performant
514 // And should provide better stack trace when debugging
515 // see https://v8.dev/blog/fast-async.
516 '@typescript-eslint/return-await': ['error', 'always'],
517
518 '@typescript-eslint/ban-ts-comment': [
519 'error',
520 {
521 // Change after we add some placeholder for old errors
522 minimumDescriptionLength: 0,
523 'ts-check': false,
524 'ts-expect-error': 'allow-with-description',
525 'ts-ignore': true,
526 'ts-nocheck': true,
527 },
528 ],
529
Nikolay Vitkov68140172025-02-20 19:25:39530 '@typescript-eslint/prefer-optional-chain': 'error',
531
Nikolay Vitkovb30f3b22025-03-04 13:38:33532 '@typescript-eslint/no-unsafe-function-type': 'error',
533
Nikolay Vitkova2ced3f2025-03-05 11:58:03534 '@typescript-eslint/no-empty-object-type': [
535 'error',
536 {
537 allowInterfaces: 'with-single-extends',
538 },
539 ],
540
Nikolay Vitkovc32e3bb2025-03-10 10:56:34541 'no-array-constructor': 'off',
542 '@typescript-eslint/no-array-constructor': 'error',
543
Nikolay Vitkov55adf672025-01-02 12:07:33544 'rulesdir/no-underscored-properties': 'error',
Nikolay Vitkov55adf672025-01-02 12:07:33545 'rulesdir/inline-type-imports': 'error',
546
547 'rulesdir/enforce-default-import-name': [
548 'error',
549 {
550 // Enforce that any import of models/trace/trace.js names the import Trace.
551 modulePath: join(
Benedikt Meurer07ad6092025-04-24 15:28:53552 import.meta.dirname,
553 'front_end',
554 'models',
555 'trace',
556 'trace.js',
557 ),
Nikolay Vitkov55adf672025-01-02 12:07:33558 importName: 'Trace',
559 },
560 ],
561 },
562 },
Nikolay Vitkov55adf672025-01-02 12:07:33563 {
564 name: 'Scripts files',
565 files: ['scripts/**/*'],
566 rules: {
567 'no-console': 'off',
Nikolay Vitkovde07efa2025-01-17 12:58:35568 'rulesdir/es-modules-import': 'off',
Nikolay Vitkov77ba8df2025-03-27 15:06:08569 'import/no-default-export': 'off',
Nikolay Vitkov55adf672025-01-02 12:07:33570 },
571 },
Nikolay Vitkov55adf672025-01-02 12:07:33572 {
573 name: 'Front-end files',
574 files: ['front_end/**/*'],
575 rules: {
576 // L10n rules are only relevant in 'front_end'.
577 'rulesdir/l10n-filename-matches': [
578 'error',
579 {
580 rootFrontendDirectory: join(import.meta.dirname, 'front_end'),
581 },
582 ],
583 'rulesdir/l10n-i18nString-call-only-with-uistrings': 'error',
584 'rulesdir/l10n-no-i18nString-calls-module-instantiation': 'error',
585 'rulesdir/l10n-no-locked-or-placeholder-only-phrase': 'error',
586 'rulesdir/l10n-no-uistrings-export': 'error',
587 'rulesdir/l10n-no-unused-message': 'error',
588 },
589 },
Nikolay Vitkov55adf672025-01-02 12:07:33590 {
591 name: 'Front-end TypeScript files',
592 files: ['front_end/**/*.ts'],
593 rules: {
594 '@typescript-eslint/explicit-function-return-type': [
595 'error',
596 {
597 allowExpressions: true,
598 allowConciseArrowFunctionExpressionsStartingWithVoid: true,
599 allowIIFEs: true,
600 },
601 ],
Danil Somsikovd12440f2025-03-31 18:45:43602 'rulesdir/no-imperative-dom-api': 'error',
Danil Somsikov9f442bb2025-04-02 09:58:27603 'rulesdir/no-lit-render-outside-of-view': 'error',
Nikolay Vitkov55adf672025-01-02 12:07:33604 'rulesdir/no-importing-images-from-src': 'error',
Nikolay Vitkov55adf672025-01-02 12:07:33605 'rulesdir/enforce-custom-event-names': 'error',
606 'rulesdir/set-data-type-reference': 'error',
607 'rulesdir/no-bound-component-methods': 'error',
Benedikt Meurer07ad6092025-04-24 15:28:53608 'rulesdir/no-adopted-style-sheets': 'error',
Nikolay Vitkov55adf672025-01-02 12:07:33609 'rulesdir/no-customized-builtin-elements': 'error',
610 'rulesdir/no-self-closing-custom-element-tagnames': 'error',
Benedikt Meurer6e534082025-01-29 10:36:02611 'rulesdir/no-a-tags-in-lit': 'error',
Nikolay Vitkov55adf672025-01-02 12:07:33612 'rulesdir/check-css-import': 'error',
613 'rulesdir/enforce-optional-properties-last': 'error',
614 'rulesdir/check-enumerated-histograms': 'error',
615 'rulesdir/check-was-shown-methods': 'error',
616 'rulesdir/static-custom-event-names': 'error',
Benedikt Meurer6e534082025-01-29 10:36:02617 'rulesdir/lit-no-attribute-quotes': 'error',
Nikolay Vitkov55adf672025-01-02 12:07:33618 'rulesdir/lit-template-result-or-nothing': 'error',
619 'rulesdir/inject-checkbox-styles': 'error',
620 'rulesdir/jslog-context-list': 'error',
Nikolay Vitkovde07efa2025-01-17 12:58:35621 'rulesdir/es-modules-import': 'error',
622 'rulesdir/html-tagged-template': 'error',
Nikolay Vitkov49d12de2025-02-12 14:41:46623 'rulesdir/enforce-custom-element-definitions-location': [
624 'error',
625 {
626 rootFrontendDirectory: join(import.meta.dirname, 'front_end'),
627 },
628 ],
Ergun Erdogmus5efc7e92025-02-21 11:36:50629 'rulesdir/enforce-ui-strings-as-const': 'error',
Nikolay Vitkov55adf672025-01-02 12:07:33630 },
631 },
Nikolay Vitkov55adf672025-01-02 12:07:33632 {
633 name: 'Front-end meta files',
634 files: ['front_end/**/*-meta.ts'],
635 rules: {
636 '@typescript-eslint/naming-convention': [
637 'error',
638 {
639 selector: 'parameter',
640 format: ['camelCase', 'PascalCase'],
641 leadingUnderscore: 'allow',
642 },
643 ],
644 },
645 },
Nikolay Vitkov55adf672025-01-02 12:07:33646 {
647 name: 'TypeScript test files',
648 files: [
649 '*.test.ts',
650 // This makes the specificity greater than the front-end ts files
651 'front_end/**/*.test.ts',
652 'test/**/*.ts',
653 '**/testing/*.ts',
Nikolay Vitkov856215c2025-04-23 14:32:57654 'scripts/eslint_rules/test/**/*',
Nikolay Vitkov3cace8f2025-02-14 16:37:53655 'extensions/cxx_debugging/e2e/**',
Nikolay Vitkov55adf672025-01-02 12:07:33656 ],
657
658 rules: {
659 // errors on it('test') with no body
660 'mocha/no-pending-tests': 'error',
661
662 // errors on {describe, it}.only
663 'mocha/no-exclusive-tests': 'error',
664
665 'mocha/no-async-describe': 'error',
666 'mocha/no-global-tests': 'error',
667 'mocha/no-nested-tests': 'error',
668
669 '@typescript-eslint/no-non-null-assertion': 'off',
670 '@typescript-eslint/explicit-function-return-type': 'off',
671
Nikolay Vitkov93062642025-02-18 09:49:30672 '@typescript-eslint/only-throw-error': [
673 'error',
674 {
675 allow: [
676 {
677 // Chai AssertionError does not extend Error
678 from: 'package',
679 package: 'chai',
680 name: ['AssertionError'],
681 },
682 ],
683 },
684 ],
685
Nikolay Vitkov55adf672025-01-02 12:07:33686 'rulesdir/check-test-definitions': 'error',
687 'rulesdir/no-assert-strict-equal-for-arrays-and-objects': 'error',
688 'rulesdir/no-assert-deep-strict-equal': 'error',
689 'rulesdir/no-assert-equal': 'error',
690 'rulesdir/no-assert-equal-boolean-null-undefined': 'error',
Danil Somsikovd12440f2025-03-31 18:45:43691 'rulesdir/no-imperative-dom-api': 'off',
Danil Somsikov9f442bb2025-04-02 09:58:27692 'rulesdir/no-lit-render-outside-of-view': 'off',
Nikolay Vitkov55adf672025-01-02 12:07:33693 'rulesdir/no-screenshot-test-outside-perf-panel': 'error',
694 'rulesdir/prefer-assert-instance-of': 'error',
695 'rulesdir/prefer-assert-is-ok': 'error',
696 'rulesdir/prefer-assert-length-of': 'error',
Benedikt Meurerebef25a2025-04-11 12:28:21697 'rulesdir/prefer-assert-strict-equal': 'error',
Benedikt Meureradbf0df2025-04-11 08:48:52698 'rulesdir/prefer-sinon-assert': 'error',
Benedikt Meurer39e51332025-01-07 13:17:54699 'rulesdir/prefer-url-string': 'error',
Nikolay Vitkov55adf672025-01-02 12:07:33700 'rulesdir/trace-engine-test-timeouts': 'error',
Nikolay Vitkov49d12de2025-02-12 14:41:46701 'rulesdir/enforce-custom-element-definitions-location': 'off',
Nikolay Vitkov55adf672025-01-02 12:07:33702 },
703
704 settings: {
705 'mocha/additionalCustomNames': [
706 {
707 name: 'describeWithDevtoolsExtension',
708 type: 'suite',
709 interfaces: ['BDD', 'TDD'],
710 },
711 {
712 name: 'describeWithEnvironment',
713 type: 'suite',
714 interfaces: ['BDD', 'TDD'],
715 },
716 {
717 name: 'describeWithLocale',
718 type: 'suite',
719 interfaces: ['BDD', 'TDD'],
720 },
721 {
722 name: 'describeWithMockConnection',
723 type: 'suite',
724 interfaces: ['BDD', 'TDD'],
725 },
726 {
727 name: 'describeWithRealConnection',
728 type: 'suite',
729 interfaces: ['BDD', 'TDD'],
730 },
731 {
732 name: 'itScreenshot',
733 type: 'testCase',
734 interfaces: ['BDD', 'TDD'],
735 },
736 ],
737 },
738 },
Nikolay Vitkov55adf672025-01-02 12:07:33739 {
Nikolay Vitkov52a17972025-01-15 12:52:10740 name: 'Use private class members rule',
Nikolay Vitkov55adf672025-01-02 12:07:33741 files: [
742 'front_end/panels/**/components/*.ts',
743 'front_end/ui/components/**/*.ts',
744 'front_end/entrypoints/**/*.ts',
745 ],
746
747 rules: {
748 'rulesdir/prefer-private-class-members': 'error',
749 },
750 },
Nikolay Vitkov55adf672025-01-02 12:07:33751 {
Nikolay Vitkov52a17972025-01-15 12:52:10752 name: 'Ignore private class members rule',
Nikolay Vitkov55adf672025-01-02 12:07:33753 files: [
754 'front_end/panels/recorder/**/*.ts',
Nikolay Vitkov55adf672025-01-02 12:07:33755 'front_end/ui/components/suggestion_input/*.ts',
756 ],
757 rules: {
758 // TODO(crbug/1402569): Reenable once https://github.com/microsoft/TypeScript/issues/48885 is closed.
759 'rulesdir/prefer-private-class-members': 'off',
760 },
761 },
Nikolay Vitkov55adf672025-01-02 12:07:33762 {
Nikolay Vitkov52a17972025-01-15 12:52:10763 name: 'Supported CSS properties rules',
Nikolay Vitkov55adf672025-01-02 12:07:33764 files: ['front_end/generated/SupportedCSSProperties.js'],
765 rules: {
766 'rulesdir/jslog-context-list': 'error',
767 },
768 },
Nikolay Vitkov55adf672025-01-02 12:07:33769 {
770 name: 'EsLint rules test',
Nikolay Vitkov856215c2025-04-23 14:32:57771 files: ['scripts/eslint_rules/tests/**/*'],
Nikolay Vitkov55adf672025-01-02 12:07:33772 rules: {
Nikolay Vitkovf1e3bd82025-02-13 11:55:22773 '@eslint-plugin/no-only-tests': 'error',
Nikolay Vitkov55adf672025-01-02 12:07:33774 },
775 },
Nikolay Vitkov55adf672025-01-02 12:07:33776 {
777 name: 'Legacy test runner',
778 files: ['front_end/legacy_test_runner/**/*'],
779 rules: {
780 'rulesdir/es-modules-import': 'off',
781 },
782 },
783 {
784 name: 'Front end component docs',
785 files: ['front_end/ui/components/docs/**/*.ts'],
786 rules: {
787 // This makes the component doc examples very verbose and doesn't add
788 // anything, so we leave return types to the developer within the
789 // component_docs folder.
790 '@typescript-eslint/explicit-function-return-type': 'off',
Benedikt Meurer6e534082025-01-29 10:36:02791 // We use Lit to help render examples sometimes and we don't use
Nikolay Vitkov55adf672025-01-02 12:07:33792 // {host: this} as often the `this` is the window.
Benedikt Meurer6e534082025-01-29 10:36:02793 'rulesdir/lit-host-this': 'off',
Danil Somsikovd12440f2025-03-31 18:45:43794 'rulesdir/no-imperative-dom-api': 'off',
Danil Somsikov9f442bb2025-04-02 09:58:27795 'rulesdir/no-lit-render-outside-of-view': 'off',
Nikolay Vitkov55adf672025-01-02 12:07:33796 },
797 },
798 {
Nikolay Vitkov52a17972025-01-15 12:52:10799 name: 'Traces import rule',
Nikolay Vitkov55adf672025-01-02 12:07:33800 files: ['front_end/models/trace/handlers/**/*.ts'],
801 rules: {
802 'rulesdir/no-imports-in-directory': [
803 'error',
804 {
805 bannedImportPaths: [
806 join(import.meta.dirname, 'front_end', 'core', 'sdk', 'sdk.js'),
807 ],
808 },
809 ],
810 },
811 },
812 {
Nikolay Vitkov52a17972025-01-15 12:52:10813 name: 'Recorder injected code',
Nikolay Vitkov55adf672025-01-02 12:07:33814 files: ['front_end/panels/recorder/injected/**/*.ts'],
815 rules: {
816 // The code is rolled up and tree-shaken independently from the regular entrypoints.
817 'rulesdir/es-modules-import': 'off',
818 },
819 },
820 {
Nikolay Vitkovb4e8dc72025-01-07 13:03:02821 name: 'Performance panel file',
Nikolay Vitkov55adf672025-01-02 12:07:33822 files: ['front_end/ui/legacy/components/perf_ui/**/*.ts'],
823 rules: {
824 // Enable tracking of canvas save() and
825 // restore() calls to try and catch bugs. Only
826 // enabled in this folder because it is an
827 // expensive rule to run and we do not need it
828 // for any code that doesn't use Canvas.
829 'rulesdir/canvas-context-tracking': 'error',
830 },
831 },
Jack Franklinc10b4972025-02-28 16:32:56832 {
833 name: 'TypeScript type-definitions',
834 files: ['**/*.d.ts'],
835 rules: {
836 // Not a useful rule for .d.ts files where we are
837 // representing an existing module.
Nikolay Vitkova2ced3f2025-03-05 11:58:03838 'import/no-default-export': 'off',
839 },
840 },
Nikolay Vitkovafc8a8c2025-03-17 10:05:28841 {
842 name: 'Config files',
843 files: ['eslint.config.mjs', '**/*/rollup.config.mjs'],
844 rules: {
845 // The config operate on the default export
846 // So allow it for them
847 'import/no-default-export': 'off',
848 },
849 },
Nikolay Vitkov856215c2025-04-23 14:32:57850]);