Главная Категории Контакты Поиск

Введение в NPM скрипты

Что такое NPM скрипты и как их можно использовать для автоматизации задач.

NpmTools·22.08.2019·читать 8 мин 🤓·Автор: Alexey Myzgin

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

Написание npm скриптов

Мы будем проводить большую часть времени в файле package.json. Здесь будут все зависимости и скрипты. Вот урезанная версия стандартного проекта:

{
  "name": "npm-lesson",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    ...
  },
  "devDependencies": {
    ...
  }
  "keywords": [],
  "author": "",
  "license": "ISC"
}

По мере продвижения мы дополним файл package.json. Скрипты будут в объекте scripts, а внешние пакеты, будут установлены в объект devDependencies.

Компиляция SCSS в CSS

Я частый пользователь SCSS, так что начнем с него. Чтобы скомпилировать SCSS в CSS, я использую node-sass. Для начала, нам нужно установить node-sass, запустив в командной строке следующее:

npm install --save-dev node-sass

Он установит node-sass в текущий каталог и добавит его в объект devDependencies в package.json. После установки мы можем использовать его в командной строке:

node-sass --output-style compressed -o dist/css src/scss

Давай разберемся, что делает эта команда. Начиная с конца, она “говорит”: найди в папке src/scss какие-либо файлы SCSS; сохрани (флаг -o это output) скомпилированный CSS в dist/css; сожми вывод (используя флаг --output-style с опцией compressed).

Теперь, давай переместим рабочий скрипт в npm scripts package.json:

"scripts": {
  "scss": "node-sass --output-style compressed -o dist/css src/scss"
}

Теперь запустим в командной строке:

npm run scss

Видим тот же вывод, что и при запуске команды node-sass непосредственно в командной строке.

Каждый раз, когда создаем npm скрипт, мы можем запустить его с помощью подобной команды - просто заменив scss на имя задачи, которую хотим выполнить.

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

"scripts": {
  "scss": "node-sass --output-style nested --indent-type tab --indent-width 4 -o dist/css src/scss"
}

Автопрефиксы к CSS с помощью PostCSS

Когда компилируем Scss в CSS, мы можем автоматически добавлять префиксы вендоров, используя Autoprefixer и PostCSS. Установим несколько модулей одновременно, разделяя их пробелами:

npm install --save-dev postcss-cli autoprefixer

Мы установим два модуля, потому что PostCSS, по умолчанию, ничего не делает. Он использует другие плагины, такие как Autoprefixer, для манипулирования предоставленным ему CSS.

Добавим новую задачу в объект scripts:

"scripts": {
  ...
  "autoprefixer": "postcss -u autoprefixer -r dist/css/*"
}

Данная задача говорит postcss, используй (флаг -u это use) авторефикс для замены (флаг -r это replace) любых файлов .css в dist/css на код с префиксом. Вот и всё! Если нужно добавить поддержку браузера, по умолчанию, для автоматического префикса, мы можем сделать это так:

"scripts": {
  ...
  "autoprefixer": "postcss -u autoprefixer --autoprefixer.browsers '> 5%, ie 9' -r dist/css/*"
}

Опять же, есть много опций, которые мы можем использовать для настройки сборки: postcss-cli и autoprefixer.

Linting стилей

При разработке кода важно придерживаться стандартного формата и стиля, чтобы свести ошибки к минимуму, и повысить эффективность работы разработчика. Stylelint помогает делать это автоматически для стилей, так что давай добавим linting стилей с помощью stylelint.

npm install --save-dev stylelint

Далее мы создадим файл .stylelintrc в корне директории. Мы можем использовать свою конфигурацию или использовать общую конфигурацию. Как сделать свою конфигурацию смотри здесь. Добавим в файл .stylelintrc:

"rules": {
  "block-no-empty": true,
  "color-hex-case": "lower",
  "color-hex-length": "short",
  "color-no-invalid-hex": true,
  "declaration-colon-space-after": "always",
  "max-empty-lines": 2,
}

Теперь добавим задачу lint:scss в объект scripts package.json:

"scripts": {
  ...
  "lint:scss": "stylelint src/scss/*.scss --syntax scss",
}

stylelint ищет любые файлы scss в папке src/scss/ и запускает их в соответствии с конфигурацией, созданной ранее. Также, есть много других настроек.

Linting JavaScript

Также есть linting для JavaScript который поможет сделать то же самое при помощи: eslint.

Установим пакет, только на этот раз, используем ярлык:

npm i -D eslint

Это тоже самое что:

npm install --save-dev eslint

После установки настраиваем некоторые основные правила для запуска кода, используя eslint. Чтобы запустить настройку, запустим следующее:

./node_modules/.bin/eslint --init

Отвечаем на вопросы, которые он задает. Это создаст новый файл в корне проекта, с которым eslint будет проверять код. Например:

