Кодстайл и

Насилие

Кодстайл?

Зачем мне
это надо?

— Не то что бы именно надо.
Пока ты работаешь один.

— Но как только команда начинает расти …

… всё больше …

… и больше …

… кто то тащит в проект свой велосипед …

… дальше всё становится действительно странно …

… и …

… и в определенный момент становится очевидно,
что нужны правила.

Что за правила нужны?

  • Скрипты (ECMA X, TypeScript, Kotlin, etc. )
  • Стили (CSS, PostCSS, Sass, Less, Stylus, etc.)
  • Рабочий процесс (git flow, github flow, gitlab flow, etc.)

Рабочий процесс включает

  • Процесс
  • Именование веток
  • Именование коммитов

А как
выбрать
кодстайл?

Его
должен поддерживать кто-то, кто
не я

— Потому что мы тут с вами не лучший мир строим, мы пишем программы, чтобы заработать деньги,
купить ружье … и … о чем это я?

Он
должен быть модным

— Потому что это увеличивает ценность членов вашей команды, как разработчиков, и потому его будет проще внедрить

Что-то
ещё?

НЕТ

— Всем начхать используете вы пробелы или табы: IDE отлично справится и с теми, и с другими. Важно единообразие.

Airbnb JavaScript Style Guide

QRCODE with link to Airbnb js style guide

https://github.com/airbnb/javascript

Airbnb CSS Style Guide

QRCODE with link to Airbnb js style guide

https://github.com/airbnb/css

Standard JavaScript Style Guide

QRCODE with link to Airbnb js style guide

https://github.com/standard/standard

A successful Git branching model

QRCODE with link to original gitflow article

https://goo.gl/GDaF

GitHub
branching model

QRCODE with link to original gitflow article

https://githubflow.github.io/

GitLab
branching model

QRCODE with link to original gitflow article

https://goo.gl/Z0w1nQ

И всё?погнали?

— Да! Это ваши коллеги из отделения в Дели бегут, чтобы внедрить ваш кодстайл. Вот прямо-таки вприпрыжку и волосы назад!

— На самом деле — нет. Никому ваш кодстайл не нужен.

Я — художник!
Не смейте меня ограничивать!

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

$ vi .editorconfig
[*]
indent_style = space
indent_size = 2
tab_width = 2
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
$ sudo npm i -g eslint
$ npm i -D eslint
$ eslint --init
$ vi .eslintrc
// .eslintrc
{
  "extends": "airbnb",
  "plugins": [
    "react",
    "jsx-a11y",
    "import"
  ]
}
$ npm info 
    "eslint-config-airbnb@latest" 
    peerDependencies

{ eslint: '^3.19.0 || ^4.3.0',
  'eslint-plugin-jsx-a11y': '^5.1.1',
  'eslint-plugin-import': '^2.7.0',
  'eslint-plugin-react': '^7.1.0' }
$ npm i -D eslint-plugin-react@latest
$ npm i -D eslint-plugin-import@latest
$ npm i -D eslint-plugin-jsx-a11y@latest
$ npm i -D eslint-config-airbnb@latest
// .eslintrc
{
  "parser": "babel-eslint",
  "extends": "airbnb",
  "plugins": [
    "react",
    "jsx-a11y",
    "import"
  ]
}
// .eslintrc
{
  "parser": "babel-eslint",
  "extends": "airbnb",
  "plugins": [
    "react",
    "jsx-a11y",
    "import"
  ],
  "env": {
    "browser": true,
    "es6": true,
  },
}
$ npm i -D babel-eslint@latest
$ eslint ./source
    --color 
    --format stylish
$ eslint ./source
    --color 
    --format stylish
    --fix

Codemod jscodeshift

QRCODE with link to jscodeshift

https://goo.gl/8M1jvp

// package.json
{ // …
  "scripts": {
    "lint:js": "eslint ./source 
                  --color 
                  --format stylish 
                  --fix"
  }
}
$ npm run lint:js

И что,
мне это ручками каждый раз запускать?

— Чтобы видеть проблемы в процессе работы
нужно настроить редактор

Я вообще через ssh!

— Добавь в процесс сборки вебпаком

$ npm i -D eslint-loader
// webpack.config.js
rules: [
  { test: /\.js$/,
    exclude: /node_modules/,
    use: [ "babel-loader", {
        loader: "eslint-loader",
        options: {
          fix: true,
}}]}],
$ webpack-cli --watch

Пример с webpack eslint-loader

QRCODE with link

