Приветствую! В ходе своей профессиональной деятельности, каждый фронтенд-разработчик сталкивается с неотъемлемой частью разработки - инструментом ESLint. ESLint представляет собой мощный статический анализатор кода, призванный обнаруживать и устранять проблемы в вашем JavaScript коде.

Сегодня мы поговорим, о настройке shareable config. Мы рассмотрим, как это может помочь продуктовым командам или фрилансеру разработчику, а далее перейдем к настройке этой конфигурации.

Shareable Config: Когда это надо?

Представьте себе ситуацию, в которой у вас имеется несколько кроссфункциональных команд, занимающихся разработкой различных продуктов компании, но при этом использующих cхожий технологический стек. В таком контексте возникает необходимость в единых стандартах и подходах к написанию кода, чтобы обеспечить единый стиль разработки. В данном случае на помощь приходит инструмент ESLint.

После того как были согласованы наборы правил, назначены ответственные за их реализацию и настройку, начинается этап реализации в проектах. Однако, при возникновении необходимости внести изменения в конфигурацию ESLint, многократное редактирование всех проектов может быть трудозатратным и неэффективным. Именно в таких ситуациях на помощь приходит создание Shareable config для ESLint и его последующая публикация в репозитории пакетов, таких как npm или других пакетных реестрах

Преимущества создания и использования Shareable config:

  1. Единое правило: Создавая Shareable config, вы устанавливаете единый набор правил для всех проектов. Это обеспечивает согласованный стиль кода и снижает вероятность возникновения ошибок, связанных с разнообразными конфигурациями.

  2. Централизованное управление: Если вам необходимо внести изменения в правила ESLint, вы можете внести их в Shareable config, и эти изменения автоматически применятся ко всем проектам, использующим эту конфигурацию. Это значительно упрощает управление и поддержку кодовой базы.

  3. Снижение затрат: Вместо того чтобы править конфигурации в каждом проекте, вы вносите изменения только в Shareable config и публикуете его. Это экономит время и уменьшает затраты на обслуживание.

  4. Публичная доступность: Публикация Shareable config в пакетном реестре позволяет другим командам и разработчикам использовать вашу конфигурацию. Это способствует распространению фронтенд стандартов и позволяет другим внедрять их в свои проекты.

Инициализация конфигурации проекта

Давайте создадим папку eslint-config-test и создадим модуль с ограниченной областью действия. Используем команду npm init --scope=@test и пройдемся по шагам установки.

В итоге мы получим файл, на основе этого:

{
  "name": "@test/eslint-config-test",
  "version": "1.0.0",
  "description": "ESLint Shareable Config",
  "main": "main.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [
    "eslint",
    "share",
    "config"
  ],
  "author": "Kim Valentin",
  "license": "ISC"
}

Здесь важно отметить @test - scope npm модуля, более подробно в документации

Сюда по степени необходимости можно добавить еще несколько опциональных свойств: "files", "repositry","bugs", "dependencies", "devDependencies", "peerDependencies" и другие. О каждом более подробно можно узнать в документации

Я добавил пару пакетов для работы конфига (их не нужно будет ставить в основном проекте, кроме peerDependencies):

"dependencies": {
    "@typescript-eslint/eslint-plugin": "^6.7.2",
    "@typescript-eslint/parser": "^6.7.2",
    "@vue/eslint-config-typescript": "^12.0.0",
    "eslint-config-airbnb-base": "^15.0.0",
    "eslint-plugin-import": "^2.28.1",
    "eslint-plugin-vue": "^9.17.0"
  },
  "devDependencies": {
    "eslint": "^8.50.0",
    "typescript": "~5.2.2"
  },
  "peerDependencies": {
    "eslint": ">= 8.50.0",
    "typescript": ">= 5.0.0"
  }

Создание модуля конфигурация

Здесь все достаточно просто мы создаем модуль для будущего использования с расширением .js. Например, у меня вышел такой модуль:

