javascript

Красивый и чистый: инструменты, которые помогают добиться почти идеального кода

  • среда, 31 октября 2018 г. в 00:17:47
https://habr.com/company/skillbox/blog/428231/
  • Блог компании Skillbox
  • JavaScript
  • Программирование
  • Учебный процесс в IT




Адил Имран — программист, работает в этой сфере давно, делясь опытом, наработками и наблюдениями с коллегами в своем блоге. Новая статья Адила — об инструментах, которые помогают писать красивый и чистый код, который хорошо работает.

От автора: вы хотите писать хороший код, но не знаете, с чего начать, пробуете читать то и это, реализуете на практике прочитанное. Но все равно, вопросов остается больше, чем ответов. Нужно ли убирать «мертвый» код? Что делать, если обнаружена неиспользуемая переменная в уже написанном проекте? Как найти проблемные паттерны и все исправить? Эти вопросы важны, и многие из нас пытаются на них ответить. Но лучше всего — все делать хорошо уже с нуля, так, чтобы потом не приходилось искать проблемные места и латать дыры, теряя время. Для создания хорошего кода есть несколько инструментов, которые можно назвать незаменимыми.

Примеры, которые мы рассмотрим в этой статье, имеют отношение к React, хотя прочитанное можно применить практически для любого веб-проекта.

Skillbox рекомендует: Практический курс «Профессия веб-разработчик».

Напоминаем: для всех читателей «Хабра» — скидка 10 000 рублей при записи на любой курс Skillbox по промокоду «Хабр».

Весь список инструментов статьи вот:

  • Prettier
  • ESLint
  • Automate Format and Lint on Save
  • Husky
  • Lint-staged
  • With Husky and Lint-staged Combined
  • EditorConfig

Начнем с Prettier


Этот инструмент — продуманный оптимизатор кода.



Зачем он нужен?

Подчищает уже готовый код. Только представьте, что вам нужно оптимизировать около 20 тысяч строк. Prettier сделает все это автоматически и быстро.

Его просто использовать и легко адаптировать под себя — над совершенствованием Prettier работает несколько команд, так что можно выбрать версию, подходящую именно вам.

Если вы начинающий программист, который хочет писать красивый код, но не знаете, с чего начать, попробуйте Prettier.

Установка

Нужно создать папку, которая называется app, и внутри папки набрать в командной строке следующее:

npm init -y

Эта команда позволит создать файл package.json.

Далее разбираемся с зависимостями.

yarn add --dev prettier

После выполнения команды внутри только что созданного файла появляется следующее:

{
  "name": "react-boiler-plate",
  "version": "1.0.0",
  "description": "A react boiler plate",
  "main": "src/index.js",
  "author": "Adeel Imran",
  "license": "MIT",
  "scripts": {
    "prettier": "prettier --write src/**/*.js"
  },
  "devDependencies": {
    "prettier": "^1.14.3"
  }
}

Далее создаем src/ папку внутри папки app. И внутри src/ файл index.js. Назвать его вообще-то можно как угодно, главное — вставить в его тело вот это:

let person =                     {
  name: "Yoda",
                designation: 'Jedi Master '
                };
 
 
              function trainJedi (jediWarrion) {
if (jediWarrion.name === 'Yoda') {
  console.log('No need! already trained');
}
console.log(`Training ${jediWarrion.name} complete`)
              }
 
trainJedi(person)
              trainJedi({ name: 'Adeel',
              designation: 'padawan'
});

Теперь у нас есть src/app/index.js с корявым кодом.

Над ним можно выполнить вот такие операции:
— форматировать вручную;
— использовать автоматизацию;
— ничего не делать (Let things go and move on).

Третью опцию лучше не выбирать, иначе зачем нам вообще инструменты по оптимизации кода? Давайте выберем второй вариант. У нас есть зависимость и скрипт Prettier внутри нашего файла package.json.

Теперь создадим prettier.config.js в папке app.

module.exports = {
  printWidth: 100,
  singleQuote: true,
  trailingComma: 'all',
  bracketSpacing: true,
  jsxBracketSameLine: false,
  tabWidth: 2,
  semi: true,
};

printWidth позволит убедиться, что в коде не больше 100 символов;
singleQuote преобразует все двойные кавычки в одинарные;
trailingComma проверит наличие всех висячих запятых в коде, особенно в конце последнего свойства объекта. Объясняется это здесь
bracketSpacing управляет пробелами в объектных литералах:

If bracketSpacing is true - Example: { foo: bar }
If bracketSpacing is false - Example: {foo: bar}
 
jsxBracketSameLine работает с форматированием JSX-элемента ">"  
 
// true example
 
<button
  className="prettier-class"
  id="prettier-id"
  onClick={this.handleClick}>
  Click Here
</button>
 
// false example
 
<button
  className="prettier-class"
  id="prettier-id"
  onClick={this.handleClick}
>
  Click Here
</button>

tabWidth определяет количество пробелов на уровне отступа.
semi — if true выводит; в конце стейтмента.
Вот полный список опций, с которыми может работать Prettier.

После того, как изначальная конфигурация готова, можно заняться скриптом.

“prettier”: “prettier — write src/**/*.js”

В примере выше скрипт ищет все .js-файлы в папке src/.
-write указывает на необходимость сохранения оптимизированных файлов с кодом.

Давайте выполним скрипт:

yarn prettier



Если у вас возникли с примером какие-то проблемы, то вот репозиторий, где можно найти все готовенькое.

ESLint


Этот инструмент анализирует код для того, чтобы помочь обнаружить проблемные паттерны, которые не соответствуют правилам и стандартам. Работает он для большинства языков программирования.

Зачем он нужен в применении к JavaScript?

Поскольку JavaScript является довольно свободным языком, разработчики часто допускают ошибки. ESLint помогает находить проблемы без выполнения написанной программы.

Чем ESLint выделяется среди себе подобных?

Его легко настраивать, он очень гибкий. Можно добавлять и убирать правила в случае необходимости — конфигурируется буквально все. Так, можно форматировать код согласно тому набору правил, которым вы пользуетесь.

Сейчас наиболее актуальны два style guides:

  • Google JavaScript Style Guide
  • Airbnb JavaScript Style Guide

Что касается меня, то я рекомендую второй вариант. Он весьма популярен, в этом можно убедиться, зайдя в его GitHub.

Сначала давайте обновим наш package.json-файл:

{
  "name": "react-boiler-plate",
  "version": "1.0.0",
  "description": "A react boiler plate",
  "main": "src/index.js",
  "author": "Adeel Imran",
  "license": "MIT",
  "scripts": {
    "lint": "eslint --debug src/",
    "lint:write": "eslint --debug src/ --fix",
    "prettier": "prettier --write src/**/*.js"
  },
  "husky": {
    "hooks": {
      "pre-commit": "lint-staged"
    }
  },
  "lint-staged": {
    "*.(js|jsx)": ["npm run lint:write", "git add"]
  },
  "devDependencies": {
    "babel-eslint": "^8.2.3",
    "eslint": "^4.19.1",
    "eslint-config-airbnb": "^17.0.0",
    "eslint-config-jest-enzyme": "^6.0.2",
    "eslint-plugin-babel": "^5.1.0",
    "eslint-plugin-import": "^2.12.0",
    "eslint-plugin-jest": "^21.18.0",
    "eslint-plugin-jsx-a11y": "^6.0.3",
    "eslint-plugin-prettier": "^2.6.0",
    "eslint-plugin-react": "^7.9.1",
    "husky": "^1.1.2",
    "lint-staged": "^7.3.0",
    "prettier": "^1.14.3"
  }
}

Что означает каждая опция:

eslint: это главный инструмент для работы с собственным кодом.
babel-eslint: пригодится, если вы работаете с Flow или экспериментальными функциями, которые еще не поддерживаются ESLint.
eslint-config-airbnb: этот пакет предоставляет разработчику конфигурацию Airbnb’s ESLint.
eslint-plugin-babel: плагин-компаньон для babel-eslint.
eslint-plugin-react: оптимизирует под react.
eslint-plugin-import: обеспечивает возможность работы с синтаксисом ES2015+ (ES6+) import/export.
eslint-plugin-prettier: оптимизирует взаимодействие ESLint с Prettier.

C базовыми вещами покончено, давайте начнем. Например, создадим файл .eslintrc.js в папке app/.

