mirror of
				https://github.com/microsoft/playwright.git
				synced 2025-06-26 21:40:17 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			430 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			430 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| /**
 | |
|  * Copyright (c) Microsoft Corporation.
 | |
|  *
 | |
|  * Licensed under the Apache License, Version 2.0 (the "License");
 | |
|  * you may not use this file except in compliance with the License.
 | |
|  * You may obtain a copy of the License at
 | |
|  *
 | |
|  * http://www.apache.org/licenses/LICENSE-2.0
 | |
|  *
 | |
|  * Unless required by applicable law or agreed to in writing, software
 | |
|  * distributed under the License is distributed on an "AS IS" BASIS,
 | |
|  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | |
|  * See the License for the specific language governing permissions and
 | |
|  * limitations under the License.
 | |
|  */
 | |
| 
 | |
| import typescriptEslint from "@typescript-eslint/eslint-plugin";
 | |
| import tsParser from "@typescript-eslint/parser";
 | |
| import notice from "eslint-plugin-notice";
 | |
| import path from "path";
 | |
| import { fileURLToPath } from "url";
 | |
| import stylistic from "@stylistic/eslint-plugin";
 | |
| import importRules from "eslint-plugin-import";
 | |
| import { fixupConfigRules } from "@eslint/compat";
 | |
| import { FlatCompat } from "@eslint/eslintrc";
 | |
| import js from "@eslint/js";
 | |
| 
 | |
| const __filename = fileURLToPath(import.meta.url);
 | |
| const __dirname = path.dirname(__filename);
 | |
| 
 | |
| const plugins = {
 | |
|   "@stylistic": stylistic,
 | |
|   "@typescript-eslint": typescriptEslint,
 | |
|   notice,
 | |
|   import: importRules,
 | |
| };
 | |
| 
 | |
| const ignores = [
 | |
|   ".github/",
 | |
|   "*.js",
 | |
|   "**/.cache/",
 | |
|   "**/*.d.ts",
 | |
|   "index.d.ts",
 | |
|   "node_modules/",
 | |
|   "output/",
 | |
|   "**/playwright-report/",
 | |
|   "packages/*/lib/",
 | |
|   "packages/playwright-core/bundles/zip/src/third_party/",
 | |
|   "packages/playwright-core/src/generated/*",
 | |
|   "packages/playwright-core/src/third_party/",
 | |
|   "packages/playwright-core/types/*",
 | |
|   "packages/playwright-ct-core/src/generated/*",
 | |
|   "packages/html-reporter/bundle.ts",
 | |
|   "packages/html-reporter/playwright.config.ts",
 | |
|   "packages/html-reporter/playwright/*",
 | |
|   "packages/html-reporter/vite.config.ts",
 | |
|   "test-results/",
 | |
|   "tests/assets/",
 | |
|   "tests/components/",
 | |
|   "tests/installation/fixture-scripts/",
 | |
|   "tests/third_party/",
 | |
|   "utils/",
 | |
| ];
 | |
| 
 | |
