Кодстайл?
Зачем мне
это надо?
— Не то что бы именно надо.
Пока ты работаешь один.
— Но как только команда начинает расти …
… всё больше …
… и больше …
… кто то тащит в проект свой велосипед …
… дальше всё становится действительно странно …
… и …
… и в определенный момент становится очевидно,
что нужны правила.
А как
выбрать
кодстайл?
Его
должен поддерживать кто-то, кто
не я
— Потому что мы тут с вами не лучший мир строим, мы пишем программы, чтобы заработать деньги,
купить ружье … и … о чем это я?
Он
должен быть модным
— Потому что это увеличивает ценность членов вашей команды, как разработчиков, и потому его будет проще внедрить
Что-то
ещё?
НЕТ
— Всем начхать используете вы пробелы или табы: IDE отлично справится и с теми, и с другими. Важно единообразие.
И всё?погнали?
— Да! Это ваши коллеги из отделения в Дели бегут, чтобы внедрить ваш кодстайл. Вот прямо-таки вприпрыжку и волосы назад!
— На самом деле — нет. Никому ваш кодстайл не нужен.
Я — художник!
Не смейте меня ограничивать!
— Если хотите внедрить кодстайл, то его нужно явно определить, автоматически проверять и принуждать использовать.
$ 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
// 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
Зашибись,
но что если
я это всё просто проигнорирую?
— Тогда, очевидно, мне придется использовать…
Хук
#!/bin/bash
# .git/hooks/pre-commit
if !(npm run lint:js --silent) then
exit 1;
fi
Ты правда думаешь, что я себе 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.
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"
}
}
ТЫ ▉▉▉!
А что там со стилями?
$ sudo npm i -g stylelint
$ npm i -D stylelint
$ npm i -D stylelint-config-standard
$ vi .stylelintrc
{
"syntax": "less",
"extends": "stylelint-config-standard",
"rules": {
// …
}
}
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
И никаких хуков, да?
— Нет.
// 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
http://silentimp.info/
@silentimp
thesilentimp@gmail.com
skype: ravencry