blob: f278ef1b75d63e427fb28c39fa5dba8aff169984 [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 Vitkov856215c2025-04-23 14:32:576import { defineConfig, globalIgnores } from 'eslint/config';
Nikolay Vitkovf1e3bd82025-02-13 11:55:227import eslintPlugin from 'eslint-plugin-eslint-plugin';
Nikolay Vitkov55adf672025-01-02 12:07:338import importPlugin from 'eslint-plugin-import';
9import jsdocPlugin from 'eslint-plugin-jsdoc';
Nikolay Vitkov41c69112025-01-31 15:20:0410import mochaPlugin from 'eslint-plugin-mocha';
Nikolay Vitkov55adf672025-01-02 12:07:3311import globals from 'globals';
Nikolay Vitkova2ced3f2025-03-05 11:58:0312import { join } from 'path';
Nikolay Vitkovf1e99c02025-05-09 12:21:4213import typescriptEslint from 'typescript-eslint';
Nikolay Vitkov55adf672025-01-02 12:07:3314
Nikolay Vitkovc9178752025-04-02 13:37:0715import rulesdirPlugin from './scripts/eslint_rules/rules-dir.mjs';
Nikolay Vitkov55adf672025-01-02 12:07:3316
Nikolay Vitkov856215c2025-04-23 14:32:5717export default defineConfig([
18 globalIgnores([
19 // Git submodules that are not in third_party
20 'build/',
21 'buildtools/',
Nikolay Vitkov5b2bcff2025-01-29 19:21:1222
Nikolay Vitkov856215c2025-04-23 14:32:5723 // Don't include the common build directory
24 'out/',
25 // Don't include third party code
26 'third_party/',
Nikolay Vitkov4dc470b2025-02-20 12:44:3227
Nikolay Vitkov856215c2025-04-23 14:32:5728 'front_end/diff/diff_match_patch.jD',
29 'front_end/models/javascript_metadata/NativeFunctions.js',
30 // All of these scripts are auto-generated so don't lint them.
31 'front_end/generated/ARIAProperties.js',
32 'front_end/generated/Deprecation.ts',
33 'front_end/generated/InspectorBackendCommands.js',
34 'front_end/generated/protocol-mapping.d.ts',
35 'front_end/generated/protocol-proxy-api.d.ts',
36 'front_end/generated/protocol.ts',
37 // Any third_party addition has its source code checked out into
38 // third_party/X/package, so we ignore that code as it's not code we author or
39 // own.
40 'front_end/third_party/*/package/',
41 // Any JS files are also not authored by devtools-frontend, so we ignore those.
42 'front_end/third_party/**/*',
43 // Lighthouse doesn't have a package/ folder but has other nested folders, so
44 // we ignore any folders within the lighthouse directory.
45 'front_end/third_party/lighthouse/*/',
46 // The CodeMirror bundle file is auto-generated and rolled-up as part of the',
47 // install script, so we don't need to lint it.
48 'front_end/third_party/codemirror.next/bundle.ts',
49 // Lit lib files are auto-generated and rolled up as part of the install script.
50 'front_end/third_party/lit/src/*.ts',
51 // @puppeteer/replay is auto-generated.
52 'front_end/third_party/puppeteer-replay/**/*.ts',
53 // Third party code we did not author for extensions
54 'extensions/cxx_debugging/third_party/**/*',
Nikolay Vitkov55adf672025-01-02 12:07:3355
Nikolay Vitkov856215c2025-04-23 14:32:5756 '**/node_modules',
57 'scripts/build/typescript/tests',
58 'scripts/migration/**/*.js',
59 'scripts/protocol_typescript/*.js',
60 'scripts/deps/tests/fixtures',
61 'test/**/fixtures/',
62 'test/e2e/**/*.js',
63 'test/shared/**/*.js',
64 ]),
Nikolay Vitkov55adf672025-01-02 12:07:3365 {
Nikolay Vitkovb4e8dc72025-01-07 13:03:0266 name: 'JavaScript files',
Nikolay Vitkov55adf672025-01-02 12:07:3367 plugins: {
Nikolay Vitkovf1e99c02025-05-09 12:21:4268 '@typescript-eslint': typescriptEslint.plugin,
Nikolay Vitkovb4e8dc72025-01-07 13:03:0269 '@stylistic': stylisticPlugin,
Nikolay Vitkovf1e3bd82025-02-13 11:55:2270 '@eslint-plugin': eslintPlugin,
Nikolay Vitkov55adf672025-01-02 12:07:3371 mocha: mochaPlugin,
72 rulesdir: rulesdirPlugin,
73 import: importPlugin,
74 jsdoc: jsdocPlugin,
Nikolay Vitkov55adf672025-01-02 12:07:3375 },
76
77 languageOptions: {
Nikolay Vitkovb4e8dc72025-01-07 13:03:0278 ecmaVersion: 'latest',
79 sourceType: 'module',
Nikolay Vitkov55adf672025-01-02 12:07:3380 globals: {
81 ...globals.browser,
82 },
Nikolay Vitkov55adf672025-01-02 12:07:3383 },
84
Nikolay Vitkov5b2bcff2025-01-29 19:21:1285 linterOptions: {
86 reportUnusedDisableDirectives: 'error',
87 },
88
Nikolay Vitkov55adf672025-01-02 12:07:3389 rules: {
90 // syntax preferences
Nikolay Vitkovb4e8dc72025-01-07 13:03:0291 '@stylistic/quotes': [
Nikolay Vitkov55adf672025-01-02 12:07:3392 'error',
93 'single',
94 {
95 avoidEscape: true,
96 allowTemplateLiterals: false,
97 },
98 ],
99
Nikolay Vitkovb4e8dc72025-01-07 13:03:02100 '@stylistic/semi': 'error',
101 '@stylistic/no-extra-semi': 'error',
102 '@stylistic/comma-style': ['error', 'last'],
103 '@stylistic/wrap-iife': ['error', 'inside'],
Nikolay Vitkov55adf672025-01-02 12:07:33104
Nikolay Vitkovb4e8dc72025-01-07 13:03:02105 '@stylistic/spaced-comment': [
Nikolay Vitkov55adf672025-01-02 12:07:33106 'error',
107 'always',
108 {
109 markers: ['*'],
110 },
111 ],
112
113 eqeqeq: 'error',
114
115 'accessor-pairs': [
116 'error',
117 {
118 getWithoutSet: false,
119 setWithoutGet: false,
120 },
121 ],
122
123 curly: 'error',
Nikolay Vitkovb4e8dc72025-01-07 13:03:02124 '@stylistic/new-parens': 'error',
125 '@stylistic/func-call-spacing': 'error',
126 '@stylistic/arrow-parens': ['error', 'as-needed'],
127 '@stylistic/eol-last': 'error',
Nikolay Vitkov55adf672025-01-02 12:07:33128 'object-shorthand': ['error', 'properties'],
129 'no-useless-rename': 'error',
130
131 // anti-patterns
132 'no-caller': 'error',
133 'no-case-declarations': 'error',
134 'no-cond-assign': 'error',
135
136 'no-console': [
137 'error',
138 {
139 allow: [
140 'assert',
141 'context',
142 'error',
143 'timeStamp',
144 'time',
145 'timeEnd',
146 'warn',
147 ],
148 },
149 ],
150
151 'no-debugger': 'error',
152 'no-dupe-keys': 'error',
153 'no-duplicate-case': 'error',
154
155 'no-else-return': [
156 'error',
157 {
158 allowElseIf: false,
159 },
160 ],
161
Nikolay Vitkovd1ebd9e2025-02-26 10:19:45162 'no-empty': [
163 'error',
164 {
165 allowEmptyCatch: true,
166 },
167 ],
168 'no-lonely-if': 'error',
169
Nikolay Vitkov55adf672025-01-02 12:07:33170 'no-empty-character-class': 'error',
171 'no-global-assign': 'error',
172 'no-implied-eval': 'error',
173 'no-labels': 'error',
174 'no-multi-str': 'error',
Nikolay Vitkovb4e8dc72025-01-07 13:03:02175 'no-object-constructor': 'error',
Nikolay Vitkov55adf672025-01-02 12:07:33176 'no-octal-escape': 'error',
177 'no-self-compare': 'error',
178 'no-shadow-restricted-names': 'error',
179 'no-unreachable': 'error',
180 'no-unsafe-negation': 'error',
181
182 'no-unused-vars': [
183 'error',
184 {
185 args: 'none',
186 vars: 'local',
187 },
188 ],
189
190 'no-var': 'error',
191 'no-with': 'error',
192 'prefer-const': 'error',
193 radix: 'error',
194 'valid-typeof': 'error',
195 'no-return-assign': ['error', 'always'],
Nikolay Vitkovf1e99c02025-05-09 12:21:42196 'no-implicit-coercion': ['error', { allow: ['!!'] }],
Nikolay Vitkov55adf672025-01-02 12:07:33197
Nikolay Vitkovc32e3bb2025-03-10 10:56:34198 'no-array-constructor': 'error',
199
Nikolay Vitkov55adf672025-01-02 12:07:33200 // es2015 features
201 'require-yield': 'error',
Nikolay Vitkovb4e8dc72025-01-07 13:03:02202 '@stylistic/template-curly-spacing': ['error', 'never'],
Nikolay Vitkov55adf672025-01-02 12:07:33203
204 // file whitespace
Nikolay Vitkovb4e8dc72025-01-07 13:03:02205 '@stylistic/no-multiple-empty-lines': [
Nikolay Vitkov55adf672025-01-02 12:07:33206 'error',
207 {
208 max: 1,
209 },
210 ],
Nikolay Vitkovb4e8dc72025-01-07 13:03:02211 '@stylistic/no-mixed-spaces-and-tabs': 'error',
212 '@stylistic/no-trailing-spaces': 'error',
213 '@stylistic/linebreak-style': ['error', 'unix'],
Nikolay Vitkov55adf672025-01-02 12:07:33214
215 /**
216 * Disabled, aspirational rules
217 */
Nikolay Vitkovb4e8dc72025-01-07 13:03:02218 '@stylistic/indent': [
Nikolay Vitkov55adf672025-01-02 12:07:33219 'off',
220 2,
221 {
222 SwitchCase: 1,
223 CallExpression: {
224 arguments: 2,
225 },
226 MemberExpression: 2,
227 },
228 ],
229
230 // brace-style is disabled, as eslint cannot enforce 1tbs as default, but allman for functions
Nikolay Vitkovb4e8dc72025-01-07 13:03:02231 '@stylistic/brace-style': [
Nikolay Vitkov55adf672025-01-02 12:07:33232 'off',
233 'allman',
234 {
235 allowSingleLine: true,
236 },
237 ],
238
239 // key-spacing is disabled, as some objects use value-aligned spacing, some not.
Nikolay Vitkovb4e8dc72025-01-07 13:03:02240 '@stylistic/key-spacing': [
Nikolay Vitkov55adf672025-01-02 12:07:33241 'off',
242 {
243 beforeColon: false,
244 afterColon: true,
245 align: 'value',
246 },
247 ],
248
Nikolay Vitkovb4e8dc72025-01-07 13:03:02249 '@stylistic/quote-props': ['error', 'as-needed'],
Nikolay Vitkov55adf672025-01-02 12:07:33250
251 // no-implicit-globals will prevent accidental globals
252 'no-implicit-globals': 'off',
253 'no-unused-private-class-members': 'error',
254
Benedikt Meurer00c39582025-01-31 09:26:27255 // Sort imports first
256 'import/first': 'error',
Nikolay Vitkov55adf672025-01-02 12:07:33257 // Closure does not properly typecheck default exports
258 'import/no-default-export': 'error',
259 /**
260 * Catch duplicate import paths. For example this would catch the following example:
261 * import {Foo} from './foo.js'
262 * import * as FooModule from './foo.js'
263 **/
264 'import/no-duplicates': 'error',
Nikolay Vitkov41c69112025-01-31 15:20:04265 /**
266 * Provides more consistency in the imports.
267 */
268 'import/order': [
269 'error',
270 {
271 // We need to group the builtin and external as clang-format
272 // can't differentiate the two
273 groups: [['builtin', 'external'], 'parent', 'sibling', 'index'],
274 'newlines-between': 'always',
275 // clang-format has it's own logic overriding this
276 named: false,
277 alphabetize: {
278 order: 'asc',
279 caseInsensitive: true,
280 },
281 },
282 ],
Nikolay Vitkov55adf672025-01-02 12:07:33283 // Try to spot '// console.log()' left over from debugging
284 'rulesdir/no-commented-out-console': 'error',
285 // Prevent imports being commented out rather than deleted.
286 'rulesdir/no-commented-out-import': 'error',
Nikolay Vitkov55adf672025-01-02 12:07:33287 'rulesdir/check-license-header': 'error',
Nikolay Vitkov55adf672025-01-02 12:07:33288 /**
289 * Ensures that JS Doc comments are properly aligned - all the starting
290 * `*` are in the right place.
291 */
292 'jsdoc/check-alignment': 'error',
293 },
294 },
295 {
296 name: 'TypeScript files',
297 files: ['**/*.ts'],
298
299 languageOptions: {
300 ecmaVersion: 'latest',
301 sourceType: 'module',
302
Nikolay Vitkovf1e99c02025-05-09 12:21:42303 parser: typescriptEslint.parser,
Nikolay Vitkov55adf672025-01-02 12:07:33304 parserOptions: {
305 allowAutomaticSingleRunInference: true,
306 project: join(
Nikolay Vitkovf1e99c02025-05-09 12:21:42307 import.meta.dirname,
308 'config',
309 'typescript',
310 'tsconfig.eslint.json',
311 ),
Nikolay Vitkov55adf672025-01-02 12:07:33312 },
313 },
314
315 rules: {
Benedikt Meurer84b0fcc2025-02-12 13:57:23316 '@typescript-eslint/array-type': [
317 'error',
318 {
319 default: 'array-simple',
320 },
321 ],
Nikolay Vitkov55adf672025-01-02 12:07:33322 '@typescript-eslint/no-explicit-any': [
323 'error',
324 {
325 ignoreRestArgs: true,
326 },
327 ],
328
329 '@typescript-eslint/explicit-member-accessibility': [
330 'error',
331 {
332 accessibility: 'no-public',
333 },
334 ],
335
Nikolay Vitkov55adf672025-01-02 12:07:33336 // run just the TypeScript unused-vars rule, else we get duplicate errors
337 'no-unused-vars': 'off',
338 '@typescript-eslint/no-unused-vars': [
339 'error',
340 {
341 argsIgnorePattern: '^_',
342 },
343 ],
344
Nikolay Vitkovb4e8dc72025-01-07 13:03:02345 '@stylistic/member-delimiter-style': [
Nikolay Vitkov55adf672025-01-02 12:07:33346 'error',
347 {
348 multiline: {
349 delimiter: 'semi',
350 requireLast: true,
351 },
352
353 singleline: {
354 delimiter: 'comma',
355 requireLast: false,
356 },
357
358 overrides: {
359 interface: {
360 singleline: {
361 delimiter: 'semi',
362 requireLast: false,
363 },
364
365 multiline: {
366 delimiter: 'semi',
367 requireLast: true,
368 },
369 },
370
371 typeLiteral: {
372 singleline: {
373 delimiter: 'comma',
374 requireLast: false,
375 },
376
377 multiline: {
378 delimiter: 'comma',
379 requireLast: true,
380 },
381 },
382 },
383 },
384 ],
385
386 '@typescript-eslint/no-floating-promises': [
387 'error',
388 {
389 ignoreVoid: true,
390 },
391 ],
392
Nikolay Vitkov55adf672025-01-02 12:07:33393 /**
394 * Enforce that enum members are explicitly defined:
395 * const enum Foo { A = 'a' } rather than const enum Foo { A }
396 */
397 '@typescript-eslint/prefer-enum-initializers': 'error',
398 /**
399 * Ban non-null assertion operator, e.g.:
400 * this.foo!.toLowerCase()
401 */
402 '@typescript-eslint/no-non-null-assertion': 'error',
403 '@typescript-eslint/consistent-type-imports': 'error',
404
405 '@typescript-eslint/naming-convention': [
406 'error',
Nikolay Vitkov52a17972025-01-15 12:52:10407 // Forbids interfaces starting with an I prefix.
408 {
409 selector: 'interface',
410 format: ['PascalCase'],
411
412 custom: {
413 regex: '^I[A-Z]',
414 match: false,
415 },
416 },
Nikolay Vitkov55adf672025-01-02 12:07:33417 {
418 selector: [
419 'function',
420 'accessor',
421 'method',
422 'property',
423 'parameterProperty',
424 ],
425 format: ['camelCase'],
426 },
427 {
428 selector: 'variable',
429
430 filter: {
431 // Ignore localization variables.
432 regex: '^(UIStrings|str_)$',
433 match: false,
434 },
435
436 format: ['camelCase'],
437 },
438 {
439 // We are using camelCase, PascalCase and UPPER_CASE for top-level constants, allow the for now.
440 selector: 'variable',
441 modifiers: ['const'],
442 filter: {
443 // Ignore localization variables.
444 regex: '^(UIStrings|str_)$',
445 match: false,
446 },
447
448 format: ['camelCase', 'UPPER_CASE', 'PascalCase'],
449 },
450 {
451 selector: 'classProperty',
452 modifiers: ['static', 'readonly'],
453 format: ['UPPER_CASE', 'camelCase'],
454 },
455 {
456 selector: 'enumMember',
457 format: ['UPPER_CASE'],
458 },
459 {
460 selector: ['typeLike'],
461 format: ['PascalCase'],
462 },
463 {
464 selector: 'parameter',
465 format: ['camelCase'],
466 leadingUnderscore: 'allow',
467 },
468 {
469 // Public methods are currently in transition and may still have leading underscores.
470 selector: 'method',
471 modifiers: ['public'],
472 format: ['camelCase'],
473 leadingUnderscore: 'allow',
474 },
475 {
476 selector: 'property',
477 modifiers: ['public'],
478 format: ['camelCase'],
479 leadingUnderscore: 'allow',
480 },
481 {
482 // Object literals may be constructed as arguments to external libraries which follow different styles.
483 selector: ['objectLiteralMethod', 'objectLiteralProperty'],
484 modifiers: ['public'],
485 format: null,
486 },
487 {
488 // Ignore type properties that require quotes
489 selector: 'typeProperty',
490 format: null,
491 modifiers: ['requiresQuotes'],
492 },
493 ],
494
Nikolay Vitkov44e30062025-01-07 14:33:04495 '@typescript-eslint/consistent-type-definitions': ['error', 'interface'],
496
Nikolay Vitkov93062642025-02-18 09:49:30497 // Disable eslint base rule
498 'no-throw-literal': 'off',
499 '@typescript-eslint/only-throw-error': 'error',
500
Nikolay Vitkov65a5a912025-02-18 18:30:26501 // Disabled this rule while investigating why it creates
502 // certain TypeScript compilation errors after fixes
503 '@typescript-eslint/no-unnecessary-type-assertion': 'off',
504
Nikolay Vitkovd36860c2025-02-19 17:50:27505 '@typescript-eslint/no-inferrable-types': 'error',
506
Nikolay Vitkovd396b272025-02-19 08:37:28507 '@typescript-eslint/consistent-generic-constructors': [
508 'error',
509 'constructor',
510 ],
511
Nikolay Vitkovd36860c2025-02-19 17:50:27512 // This is more performant
513 // And should provide better stack trace when debugging
514 // see https://v8.dev/blog/fast-async.
515 '@typescript-eslint/return-await': ['error', 'always'],
516
517 '@typescript-eslint/ban-ts-comment': [
518 'error',
519 {
520 // Change after we add some placeholder for old errors
521 minimumDescriptionLength: 0,
522 'ts-check': false,
523 'ts-expect-error': 'allow-with-description',
524 'ts-ignore': true,
525 'ts-nocheck': true,
526 },
527 ],
528
Nikolay Vitkov68140172025-02-20 19:25:39529 '@typescript-eslint/prefer-optional-chain': 'error',
530
Nikolay Vitkovb30f3b22025-03-04 13:38:33531 '@typescript-eslint/no-unsafe-function-type': 'error',
532
Nikolay Vitkova2ced3f2025-03-05 11:58:03533 '@typescript-eslint/no-empty-object-type': [
534 'error',
535 {
536 allowInterfaces: 'with-single-extends',
537 },
538 ],
539
Nikolay Vitkovc32e3bb2025-03-10 10:56:34540 'no-array-constructor': 'off',
541 '@typescript-eslint/no-array-constructor': 'error',
542
Nikolay Vitkov55adf672025-01-02 12:07:33543 'rulesdir/no-underscored-properties': 'error',
Nikolay Vitkov55adf672025-01-02 12:07:33544 'rulesdir/inline-type-imports': 'error',
545
546 'rulesdir/enforce-default-import-name': [
547 'error',
548 {
549 // Enforce that any import of models/trace/trace.js names the import Trace.
550 modulePath: join(
Nikolay Vitkovf1e99c02025-05-09 12:21:42551 import.meta.dirname,
552 'front_end',
553 'models',
554 'trace',
555 'trace.js',
556 ),
Nikolay Vitkov55adf672025-01-02 12:07:33557 importName: 'Trace',
558 },
559 ],
560 },
561 },
Nikolay Vitkov55adf672025-01-02 12:07:33562 {
563 name: 'Scripts files',
564 files: ['scripts/**/*'],
565 rules: {
566 'no-console': 'off',
Nikolay Vitkovde07efa2025-01-17 12:58:35567 'rulesdir/es-modules-import': 'off',
Nikolay Vitkov77ba8df2025-03-27 15:06:08568 'import/no-default-export': 'off',
Nikolay Vitkov55adf672025-01-02 12:07:33569 },
570 },
Nikolay Vitkov55adf672025-01-02 12:07:33571 {
572 name: 'Front-end files',
573 files: ['front_end/**/*'],
574 rules: {
575 // L10n rules are only relevant in 'front_end'.
576 'rulesdir/l10n-filename-matches': [
577 'error',
578 {
579 rootFrontendDirectory: join(import.meta.dirname, 'front_end'),
580 },
581 ],
582 'rulesdir/l10n-i18nString-call-only-with-uistrings': 'error',
583 'rulesdir/l10n-no-i18nString-calls-module-instantiation': 'error',
584 'rulesdir/l10n-no-locked-or-placeholder-only-phrase': 'error',
585 'rulesdir/l10n-no-uistrings-export': 'error',
586 'rulesdir/l10n-no-unused-message': 'error',
587 },
588 },
Nikolay Vitkov55adf672025-01-02 12:07:33589 {
590 name: 'Front-end TypeScript files',
591 files: ['front_end/**/*.ts'],
592 rules: {
593 '@typescript-eslint/explicit-function-return-type': [
594 'error',
595 {
596 allowExpressions: true,
597 allowConciseArrowFunctionExpressionsStartingWithVoid: true,
598 allowIIFEs: true,
599 },
600 ],
Danil Somsikovd12440f2025-03-31 18:45:43601 'rulesdir/no-imperative-dom-api': 'error',
Danil Somsikov9f442bb2025-04-02 09:58:27602 'rulesdir/no-lit-render-outside-of-view': 'error',
Nikolay Vitkov55adf672025-01-02 12:07:33603 'rulesdir/no-importing-images-from-src': 'error',
Nikolay Vitkov55adf672025-01-02 12:07:33604 'rulesdir/enforce-custom-event-names': 'error',
605 'rulesdir/set-data-type-reference': 'error',
606 'rulesdir/no-bound-component-methods': 'error',
Benedikt Meurer07ad6092025-04-24 15:28:53607 'rulesdir/no-adopted-style-sheets': 'error',
Nikolay Vitkov55adf672025-01-02 12:07:33608 'rulesdir/no-customized-builtin-elements': 'error',
609 'rulesdir/no-self-closing-custom-element-tagnames': 'error',
Benedikt Meurer6e534082025-01-29 10:36:02610 'rulesdir/no-a-tags-in-lit': 'error',
Nikolay Vitkov55adf672025-01-02 12:07:33611 'rulesdir/check-css-import': 'error',
612 'rulesdir/enforce-optional-properties-last': 'error',
613 'rulesdir/check-enumerated-histograms': 'error',
614 'rulesdir/check-was-shown-methods': 'error',
615 'rulesdir/static-custom-event-names': 'error',
Benedikt Meurer6e534082025-01-29 10:36:02616 'rulesdir/lit-no-attribute-quotes': 'error',
Nikolay Vitkov55adf672025-01-02 12:07:33617 'rulesdir/lit-template-result-or-nothing': 'error',
618 'rulesdir/inject-checkbox-styles': 'error',
619 'rulesdir/jslog-context-list': 'error',
Nikolay Vitkovde07efa2025-01-17 12:58:35620 'rulesdir/es-modules-import': 'error',
621 'rulesdir/html-tagged-template': 'error',
Nikolay Vitkov49d12de2025-02-12 14:41:46622 'rulesdir/enforce-custom-element-definitions-location': [
623 'error',
624 {
625 rootFrontendDirectory: join(import.meta.dirname, 'front_end'),
626 },
627 ],
Ergun Erdogmus5efc7e92025-02-21 11:36:50628 'rulesdir/enforce-ui-strings-as-const': 'error',
Nikolay Vitkov55adf672025-01-02 12:07:33629 },
630 },
Nikolay Vitkov55adf672025-01-02 12:07:33631 {
632 name: 'Front-end meta files',
633 files: ['front_end/**/*-meta.ts'],
634 rules: {
635 '@typescript-eslint/naming-convention': [
636 'error',
637 {
638 selector: 'parameter',
639 format: ['camelCase', 'PascalCase'],
640 leadingUnderscore: 'allow',
641 },
642 ],
643 },
644 },
Nikolay Vitkov55adf672025-01-02 12:07:33645 {
646 name: 'TypeScript test files',
647 files: [
648 '*.test.ts',
649 // This makes the specificity greater than the front-end ts files
650 'front_end/**/*.test.ts',
651 'test/**/*.ts',
652 '**/testing/*.ts',
Nikolay Vitkov856215c2025-04-23 14:32:57653 'scripts/eslint_rules/test/**/*',
Nikolay Vitkov3cace8f2025-02-14 16:37:53654 'extensions/cxx_debugging/e2e/**',
Nikolay Vitkov55adf672025-01-02 12:07:33655 ],
656
657 rules: {
658 // errors on it('test') with no body
659 'mocha/no-pending-tests': 'error',
660
661 // errors on {describe, it}.only
662 'mocha/no-exclusive-tests': 'error',
663
664 'mocha/no-async-describe': 'error',
665 'mocha/no-global-tests': 'error',
666 'mocha/no-nested-tests': 'error',
667
668 '@typescript-eslint/no-non-null-assertion': 'off',
669 '@typescript-eslint/explicit-function-return-type': 'off',
670
Nikolay Vitkov93062642025-02-18 09:49:30671 '@typescript-eslint/only-throw-error': [
672 'error',
673 {
674 allow: [
675 {
676 // Chai AssertionError does not extend Error
677 from: 'package',
678 package: 'chai',
679 name: ['AssertionError'],
680 },
681 ],
682 },
683 ],
684
Nikolay Vitkov55adf672025-01-02 12:07:33685 'rulesdir/check-test-definitions': 'error',
686 'rulesdir/no-assert-strict-equal-for-arrays-and-objects': 'error',
687 'rulesdir/no-assert-deep-strict-equal': 'error',
688 'rulesdir/no-assert-equal': 'error',
689 'rulesdir/no-assert-equal-boolean-null-undefined': 'error',
Danil Somsikovd12440f2025-03-31 18:45:43690 'rulesdir/no-imperative-dom-api': 'off',
Danil Somsikov9f442bb2025-04-02 09:58:27691 'rulesdir/no-lit-render-outside-of-view': 'off',
Nikolay Vitkov55adf672025-01-02 12:07:33692 'rulesdir/no-screenshot-test-outside-perf-panel': 'error',
693 'rulesdir/prefer-assert-instance-of': 'error',
694 'rulesdir/prefer-assert-is-ok': 'error',
695 'rulesdir/prefer-assert-length-of': 'error',
Benedikt Meurerebef25a2025-04-11 12:28:21696 'rulesdir/prefer-assert-strict-equal': 'error',
Benedikt Meureradbf0df2025-04-11 08:48:52697 'rulesdir/prefer-sinon-assert': 'error',
Benedikt Meurer39e51332025-01-07 13:17:54698 'rulesdir/prefer-url-string': 'error',
Nikolay Vitkov55adf672025-01-02 12:07:33699 'rulesdir/trace-engine-test-timeouts': 'error',
Nikolay Vitkov49d12de2025-02-12 14:41:46700 'rulesdir/enforce-custom-element-definitions-location': 'off',
Nikolay Vitkov55adf672025-01-02 12:07:33701 },
702
703 settings: {
704 'mocha/additionalCustomNames': [
705 {
706 name: 'describeWithDevtoolsExtension',
707 type: 'suite',
708 interfaces: ['BDD', 'TDD'],
709 },
710 {
711 name: 'describeWithEnvironment',
712 type: 'suite',
713 interfaces: ['BDD', 'TDD'],
714 },
715 {
716 name: 'describeWithLocale',
717 type: 'suite',
718 interfaces: ['BDD', 'TDD'],
719 },
720 {
721 name: 'describeWithMockConnection',
722 type: 'suite',
723 interfaces: ['BDD', 'TDD'],
724 },
725 {
726 name: 'describeWithRealConnection',
727 type: 'suite',
728 interfaces: ['BDD', 'TDD'],
729 },
730 {
731 name: 'itScreenshot',
732 type: 'testCase',
733 interfaces: ['BDD', 'TDD'],
734 },
735 ],
736 },
737 },
Nikolay Vitkov55adf672025-01-02 12:07:33738 {
Nikolay Vitkov52a17972025-01-15 12:52:10739 name: 'Use private class members rule',
Nikolay Vitkov55adf672025-01-02 12:07:33740 files: [
741 'front_end/panels/**/components/*.ts',
742 'front_end/ui/components/**/*.ts',
743 'front_end/entrypoints/**/*.ts',
744 ],
745
746 rules: {
747 'rulesdir/prefer-private-class-members': 'error',
748 },
749 },
Nikolay Vitkov55adf672025-01-02 12:07:33750 {
Nikolay Vitkov52a17972025-01-15 12:52:10751 name: 'Ignore private class members rule',
Nikolay Vitkov55adf672025-01-02 12:07:33752 files: [
753 'front_end/panels/recorder/**/*.ts',
Nikolay Vitkov55adf672025-01-02 12:07:33754 'front_end/ui/components/suggestion_input/*.ts',
755 ],
756 rules: {
757 // TODO(crbug/1402569): Reenable once https://github.com/microsoft/TypeScript/issues/48885 is closed.
758 'rulesdir/prefer-private-class-members': 'off',
759 },
760 },
Nikolay Vitkov55adf672025-01-02 12:07:33761 {
Nikolay Vitkov52a17972025-01-15 12:52:10762 name: 'Supported CSS properties rules',
Nikolay Vitkov55adf672025-01-02 12:07:33763 files: ['front_end/generated/SupportedCSSProperties.js'],
764 rules: {
765 'rulesdir/jslog-context-list': 'error',
766 },
767 },
Nikolay Vitkov55adf672025-01-02 12:07:33768 {
769 name: 'EsLint rules test',
Nikolay Vitkov856215c2025-04-23 14:32:57770 files: ['scripts/eslint_rules/tests/**/*'],
Nikolay Vitkov55adf672025-01-02 12:07:33771 rules: {
Nikolay Vitkovf1e3bd82025-02-13 11:55:22772 '@eslint-plugin/no-only-tests': 'error',
Nikolay Vitkov55adf672025-01-02 12:07:33773 },
774 },
Nikolay Vitkov55adf672025-01-02 12:07:33775 {
776 name: 'Legacy test runner',
777 files: ['front_end/legacy_test_runner/**/*'],
778 rules: {
779 'rulesdir/es-modules-import': 'off',
780 },
781 },
782 {
783 name: 'Front end component docs',
784 files: ['front_end/ui/components/docs/**/*.ts'],
785 rules: {
786 // This makes the component doc examples very verbose and doesn't add
787 // anything, so we leave return types to the developer within the
788 // component_docs folder.
789 '@typescript-eslint/explicit-function-return-type': 'off',
Benedikt Meurer6e534082025-01-29 10:36:02790 // We use Lit to help render examples sometimes and we don't use
Nikolay Vitkov55adf672025-01-02 12:07:33791 // {host: this} as often the `this` is the window.
Benedikt Meurer6e534082025-01-29 10:36:02792 'rulesdir/lit-host-this': 'off',
Danil Somsikovd12440f2025-03-31 18:45:43793 'rulesdir/no-imperative-dom-api': 'off',
Danil Somsikov9f442bb2025-04-02 09:58:27794 'rulesdir/no-lit-render-outside-of-view': 'off',
Nikolay Vitkov55adf672025-01-02 12:07:33795 },
796 },
797 {
Nikolay Vitkov52a17972025-01-15 12:52:10798 name: 'Traces import rule',
Nikolay Vitkov55adf672025-01-02 12:07:33799 files: ['front_end/models/trace/handlers/**/*.ts'],
800 rules: {
801 'rulesdir/no-imports-in-directory': [
802 'error',
803 {
804 bannedImportPaths: [
805 join(import.meta.dirname, 'front_end', 'core', 'sdk', 'sdk.js'),
806 ],
807 },
808 ],
809 },
810 },
811 {
Nikolay Vitkov52a17972025-01-15 12:52:10812 name: 'Recorder injected code',
Nikolay Vitkov55adf672025-01-02 12:07:33813 files: ['front_end/panels/recorder/injected/**/*.ts'],
814 rules: {
815 // The code is rolled up and tree-shaken independently from the regular entrypoints.
816 'rulesdir/es-modules-import': 'off',
817 },
818 },
819 {
Nikolay Vitkovb4e8dc72025-01-07 13:03:02820 name: 'Performance panel file',
Nikolay Vitkov55adf672025-01-02 12:07:33821 files: ['front_end/ui/legacy/components/perf_ui/**/*.ts'],
822 rules: {
823 // Enable tracking of canvas save() and
824 // restore() calls to try and catch bugs. Only
825 // enabled in this folder because it is an
826 // expensive rule to run and we do not need it
827 // for any code that doesn't use Canvas.
828 'rulesdir/canvas-context-tracking': 'error',
829 },
830 },
Jack Franklinc10b4972025-02-28 16:32:56831 {
832 name: 'TypeScript type-definitions',
833 files: ['**/*.d.ts'],
834 rules: {
835 // Not a useful rule for .d.ts files where we are
836 // representing an existing module.
Nikolay Vitkova2ced3f2025-03-05 11:58:03837 'import/no-default-export': 'off',
838 },
839 },
Nikolay Vitkovafc8a8c2025-03-17 10:05:28840 {
841 name: 'Config files',
842 files: ['eslint.config.mjs', '**/*/rollup.config.mjs'],
843 rules: {
844 // The config operate on the default export
845 // So allow it for them
846 'import/no-default-export': 'off',
847 },
848 },
Nikolay Vitkov856215c2025-04-23 14:32:57849]);