35 Star 155 Fork 1

Gitee 极速下载/gitlabhq

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
文件
此仓库是为了提升国内下载速度的镜像仓库,每日同步一次。 原始仓库: https://github.com/gitlabhq/gitlabhq
克隆/下载
eslint.config.mjs 21.35 KB
一键复制 编辑 原始数据 按行查看 历史
GitLab Bot 提交于 2025-05-22 23:12 +08:00 . Add latest changes from gitlab-org/gitlab@master
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727
/* 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),
];
Loading...
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
Ruby
1
https://gitee.com/mirrors/gitlab.git
git@gitee.com:mirrors/gitlab.git
mirrors
gitlab
gitlabhq
master

搜索帮助