{
  "env": {
    "browser": true,
    "es6": true
  },
  "extends": "eslint:recommended",
  "parserOptions": {
    "sourceType": "module"
  },
  "rules": {
    "indent": ["error", "tab"],
    "linebreak-style": ["error", "unix"],
    "quotes": ["error", "single"],
    "semi": ["error", "always"]
  }
}

Теперь давай добавим задачу lint в объект scripts package.json:

"scripts": {
  ...
  "lint": "eslint src/js"
}

Он ищет любые файлы JavaScript в папке src/js и запускает их в соответствии с конфигурацией, созданной ранее. Еще есть много других настроек.

Uglifying файлы JavaScript

Давай поработаем над объединением и минимизацией файлов JavaScript с помощью uglify-js. Для начала установим uglify-js:

npm i -D uglify-js

Затем мы можем настроить задачу uglify в package.json:

"scripts": {
  ...
  "uglify": "mkdir -p dist/js && uglifyjs src/js/*.js -m -o dist/js/app.js"
}

Одна из классных особенностей npm скриптов заключается в том, что они, по сути, являются псевдонимами для задач командной строки, которые мы хотим запускать снова и снова. Это означает, что мы можем использовать стандартный код командной строки прямо в скрипте. В этой задаче используются две стандартные функции командной строки: mkdir и &&.

Первая половина этой задачи - mkdir -p dist/js гласит: создай структуру папок (mkdir), но только если она не существует (флаг -p). Как только это завершится успешно, выполни команду uglifyjs. && - позволяет объединить несколько команд, последовательно выполняя каждую из них, если предыдущая команда выполнена успешно.

Вторая половина этой задачи говорит - uglifyjs - начинать со всех файлов JS (*.js) в директории src/js/, применять команду “mangle” (флаг -m) и выводить результат в dist/js/app.js. Вот документация для полного списка опций.

Давай обновим задачу uglify, чтобы создать сжатую версию dist/js/app.js. Добавим цепочку для другой команды uglifyjs и передадим флаг «compress» (-c):

"scripts": {
  ...
  "uglify": "mkdir -p dist/js && uglifyjs src/js/*.js -m -o dist/js/app.js && uglifyjs src/js/*.js -m -c -o dist/js/app.min.js"
}

Сжатие изображений

Давай теперь обратим внимание на сжатие изображений. Одна из лучших вещей, которую можно сделать для увеличения скорости страницы, - это уменьшить размер изображений.

Установим imagemin-cli:

npm i -D imagemin-cli

Imagemin хорош, так как он сжимает большинство типов изображений, включая GIF, JPG, PNG и SVG. Мы можем передать ему папку с изображениями, например:

"scripts": {
  ...
  "imagemin": "imagemin src/images/* --out-dir=dist/images",
}

Эта задача говорит imagemin найти и сжать все изображения в src/images/*, и поместить их в dist/images. Проверь документацию для всех доступных вариантов.

SVG Sprites

Популярность SVG возросла за последние несколько лет, и на это есть причины. Они четкие на всех устройствах, редактируются с помощью CSS и удобны для чтения с экрана. Однако, программное обеспечение для редактирования SVG обычно оставляет ненужный код. К счастью, svgo может помочь, удалив всё это.

Мы также можем автоматизировать процесс объединения и разбивки SVG-файлов в один SVG-файл (спрайт) (подробнее об этом здесь). Чтобы автоматизировать этот процесс, можно установить svg-sprite-generator.

npm i -D svgo svg-sprite-generator

После установки добавь задачу в объект scripts в package.json:

"scripts": {
  ...
  "icons": "svgo -f src/images/icons -o dist/images/icons && svg-sprite-generate -d dist/images/icons -o dist/images/icons/sprite.svg"
}

Команда icons выполнит три действия в зависимости от наличия двух директив &&. Сначала используем svgo, передавая папку SVG (флаг -f); это сожмет все SVG внутри папки. Во-вторых, создадим папку dist/images, если она ещё не существует (с помощью команды mkdir -p). И под конец, используем svg-sprite-generator, передавая ему папку SVG (флаг -d) и путь, по которому мы хотим вывести SVG-спрайт (флаг -o).

BrowserSync

BrowserSync может делать несколько вещей: запускать локальный сервер, автоматически вставлять обновленные файлы в любой подключенный браузер и синхронизировать щелчки, и прокрутки между браузерами. Установим и добавим задачу:

npm i -D browser-sync
"scripts": {
  ...
  "serve": "browser-sync start --server --files 'dist/css/*.css, dist/js/*.js'"
}

Задача BrowserSync запускает сервер (флаг --server), используя текущий путь в качестве корневого, по умолчанию. Флаг --files указывает BrowserSync на просмотр любого файла CSS или JS в папке dist; всякий раз, когда что-то там меняется, он автоматически вставляет измененные файлы на страницу.

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

Группировка задач

Со всеми вышеуказанными задачами мы сможем:

  • скомпилировать SCSS в CSS, автоматически добавить префиксы и stylelint;
  • lint и uglify JavaScript;
  • сжать изображения;
  • преобразовать папку SVG в один SVG-спрайт;
  • запустить локальный сервер и автоматически внести изменения в любой браузер, подключенный к серверу.

Объединение задач CSS

Давай добавим задачу, которая объединяет три задачи связанные с CSS (lint, обработка Sass и запуск Autoprefixer), что бы нам не нужно было запускать каждую из них по отдельности:

"scripts": {
  ...
  "build:css": "npm run lint:scss && npm run scss && npm run autoprefixer"
}

Когда запускаем npm run build:css, он сообщает командной строке - выполнить npm run lint:scss; после успешного завершения, он (&&) запустит npm run scss, а потом npm run autoprefixer.

Объединение задач JavaScript

Как и в случае с задачей build:css, можем объединить задачи JavaScript вместе, чтобы упростить их выполнение.

"scripts": {
  ...
  "build:js": "npm run lint && npm run uglify"
}

Теперь можем вызвать npm run build:js, чтобы подсветить ошибки через lint, объединить и минимизировать файлы JavaScript через uglify одной командой.

Объединение оставшихся задач

Мы можем сделать то же самое для задач с изображениями, а также для задачи, которая объединяет все задачи сборки в одну:

"scripts": {
  ...
  "build:images": "npm run imagemin && npm run icons",
  "build:all": "npm run build:css && npm run build:js && npm run build:images",
}

Наблюдая за изменениями

Одна из самых полезных вещей, которые мы можем сделать - это добавить задачи, которые следят за изменениями и запускаются автоматически при изменении файлов. Для этого я рекомендую использовать onchange. Установим как обычно:

npm i -D onchange
"scripts": {
  ...
  "watch:css": "onchange 'src/scss/*.scss' -- npm run build:css",
  "watch:js": "onchange 'src/js/*.js' -- npm run build:js",
  "watch:images": "onchange 'src/images/**/*' -- npm run build:images",
}