module.exports = {
  root: true,

  env: {
    es6: true,
    browser: true,
    node: true,
  },

  extends: [
    'plugin:@typescript-eslint/recommended',
    'plugin:vue/vue3-recommended',
    '@vue/eslint-config-typescript',
    'airbnb-base',
  ],

  parserOptions: {
    parser: require.resolve('@typescript-eslint/parser'),
    extraFileExtensions: ['.vue'],
    ecmaVersion: 'latest',
    sourceType: "module",
    ecmaFeatures: {
      jsx: true,
    },
  },

  globals: {
    JSX: true,
  },

  plugins: ['@typescript-eslint'],

  rules: {
    semi: ['error', 'never'],
    'no-param-reassign': 'off',
    'no-void': 'off',
    'no-nested-ternary': 'off',
    'max-classes-per-file': 'off',
    'linebreak-style': 0, // ignore linebreak-style
    'no-plusplus': 'off',
    'max-len': ['error', {
      code: 255,
      ignoreComments: true,
    }],
    'vue/block-order': ['error', {
      'order': [ [ 'script', 'template' ], 'style' ]
    }],
    'no-use-before-define': 'off',
    '@typescript-eslint/no-use-before-define': ['error', { ignoreTypeReferences: true }],
    'no-underscore-dangle': 'off',
    'no-shadow': 0,
    '@typescript-eslint/no-shadow': 0,
    'new-cap': ['error', { newIsCap: false }],
    'import/first': 'off',
    'import/named': 'error',
    'import/namespace': 'error',
    'import/default': 'error',
    'import/export': 'error',
    'import/extensions': 'off',
    'import/no-unresolved': 'off',
    'import/no-extraneous-dependencies': 'off',
    'import/prefer-default-export': 'off',

    // 'prefer-promise-reject-errors': 'off',

    quotes: ['warn', 'single', { avoidEscape: true }],

    // this rule, if on, would require explicit return type on the `render` function
    '@typescript-eslint/explicit-function-return-type': 'off',

    // in plain CommonJS modules, you can't use `import foo = require('foo')` to pass this rule, so it has to be disabled
    '@typescript-eslint/no-var-requires': 'off',

    // The core 'no-unused-vars' rules (in the eslint:recommended ruleset)
    // does not work with type definitions
    'no-unused-vars': 'off',

    // allow debugger during development only
    'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off',
    '@typescript-eslint/member-delimiter-style': ['error', {
      multiline: {
        delimiter: 'none',
        requireLast: true,
      },
      singleline: {
        delimiter: 'comma',
        requireLast: false,
      },
      multilineDetection: 'brackets',
    }],
    '@typescript-eslint/no-explicit-any': 'warn',
  },
}

Публикация пакета

Пару шагов и мы создали с вами наш конфиг. Далее мы можем опубликовать пакет в npm или package registry Gitlab. Настроить CI и использовать.

P.S. Если вы решили использовать package registry Gitlab, у вас будет несколько вариантов, либо настроить publishConfig в package.json и ssh ключ в CI. Либо использовать такую конструкцию:

"devDependencies": {
    "@test/eslint-config-test": "git+ssh://user@host/path/eslint-config-test#branch"
}

Использование пакета

Необходимо на проекте установить нужные зависимости (peerDependencies). Далее можно создать файл .eslintrc.cjs если еще у вас его не было. И там использовать ваш пакет.

module.exports = {
  extends: [
    '@test/eslint-config-test',
  ],
}

Вы также можете указать дополнительные правила, которые перезапишут ваш конфиг, и будут применяться локально к текущему проекту.

Заключение

Пора подводить итоги, если вы раньше таскали ваш файл настроек и перекидывали с одного проекта на другой, то данная статья вам поможет. Всего немного вашего времени, и вы сделаете дополнительную автоматизацию, которая позволит модифицировать ваши правила для написания кода в едином месте.

Если вам интересно больше технического контента, немного лайва из жизни молодого тимлида, добро пожаловать в мой телеграмм канал