blob: e1db1cc2f84e7e52e1800f5a2d1e9fbb9cf22656 [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 Vitkovf1e3bd82025-02-13 11:55:228import eslintPlugin from 'eslint-plugin-eslint-plugin';
Nikolay Vitkov55adf672025-01-02 12:07:339import importPlugin from 'eslint-plugin-import';
10import jsdocPlugin from 'eslint-plugin-jsdoc';
Nikolay Vitkov41c69112025-01-31 15:20:0411import mochaPlugin from 'eslint-plugin-mocha';
12import rulesdirPlugin from 'eslint-plugin-rulesdir';
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
16rulesdirPlugin.RULES_DIR = join(
Nikolay Vitkova2ced3f2025-03-05 11:58:0317 import.meta.dirname,
18 'scripts',
19 'eslint_rules',
20 'lib',
Nikolay Vitkov55adf672025-01-02 12:07:3321);
22
23/**
24 * @type {import('eslint').Linter.Config[]}
25 */
26export default [
27 {
Nikolay Vitkov52a17972025-01-15 12:52:1028 name: 'Ignore list',
Nikolay Vitkov55adf672025-01-02 12:07:3329 ignores: [
Nikolay Vitkov5b2bcff2025-01-29 19:21:1230 // Git submodules that are not in third_party
31 'build/',
32 'buildtools/',
33
Nikolay Vitkov4dc470b2025-02-20 12:44:3234 // Don't include the common build directory
35 'out/',
36 // Don't include third party code
37 'third_party/',
38
Nikolay Vitkov55adf672025-01-02 12:07:3339 'front_end/diff/diff_match_patch.jD',
40 'front_end/models/javascript_metadata/NativeFunctions.js',
41 // All of these scripts are auto-generated so don't lint them.
42 'front_end/generated/ARIAProperties.js',
43 'front_end/generated/Deprecation.ts',
44 'front_end/generated/InspectorBackendCommands.js',
45 'front_end/generated/protocol-mapping.d.ts',
46 'front_end/generated/protocol-proxy-api.d.ts',
47 'front_end/generated/protocol.ts',
48 // Any third_party addition has its source code checked out into
49 // third_party/X/package, so we ignore that code as it's not code we author or
50 // own.
51 'front_end/third_party/*/package/',
52 // Any JS files are also not authored by devtools-frontend, so we ignore those.
Nikolay Vitkov52a17972025-01-15 12:52:1053 'front_end/third_party/**/*',
Nikolay Vitkov55adf672025-01-02 12:07:3354 // Lighthouse doesn't have a package/ folder but has other nested folders, so
55 // we ignore any folders within the lighthouse directory.
56 'front_end/third_party/lighthouse/*/',
57 // The CodeMirror bundle file is auto-generated and rolled-up as part of the',
58 // install script, so we don't need to lint it.
59 'front_end/third_party/codemirror.next/bundle.ts',
60 // Lit lib files are auto-generated and rolled up as part of the install script.
61 'front_end/third_party/lit/src/*.ts',
62 // @puppeteer/replay is auto-generated.
63 'front_end/third_party/puppeteer-replay/**/*.ts',
Nikolay Vitkov52a17972025-01-15 12:52:1064 // Third party code we did not author for extensions
65 'extensions/cxx_debugging/third_party/**/*',
Nikolay Vitkov55adf672025-01-02 12:07:3366
67 '**/node_modules',
68 'scripts/build/typescript/tests',
69 'scripts/migration/**/*.js',
70 'scripts/protocol_typescript/*.js',
71 'scripts/deps/tests/fixtures',
72 'test/**/fixtures/',
73 'test/e2e/**/*.js',
74 'test/shared/**/*.js',
Nikolay Vitkov55adf672025-01-02 12:07:3375 ],
76 },
77 {
Nikolay Vitkovb4e8dc72025-01-07 13:03:0278 name: 'JavaScript files',
Nikolay Vitkov55adf672025-01-02 12:07:3379 plugins: {
80 '@typescript-eslint': typescriptPlugin,
Nikolay Vitkovb4e8dc72025-01-07 13:03:0281 '@stylistic': stylisticPlugin,
Nikolay Vitkovf1e3bd82025-02-13 11:55:2282 '@eslint-plugin': eslintPlugin,
Nikolay Vitkov55adf672025-01-02 12:07:3383 mocha: mochaPlugin,
84 rulesdir: rulesdirPlugin,
85 import: importPlugin,
86 jsdoc: jsdocPlugin,
Nikolay Vitkov55adf672025-01-02 12:07:3387 },
88
89 languageOptions: {
Nikolay Vitkovb4e8dc72025-01-07 13:03:0290 ecmaVersion: 'latest',
91 sourceType: 'module',
Nikolay Vitkov55adf672025-01-02 12:07:3392 globals: {
93 ...globals.browser,
94 },
Nikolay Vitkov55adf672025-01-02 12:07:3395 },
96
Nikolay Vitkov5b2bcff2025-01-29 19:21:1297 linterOptions: {
98 reportUnusedDisableDirectives: 'error',
99 },
100
Nikolay Vitkov55adf672025-01-02 12:07:33101 rules: {
102 // syntax preferences
Nikolay Vitkovb4e8dc72025-01-07 13:03:02103 '@stylistic/quotes': [
Nikolay Vitkov55adf672025-01-02 12:07:33104 'error',
105 'single',
106 {
107 avoidEscape: true,
108 allowTemplateLiterals: false,
109 },
110 ],
111
Nikolay Vitkovb4e8dc72025-01-07 13:03:02112 '@stylistic/semi': 'error',
113 '@stylistic/no-extra-semi': 'error',
114 '@stylistic/comma-style': ['error', 'last'],
115 '@stylistic/wrap-iife': ['error', 'inside'],
Nikolay Vitkov55adf672025-01-02 12:07:33116
Nikolay Vitkovb4e8dc72025-01-07 13:03:02117 '@stylistic/spaced-comment': [
Nikolay Vitkov55adf672025-01-02 12:07:33118 'error',
119 'always',
120 {
121 markers: ['*'],
122 },
123 ],
124
125 eqeqeq: 'error',
126
127 'accessor-pairs': [
128 'error',
129 {
130 getWithoutSet: false,
131 setWithoutGet: false,
132 },
133 ],
134
135 curly: 'error',
Nikolay Vitkovb4e8dc72025-01-07 13:03:02136 '@stylistic/new-parens': 'error',
137 '@stylistic/func-call-spacing': 'error',
138 '@stylistic/arrow-parens': ['error', 'as-needed'],
139 '@stylistic/eol-last': 'error',
Nikolay Vitkov55adf672025-01-02 12:07:33140 'object-shorthand': ['error', 'properties'],
141 'no-useless-rename': 'error',
142
143 // anti-patterns
144 'no-caller': 'error',
145 'no-case-declarations': 'error',
146 'no-cond-assign': 'error',
147
148 'no-console': [
149 'error',
150 {
151 allow: [
152 'assert',
153 'context',
154 'error',
155 'timeStamp',
156 'time',
157 'timeEnd',
158 'warn',
159 ],
160 },
161 ],
162
163 'no-debugger': 'error',
164 'no-dupe-keys': 'error',
165 'no-duplicate-case': 'error',
166
167 'no-else-return': [
168 'error',
169 {
170 allowElseIf: false,
171 },
172 ],
173
Nikolay Vitkovd1ebd9e2025-02-26 10:19:45174 'no-empty': [
175 'error',
176 {
177 allowEmptyCatch: true,
178 },
179 ],
180 'no-lonely-if': 'error',
181
Nikolay Vitkov55adf672025-01-02 12:07:33182 'no-empty-character-class': 'error',
183 'no-global-assign': 'error',
184 'no-implied-eval': 'error',
185 'no-labels': 'error',
186 'no-multi-str': 'error',
Nikolay Vitkovb4e8dc72025-01-07 13:03:02187 'no-object-constructor': 'error',
Nikolay Vitkov55adf672025-01-02 12:07:33188 'no-octal-escape': 'error',
189 'no-self-compare': 'error',
190 'no-shadow-restricted-names': 'error',
191 'no-unreachable': 'error',
192 'no-unsafe-negation': 'error',
193
194 'no-unused-vars': [
195 'error',
196 {
197 args: 'none',
198 vars: 'local',
199 },
200 ],
201
202 'no-var': 'error',
203 'no-with': 'error',
204 'prefer-const': 'error',
205 radix: 'error',
206 'valid-typeof': 'error',
207 'no-return-assign': ['error', 'always'],
Nikolay Vitkovbcd8cd92025-03-27 16:31:45208 'no-implicit-coercion': ['error', { allow: ['!!'] }],
Nikolay Vitkov55adf672025-01-02 12:07:33209
Nikolay Vitkovc32e3bb2025-03-10 10:56:34210 'no-array-constructor': 'error',
211
Nikolay Vitkov55adf672025-01-02 12:07:33212 // es2015 features
213 'require-yield': 'error',
Nikolay Vitkovb4e8dc72025-01-07 13:03:02214 '@stylistic/template-curly-spacing': ['error', 'never'],
Nikolay Vitkov55adf672025-01-02 12:07:33215
216 // file whitespace
Nikolay Vitkovb4e8dc72025-01-07 13:03:02217 '@stylistic/no-multiple-empty-lines': [
Nikolay Vitkov55adf672025-01-02 12:07:33218 'error',
219 {
220 max: 1,
221 },
222 ],
Nikolay Vitkovb4e8dc72025-01-07 13:03:02223 '@stylistic/no-mixed-spaces-and-tabs': 'error',
224 '@stylistic/no-trailing-spaces': 'error',
225 '@stylistic/linebreak-style': ['error', 'unix'],
Nikolay Vitkov55adf672025-01-02 12:07:33226
227 /**
228 * Disabled, aspirational rules
229 */
Nikolay Vitkovb4e8dc72025-01-07 13:03:02230 '@stylistic/indent': [
Nikolay Vitkov55adf672025-01-02 12:07:33231 'off',
232 2,
233 {
234 SwitchCase: 1,
235 CallExpression: {
236 arguments: 2,
237 },
238 MemberExpression: 2,
239 },
240 ],
241
242 // brace-style is disabled, as eslint cannot enforce 1tbs as default, but allman for functions
Nikolay Vitkovb4e8dc72025-01-07 13:03:02243 '@stylistic/brace-style': [
Nikolay Vitkov55adf672025-01-02 12:07:33244 'off',
245 'allman',
246 {
247 allowSingleLine: true,
248 },
249 ],
250
251 // key-spacing is disabled, as some objects use value-aligned spacing, some not.
Nikolay Vitkovb4e8dc72025-01-07 13:03:02252 '@stylistic/key-spacing': [
Nikolay Vitkov55adf672025-01-02 12:07:33253 'off',
254 {
255 beforeColon: false,
256 afterColon: true,
257 align: 'value',
258 },
259 ],
260
Nikolay Vitkovb4e8dc72025-01-07 13:03:02261 '@stylistic/quote-props': ['error', 'as-needed'],
Nikolay Vitkov55adf672025-01-02 12:07:33262
263 // no-implicit-globals will prevent accidental globals
264 'no-implicit-globals': 'off',
265 'no-unused-private-class-members': 'error',
266
Benedikt Meurer00c39582025-01-31 09:26:27267 // Sort imports first
268 'import/first': 'error',
Nikolay Vitkov55adf672025-01-02 12:07:33269 // Closure does not properly typecheck default exports
270 'import/no-default-export': 'error',
271 /**
272 * Catch duplicate import paths. For example this would catch the following example:
273 * import {Foo} from './foo.js'
274 * import * as FooModule from './foo.js'
275 **/
276 'import/no-duplicates': 'error',
Nikolay Vitkov41c69112025-01-31 15:20:04277 /**
278 * Provides more consistency in the imports.
279 */
280 'import/order': [
281 'error',
282 {
283 // We need to group the builtin and external as clang-format
284 // can't differentiate the two
285 groups: [['builtin', 'external'], 'parent', 'sibling', 'index'],
286 'newlines-between': 'always',
287 // clang-format has it's own logic overriding this
288 named: false,
289 alphabetize: {
290 order: 'asc',
291 caseInsensitive: true,
292 },
293 },
294 ],
Nikolay Vitkov55adf672025-01-02 12:07:33295 // Try to spot '// console.log()' left over from debugging
296 'rulesdir/no-commented-out-console': 'error',
297 // Prevent imports being commented out rather than deleted.
298 'rulesdir/no-commented-out-import': 'error',
Nikolay Vitkov55adf672025-01-02 12:07:33299 'rulesdir/check-license-header': 'error',
Nikolay Vitkov55adf672025-01-02 12:07:33300 /**
301 * Ensures that JS Doc comments are properly aligned - all the starting
302 * `*` are in the right place.
303 */
304 'jsdoc/check-alignment': 'error',
305 },
306 },
307 {
308 name: 'TypeScript files',
309 files: ['**/*.ts'],
310
311 languageOptions: {
312 ecmaVersion: 'latest',
313 sourceType: 'module',
314
Nikolay Vitkovb4e8dc72025-01-07 13:03:02315 parser: tsParser,
Nikolay Vitkov55adf672025-01-02 12:07:33316 parserOptions: {
317 allowAutomaticSingleRunInference: true,
Connor Clarke7a64892025-03-27 00:21:28318 projectService: true,
Nikolay Vitkov55adf672025-01-02 12:07:33319 project: join(
Nikolay Vitkova2ced3f2025-03-05 11:58:03320 import.meta.dirname,
321 'config',
322 'typescript',
323 'tsconfig.eslint.json',
324 ),
Nikolay Vitkov55adf672025-01-02 12:07:33325 },
326 },
327
328 rules: {
Benedikt Meurer84b0fcc2025-02-12 13:57:23329 '@typescript-eslint/array-type': [
330 'error',
331 {
332 default: 'array-simple',
333 },
334 ],
Nikolay Vitkov55adf672025-01-02 12:07:33335 '@typescript-eslint/no-explicit-any': [
336 'error',
337 {
338 ignoreRestArgs: true,
339 },
340 ],
341
342 '@typescript-eslint/explicit-member-accessibility': [
343 'error',
344 {
345 accessibility: 'no-public',
346 },
347 ],
348
Nikolay Vitkov55adf672025-01-02 12:07:33349 // run just the TypeScript unused-vars rule, else we get duplicate errors
350 'no-unused-vars': 'off',
351 '@typescript-eslint/no-unused-vars': [
352 'error',
353 {
354 argsIgnorePattern: '^_',
355 },
356 ],
357
Nikolay Vitkovb4e8dc72025-01-07 13:03:02358 '@stylistic/member-delimiter-style': [
Nikolay Vitkov55adf672025-01-02 12:07:33359 'error',
360 {
361 multiline: {
362 delimiter: 'semi',
363 requireLast: true,
364 },
365
366 singleline: {
367 delimiter: 'comma',
368 requireLast: false,
369 },
370
371 overrides: {
372 interface: {
373 singleline: {
374 delimiter: 'semi',
375 requireLast: false,
376 },
377
378 multiline: {
379 delimiter: 'semi',
380 requireLast: true,
381 },
382 },
383
384 typeLiteral: {
385 singleline: {
386 delimiter: 'comma',
387 requireLast: false,
388 },
389
390 multiline: {
391 delimiter: 'comma',
392 requireLast: true,
393 },
394 },
395 },
396 },
397 ],
398
399 '@typescript-eslint/no-floating-promises': [
400 'error',
401 {
402 ignoreVoid: true,
403 },
404 ],
405
Nikolay Vitkov55adf672025-01-02 12:07:33406 /**
407 * Enforce that enum members are explicitly defined:
408 * const enum Foo { A = 'a' } rather than const enum Foo { A }
409 */
410 '@typescript-eslint/prefer-enum-initializers': 'error',
411 /**
412 * Ban non-null assertion operator, e.g.:
413 * this.foo!.toLowerCase()
414 */
415 '@typescript-eslint/no-non-null-assertion': 'error',
416 '@typescript-eslint/consistent-type-imports': 'error',
417
418 '@typescript-eslint/naming-convention': [
419 'error',
Nikolay Vitkov52a17972025-01-15 12:52:10420 // Forbids interfaces starting with an I prefix.
421 {
422 selector: 'interface',
423 format: ['PascalCase'],
424
425 custom: {
426 regex: '^I[A-Z]',
427 match: false,
428 },
429 },
Nikolay Vitkov55adf672025-01-02 12:07:33430 {
431 selector: [
432 'function',
433 'accessor',
434 'method',
435 'property',
436 'parameterProperty',
437 ],
438 format: ['camelCase'],
439 },
440 {
441 selector: 'variable',
442
443 filter: {
444 // Ignore localization variables.
445 regex: '^(UIStrings|str_)$',
446 match: false,
447 },
448
449 format: ['camelCase'],
450 },
451 {
452 // We are using camelCase, PascalCase and UPPER_CASE for top-level constants, allow the for now.
453 selector: 'variable',
454 modifiers: ['const'],
455 filter: {
456 // Ignore localization variables.
457 regex: '^(UIStrings|str_)$',
458 match: false,
459 },
460
461 format: ['camelCase', 'UPPER_CASE', 'PascalCase'],
462 },
463 {
464 selector: 'classProperty',
465 modifiers: ['static', 'readonly'],
466 format: ['UPPER_CASE', 'camelCase'],
467 },
468 {
469 selector: 'enumMember',
470 format: ['UPPER_CASE'],
471 },
472 {
473 selector: ['typeLike'],
474 format: ['PascalCase'],
475 },
476 {
477 selector: 'parameter',
478 format: ['camelCase'],
479 leadingUnderscore: 'allow',
480 },
481 {
482 // Public methods are currently in transition and may still have leading underscores.
483 selector: 'method',
484 modifiers: ['public'],
485 format: ['camelCase'],
486 leadingUnderscore: 'allow',
487 },
488 {
489 selector: 'property',
490 modifiers: ['public'],
491 format: ['camelCase'],
492 leadingUnderscore: 'allow',
493 },
494 {
495 // Object literals may be constructed as arguments to external libraries which follow different styles.
496 selector: ['objectLiteralMethod', 'objectLiteralProperty'],
497 modifiers: ['public'],
498 format: null,
499 },
500 {
501 // Ignore type properties that require quotes
502 selector: 'typeProperty',
503 format: null,
504 modifiers: ['requiresQuotes'],
505 },
506 ],
507
Nikolay Vitkov44e30062025-01-07 14:33:04508 '@typescript-eslint/consistent-type-definitions': ['error', 'interface'],
509
Nikolay Vitkov93062642025-02-18 09:49:30510 // Disable eslint base rule
511 'no-throw-literal': 'off',
512 '@typescript-eslint/only-throw-error': 'error',
513
Nikolay Vitkov65a5a912025-02-18 18:30:26514 // Disabled this rule while investigating why it creates
515 // certain TypeScript compilation errors after fixes
516 '@typescript-eslint/no-unnecessary-type-assertion': 'off',
517
Nikolay Vitkovd36860c2025-02-19 17:50:27518 '@typescript-eslint/no-inferrable-types': 'error',
519
Nikolay Vitkovd396b272025-02-19 08:37:28520 '@typescript-eslint/consistent-generic-constructors': [
521 'error',
522 'constructor',
523 ],
524
Nikolay Vitkovd36860c2025-02-19 17:50:27525 // This is more performant
526 // And should provide better stack trace when debugging
527 // see https://v8.dev/blog/fast-async.
528 '@typescript-eslint/return-await': ['error', 'always'],
529
530 '@typescript-eslint/ban-ts-comment': [
531 'error',
532 {
533 // Change after we add some placeholder for old errors
534 minimumDescriptionLength: 0,
535 'ts-check': false,
536 'ts-expect-error': 'allow-with-description',
537 'ts-ignore': true,
538 'ts-nocheck': true,
539 },
540 ],
541
Nikolay Vitkov68140172025-02-20 19:25:39542 '@typescript-eslint/prefer-optional-chain': 'error',
543
Nikolay Vitkovb30f3b22025-03-04 13:38:33544 '@typescript-eslint/no-unsafe-function-type': 'error',
545
Nikolay Vitkova2ced3f2025-03-05 11:58:03546 '@typescript-eslint/no-empty-object-type': [
547 'error',
548 {
549 allowInterfaces: 'with-single-extends',
550 },
551 ],
552
Nikolay Vitkovc32e3bb2025-03-10 10:56:34553 'no-array-constructor': 'off',
554 '@typescript-eslint/no-array-constructor': 'error',
555
Nikolay Vitkov55adf672025-01-02 12:07:33556 'rulesdir/no-underscored-properties': 'error',
Nikolay Vitkov55adf672025-01-02 12:07:33557 'rulesdir/inline-type-imports': 'error',
558
559 'rulesdir/enforce-default-import-name': [
560 'error',
561 {
562 // Enforce that any import of models/trace/trace.js names the import Trace.
563 modulePath: join(
Nikolay Vitkova2ced3f2025-03-05 11:58:03564 import.meta.dirname,
565 'front_end',
566 'models',
567 'trace',
568 'trace.js',
569 ),
Nikolay Vitkov55adf672025-01-02 12:07:33570 importName: 'Trace',
571 },
572 ],
573 },
574 },
Nikolay Vitkov55adf672025-01-02 12:07:33575 {
576 name: 'Scripts files',
577 files: ['scripts/**/*'],
578 rules: {
579 'no-console': 'off',
Nikolay Vitkovde07efa2025-01-17 12:58:35580 'rulesdir/es-modules-import': 'off',
Nikolay Vitkov77ba8df2025-03-27 15:06:08581 'import/no-default-export': 'off',
Nikolay Vitkov55adf672025-01-02 12:07:33582 },
583 },
Nikolay Vitkov55adf672025-01-02 12:07:33584 {
585 name: 'Front-end files',
586 files: ['front_end/**/*'],
587 rules: {
588 // L10n rules are only relevant in 'front_end'.
589 'rulesdir/l10n-filename-matches': [
590 'error',
591 {
592 rootFrontendDirectory: join(import.meta.dirname, 'front_end'),
593 },
594 ],
595 'rulesdir/l10n-i18nString-call-only-with-uistrings': 'error',
596 'rulesdir/l10n-no-i18nString-calls-module-instantiation': 'error',
597 'rulesdir/l10n-no-locked-or-placeholder-only-phrase': 'error',
598 'rulesdir/l10n-no-uistrings-export': 'error',
599 'rulesdir/l10n-no-unused-message': 'error',
600 },
601 },
Nikolay Vitkov55adf672025-01-02 12:07:33602 {
603 name: 'Front-end TypeScript files',
604 files: ['front_end/**/*.ts'],
605 rules: {
606 '@typescript-eslint/explicit-function-return-type': [
607 'error',
608 {
609 allowExpressions: true,
610 allowConciseArrowFunctionExpressionsStartingWithVoid: true,
611 allowIIFEs: true,
612 },
613 ],
Danil Somsikovd12440f2025-03-31 18:45:43614 'rulesdir/no-imperative-dom-api': 'error',
Nikolay Vitkov55adf672025-01-02 12:07:33615 'rulesdir/no-importing-images-from-src': 'error',
616 'rulesdir/enforce-bound-render-for-schedule-render': 'error',
617 'rulesdir/enforce-custom-event-names': 'error',
618 'rulesdir/set-data-type-reference': 'error',
619 'rulesdir/no-bound-component-methods': 'error',
620 'rulesdir/no-customized-builtin-elements': 'error',
621 'rulesdir/no-self-closing-custom-element-tagnames': 'error',
Benedikt Meurer6e534082025-01-29 10:36:02622 'rulesdir/no-a-tags-in-lit': 'error',
Nikolay Vitkov55adf672025-01-02 12:07:33623 'rulesdir/check-css-import': 'error',
624 'rulesdir/enforce-optional-properties-last': 'error',
625 'rulesdir/check-enumerated-histograms': 'error',
626 'rulesdir/check-was-shown-methods': 'error',
627 'rulesdir/static-custom-event-names': 'error',
Benedikt Meurer6e534082025-01-29 10:36:02628 'rulesdir/lit-no-attribute-quotes': 'error',
Nikolay Vitkov55adf672025-01-02 12:07:33629 'rulesdir/lit-template-result-or-nothing': 'error',
630 'rulesdir/inject-checkbox-styles': 'error',
631 'rulesdir/jslog-context-list': 'error',
Nikolay Vitkovde07efa2025-01-17 12:58:35632 'rulesdir/es-modules-import': 'error',
633 'rulesdir/html-tagged-template': 'error',
Nikolay Vitkov49d12de2025-02-12 14:41:46634 'rulesdir/enforce-custom-element-definitions-location': [
635 'error',
636 {
637 rootFrontendDirectory: join(import.meta.dirname, 'front_end'),
638 },
639 ],
Ergun Erdogmus5efc7e92025-02-21 11:36:50640 'rulesdir/enforce-ui-strings-as-const': 'error',
Nikolay Vitkov55adf672025-01-02 12:07:33641 },
642 },
Nikolay Vitkov55adf672025-01-02 12:07:33643 {
644 name: 'Front-end meta files',
645 files: ['front_end/**/*-meta.ts'],
646 rules: {
647 '@typescript-eslint/naming-convention': [
648 'error',
649 {
650 selector: 'parameter',
651 format: ['camelCase', 'PascalCase'],
652 leadingUnderscore: 'allow',
653 },
654 ],
655 },
656 },
Nikolay Vitkov55adf672025-01-02 12:07:33657 {
658 name: 'TypeScript test files',
659 files: [
660 '*.test.ts',
661 // This makes the specificity greater than the front-end ts files
662 'front_end/**/*.test.ts',
663 'test/**/*.ts',
664 '**/testing/*.ts',
665 'scripts/eslint_rules/test/**/*.js',
Nikolay Vitkov3cace8f2025-02-14 16:37:53666 'extensions/cxx_debugging/e2e/**',
Nikolay Vitkov55adf672025-01-02 12:07:33667 ],
668
669 rules: {
670 // errors on it('test') with no body
671 'mocha/no-pending-tests': 'error',
672
673 // errors on {describe, it}.only
674 'mocha/no-exclusive-tests': 'error',
675
676 'mocha/no-async-describe': 'error',
677 'mocha/no-global-tests': 'error',
678 'mocha/no-nested-tests': 'error',
679
680 '@typescript-eslint/no-non-null-assertion': 'off',
681 '@typescript-eslint/explicit-function-return-type': 'off',
682
Nikolay Vitkov93062642025-02-18 09:49:30683 '@typescript-eslint/only-throw-error': [
684 'error',
685 {
686 allow: [
687 {
688 // Chai AssertionError does not extend Error
689 from: 'package',
690 package: 'chai',
691 name: ['AssertionError'],
692 },
693 ],
694 },
695 ],
696
Nikolay Vitkov55adf672025-01-02 12:07:33697 'rulesdir/check-test-definitions': 'error',
698 'rulesdir/no-assert-strict-equal-for-arrays-and-objects': 'error',
699 'rulesdir/no-assert-deep-strict-equal': 'error',
700 'rulesdir/no-assert-equal': 'error',
701 'rulesdir/no-assert-equal-boolean-null-undefined': 'error',
Danil Somsikovd12440f2025-03-31 18:45:43702 'rulesdir/no-imperative-dom-api': 'off',
Nikolay Vitkov55adf672025-01-02 12:07:33703 'rulesdir/no-screenshot-test-outside-perf-panel': 'error',
704 'rulesdir/prefer-assert-instance-of': 'error',
705 'rulesdir/prefer-assert-is-ok': 'error',
706 'rulesdir/prefer-assert-length-of': 'error',
Benedikt Meurer39e51332025-01-07 13:17:54707 'rulesdir/prefer-url-string': 'error',
Nikolay Vitkov55adf672025-01-02 12:07:33708 'rulesdir/trace-engine-test-timeouts': 'error',
Nikolay Vitkov49d12de2025-02-12 14:41:46709 'rulesdir/enforce-custom-element-definitions-location': 'off',
Nikolay Vitkov55adf672025-01-02 12:07:33710 },
711
712 settings: {
713 'mocha/additionalCustomNames': [
714 {
715 name: 'describeWithDevtoolsExtension',
716 type: 'suite',
717 interfaces: ['BDD', 'TDD'],
718 },
719 {
720 name: 'describeWithEnvironment',
721 type: 'suite',
722 interfaces: ['BDD', 'TDD'],
723 },
724 {
725 name: 'describeWithLocale',
726 type: 'suite',
727 interfaces: ['BDD', 'TDD'],
728 },
729 {
730 name: 'describeWithMockConnection',
731 type: 'suite',
732 interfaces: ['BDD', 'TDD'],
733 },
734 {
735 name: 'describeWithRealConnection',
736 type: 'suite',
737 interfaces: ['BDD', 'TDD'],
738 },
739 {
740 name: 'itScreenshot',
741 type: 'testCase',
742 interfaces: ['BDD', 'TDD'],
743 },
744 ],
745 },
746 },
Nikolay Vitkov55adf672025-01-02 12:07:33747 {
Nikolay Vitkov52a17972025-01-15 12:52:10748 name: 'Use private class members rule',
Nikolay Vitkov55adf672025-01-02 12:07:33749 files: [
750 'front_end/panels/**/components/*.ts',
751 'front_end/ui/components/**/*.ts',
752 'front_end/entrypoints/**/*.ts',
753 ],
754
755 rules: {
756 'rulesdir/prefer-private-class-members': 'error',
757 },
758 },
Nikolay Vitkov55adf672025-01-02 12:07:33759 {
Nikolay Vitkov52a17972025-01-15 12:52:10760 name: 'Ignore private class members rule',
Nikolay Vitkov55adf672025-01-02 12:07:33761 files: [
762 'front_end/panels/recorder/**/*.ts',
Nikolay Vitkov55adf672025-01-02 12:07:33763 'front_end/ui/components/suggestion_input/*.ts',
764 ],
765 rules: {
766 // TODO(crbug/1402569): Reenable once https://github.com/microsoft/TypeScript/issues/48885 is closed.
767 'rulesdir/prefer-private-class-members': 'off',
768 },
769 },
Nikolay Vitkov55adf672025-01-02 12:07:33770 {
Nikolay Vitkov52a17972025-01-15 12:52:10771 name: 'Supported CSS properties rules',
Nikolay Vitkov55adf672025-01-02 12:07:33772 files: ['front_end/generated/SupportedCSSProperties.js'],
773 rules: {
774 'rulesdir/jslog-context-list': 'error',
775 },
776 },
Nikolay Vitkov55adf672025-01-02 12:07:33777 {
778 name: 'EsLint rules test',
Nikolay Vitkovf1e3bd82025-02-13 11:55:22779 files: ['scripts/eslint_rules/tests/**/*.js'],
Nikolay Vitkov55adf672025-01-02 12:07:33780 rules: {
Nikolay Vitkovf1e3bd82025-02-13 11:55:22781 '@eslint-plugin/no-only-tests': 'error',
Nikolay Vitkov55adf672025-01-02 12:07:33782 },
783 },
Nikolay Vitkov55adf672025-01-02 12:07:33784 {
785 name: 'Legacy test runner',
786 files: ['front_end/legacy_test_runner/**/*'],
787 rules: {
788 'rulesdir/es-modules-import': 'off',
789 },
790 },
791 {
792 name: 'Front end component docs',
793 files: ['front_end/ui/components/docs/**/*.ts'],
794 rules: {
795 // This makes the component doc examples very verbose and doesn't add
796 // anything, so we leave return types to the developer within the
797 // component_docs folder.
798 '@typescript-eslint/explicit-function-return-type': 'off',
Benedikt Meurer6e534082025-01-29 10:36:02799 // We use Lit to help render examples sometimes and we don't use
Nikolay Vitkov55adf672025-01-02 12:07:33800 // {host: this} as often the `this` is the window.
Benedikt Meurer6e534082025-01-29 10:36:02801 'rulesdir/lit-host-this': 'off',
Danil Somsikovd12440f2025-03-31 18:45:43802 'rulesdir/no-imperative-dom-api': 'off',
Nikolay Vitkov55adf672025-01-02 12:07:33803 },
804 },
805 {
Nikolay Vitkov52a17972025-01-15 12:52:10806 name: 'Traces import rule',
Nikolay Vitkov55adf672025-01-02 12:07:33807 files: ['front_end/models/trace/handlers/**/*.ts'],
808 rules: {
809 'rulesdir/no-imports-in-directory': [
810 'error',
811 {
812 bannedImportPaths: [
813 join(import.meta.dirname, 'front_end', 'core', 'sdk', 'sdk.js'),
814 ],
815 },
816 ],
817 },
818 },
819 {
Nikolay Vitkov52a17972025-01-15 12:52:10820 name: 'Recorder injected code',
Nikolay Vitkov55adf672025-01-02 12:07:33821 files: ['front_end/panels/recorder/injected/**/*.ts'],
822 rules: {
823 // The code is rolled up and tree-shaken independently from the regular entrypoints.
824 'rulesdir/es-modules-import': 'off',
825 },
826 },
827 {
Nikolay Vitkovb4e8dc72025-01-07 13:03:02828 name: 'Performance panel file',
Nikolay Vitkov55adf672025-01-02 12:07:33829 files: ['front_end/ui/legacy/components/perf_ui/**/*.ts'],
830 rules: {
831 // Enable tracking of canvas save() and
832 // restore() calls to try and catch bugs. Only
833 // enabled in this folder because it is an
834 // expensive rule to run and we do not need it
835 // for any code that doesn't use Canvas.
836 'rulesdir/canvas-context-tracking': 'error',
837 },
838 },
Jack Franklinc10b4972025-02-28 16:32:56839 {
840 name: 'TypeScript type-definitions',
841 files: ['**/*.d.ts'],
842 rules: {
843 // Not a useful rule for .d.ts files where we are
844 // representing an existing module.
Nikolay Vitkova2ced3f2025-03-05 11:58:03845 'import/no-default-export': 'off',
846 },
847 },
Nikolay Vitkovafc8a8c2025-03-17 10:05:28848 {
849 name: 'Config files',
850 files: ['eslint.config.mjs', '**/*/rollup.config.mjs'],
851 rules: {
852 // The config operate on the default export
853 // So allow it for them
854 'import/no-default-export': 'off',
855 },
856 },
Nikolay Vitkov55adf672025-01-02 12:07:33857];