Commit c8639409 authored by Nikolay Gromov's avatar Nikolay Gromov

Допилил новый автомат (без библиотеки компонентов)

parent cc1d9053
# перечисление игнорируемых stylelint-ом файлов
src/scss/blocks-lib.scss
src/scss/style.scss
src/blocks/sprite-png/sprite-png.scss
src/blocks/page/page.scss
src/blocks/blocks-library/blocks-library.scss
# Стартовый проект с gulp ![Test Status](https://travis-ci.org/nicothin/NTH-start-project.svg?branch=master) [![devDependencies Status](https://david-dm.org/nicothin/NTH-start-project/dev-status.svg)](https://david-dm.org/nicothin/NTH-start-project?type=dev) [![dependencies Status](https://david-dm.org/nicothin/NTH-start-project/status.svg)](https://david-dm.org/nicothin/NTH-start-project)
## Структура проекта
```bash
......@@ -8,12 +9,13 @@ src/
blocks/ # Блоки
favicon/ # Фавиконки
fonts/ # Шрифты
img/ # Изображения, не принадлежащие ни одному БЭМ-блоку
img/ # Доп. изображения
pages/ # Страницы проекта
pug/ # Служебные pug-файлы (шаблоны, примеси)
pug/ # Служебные pug-файлы
scss/ # Служебные стилевые файлы
```
## Как это работает
При `npm start` (запускается дефолтная задача gulp):
......@@ -25,149 +27,57 @@ src/
5. Генерируются спрайты, в папку сборки копируются доп. файлы и картинки.
5. Записывается `./src/scss/style.scss`, в котором:
- Импорты файлов из `config.js#addStyleBefore`.
- Импорты файлов БЭМ-блоков, использующихся на проекте (если scss-файлы существуют).
- Импорты файлов БЭМ-блоков, использующихся на проекте (если их scss-файлы существуют).
- Импорты файлов из `config.js#addStyleAfter`.
5. Записывается `./src/js/entry.js`, в котором:
- `require` файлов из `config.js#addJsBefore`.
- `require` файлов БЭМ-блоков, использующихся на проекте (если их js-файлы существуют).
- `require` файлов из `config.js#addJsAfter`.
5. Компилируются и обрабатываются PostCSS-ом стили (`./src/scss/style.scss`), собирается скрипт (`./src/js/entry.js`).
5. Запускается локальный сервер и слежение за файлами для пересборки.
## Прочие команды
`npm run build` — Сборка со сжатыми css- и js-файлами
## Как это работает
При `npm start` (запускается дефолтная задача gulp):
1. Создаётся глобальная переменная `config` с настройками (из `./config.js`).
2. Очищается папка сборки (`./build/`).
3. Записывается `./src/pug/mixins.pug` и компилируются в html файлы страниц (`./src/pages/`). **В процессе обновляется список блоков** (`config.blocks`), если есть какие-то новые блоки, записывается файл `./config.js`.
4. Записывается `./src/scss/style.scss`, в котором:
- Импорты файлов из `config.addStyleBefore`.
- Импорты файлов БЭМ-блоков, использующихся на проекте (из `config.blocks`, но только если сами scss-файлы существуют в папках блоков).
- Импорты файлов из `config.addStyleAfter`.
5. Записывается `./src/js/entry.js`, в котором:
- Подключения файлов из `config.addJsBefore`.
- Подключения js-файлов js-файлов БЭМ-блоков, использующихся на проекте (из `config.blocks`, но только если сами js-файлы существуют в папках блоков).
- Подключения файлов из `config.addJsAfter`.
6. Компилируется и обрабатывается PostCSS-ом Sass, собирается JS.
7. Копируются все статичные файлы (список в `config.copyAssets`) и картинки БЭМ-блоков (подпапка `img/` в папке используемого блока).
8. Запускается слежение и сервер.
9. PROFIT
### Изменение в папке, откуда собирается растровый спрайт
### Изменение в папке, откуда собирается векторный спрайт
### Изменение в папке добавочных картинок
### Изменение в папке добавочных файлов
### Изменение файла `config.json`
добавить возможность включать в сборку блоки, не найденные на странице
## СТАРОЕ
---
<table>
<thead>
<tr>
<th>Команда</th>
<th>Результат</th>
</tr>
</thead>
<tbody>
<tr>
<td width="22%"><code>npm i</code></td>
<td>Установить зависимости</td>
</tr>
<tr>
<td><code>npm start</code></td>
<td>Запустить сборку, сервер и слежение за файлами</td>
</tr>
<tr>
<td><code>npm start ЗАДАЧА</code></td>
<td>Запустить задачу с названием ЗАДАЧА (список задач в <code>./gulpfile.js</code>)</td>
</tr>
<tr>
<td><code>npm run img-opt</code></td>
<td>Оптимизация изображений из папки <code>./src/img</code></td>
</tr>
<tr>
<td><code>npm run build</code></td>
<td>Сборка проекта без карт кода (сжатый вид, как результат работы)</td>
</tr>
<tr>
<td><code>npm run deploy</code></td>
<td>Сборка проекта без карт кода и отправка содержимого папки сборки на github-pages</td>
</tr>
<tr>
<td><code>npm run test:style</code></td>
<td>Проверка стилевой составляющей проекта <a href="https://stylelint.io/">stylelint</a></td>
</tr>
<tr>
<td><code>npm start test:pug</code></td>
<td>Проверка pug-файлов проекта <a href="https://github.com/nicothin/gulp-pug-lint">форкнутым gulp-pug-lint</a></td>
</tr>
</tbody>
</table>
Предполагается, что все команды вы выполняете в bash (для OSX и Linux это самый обычный встроенный терминал, для Windows это, к примеру, Git Bash). В Windows установку пакетов (`npm i`) нужно выполять в терминале, запущенном от имени администратора.
`npm run deploy` — Сборка проекта без карт кода и отправка содержимого папки сборки на gh-pages
## Как начать новый проект c этим репозиторием
1. Клонировать этот репозиторий в новую папку (`git clone https://github.com/nicothin/NTH-start-project.git new-project`, см. [шпаргалку](https://github.com/nicothin/web-development/tree/master/git#%D0%9A%D0%BB%D0%BE%D0%BD%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5-%D1%80%D0%B5%D0%BF%D0%BE%D0%B7%D0%B8%D1%82%D0%BE%D1%80%D0%B8%D1%8F)) и зайти в неё (`cd new-project`).
2. Стереть историю разработки этого репозитория (`rm -rf .git`), инициировать новый (`git init`), создать удалённый репозиторий и привязать его (`git remote add origin АДРЕС`, см. [шпаргалку](https://github.com/nicothin/web-development/tree/master/git#%D0%A3%D0%B4%D0%B0%D0%BB%D1%91%D0%BD%D0%BD%D1%8B%D0%B5-%D1%80%D0%B5%D0%BF%D0%BE%D0%B7%D0%B8%D1%82%D0%BE%D1%80%D0%B8%D0%B8)).
3. Отредактировать `README.md`, `package.json` (название проекта, автор, лицензия, сторонние пакеты и пр.) и `projectConfig.json` (нужные на проекте блоки, сторонние пакеты). Удалив пакет из `package.json`, не забудьте убрать его из сборки в `projectConfig.json`.
3. Отредактировать `LICENSE`, `README.md`, `package.json` (название проекта, автор, лицензия, необходимые пакеты и пр.) и `config.js` (нужные на проекте блоки, сторонние пакеты). Удалив пакет из `package.json`, не забудьте убрать его из `config.js`.
4. Установить зависимости (`npm i`).
5. Запустить сервер разработки (`npm start`).
Если при вызове `npm start` ничего не происходит, смотрите `./projectConfig.json` (вероятно, он содержит синтаксическую ошибку, проверить можно [линтером](http://jsonlint.com/)).
## Парадигма
- Именование классов по БЭМ, разметка в [pug](https://pugjs.org/) и стилизация [Sass](http://sass-lang.com/). См. [Как работать с CSS-препроцессорами и БЭМ](http://nicothin.github.io/idiomatic-pre-CSS/)
- Каждый БЭМ-блок в своей папке внутри `./src/blocks/`.
- Список использованных в проекте БЭМ-блоков и доп. файлов указан в `./projectConfig.json`. Это главный конфигурационный файл проекта.
- Каждый БЭМ-блок в своей папке внутри `src/blocks/`.
- Список использованных в проекте доп. файлов указан в `config.js`.
- Есть глобальные файлы: стилевые (стили печати), js (по умолчанию пуст), шрифты, картинки.
- Диспетчер подключения стилей `./src/scss/style.scss` генерируется автоматически при старте любой gulp-задачи (на основе данных из `./projectConfig.json`).
- Список pug-примесей `./src/pug/mixins.pug` генерируется автоматически при старте любой gulp-задачи (на основе данных из `./projectConfig.json`), содержит `include` всех существующих pug-файлов блоков (pug-файл блока не является обязательным).
- Перед созданием коммита запускается проверка стилевых файлов, входящих в коммит и всех pug-файлов. При наличии ошибок коммит не происходит (ошибки будут выведены в терминал).
- Есть механизм быстрого создания нового блока: `node createBlock.js new-block` (создаёт файлы, папки, прописывает блок в `./projectConfig.json`).
- Список pug-примесей `src/pug/mixins.pug` генерируется автоматически, содержит `include` всех существующих pug-файлов блоков.
- Диспетчер подключения стилей `src/scss/style.scss` генерируется автоматически.
- Входная точка обработки js (`src/js/entry.js`) генерируется автоматически.
- Перед созданием коммита запускается проверка файлов, входящих в коммит. При наличии ошибок коммит не происходит, ошибки будут выведены в терминал.
- Есть механизм быстрого создания нового блока: `node createBlock.js new-block` (создаёт файлы, папки).
## Разметка
Используется [pug](https://pugjs.org/api/getting-started.html). HTML никак не обрабатывается.
По умолчанию используются [наследование шаблонов](https://pugjs.org/language/inheritance.html) — все страницы (см. `./src/index.pug`) являются расширениями шаблонов, в страницах описывается только содержимое «шапки», «подвала» и контентной области посредством [блоков](https://pugjs.org/language/inheritance.html#block-append-prepend).
Каждый блок (в `./src/blocks/`) может содержать pug-файл с одноименной примесью, который будет взят includ-ом в файле `./src/pug/mixins.pug` (формируется автоматически на основании списка используемых на проекте блоков).
По умолчанию используются [наследование шаблонов](https://pugjs.org/language/inheritance.html) — все страницы (см. `src/index.pug`) являются расширениями шаблонов, в страницах описывается только содержимое «шапки», «подвала» и контентной области посредством [блоков](https://pugjs.org/language/inheritance.html#block-append-prepend).
Каждый блок (в `src/blocks/`) может содержать pug-файл с одноименной примесью, который будет взят includ-ом в файле `src/pug/mixins.pug`.
## Стили
Файл-диспетчер подключений (`./src/scss/style.scss`) формируется автоматически на основании указанных в `./projectConfig.json` блоков и доп. файлов. Писать в `./src/scss/style.scss` что-либо руками бессмысленно: при старте автоматизации файл будет перезаписан.
Диспетчер подключений (`src/scss/style.scss`) формируется автоматически, писать в него что-либо руками бессмысленно: при старте автоматизации файл будет перезаписан.
Используемый постпроцессинг:
......@@ -175,9 +85,9 @@ src/
2. [css-mqpacker](https://github.com/hail2u/node-css-mqpacker)
3. [postcss-import](https://github.com/postcss/postcss-import)
4. [postcss-inline-svg](https://github.com/TrySound/postcss-inline-svg)
5. [gulp-cleancss](https://github.com/mgcrea/gulp-cleancss) (только в режиме сборки без карт кода)
6. [postcss-object-fit-images](https://github.com/ronik-design/postcss-object-fit-images) (в паре с [полифилом](https://github.com/bfred-it/object-fit-images))
### Модульная сетка (flexbox)
По умолчанию в сборку берётся [файл с примесями](https://github.com/nicothin/NTH-start-project/blob/master/src/scss/mixins/grid-mixins.scss), возвращающими правила модульной сетки. Никаких селекторов в CSS не добавляет, нужно писать семантические селекторы и вызывать примеси, передавая им настройки сетки. Настройки по умолчанию вынесены в переменные (`$grid-columns: 12;` и `$grid-gutter-width: 30px;`).
......@@ -185,227 +95,43 @@ src/
Посмотреть примеры и попробовать вживую можно в [этом примере с codepen.io](https://codepen.io/nicothin/pen/aJEOwE?editors=1100).
## Блоки
Каждый блок лежит в `./src/blocks/` в своей папке. Каждый блок — как минимум, папка и одноимённые scss- и pug-файл.
Каждый блок лежит в `src/blocks/` в своей папке.
Возможное содержимое блока:
```bash
demo-block/ # Папка блока.
img/ # Изображения, используемые блоком и обрабатываемые автоматикой сборки.
bg-img/ # Изображения для использования в стилях (не обрабатываются автоматикой сборки).
img/ # Изображения, используемые блоком и обрабатываемые автоматикой сборки.
demo-block.pug # Разметка (pug-примесь, отдающая разметку блока, описание API примеси).
demo-block.scss # Стилевой файл блока.
demo-block.js # js-файл блока.
demo-block--mod.scss # Отдельный стилевой файл БЭМ-модификатора блока.
demo-block--mod.js # js-файл для отдельного БЭМ-модификатора блока.
readme.md # Описание для документации, подсказки.
```
## Подключение блоков
Список используемых блоков и доп. файлов указан в `./projectConfig.json`. Список файлов и папок, взятых в обработку можно увидеть в терминале, если раскомментировать строку `console.log(lists);` в `gulpfile.js`.
**ВНИМАНИЕ!** `./projectConfig.json` — это JSON. Это строгий синтаксис, у последнего элемента в любом контексте не должно быть запятой в конце строки.
### `blocks`
Объект с блоками, используемыми на проекте. Каждый блок — отдельная папка с файлами, по умолчанию лежат в `./src/blocks/`.
Каждое подключение блока — массив, который можно оставить пустым или указать файлы элементов или модификаторов, если они написаны в виде отдельных файлов. В обоих случаях в обработку будут взяты одноименные стилевые файлы, pug-файл, js-файлы и картинки из папки `img/` блока.
Пример, подключающий 3 блока:
```
"blocks": {
"page": [],
"page-header": [],
"page-footer": []
}
```
### `addCssBefore`
Массив с дополнительными стилевыми файлами, которые будут взяты в компиляцию ПЕРЕД стилевыми файлами блоков.
Пример, берущий в компиляцию переменные, примеси, функции и один дополнительный файл из папки зависимостей (он будет преобразован в css-импорт, который при постпроцессинге ([postcss-import](https://github.com/postcss/postcss-import)) будет заменен на содержимое файла).
```
"addCssBefore": [
"./src/scss/variables.scss",
"./src/scss/mixins.scss",
"./src/scss/functions.scss",
"../../node_modules/owl.carousel/dist/assets/owl.carousel.css"
],
```
### `addCssAfter`
Массив с дополнительными стилевыми файлами, которые будут взяты в компиляцию ПОСЛЕ стилевых файлов блоков.
```
"addCssAfter": [
"./src/scss/print.scss"
],
```
### `singleCompiled`
Массив стилевых файлов, которые будут скомпилированы независимо.
Пример: указанный файл будет скомпилирован в папку сборки как `blocks-library.css`
```
"singleCompiled": [
"./src/scss/blocks-library.scss"
],
```
### `addJsBefore`
Массив js-файлов, которые будут взяты в обработку (конкатенация/сжатие) ПЕРЕД js-файлами блоков.
Пример, добавляющий в список обрабатываемых js-файлов несколько зависимостей:
```
"addJsBefore": [
"./node_modules/jquery/dist/jquery.min.js",
"./node_modules/jquery-migrate/dist/jquery-migrate.min.js",
"./node_modules/nouislider/distribute/nouislider.js"
],
```
### `addJsAfter`
Массив js-файлов, которые будут взяты в обработку (конкатенация/сжатие) ПОСЛЕ js-файлов блоков.
Пример, добавляющий в конец списка обрабатываемых js-файлов глобальный скрипт.
```
"addJsAfter": [
"./src/js/global-script.js"
],
```
### `addImages`
Массив дополнительных изображений, добавляемый ПЕРЕД массивом изображений из блоков (внимание: при совпадении имен файлов, файлы из блоков имеют более высокий приоритет и затрут файлы из этого массива).
```
"addImages": [
"./src/img/*.{jpg,jpeg,gif,png,svg,ico}"
],
```
### `copiedCss`
Массив css-файлов, которые копируются в папку сборки, подпапку `css/`
### `copiedJs`
Массив js-файлов, которые копируются в папку сборки, подпапку `js/`
### Пример `./projectConfig.json`
```json
{
"blocks": {
"page-header": [],
"page-footer": [
"__extra-element",
"--extra-modifier"
]
},
"addCssBefore": [
"./src/scss/variables.scss"
],
"addCssAfter": [
"./src/scss/print.scss"
],
"singleCompiled": [],
"addJsBefore": [
"./node_modules/jquery/dist/jquery.min.js",
"./node_modules/jquery-migrate/dist/jquery-migrate.min.js"
],
"addJsAfter": [
"./src/js/global-script.js"
],
"addImages": [
"./src/img/*.{jpg,jpeg,gif,png,svg}"
],
"copiedCss": [],
"copiedJs": [],
"dirs": {
"srcPath": "./src/",
"buildPath": "./build/",
"blocksDirName": "blocks"
}
}
```
В результате в обработку будут взяты (в указанной последовательности):
```bash
css:
[ './src/scss/variables.scss',
'./src/blocks/page-header/page-header.scss',
'./src/blocks/page-footer/page-footer.scss',
'./src/blocks/page-footer/page-footer__extra-element.scss',
'./src/blocks/page-footer/page-footer--extra-modifier.scss',
'./src/scss/print.scss' ],
js:
[ './node_modules/jquery/dist/jquery.min.js',
'./node_modules/jquery-migrate/dist/jquery-migrate.min.js',
'./src/blocks/page-header/page-header.js',
'./src/blocks/page-footer/page-footer.js',
'./src/blocks/page-footer/page-footer__extra-element.js',
'./src/blocks/page-footer/page-footer--extra-modifier.js',
'./src/js/global-script.js' ],
img:
[ './src/img/*.{jpg,jpeg,gif,png,svg}',
'./src/blocks/page-header/img/*.{jpg,jpeg,gif,png,svg}',
'./src/blocks/page-footer/img/*.{jpg,jpeg,gif,png,svg}' ]
pug:
[ './src/blocks/page-header/page-header.pug',
'./src/blocks/page-footer/page-footer.pug' ]
```
Если блок (css-класс блока) использован в разметке, то блок будет взят в сборку.
Если нужно взять в сборку блок без использования его класса в разметке, нужно упомянуть блок в `config.js#alwaysAddBlocks`.
## Удобное создание нового блока
Предусмотрена команда для быстрого создания файловой структуры нового блока. По умолчанию создаются: scss- и pug-файл, `readme.md` блока и его подпапки `img` и `bg-img`
Предусмотрена команда для быстрого создания файловой структуры нового блока. По умолчанию создаются: scss-файл и подпапки `img`, `bg-img`
```bash
# формат: node createBlock.js ИМЯБЛОКА [доп. расширения через пробел]
node createBlock.js block-test # создаст папку блока, block-test.pug, block-test.scss, подпапки img/ и bg-img/ для этого блока
node createBlock.js block-test # создаст папку блока, block-test.scss, подпапки img/ и bg-img/ для этого блока
node createBlock.js block-test pug js # создаст папку блока, block-test.scss, block-test.pug, block-test.js, подпапки img/ и bg-img/ для этого блока
```
Если блок уже существует, файлы не будут затёрты, но создадутся те файлы, которые ещё не существуют.
## Назначение папок
```bash
build/ # Папка сборки, здесь работает сервер автообновлений.
src/ # Исходные файлы.
blocks/ # - блоки проекта.
css/ # - добавочные css-файлы (нужно подключить в copiedCss, иначе игнорируются).
favicon/ # - файлы для фавиконок и смежных технологий.
fonts/ # - шрифты проекта (будут автоматически скопированы в папку сборки).
img/ # - добавочные или общие для нескольких блоков картинки (нужно подключить в addImages, иначе игнорируются).
js/ # - добавочные js-файлы (нужно подключить в addJsBefore, addJsAfter или copiedJs, иначе игнорируются).
pug/ # - примеси, шаблоны pug.
scss/ # - стили (файл style.scss скомпилируется, прочие нужно подключить в addCssBefore, addCssAfter или singleCompiled, иначе игнорируются).
index.pug # - главная страница проекта.
blocks-demo.pug # - библиотека блоков.
```
## Нравится проект?
Ставьте звезду в верхнем правом углу и/или [угостите меня кофе](https://money.yandex.ru/to/41001252765094), переведя сколь угодно символическую сумму.
......@@ -21,7 +21,7 @@ let config =
],
"addJsBefore": [],
"addJsAfter": [
"script.js"
"./script.js"
],
"addAssets": {
"src/img/demo-avatar-*": "img/",
......
......@@ -5,19 +5,19 @@
// Использование: node createBlock.js [имя блока] [доп. расширения через пробел]
const fs = require('fs');
const projectConfig = require('./projectConfig.json');
const projectConfig = require('./config.js');
const dirs = projectConfig.dirs;
const dir = projectConfig.dir;
const mkdirp = require('mkdirp');
const blockName = process.argv[2]; // получим имя блока
const defaultExtensions = ['scss', 'md', 'pug', 'img', 'bg-img']; // расширения по умолчанию
const extensions = uniqueArray(defaultExtensions.concat(process.argv.slice(3))); // добавим введенные при вызове расширения (если есть)
const blockName = process.argv[2];
const defaultExtensions = ['scss', 'img', 'bg-img']; // расширения по умолчанию
const extensions = uniqueArray(defaultExtensions.concat(process.argv.slice(3)));
// Если есть имя блока
if (blockName) {
const dirPath = `${dirs.srcPath + dirs.blocksDirName}/${blockName}/`; // полный путь к создаваемой папке блока
mkdirp(dirPath, (err) => { // создаем
const dirPath = `${dir.blocks}${blockName}/`; // полный путь к создаваемой папке блока
mkdirp(dirPath, (err) => { // создаем
// Если какая-то ошибка — покажем
if (err) {
console.error(`[NTH] Отмена операции: ${err}`);
......@@ -25,45 +25,31 @@ if (blockName) {
// Нет ошибки, поехали!
else {
console.log(`[NTH] Создание папки ${dirPath} (если отсутствует)`);
console.log(`[NTH] Создание папки: ${dirPath} (если отсутствует)`);
// Обходим массив расширений и создаем файлы, если они еще не созданы
extensions.forEach((extension) => {
const filePath = `${dirPath + blockName}.${extension}`; // полный путь к создаваемому файлу
let fileContent = ''; // будущий контент файла
let fileCreateMsg = ''; // будущее сообщение в консоли при создании файла
let fileContent = ''; // будущий контент файла
let fileCreateMsg = ''; // будущее сообщение в консоли при создании файла
// Если это SCSS
if (extension === 'scss') {
fileContent = `// В этом файле должны быть стили для БЭМ-блока ${blockName}, его элементов,\n// модификаторов, псевдоселекторов, псевдоэлементов, @media-условий...\n// Очередность: http://nicothin.github.io/idiomatic-pre-CSS/#priority\n\n.${blockName} {\n\n $block-name: &; // #{$block-name}__element\n}\n`;
// fileCreateMsg = '';
// Добавление импорта файла в диспетчер подключений, если он есть и в нем есть импорты
// TODO
}
// Если это HTML
else if (extension === 'html') {
fileContent = `<div class="${blockName}">content</div>\n`;
}
// Если это JS
else if (extension === 'js') {
fileContent = `// document.addEventListener(\'DOMContentLoaded\', function(){});\n// (function(){\n// код\n// }());\n`;
}
// Если это md
else if (extension === 'js') {
else if (extension === 'md') {
fileContent = '';
}
// Если это pug
else if (extension === 'pug') {
fileContent = `//- Все примеси в этом файле должны начинаться c имени блока (${blockName})\n\nmixin ${blockName}(text, mods)\n\n //- Принимает:\n //- text {string} - текст\n //- mods {string} - список модификаторов\n //- Вызов:\n +${blockName}('Текст', 'some-mod')\n\n -\n // список модификаторов\n var allMods = '';\n if(typeof(mods) !== 'undefined' && mods) {\n var modsList = mods.split(',');\n for (var i = 0; i < modsList.length; i++) {\n allMods = allMods + ' ${blockName}--' + modsList[i].trim();\n }\n }\n\n .${blockName}(class=allMods)&attributes(attributes)\n .${blockName}__inner!= text\n`;
// Добавление примеси файл примесей, если он есть и в нем есть подключение примесей
// TODO
fileContent = `//- Все примеси в этом файле должны начинаться c имени блока (${blockName})\n\nmixin ${blockName}(text, mods)\n\n //- Принимает:\n //- text {string} - текст\n //- mods {string} - список модификаторов\n //- Вызов:\n +${blockName}('Текст', 'some-mod')\n\n -\n // список модификаторов\n var allMods = '';\n if(typeof(mods) !== 'undefined' && mods) {\n var modsList = mods.split(',');\n for (var i = 0; i < modsList.length; i++) {\n allMods = allMods + ' ${blockName}--' + modsList[i].trim();\n }\n }\n\n .${blockName}(class=allMods)&attributes(attributes)\n .${blockName}__inner!= text\n block\n`;
}
// Если нужна подпапка для картинок
else if (extension === 'img') {
const imgFolder = `${dirPath}img/`;
if (fileExist(imgFolder) === false) {
......@@ -76,7 +62,6 @@ if (blockName) {
}
}
// Если нужна подпапка для необрабатываемых картинок
else if (extension === 'bg-img') {
const imgFolder = `${dirPath}bg-img/`;
if (fileExist(imgFolder) === false) {
......@@ -89,7 +74,6 @@ if (blockName) {
}
}
// Создаем файл, если он еще не существует
if (fileExist(filePath) === false && extension !== 'img' && extension !== 'bg-img' && extension !== 'md') {
fs.writeFile(filePath, fileContent, (err) => {
if (err) {
......@@ -117,38 +101,23 @@ if (blockName) {
}
});
// Добавим созданный блок в projectConfig.json
let hasThisBlock = false;
for (const block in projectConfig.blocks) {
if (block === blockName) {
hasThisBlock = true;
break;
}
}
if (!hasThisBlock) {
projectConfig.blocks[blockName] = [];
const newPackageJson = JSON.stringify(projectConfig, '', 2);
fs.writeFileSync('./projectConfig.json', newPackageJson);
console.log('[NTH] Подключение блока добавлено в projectConfig.json');
}
}
});
} else {
console.log('[NTH] Отмена операции: не указан блок');
}
// Оставить в массиве только уникальные значения (убрать повторы)
function uniqueArray(arr) {
const objectTemp = {};
for (let i = 0; i < arr.length; i++) {
const str = arr[i];
objectTemp[str] = true; // запомнить строку в виде свойства объекта
objectTemp[str] = true;
}
return Object.keys(objectTemp);
}
// Проверка существования файла
function fileExist(path) {
const fs = require('fs');
try {
......
......@@ -14,17 +14,15 @@ const jsonFormat = require('json-format');
const browserSync = require('browser-sync').create();
const debug = require('gulp-debug');
const sass = require('gulp-sass');
const notify = require('gulp-notify');
const gulpIf = require('gulp-if');
const browserify = require('browserify');
const source = require('vinyl-source-stream');
const webpackStream = require('webpack-stream');
const buffer = require('vinyl-buffer');
const uglify = require('gulp-uglify');
const postcss = require('gulp-postcss');
const autoprefixer = require("autoprefixer");
const mqpacker = require("css-mqpacker");
const atImport = require("postcss-import");
const cleanss = require('gulp-cleancss');
const csso = require('gulp-csso');
const inlineSVG = require('postcss-inline-svg');
const objectFitImages = require('postcss-object-fit-images');
const cpy = require('cpy');
......@@ -34,6 +32,8 @@ const spritesmith = require('gulp.spritesmith');
const merge = require('merge-stream');
const imagemin = require('gulp-imagemin');
const prettyHtml = require('gulp-pretty-html');
const ghpages = require('gh-pages');
const path = require('path');
// Глобальные настройки этого запуска
const isDev = !process.env.NODE_ENV || process.env.NODE_ENV == 'dev';
......@@ -62,7 +62,7 @@ let prettyOption = {
// Список и настройки плагинов postCSS
let postCssPlugins = [
autoprefixer(),
autoprefixer({grid: true}),
mqpacker({
sort: true
}),
......@@ -216,43 +216,25 @@ function compileSass() {
.pipe(debug({title: 'Compiles:'}))
.pipe(sass({includePaths: [__dirname+'/']}))
.pipe(postcss(postCssPlugins))
.pipe(gulpIf(!isDev, cleanss()))
.pipe(csso({
restructure: false,
}))
.pipe(dest(`${dir.build}/css`, { sourcemaps: '.' }))
.pipe(browserSync.stream());
}
exports.compileSass = compileSass;
function buildJs() {
// TODO впилить сюда вебпацкЪ
// var sourcemaps = require('gulp-sourcemaps');
return browserify({
entries: dir.src + '/js/entry.js',
debug: true
})
.transform('babelify', {presets: ['@babel/preset-env',]})
.bundle()
.pipe(source('bundle.js'))
.pipe(buffer())
// .pipe(sourcemaps.init({loadMaps: true}))
.pipe(gulpIf(!isDev, uglify()))
// .pipe(sourcemaps.write('./'))
.pipe(dest(dir.build + '/js'));
}
exports.buildJs = buildJs;
function writeJsRequiresFile(cb) {
// console.log( config.blocks );
let msg = `\n/*!*${doNotEditMsg.replace(/\n /gm,'\n * ').replace(/\n\n$/,'\n */\n\n')}`;
let jsRequires = msg;
config.addJsBefore.forEach(function(src) {
nth.config.addJsBefore.forEach(function(src) {
jsRequires += `require('${src}');\n`;
});
config.blocks.forEach(function(block) {
nth.blocksFromHtml.forEach(function(block) {
if(fileExist(`${dir.blocks}${block}/${block}.js`)) jsRequires += `require('../blocks/${block}/${block}.js');\n`;
});
config.addJsAfter.forEach(function(src) {
nth.config.addJsAfter.forEach(function(src) {
jsRequires += `require('${src}');\n`;
});
jsRequires += msg;
......@@ -262,68 +244,71 @@ function writeJsRequiresFile(cb) {
exports.writeJsRequiresFile = writeJsRequiresFile;
function writeBlocksLibSass(cb) {
let allBlocksWithStyleFiles = getDirectories('scss');
let styleImports = '';
config.addStyleBefore.forEach(function(src) {
styleImports += `@import "${src}";\n`;
});
allBlocksWithStyleFiles.forEach(function(block) {
let src = `${dir.blocks}${block}/${block}.scss`;
styleImports += `@import "${src}";\n`;
});
config.addStyleAfter.forEach(function(src) {
styleImports += `@import "${src}";\n`;
});
fs.writeFileSync(`${dir.src}scss/blocks-lib.scss`, styleImports);
cb();
function buildJs() {
return src(`${dir.src}js/entry.js`)
.pipe(plumber())
.pipe(webpackStream({
mode: 'development',
output: {
filename: 'bundle.js',
},
module: {
rules: [
{
test: /\.(js)$/,
exclude: /(node_modules)/,
loader: 'babel-loader',
query: {
presets: ['@babel/preset-env']
}
}
]
},
// externals: {
// jquery: 'jQuery'
// }
}))
.pipe(gulpIf(!isDev, uglify()))
.pipe(dest(`${dir.build}js`));
}
exports.writeBlocksLibSass = writeBlocksLibSass;
exports.buildJs = buildJs;
function writeBlocksLibJs(cb) {
let allBlocksWithJsFiles = getDirectories('js');
let jsRequires = '';
config.addJsBefore.forEach(function(src) {
jsRequires += `require('${src}');\n`;
});
allBlocksWithJsFiles.forEach(function(block) {
jsRequires += `require('../blocks/${block}/${block}.js');\n`;
});
config.addJsAfter.forEach(function(src) {
jsRequires += `require('${src}');\n`;
});
fs.writeFileSync(`${dir.src}js/blocks-lib.js`, jsRequires);
cb();
}
exports.writeBlocksLibJs = writeBlocksLibJs;
// function writeBlocksLibSass(cb) {
// let allBlocksWithStyleFiles = getDirectories('scss');
// let styleImports = '';
// config.addStyleBefore.forEach(function(src) {
// styleImports += `@import "${src}";\n`;
// });
// allBlocksWithStyleFiles.forEach(function(block) {
// let src = `${dir.blocks}${block}/${block}.scss`;
// styleImports += `@import "${src}";\n`;
// });
// config.addStyleAfter.forEach(function(src) {
// styleImports += `@import "${src}";\n`;
// });
// fs.writeFileSync(`${dir.src}scss/blocks-lib.scss`, styleImports);
// cb();
// }
// exports.writeBlocksLibSass = writeBlocksLibSass;
function compileBlocksLibSass() {
return src(`${dir.src}scss/blocks-lib.scss`, { sourcemaps: true })
.pipe(plumber())
.pipe(debug({title: 'Compiles:'}))
.pipe(sass({includePaths: [__dirname+'/']}))
.pipe(postcss(postCssPlugins))
.pipe(dest(`${dir.build}/css`, { sourcemaps: '.' }))
.pipe(browserSync.stream());
}
exports.compileBlocksLibSass = compileBlocksLibSass;
function buildBlocksLibJs() {
return browserify({
entries: dir.src + '/js/blocks-lib.js',
debug: true
})
.transform('babelify', {presets: ['@babel/preset-env',]})
.bundle()
.pipe(source('blocks-lib.js'))
.pipe(buffer())
.pipe(gulpIf(!isDev, uglify()))
.pipe(dest(dir.build + '/js'));
}
exports.buildBlocksLibJs = buildBlocksLibJs;
// function writeBlocksLibJs(cb) {
// let allBlocksWithJsFiles = getDirectories('js');
// let jsRequires = '';
// config.addJsBefore.forEach(function(src) {
// jsRequires += `require('${src}');\n`;
// });
// allBlocksWithJsFiles.forEach(function(block) {
// jsRequires += `require('../blocks/${block}/${block}.js');\n`;
// });
// config.addJsAfter.forEach(function(src) {
// jsRequires += `require('${src}');\n`;
// });
// fs.writeFileSync(`${dir.src}js/blocks-lib.js`, jsRequires);
// cb();
// }
// exports.writeBlocksLibJs = writeBlocksLibJs;
function clearBuildDir() {
......@@ -340,6 +325,11 @@ function reload(done) {
done();
}
function deploy(cb) {
ghpages.publish(path.join(process.cwd(), dir.build), cb);
}
exports.deploy = deploy;
function serve() {
......@@ -354,9 +344,8 @@ function serve() {
// Страницы: изменение, добавление
watch([`${dir.src}pages/**/*.pug`], { events: ['change', 'add'], delay: 100 }, series(
compilePugFast,
parallel(writeSassImportsFile, ),
// parallel(writeSassImportsFile, writeJsRequiresFile),
// parallel(compileSass, buildJs),
parallel(writeSassImportsFile, writeJsRequiresFile),
parallel(compileSass, buildJs),
reload
));
......@@ -375,7 +364,7 @@ function serve() {
watch([`${dir.blocks}**/*.pug`], { events: ['change', 'add'], delay: 100 }, series(
compilePug,
writeSassImportsFile,
// compileSass,
compileSass,
reload
));
......@@ -385,20 +374,28 @@ function serve() {
// Шаблоны pug: все события
watch([`${dir.src}pug/**/*.pug`, `!${dir.src}pug/mixins.pug`], { delay: 100 }, series(
compilePug,
parallel(writeSassImportsFile, ),
// parallel(writeSassImportsFile, writeJsRequiresFile),
// parallel(compileSass, buildJs),
parallel(writeSassImportsFile, writeJsRequiresFile),
parallel(compileSass, buildJs),
reload,
));
// // Стили Блоков: все события
// watch([`${dir.blocks}**/*.scss`], { events: ['all'], delay: 100 }, series(writeSassImportsFile, writeBlocksLibSass, compileSass, compileBlocksLibSass));
// Стили Блоков: все события
watch([`${dir.blocks}**/*.scss`], { events: ['all'], delay: 100 }, series(
writeSassImportsFile,
compileSass,
));
// // Стилевые глобальные файлы: все события
// watch([`${dir.src}scss/**/*.scss`, `!${dir.src}scss/style.scss`, `!${dir.src}scss/blocks-lib.scss`], { events: ['all'], delay: 100 }, series(compileSass, compileBlocksLibSass));
// Стилевые глобальные файлы: все события
watch([`${dir.src}scss/**/*.scss`, `!${dir.src}scss/style.scss`, `!${dir.src}scss/blocks-lib.scss`], { events: ['all'], delay: 100 }, series(
compileSass,
));
// // Скриптовые глобальные файлы: все события
// watch([`${dir.src}js/**/*.js`, `!${dir.src}js/entry.js`, `!${dir.src}js/blocks-lib.js`, `${dir.blocks}**/*.js`], { events: ['all'], delay: 100 }, series(writeJsRequiresFile, writeBlocksLibJs, buildJs, buildBlocksLibJs, reload));
// Скриптовые глобальные файлы: все события
watch([`${dir.src}js/**/*.js`, `!${dir.src}js/entry.js`, `!${dir.src}js/blocks-lib.js`, `${dir.blocks}**/*.js`], { events: ['all'], delay: 100 }, series(
writeJsRequiresFile,
buildJs,
reload
));
// Картинки: все события
watch([`${dir.blocks}**/img/*.{jpg,jpeg,png,gif,svg,webp}`], { events: ['all'], delay: 100 }, series(copyImg, reload));
......@@ -414,31 +411,27 @@ function serve() {
watch([`${dir.blocks}sprite-png/png/*.png`], { events: ['all'], delay: 100 }, series(
generatePngSprite,
copyImg,
// compileSass,
compileSass,
reload,
));
}
exports.default = series(
exports.build = series(
parallel(clearBuildDir, writePugMixinsFile),
parallel(compilePugFast, copyAssets, generateSvgSprite, generatePngSprite),
parallel(copyImg, writeSassImportsFile),
// parallel(writeSassImportsFile, writeJsRequiresFile, writeBlocksLibSass, writeBlocksLibJs),
serve,
parallel(copyImg, writeSassImportsFile, writeJsRequiresFile),
parallel(compileSass, buildJs),
);
// exports.default = series(
// parallel(clearBuildDir, writePugMixinsFile),
// parallel(compilePugFast, copyAssets),
// parallel(generateSvgSprite, generatePngSprite),
// parallel(copyImg),
// parallel(writeSassImportsFile, writeJsRequiresFile, writeBlocksLibSass, writeBlocksLibJs),
// parallel(compileSass, compileBlocksLibSass, buildJs, buildBlocksLibJs),
// serve,
// );
exports.default = series(
parallel(clearBuildDir, writePugMixinsFile),
parallel(compilePugFast, copyAssets, generateSvgSprite, generatePngSprite),
parallel(copyImg, writeSassImportsFile, writeJsRequiresFile),
parallel(compileSass, buildJs),
serve,
);
......@@ -486,19 +479,6 @@ function getClassesToBlocksList(file, enc, cb) {
cb();
}
/**
* Запись конфигурационного файла
* @param {object} config Конфиг
*/
// function writeConfig(config) {
// var settings = { type: 'space', size: 2 }
// let configText = '// Файл перезаписывается программно при работе автоматизации\nlet config =\n' + jsonFormat(config, settings) + ';\n\nmodule.exports = config;\n';
// fs.writeFile('./config.js', configText, function(err){
// if (err) throw err;
// console.log('---------- Записан новый config.js');
// });
// }
//
/**
* Pug-фильтр, выводящий содержимое pug-файла в виде форматированного текста
......
'use strict';
// Подключения зависимостей
const fs = require('fs');
const gulp = require('gulp');
const browserSync = require('browser-sync').create();
const postcss = require('gulp-postcss');
const autoprefixer = require("autoprefixer");
const mqpacker = require("css-mqpacker");
const atImport = require("postcss-import");
const cleanss = require('gulp-cleancss');
const inlineSVG = require('postcss-inline-svg');
const objectFitImages = require('postcss-object-fit-images');
const plumber = require('gulp-plumber');
const notify = require('gulp-notify');
const gulpIf = require('gulp-if');
const debug = require('gulp-debug');
const rename = require('gulp-rename');
const size = require('gulp-size');
const del = require('del');
const newer = require('gulp-newer');
// Получение настроек проекта из projectConfig.json
let projectConfig = require('./projectConfig.json');
let dirs = projectConfig.dirs;
let lists = getFilesList(projectConfig);
// console.log(lists);
// Получение адреса репозитория
let repoUrl = require('./package.json').repository.url.replace(/\.git$/g, '');
// console.log(repoUrl);
// Сообщение, записываемое в стилевой файл
let styleFileMsg = '/*!*\n * ВНИМАНИЕ! Этот файл генерируется автоматически.\n * Не пишите сюда ничего вручную, все такие правки будут потеряны при следующей компиляции.\n * Правки без возможности компиляции ДОЛЬШЕ И ДОРОЖЕ в 2-3 раза.\n * Нужны дополнительные стили? Создайте новый css-файл и подключите его к странице.\n * Читайте ./README.md для понимания.\n */\n\n';
// Формирование и запись диспетчера подключений (style.scss), который компилируется в style.min.css
let styleImports = styleFileMsg;
lists.css.forEach(function(blockPath) {
styleImports += '@import \''+blockPath+'\';\n';
});
styleImports = styleImports += styleFileMsg;
fs.writeFileSync(dirs.srcPath + 'scss/style.scss', styleImports);
// Формирование и запись списка примесей (mixins.pug) со списком инклудов всех pug-файлов блоков
let pugMixins = '//- ВНИМАНИЕ! Этот файл генерируется автоматически. Не пишите сюда ничего вручную!\n//- Читайте ./README.md для понимания.\n\n';
lists.pug.forEach(function(blockPath) {
pugMixins += 'include '+blockPath+'\n';
});
fs.writeFileSync(dirs.srcPath + 'pug/mixins.pug', pugMixins);
// Определение: разработка это или финальная сборка
// Запуск `NODE_ENV=production npm start [задача]` приведет к сборке без sourcemaps
const isDev = !process.env.NODE_ENV || process.env.NODE_ENV == 'dev';
// Перечисление и настройки плагинов postCSS, которыми обрабатываются стилевые файлы
let postCssPlugins = [
autoprefixer(), // настройки вынесены в package.json, дабы получать их для любой задачи
mqpacker({
sort: true
}),
atImport(),
inlineSVG(),
objectFitImages(),
];
// Очистка папки сборки
gulp.task('clean', function () {
console.log('---------- Очистка папки сборки');
return del([
dirs.buildPath + '/**/*',
'!' + dirs.buildPath + '/readme.md'
]);
});
// Компиляция стилей блоков проекта (и добавочных)
gulp.task('style', function () {
const sass = require('gulp-sass');
const sourcemaps = require('gulp-sourcemaps');
const wait = require('gulp-wait');
const insert = require('gulp-insert');
console.log('---------- Компиляция стилей');
return gulp.src(dirs.srcPath + 'scss/style.scss')
.pipe(plumber({
errorHandler: function(err) {
notify.onError({
title: 'Styles compilation error',
message: err.message
})(err);
this.emit('end');
}
}))
.pipe(wait(100))
.pipe(gulpIf(isDev, sourcemaps.init()))
.pipe(debug({title: "Style:"}))
.pipe(sass({includePaths: [__dirname+'/']}))
.pipe(postcss(postCssPlugins))
.pipe(insert.append(styleFileMsg))
.pipe(gulpIf(!isDev, cleanss()))
.pipe(rename('style.min.css'))
.pipe(gulpIf(isDev, sourcemaps.write('/')))
.pipe(size({
title: 'Размер',
showFiles: true,
showTotal: false,
}))
.pipe(gulp.dest(dirs.buildPath + '/css'))
.pipe(browserSync.stream());
});
// Компиляция отдельных файлов
gulp.task('style:single', function (callback) {
if(projectConfig.singleCompiled.length) {
const sass = require('gulp-sass');
const sourcemaps = require('gulp-sourcemaps');
const wait = require('gulp-wait');
const insert = require('gulp-insert');
console.log('---------- Компиляция добавочных стилей');
return gulp.src(projectConfig.singleCompiled)
.pipe(plumber({
errorHandler: function(err) {
notify.onError({
title: 'Single style compilation error',
message: err.message
})(err);
this.emit('end');
}
}))
.pipe(wait(100))
.pipe(gulpIf(isDev, sourcemaps.init()))
.pipe(debug({title: "Single style:"}))
.pipe(sass())
.pipe(postcss(postCssPlugins))
.pipe(insert.append(styleFileMsg))
.pipe(gulpIf(!isDev, cleanss()))
.pipe(gulpIf(isDev, sourcemaps.write('/')))
.pipe(size({
title: 'Размер',
showFiles: true,
showTotal: false,
}))
.pipe(gulp.dest(dirs.buildPath + '/css'))
.pipe(browserSync.stream());
}
else {
callback();
}
});
// Копирование добавочных CSS, которые хочется иметь отдельными файлами
gulp.task('copy:css', function(callback) {
if(projectConfig.copiedCss.length) {
return gulp.src(projectConfig.copiedCss)
.pipe(postcss(postCssPlugins))
.pipe(cleanss())
.pipe(size({
title: 'Размер',
showFiles: true,
showTotal: false,
}))
.pipe(gulp.dest(dirs.buildPath + '/css'))
.pipe(browserSync.stream());
}
else {
callback();
}
});
// Копирование изображений
gulp.task('copy:img', function () {
console.log('---------- Копирование изображений');
return gulp.src(lists.img)
.pipe(newer(dirs.buildPath + '/img')) // оставить в потоке только изменившиеся файлы
.pipe(size({
title: 'Размер',
showFiles: true,
showTotal: false,
}))
.pipe(gulp.dest(dirs.buildPath + '/img'));
});
// Копирование JS
gulp.task('copy:js', function (callback) {
if(projectConfig.copiedJs.length) {
return gulp.src(projectConfig.copiedJs)
.pipe(size({
title: 'Размер',
showFiles: true,
showTotal: false,
}))
.pipe(gulp.dest(dirs.buildPath + '/js'));
}
else {
callback();
}
});
// Копирование шрифтов
gulp.task('copy:fonts', function () {
console.log('---------- Копирование шрифтов');
return gulp.src(dirs.srcPath + '/fonts/*.{ttf,woff,woff2,eot,svg}')
.pipe(newer(dirs.buildPath + '/fonts')) // оставить в потоке только изменившиеся файлы
.pipe(size({
title: 'Размер',
showFiles: true,
showTotal: false,
}))
.pipe(gulp.dest(dirs.buildPath + '/fonts'));
});
// Фавиконки
gulp.task('copy:favicon', function () {
console.log('---------- Копирование фавиконок');
return gulp.src(dirs.srcPath + '/favicon/*.{png,ico,svg}')
.pipe(gulp.dest(dirs.buildPath + '/img/favicon'));
});
gulp.task('copy:favicon:data', function () {
return gulp.src(dirs.srcPath + '/favicon/*.{xml,webmanifest}')
.pipe(gulp.dest(dirs.buildPath + '/'));
});
// Сборка SVG-спрайта для блока sprite-svg
let spriteSvgPath = dirs.srcPath + dirs.blocksDirName + '/sprite-svg/svg/';
gulp.task('sprite:svg', function (callback) {
if((projectConfig.blocks['sprite-svg']) !== undefined) {
const svgstore = require('gulp-svgstore');
const svgmin = require('gulp-svgmin');
const cheerio = require('gulp-cheerio');
if(fileExist(spriteSvgPath) !== false) {
console.log('---------- Сборка SVG спрайта');
return gulp.src(spriteSvgPath + '*.svg')
.pipe(svgmin(function (file) {
return {
plugins: [{
cleanupIDs: {
minify: true
}
}]
}
}))
.pipe(svgstore({ inlineSvg: true }))
.pipe(cheerio({
run: function($) {
$('svg').attr('style', 'display:none');
},
parserOptions: {
xmlMode: true
}
}))
.pipe(rename('sprite-svg.svg'))
.pipe(size({
title: 'Размер',
showFiles: true,
showTotal: false,
}))
.pipe(gulp.dest(dirs.srcPath + dirs.blocksDirName + '/sprite-svg/img/'));
}
else {
console.log('---------- Сборка SVG спрайта: ОТМЕНА, нет папки с картинками');
callback();
}
}
else {
console.log('---------- Сборка SVG спрайта: ОТМЕНА, блок не используется на проекте');
callback();
}
});
// Сборка растрового спрайта для блока sprite-png
let spritePngPath = dirs.srcPath + dirs.blocksDirName + '/sprite-png/png/';
gulp.task('sprite:png', function (callback) {
if((projectConfig.blocks['sprite-png']) !== undefined) {
const spritesmith = require('gulp.spritesmith');
const buffer = require('vinyl-buffer');
const merge = require('merge-stream');
const imagemin = require('gulp-imagemin');
if(fileExist(spritePngPath) !== false) {
del(dirs.srcPath + dirs.blocksDirName + '/sprite-png/img/*.png');
let fileName = 'sprite-' + Math.random().toString().replace(/[^0-9]/g, '') + '.png';
let spriteData = gulp.src(spritePngPath + '*.png')
.pipe(spritesmith({
imgName: fileName,
cssName: 'sprite-png.scss',
padding: 4,
imgPath: '../img/' + fileName
}));
let imgStream = spriteData.img
.pipe(buffer())
.pipe(imagemin([
imagemin.optipng({ optimizationLevel: 5 }),
]))
.pipe(gulp.dest(dirs.srcPath + dirs.blocksDirName + '/sprite-png/img/'));
let cssStream = spriteData.css
.pipe(gulp.dest(dirs.srcPath + dirs.blocksDirName + '/sprite-png/'));
return merge(imgStream, cssStream);
}
else {
console.log('---------- Сборка PNG спрайта: ОТМЕНА, нет папки с картинками');
callback();
}
}
else {
console.log('---------- Сборка PNG спрайта: ОТМЕНА, блок не используется на проекте');
callback();
}
});
// Сборка Pug
let classes = [];
gulp.task('pug', function() {
const pug = require('gulp-pug');
const through2 = require('through2');
// const chalk = require('chalk');
const getClassesFromHtml = require('get-classes-from-html');
const htmlbeautify = require('gulp-html-beautify');
const replace = require('gulp-replace');
console.log('---------- Сборка Pug');
// Pug-фильтр, выводящий содержимое pug-файла в виде форматированного текста
const filterShowCode = function (text, options) {
var lines = text.split('\n');
var result = '<pre class="code">\n';
if (typeof(options['first-line']) !== 'undefined') result = result + '<code>' + options['first-line'] + '</code>\n';
for (var i = 0; i < (lines.length - 1); i++) { // (lines.length - 1) для срезания последней строки (пустая)
result = result + '<code>' + lines[i] + '</code>\n';
}
result = result + '</pre>\n';
result = result.replace(/<code><\/code>/g, '<code>&nbsp;</code>');
return result;
}
return gulp.src([
dirs.srcPath + '*.pug',
])
.pipe(plumber())
.pipe(pug({
data: {
repoUrl: repoUrl, // передаем pug-у адрес репозитория проекта
},
filters: {
'show-code': filterShowCode
},
// compileDebug: false,
}))
.pipe(through2.obj(function (file, enc, cb) {
if (file.isNull()) {
cb(null, file);
return;
}
if(file.relative != 'blocks-demo.html'){
const data = file.contents.toString();
let thisClasses = getClassesFromHtml(data);
thisClasses.forEach(function(item, i, arr) {
if (item.indexOf('__') + 1 === 0 && item.indexOf('--') + 1 === 0) {
classes.push(item)
}
});
file.contents = new Buffer(data);
}
this.push(file);
cb();
}))
.on('end', function(){
console.log(classes);
})
.pipe(htmlbeautify())
// и... привет бьютификатору!
.pipe(replace(/^(\s*)(<header.+?>)(.*)(<\/header>)/gm, '$1$2\n$1 $3\n$1$4'))
.pipe(replace(/^(\s*)(<footer.+?>)(.*)(<\/footer>)/gm, '$1$2\n$1 $3\n$1$4'))
.pipe(replace(/^\s*<section.+>/gm, '\n$&'))
.pipe(replace(/^\s*<\/section>/gm, '$&\n'))
.pipe(replace(/^\s*<article.+>/gm, '\n$&'))
.pipe(replace(/^\s*<\/article>/gm, '$&\n'))
.pipe(replace(/\n\n\n/gm, '\n\n'))
.pipe(gulp.dest(dirs.buildPath));
});
gulp.task('test:pug', function () {
const pugLinter = require('gulp-pug-lint');
return gulp
.src('src/**/*.pug')
.pipe(pugLinter());
});
// Конкатенация и углификация Javascript
gulp.task('js', function (callback) {
const uglify = require('gulp-uglify');
const concat = require('gulp-concat');
if(lists.js.length > 0){
console.log('---------- Обработка JS');
return gulp.src(lists.js)
.pipe(plumber({
errorHandler: function(err) {
notify.onError({
title: 'Javascript concat/uglify error',
message: err.message
})(err);
this.emit('end');
}
}))
.pipe(concat('script.min.js'))
.pipe(gulpIf(!isDev, uglify().on('error', function(e){console.log(e);})))
.pipe(size({
title: 'Размер',
showFiles: true,
showTotal: false,
}))
.pipe(gulp.dest(dirs.buildPath + '/js'));
}
else {
console.log('---------- Обработка JS: в сборке нет JS-файлов');
callback();
}
});
gulp.task('browserify', function () {
var browserify = require('browserify');
var source = require('vinyl-source-stream');
var buffer = require('vinyl-buffer');
var uglify = require('gulp-uglify');
var sourcemaps = require('gulp-sourcemaps');
var log = require('gulplog');
var b = browserify({
entries: dirs.srcPath + '/js/global-script.js',
debug: true
})
// .add([dirs.srcPath + '/js/scri.js']) // добавить все необходимые файлы
.transform('babelify', {presets: ['@babel/preset-env']});
return b.bundle()
.pipe(source('script.js'))
.pipe(gulp.dest(dirs.buildPath + '/js'))
.pipe(rename('script.min.js'))
.pipe(buffer())
.pipe(sourcemaps.init({loadMaps: true}))
.pipe(uglify())
.on('error', function(){ console.log('error'); })
.pipe(sourcemaps.write('./'))
.pipe(gulp.dest(dirs.buildPath + '/js'));
});
// Ручная оптимизация изображений
// Использование: folder=src/img npm start img:opt
const folder = process.env.folder;
gulp.task('img:opt', function (callback) {
const imagemin = require('gulp-imagemin');
// const pngquant = require('imagemin-pngquant');
if(folder){
console.log('---------- Оптимизация картинок');
return gulp.src(folder + '/*.{jpg,jpeg,gif,png,svg}')
.pipe(imagemin([
imagemin.gifsicle({ interlaced: true }),
imagemin.jpegtran({ progressive: true }),
imagemin.optipng({ optimizationLevel: 5 }),
imagemin.svgo({
plugins: [
{ removeViewBox: false },
{ cleanupIDs: false }
]
})
]))
.pipe(gulp.dest(folder));
}
else {
console.log('---------- Оптимизация картинок: ошибка (не указана папка)');
console.log('---------- Пример вызова команды: folder=src/blocks/block-name/img npm start img:opt');
callback();
}
});
// Сборка всего
gulp.task('build', gulp.series(
'clean',
gulp.parallel('sprite:svg', 'sprite:png', 'copy:favicon', 'copy:favicon:data'),
gulp.parallel('style', 'style:single', 'js', 'copy:css', 'copy:img', 'copy:js', 'copy:fonts'),
'pug'
));
// Отправка в GH pages (ветку gh-pages репозитория)
gulp.task('deploy', function(cb) {
const ghpages = require('gh-pages');
const path = require('path');
console.log('---------- Публикация содержимого ./build/ на GH pages');
var ghPagesUrl;
if (repoUrl) {
var urlParts = repoUrl.split('/');
if (urlParts[2] == 'github.com') {
ghPagesUrl = 'http://' + urlParts[3] + '.github.io/' + urlParts[4] + '/';
}
console.log('---------- ' + ghPagesUrl);
}
ghpages.publish(path.join(process.cwd(), dirs.buildPath), cb);
// return gulp.src(dirs.buildPath + '**/*')
// .pipe(ghPages());
});
// Локальный сервер, слежение
gulp.task('serve', gulp.series('build', function() {
browserSync.init({
server: dirs.buildPath,
port: 8080,
startPath: 'index.html',
open: false,
});
// Стили
let stylePaths = [
dirs.srcPath + 'scss/style.scss',
dirs.srcPath + 'scss/variables.scss',
dirs.srcPath + 'scss/mixins/*.scss',
];
for (let i = 0, len = lists.blocksDirs.length; i < len; ++i) {
stylePaths.push(dirs.srcPath + lists.blocksDirs[i] + '*.scss');
}
stylePaths.concat(projectConfig.addCssBefore, projectConfig.addCssAfter);
// console.log(stylePaths);
gulp.watch(stylePaths, gulp.series('style'));
// Стили, которые нужно компилировать отдельно
if(projectConfig.singleCompiled.length) {
gulp.watch(projectConfig.singleCompiled, gulp.series('style:single'));
}
// CSS-файлы, которые нужно просто копировать
if(projectConfig.copiedCss.length) {
gulp.watch(projectConfig.copiedCss, gulp.series('copy:css', reload));
}
// Изображения
if(lists.img.length) {
gulp.watch(lists.img, gulp.series('copy:img', reload));
}
// JS-файлы, которые нужно просто копировать
if(projectConfig.copiedJs.length) {
gulp.watch(projectConfig.copiedJs, gulp.series('copy:js', reload));
}
// Шрфты
gulp.watch(dirs.srcPath + 'fonts/*.{ttf,woff,woff2,eot,svg}', gulp.series('copy:fonts', reload));
// Pug-файлы
let pugPaths = [
dirs.srcPath + '*.pug',
dirs.srcPath + 'pug/*.pug',
];
for (let i = 0, len = lists.blocksDirs.length; i < len; ++i) {
pugPaths.push(dirs.srcPath + lists.blocksDirs[i] + '*.pug');
}
// console.log(pugPaths);
if(lists.pug.length) {
gulp.watch(pugPaths, gulp.series('pug', reload));
}
// JS-файлы блоков
if(lists.js.length) {
gulp.watch(lists.js, gulp.series('js', reload));
}
// SVG-изображения, попадающие в спрайт
if((projectConfig.blocks['sprite-svg']) !== undefined) {
gulp.watch('*.svg', {cwd: spriteSvgPath}, gulp.series('sprite:svg', reload));
}
// PNG-изображения, попадающие в спрайт
if((projectConfig.blocks['sprite-png']) !== undefined) {
gulp.watch('*.png', {cwd: spritePngPath}, gulp.series('sprite:png', reload));
}
}));
// Задача по умолчанию
gulp.task('default',
gulp.series('serve')
);
/**
* Вернет объект с обрабатываемыми файлами и папками
* @param {object}
* @return {object}
*/
function getFilesList(config){
let res = {
'css': [],
'js': [],
'img': [],
'pug': [],
'blocksDirs': [],
};
// Обходим массив с блоками проекта
for (let blockName in config.blocks) {
var blockPath = config.dirs.srcPath + config.dirs.blocksDirName + '/' + blockName + '/';
if(fileExist(blockPath)) {
// Разметка (Pug)
if(fileExist(blockPath + blockName + '.pug')){
res.pug.push('../' + config.dirs.blocksDirName + '/' + blockName + '/' + blockName + '.pug');
// TODO переделать так, чтобы можно было использовать в вотчере
}
else {
console.log('---------- Блок ' + blockName + ' указан как используемый, но не имеет pug-файла.');
}
// Стили
if(fileExist(blockPath + blockName + '.scss')){
res.css.push(blockPath + blockName + '.scss');
if(config.blocks[blockName].length) {
config.blocks[blockName].forEach(function(elementName) {
if(fileExist(blockPath + blockName + elementName + '.scss')){
res.css.push(blockPath + blockName + elementName + '.scss');
}
});
}
}
else {
console.log('---------- Блок ' + blockName + ' указан как используемый, но не имеет scss-файла.');
}
// Скрипты
if(fileExist(blockPath + blockName + '.js')){
res.js.push(blockPath + blockName + '.js');
if(config.blocks[blockName].length) {
config.blocks[blockName].forEach(function(elementName) {
if(fileExist(blockPath + blockName + elementName + '.js')){
res.js.push(blockPath + blockName + elementName + '.js');
}
});
}
}
else {
// console.log('---------- Блок ' + blockName + ' указан как используемый, но не имеет JS-файла.');
}
// Картинки (тупо от всех блоков, без проверки)
res.img.push(config.dirs.srcPath + config.dirs.blocksDirName + '/' + blockName + '/img/*.{jpg,jpeg,gif,png,svg}');
// Список директорий
res.blocksDirs.push(config.dirs.blocksDirName + '/' + blockName + '/');
}
else {
console.log('ERR ------ Блок ' + blockPath + ' указан как используемый, но такой папки нет!');
}
}
// Добавления
res.css = res.css.concat(config.addCssAfter);
res.css = config.addCssBefore.concat(res.css);
res.js = res.js.concat(config.addJsAfter);
res.js = config.addJsBefore.concat(res.js);
res.img = config.addImages.concat(res.img);
return res;
}
/**
* Проверка существования файла или папки
* @param {string} path Путь до файла или папки]
* @return {boolean}
*/
function fileExist(filepath){
let flag = true;
try{
fs.accessSync(filepath, fs.F_OK);
}catch(e){
flag = false;
}
return flag;
}
// Перезагрузка браузера
function reload (done) {
browserSync.reload();
done();
}
......@@ -10,12 +10,9 @@
},
"scripts": {
"test:style": "stylelint \"src/**/*.scss\" --syntax=scss",
"test:editorconfig": "editorconfig-cli src/**/*.{js,pug}",
"test": "npm run test:style && npm run test:editorconfig && npm run build",
"start": "gulp",
"deploy": "cross-env NODE_ENV=production ./node_modules/.bin/gulp build && cross-env ./node_modules/.bin/gulp deploy",
"build": "cross-env NODE_ENV=production npm start build",
"img-opt": "cross-env folder=src/img npm start img:opt",
"lint-staged": "lint-staged"
},
"browserslist": [
......@@ -29,13 +26,12 @@
"*.scss": "stylelint --syntax=scss"
},
"devDependencies": {
"@babel/core": "^7.1.6",
"@babel/preset-env": "^7.1.6",
"@babel/core": "^7.2.2",
"@babel/preset-env": "^7.2.3",
"@htmlacademy/editorconfig-cli": "^1.0.0",
"autoprefixer": "^9.3.1",
"babelify": "^10.0.0",
"babel-loader": "^8.0.4",
"browser-sync": "^2.18.8",
"browserify": "^16.2.3",
"cpy": "^7.0.1",
"cross-env": "^5.0.0",
"css-mqpacker": "^7.0.0",
......@@ -43,28 +39,22 @@
"get-classes-from-html": "^1.0.1",
"gh-pages": "^2.0.1",
"gulp": "^4.0.0",
"gulp-cleancss": "^0.2.2",
"gulp-concat": "^2.6.1",
"gulp-csso": "^3.0.1",
"gulp-debug": "^4.0.0",
"gulp-if": "^2.0.2",
"gulp-imagemin": "^5.0.3",
"gulp-insert": "^0.5.0",
"gulp-newer": "^1.3.0",
"gulp-notify": "^3.0.0",
"gulp-plumber": "^1.1.0",
"gulp-postcss": "^8.0.0",
"gulp-pretty-html": "^2.0.9",
"gulp-pug": "^4.0.1",
"gulp-pug-lint": "git+https://github.com/nicothin/gulp-pug-lint.git",
"gulp-rename": "^1.2.2",
"gulp-replace": "^1.0.0",
"gulp-sass": "^4.0.1",
"gulp-size": "^3.0.0",
"gulp-sourcemaps": "^2.4.1",
"gulp-svgmin": "^2.1.0",
"gulp-svgstore": "^7.0.0",
"gulp-uglify": "^3.0.0",
"gulp-wait": "0.0.2",
"gulp.spritesmith": "^6.3.0",
"husky": "^1.1.4",
"json-format": "^1.0.1",
......@@ -72,13 +62,15 @@
"lint-staged": "^8.0.4",
"merge-stream": "^1.0.1",
"mkdirp": "^0.5.1",
"path": "^0.12.7",
"postcss-import": "^12.0.1",
"postcss-inline-svg": "^3.0.0",
"postcss-object-fit-images": "^1.1.2",
"stylelint": "^9.1.1",
"through2": "^3.0.0",
"vinyl-buffer": "^1.0.1",
"vinyl-source-stream": "^2.0.0"
"webpack": "^4.27.1",
"webpack-stream": "^5.2.1"
},
"dependencies": {
"baron": "^3.0.3",
......
{
"blocks": {
"page": [],
"typo": [],
"sprite-svg": [],
"sprite-png": [
"__demo"
],
"page-header": [],
"page-footer": [],
"logo": [],
"main-nav": [],
"close": [],
"burger": [],
"code": [],
"thumb": [],
"alert": [],
"menu": [],
"label": [],
"loader": [],
"table": [],
"table-responsive": [],
"pagination": [],
"breadcrumbs": [],
"progress": [],
"pie-chart": [],
"embed-responsive": [],
"comment": [],
"tooltip": [],
"btn": [],
"field-text": [
"--error"
],
"field-checkbox": [
"__input-wrap--error"
],
"field-radio": [
"__input-wrap--error"
],
"field-toggler": [],
"field-file": [
"--error"
],
"field-range": [],
"field-select": [
"--error"
],
"field-actions": [],
"fields-group": [],
"form": [],
"is-mobile": [],
"scroll-link": [],
"to-top": [],
"dropdown": [],
"owl-carousel": [],
"swipe": [],
"nouislider": [],
"off-canvas": [],
"modal": [],
"tabs": [],
"object-fit-polyfill": [],
"or": [],
"baron": [],
"blocks-library": []
},
"addCssBefore": [
"./src/scss/functions.scss",
"./src/scss/variables.scss",
"./src/scss/mixins.scss",
"../../node_modules/owl.carousel/dist/assets/owl.carousel.css",
"../../node_modules/nouislider/distribute/nouislider.css"
],
"addCssAfter": [
"./src/scss/print.scss"
],
"singleCompiled": [
"./src/scss/blocks-library.scss"
],
"addJsBefore": [
"./node_modules/jquery/dist/jquery.min.js",
"./node_modules/jquery-migrate/dist/jquery-migrate.min.js",
"./node_modules/svg4everybody/dist/svg4everybody.js",
"./node_modules/ismobilejs/dist/isMobile.min.js",
"./node_modules/owl.carousel/dist/owl.carousel.js",
"./node_modules/swipejs/build/swipe.min.js",
"./node_modules/nouislider/distribute/nouislider.js",
"./node_modules/object-fit-images/dist/ofi.js",
"./node_modules/baron/baron.min.js"
],
"addJsAfter": [
"./src/js/global-script.js"
],
"addImages": [
"./src/img/*.{jpg,jpeg,gif,png,svg,ico}"
],
"copiedCss": [],
"copiedJs": [],
"dirs": {
"srcPath": "./src/",
"buildPath": "./build/",
"blocksDirName": "blocks"
}
}
\ No newline at end of file
......@@ -16,9 +16,6 @@ block header
block content
h1 Заголовок с каким-то эффектом
p Содержимое. #[a(href='blocks-demo.html') Библиотека блоков].
.row
.closess
.alertfgdf
block footer
+page-footer()
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment