Frontender Magazine

Автоматизация задач с помощью npm run

Есть несколько модных инструментов для автоматизации сборки в javascript-проектах, которые я никогда не находил привлекательными, потому как знаком с менее известной командой npm run, которой вполне достаточно для всего, что мне необходимо делать, сохраняя при этом достаточно маленький конфигурационный файл.

Вот несколько трюков, которые я использую чтобы получить максимальную отдачу от npm run и полей script в package.json.

Поле «script»

Возможно вы этого не знали, но npm содержит поле под названием scripts в файле package.json проекта для того, чтобы делать такие команды, как npm test, фактически, выполняющая содержимое поля scripts.test, и npm start, вызывающая инструкции из поля scripts.start.

npm test и npm start — это, всего лишь, удобные ссылки для npm run test и npm run start. С помощью npm run можно выполнить совершенно любое содержимое любого поля внутри scripts.

Кроме того, npm run великолепен ещё и потому, что npm автоматически добавляет в $PATH директорию node_modules/.bin, так что вы можете напрямую запускать команды из dependencies или devDependencies, без необходимости устанавливать это модули глобально. npm-пакеты, которые вы хотели бы включить в свой воркфлоу, должны иметь всего лишь простой интерфейс командной строки, и вы сможете написать простую автоматизацию самостоятельно.

Сборка javascript

Я пишу клиентский код, используя для его организации принятые в commonjs module.exports и require() и подключая модули, опубликованные в npm. browserify может преобразовать все вызовы require() в статический код, на этапе сборки, создав единый склееный бандл-файл, который можно загрузить, используя тег script. Для использования browserify я просто держу в package.json поле scripts['build-js'], которое выглядит так:

"build-js": "browserify browser/main.js > static/bundle.js"

Если я хочу собрать javascript для продакшна, я также выполняю минификацию — подключаю uglify-js как devDependency и дописываю его через пайп:

"build-js": "browserify browser/main.js | uglifyjs -mc > static/bundle.js"

Отслеживание изменений в javascript

Для автоматической перекомпиляции клиентского javascript при любых изменениях файлов, я просто заменяю команду browserify на watchify и добавляю ключи -d и -v для дебага и более подробного вывода.

"watch-js": "watchify browser/main.js -o static/bundle.js -dv"

Сборка CSS

Я обнаружил, что cat обычно полностью удовлетворяет мои потребности, так что я просто держу для сборки что-то вроде этого:

"build-css": "cat static/pages/*.css tabs/*/*.css > static/bundle.css"

Отслеживание изменений в css

Так же как и с watchify, я пересобираю css при изменениях с помощью замены cat на catw:

"watch-css": "catw static/pages/*.css tabs/*/*.css -o static/bundle.css -v"

Последовательности задач

Если у вас есть две задачи, которые вы хотели бы запускать последовательно, то вы можете записать их через npm run и разделив их с помощью &&:

"build": "npm run build-js && npm run build-css"

Параллельные задачи

Если вам нужно запустить несколько задач параллельно, просто разделите их с помощью &:

"watch": "npm run watch-js & npm run watch-css"

Получившийся package.json

Соединив всё, о чем я говорил, мы получим примерно такой package.json

{
  "name": "my-silly-app",
  "version": "1.2.3",
  "private": true,
  "dependencies": {
    "browserify": "~2.35.2",
    "uglifyjs": "~2.3.6"
  },
  "devDependencies": {
    "watchify": "~0.1.0",
    "catw": "~0.0.1",
    "tap": "~0.4.4"
  },
  "scripts": {
    "build-js": "browserify browser/main.js | uglifyjs -mc > static/bundle.js",
    "build-css": "cat static/pages/*.css tabs/*/*.css",
    "build": "npm run build-js && npm run build-css",
    "watch-js": "watchify browser/main.js -o static/bundle.js -dv",
    "watch-css": "catw static/pages/*.css tabs/*/*.css -o static/bundle.css -v",
    "watch": "npm run watch-js & npm run watch-css",
    "start": "node server.js",
    "test": "tap test/*.js"
  }
}

Если мне нужно выполнить сборку для продакшна, я просто выполняю npm run build. Для локальной разработки я запущу npm run watch.

Вы можете расширять базовое приближение как хотите! Например, вам может понадобиться выполнить build до запуска start, в этом случае вы просто напишете:

"start": "npm run build && node server.js"

Или, возможно, вы захотите создать команду npm run start-dev, которая также запустит вотчеры:

"start-dev": "npm run watch & npm start"

Вы можете реорганизовать все части так, как хотите!

Когда становится действительно сложно…

Если вы поняли, что вбили слишком много команд в одно поле scripts, то советую вам подумать над тем, чтобы вынести некоторые из этих команд в отдельное место, такое как bin/.

Эти скрипты можно написать на bash, на node, на perl, да на чем угодно! Просто добавьте свойство #! в начало файла, выполните chmod +x, получится так:

#!/bin/bash
(cd site/main; browserify browser/main.js | uglifyjs -mc > static/bundle.js)
(cd site/xyz; browserify browser.js > static/bundle.js)

"build-js": "bin/build.sh"

Если вам совершенно точно нужно собирать ваш проект на windows, просто удостоверьтесь, что у разработчиков, которые пользуются windows, есть копия msysgit, которая поставляется вместе с bash, cygwin или чем-то похожим. Или предложите им перейти на UNIX.

Я много экспериментировал, решая проблему невозможности запуска некоторых команд bash в терминале windows, но работа пока далека от завершения.

Вывод

Я надеюсь, что те примеры использования npm run, которые я описал здесь, помогут тем из вас, кто не был вдохновлен текущим состоянием дел в инструментах для сборки фронтенда, и особенно тем, кто, как и я, не проникся призывами этих инструментов. Я предпочитаю инструменты, пропитанные наследием UNIX, такие, как git, или npm, о котором я говорил здесь. Эти инструменты предоставляют быстрый и минималистичный интерфейс, с которым можно взаимодействовать через bash. Некоторые из этих штук не требуют долгих церемоний, или обсуждений. Можно зайти очень далеко, используя очень простые инструменты, которые делают очень обычные вещи.

Если вам не нравится стиль npm run, о котором я рассказал здесь, вы можете присмотреться к Мakefile, как к простой и проверенной временем альтернативе.

Если вы заметили ошибку, вы всегда можете отредактировать статью, создать issue или просто написать об этом Антону Немцеву в skype ravencry.

James Halliday
Автор:
James Halliday
GitHub:
substack
Twitter:
@substack
Сaйт:
http://substack.net/
Антон Шувалов

Комментарии (5 комментариев, если быть точным)

Автар пользователя
aliaksandr-master

при всем этом это подходит только для очень малых проектов. У нас к примеру 40% кода генерируется грантом. 3 плана билд процесса. больше 80 подтасков. изза такого количества возложенных на грант обязанностей он является отдельным подпроектом и его структура тоже является собираемой специальным образом. грант собираемый грантом, даже звучит забавно =) Такое просто невозможно организовать на npm run, так что бы можно было адекватно поддерживать. npm run используем только что бы после npm install запусть сборку конфига и тасков гранта.

Автар пользователя
A

У меня тоже есть проект, в котором грант грантом собирается, но проанализировав причины, я пришел к выводу, что вся монструозность обусловленна, по большему счету, стилем гранта: декларативностью и декомпозицией на отдельные задачи. Я думаю, что отдельные скрипты-сборщики, скажем, на bash выглядели бы в разы более опрятными и лаконичными.

Не соглашусь, что все ограничивается размерами проектов. Все очень сильно зависит от задач и знаний. Так многие плагины для GruntJS можно заменить парой строк на Node.js, а 300-строчный нечитаемый json из Gruntfile можно переписать в 100 строк на Bash, сделав его, как мне кажется, более читаемым.

Автар пользователя
aliaksandr-master

Да, конечно баш и питон скрипт получится немного элегантней, но читать его "обычным людям фронтенда" сложней, мы писали вначале все на баше. скрипты на ноде конечно спасают, но не кажется, вам, что все придет к мультитаскам и опять же к неявным конфигам (и докам по ним) для реюзабельности самописных тасков ? как мне кажется попахивает велосипедом в перспективе. Монструзность конфигов гранта легко убирается за счет понятной структуры файлов конфигов. Опять же есть для этого специальные грант плагины. Мне самому грант очень НЕ нравится из-за ряда кейсов, при которых приходится жестко костылять.

Автар пользователя
A

Все очень сильно зависит от задач и знаний: если в вашей команде сборкой фронтенда занимаются фронтенд-разработчики, которые не умеют в bash или nodejs, то grunt — отличный выбор. У каждого инструмента есть свое предназначение, я вот тоже не люблю грант, но уверен, что для большинства фронтендщиков грант — потрясающий способ экономить время. Массу времени. Так что grunt рулит :)

Автар пользователя
iamstarkov

если начать использовать npm-run-all то сборка с npm scripts выходит на новый уровень