Commit dc5885f5 authored by Nikolay Gromov's avatar Nikolay Gromov

Формирование списка CSS-импортов

parent 9a13f36d
'use strict'; 'use strict';
// Получение настроек папок из package.json // Подключим зависимости
const pjson = require('./package.json'); const fs = require('fs');
const dirs = pjson.config.directories;
const ghPagesUrl = pjson.config.ghPages;
// Зависимости проекта
const gulp = require('gulp'); const gulp = require('gulp');
const less = require('gulp-less'); const gulpSequence = require('gulp-sequence');
const debug = require('gulp-debug');
const sourcemaps = require('gulp-sourcemaps');
const cleanss = require('gulp-cleancss');
const postcss = require('gulp-postcss');
const autoprefixer = require('autoprefixer');
const mqpacker = require('css-mqpacker');
const rename = require('gulp-rename');
const gulpIf = require('gulp-if');
const del = require('del'); const del = require('del');
const imagemin = require('gulp-imagemin'); const postcss = require('gulp-postcss');
const pngquant = require('imagemin-pngquant'); const postcssMediaVariables = require('postcss-media-variables');
const svgstore = require('gulp-svgstore');
const svgmin = require('gulp-svgmin'); // Получим настройки проекта из package.json
const path = require('path'); let pjson = require('./package.json');
const cheerio = require('gulp-cheerio'); let dirs = pjson.configProject.dirs;
const fileinclude = require('gulp-file-include'); let blocksObject = pjson.configProject.blocks;
const newer = require('gulp-newer');
const notify = require('gulp-notify'); // Обойдем импорты диспетчера подключений, сформируем массив импортов блоков
const uglify = require('gulp-uglify'); let styleFilePath = dirs.srcPath + 'css/style.css';
const concat = require('gulp-concat'); let styleFileContent = fs.readFileSync(styleFilePath, 'utf8');
const browserSync = require('browser-sync').create(); let blockRegExp = new RegExp('/'+dirs.blocksDirName+'/', 'i');
const replace = require('gulp-replace'); let styleFileImportsArray = styleFileContent.split('\n').filter(function(item) {
const ghPages = require('gulp-gh-pages'); if(/^(\s*)@import/.test(item) && blockRegExp.test(item)) return true;
const size = require('gulp-size'); else return false;
const fs = require('fs');
// Запуск `NODE_ENV=production npm start [задача]` приведет к сборке без sourcemaps
const isDev = !process.env.NODE_ENV || process.env.NODE_ENV == 'dev';
// Запуск `port=3004 npm start` приведет к запуску сервера обновлений на 3004 порту и всей обычной автоматизации
const port = process.env.port ? process.env.port : 3000;
// Файлы компилируемых компонентов
let blocks = getComponentsFiles();
// Вывод в консоль информации о взятых в сборку файлах (без LESS)
if(blocks.js.length) {
console.log('---------- В сборку и обработку взяты JS-файлы (указана последовательность):');
console.log(blocks.js);
}
if(blocks.img.length) {
console.log('---------- В сборку и обработку взяты изображения:');
console.log(blocks.img);
}
if(blocks.additionalCss.length) {
console.log('---------- В сборку скопированы добавочные CSS:');
console.log(blocks.additionalCss);
}
// Компиляция LESS
gulp.task('less', function () {
console.log('---------- Компиляция LESS');
return gulp.src(dirs.source + '/less/style.less')
.pipe(gulpIf(isDev, sourcemaps.init()))
.pipe(debug({title: "LESS:"}))
.pipe(less())
.on('error', notify.onError(function(err){
return {
title: 'Styles compilation error',
message: err.message
}
}))
.pipe(postcss([
autoprefixer({browsers: ['last 2 version']}),
mqpacker({
sort: true
}),
]))
.pipe(gulpIf(!isDev, cleanss()))
.pipe(rename('style.min.css'))
.pipe(debug({title: "RENAME:"}))
.pipe(gulpIf(isDev, sourcemaps.write('/')))
.pipe(size({
title: 'Размер',
showFiles: true,
showTotal: false,
}))
.pipe(gulp.dest(dirs.build + '/css'))
.pipe(browserSync.stream());
});
// Копирование добавочных CSS, которые хочется иметь отдельными файлами
gulp.task('copy:css', function(callback) {
if(blocks.additionalCss.length > 0) {
console.log('---------- копирование CSS');
return gulp.src(blocks.additionalCss, {since: gulp.lastRun('copy:css')})
.pipe(postcss([
autoprefixer({browsers: ['last 2 version']}),
mqpacker
]))
.pipe(cleanss())
.pipe(gulp.dest(dirs.build + '/css'));
}
else {
console.log('---------- Копирование CSS: нет дополнительного CSS');
callback();
}
});
// Копирование и оптимизация изображений
gulp.task('img', function () {
console.log('---------- Копирование и оптимизация картинок');
return gulp.src(blocks.img, {since: gulp.lastRun('img')}) // только для изменившихся с последнего запуска файлов
.pipe(newer(dirs.build + '/img')) // оставить в потоке только изменившиеся файлы
.pipe(imagemin({
progressive: true,
svgoPlugins: [{removeViewBox: false}],
use: [pngquant()]
}))
.pipe(gulp.dest(dirs.build + '/img'));
});
// Оптимизация изображений // folder=src/img/icons/ npm start img:opt
const folder = process.env.folder;
gulp.task('img:opt', function (callback) {
if(folder){
console.log('---------- Оптимизация картинок');
return gulp.src(folder + '/*.{jpg,jpeg,gif,png,svg}')
.pipe(imagemin({
progressive: true,
svgoPlugins: [{removeViewBox: false}],
use: [pngquant()]
}))
.pipe(gulp.dest(folder));
}
else {
console.log('---------- Оптимизация картинок: ошибка (не указана папка)');
console.log('---------- Пример вызова команды: folder=src/blocks/block-name/img_to_bg/ npm start img:opt');
callback();
}
});
// Копирование шрифтов
gulp.task('fonts:copy', function () {
console.log('---------- Копирование шрифтов');
return gulp.src(dirs.source + '/fonts/*.{ttf,woff,woff2,eot,svg}', {since: gulp.lastRun('fonts:copy')})
.pipe(newer(dirs.build + '/fonts')) // оставить в потоке только изменившиеся файлы
.pipe(size({
title: 'Размер',
showFiles: true,
showTotal: false,
}))
.pipe(gulp.dest(dirs.build + '/fonts'));
}); });
// console.log(styleFileImportsArray);
// Сборка SVG-спрайта для блока sprite-svg--localstorage // Обойдем объект с необходимыми блоками, поищем их импорты в массиве импортов
gulp.task('svgstore', function (callback) { let newImports = '';
let spritePath = dirs.source + '/blocks/sprite-svg--localstorage/svg/'; for (let blockName in blocksObject) {
if(fileExist(spritePath) !== false) { if(blocksObject[blockName].length) {
console.log('---------- Сборка SVG спрайта'); newImports += getNeedImport(blockName, blockName);
return gulp.src(spritePath + '*.svg') blocksObject[blockName].forEach(function(elementName) {
.pipe(svgmin(function (file) { newImports += getNeedImport(blockName, blockName+elementName);
return { });
plugins: [{
cleanupIDs: {
minify: true
}
}]
}
}))
.pipe(svgstore({ inlineSvg: true }))
.pipe(cheerio(function ($) {
$('svg').attr('style', 'display:none');
}))
.pipe(rename('sprite-svg--ls.svg'))
.pipe(size({
title: 'Размер',
showFiles: true,
showTotal: false,
}))
.pipe(gulp.dest(dirs.source + '/blocks/sprite-svg--localstorage/img'));
} }
else { else {
console.log('---------- Сборка SVG спрайта: нет папки с картинками'); newImports += getNeedImport(blockName, blockName);
callback();
} }
}); }
function getNeedImport(blockName, fileName){
// Сборка HTML let hasImport = false;
gulp.task('html', function() { // console.log('test '+blockName+'/'+fileName+'.css ');
console.log('---------- сборка HTML'); for (var i = 0; i < styleFileImportsArray.length; i++) {
return gulp.src(dirs.source + '/*.html') let blockImportRegExp = new RegExp('/'+blockName+'/'+fileName+'.css', 'i');
.pipe(fileinclude({ if(blockImportRegExp.test(styleFileImportsArray[i])) {
prefix: '@@', hasImport = true;
basepath: '@file', break;
indent: true, }
}))
.pipe(replace(/\n\s*<!--DEV[\s\S]+?-->/gm, ''))
.pipe(gulp.dest(dirs.build));
});
// Конкатенация и углификация Javascript
gulp.task('js', function (callback) {
if(blocks.js.length > 0){
console.log('---------- Обработка JS');
return gulp.src(blocks.js)
.pipe(gulpIf(isDev, sourcemaps.init()))
.pipe(concat('script.min.js'))
.pipe(gulpIf(!isDev, uglify()))
.on('error', notify.onError(function(err){
return {
title: 'Javascript uglify error',
message: err.message
}
}))
.pipe(gulpIf(isDev, sourcemaps.write('.')))
.pipe(size({
title: 'Размер',
showFiles: true,
showTotal: false,
}))
.pipe(gulp.dest(dirs.build + '/js'));
}
else {
console.log('---------- Обработка JS: в сборке нет JS-файлов');
callback();
} }
}); if(!hasImport) return '@import url('+dirs.srcPath+dirs.blocksDirName+'/'+blockName+'/'+fileName+'.css);\n'
else return '';
}
// console.log(newImports);
// Копирование JS-библиотек (если есть) // Запуск `NODE_ENV=production npm start [задача]` приведет к сборке без sourcemaps
gulp.task('js:copy', function (callback) { const isDev = !process.env.NODE_ENV || process.env.NODE_ENV == 'dev';
let jsLibs = [];
if(fileExistAndHasContent(dirs.source + '/js/jquery.3.1.1.min.js')) {
jsLibs.push(dirs.source + '/js/jquery.3.1.1.min.js');
}
if(jsLibs){
console.log('---------- Копирование JS-библиотек');
return gulp.src(jsLibs)
.pipe(gulp.dest(dirs.build + '/js'));
}
else {
callback();
}
});
// Очистка папки сборки // Очистка папки сборки
gulp.task('clean', function () { gulp.task('clean', function () {
console.log('---------- Очистка папки сборки'); console.log('---------- Очистка папки сборки');
return del([ return del([
dirs.build + '/**/*', dirs.buildPath + '/**/*',
'!' + dirs.build + '/readme.md' '!' + dirs.buildPath + '/readme.md'
]); ]);
}); });
// Сборка всего
gulp.task('build', gulp.series(
'clean',
'svgstore',
gulp.parallel('less', 'copy:css', 'img', 'js', 'js:copy', 'fonts:copy'),
'html'
));
// Локальный сервер, слежение
gulp.task('serve', gulp.series('build', function() {
browserSync.init({
server: dirs.build,
port: port,
startPath: 'index.html'
});
gulp.watch([
dirs.source + '/*.html',
dirs.source + '/_include/*.html',
dirs.source + '/blocks/**/*.html',
], gulp.series('html', reloader));
gulp.watch(blocks.less, gulp.series('less'));
if(blocks.img) {
gulp.watch(blocks.img, gulp.series('img', reloader));
}
if(blocks.js) {
gulp.watch(blocks.js, gulp.series('js', reloader));
}
gulp.watch(dirs.source + '/fonts/*.{ttf,woff,woff2,eot,svg}', gulp.series('fonts:copy', reloader));
}));
// Отправка в GH pages (ветку gh-pages репозитория)
gulp.task('deploy', function() {
console.log('---------- Публикация ./build/ на GH pages');
console.log('---------- '+ ghPagesUrl);
return gulp.src('./build/**/*')
.pipe(ghPages());
});
// Задача по умолчанию
gulp.task('default',
gulp.series('serve')
);
// Перезагрузка в браузере
function reloader(done) {
browserSync.reload();
done();
}
// Определение собираемых компонентов
function getComponentsFiles() {
// Создаем объект для служебных данных
let сomponentsFilesList = {
less: [], // тут будут LESS-файлы в том же порядке, в котором они подключены
js: [], // тут будут JS-файлы в том же порядке, в котором подключены LESS-файлы
img: [], // тут будет массив из «путь_до_блока/img/*.{jpg,jpeg,gif,png,svg}» для всех импортируемых блоков
additionalCss: [], // тут будут добавочные CSS-файлы блоков в том же порядке, в котором подключены LESS-файлы
};
// Читаем файл диспетчера подключений
let connectManager = fs.readFileSync(dirs.source + '/less/style.less', 'utf8');
// Делаем из строк массив, фильтруем массив, оставляя только строки с незакомментированными импортами
let fileSystem = connectManager.split('\n').filter(function(item) {
if(/^(\s*)@import/.test(item)) return true;
else return false;
});
// Обойдём массив и запишем его части в объект результирующей переменной
fileSystem.forEach(function(item, i) {
// Попробуем вычленить блок из строки импорта
let componentData = /\/blocks\/(.+?)(\/)(.+?)(?=.(less|css))/g.exec(item);
// Если это блок и получилось извлечь имя файла
if (componentData !== null && componentData[3]) {
let componentName = componentData[1]; // Название блока
let blockDir = dirs.source + '/blocks/' + componentName; // Папка блока
let componentFileName = componentData[3]; // Имя подключаемого файла без расширения
let jsFile = blockDir + '/' + componentFileName + '.js'; // Имя JS-файла, который нужно взять в сборку
let cssFile = blockDir + '/' + componentFileName + '.css'; // Имя CSS-файла, который нужно обработать
let imagesDir = blockDir + '/img'; // Папка с картинками, которую нужно взять в обработку
// Добавляем в массив с результатом LESS-файл
сomponentsFilesList.less.push(dirs.source + componentData[0] + '.' + componentData[4]);
// Если существует JS-файл — добавляем его в массив с результатом
if(fileExistAndHasContent(jsFile)) {
сomponentsFilesList.js.push(jsFile);
}
// Если существует CSS-файл — добавляем его в массив с результатом
if(fileExistAndHasContent(cssFile)) {
сomponentsFilesList.additionalCss.push(cssFile);
}
// Если есть папка с изображениями, добавляем её в массив с результатом
if(fileExist(imagesDir) !== false) {
сomponentsFilesList.img.push(imagesDir + '/*.{jpg,jpeg,gif,png,svg}');
}
}
});
// Добавим глобальныe LESS-файлы в массив с обрабатываемыми LESS-файлами
сomponentsFilesList.less.push(dirs.source + '/less/**/*.less');
// Добавим глобальный JS-файл в начало массива с обрабатываемыми JS-файлами
if(fileExistAndHasContent(dirs.source + '/js/global-script.js')) {
сomponentsFilesList.js.unshift(dirs.source + '/js/global-script.js');
}
// Если хочется иметь jQuery в конкатенируемом JS, раскомментируйте эти строки
// if(fileExistAndHasContent(dirs.source + '/js/jquery.js')) {
// сomponentsFilesList.js.unshift(dirs.source + '/js/jquery.js'); // добавляем в начало
// }
// Если хочется иметь в конкатенируемом JS ещё какие-то файлы, пишите это здесь
// if(fileExistAndHasContent(dirs.source + '/js/file_name.js')) {
// сomponentsFilesList.js.unshift(dirs.source + '/js/file_name.js'); // добавляем в начало
// или
// сomponentsFilesList.js.push(dirs.source + '/js/file_name.js'); // добавляем в конец
// }
// Добавим глобальный CSS-файл в начало массива с обрабатываемыми CSS-файлами
if(fileExistAndHasContent(dirs.source + '/css/global-css.css')) {
сomponentsFilesList.additionalCss.unshift(dirs.source + '/css/global-css.css');
}
// Если хочется иметь в папке сборки какие-то еще отдельные CSS-файлы, пишите их здесь
// if(fileExistAndHasContent(dirs.source + '/css/file_name.css')) {
// сomponentsFilesList.additionalCss.unshift(dirs.source + '/css/file_name.css');
// }
// Добавим глобальные изображения
сomponentsFilesList.img.unshift(dirs.source + '/img/*.{jpg,jpeg,gif,png,svg}');
сomponentsFilesList.img = uniqueArray(сomponentsFilesList.img);
// Возвращаем объект со служебными данными
return сomponentsFilesList;
}
// Проверка существования файла и его размера (размер менее 2байт == файла нет)
function fileExistAndHasContent(path) {
const fs = require('fs');
try {
fs.statSync(path);
if(fs.statSync(path).size > 1) return true;
else return false;
} catch(err) {
return !(err && err.code === 'ENOENT');
}
}
// Проверка существования файла
function fileExist(path) {
const fs = require('fs');
try {
fs.statSync(path);
} catch(err) {
return !(err && err.code === 'ENOENT');
}
}
// Оставить в массиве только уникальные значения (убрать повторы)
function uniqueArray(arr) {
var objectTemp = {};
for (var i = 0; i < arr.length; i++) {
var str = arr[i];
objectTemp[str] = true; // запомнить строку в виде свойства объекта
}
return Object.keys(objectTemp);
}
{ {
"name": "NTH-start-project", "name": "NTH-start-project",
"version": "1.1.0", "version": "2.0.0",
"description": "Start a library of blocks and technical solutions for HTML/CSS/js page layout.", "description": "Start a library of blocks and technical solutions for HTML/CSS/js page layout.",
"author": "Nikolay Gromov",
"license": "MIT",
"repository": {
"type": "git",
"url": "https://github.com/nicothin/NTH-start-project.git"
},
"configProject": {
"blocks": {
"page-header": [],
"page-footer": [
"__inner",
"--some-mod"
]
},
"dirs": {
"srcPath": "./src/",
"buildPath": "./build/",
"blocksDirName": "blocks"
}
},
"scripts": { "scripts": {
"test": "stylelint \"src/**/*.less\"", "test": "stylelint \"src/**/*.less\"",
"start": "gulp", "start": "gulp",
...@@ -9,49 +29,11 @@ ...@@ -9,49 +29,11 @@
"build": "cross-env NODE_ENV=production npm start build", "build": "cross-env NODE_ENV=production npm start build",
"precommit": "npm test" "precommit": "npm test"
}, },
"author": "Nikolay Gromov",
"license": "MIT",
"repository": {
"type": "git",
"url": "https://github.com/nicothin/NTH-start-project.git"
},
"devDependencies": { "devDependencies": {
"autoprefixer": "^6.3.3", "del": "^2.2.2",
"browser-sync": "^2.11.1", "gulp": "^3.9.1",
"cross-env": "^3.1.4", "gulp-postcss": "^6.3.0",
"css-mqpacker": "^5.0.1", "gulp-sequence": "^0.4.6",
"del": "^2.2.0", "postcss-media-variables": "^2.0.0"
"gulp": "github:gulpjs/gulp#4.0",
"gulp-cheerio": "^0.6.2",
"gulp-cleancss": "^0.2.2",
"gulp-concat": "^2.6.0",
"gulp-debug": "^3.0.0",
"gulp-file-include": "^1.0.0",
"gulp-gh-pages": "^0.5.4",
"gulp-if": "^2.0.0",
"gulp-imagemin": "^3.1.1",
"gulp-less": "^3.1.0",
"gulp-newer": "^1.1.0",
"gulp-notify": "^3.0.0",
"gulp-postcss": "^6.1.0",
"gulp-rename": "^1.2.2",
"gulp-replace": "^0.5.4",
"gulp-size": "^2.1.0",
"gulp-sourcemaps": "^2.4.1",
"gulp-svgmin": "^1.2.1",
"gulp-svgstore": "^6.1.0",
"gulp-uglify": "^2.0.1",
"husky": "^0.13.1",
"imagemin-pngquant": "^5.0.0",
"mkdirp": "^0.5.1",
"stylelint": "^7.1.0",
"stylelint-declaration-use-variable": "^1.5.0"
},
"config": {
"ghPages": "http://nicothin.github.io/NTH-start-project/index.html",
"directories": {
"source": "./src",
"build": "./build"
}
} }
} }
/**
* ВНИМАНИЕ!
* Список подключений из папки блоков при старте автоматизации сверяется со
* списком используемых блоков в ./package.json и если там указаны какие-либо
* блоки, отсутствующие здесь, их импорты будут добавлены в нижней части файла.
*/
@import url(variables.css);
@import url(some-block-TESE.css);
/* @import url(global.css); */
@import url(../blocks/page-header/page-header.css);
@import url(../blocks/btn/btn.css);
:root {
/* Базовые цвета https://colorscheme.ru/color-converter.html */
--black: hsl(0, 0%, 0%);
--gray-darkest: hsl(0, 0%, 10%);
--gray-darker: hsl(0, 0%, 20%);
--gray-darken: hsl(0, 0%, 30%);
--gray-dark: hsl(0, 0%, 40%);
--gray: hsl(0, 0%, 50%);
--gray-light: hsl(0, 0%, 60%);
--gray-lighten: hsl(0, 0%, 70%);
--gray-lighter: hsl(0, 0%, 80%);
--gray-lightest: hsl(0, 0%, 90%);
--white: hsl(0, 0%, 100%);
--color-main: hsl(208, 98%, 43%);
--color-success: hsl(120, 39%, 54%);
--color-danger: hsl(2, 64%, 58%);
/* Семантические цвета */
--text-color: var(--gray-darkest, hsl(0, 0%, 10%));
--text-color--muted: var(--gray, hsl(0, 0%, 50%));
--body-bg: var(--white, #fff);
--link-color: var(--color-main, hsl(208, 98%, 43%));
--link-color--hover: var(--color-main, hsl(208, 98%, 43%));
--border-color: var(--gray-light, hsl(0, 0%, 60%));
/* Базовая типографика */
--font-size: 14px;
--font-size--h1: 2.25em;
--font-size--h2: 1.875em;
--font-size--h3: 1.5em;
--font-size--h4: 1.25em;
--font-size--h5: 1em;
--font-size--h6: 1em;
--font-size--small: 0.75em;
--line-height: 1.375em;
--font-family: -apple-system, BlinkMacSystemFont, 'Roboto', 'Ubuntu', 'Droid Sans', 'Helvetica Neue', 'Arial', sans-serif;
--font-family--serif: 'Georgia', 'Times New Roman', 'Times', serif;
--font-family--monospace: 'Menlo', 'Monaco', 'Consolas', 'Liberation Mono', 'Courier New', monospace;
--font-family--headings: var(--font-family, inherit);
/* Breakepoints и @media */
--screen-sm: 600px;
@custom-media --screen-sm (min-width: var(--screen-sm));
@custom-media --screen-sm-2-md (min-width: var(--screen-sm)) and (max-width: calc(var(--screen-sm) - 1));
@custom-media --screen-before-sm (max-width: calc(var(--screen-sm) - 1));
/* --screen-md: 900px; */
/* @custom-media --screen-md (min-width: 900px); */
/* @custom-media --screen-md-max (max-width: 899px); */
/* --screen-lg: 1200px; */
/* @custom-media --screen-lg (min-width: 1200px); */
/* @custom-media --screen-lg-max (max-width: 1199px); */
/* --screen-xl: 1800px; */
/* @custom-media --screen-xl (min-width: 1800px); */
/* @custom-media --screen-xl-max (max-width: 1799px); */
}
/*
// Модульная сетка
@grid-columns: 12;
@grid-gutter-width: 30px;
// Ширины
@screen-xs: 0;
@screen-sm: 480px;
@screen-md: 768px;
@screen-lg: 992px;
@screen-xl: 1200px;
@screen-xxl: 1800px;
@container-sm: 100%;
@container-md: 100%;
@container-lg: @screen-lg - 30;
@container-xl: @screen-xl - 30;
@container-xxl: @screen-xxl - 30;
// Z-index
@z-index-dropdown: 10;
// Формы, кнопки
@field-padding-top: 0.313em;
@field-padding-bottom: 0.313em;
@field-padding-horizontal: 0.438em;
// Разное
@border-radius: 3px;
@opacity: 0.7;
@transition-time: 0.3s;
@shadow: 0 4px 2px -2px rgba(0, 0, 0, 0.3);
*/
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