Гоголев Сергей
Находит устаревшие зависимости
$ npm outdated
Package Current Wanted Latest Location mocha 2.3.3 2.3.3 2.4.5 mocha nodemon 1.8.0 1.8.0 1.9.1 nodemon lodash 3.10.1 3.10.1 4.6.1 lodash supertest 1.1.0 1.1.0 1.2.0 supertest vow 0.4.11 0.4.12 0.4.12 vow-fs > vow
$ npm update
Находит уязвимости в зависимостях
$ npm install -g nsp
$ nsp check --output summary
(+) 1 vulnerabilities found Name Installed Patched negotiator 0.5.3 >= 0.6.1 Path ... > express@3.21.2 > ... > negotiator@0.5.3 More Info https://nodesecurity.io/advisories/106
$ nsp check --output summary
(+) No known vulnerabilities found1 vulnerabilities found
Не стабилен – нет мажорной версии
Параллельная установка
– в среднем в 2.5 раза быстрее
Фиксирует всё дерево зависимостей
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
# yarn lockfile v1
JSONStream@^0.8.4:
version "0.8.4"
resolved "https://registry.yarnpkg.com/../JSONStream-0.8.4.tgz#91657df"
dependencies:
jsonparse "0.0.5"
$ yarn add --dev bluebird
[1/4] 🔍 Resolving packages...
[2/4] 🚚 Fetching packages...
[3/4] 🔗 Linking dependencies...
[4/4] 📃 Building fresh packages...
success Saved lockfile.
success Saved 1 new dependency.
└─ bluebird@3.4.7
✨ Done in 3.85s.
$ yarn install # Использует уже зафиксированные версии
$ yarn outdated
$ yarn upgrade
Yarn vs npm: Everything You Need to Know
Tim Severien
|
vs |
|
const pi = 3.141592653589;
const e = 2.71828182;
const φ = 1.618033988;
for(let i = 0; i < 10; i++) {
console.log(i);
}
if (user.isAuthorized) {
if (notes.length > 0) {
for(let i = 0; i < note.length; i++) {
console.log(notes[i]);
}
} else {
console.log('Notes not found!')
}
}
function renderNotes(res, user, notes) {
if (!user.isAuthorized) {
res.sendStatus(403); // 1
} else if (notes) {
res.render('notes', notes); // 2
} else {
res.sendStatus(404); // 3
}
}
В этом примере равна трём
exports.list = function (req, res) { // Используй стрелочные
let notes = Note.findAll(); // Используй const
let data = { // И здесь
notes: notes,
meta: req['meta'] // Здесь можно так: req.meta
};
res.render('notes', data);
};
Упростить чтение кода всей команды
Избежать конфликтов
Сделать ревью кода полезнее
Избежать типичных ошибок в коде
Сделать код качественнее
# Variable declaration
* Each variable should be declared:
* using a var statement;
* only once in the current scope;
* on a new line;
* as close as possible to the place
where it's first used.
* Each var statement should have only
one variable declared in it.
github.com/ymaps/codestyle/javascript.md
[*]
indent_size = 4
indent_style = space
[*.json]
indent_size = 2
editorconfig.org
$ npm install --save-dev eslint
{
"rules": {
"no-unused-vars": 0,
"max-len": [1, 100],
"max-params": [2, 3]
}
}
0 - Выключено 1 - Предупреждение 2 - Ошибкаeslint.org/docs/rules
$ npm install --save-dev eslint-config-xo
{
"extends": "xo",
"rules": {
"max-len": [2, 79],
"max-params": [2, 3]
}
}
npms.io/search?q=eslint-config
build/
**/*.min.js
node_modules
function onError(err, req, res, next) {
/* eslint no-unused-vars: 0 */
/* eslint max-params: [2, 4] */
res.sendStatus(500);
}
$ node_modules/.bin/eslint .
/Users/gogoleff/Workspace/urfu-2016/notes-app-example/index.js 3:29 error Missing semicolon semi 20:34 error Missing semicolon semi
$ node_modules/.bin/eslint . --fix
$ npm install --save-dev stylelint
$ npm install --save-dev stylelint-config-standard
$ npm install --save-dev stylefmt
{
"extends": "stylelint-config-standard",
"rules": {
"color-hex-case": "lower"
},
"ignoreFiles": [
"build/*"
]
}
stylelint.io/user-guide/rules/
Запуск комплексных команд в виде простых запоминающихся алиасов к ним
start:
node index.js
lint:
stylelint public/*.css
eslint *.js
test: lint
mocha test/
$ make test
Building Web Software With Make
Mark McDonnell
const gulp = require('gulp');
const eslint = require('gulp-eslint');
gulp.task('lint', () => {
gulp
.src('*.js')
.pipe(eslint())
});
$ gulp lint
Building With Gulp
Callum Macrae
{
"name": "awesome-notes",
"dependencies": {
"mocha": "4.0.0"
},
"scripts": {
"test": "mocha test/",
}
}
$ npm run test
{
"scripts": {
"clean": "rm -rf node_modules/"
}
}
Помним о мультиплатформе!
{
"devDependencies": {
"rimraf": "2.5.2"
},
"scripts": {
"clean": "rimraf node_modules/"
}
}
{
"scripts": {
"check:lint": "eslint .",
"check:test": "mocha test/",
"check": "npm run check:lint && npm run check:test"
}
}
{
"devDependencies": {
"npm-run-all": "1.5.2"
},
"scripts": {
"check:lint": "eslint .",
"check:test": "mocha test/",
"check": "npm-run-all check:lint check:test"
}
}
{
"scripts": {
"lint:css": "stylelint **/*.css",
"lint:js": "eslint .",
"lint": "npm run lint:css & npm run lint:js"
}
}
{
"scripts": {
"lint:css": "stylelint **/*.css",
"lint:js": "eslint .",
"lint": "npm-run-all --parallel lint:css lint:js"
}
}
{
"scripts": {
"lint:css": "stylelint **/*.css",
"lint:js": "eslint .",
"lint": "npm-run-all --parallel lint:*"
}
}
{
"config": {
"report": "nyan"
},
"scripts": {
"test": "mocha test/ -R $npm_package_config_report"
}
}
// scripts/favicon.js
const fs = require('fs');
const toIco = require('to-ico');
const logo = fs.readFileSync('logo.png');
toIco(logo)
.then(fs.writeFileSync.bind(fs, 'favicon.ico'));
{
"scripts": {
"favicon": "node scripts/favicon.js"
}
}
{
"scripts": {
"start": "node app/index.js",
}
}
$ npm run start -- --mode=debug
$ node app/index.js --mode=debug
Advanced front-end automation with npm
Kate Hudson
How to Use npm as a Build Tool
Keith Cirkel
Полная изоляция
Разделение ресурсов (CPU, memory, disk)
Тяжёлые на подъём
Требовательны к дисковому пространству
Лёгкие на подъём
Экономят дисковое пространство
Изоляция уязвима
Привязаны к ядру одной OS
Готовые образы для основы:
Ubuntu, Node.js, MySQL, Mongo
Now: устанавливаем
$ npm install -g now
Now: подготавливаем приложение
app/
└── index.js
package.json
app.listen(process.env.PORT || 8080)
Now: подготавливаем приложение
app/ └── index.js package.json Dockerfile
FROM node:6
COPY package.json /
COPY app /app
RUN npm i --production
ENV PORT 80
EXPOSE 80
CMD node app/index.js
Now: деплоим
$ now install --docker
> Ready! https://notes-app-example-bpehuootqx.now.sh [3s] > Upload [====================] 100% 0.0s > ... > ▲ docker build > ... > Step 6 : EXPOSE 80 > ---> 9459d27e483b > ---> Running in 5f2b53f95673 > Step 7 : CMD node app/index.js > ---> b07beb9eb1e2 > ---> Running in cb1f8e626245 > Successfully built 777656ec62fc > ▲ Storing image > ▲ Deploying image > ▲ Container started > Server started on 80 > Deployment complete!
Heroku: устанавливаем
Heroku CLI$ heroku login
$ heroku help
Heroku: подготавливаем приложение
app/ └── index.js package.json
{
"engines": {
"node": "6.5.0"
}
}
Heroku: подготавливаем приложение
app/
└── index.js
package.json
app.listen(process.env.PORT || 8080)
Heroku: подготавливаем приложение
app/ └── index.js package.json Procfile
web: node app/index.js
Process Types and the Procfile
Heroku: проверяем локально
$ heroku local web
5:24:52 PM web.1 | Server started on 5000 5:25:12 PM web.1 | GET / 200 33.687 ms - 893 5:25:20 PM web.1 | POST /notes 302 27.246 ms - 56 5:25:20 PM web.1 | GET /notes 200 7.074 ms - 943
Heroku: деплоим
$ heroku create urfu2016-notes
Creating ⬢ urfu2016-notes... done https://urfu2016-notes.herokuapp.com/ | https://git.heroku.com/urfu2016-notes.git
Heroku: деплоим
$ git push heroku master
remote: Compressing source files... done. remote: Building source: remote: remote: -----> Using set buildpack heroku/nodejs remote: -----> Node.js app detected remote: remote: -----> Creating runtime environment remote: remote: NODE_ENV=production remote: NODE_MODULES_CACHE=true remote: remote: -----> Installing binaries remote: engines.node (package.json): 4.4.0urfu2016-notes.herokuapp.com
Heroku: управляем
$ heroku restart
$ heroku logs
$ heroku ps:scale web=1:Free
$ heroku ps:scale web=2 Scaling dynos... ! ▸ Cannot update to more than 1 Free size dynos per process type
Docker и Heroku
A Beginner-Friendly Introduction to Containers, VMs and Docker
Preethi Kasireddy
Architecting Containers
Scott McCarty
Heroku Architecture
devcenter.heroku.com
Файлы изображений, иконок, стилей.
Не требуют вычислительный ресурсов.
app/ └── index.js └── routes.js └── models └── controllers └── views └── public └── styles.css └── logo.png
Разместить статику ближе к пользователю
Content Delivery Network
Географически распределённая сетевая инфраструктура
One Machine, One IP
Many Machines, One IP
Размещение статики ближе к пользователю
Кеширование
Сжатие текстовой статики (gzip):
html, css, js, json, ...
Обеспечение 100% доступности
Количество точек присутствия
Point of Presence
Политика кеширования
Политика устаревания
API
$ npm install --save-dev surge
$ surge -p ./public -d urfu2016-notes.surge.sh
Surge - surge.sh
email: email@example.com
token: *****************
project path: ./app/public
size: 3 files, 19.2 KB
domain: urfu2016-notes.surge.sh
upload: [====================] 100%, eta: 0.0s
propagate on CDN: [====================] 100%
plan: Free
users: email@example.com
Success! Project is published and running at urfu2016-notes...
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>{{ title }}</title>
<link rel="stylesheet" href="/styles.css">
<link rel="stylesheet"
href="https://urfu2016-notes.surge.sh/styles.css">
</head>
<body>
{{> notes }}
</body>
</html>
Первый ответ от Surge
HTTP/1.1 200 OK
Cache-Control: public, max-age=31536000, no-cache
Content-Type: text/css; charset=UTF-8
ETag: d1d3c5c4cdb2568785ba1a366b7fb048
Server: SurgeCDN/0.12.2
body {
font-family: Arial, sans-serif;
}
Повторный запрос браузера
GET /styles.css HTTP/1.1
Host: urfu2016-notes.surge.sh
If-None-Match: d1d3c5c4cdb2568785ba1a366b7fb048
Если ETag равен If-None-Match,
то ответ от Surge будет очень коротким
HTTP/1.1 304 Not Modified
Знакомство с Content Delivery Network
Webzilla
What is Anycast and How it works
Sarath Pillai
Content Delivery Networks
Rajkumar Buyya
Автоматизиция сборки, тестирования и деплоя исходного кода по факту его изменения
Автоматизация рутины
Неизбежное тестирование кода
Быстрая доставка до конечного пользователя
Скачиваем клиент Travis CLI
$ travis login
$ travis init node_js
.travis.yml file created!
urfu-2016/notes-app-example: enabled :)
language: node_js
node_js:
- '0.11'
- '0.10'
language: node_js
node_js:
- '6'
install:
- npm install
before_script:
- npm run lint
$ travis setup heroku
Deploy only from urfu-2016/notes-app-example? |yes| yes
Encrypt API key? |yes| yes
deploy:
provider: heroku
api_key:
secure: HDS9M8xVVa6NyI9QV1ZDvW4Xp+...
app: urfu2016-notes
on:
repo: urfu-2016/notes-app-example
$ travis env set SURGE_LOGIN user@example.com
$ travis env set SURGE_PASSWORD my_secure_password
[+] setting environment variable $SURGE_LOGIN
language: node_js
# ...
after_success:
- '["${TRAVIS_PULL_REQUEST}" = "false"] && npm run deploy:surge'
Репозиторий
github.com/urfu-2016/notes-app-example
Адрес в Heroku
urfu2016-notes.herokuapp.com
Адрес в Now
notes-app-example-bpehuootqx.now.sh