Грузится

Настройки


Tools

Моя GULP сборка

Автоматизирует рутинные операции, позволяет верстать быстрее и качественней.

Файловая структура

gulp-starter-pack
|   gulpfile.js <!-- Корневой файл операций GULP -->
|   package.json
|   README.md
|   tailwind.config.cjs <!--Переменные Tailwind CSS-->
|   
+---dist <!-- Тут скомпилированный проект -->
|   |   index.html
|   |   
|   +---css
|   |       style.css <!-- Рабочий файл стилей -->
|   |       style.scss <!-- SCSS c сохранением вложенности -->
|   |       
|   +---files <!-- Любые файлы, не требующие обработки -->
|   +---fonts <!-- Сконвертированные шрифты (woff, woff2) -->
|   +---img <!-- Обработанные изображения -->
|   \---js
|           app.js <!-- Скомпилированные скрипты -->
|           
+---gulp <!-- Собственно тут весь функционал GULP -->
|   |   version.json <!-- Присвоенная версия CSS и JS файлам -->
|   |   
|   +---config
|   |       ftp.js <!-- Параметры соединения с FTP -->
|   |       path.js <!-- Все пути проекта -->
|   |       plugins.js <!-- Плагины, используемые в нескольких тасках -->
|   |       
|   \---tasks <!-- Собственно сценарии всех операций -->
|           copy.js <!-- Простое копирование содержимого папки src/files в dist -->
|           fonts.js <!-- Конвертирование шрифтов в woff, woff2 и подключение их в стили -->
|           ftp.js <!-- Выгрузка прокта на FTP -->
|           html.js <!-- Обработка Pug/Markdown, HTML -->
|           images.js <!-- Обработка растровых картинок -->
|           js.js <!-- Обработка JavaScript с помощью Webpack -->
|           postcss.js <!-- Обработка стилей с помощью PostCSS -->
|           reset.js <!--Очистка dist папки перед продакшен билдом -->
|           server.js <!-- Используемый порт и автообновление браузера -->
|           svgSprite.js <!-- Создание спрайта с svg иконками из папки icons -->
|           zip.js <!-- Упаковка проекта в zip архив -->
|           
+---node_modules
|   
\---src <!-- Рабочая папка с исходниками проекта -->
	|   index.html <!-- Дефолтный html файл. Тут же создаём 404.html и т.п. -->
	|   
	+---css <!-- Исходники PostCSS стилей (или обычный CSS) -->
	|   |   style.css <!-- Тут подключаются все стили -->
	|   |   
	|   +---defs
	|   |       defs.css <!-- Основные переменные проекта -->
	|   |       fonts.css <!-- Подключенные локальные шрифты таском fonts.js -->
	|   |       nullstyle.css <!-- Обнуление браузерных стилей -->
	|   |       
	|   \---schemes <!-- Цвета и эффекты проекта -->
	|           
	+---files <!-- Все файлы отсюда будут скопированы в dist без обработки -->
	|       favicon.ico
	|       
	+---fonts <!-- Локальные шрифты в любом формате -->
	+---html <!-- Тут все HTML импорты, чтобы не захламлять корневую папку -->
	|   |   homepage.html
	|   |   
	|   \---static <!-- Переиспользуемые фрагменты HTML -->
	|           dev-grid.html <!-- Сетка для контроля вёрстки -->
	|           footer.html
	|           header.html
	|           
	+---icons <!-- SVG, которые необходимо объединить в спрайт -->
	+---img <!-- Растровые картинки, которые необходимо обработать -->
	\---js <!-- Исходники JavaScript (ES6) -->
		|   app.js <!-- Подключение и инициация скриптов -->
		|   
		\---modules <!-- Объемные скрипты тут, чтобы не перегружать корневой файл -->
					functions.js

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

Основной (корневой) сценарий GULP находится в gulpfile.js, куда уже подключено всё остальное.

import { path } from './gulp/config/path.js';

В gulp/config/path.js указаны все пути к исходникам и целевому расположению обработанных файлов

const buildFolder = `./dist`;
// const buildFolder = process.env.npm_package_name;

Заменить верхнюю строку нижней, если вместо dist папки должна создаваться папка с названием проекта (значение «name» в package.json).

Также надо проверить, чтобы значение ftp в gulp/config/path.js (последняя строка) совпадало с названием корневой папки на хостинге.

Плагины и процесс обработки

Переиспользуемые плагины (которые используются, например, как для HTML, так и для CSS тасков) импортированы в gulp/config/plugins.js

import replace from 'gulp-replace'; // Замена содержимого файлов (ниже подробнее)
import plumber from 'gulp-plumber'; // Проверяет успешность отработки всей цепочки обработки в тасках
import notify from 'gulp-notify'; // Показывает ошибки выполнения тасков в панели уведомлений
import browsersync from 'browser-sync'; // Автообновление окна браузера в случае изменений
import newer from 'gulp-newer'; // Сравнивает дату изменения файлов (чтобы картинки постоянно не обрабатывать)
import ifPlugin from 'gulp-if'; // Позволяет использовать условия типа if…else в циклах GULP
import sourcemaps from 'gulp-sourcemaps'; // Гененрирует Source Maps CSS, JS

Пришлось сделать маску пути к корневой папке проекта через $, чтобы одновременно работали плагины VS Code «Path Autocomplete» и «Image preview» (я там даже отметился).

Потом этот префикс убирается в HTML таске плагином «gulp-replace»: app.plugins.replace(/\$img\//g, 'img/')

Опишу подробно что конкретно какой таск делает.

HTML

import fileInclude from 'gulp-file-include';
import webpHtmlNosvg from 'gulp-webp-html-nosvg';
import versionNumber from 'gulp-version-number';
// import pug from 'gulp-pug';

export const html = (done) => {
	app.gulp
		.src(app.path.src.html)
		.pipe(
			app.plugins.plumber(
				app.plugins.notify.onError({
					title: 'HTML',
					message: 'Error: <%= error.message %>',
				}),
			),
		)
		.pipe(fileInclude())
		// .pipe(
		// 	pug({
		// 		pretty: true,
		// 		verbose: true,
		// 	}),
		// )
		.pipe(app.plugins.replace(/\$img\//g, 'img/'))
		.pipe(webpHtmlNosvg())
		.pipe(
			app.plugins.if(
				app.isBuild,
				versionNumber({
					value: '%DT%',
					append: {
						key: '_v',
						cover: 0,
						to: ['css', 'js'],
					},
					output: {
						file: 'gulp/version.json',
					},
				}),
			),
		)
		.pipe(app.gulp.dest(app.path.build.html))
		.pipe(app.plugins.browsersync.stream());
	return done();
};
gulp-file-include

Вставляет в HTML содержимое файлов, добавленных через @@include('file.ext'). Работает не только с HTML, например так можно инлайнить SVG: @@include('../parts/logo.svg'). Можно передавать переменные в добавляемое.

Пример с передачей заголовка использован в этом проекте, но есть вот такое применение:

@@include('../any.svg',{"img":"icon_01.png"})
@@include('../any.svg',{"img":"icon_02.png"})
<image width="48" height="48" transform="translate(12 12)" href="@@img" />

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

gulp-pug

Преобразует Pug в HTML. Раскомментировать импорт и инициацию после fileInclude если нужно.

Если нужно обрабатывать Markdown, то вот плагин. Подключать также после fileInclude.

gulp-webp-html-nosvg

Делает из <img src="img/any.jpg"> такое:

<picture>
	<source srcset="img/any.webp" type="image/webp" />
	<img src="img/any.jpg" />
</picture>

Преобразовываться в WebP картинки будут в image таске.

gulp-version-number

Добавляет весию к js и css, чтобы при обновлении у клиентов сайта не использовалось старое из кеша. Пример:
<script src="js/app.js?_v=20221229143711"></script>
Для разработки не нужен, так что запускается только для продакшена.

CSS

Тут я конкретно угорел, чтобы сделать всё идеально, в процессе тестируя херову кучу плагинов и мутируя с их опциями. В итоге получилось вот что:

import postcss from 'gulp-postcss';
import rename from 'gulp-rename';
import cssnano from 'cssnano';
import cleanCss from 'gulp-clean-css';
import postcssCenter from 'postcss-center';
import webpcss from 'webp-in-css/plugin.js';
import willChange from 'postcss-will-change-transition';
import flexBugsFixes from 'postcss-flexbugs-fixes';
import postcssCustomMedia from 'postcss-custom-media';
import sortMediaQuery from 'postcss-sort-media-queries';
import postcssNested from 'postcss-nested';
import postcssImport from 'postcss-import';
import postcssMixins from 'postcss-mixins';
import postcssSimpleVars from 'postcss-simple-vars';
import postcssShort from 'postcss-short';
import postcssPrettify from 'postcss-prettify';
// import postcssPxtorem from 'postcss-pxtorem';

// import tailwindcss from 'tailwindcss';
// import tailwindConfig from '../../tailwind.config.cjs';

const postCSS = [
	postcssImport(),
	postcssCustomMedia(),
	postcssSimpleVars(),
	postcssMixins(),
	postcssShort(),
	postcssCenter(),
	// tailwindcss(tailwindConfig),
	postcssPrettify(),
];

const postCssOptionsDev = [
	postcssNested(),
	cssnano({
		preset: [
			'lite',
			{
				normalizeWhitespace: false,
				discardComments: false,
				cssDeclarationSorter: { order: 'smacss' },
			},
		],
	}),
];

const postCssOptionsBuild = [
	postcssNested(),
	sortMediaQuery(),
	flexBugsFixes(),
	webpcss(),
	// postcssPxtorem({ rootValue: 16 }),
	willChange(),
	cssnano({
		preset: [
			'advanced',
			{
				normalizeWhitespace: true,
				cssDeclarationSorter: { order: 'smacss' },
				discardComments: { removeAll: true },
				normalizeWhitespace: false,
			},
			// Preset options: https://cssnano.co/docs/what-are-optimisations/
		],
	}),
];

export const css = () => {
	app.gulp
		.src(app.path.src.css)
		.pipe(rename({ extname: '.scss' }))
		.pipe(postcss(postCSS))
		.pipe(app.gulp.dest(app.path.build.css));
	return app.gulp
		.src(app.path.src.css)
		.pipe(
			app.plugins.plumber(
				app.plugins.notify.onError({
					title: 'CSS',
					message: 'Error: <%= error.message %>',
				}),
			),
		)
		.pipe(app.plugins.if(app.isDev, app.plugins.sourcemaps.init()))
		.pipe(app.plugins.replace(/\$img\//g, '../img/'))
		.pipe(postcss(postCSS))
		.pipe(postcss(app.isBuild ? postCssOptionsBuild : postCssOptionsDev))
		.pipe(app.plugins.if(app.isBuild, cleanCss()))
		.pipe(app.plugins.if(app.isDev, app.plugins.sourcemaps.write()))
		.pipe(rename({ extname: '.css' }))
		.pipe(app.gulp.dest(app.path.build.css))
		.pipe(app.plugins.browsersync.stream());
};

Препроцессором был выбран PostCSS, т.к. он самый вариативный из-за кучи плагинов и довольно быстрый (гораздо быстрее обрабатывает, чем тот же SCSS).

gulp-postcss

Собственно сам препроцессор.

gulp-rename

Переименовывает файлы (например тут меняет .CSS на .SCSS, когда нужно сделать несжатую копию стилей).

cssnano

Охреневший плагин, который делает дофига всего. Если кратко, то я пришел к тому, что для разработки я использую пресет lite c отключенной минимизацией кода и сохранением комментов. Для продакшена пресет advanced с отключенной минимизацией кода — он минимизируется уже после создания несжатой копии в SCSS.

Для продакшена делается вот что:

  • Добавляются браузерные префиксы
  • Сортируются CSS свойства по системе smacss
  • Упрощаются калки (calc(2rem * 1.5) → 3rem)
  • Цвета преобразуются в HEX и по возможности сокращаются

Из всего, что делается я перечислил только вот столько. Ознакомиться со всеми с настройками пресетов можно тут. Если что-то нужно отключить или изменить опции, то вот тут:

cssnano({
	preset: [
		'advanced',
		{
			cssDeclarationSorter: { order: 'smacss' },
			discardComments: { removeAll: true },
			normalizeWhitespace: false,
		},
	],
}),
gulp-clean-css

Минимизирует CSS. Минификатор у cssnano кривоватый, поэтому этот.

postcss-center

Ускоряет вёрстку за счёт вот чего:

.element {
	top: center;
	left: center;
}
.element {
	position: absolute;
	top: 50%;
	left: 50%;
	margin-right: -50%;
	transform: translate(-50%, -50%)
}
webp-in-css

Добавляет стили для WebP

.logo {
  width: 30px;
  height: 30px;
  background: url(/logo.png);
}
.logo {
  width: 30px;
  height: 30px;
  background: url(/logo.webp) no-repeat;
}
body.webp .logo {
  background-image: url(/logo.webp);
}
body.no-webp .logo, body.no-js .logo {
  background-image: url(/logo.png);
}

Скрипт для проверки поддержки WebP находится в src/js/modules/functions.js

postcss-will-change-transition
.foo {
    transition: opacity 0.2s ease, width 0.2s ease;
}
.foo {
    transition: opacity 0.2s ease, width 0.2s ease;
    will-change: opacity, width;
}
postcss-flexbugs-fixes

Исправляет flex свойства так, чтобы некоторые браузеры не корёжило

.foo { flex: 1 0 calc(1vw - 1px); }
.foo {
  flex-grow: 1;
  flex-shrink: 0;
  flex-basis: calc(1vw - 1px);
}
postcss-custom-media

Меня всегда бесило писать эти сраные медиазапросы, но вот я нашел этот плагин и теперь снова могу радоваться жизни. Пример использования:

@custom-media --md (max-width: 852px);

h2 {
	font-size: 21px;
	@media (--md) {
		font-size: 18px;
	}
}
postcss-sort-media-queries

Из названия всё понятно — группирует стили медиазапроса в один блок.

postcss-nested

Убирает вложенность, по сути делая из PostCSS обычный CSS. Типа как SCSS препроцессор.

postcss-import

Корневой CSS файл проекта намекает:

@import 'defs/fonts';
@import 'defs/nullstyle';
@import 'schemes/heycisco-main';
@import 'defs/defs';

/* @tailwind base;
@tailwind components;
@tailwind utilities; */
postcss-mixins

Можно использовать миксины же. Инфа тут.

postcss-simple-vars

Добавляет поддержку переменных, как в SCSS — $width: 100px;

postcss-short

Плагин неплохо ускоряет вёрстку. Примеры:

.icon {
  size: 48px;
}
.frame {
  margin: * auto;
}
.canvas {
  color: #abccfc #212231;
}
.icon {
  width: 48px;
  height: 48px;
}
.frame {
  margin-right: auto;
  margin-left: auto;
}
.canvas {
  color: #abccfc;
  background-color: #212231;
}

Да дофига ещё чего. Вот тут вся инфа.

postcss-prettify

Форматирует код в нормальный вид. Используется при разработке и для создания несжатого SCSS.

postcss-pxtorem

Преобразует px в rem. В опциях функции нужно установить значение rem: postcssPxtorem({rootValue: 16})

Ну и раскомментировать импорт и вызов функции.

tailwindcss

Интеграция с Tailwind CSS. Раскомментировать соответствующие строки если нужен.

JS

import webpack from 'webpack-stream';

export const js = () => {
	return app.gulp
		.src(app.path.src.js)
		.pipe(
			app.plugins.plumber(
				app.plugins.notify.onError({
					title: 'JS',
					message: 'Error: <%= error.message %>',
				}),
			),
		)
		.pipe(app.plugins.if(app.isDev, app.plugins.sourcemaps.init()))
		.pipe(
			webpack({
				mode: app.isBuild ? 'production' : 'development',
				output: {
					filename: 'app.js',
				},
				module: {
					rules: [
						{
							test: /\.(js)$/,
							exclude: /(node_modules)/,
							loader: 'babel-loader',
							options: {
								presets: ['@babel/env'],
							},
						},
					],
				},
			}),
		)
		.pipe(app.plugins.if(app.isDev, app.plugins.sourcemaps.write()))
		.pipe(app.gulp.dest(app.path.build.js))
		.pipe(app.plugins.browsersync.stream());
};

Собственно Webpack всё сам прекрасно делает, а я только говорю ему дев или прод надо мутить.

Images

import webp from 'gulp-webp';
import imagemin from 'gulp-imagemin';
// import sharpResponsive from 'gulp-sharp-responsive';

export const images = () => {
	return (
		app.gulp
			.src(app.path.src.images)
			.pipe(
				app.plugins.plumber(
					app.plugins.notify.onError({
						title: 'IMAGES',
						message: 'Error: <%= error.message %>',
					}),
				),
			)
			.pipe(app.plugins.newer(app.path.build.images))
			.pipe(webp())
			// .pipe(
			// 	app.plugins.if(
			// 		app.isBuild,
			// 		sharpResponsive({
			// 			includeOriginalFile: true,
			// 			formats: [
			// 				{ width: 640, rename: { suffix: '-sm' } },
			// 				{ width: 1024, rename: { suffix: '-lg' } },
			// 			],
			// 		})
			// 	)
			// )
			.pipe(app.gulp.dest(app.path.build.images))
			.pipe(app.gulp.src(app.path.src.images))
			.pipe(app.plugins.newer(app.path.build.images))
			.pipe(
				app.plugins.if(
					app.isBuild,
					imagemin({
						progressive: true,
						svgoPlugins: [{ removeViewBox: false }],
						interlaced: true,
						optimizationLevel: 4,
					}),
				),
			)
			// .pipe(
			// 	app.plugins.if(
			// 		app.isBuild,
			// 		sharpResponsive({
			// 			includeOriginalFile: true,
			// 			formats: [
			// 				{ width: 640, rename: { suffix: '-sm' } },
			// 				{ width: 1024, rename: { suffix: '-lg' } },
			// 			],
			// 		})
			// 	)
			// )
			.pipe(app.gulp.dest(app.path.build.images))
			.pipe(app.gulp.src(app.path.src.svg))
			.pipe(app.gulp.dest(app.path.build.images))
			.pipe(app.plugins.browsersync.stream())
	);
};
gulp-webp

Преобразует изображения в WebP.

gulp-imagemin

Сжимает картинки.

imagemin({
	progressive: true,
	svgoPlugins: [{ removeViewBox: false }],
	interlaced: true,
	optimizationLevel: 4,
}),

progressive — Progressive JPEG. Это когда загружается так, а не так.

svgoPlugins > removeViewBox — выключено, потому что иногда SVG корёжит.

interlaced — гифки будут грузиться чередуя строки. Алгоритм другой, чем у JPG, но в целом что-то похожее.

optimizationLevel — опытным путём пришел к тому, что это норм сжатие.

gulp-sharp-responsive

Создаёт копии картинок другого размера. Раскомментировать строки, если нужен.

sharpResponsive({
	includeOriginalFile: true,
	formats: [
		{ width: 640, rename: { suffix: '-sm' } },
		{ width: 1024, rename: { suffix: '-lg' } },
	],
})

includeOriginalFile — если false, то картинку в исходном разрешении не потащит в целевую папку.

formats — тут указываются размеры и суффиксы ресайзнутых картинок.

Fonts

import fs from 'fs';
import fonter from 'gulp-fonter';
import ttf2woff2 from 'gulp-ttf2woff2';
let checkFonts = false;

export const otfToTtf = () => {
	let fontsDestDir = app.path.build.fonts;
	if (!fs.existsSync(fontsDestDir) || checkFonts === true) {
		checkFonts = true;
		return app.gulp
			.src(`${app.path.srcFolder}/fonts/{*,!icons}.otf`, {})
			.pipe(
				app.plugins.plumber(
					app.plugins.notify.onError({
						title: 'FONTS',
						message: 'Error: <%= error.message %>',
					}),
				),
			)
			.pipe(
				fonter({
					formats: ['ttf'],
				}),
			)
			.pipe(app.gulp.dest(`${app.path.srcFolder}/fonts/`));
	} else {
		return Promise.resolve('the value is ignored');
	}
};

export const ttfToWoff = () => {
	let fontsDestDir = app.path.build.fonts;
	if (!fs.existsSync(fontsDestDir) || checkFonts === true) {
		checkFonts = true;
		return app.gulp
			.src(`${app.path.srcFolder}/fonts/{*,!icons}.ttf`, {})
			.pipe(
				app.plugins.plumber(
					app.plugins.notify.onError({
						title: 'FONTS',
						message: 'Error: <%= error.message %>',
					}),
				),
			)
			.pipe(
				fonter({
					formats: ['woff'],
				}),
			)
			.pipe(app.gulp.dest(`${app.path.build.fonts}`))
			.pipe(app.gulp.src(`${app.path.srcFolder}/fonts/{*,!icons}.ttf`))
			.pipe(ttf2woff2())
			.pipe(app.gulp.dest(`${app.path.build.fonts}`));
	} else {
		console.log('"dest/fonts" уже существует!');
		return Promise.resolve('the value is ignored');
	}
};

export const iconFont = () => {
	return app.gulp
		.src(`${app.path.srcFolder}/fonts/icons/*`, {})
		.pipe(app.gulp.dest(`${app.path.build.fonts}/icons/`));
};

export const fontsStyle = () => {
	let fontsFile = `${app.path.srcFolder}/css/defs/fonts.css`;
	fs.readdir(app.path.build.fonts, function (err, fontsFiles) {
		if (fontsFiles) {
			if (!fs.existsSync(fontsFile)) {
				fs.writeFile(fontsFile, '', cb);
				let newFileOnly;
				for (var i = 0; i < fontsFiles.length; i++) {
					let fontFileName = fontsFiles[i].split('.')[0];
					if (newFileOnly !== fontFileName) {
						let fontName = fontFileName.split('-')[0]
							? fontFileName.split('-')[0]
							: fontFileName;
						let fontWeight = fontFileName.split('-')[1]
							? fontFileName.split('-')[1]
							: fontFileName;
						if (fontWeight.toLowerCase() === 'thin') {
							fontWeight = 100;
						} else if (fontWeight.toLowerCase() === 'extralight') {
							fontWeight = 200;
						} else if (fontWeight.toLowerCase() === 'light') {
							fontWeight = 300;
						} else if (fontWeight.toLowerCase() === 'medium') {
							fontWeight = 500;
						} else if (fontWeight.toLowerCase() === 'semibold') {
							fontWeight = 600;
						} else if (fontWeight.toLowerCase() === 'bold') {
							fontWeight = 700;
						} else if (
							fontWeight.toLowerCase() === 'extrabold' ||
							fontWeight.toLowerCase() === 'heavy'
						) {
							fontWeight = 800;
						} else if (fontWeight.toLowerCase() === 'black') {
							fontWeight = 900;
						} else {
							fontWeight = 400;
						}
						fs.appendFile(
							fontsFile,
							`@font-face {\n\tfont-family: ${fontName};\n\tfont-display: swap;\n\tsrc: url("../fonts/${fontFileName}.woff2") format("woff2"), url("../fonts/${fontFileName}.woff") format("woff");\n\tfont-weight: ${fontWeight};\n\tfont-style: normal;\n}\r\n`,
							cb,
						);
						newFileOnly = fontFileName;
					}
				}
			} else {
				console.log('"src/css/defs/fonts.css" уже существует!');
			}
		}
	});
	return app.gulp.src(`${app.path.srcFolder}`);
	function cb() {}
};
gulp-fonter

Преобразует шрифты в woff, TTFформаты. Собственно, TTF нужен только для следующего плагина, а на выходе будут только шрифты в woff и woff2 форматах.

gulp-ttf2woff2

Преобразует TTF в woff2.

Цикл

Если в папке src/fonts есть шрифты, а папки dist/fonts не существует, то шрифты будут обрабатываться. Соответственно, если добавлен новый шрифт, то нужно удалить папку dist/fonts, чтобы снова сконвертировать шрифты. Функции:

otfToTtf() => Вначале все OTF конвертируем в TTF (gulp-ttf2woff2 не поддерживает OTF).

ttfToWoff() => Затем все TTF конвертируем в woff. Конвертируем TTF в woff2.

fontsStyle() => Создаёт CSS файл, где подключаются шрифты. Запускается, если файла src/css/defs/fonts.css не существует. После того, как создаст неплохо бы проверить чего там оно наподключало, т.к. если названия шрифтов кривые, то придётся вручную подправлять.

Например, если в названии шрифта есть «light», то при в стили добавится font-weight = 300, а если исходный шрифт назывался «Montserrat-SemiBold.ttf», то при подключении будет font-weight = 600.

Значение по умолчанию — font-weight = 400

Прочее

Да, бля, влом печатать стало, потом распишу остальное. Короче:

gulp/tasks/svgSprite.js создаёт спрайт с SVG иконками в папке src/icons.

gulp/tasks/zip.js упаковывает файлы в ZIP архив.

gulp/tasks/ftp.js отправляет файлы на FTP (туда свои логины/пароли от FTP вхерачить надо).


Команды

  • npm run dev — режим разработки с автообновлением браузера.
  • npm run build — компиляция под продакшен.
  • npm run zip — упаковка проекта в zip архив.
  • npm run ftp — выгрузка проекта на FTP.
  • npm run icons — создание SVG спрайта с иконками.

Ссылка на сборку