onchange ожидает путь в виде строки к файлам, за которыми мы собираемся наблюдать. Передадим исходные файлы scss, js и images для наблюдения. Команда, которую мы хотим выполнить, идет после -- и запускается каждый раз, когда файлы по указанному пути добавляются, изменяются или удаляются.

Ещё одна команда “watch”, завершающая процесс сборки скриптов npm

Установим ещё один пакет, parallelshell:

npm i -D parallelshell
"scripts": {
  ...
  "watch:all": "parallelshell 'npm run serve' 'npm run watch:css' 'npm run watch:js' 'npm run watch:images'"
}

parallelshell принимает несколько строк, которые передаем для запуска нескольких задач npm run.

Зачем использовать parallelshell для объединения нескольких задач, вместо использования &&, как в предыдущих примерах?

Трудность заключается в том, что && объединяет команды в цепочку и ожидает успешного завершения каждой команды, прежде чем начинать следующую. Однако, поскольку мы выполняем команды watch, которые никогда не заканчиваются, мы застряли бы в бесконечной петле.

Поэтому, использование parallelshell позволяет запускать несколько команд watch одновременно.

Эта задача запускает сервер с помощью BrowserSync через npm run serve. Затем она запускает команды наблюдения для файлов CSS, JavaScript и картинок. Каждый раз, когда изменяется любой из этих файловых расширений, задача наблюдения выполняет соответствующую задачу сборки; поскольку BrowserSync настроен на отслеживание изменений в папке dist, он автоматически добавляет новые файлы в любой браузер, связанный с его URL-адресом.

Также, вместо parallelshell можно использовать npm-run-all. Он позволят запускать задачи последовательно или параллельно. В данном случае скрипт будет выглядеть так:

"scripts": {
  ...
  "watch": "run-p serve watch:*",
}

При запуске npm run watch, команда запустит параллельно скрипт serve и все команды, начало которых соответствует паттерну watch:*. Если используем эту команду, тогда скрипт "watch:all" нужно убрать.

Другие полезные задачи

npm идёт с большим количеством встроенных задач. Давай напишем ещё одну задачу, используя один из этих встроенных скриптов.

"scripts": {
  ...
  "postinstall": "npm run watch:all"
}

postinstall запускается сразу после запуска npm install в командной строке. Это удобно, особенно при работе в командах; когда кто-то клонирует проект и запускает npm install, задача watch:all запустится немедленно. Она будет автоматически запускать сервер, открывать окно браузера и следить за изменениями в файлах.

Заключение

Я создал проект npm-lesson со всеми задачами, которые можно использовать в качестве отправной точки. Если у тебя есть вопросы, пожалуйста, напиши мне. Буду рад помочь!

Website, name & logo
Copyright © 2022. Alex Myzgin