代码拉取完成,页面将自动刷新
/* eslint-disable import/no-default-export */
import path from 'node:path';
import { existsSync } from 'node:fs';
import localRules from 'eslint-plugin-local-rules';
import js from '@eslint/js';
import { FlatCompat } from '@eslint/eslintrc';
// eslint-disable-next-line import/no-unresolved
import graphqlPlugin from '@graphql-eslint/eslint-plugin';
import * as todoLists from './.eslint_todo/index.mjs';
const { dirname } = import.meta;
const compat = new FlatCompat({
baseDirectory: dirname,
recommendedConfig: js.configs.recommended,
allConfig: js.configs.all,
});
const extendConfigs = [
'plugin:@gitlab/default',
'plugin:@gitlab/i18n',
'plugin:no-jquery/slim',
'plugin:no-jquery/deprecated-3.4',
'plugin:no-unsanitized/recommended-legacy',
'./tooling/eslint-config/conditionally_ignore.js',
'plugin:@gitlab/jest',
];
// Allowing JiHu to add rules on their side since the update from
// eslintrc.yml to eslint.config.mjs is not allowing subdirectory
// rewrite.
let jhConfigs = [];
if (existsSync(path.resolve(dirname, 'jh'))) {
const pathToJhConfig = path.resolve(dirname, 'jh/eslint.config.js');
// eslint-disable-next-line import/no-dynamic-require, no-unsanitized/method
jhConfigs = (await import(pathToJhConfig)).default;
}
const jestConfig = {
files: ['{,ee/}spec/frontend/**/*.js'],
settings: {
// We have to teach eslint-plugin-import what node modules we use
// otherwise there is an error when it tries to resolve them
'import/core-modules': ['events', 'fs', 'path'],
'import/resolver': {
jest: {
jestConfigFile: 'jest.config.js',
},
},
},
rules: {
'@gitlab/vtu-no-explicit-wrapper-destroy': 'error',
'jest/expect-expect': [
'off',
{
assertFunctionNames: ['expect*', 'assert*', 'testAction'],
},
],
'@gitlab/no-global-event-off': 'off',
'import/no-unresolved': [
'error',
// The test fixtures and graphql schema are dynamically generated in CI
// during the `frontend-fixtures` and `graphql-schema-dump` jobs.
// They may not be present during linting.
{
ignore: ['^test_fixtures/', 'tmp/tests/graphql/gitlab_schema.graphql'],
},
],
},
};
/** An object to make it easier to reuse restricted imports */
const restrictedImports = {
axios: {
name: 'axios',
message: 'Import axios from ~/lib/utils/axios_utils instead.',
},
mousetrap: {
name: 'mousetrap',
message: 'Import { Mousetrap } from ~/lib/mousetrap instead.',
},
sentry: {
name: '@sentry/browser',
message: 'Use "import * as Sentry from \'~/sentry/sentry_browser_wrapper\';" instead',
},
vuex: {
name: 'vuex',
message:
'See our documentation on "Migrating from VueX" for tips on how to avoid adding new VueX stores.',
},
};
export default [
{
ignores: [
'app/assets/javascripts/locale/**/app.js',
'builds/',
'coverage/',
'coverage-frontend/',
'node_modules/',
'public/',
'tmp/',
'vendor/',
'sitespeed-result/',
'fixtures/**/*.graphql',
'storybook/public',
'spec/fixtures/**/*.graphql',
],
},
...compat.extends(...extendConfigs),
...compat.plugins('no-jquery'),
{
rules: {
'no-unused-vars': [
'error',
{
caughtErrors: 'none',
ignoreRestSiblings: true,
},
],
},
},
{
files: ['**/*.{js,vue}'],
plugins: {
'local-rules': localRules,
},
languageOptions: {
globals: {
__webpack_public_path__: true,
gl: false,
gon: false,
localStorage: false,
IS_EE: false,
},
},
settings: {
'import/resolver': {
webpack: {
config: './config/webpack.config.js',
},
},
},
rules: {
'import/no-commonjs': 'error',
'import/no-default-export': 'off',
// Use dependency-cruiser to get an accurate analysis on circular dependencies
// and for better performance
'import/no-cycle': 'off',
'no-underscore-dangle': [
'error',
{
allow: ['__', '_links'],
},
],
'import/no-unresolved': [
'error',
{
ignore: ['^(ee|jh)_component/', '^jh_else_ee/'],
},
],
'lines-between-class-members': 'off',
'no-jquery/no-animate-toggle': 'off',
'no-jquery/no-event-shorthand': 'off',
'no-jquery/no-serialize': 'error',
'promise/always-return': 'off',
'promise/no-callback-in-promise': 'off',
'@gitlab/no-global-event-off': 'error',
'@gitlab/vue-no-new-non-primitive-in-template': [
'error',
{
allowNames: ['class(es)?$', '^style$', '^to$', '^$', '^variables$', 'attrs?$'],
},
],
'@gitlab/vue-no-undef-apollo-properties': 'error',
'@gitlab/tailwind-no-interpolation': 'error',
'@gitlab/vue-tailwind-no-interpolation': 'error',
'@gitlab/tailwind-no-max-width-media-queries': 'error',
'@gitlab/vue-tailwind-no-max-width-media-queries': 'error',
'no-param-reassign': [
'error',
{
props: true,
ignorePropertyModificationsFor: ['acc', 'accumulator', 'el', 'element', 'state'],
ignorePropertyModificationsForRegex: ['^draft'],
},
],
'import/order': [
'error',
{
groups: ['builtin', 'external', 'internal', 'parent', 'sibling', 'index'],
pathGroups: [
{
pattern: '~/**',
group: 'internal',
},
{
pattern: 'emojis/**',
group: 'internal',
},
{
pattern: '{ee_,jh_,}empty_states/**',
group: 'internal',
},
{
pattern: '{ee_,jh_,}icons/**',
group: 'internal',
},
{
pattern: '{ee_,jh_,}images/**',
group: 'internal',
},
{
pattern: 'vendor/**',
group: 'internal',
},
{
pattern: 'shared_queries/**',
group: 'internal',
},
{
pattern: '{ee_,}spec/**',
group: 'internal',
},
{
pattern: '{ee_,jh_,}jest/**',
group: 'internal',
},
{
pattern: '{ee_,jh_,any_}else_ce/**',
group: 'internal',
},
{
pattern: 'ee/**',
group: 'internal',
},
{
pattern: '{ee_,jh_,}component/**',
group: 'internal',
},
{
pattern: 'jh_else_ee/**',
group: 'internal',
},
{
pattern: 'jh/**',
group: 'internal',
},
{
pattern: '{test_,}helpers/**',
group: 'internal',
},
{
pattern: 'test_fixtures/**',
group: 'internal',
},
],
alphabetize: {
order: 'ignore',
},
},
],
'no-restricted-syntax': [
'error',
{
selector: "ImportSpecifier[imported.name='GlSkeletonLoading']",
message: 'Migrate to GlSkeletonLoader, or import GlDeprecatedSkeletonLoading.',
},
{
selector: "ImportSpecifier[imported.name='GlSafeHtmlDirective']",
message: 'Use directive at ~/vue_shared/directives/safe_html.js instead.',
},
{
selector: 'Literal[value=/docs.gitlab.+\\u002Fee/]',
message:
'No hard coded url, use `DOCS_URL_IN_EE_DIR` in `jh_else_ce/lib/utils/url_utility`',
},
{
selector: 'TemplateElement[value.cooked=/docs.gitlab.+\\u002Fee/]',
message:
'No hard coded url, use `DOCS_URL_IN_EE_DIR` in `jh_else_ce/lib/utils/url_utility`',
},
{
selector: 'Literal[value=/(?=.*docs.gitlab.*)(?!.*\\u002Fee\\b.*)/]',
message: 'No hard coded url, use `DOCS_URL` in `jh_else_ce/lib/utils/url_utility`',
},
{
selector: 'TemplateElement[value.cooked=/(?=.*docs.gitlab.*)(?!.*\\u002Fee\\b.*)/]',
message: 'No hard coded url, use `DOCS_URL` in `jh_else_ce/lib/utils/url_utility`',
},
{
selector: 'Literal[value=/(?=.*about.gitlab.*)(?!.*\\u002Fblog\\b.*)/]',
message: 'No hard coded url, use `PROMO_URL` in `jh_else_ce/lib/utils/url_utility`',
},
{
selector: 'TemplateElement[value.cooked=/(?=.*about.gitlab.*)(?!.*\\u002Fblog\\b.*)/]',
message: 'No hard coded url, use `PROMO_URL` in `jh_else_ce/lib/utils/url_utility`',
},
{
selector:
'TemplateLiteral[expressions.0.name=DOCS_URL] > TemplateElement[value.cooked=/\\u002Fjh|\\u002Fee/]',
message:
'`/ee` or `/jh` path found in docs url, use `DOCS_URL_IN_EE_DIR` in `jh_else_ce/lib/utils/url_utility`',
},
{
selector:
"MemberExpression[object.type='ThisExpression'][property.name=/(\\$delete|\\$set)/]",
message:
"Vue 2's set/delete methods are not available in Vue 3. Create/assign new objects with the desired properties instead.",
},
],
'no-restricted-properties': [
'error',
{
object: 'window',
property: 'open',
message:
'Use `visitUrl` in `jh_else_ce/lib/utils/url_utility` to avoid cross-site leaks.',
},
{
object: 'vm',
property: '$delete',
message:
"Vue 2's set/delete methods are not available in Vue 3. Create/assign new objects with the desired properties instead.",
},
{
object: 'Vue',
property: 'delete',
message:
"Vue 2's set/delete methods are not available in Vue 3. Create/assign new objects with the desired properties instead.",
},
{
object: 'vm',
property: '$set',
message:
"Vue 2's set/delete methods are not available in Vue 3. Create/assign new objects with the desired properties instead.",
},
{
object: 'Vue',
property: 'set',
message:
"Vue 2's set/delete methods are not available in Vue 3. Create/assign new objects with the desired properties instead.",
},
],
'no-restricted-imports': [
'error',
{
paths: [
restrictedImports.axios,
restrictedImports.mousetrap,
restrictedImports.sentry,
restrictedImports.vuex,
],
patterns: [
{
group: ['react', 'react-dom/*'],
message:
'We do not allow usage of React in our codebase except for the graphql_explorer',
},
],
},
],
'unicorn/prefer-dom-node-dataset': ['error'],
'no-unsanitized/method': [
'error',
{
escape: {
methods: ['sanitize'],
},
},
],
'no-unsanitized/property': [
'error',
{
escape: {
methods: ['sanitize'],
},
},
],
'unicorn/no-array-callback-reference': 'off',
'vue/no-undef-components': [
'error',
{
ignorePatterns: ['^router-link$', '^router-view$', '^gl-emoji$'],
},
],
'local-rules/require-valid-help-page-path': 'error',
'local-rules/vue-require-valid-help-page-link-component': 'error',
},
},
{
files: ['**/*.vue'],
rules: {
'vue/no-unused-properties': [
'error',
{
groups: ['props', 'data', 'computed', 'methods'],
},
],
},
},
{
files: ['{,ee/,jh/}spec/frontend*/**/*'],
rules: {
'@gitlab/require-i18n-strings': 'off',
'@gitlab/no-runtime-template-compiler': 'off',
'@gitlab/tailwind-no-interpolation': 'off',
'@gitlab/vue-tailwind-no-interpolation': 'off',
'@gitlab/no-max-width-media-queries': 'off',
'@gitlab/vue-tailwind-no-max-width-media-queries': 'off',
'require-await': 'error',
'import/no-dynamic-require': 'off',
'no-import-assign': 'off',
'no-restricted-syntax': [
'error',
{
selector:
'CallExpression[callee.object.name=/(wrapper|vm)/][callee.property.name="setData"]',
message: 'Avoid using "setData" on VTU wrapper',
},
{
selector:
"MemberExpression[object.type!='ThisExpression'][property.type='Identifier'][property.name='$nextTick']",
message:
'Using $nextTick from a component instance is discouraged. Import nextTick directly from the Vue package.',
},
{
selector: "Identifier[name='setImmediate']",
message:
'Prefer explicit waitForPromises (or equivalent), or jest.runAllTimers (or equivalent) to vague setImmediate calls.',
},
{
selector: "ImportSpecifier[imported.name='GlSkeletonLoading']",
message: 'Migrate to GlSkeletonLoader, or import GlDeprecatedSkeletonLoading.',
},
{
selector:
"CallExpression[arguments.length=1][arguments.0.type='Literal'] CallExpression[callee.property.name='toBe'] CallExpression[callee.property.name='attributes'][arguments.length=1][arguments.0.value='disabled']",
message:
'Avoid asserting disabled attribute exact value, because Vue.js 2 and Vue.js 3 renders it differently. Use toBeDefined / toBeUndefined instead',
},
{
selector:
"MemberExpression[object.object.name='Vue'][object.property.name='config'][property.name='errorHandler']",
message:
'Use setErrorHandler/resetVueErrorHandler from helpers/set_vue_error_handler.js instead.',
},
{
selector: 'Literal[value=/docs.gitlab.+\\u002Fee/]',
message:
'No hard coded url, use `DOCS_URL_IN_EE_DIR` in `jh_else_ce/lib/utils/url_utility`',
},
{
selector: 'TemplateElement[value.cooked=/docs.gitlab.+\\u002Fee/]',
message:
'No hard coded url, use `DOCS_URL_IN_EE_DIR` in `jh_else_ce/lib/utils/url_utility`',
},
{
selector: 'Literal[value=/(?=.*docs.gitlab.*)(?!.*\\u002Fee\\b.*)/]',
message: 'No hard coded url, use `DOCS_URL` in `jh_else_ce/lib/utils/url_utility`',
},
{
selector: 'TemplateElement[value.cooked=/(?=.*docs.gitlab.*)(?!.*\\u002Fee\\b.*)/]',
message: 'No hard coded url, use `DOCS_URL` in `jh_else_ce/lib/utils/url_utility`',
},
{
selector: 'Literal[value=/(?=.*about.gitlab.*)(?!.*\\u002Fblog\\b.*)/]',
message: 'No hard coded url, use `PROMO_URL` in `jh_else_ce/lib/utils/url_utility`',
},
{
selector: 'TemplateElement[value.cooked=/(?=.*about.gitlab.*)(?!.*\\u002Fblog\\b.*)/]',
message: 'No hard coded url, use `PROMO_URL` in `jh_else_ce/lib/utils/url_utility`',
},
{
selector:
'TemplateLiteral[expressions.0.name=DOCS_URL] > TemplateElement[value.cooked=/\\u002Fjh|\\u002Fee/]',
message:
'`/ee` or `/jh` path found in docs url, use `DOCS_URL_IN_EE_DIR` in `jh_else_ce/lib/utils/url_utility`',
},
{
selector: 'CallExpression[callee.property.name=/(\\$delete|\\$set)/]',
message:
"Vue 2's set/delete methods are not available in Vue 3. Create/assign new objects with the desired properties instead.",
},
],
'no-restricted-properties': [
'error',
{
object: 'Vue',
property: 'delete',
message:
"Vue 2's set/delete methods are not available in Vue 3. Create/assign new objects with the desired properties instead.",
},
{
object: 'Vue',
property: 'set',
message:
"Vue 2's set/delete methods are not available in Vue 3. Create/assign new objects with the desired properties instead.",
},
],
'no-unsanitized/method': 'off',
'no-unsanitized/property': 'off',
'local-rules/require-valid-help-page-path': 'off',
'local-rules/vue-require-valid-help-page-link-component': 'off',
'no-restricted-imports': [
'error',
{
paths: [
restrictedImports.axios,
restrictedImports.mousetrap,
restrictedImports.sentry,
restrictedImports.vuex,
{
name: '~/locale',
importNames: ['__', 's__'],
message:
'Do not externalize strings in specs: https://docs.gitlab.com/ee/development/i18n/externalization.html#test-files-jest',
},
],
},
],
},
},
{
files: [
'config/**/*',
'scripts/**/*',
'**/*.config.js',
'**/*.config.*.js',
'**/jest_resolver.js',
'eslint.config.mjs',
],
rules: {
'@gitlab/require-i18n-strings': 'off',
'import/extensions': 'off',
'import/no-extraneous-dependencies': 'off',
'import/no-commonjs': 'off',
'import/no-nodejs-modules': 'off',
'filenames/match-regex': 'off',
'no-console': 'off',
},
},
{
files: ['**/*.stories.js'],
rules: {
'filenames/match-regex': 'off',
'@gitlab/require-i18n-strings': 'off',
'import/no-unresolved': [
'error',
// The test fixtures are dynamically generated in CI during
// the `frontend-fixtures` job. They may not be present during linting.
{
ignore: ['^test_fixtures/'],
},
],
},
},
{
files: ['**/*.graphql'],
languageOptions: {
parserOptions: {
parser: graphqlPlugin.parser,
graphQLConfig: {
documents: '{,ee/,jh/}app/**/*.graphql',
schema: './tmp/tests/graphql/gitlab_schema_apollo.graphql',
},
},
},
plugins: {
'@graphql-eslint': graphqlPlugin,
},
rules: {
'filenames/match-regex': 'off',
'spaced-comment': 'off',
'@graphql-eslint/no-anonymous-operations': 'error',
'@graphql-eslint/unique-operation-name': 'error',
'@graphql-eslint/require-selections': 'error',
'@graphql-eslint/no-unused-variables': 'error',
'@graphql-eslint/no-unused-fragments': 'error',
'@graphql-eslint/no-duplicate-fields': 'error',
},
},
{
files: [
'app/assets/javascripts/projects/settings/branch_rules/queries/branch_rules_details.query.graphql',
'app/assets/javascripts/projects/settings/repository/branch_rules/graphql/mutations/create_branch_rule.mutation.graphql',
'app/assets/javascripts/projects/settings/repository/branch_rules/graphql/queries/branch_rules.query.graphql',
'ee/app/assets/javascripts/projects/settings/branch_rules/queries/branch_rules_details.query.graphql',
'ee/app/assets/javascripts/projects/settings/repository/branch_rules/graphql/queries/branch_rules.query.graphql',
],
rules: {
'@graphql-eslint/require-selections': 'off',
},
},
{
files: ['{,spec/}tooling/**/*'],
rules: {
'no-undef': 'off',
'import/no-commonjs': 'off',
'import/no-extraneous-dependencies': 'off',
'no-restricted-syntax': 'off',
'@gitlab/require-i18n-strings': 'off',
},
},
// JIRA subscriptions config
{
files: ['app/assets/javascripts/jira_connect/subscriptions/**/*.{js,vue}'],
languageOptions: {
globals: {
AP: 'readonly',
},
},
rules: {
'@gitlab/require-i18n-strings': 'off',
'@gitlab/vue-require-i18n-strings': 'off',
},
},
// Storybook config
{
files: ['storybook/**/*.{js,vue}'],
rules: {
'@gitlab/require-i18n-strings': 'off',
'import/no-extraneous-dependencies': 'off',
'import/no-commonjs': 'off',
'import/no-nodejs-modules': 'off',
'filenames/match-regex': 'off',
'no-console': 'off',
'import/no-unresolved': 'off',
},
},
// Jest config
jestConfig,
// Integration tests config
{
files: ['{,ee/}spec/frontend_integration/**/*.js'],
settings: {
...jestConfig.settings,
'import/resolver': {
jest: {
jestConfigFile: 'jest.config.integration.js',
},
},
},
rules: {
...jestConfig.rules,
'no-restricted-imports': ['error', 'fs'],
},
languageOptions: {
globals: {
mockServer: false,
},
},
},
/*
contracts specs are a little different, as they are not "normal" jest specs.
They are actually executing `jest` and e.g. do proper non-mocked calls with axios in order
to check API contracts.
They also do not directly execute library code, so some of our usual linting rules for app code
like no-restricted-imports or i18n rules make no sense here and we can disable them.
For reference: https://docs.gitlab.com/development/testing_guide/contract/
*/
{
files: ['{,ee/}spec/contracts/consumer/**/*.js'],
settings: {
'import/core-modules': ['@pact-foundation/pact', 'jest-pact'],
},
rules: {
'@gitlab/require-i18n-strings': 'off',
'no-restricted-imports': 'off',
},
},
...jhConfigs,
...Object.values(todoLists),
];
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。