https://github.com/SilentImp/lintWebpack

Зашибись,
но что если
я это всё просто проигнорирую?

— Тогда, очевидно, мне придется использовать…

Хук

#!/bin/bash
# .git/hooks/pre-commit
if !(npm run lint:js --silent) then
  exit 1;
fi

Source Control
Integrations

QRCODE with link

http://bit.ly/2w5y30Q

Ты правда думаешь, что я себе pre-commit установлю?

Я себе что, враг? И вообще — сложно же.

В репу хуки не добавить

$ npm i -D lint-staged
$ npm i -D husky
// package.json
{ // …
  "scripts": { // …
    "precommit": "lint-staged"
  },
  "lint-staged": { // …
    "*.{js,jsx}": "eslint"
  }
}

Много ли останется так от моего уникального кода?

$ npm i -D -E prettier
// .prettierrc
{
  "parser": "babylon",
  "useTabs": false,
  "tabWidth": 2,
  "singleQuote": true,
  "semi": true,
  "printWidth": 100,
  "trailingComma": "all",
  "bracketSpacing": true,
  "jsxBracketSameLine": true,
  "proseWrap": false,
}

// package.json
{ 
  "scripts": { 
    "format": "prettier 
                  --config .prettierrc
                  --write 'src/**/*.{js,jsx}'"
  }, 
}
$ npm i -D eslint-plugin-prettier
// .eslintrc
{
  "plugins": [
    "prettier",
  ],
  "rules": {
    "prettier/prettier": "error"
  },
}
$ npm i -D eslint-config-prettier
// .eslintrc
{
  "extends": [
    "airbnb", 
    "prettier", 
    "prettier/react"
  ],
}

▉▉▉▉

О! А что с типами данных?

// @flow
function concat(a: string, b: string) {
  return a + b;
}
function concat(a: string, b: string) {
  return a + b;
}

Сделаем из JavaScript настоящий язык!

JAVA

/**
 * Cities - Class to work with unique cities
 * @class
 * @namespace
 */
class Cities {
  /**
   * @type {array}
   * @static
   */
  static collector = [];
}
/**
 * getCity - Get city by index
 * @throws {Error} if index not found
 * @param {number} index city index
 * @return {string} city name
 */
getCity(index) {
  // …
}

И что это делает?

— Ничего.

npm i -D jsdoc
// package.json
"scripts": {
  "doc": "jsdoc src/*.js -d docs"
}
npm run doc
npm i -D jsdoc-babel
// conf.json
{
  "plugins": ["node_modules/jsdoc-babel"]
}
npm i -D documentation
// package.json
"scripts": {
  "doc": "documentation 
    build src/*.js 
    -f html -o docs"
}
npm run doc
// README.md
# Cities
Cool librarie that …

# API
// package.json
"scripts": {
  "readme": "documentation 
    readme src/*.js 
    --section=API"
}
npm run readme

Здорово. Линтинг типов где?

— TS умеет читать JSDoc.

Type Safe JavaScript with JSDoc

QRCODE with link to article about configuring jsdoc type checks

https://goo.gl/sBHmmH

npm i -D typescript
// tsconfig.json
{
  "compilerOptions": {
    "target": "ESNext",
    "noEmit": true,
    "allowJs": true,
    "checkJs": true,
  },
  "include": [
    "src/**/*.js"
  ]
}
// package.json
"scripts": {
  "lint": "tsc"
}
/**
 * add - Add new city
 * @param {string} word city name
 */
add(word) {
  //…
}
citys.add(222);
npm run lint
// package.json
{ // …
  "scripts": { // …
    "precommit": "lint-staged"
  },
  "lint-staged": { // …
    "*.{js,jsx}": "tsc --target ESNext 
      --noEmit --allowJs --checkJs"
  }
}

Репозиторий с примером

QRCODE with link to example repo

https://github.com/SilentImp/lintTypes

ТЫ ▉▉▉!

А что там со стилями?

$ sudo npm i -g stylelint
$ npm i -D stylelint
$ npm i -D stylelint-config-standard
$ vi .stylelintrc
{
  "syntax": "less",
  "extends": "stylelint-config-standard",
  "rules": {
    // …
  }
}

Airbnb Stylelint Config PR

QRCODE with link

http://bit.ly/2zTHQHN

Airbnb Stylelint Config #1

QRCODE with link

http://bit.ly/2vwQmsD

Airbnb Stylelint Config #2

QRCODE with link

http://bit.ly/2wGfVMU