| export const baseRules = {
 | |
|   "@typescript-eslint/no-unused-vars": [
 | |
|     2,
 | |
|     { args: "none", caughtErrors: "none" },
 | |
|   ],
 | |
| 
 | |
|   /**
 | |
|    * Enforced rules
 | |
|    */
 | |
|   // syntax preferences
 | |
|   "object-curly-spacing": ["error", "always"],
 | |
|   quotes: [
 | |
|     2,
 | |
|     "single",
 | |
|     {
 | |
|       avoidEscape: true,
 | |
|       allowTemplateLiterals: true,
 | |
|     },
 | |
|   ],
 | |
|   "jsx-quotes": [2, "prefer-single"],
 | |
|   "no-extra-semi": 2,
 | |
|   "@stylistic/semi": [2],
 | |
|   "comma-style": [2, "last"],
 | |
|   "wrap-iife": [2, "inside"],
 | |
|   "spaced-comment": [
 | |
|     2,
 | |
|     "always",
 | |
|     {
 | |
|       markers: ["*"],
 | |
|     },
 | |
|   ],
 | |
|   eqeqeq: [2],
 | |
|   "accessor-pairs": [
 | |
|     2,
 | |
|     {
 | |
|       getWithoutSet: false,
 | |
|       setWithoutGet: false,
 | |
|     },
 | |
|   ],
 | |
|   "brace-style": [2, "1tbs", { allowSingleLine: true }],
 | |
|   curly: [2, "multi-or-nest", "consistent"],
 | |
|   "new-parens": 2,
 | |
|   "arrow-parens": [2, "as-needed"],
 | |
|   "prefer-const": 2,
 | |
|   "quote-props": [2, "consistent"],
 | |
|   "nonblock-statement-body-position": [2, "below"],
 | |
| 
 | |
|   // anti-patterns
 | |
|   "no-var": 2,
 | |
|   "no-with": 2,
 | |
|   "no-multi-str": 2,
 | |
|   "no-caller": 2,
 | |
|   "no-implied-eval": 2,
 | |
|   "no-labels": 2,
 | |
|   "no-new-object": 2,
 | |
|   "no-octal-escape": 2,
 | |
|   "no-self-compare": 2,
 | |
|   "no-shadow-restricted-names": 2,
 | |
|   "no-cond-assign": 2,
 | |
|   "no-debugger": 2,
 | |
|   "no-dupe-keys": 2,
 | |
|   "no-duplicate-case": 2,
 | |
|   "no-empty-character-class": 2,
 | |
|   "no-unreachable": 2,
 | |
|   "no-unsafe-negation": 2,
 | |
|   radix: 2,
 | |
|   "valid-typeof": 2,
 | |
|   "no-implicit-globals": [2],
 | |
|   "no-unused-expressions": [
 | |
|     2,
 | |
|     { allowShortCircuit: true, allowTernary: true, allowTaggedTemplates: true },
 | |
|   ],
 | |
|   "no-proto": 2,
 | |
| 
 | |
|   // es2015 features
 | |
|   "require-yield": 2,
 | |
|   "template-curly-spacing": [2, "never"],
 | |
| 
 | |
|   // spacing details
 | |
|   "space-infix-ops": 2,
 | |
|   "space-in-parens": [2, "never"],
 | |
|   "array-bracket-spacing": [2, "never"],
 | |
|   "comma-spacing": [2, { before: false, after: true }],
 | |
|   "keyword-spacing": [2, "always"],
 | |
|   "space-before-function-paren": [
 | |
|     2,
 | |
|     {
 | |
|       anonymous: "never",
 | |
|       named: "never",
 | |
|       asyncArrow: "always",
 | |
|     },
 | |
|   ],
 | |
|   "no-whitespace-before-property": 2,
 | |
|   "keyword-spacing": [
 | |
|     2,
 | |
|     {
 | |
|       overrides: {
 | |
|         if: { after: true },
 | |
|         else: { after: true },
 | |
|         for: { after: true },
 | |
|         while: { after: true },
 | |
|         do: { after: true },
 | |
|         switch: { after: true },
 | |
|         return: { after: true },
 | |
|       },
 | |
|     },
 | |
|   ],
 | |
|   "arrow-spacing": [
 | |
|     2,
 | |
|     {
 | |
|       after: true,
 | |
|       before: true,
 | |
|     },
 | |
|   ],
 | |
|   "@stylistic/func-call-spacing": 2,
 | |
|   "@stylistic/type-annotation-spacing": 2,
 | |
| 
 | |
|   // file whitespace
 | |
|   "no-multiple-empty-lines": [2, { max: 2, maxEOF: 0 }],
 | |
|   "no-mixed-spaces-and-tabs": 2,
 | |
|   "no-trailing-spaces": 2,
 | |
|   "linebreak-style": [process.platform === "win32" ? 0 : 2, "unix"],
 | |
|   indent: [
 | |
|     2,
 | |
|     2,
 | |
|     { SwitchCase: 1, CallExpression: { arguments: 2 }, MemberExpression: 2 },
 | |
|   ],
 | |
|   "key-spacing": [
 | |
|     2,
 | |
|     {
 | |
|       beforeColon: false,
 | |
|     },
 | |
|   ],
 | |
|   "eol-last": 2,
 | |
| 
 | |
|   // copyright
 | |
|   "notice/notice": [
 | |
|     2,
 | |
|     {
 | |
|       mustMatch: "Copyright",
 | |
|       templateFile: path.join(__dirname, "utils", "copyright.js"),
 | |
|     },
 | |
|   ],
 | |
| 
 | |
|   // react
 | |
|   "react/react-in-jsx-scope": 0,
 | |
| };
 | |
| 
 | |
| const noFloatingPromisesRules = {
 | |
|   "@typescript-eslint/no-floating-promises": "error",
 | |
| };
 | |
| 
 | |
| const noBooleanCompareRules = {
 | |
|   "@typescript-eslint/no-unnecessary-boolean-literal-compare": 2,
 | |
| };
 | |