module.exports = {
env: {
es6: true,
browser: true,
node: true,
},
extends: ['airbnb', 'plugin:jest/recommended', 'jest-enzyme'],
plugins: [
'babel',
'import',
'jsx-a11y',
'react',
'prettier',
],
parser: 'babel-eslint',
parserOptions: {
ecmaVersion: 6,
sourceType: 'module',
ecmaFeatures: {
jsx: true
}
},
rules: {
'linebreak-style': 'off', // Don't play nicely with Windows.
 
'arrow-parens': 'off', // Incompatible with prettier
'object-curly-newline': 'off', // Incompatible with prettier
'no-mixed-operators': 'off', // Incompatible with prettier
'arrow-body-style': 'off', // Not our taste?
'function-paren-newline': 'off', // Incompatible with prettier
'no-plusplus': 'off',
'space-before-function-paren': 0, // Incompatible with prettier
 
'max-len': ['error', 100, 2, { ignoreUrls: true, }], // airbnb is allowing some edge cases
'no-console': 'error', // airbnb is using warn
'no-alert': 'error', // airbnb is using warn
 
'no-param-reassign': 'off', // Not our taste?
"radix": "off", // parseInt, parseFloat radix turned off. Not my taste.
 
'react/require-default-props': 'off', // airbnb use error
'react/forbid-prop-types': 'off', // airbnb use error
'react/jsx-filename-extension': ['error', { extensions: ['.js'] }], // airbnb is using .jsx
 
'prefer-destructuring': 'off',
 
'react/no-find-dom-node': 'off', // I don't know
'react/no-did-mount-set-state': 'off',
'react/no-unused-prop-types': 'off', // Is still buggy
'react/jsx-one-expression-per-line': 'off',
 
"jsx-a11y/anchor-is-valid": ["error", { "components": ["Link"], "specialLink": ["to"] }],
"jsx-a11y/label-has-for": [2, {
"required": {
"every": ["id"]
}
}], // for nested label htmlFor error
 
'prettier/prettier': ['error'],
},
};

Добавляем файл .eslintignore в папку app/.

/.git
/.vscode
node_modules

Что делает файл .eslintrc.js?

Давайте посмотрим:

module.exports = {
   env:{},
   extends: {},
   plugin: {},
   parser: {},
   parserOptions: {},
   rules: {},
};

env: среда определяет глобальные переменные, которые уже предопределены. Доступные среды в нашем случае — es6, браузер и нода. Es6 сделает доступными функции ECMAScript 6 кроме модулей. Browser добавит все глобальные переменные, такие как Windows. Соответственно node добавит все глобальные переменные Node.

extends: массив строк — каждая дополнительная конфигурация расширяет предыдущие. Прямо сейчас мы используем linting-правила с airbnb, которые расширяются на на jest, а затем на jest-enzyme.

Plugins: это базовые linting-правила, которые мы хотим использовать. Мы работаем с babel, import, jsx-a11y, react, prettier и всем, что я указал выше.

parser: по умолчанию ESLint использует Espree, но поскольку мы работаем с babel, то нужно использовать Babel-ESLint.

parserOptions: когда мы изменяем дефолтный парсер для Espree на babel-eslint, нам необходимо уточнить parserOptions .

rules: любые правила мы можем изменять или замещать здесь.

Если все ясно, давайте поговорим о .eslintignore. Эта опция помогает указывать все пути, которые нет необходимости обрабатывать при помощи ESLint. Я использую всего три таких пути:
/.git — когда не хочу затрагивать свои git-файлы
/.vscode, поскольку работаю с VS Code, а у этого редактора есть собственная конфигурация, которую необходимо уточнять для каждого проекта и я не хочу в нее лезть здесь.
node_modules — зависимости я тоже не трогаю, поэтому добавил их в список.

С этим все, давайте поговорим о только что добавленных скриптах для нашего package.json

«lint»: «eslint --debug src/»
«lint:write»: «eslint --debug src/ --fix»


$ yarn lint — запуская эту команду, вы проверяете все ваши файлы в src/, в итоге получаете подробный журнал с описанием проблемных мест в каждом файле, где будут найдены ошибки, которые затем сможете вручную запустить и исправить.



$ yarn lint:write — эта команда делает примерно то же самое, что и предыдущая. Единственное отличие в том, что здесь у yarn уже есть право записи — команда исправляет ошибки, удаляя их из кода.