stylelint ./src/**/*.less 
            --syntax less 
            --color 
            --formatter verbose
stylelint ./src/**/*.less 
            --syntax less 
            --color 
            --formatter verbose
            --fix
// package.json
{ // …
  "scripts": {
    "lint:less": "stylelint ./src/**/*.less 
                            --syntax less 
                            --color 
                            --formatter verbose
                            --fix"
  }
}
$ npm run lint:less

А сейчас ты будешь повторять историю про вебпак?

— Почти слово в слово.

$ npm i -D postcss-loader 
$ npm i -D postcss-syntax
$ npm i -D mini-css-extract-plugin
// .stylelintrc
{
  "syntax": "postcss-syntax",
  "extends": "stylelint-config-standard"
}
// postcss.config.js
module.exports = () => ({
  plugins: {
    'stylelint': true,
  },
});
// webpack.config.js
rules: [
  {
    test: /\.css/,
    use: [ MiniCssExtractPlugin.loader,
      { loader: 'css-loader',
        options: {
          minimize: true,
          modules: true,
          importLoaders: 1,
          localIdentName: '[local]~~~~[hash:base64:24]',
      }},
      'postcss-loader']}],
$ webpack-cli --watch

Пример с webpack stylelint

QRCODE with link

https://github.com/SilentImp/lintWebpack

И никаких хуков, да?

— Нет.

// package.json
{ // …
  "scripts": { // …
    "precommit": "lint-staged"
  },
  "lint-staged": { // …
    "*.less": "stylelint"
  }
}

▉▉▉ ▉▉ ▉
▉▉▉▉▉

▉▉▉▉▉▉▉

А что
насчет рабочего процесса?

— Ещё больше хуков…

vi ./hooks/commit-msg
chmod 0755 ./hooks/commit-msg
#!/bin/bash
MESSAGE=`cat "$1"`;
if (![[ "$MESSAGE" =~ ^[A-Z]+-[0-9]+\ -\ .*$ ]]) 
  && [ "$MESSAGE" != "merge" ]; 
  then 
    echo "Wrong commit message format."; 
    exit 1;
fi
if [[ "$MESSAGE" =~ ([а-яА-Я]+) ]]; then 
  echo "Don't use russian for commit message."; 
  exit 2;
fi
if (![[ "$MESSAGE" =~ ^[A-Z]+-[0-9]+\ -\ .{5,}$ ]]) 
  && [ "$MESSAGE" != "merge" ]; then 
    echo "Commit message is too short, 
          try to be more descriptive."; 
    exit 3;
fi
// package.json
{ // …
  "scripts": { // …
    "commitmsg": "./hooks/commit-msg ${GIT_PARAMS}"
  },
  // …
}
npm init
npm i -S command-line-args
// index.js
#! /usr/bin/env node
// index.js
#! /usr/bin/env node
const fs = require('fs');
const filePath = process.argv[2];
if (!fs.existsSync(filePath)) {
  console.log('Can not read commit message');
  process.exit(1);
}
const message = fs.readFileSync(filePath);
const commandLineArgs = require('command-line-args');
const optionDefinitions = [{ 
  name: 'regexp', 
  alias: 'r', 
  type: String, 
  defaultValue: '^[A-Z]+-[0-9]+\s-\s[\W\w\s]{5,}[\s]*$' },
];
const cli = commandLineArgs(optionDefinitions);
const commonRegexp = new RegExp(
  cli.regexp, 
  'ig'
);
if (!commonRegexp.test(message)) {
  console.log("Wrong commit message format");
  process.exit(3);
}
process.exit(0);
// package.json
"name": "my-message-hook",
"main": "index.js",
"bin": {"commit-msg": "index.js"},
"scripts": {
  "release": "npm version patch && 
              git push --tags && 
              npm publish --access public"
}
npm i -S my-message-hook
// package.json
"scripts": {
  "commitmsg": "commit-msg  
                  ${GIT_PARAMS} 
                  -r MyRegExp"
},

И всё?

— Нет

--no-verify!

— Сейчас поправим!

// .gitlab-ci.yml
image: node:latest

stages:
- test
// .gitlab-ci.yml
lint:
  stage: test
  before_script:
    - npm i
  script:
    - npm run lint

▉▉▉▉▉,
что дальше?

Просто хотел дать тебе знать,
что мы сделали куклу вуду,
▉▉▉▉▉▉

Anton Nemtsev

skype: ravencry

QRCODE cо ссылкой на доклад

http://bit.ly/2AVZquZ