| 
 | |
| const noWebGlobalsRuleList = [
 | |
|   // Keep in sync with builtins from utilityScript.ts
 | |
|   { name: "window", message: "Use InjectedScript.window instead" },
 | |
|   { name: "document", message: "Use InjectedScript.document instead" },
 | |
|   { name: "globalThis", message: "Use InjectedScript.window instead" },
 | |
|   { name: "setTimeout", message: "Use InjectedScript.utils.builtins.setTimeout instead" },
 | |
|   { name: "clearTimeout", message: "Use InjectedScript.utils.builtins.clearTimeout instead" },
 | |
|   { name: "setInterval", message: "Use InjectedScript.utils.builtins.setInterval instead" },
 | |
|   { name: "clearInterval", message: "Use InjectedScript.utils.builtins.clearInterval instead" },
 | |
|   { name: "requestAnimationFrame", message: "Use InjectedScript.utils.builtins.requestAnimationFrame instead" },
 | |
|   { name: "cancelAnimationFrame", message: "Use InjectedScript.utils.builtins.cancelAnimationFrame instead" },
 | |
|   { name: "requestIdleCallback", message: "Use InjectedScript.utils.builtins.requestIdleCallback instead" },
 | |
|   { name: "cancelIdleCallback", message: "Use InjectedScript.utils.builtins.cancelIdleCallback instead" },
 | |
|   { name: "Date", message: "Use InjectedScript.utils.builtins.Date instead" },
 | |
|   { name: "Intl", message: "Use InjectedScript.utils.builtins.Intl instead" },
 | |
|   { name: "performance", message: "Use InjectedScript.utils.builtins.performance instead" },
 | |
| ];
 | |
| 
 | |
| const noNodeGlobalsRuleList = [{ name: "process" }];
 | |
| 
 | |
| const importOrderRules = {
 | |
|   "import/order": [
 | |
|     2,
 | |
|     {
 | |
|       groups: [
 | |
|         "builtin",
 | |
|         "external",
 | |
|         "internal",
 | |
|         ["parent", "sibling"],
 | |
|         "index",
 | |
|         "type",
 | |
|       ],
 | |
|       "newlines-between": "always",
 | |
|     },
 | |
|   ],
 | |
|   "import/consistent-type-specifier-style": [2, "prefer-top-level"],
 | |
| };
 | |
| 
 | |
| const languageOptions = {
 | |
|   parser: tsParser,
 | |
|   ecmaVersion: 9,
 | |
|   sourceType: "module",
 | |
| };
 | |
| 
 | |
| const languageOptionsWithTsConfig = {
 | |
|   parser: tsParser,
 | |
|   ecmaVersion: 9,
 | |
|   sourceType: "module",
 | |
|   parserOptions: {
 | |
|     project: path.join(__dirname, "tsconfig.json"),
 | |
|   },
 | |
| };
 | |
| 
 | |
| const compat = new FlatCompat({
 | |
|   baseDirectory: __dirname,
 | |
|   recommendedConfig: js.configs.recommended,
 | |
|   allConfig: js.configs.all,
 | |
| });
 | |
| const reactBaseConfig = fixupConfigRules(
 | |
|   compat.extends("plugin:react/recommended", "plugin:react-hooks/recommended")
 | |
| );
 | |
| const reactFiles = [
 | |
|   `packages/html-reporter/src/**/*.ts`,
 | |
|   `packages/html-reporter/src/**/*.tsx`,
 | |
|   `packages/recorder/src/**/*.ts`,
 | |
|   `packages/recorder/src/**/*.tsx`,
 | |
|   `packages/trace-viewer/src/**/*.ts`,
 | |
|   `packages/trace-viewer/src/**/*.tsx`,
 | |
|   `packages/web/src/**/*.ts`,
 | |
|   `packages/web/src/**/*.tsx`,
 | |
| ];
 | |
| 
 | |
| function reactPackageSection(packageName) {
 | |
|   return {
 | |
|     files: [
 | |
|       `packages/${packageName}/src/**/*.ts`,
 | |
|       `packages/${packageName}/src/**/*.tsx`,
 | |
|       `packages/web/src/**/*.ts`,
 | |
|       `packages/web/src/**/*.tsx`,
 | |
|     ],
 | |
|     languageOptions: {
 | |
|       parser: tsParser,
 | |
|       ecmaVersion: 9,
 | |
|       sourceType: "module",
 | |
|       parserOptions: {
 | |
|         project: path.join(__dirname, "packages", packageName, "tsconfig.json"),
 | |
|       },
 | |
|     },
 | |
|     rules: {
 | |
|       ...baseRules,
 | |
|       "no-console": 2,
 | |
|     },
 | |
|   };
 | |
| }
 | |
| 
 | |
| export default [
 | |
|   {
 | |
|     ignores,
 | |
|   },
 | |
|   {
 | |
|     files: ["**/*.ts", "**/*.tsx"],
 | |
|     plugins,
 | |
|     languageOptions,
 | |
|     rules: baseRules,
 | |
|     settings: {
 | |
|       react: { version: "detect" },
 | |
|     },
 | |
|   },
 | |
|   {
 | |
|     files: ["packages/**/*.ts"],
 | |
|     languageOptions: languageOptionsWithTsConfig,
 | |
|     rules: {
 | |
|       "no-console": 2,
 | |
|       "no-restricted-properties": [
 | |
|         2,
 | |
|         {
 | |
|           object: "process",
 | |
|           property: "exit",
 | |
|           message:
 | |
|             "Please use gracefullyProcessExitDoNotHang function to exit the process.",
 | |
|         },
 | |
|       ],
 | |
|     },
 | |
|   },
 | |
|   {
 | |
|     files: ["packages/**/*.ts"],
 | |
|     rules: {
 | |
|       ...importOrderRules,
 | |
|     },
 | |
|   },
 | |
|   {
 | |
|     files: ["packages/playwright/**/*.ts"],
 | |
|     rules: {
 | |
|       ...noFloatingPromisesRules,
 | |
|     },
 | |
|   },
 | |
|   {
 | |
|     files: ["packages/playwright/src/reporters/**/*.ts"],
 | |
|     languageOptions: languageOptionsWithTsConfig,
 | |
|     rules: {
 | |
|       "no-console": "off",
 | |
|     },
 | |
|   },
 | |
|   {
 | |
|     files: [
 | |
|       "packages/playwright-core/src/utils/**/*.ts",
 | |
|     ],
 | |
|     languageOptions: languageOptionsWithTsConfig,
 | |
|     rules: {
 | |
|       "no-restricted-globals": [
 | |
|         "error",
 | |
|         ...noNodeGlobalsRuleList,
 | |
|         ...noWebGlobalsRuleList,
 | |
|       ],
 | |
|       ...noFloatingPromisesRules,
 | |
|       ...noBooleanCompareRules,
 | |
|     },
 | |
|   },
 | |
|   {
 | |
|     files: [
 | |
|       "packages/injected/src/**/*.ts",
 | |
|     ],
 | |
|     languageOptions: languageOptionsWithTsConfig,
 | |
|     rules: {
 | |
|       "no-restricted-globals": [
 | |
|         "error",
 | |
|         ...noWebGlobalsRuleList,
 | |
|       ],
 | |
|       ...noFloatingPromisesRules,
 | |
|       ...noBooleanCompareRules,
 | |
|     },
 | |
|   },
 | |
|   {
 | |
|     files: [
 | |
|       "packages/playwright-core/src/client/**/*.ts",
 | |
|       "packages/playwright-core/src/protocol/**/*.ts",
 | |
|     ],
 | |
|     languageOptions: languageOptionsWithTsConfig,
 | |
|     rules: {
 | |
|       "no-restricted-globals": [
 | |
|         "error",
 | |
|         ...noNodeGlobalsRuleList,
 | |
|       ],
 | |
|       ...noFloatingPromisesRules,
 | |
|       ...noBooleanCompareRules,
 | |
|     },
 | |
|   },
 | |
|   {
 | |
|     files: ["tests/**/*.spec.js", "tests/**/*.ts"],
 | |
|     languageOptions: {
 | |
|       parser: tsParser,
 | |
|       ecmaVersion: 9,
 | |
|       sourceType: "module",
 | |
|       parserOptions: {
 | |
|         project: path.join(__dirname, "tests", "tsconfig.json"),
 | |
|       },
 | |
|     },
 | |
|     rules: {
 | |
|       ...noFloatingPromisesRules,
 | |
|     },
 | |
|   },
 | |
|   ...reactBaseConfig.map((config) => ({
 | |
|     ...config,
 | |
|     files: reactFiles,
 | |
|   })),
 | |
|   reactPackageSection("html-reporter"),
 | |
|   reactPackageSection("recorder"),
 | |
|   reactPackageSection("trace-viewer"),
 | |
| ];
 | 