Так, ну если вы продержались до этого момента, то честь вам и хвала.



Husky


Ну а здесь вы можете выполнять некоторые действия во время коммита или пуша кода в ветку.

Все, что нужно — просто установить Husky:

yarn add --dev husky

Далее добавляем в файл package.json сниппет:

"husky": {    
   "hooks": {      
     "pre-commit": "YOUR_COMMAND_HERE",
     "pre-push": "YOUR_COMMAND_HERE"  
   } 
},

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

Lint-staged


Помогает предотвратить попадание плохого кода в вашу git-ветку.

Почему Lint-staged?

Проверка кода в большинстве случаев должна выполняться до коммита. Таким образом вы сможете предотвратить попадание ошибок в репозиторий и улучшить общее качество программы. Но запуск lint для всего проекта — процесс довольно медленный, причем результаты обработки могут быть иррелеватными. В конце концов, вам ведь нужно обработать лишь файлы, которые вы хотите закоммитить.

Все, что нужно сделать, — установить проект:

yarn add --dev lint-staged

Далее в package.json-файл добавить вот это:

"lint-staged": {    
   "*.(js|jsx)": ["npm run lint:write", "git add"] 
},

Так вы запустите lint: write, добавляя ее затем в область стейджа. Команда работает для файлов .js & .jsx, но вы можете сделать то же самое и для других файлов, если хотите.

Объединяем Husky и Lint-staged


Каждый раз, когда вы коммитите ваш код, запускается скрипт, который называется lint-staged. Он инициирует выполнение npm run lint:write, что позволит проверить и отформатировать код. Затем уже проверенный код попадает в область стейджа и выполняется коммит.

Финальный файл package.json должен выглядеть следующим образом:

{
  "name": "react-boiler-plate",
  "version": "1.0.0",
  "description": "A react boiler plate",
  "main": "src/index.js",
  "author": "Adeel Imran",
  "license": "MIT",
  "scripts": {
    "lint": "eslint --debug src/",
    "lint:write": "eslint --debug src/ --fix",
    "prettier": "prettier --write src/**/*.js"
  },
  "husky": {
    "hooks": {
      "pre-commit": "lint-staged"
    }
  },
  "lint-staged": {
    "*.(js|jsx)": ["npm run lint:write", "git add"]
  },
  "devDependencies": {
    "babel-eslint": "^8.2.3",
    "eslint": "^4.19.1",
    "eslint-config-airbnb": "^17.0.0",
    "eslint-config-jest-enzyme": "^6.0.2",
    "eslint-plugin-babel": "^5.1.0",
    "eslint-plugin-import": "^2.12.0",
    "eslint-plugin-jest": "^21.18.0",
    "eslint-plugin-jsx-a11y": "^6.0.3",
    "eslint-plugin-prettier": "^2.6.0",
    "eslint-plugin-react": "^7.9.1",
    "husky": "^1.1.2",
    "lint-staged": "^7.3.0",
    "prettier": "^1.14.3"
  }
}

Теперь каждый раз, когда вы будете выполнять это
$ git add.
$ git commit -m «some descriptive message here»


код будет форматироваться в автоматическом режиме на основе правил из файла .eslintrc.js.

Поговорим об EditorConfig


Сначала создадим файл .editorconfig в директории app/. В него вставим следующий код:

# EditorConfig is awesome: http://EditorConfig.org
 
# top-most EditorConfig file
root = true
 
[*.md]
trim_trailing_whitespace = false
 
[*.js]
trim_trailing_whitespace = true
 
# Unix-style newlines with a newline ending every file
[*]
indent_style = space
indent_size = 2
end_of_line = lf
charset = utf-8
insert_final_newline = true
max_line_length = 100


Вот список редакторов, которые поддерживают работу EditorCondig. В список входит следующее — Web storm, App code, Atom, eclipse, emacs, bbedit.

Код выше делает вот что:

  • Вырезает пробелы из файлов .md и .js.
  • Задает стиль отступов вместо пробелов.
  • Устанавливает размер отступа до 2.
  • Приводит конец строки к единому стандарту.
  • Добавляет новую строку в конец файла.
  • Устанавливает длину строки в 100 символов.

Собственно, теперь все готово. Если вам нужен исходный код, вот он.

Skillbox рекомендует: