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"),
 | 
						|
];
 |