Как настраивать сборку проекта. Часть 1: простая сборка CLI

В первой части мы разберем, как настроить простую сборку проекта с помощью CLI (Command Line Interface), то есть командной строки.

Простая сборка CLI

Для того, чтобы понять, как настраивать сборку, нужно сначала определиться, какие файлы у нас должны получиться на выходе. Чаще всего, это:

  • HTML
  • CSS
  • JS
  • Изображения
  • Прочие ресурсы (шрифты, аудиофайлы и т.д.)

HTML может собираться из:

  • HTML (то есть не собираться вообще)
  • Шаблонизаторов (Pug, Handlebars, Twig и т.д.)

CSS может собираться из:

  • CSS (то есть не собираться вообще)
  • Препроцессоров (Sass, Less, Stylus и т.д.)
  • PostCSS (с различными плагинами: autoprefixer, cssnano, postcss-preset-env и т.д.)

JS может собираться с помощью:

  • Babel (с различными плагинами: @babel/preset-env и т.д)
  • Webpack
  • Rollup

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

Command line interface

Рассмотрим, как настроить сборку с помощью CLI, то есть в командной строке.

Шаблоны

Возьмем в качестве примера популярный шаблонизатор Pug. У него есть отдельная утилита pug-cli.

Для настройки компилирования Pug в HTML установим следующий пакет:

npm install --save-dev pug-cli

В файле package.json пропишем задачу, которую назовем «build:pug»:

{
  "build:pug": "pug src/pug --out build --pretty"
}

src/pug — путь к директории с pug-файлами, которые необходимо скомпилировать.

build — директория, куда будут помещаться уже скомпилированные html-файлы.

Далее идут настройки. Полный список всевозможных настроек можно увидеть, введя команду:

pug --help

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

{
  "name": "project-title",
  "version": "1.0.0",
  "scripts": {
  "build:pug": "pug src/pug --out build --pretty"
  },
  "devDependencies": {
    "pug-cli": "^1.0.0-alpha6"
  }
}

Запуск задачи для компилирования Pug в HTML осуществляется таким образом:

npm run build:pug

Стили

Возьмем в качестве примера препроцессор Sass и плагин postcss-cli.

Сначала наши стили будут компилироваться из SCSS в CSS, а затем этот же выходной файл обработается PostCSS, и его плагин autoprefixer автоматически расставит префиксы для корректной работы стилей во всех современных браузерах.

Для подключения Sass необходимо установить следующий пакет:

npm install --save-dev node-sass

Чтобы установить postcss-cli и autoprefixer, воспользуемся командой:

npm install --save-dev postcss-cli autoprefixer

Далее необходимо создать файл конфигурации postcss.config.js, где будут подключаться необходимые PostCSS-плагины (в данном случает autoprefixer):

module.exports = {
  plugins: [
    require('autoprefixer')
  ]
};

В файле package.json пропишем задачу «build:scss», которая будет отвечать за компилирование SCSS в CSS:

{
  "build:scss": "node-sass src/scss --output build/css"
}

src/scss — директория, в которой будут располагаться .scss файлы. Обратите внимание, что главный файл у нас всегда один — main.scss. Только он будет компилироваться в .css. А остальные файлы стилей должны обязательно иметь название, начинающееся с нижнего подчеркивания и подключаться в главном main.scss с помощью @import. Пример:

build/css — директория для скомпилированных файлов стилей.

Также можно указать дополнительные опции. Посмотреть их полный список можно введя команду:

node-sass --help

Создадим дополнительную задачу, которая будет предназначена для постобработки получившегося на выходе файла плагином PostCSS:

{
  "build:css": "npm run build:scss && postcss build/css --replace"
}

То есть, плагин берет скомпилированный файл из build/css, обрабатывает его и перезаписывает.

Список дополнительных опций можно посмотреть в документации.

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

{
  "name": "project-title",
  "version": "1.0.0",
  "scripts": {
    "build:pug": "pug src/pug --out build --pretty",
    "build:scss": "node-sass src/scss --output build/css",
    "build:css": "npm run build:scss && postcss build/css --replace"
  },
  "devDependencies": {
    "autoprefixer": "^9.5.1",
    "node-sass": "^4.12.0",
    "postcss-cli": "^6.1.2",
    "pug-cli": "^1.0.0-alpha6"
  }
}

Для совместимости с браузерами нам необходимо создать файл .browserlistrc с содержимым:

defaults

Запуск задачи для компилирования SCSS в CSS:

npm run build:scss

Запуск задачи для обработки итогового файла PostCSS-ом:

npm run build:css

Скрипты

Скрипты будут компилироваться с помощью Rollup, который в свою очередь будет использовать Babel. Babel нужен для того, чтобы мы могли применять современные возможности JavaScript, поддерживающиеся не во всех браузерах.

Запускаем следующую команду для установки Rollup:

npm install --save-dev rollup

Для установки самого Babel, воспользуемся командой:

npm i --save-dev @babel/core @babel/preset-env

А следующая команда устанавливает пакеты Rollup и Babel:

npm i --save-dev rollup-plugin-babel rollup-plugin-node-resolve

rollup-plugin-babel — для связи Rollup с Babel. rollup-plugin-node-resolve — для импорта из node_modules

Затем необходимо создать файл .babelrc с кодом:

{
  "presets": [
    ["@babel/preset-env", {"modules": false}]
  ]
}

Далее нужно создать файл конфигурации rollup.config.js:

import resolve from 'rollup-plugin-node-resolve';
import babel from 'rollup-plugin-babel';

module.exports = {
  input: 'src/js/main.js',
  output: {
    file: 'build/js/main.js',
    format: 'iife'
  },
  plugins: [
    resolve(),
    babel({
      exclude: 'node_modules/**'
    }),
  ]
};

В input нужно указать путь к исходному JavaScript файлу.

В output задаем путь, где будет располагаться скомпилированный файл и формат вывода (amd, cjs, esm, iife или umd).

Далее создадим задачу, которую назовем «build:js»:

{
  "build:js": "rollup -c"
}

-c означает, что используется конфигурационный файл rollup.config.js. Список всевозможных опций можно посмотреть по ссылке.

Обратите внимание, что в качестве исходного файла, мы указываем только путь к главному файлу main.js, который будет компилироваться. Остальные скрипты должны подключаться в main.js с помощью import. Пример:

Файл package.json в данный момент имеет вид:

{
  "name": "project-title",
  "version": "1.0.0",
  "scripts": {
  "build:pug": "pug src/pug --out build --pretty",
  "build:scss": "node-sass src/scss --output build/css",
  "build:css": "npm run build:scss && postcss build/css --replace",
  "build:js": "rollup -c"
  },
  "devDependencies": {
    "@babel/core": "^7.4.4",
    "@babel/preset-env": "^7.4.4",
    "autoprefixer": "^9.5.1",
    "node-sass": "^4.12.0",
    "postcss-cli": "^6.1.2",
    "pug-cli": "^1.0.0-alpha6",
    "rollup": "^1.11.3",
    "rollup-plugin-babel": "^4.3.2",
    "rollup-plugin-node-resolve": "^4.2.3"
  }
}

Запуск задачи для компилирования JavaScript осуществляется таким образом:

npm run build:js

Ресурсы

Для изображений у нас будет папка images, а для прочих ресурсов, таких как шрифты, аудиофайлы, фавиконы и т.д. — директория resources. В этих папках может быть любое количество вложенных директорий и файлов.

Копированием будет заниматься специальный пакет cpx:

npm install --save-dev cpx

Пропишем в package.json задачу для копирования изображений из src/images в папку build, которую назовем «build:images»:

{
  "build:images": "cpx src/images/**/* build/images --verbose"
}

Для копирования прочих ресурсов из src/resources в build создадим задачу:

{
  "build:resources": "cpx src/resources/**/{*,.*} build --verbose"
}

Обратите внимание, что учитываются также и файлы, начинающиеся с точки.

Список возможных опций можно посмотреть в соответствующем разделе документации.

Файл package.json в данный момент имеет следующий вид:

{
  "name": "project-title",
  "version": "1.0.0",
  "scripts": {
    "build:pug": "pug src/pug --out build --pretty",
    "build:scss": "node-sass src/scss --output build/css",
    "build:css": "npm run build:scss && postcss build/css --replace",
    "build:js": "rollup -c",
    "build:images": "cpx src/images/**/* build/images --verbose",
    "build:resources": "cpx src/resources/**/{*,.*} build --verbose"
  },
  "devDependencies": {
    "@babel/core": "^7.4.4",
    "@babel/preset-env": "^7.4.4",
    "autoprefixer": "^9.5.1",
    "cpx": "^1.5.0",
    "node-sass": "^4.12.0",
    "postcss-cli": "^6.1.2",
    "pug-cli": "^1.0.0-alpha6",
    "rollup": "^1.11.3",
    "rollup-plugin-babel": "^4.3.2",
    "rollup-plugin-node-resolve": "^4.2.3"
  }
}

Запуск задачи для копирования изображений:

npm run build:images

Запуск задачи для копирования прочих ресурсов:

npm run build:resources

Общий build

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

Для параллельного запуска задач в консоли, необходимо установить пакет npm-run-all:

npm install --save-dev npm-run-all

После чего создадим задачу «build» в package.json:

{
  "build": "run-p build:*"
}

Файл package.json сейчас выглядит так:

{
  "name": "project-title",
  "version": "1.0.0",
  "scripts": {
    "build:pug": "pug src/pug --out build --pretty",
    "build:scss": "node-sass src/scss --output build/css",
    "build:css": "npm run build:scss && postcss build/css --replace",
    "build:js": "rollup -c",
    "build:images": "cpx src/images/**/* build/images --verbose",
    "build:resources": "cpx src/resources/**/{*,.*} build --verbose",
    "build": "run-p build:*"
  },
  "devDependencies": {
    "@babel/core": "^7.4.4",
    "@babel/preset-env": "^7.4.4",
    "autoprefixer": "^9.5.1",
    "cpx": "^1.5.0",
    "node-sass": "^4.12.0",
    "npm-run-all": "^4.1.5",
    "postcss-cli": "^6.1.2",
    "pug-cli": "^1.0.0-alpha6",
    "rollup": "^1.11.3",
    "rollup-plugin-babel": "^4.3.2",
    "rollup-plugin-node-resolve": "^4.2.3"
  }
}

Запуск задачи для общего билда:

npm run build

Запуск локального сервера

Используя пакет browser-sync, можно запустить локальный сервер для разработки. Установим его с помощью команды:

npm install --save-dev browser-sync

Создадим задачу в package.json:

{
  "serve": "browser-sync start --server build --files build --no-notify"
}

Эта команда запускает сервер, используя все файлы из директории build. Список всех параметров можно посмотреть в документации.

package.json на данном этапе выглядит так:

{
  "name": "project-title",
  "version": "1.0.0",
  "scripts": {
    "build:pug": "pug src/pug --out build --pretty",
    "build:scss": "node-sass src/scss --output build/css",
    "build:css": "npm run build:scss && postcss build/css --replace",
    "build:js": "rollup -c",
    "build:images": "cpx src/images/**/* build/images --verbose",
    "build:resources": "cpx src/resources/**/{*,.*} build --verbose",
    "build": "run-p build:*",
    "serve": "browser-sync start --server build --files build --no-notify"
  },
  "devDependencies": {
    "@babel/core": "^7.4.4",
    "@babel/preset-env": "^7.4.4",
    "autoprefixer": "^9.5.1",
    "browser-sync": "^2.26.5",
    "cpx": "^1.5.0",
    "node-sass": "^4.12.0",
    "npm-run-all": "^4.1.5",
    "postcss-cli": "^6.1.2",
    "pug-cli": "^1.0.0-alpha6",
    "rollup": "^1.11.3",
    "rollup-plugin-babel": "^4.3.2",
    "rollup-plugin-node-resolve": "^4.2.3"
  }
}

Запустить задачу для создания сервера можно следующей командой:

npm run serve

Watch

Дальше нам хотелось бы, чтобы при изменении/добавлении каких-либо файлов, сборка на это реагировала, и сама их перекомпилировала. Для этого нужно создать «вотчеры» (watch).

Пропишем в package.json следующие задачи:

{
  "start:pug": "npm run build:pug -- --watch",
  "start:scss": "npm run build:scss -- --watch",
  "start:js": "npm run build:js -- --watch",
  "start:images": "npm run build:images -- --watch",
  "start:resources": "npm run build:resources -- --watch"
}

Всё, что делают эти команды — просто запускают уже существующие задачи с параметром --watch для отслеживания изменений в файлах.

Также создадим общую задачу «start», которая будет параллельно запускать все «вотчи»:

{
  "start": "run-p start:*"
}

И добавим в эту команду запуск задачи «serve», которую мы настраивали в предыдущем разделе:

"start": "run-p start:* serve"

В итоге файл package.json должен выглядит так:

{
  "name": "project-title",
  "version": "1.0.0",
  "scripts": {
    "build:pug": "pug src/pug --out build --pretty",
    "build:scss": "node-sass src/scss --output build/css",
    "build:css": "npm run build:scss && postcss build/css --replace",
    "build:js": "rollup -c",
    "build:images": "cpx src/images/**/* build/images --verbose",
    "build:resources": "cpx src/resources/**/{*,.*} build --verbose",
    "build": "run-p build:*",
    "start:pug": "npm run build:pug -- --watch",
    "start:scss": "npm run build:scss && npm run build:scss -- --watch",
    "start:js": "npm run build:js -- --watch",
    "start:images": "npm run build:images -- --watch",
    "start:resources": "npm run build:resources -- --watch",
    "start": "run-p start:* serve",
    "serve": "browser-sync start --server build --files build --no-notify"
  },
  "devDependencies": {
    "@babel/core": "^7.4.4",
    "@babel/preset-env": "^7.4.4",
    "autoprefixer": "^9.5.1",
    "browser-sync": "^2.26.5",
    "cpx": "^1.5.0",
    "node-sass": "^4.12.0",
    "npm-run-all": "^4.1.5",
    "postcss-cli": "^6.1.2",
    "pug-cli": "^1.0.0-alpha6",
    "rollup": "^1.11.3",
    "rollup-plugin-babel": "^4.3.2",
    "rollup-plugin-node-resolve": "^4.2.3"
  }
}

Таким образом, шаг за шагом можно усовершенствовать сборку проекта, расширяя ее возможности, путем подключения необходимых плагинов. В данной статье мы разобрали примеры, как применять и настраивать в своей сборке одни из часто используемых библиотек.

Во второй части мы разберем примеры, как настраивать сборку проекта с помощью Gulp.