diff --git a/.babelrc b/.babelrc
index 30b08cc2..b4d43ab3 100644
--- a/.babelrc
+++ b/.babelrc
@@ -1,5 +1,3 @@
{
- "presets": [
- ["@babel/preset-env", {"modules": false}]
- ]
+ "presets": ["@babel/preset-env"]
}
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
index 4a1c61ba..960be9a0 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,11 +1,3 @@
-.DS_Store
-.AppleDouble
-.LSOverride
-.vs
-
-.vscode/
-node_modules/
-package.lock.json
-yarn.lock
+node_modules
package-lock.json
-yarn.lock
+yarn.lock
\ No newline at end of file
diff --git a/README.md b/README.md
deleted file mode 100644
index 45746311..00000000
--- a/README.md
+++ /dev/null
@@ -1,43 +0,0 @@
-
-
-[](https://css_notes.dyaka.com/startpack)
-
-# Easy-webdev-startpack
-
-    
-
-## Сборка Gulp4 с плагинами для разработки вебпроектов.
-
-Вы можете поддержать разработку проекта, перейдя [по ссылке](https://css_notes.diaka.ua/startpack) и оставив мне что-то на кофе.
-
-[](http://www.youtube.com/watch?v=_j38UEpskPU "Мануал по работе со сборкой Easy-webdev-startpack")
-
-Смотрите [вики](https://github.com/budfy/Easy-webdev-startpack/wiki) сборки, чтобы узнать, как ею пользоваться правильно. В вики справа есть разделы и они по мере наличия времени и возникновения вопросов дополняются.
-
-
-## Что есть в сборке:
-
-- компилятор для препроцессора scss/sass;
-- минификация готового css;
-- автопрефиксер;
-- импорт одних файлов в другие, который позволяет собирать html из модулей;
-- babel;
-- конвертация шрифтов из ttf в eot, woff и woff2;
-- полуавтоматическое формирование и подключение @font-face;
-- для живого обновления страниц - browsersync;
-- карта исходников для отображения в браузере, из какого scss взят кусок css;
-- сжатие изображений;
-- адекватное сжатие png через pngquant;
-- сжатие svg;
-- создание svg-спрайтов и конвертация svg в background-image;
-- ~~конвертация растровых изображений в webp и **автозамена в html и css ресурсов** для загрузки webp и подмены на jpg/png если браузер не понимает~~ **удалено из текущей версии в связи с неработоспособностью плагинов**;
-- живая перезагрузка при работе с PHP (совместно с openserver);
-- вывод размеров файлов в консоли во время работы gulp (для понимания, что сжалось и на сколько);
-
-## Тасклист следующей версии:
-
-- создание png-спрайтов;
-- разделение задач на dev и build (работа с разными плагинами и вызов разными командами);
-- etc.
-
-## Об ошибках, пожалуйста, сообщайте в [ишьюс](https://github.com/budfy/Easy-webdev-startpack/issues). Там же можно оставить свои предложения по функционалу сборки.
diff --git a/_config.yml b/_config.yml
deleted file mode 100644
index b8497135..00000000
--- a/_config.yml
+++ /dev/null
@@ -1 +0,0 @@
-theme: jekyll-theme-leap-day
\ No newline at end of file
diff --git a/src/svg/include/.gitkeep b/build/.gitkeep
similarity index 100%
rename from src/svg/include/.gitkeep
rename to build/.gitkeep
diff --git a/build/css/libs.min.css b/build/css/libs.min.css
deleted file mode 100644
index 6b3cf8d8..00000000
--- a/build/css/libs.min.css
+++ /dev/null
@@ -1,3 +0,0 @@
-/*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */html{line-height:1.15;-webkit-text-size-adjust:100%}body{margin:0}main{display:block}h1{font-size:2em;margin:0.67em 0}hr{box-sizing:content-box;height:0;overflow:visible}pre{font-family:monospace, monospace;font-size:1em}a{background-color:transparent}abbr[title]{border-bottom:none;text-decoration:underline;text-decoration:underline dotted}b,strong{font-weight:bolder}code,kbd,samp{font-family:monospace, monospace;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-0.25em}sup{top:-0.5em}img{border-style:none}button,input,optgroup,select,textarea{font-family:inherit;font-size:100%;line-height:1.15;margin:0}button,input{overflow:visible}button,select{text-transform:none}button,[type="button"],[type="reset"],[type="submit"]{-webkit-appearance:button}button::-moz-focus-inner,[type="button"]::-moz-focus-inner,[type="reset"]::-moz-focus-inner,[type="submit"]::-moz-focus-inner{border-style:none;padding:0}button:-moz-focusring,[type="button"]:-moz-focusring,[type="reset"]:-moz-focusring,[type="submit"]:-moz-focusring{outline:1px dotted ButtonText}fieldset{padding:0.35em 0.75em 0.625em}legend{box-sizing:border-box;color:inherit;display:table;max-width:100%;padding:0;white-space:normal}progress{vertical-align:baseline}textarea{overflow:auto}[type="checkbox"],[type="radio"]{box-sizing:border-box;padding:0}[type="number"]::-webkit-inner-spin-button,[type="number"]::-webkit-outer-spin-button{height:auto}[type="search"]{-webkit-appearance:textfield;outline-offset:-2px}[type="search"]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}details{display:block}summary{display:list-item}template{display:none}[hidden]{display:none}
-
-/*# sourceMappingURL=../sourcemaps/libs.min.css.map */
diff --git a/build/css/style.min.css b/build/css/style.min.css
deleted file mode 100644
index c46193d2..00000000
--- a/build/css/style.min.css
+++ /dev/null
@@ -1,3 +0,0 @@
-/*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */html{line-height:1.15;-webkit-text-size-adjust:100%}body{margin:0}details,main{display:block}h1{font-size:2em;margin:.67em 0}hr{-webkit-box-sizing:content-box;box-sizing:content-box;height:0;overflow:visible}code,kbd,pre,samp{font-family:monospace,monospace;font-size:1em}a{background-color:transparent}abbr[title]{border-bottom:none;text-decoration:underline;-webkit-text-decoration:underline dotted;text-decoration:underline dotted}b,strong{font-weight:bolder}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}img{border-style:none}button,input,optgroup,select,textarea{font-family:inherit;font-size:100%;line-height:1.15;margin:0}button,input{overflow:visible}button,select{text-transform:none}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{border-style:none;padding:0}[type=button]:-moz-focusring,[type=reset]:-moz-focusring,[type=submit]:-moz-focusring,button:-moz-focusring{outline:ButtonText dotted 1px}fieldset{padding:.35em .75em .625em}legend{-webkit-box-sizing:border-box;box-sizing:border-box;color:inherit;display:table;max-width:100%;padding:0;white-space:normal}progress{vertical-align:baseline}textarea{overflow:auto}[type=checkbox],[type=radio]{-webkit-box-sizing:border-box;box-sizing:border-box;padding:0}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}[hidden],template{display:none}
-html{-webkit-box-sizing:border-box;box-sizing:border-box;font-size:16px}*,::after,::before{-webkit-box-sizing:inherit;box-sizing:inherit}body,html{height:100%;position:relative}h1,h2,h3,h4,h5,h6,li,ol,p,ul{margin:0}button:focus,input:focus{outline:transparent}button{padding:0;margin:0;border:0;background-color:transparent;border-radius:0;cursor:pointer;-webkit-appearance:none}.flex{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:start;-ms-flex-align:start;align-items:flex-start;-webkit-box-pack:start;-ms-flex-pack:start;justify-content:flex-start}.--just-space{-webkit-box-pack:justify;-ms-flex-pack:justify;justify-content:space-between}.--just-center{-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center}.--just-end{-webkit-box-pack:end;-ms-flex-pack:end;justify-content:flex-end}.--align-str{-webkit-box-align:stretch;-ms-flex-align:stretch;align-items:stretch}.--align-center{-webkit-box-align:center;-ms-flex-align:center;align-items:center}.--align-end{-webkit-box-align:end;-ms-flex-align:end;align-items:flex-end}
-/*# sourceMappingURL=../sourcemaps/style.min.css.map */
diff --git a/build/index.html b/build/index.html
deleted file mode 100644
index cb2fd71f..00000000
--- a/build/index.html
+++ /dev/null
@@ -1,33 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
- Easy webdev startpack - test
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/build/sourcemaps/libs.min.css.map b/build/sourcemaps/libs.min.css.map
deleted file mode 100644
index b50b1883..00000000
--- a/build/sourcemaps/libs.min.css.map
+++ /dev/null
@@ -1 +0,0 @@
-{"version":3,"sources":["normalize.css"],"names":[],"mappings":"AAAA,4EAA4E,AAU5E,IAAI,AAAC,CACH,WAAW,CAAE,IAAI,CACjB,wBAAwB,CAAE,IAAI,CAC/B,AASD,IAAI,AAAC,CACH,MAAM,CAAE,CAAC,CACV,AAMD,IAAI,AAAC,CACH,OAAO,CAAE,KAAK,CACf,AAOD,EAAE,AAAC,CACD,SAAS,CAAE,GAAG,CACd,MAAM,CAAE,QAAQ,CACjB,AAUD,EAAE,AAAC,CACD,UAAU,CAAE,WAAW,CACvB,MAAM,CAAE,CAAC,CACT,QAAQ,CAAE,OAAO,CAClB,AAOD,GAAG,AAAC,CACF,WAAW,CAAE,oBAAoB,CACjC,SAAS,CAAE,GAAG,CACf,AASD,CAAC,AAAC,CACA,gBAAgB,CAAE,WAAW,CAC9B,AAOD,IAAI,CAAA,KAAC,CAAO,CACV,aAAa,CAAE,IAAI,CACnB,eAAe,CAAE,SAAS,CAC1B,eAAe,CAAE,gBAAgB,CAClC,AAMD,CAAC,CACD,MAAM,AAAC,CACL,WAAW,CAAE,MAAM,CACpB,AAOD,IAAI,CACJ,GAAG,CACH,IAAI,AAAC,CACH,WAAW,CAAE,oBAAoB,CACjC,SAAS,CAAE,GAAG,CACf,AAMD,KAAK,AAAC,CACJ,SAAS,CAAE,GAAG,CACf,AAOD,GAAG,CACH,GAAG,AAAC,CACF,SAAS,CAAE,GAAG,CACd,WAAW,CAAE,CAAC,CACd,QAAQ,CAAE,QAAQ,CAClB,cAAc,CAAE,QAAQ,CACzB,AAED,GAAG,AAAC,CACF,MAAM,CAAE,OAAO,CAChB,AAED,GAAG,AAAC,CACF,GAAG,CAAE,MAAM,CACZ,AASD,GAAG,AAAC,CACF,YAAY,CAAE,IAAI,CACnB,AAUD,MAAM,CACN,KAAK,CACL,QAAQ,CACR,MAAM,CACN,QAAQ,AAAC,CACP,WAAW,CAAE,OAAO,CACpB,SAAS,CAAE,IAAI,CACf,WAAW,CAAE,IAAI,CACjB,MAAM,CAAE,CAAC,CACV,AAOD,MAAM,CACN,KAAK,AAAC,CACJ,QAAQ,CAAE,OAAO,CAClB,AAOD,MAAM,CACN,MAAM,AAAC,CACL,cAAc,CAAE,IAAI,CACrB,AAMD,MAAM,EACN,IAAC,CAAK,QAAL,AAAa,GACd,IAAC,CAAK,OAAL,AAAY,GACb,IAAC,CAAK,QAAL,AAAa,CAAE,CACd,kBAAkB,CAAE,MAAM,CAC3B,AAMD,MAAM,kBAAkB,EACxB,IAAC,CAAK,QAAL,AAAa,CAAC,kBAAkB,EACjC,IAAC,CAAK,OAAL,AAAY,CAAC,kBAAkB,EAChC,IAAC,CAAK,QAAL,AAAa,CAAC,kBAAkB,AAAC,CAChC,YAAY,CAAE,IAAI,CAClB,OAAO,CAAE,CAAC,CACX,AAMD,MAAM,eAAe,EACrB,IAAC,CAAK,QAAL,AAAa,CAAC,eAAe,EAC9B,IAAC,CAAK,OAAL,AAAY,CAAC,eAAe,EAC7B,IAAC,CAAK,QAAL,AAAa,CAAC,eAAe,AAAC,CAC7B,OAAO,CAAE,qBAAqB,CAC/B,AAMD,QAAQ,AAAC,CACP,OAAO,CAAE,qBAAqB,CAC/B,AASD,MAAM,AAAC,CACL,UAAU,CAAE,UAAU,CACtB,KAAK,CAAE,OAAO,CACd,OAAO,CAAE,KAAK,CACd,SAAS,CAAE,IAAI,CACf,OAAO,CAAE,CAAC,CACV,WAAW,CAAE,MAAM,CACpB,AAMD,QAAQ,AAAC,CACP,cAAc,CAAE,QAAQ,CACzB,AAMD,QAAQ,AAAC,CACP,QAAQ,CAAE,IAAI,CACf,CAOD,IAAC,CAAK,UAAL,AAAe,GAChB,IAAC,CAAK,OAAL,AAAY,CAAE,CACb,UAAU,CAAE,UAAU,CACtB,OAAO,CAAE,CAAC,CACX,CAMD,IAAC,CAAK,QAAL,AAAa,CAAC,2BAA2B,EAC1C,IAAC,CAAK,QAAL,AAAa,CAAC,2BAA2B,AAAC,CACzC,MAAM,CAAE,IAAI,CACb,CAOD,IAAC,CAAK,QAAL,AAAa,CAAE,CACd,kBAAkB,CAAE,SAAS,CAC7B,cAAc,CAAE,IAAI,CACrB,CAMD,IAAC,CAAK,QAAL,AAAa,CAAC,2BAA2B,AAAC,CACzC,kBAAkB,CAAE,IAAI,CACzB,AAOD,4BAA4B,AAAC,CAC3B,kBAAkB,CAAE,MAAM,CAC1B,IAAI,CAAE,OAAO,CACd,AASD,OAAO,AAAC,CACN,OAAO,CAAE,KAAK,CACf,AAMD,OAAO,AAAC,CACN,OAAO,CAAE,SAAS,CACnB,AASD,QAAQ,AAAC,CACP,OAAO,CAAE,IAAI,CACd,CAMD,MAAC,CAAQ,CACP,OAAO,CAAE,IAAI,CACd","file":"../normalize.css/libs.min.css","sourcesContent":["/*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */\n\n/* Document\n ========================================================================== */\n\n/**\n * 1. Correct the line height in all browsers.\n * 2. Prevent adjustments of font size after orientation changes in iOS.\n */\n\nhtml {\n line-height: 1.15; /* 1 */\n -webkit-text-size-adjust: 100%; /* 2 */\n}\n\n/* Sections\n ========================================================================== */\n\n/**\n * Remove the margin in all browsers.\n */\n\nbody {\n margin: 0;\n}\n\n/**\n * Render the `main` element consistently in IE.\n */\n\nmain {\n display: block;\n}\n\n/**\n * Correct the font size and margin on `h1` elements within `section` and\n * `article` contexts in Chrome, Firefox, and Safari.\n */\n\nh1 {\n font-size: 2em;\n margin: 0.67em 0;\n}\n\n/* Grouping content\n ========================================================================== */\n\n/**\n * 1. Add the correct box sizing in Firefox.\n * 2. Show the overflow in Edge and IE.\n */\n\nhr {\n box-sizing: content-box; /* 1 */\n height: 0; /* 1 */\n overflow: visible; /* 2 */\n}\n\n/**\n * 1. Correct the inheritance and scaling of font size in all browsers.\n * 2. Correct the odd `em` font sizing in all browsers.\n */\n\npre {\n font-family: monospace, monospace; /* 1 */\n font-size: 1em; /* 2 */\n}\n\n/* Text-level semantics\n ========================================================================== */\n\n/**\n * Remove the gray background on active links in IE 10.\n */\n\na {\n background-color: transparent;\n}\n\n/**\n * 1. Remove the bottom border in Chrome 57-\n * 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari.\n */\n\nabbr[title] {\n border-bottom: none; /* 1 */\n text-decoration: underline; /* 2 */\n text-decoration: underline dotted; /* 2 */\n}\n\n/**\n * Add the correct font weight in Chrome, Edge, and Safari.\n */\n\nb,\nstrong {\n font-weight: bolder;\n}\n\n/**\n * 1. Correct the inheritance and scaling of font size in all browsers.\n * 2. Correct the odd `em` font sizing in all browsers.\n */\n\ncode,\nkbd,\nsamp {\n font-family: monospace, monospace; /* 1 */\n font-size: 1em; /* 2 */\n}\n\n/**\n * Add the correct font size in all browsers.\n */\n\nsmall {\n font-size: 80%;\n}\n\n/**\n * Prevent `sub` and `sup` elements from affecting the line height in\n * all browsers.\n */\n\nsub,\nsup {\n font-size: 75%;\n line-height: 0;\n position: relative;\n vertical-align: baseline;\n}\n\nsub {\n bottom: -0.25em;\n}\n\nsup {\n top: -0.5em;\n}\n\n/* Embedded content\n ========================================================================== */\n\n/**\n * Remove the border on images inside links in IE 10.\n */\n\nimg {\n border-style: none;\n}\n\n/* Forms\n ========================================================================== */\n\n/**\n * 1. Change the font styles in all browsers.\n * 2. Remove the margin in Firefox and Safari.\n */\n\nbutton,\ninput,\noptgroup,\nselect,\ntextarea {\n font-family: inherit; /* 1 */\n font-size: 100%; /* 1 */\n line-height: 1.15; /* 1 */\n margin: 0; /* 2 */\n}\n\n/**\n * Show the overflow in IE.\n * 1. Show the overflow in Edge.\n */\n\nbutton,\ninput { /* 1 */\n overflow: visible;\n}\n\n/**\n * Remove the inheritance of text transform in Edge, Firefox, and IE.\n * 1. Remove the inheritance of text transform in Firefox.\n */\n\nbutton,\nselect { /* 1 */\n text-transform: none;\n}\n\n/**\n * Correct the inability to style clickable types in iOS and Safari.\n */\n\nbutton,\n[type=\"button\"],\n[type=\"reset\"],\n[type=\"submit\"] {\n -webkit-appearance: button;\n}\n\n/**\n * Remove the inner border and padding in Firefox.\n */\n\nbutton::-moz-focus-inner,\n[type=\"button\"]::-moz-focus-inner,\n[type=\"reset\"]::-moz-focus-inner,\n[type=\"submit\"]::-moz-focus-inner {\n border-style: none;\n padding: 0;\n}\n\n/**\n * Restore the focus styles unset by the previous rule.\n */\n\nbutton:-moz-focusring,\n[type=\"button\"]:-moz-focusring,\n[type=\"reset\"]:-moz-focusring,\n[type=\"submit\"]:-moz-focusring {\n outline: 1px dotted ButtonText;\n}\n\n/**\n * Correct the padding in Firefox.\n */\n\nfieldset {\n padding: 0.35em 0.75em 0.625em;\n}\n\n/**\n * 1. Correct the text wrapping in Edge and IE.\n * 2. Correct the color inheritance from `fieldset` elements in IE.\n * 3. Remove the padding so developers are not caught out when they zero out\n * `fieldset` elements in all browsers.\n */\n\nlegend {\n box-sizing: border-box; /* 1 */\n color: inherit; /* 2 */\n display: table; /* 1 */\n max-width: 100%; /* 1 */\n padding: 0; /* 3 */\n white-space: normal; /* 1 */\n}\n\n/**\n * Add the correct vertical alignment in Chrome, Firefox, and Opera.\n */\n\nprogress {\n vertical-align: baseline;\n}\n\n/**\n * Remove the default vertical scrollbar in IE 10+.\n */\n\ntextarea {\n overflow: auto;\n}\n\n/**\n * 1. Add the correct box sizing in IE 10.\n * 2. Remove the padding in IE 10.\n */\n\n[type=\"checkbox\"],\n[type=\"radio\"] {\n box-sizing: border-box; /* 1 */\n padding: 0; /* 2 */\n}\n\n/**\n * Correct the cursor style of increment and decrement buttons in Chrome.\n */\n\n[type=\"number\"]::-webkit-inner-spin-button,\n[type=\"number\"]::-webkit-outer-spin-button {\n height: auto;\n}\n\n/**\n * 1. Correct the odd appearance in Chrome and Safari.\n * 2. Correct the outline style in Safari.\n */\n\n[type=\"search\"] {\n -webkit-appearance: textfield; /* 1 */\n outline-offset: -2px; /* 2 */\n}\n\n/**\n * Remove the inner padding in Chrome and Safari on macOS.\n */\n\n[type=\"search\"]::-webkit-search-decoration {\n -webkit-appearance: none;\n}\n\n/**\n * 1. Correct the inability to style clickable types in iOS and Safari.\n * 2. Change font properties to `inherit` in Safari.\n */\n\n::-webkit-file-upload-button {\n -webkit-appearance: button; /* 1 */\n font: inherit; /* 2 */\n}\n\n/* Interactive\n ========================================================================== */\n\n/*\n * Add the correct display in Edge, IE 10+, and Firefox.\n */\n\ndetails {\n display: block;\n}\n\n/*\n * Add the correct display in all browsers.\n */\n\nsummary {\n display: list-item;\n}\n\n/* Misc\n ========================================================================== */\n\n/**\n * Add the correct display in IE 10+.\n */\n\ntemplate {\n display: none;\n}\n\n/**\n * Add the correct display in IE 10.\n */\n\n[hidden] {\n display: none;\n}\n"]}
\ No newline at end of file
diff --git a/build/sourcemaps/style.min.css.map b/build/sourcemaps/style.min.css.map
deleted file mode 100644
index 5d58dcd1..00000000
--- a/build/sourcemaps/style.min.css.map
+++ /dev/null
@@ -1 +0,0 @@
-{"version":3,"sources":["libs.scss","global/_04-global.scss","global/_01-mixins.scss"],"names":[],"mappings":"AAAA,4EAA4E,KAW1E,YAAA,KACA,yBAAA,KACD,KAUC,OAAA,EA+RD,QA9RA,KAOC,QAAA,MACD,GAQC,UAAA,IACA,OAAA,MAAA,EACD,GAWC,mBAAA,YAAA,WAAA,YACA,OAAA,EACA,SAAA,QA0CD,KAAA,IAzCA,IAyCA,KAjCC,YAAA,SAAA,CAAA,UACA,UAAA,IACD,EAUC,iBAAA,YACD,YAQC,mBACA,gBAAA,UACA,wBAAA,UAAA,OAAA,gBAAA,UAAA,OACD,EAAA,OAQC,YAAA,OAaD,MAOC,UAAA,IACD,IAAA,IASC,UAAA,IACA,YAAA,EACA,SAAA,SACA,eAAA,SACD,IAGC,OAAA,OACD,IAGC,IAAA,MACD,IAUC,aAAA,KACD,OAAA,MAAA,SAAA,OAAA,SAeC,YAAA,QACA,UAAA,KACA,YAAA,KACA,OAAA,EACD,OAAA,MASC,SAAA,QACD,OAAA,OASC,eAAA,KACD,cAAA,aAAA,cAAA,OAUC,mBAAA,OACD,gCAAA,+BAAA,gCAAA,yBAUC,aAAA,KACA,QAAA,EACD,6BAAA,4BAAA,6BAAA,sBAUC,QAAA,WAAA,OAAA,IACD,SAOC,QAAA,MAAA,MAAA,OACD,OAUC,mBAAA,WAAA,WAAA,WACA,MAAA,QACA,QAAA,MACA,UAAA,KACA,QAAA,EACA,YAAA,OACD,SAOC,eAAA,SACD,SAOC,SAAA,KACD,gBAAA,aASC,mBAAA,WAAA,WAAA,WACA,QAAA,EACD,yCAAA,yCAQC,OAAA,KACD,cAQC,mBAAA,UACA,eAAA,KACD,yCAOC,mBAAA,KACD,6BAQC,mBAAA,OACA,KAAA,QAYD,QAOC,QAAA,UAYD,SAXA,SAUC,QAAA;ACnVF,KACC,mBAAA,WAAA,WAAA,WACA,UAAA,KACA,EAAA,QAAA,SAKA,mBAAA,QAAA,WAAA,QACA,KAAA,KAIA,OAAA,KACA,SAAA,SACA,GAAA,GAAA,GAAA,GAAA,GAAA,GAAA,GAAA,GAAA,EAAA,GAcA,OAAA,EACA,aAAA,YAIA,QAAA,YACA,OCsBA,QAAA,EACA,OAAA,EACA,OAAA,EACA,iBAAA,YACA,cAAA,EACA,OAAA,QACA,mBAAA,KDxBA,MAGA,QAAA,YAAA,QAAA,YAAA,QAAA,KACA,kBAAA,MAAA,eAAA,MAAA,YAAA,WACA,iBAAA,MAAA,cAAA,MAAA,gBAAA,WACA,cAGA,iBAAA,QAAA,cAAA,QAAA,gBAAA,cACA,eAGA,iBAAA,OAAA,cAAA,OAAA,gBAAA,OACA,YAGA,iBAAA,IAAA,cAAA,IAAA,gBAAA,SACA,aAGA,kBAAA,QAAA,eAAA,QAAA,YAAA,QACA,gBAGA,kBAAA,OAAA,eAAA,OAAA,YAAA,OACA,aAGA,kBAAA,IAAA,eAAA,IAAA,YAAA","file":"../scss/style.min.css","sourcesContent":["/*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */\n\n/* Document\n ========================================================================== */\n\n/**\n * 1. Correct the line height in all browsers.\n * 2. Prevent adjustments of font size after orientation changes in iOS.\n */\n\nhtml {\n line-height: 1.15; /* 1 */\n -webkit-text-size-adjust: 100%; /* 2 */\n}\n\n/* Sections\n ========================================================================== */\n\n/**\n * Remove the margin in all browsers.\n */\n\nbody {\n margin: 0;\n}\n\n/**\n * Render the `main` element consistently in IE.\n */\n\nmain {\n display: block;\n}\n\n/**\n * Correct the font size and margin on `h1` elements within `section` and\n * `article` contexts in Chrome, Firefox, and Safari.\n */\n\nh1 {\n font-size: 2em;\n margin: 0.67em 0;\n}\n\n/* Grouping content\n ========================================================================== */\n\n/**\n * 1. Add the correct box sizing in Firefox.\n * 2. Show the overflow in Edge and IE.\n */\n\nhr {\n box-sizing: content-box; /* 1 */\n height: 0; /* 1 */\n overflow: visible; /* 2 */\n}\n\n/**\n * 1. Correct the inheritance and scaling of font size in all browsers.\n * 2. Correct the odd `em` font sizing in all browsers.\n */\n\npre {\n font-family: monospace, monospace; /* 1 */\n font-size: 1em; /* 2 */\n}\n\n/* Text-level semantics\n ========================================================================== */\n\n/**\n * Remove the gray background on active links in IE 10.\n */\n\na {\n background-color: transparent;\n}\n\n/**\n * 1. Remove the bottom border in Chrome 57-\n * 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari.\n */\n\nabbr[title] {\n border-bottom: none; /* 1 */\n text-decoration: underline; /* 2 */\n text-decoration: underline dotted; /* 2 */\n}\n\n/**\n * Add the correct font weight in Chrome, Edge, and Safari.\n */\n\nb,\nstrong {\n font-weight: bolder;\n}\n\n/**\n * 1. Correct the inheritance and scaling of font size in all browsers.\n * 2. Correct the odd `em` font sizing in all browsers.\n */\n\ncode,\nkbd,\nsamp {\n font-family: monospace, monospace; /* 1 */\n font-size: 1em; /* 2 */\n}\n\n/**\n * Add the correct font size in all browsers.\n */\n\nsmall {\n font-size: 80%;\n}\n\n/**\n * Prevent `sub` and `sup` elements from affecting the line height in\n * all browsers.\n */\n\nsub,\nsup {\n font-size: 75%;\n line-height: 0;\n position: relative;\n vertical-align: baseline;\n}\n\nsub {\n bottom: -0.25em;\n}\n\nsup {\n top: -0.5em;\n}\n\n/* Embedded content\n ========================================================================== */\n\n/**\n * Remove the border on images inside links in IE 10.\n */\n\nimg {\n border-style: none;\n}\n\n/* Forms\n ========================================================================== */\n\n/**\n * 1. Change the font styles in all browsers.\n * 2. Remove the margin in Firefox and Safari.\n */\n\nbutton,\ninput,\noptgroup,\nselect,\ntextarea {\n font-family: inherit; /* 1 */\n font-size: 100%; /* 1 */\n line-height: 1.15; /* 1 */\n margin: 0; /* 2 */\n}\n\n/**\n * Show the overflow in IE.\n * 1. Show the overflow in Edge.\n */\n\nbutton,\ninput { /* 1 */\n overflow: visible;\n}\n\n/**\n * Remove the inheritance of text transform in Edge, Firefox, and IE.\n * 1. Remove the inheritance of text transform in Firefox.\n */\n\nbutton,\nselect { /* 1 */\n text-transform: none;\n}\n\n/**\n * Correct the inability to style clickable types in iOS and Safari.\n */\n\nbutton,\n[type=\"button\"],\n[type=\"reset\"],\n[type=\"submit\"] {\n -webkit-appearance: button;\n}\n\n/**\n * Remove the inner border and padding in Firefox.\n */\n\nbutton::-moz-focus-inner,\n[type=\"button\"]::-moz-focus-inner,\n[type=\"reset\"]::-moz-focus-inner,\n[type=\"submit\"]::-moz-focus-inner {\n border-style: none;\n padding: 0;\n}\n\n/**\n * Restore the focus styles unset by the previous rule.\n */\n\nbutton:-moz-focusring,\n[type=\"button\"]:-moz-focusring,\n[type=\"reset\"]:-moz-focusring,\n[type=\"submit\"]:-moz-focusring {\n outline: 1px dotted ButtonText;\n}\n\n/**\n * Correct the padding in Firefox.\n */\n\nfieldset {\n padding: 0.35em 0.75em 0.625em;\n}\n\n/**\n * 1. Correct the text wrapping in Edge and IE.\n * 2. Correct the color inheritance from `fieldset` elements in IE.\n * 3. Remove the padding so developers are not caught out when they zero out\n * `fieldset` elements in all browsers.\n */\n\nlegend {\n box-sizing: border-box; /* 1 */\n color: inherit; /* 2 */\n display: table; /* 1 */\n max-width: 100%; /* 1 */\n padding: 0; /* 3 */\n white-space: normal; /* 1 */\n}\n\n/**\n * Add the correct vertical alignment in Chrome, Firefox, and Opera.\n */\n\nprogress {\n vertical-align: baseline;\n}\n\n/**\n * Remove the default vertical scrollbar in IE 10+.\n */\n\ntextarea {\n overflow: auto;\n}\n\n/**\n * 1. Add the correct box sizing in IE 10.\n * 2. Remove the padding in IE 10.\n */\n\n[type=\"checkbox\"],\n[type=\"radio\"] {\n box-sizing: border-box; /* 1 */\n padding: 0; /* 2 */\n}\n\n/**\n * Correct the cursor style of increment and decrement buttons in Chrome.\n */\n\n[type=\"number\"]::-webkit-inner-spin-button,\n[type=\"number\"]::-webkit-outer-spin-button {\n height: auto;\n}\n\n/**\n * 1. Correct the odd appearance in Chrome and Safari.\n * 2. Correct the outline style in Safari.\n */\n\n[type=\"search\"] {\n -webkit-appearance: textfield; /* 1 */\n outline-offset: -2px; /* 2 */\n}\n\n/**\n * Remove the inner padding in Chrome and Safari on macOS.\n */\n\n[type=\"search\"]::-webkit-search-decoration {\n -webkit-appearance: none;\n}\n\n/**\n * 1. Correct the inability to style clickable types in iOS and Safari.\n * 2. Change font properties to `inherit` in Safari.\n */\n\n::-webkit-file-upload-button {\n -webkit-appearance: button; /* 1 */\n font: inherit; /* 2 */\n}\n\n/* Interactive\n ========================================================================== */\n\n/*\n * Add the correct display in Edge, IE 10+, and Firefox.\n */\n\ndetails {\n display: block;\n}\n\n/*\n * Add the correct display in all browsers.\n */\n\nsummary {\n display: list-item;\n}\n\n/* Misc\n ========================================================================== */\n\n/**\n * Add the correct display in IE 10+.\n */\n\ntemplate {\n display: none;\n}\n\n/**\n * Add the correct display in IE 10.\n */\n\n[hidden] {\n display: none;\n}\n","html {\r\n\tbox-sizing: border-box;\r\n\tfont-size: 16px;\r\n}\r\n\r\n*,\r\n*::before,\r\n*::after {\r\n\tbox-sizing: inherit;\r\n}\r\n\r\nhtml,\r\nbody {\r\n\theight: 100%;\r\n\tposition: relative;\r\n}\r\n\r\nbody {}\r\n\r\nul,\r\nol,\r\nli,\r\np,\r\nh1,\r\nh2,\r\nh3,\r\nh4,\r\nh5,\r\nh6 {\r\n\tmargin: 0;\r\n}\r\n\r\nbutton:focus,\r\ninput:focus {\r\n\toutline: transparent;\r\n}\r\n\r\nbutton {\r\n\t@include no-btn;\r\n}\r\n\r\n.flex {\r\n\tdisplay: flex;\r\n\talign-items: flex-start;\r\n\tjustify-content: flex-start;\r\n}\r\n\r\n.--just-space {\r\n\tjustify-content: space-between;\r\n}\r\n\r\n.--just-center {\r\n\tjustify-content: center;\r\n}\r\n\r\n.--just-end {\r\n\tjustify-content: flex-end;\r\n}\r\n\r\n.--align-str {\r\n\talign-items: stretch;\r\n}\r\n\r\n.--align-center {\r\n\talign-items: center;\r\n}\r\n\r\n.--align-end {\r\n\talign-items: flex-end;\r\n}","@mixin bg ($size) {\r\n\tbackground-size: #{$size};\r\n\tbackground-position: center center;\r\n\tbackground-repeat: no-repeat;\r\n}\r\n\r\n@mixin centering ($dir) {\r\n\tposition: absolute;\r\n\r\n\t@if $dir==v {\r\n\t\ttop: 50%;\r\n\t\ttransform: translateY(-50%);\r\n\t}\r\n\r\n\t@else if $dir==h {\r\n\t\tleft: 50%;\r\n\t\ttransform: translateX(-50%);\r\n\t}\r\n\r\n\t@else {\r\n\t\ttop: 50%;\r\n\t\tleft: 50%;\r\n\t\ttransform: translate(-50%, -50%);\r\n\t}\r\n}\r\n\r\n@mixin btn_anim($scaleMax, $scaleMin) {\r\n\ttransform-origin: center center;\r\n\ttransition: all ease-out 240ms;\r\n\r\n\t&:hover {\r\n\t\ttransform: scale($scaleMax);\r\n\t}\r\n\r\n\t&:focus {\r\n\t\ttransform: scale($scaleMax) trahslateY(-5%);\r\n\t}\r\n\r\n\t&:active {\r\n\t\ttransform: scale($scaleMin);\r\n\t}\r\n}\r\n\r\n@mixin font-face($name, $file, $weight: 400, $style: normal) {\r\n\t@font-face {\r\n\t\tfont-family: \"#{$name}\";\r\n\t\tsrc: url('../fonts/#{$file}.eot');\r\n\t\tsrc: url('../fonts/#{$file}.eot?#iefix') format('embedded-opentype'),\r\n\t\turl('../fonts/#{$file}.woff2') format('woff2'),\r\n\t\turl('../fonts/#{$file}.woff') format('woff');\r\n\t\tfont-weight: $weight;\r\n\t\tfont-style: $style;\r\n\t\tfont-display: swap;\r\n\t}\r\n}\r\n\r\n@mixin no-btn {\r\n\tpadding: 0;\r\n\tmargin: 0;\r\n\tborder: 0;\r\n\tbackground-color: transparent;\r\n\tborder-radius: 0;\r\n\tcursor: pointer;\r\n\t-webkit-appearance: none;\r\n}"]}
\ No newline at end of file
diff --git a/folders.txt b/folders.txt
new file mode 100644
index 00000000..a69b31e2
--- /dev/null
+++ b/folders.txt
@@ -0,0 +1,16 @@
+💼 project/
+├──── 📡 build/ - пустая директория для скомпилированных файлов проекта
+├──── 🧶 node-modules/ - директория для node-модулей
+├──┬─ 🗄 src/ - директория для исходных файлов проекта
+│ ├──┬─ ⚙ components/ - директория для компонентов
+│ │ ├─── 🧩 bem-blocks/ - директория для БЭМ-блоков
+│ │ └─── 🗂 page-blocks/ - директория для типовых блоков страницы, таких как хедер, футер и т.п.
+│ ├──── 🔤 fonts/ - директория для шрифтов
+│ ├──── 🖼 img/ - директория для изображений
+│ ├──── 📑 js/ - директория для файлов JavaScript
+│ ├──┬─ 📜 scss/ - директория для файлов стилей
+│ │ └─── 🧬 base/ - директория для базовых стилей, которые мы изщменять не будем
+│ └──┬─ ⛱ svg/ - директория для файлов SVG
+│ └─── 🏷 css/ - директория для SVG-файлов, которые будут интегрироваться в CSS
+│
+└──── 🛠 tasks/ - директория для модулей функций Gulp
\ No newline at end of file
diff --git a/gulpfile.js b/gulpfile.js
index ab4c41e7..51026cff 100644
--- a/gulpfile.js
+++ b/gulpfile.js
@@ -1,416 +1,51 @@
-"use strict";
-
const gulp = require('gulp');
-const sass = require('gulp-sass');
-const bs = require('browser-sync');
-const rename = require('gulp-rename');
-const prefixer = require('gulp-autoprefixer');
-const cleanCss = require('gulp-clean-css');
-const bulkSass = require('gulp-sass-bulk-importer');
-const concat = require('gulp-concat');
-const map = require('gulp-sourcemaps');
-const include = require('gulp-file-include');
-const htmlmin = require('gulp-htmlmin');
-const uglify = require('gulp-uglify-es').default;
-const babel = require('gulp-babel');
-const size = require('gulp-size');
-const changed = require('gulp-changed');
-const imagemin = require('gulp-imagemin');
-const recompress = require('imagemin-jpeg-recompress');
-const pngquant = require('imagemin-pngquant');
-const svgmin = require('gulp-svgmin');
-const svgcss = require('gulp-svg-css-pseudo');
-const svgsprite = require('gulp-svg-sprite');
-const svgInclude = require('gulp-embed-svg');
-const ttf2woff2 = require('gulp-ttftowoff2');
-const ttf2woff = require('gulp-ttf2woff');
-const ttf2eot = require('gulp-ttf2eot');
-const fs = require('fs');
-const ftp = require('vinyl-ftp');
-const {
- strict
-} = require('assert');
-
-const js_plugins = [];
-const css_plugins = [
- 'node_modules/normalize.css/normalize.css'
-];
-
-let settings_size = {
- 'gzip': true,
- 'pretty': true,
- 'showFiles': true,
- 'showTotal': true
- },
- svgmin_plugins = {
- plugins: [{
- removeComments: true
- },
- {
- removeEmptyContainers: true
- }
- ]
- },
- connect = ftp.create({
- host: '',
- user: '',
- pass: '',
- parallel: 10,
- log: ''
- });
-
-gulp.task('libs_styles', () => {
- if (css_plugins.length > 0) {
- return gulp
- .src(css_plugins)
- .pipe(map.init())
- .pipe(sass({
- outputStyle: 'compressed'
- }).on('error', sass.logError))
- .pipe(concat('libs.min.css'))
- .pipe(map.write('../sourcemaps/'))
- .pipe(size(settings_size))
- .pipe(gulp.dest('build/css/'))
- } else {
- return true;
- }
-});
-
-gulp.task('dev_styles', () => {
- return gulp
- .src('src/scss/*.scss', '!src/scss/libs.scss')
- .pipe(map.init())
- .pipe(bulkSass())
- .pipe(sass({
- outputStyle: 'compressed'
- }).on('error', sass.logError))
- .pipe(prefixer({
- overrideBrowserslist: ['last 8 versions'],
- browsers: [
- 'Android >= 4',
- 'Chrome >= 20',
- 'Firefox >= 24',
- 'Explorer >= 11',
- 'iOS >= 6',
- 'Opera >= 12',
- 'Safari >= 6',
- ],
- }))
- .pipe(cleanCss({
- level: 2
- }))
- .pipe(concat('style.min.css'))
- .pipe(map.write('../sourcemaps/'))
- .pipe(gulp.dest('build/css/'))
- .pipe(size(settings_size))
- .pipe(bs.stream())
-});
-
-gulp.task('style',
- gulp.series(
- 'libs_styles',
- 'dev_styles'
- )
-);
-
-gulp.task('libs_js', () => {
- if (js_plugins.length) {
- return gulp
- .src(js_plugins)
- .pipe(map.init())
- .pipe(concat('libs.min.js'))
- .pipe(uglify())
- .pipe(map.write('../sourcemaps/'))
- .pipe(size(settings_size))
- .pipe(gulp.dest('build/js/'))
- } else {
- return true
- }
-});
-
-gulp.task('dev_js', () => {
- return gulp
- .src(['src/js/01__main.js', 'src/components/bem-blocks/**/*.js'])
- .pipe(map.init())
- .pipe(uglify())
- .pipe(concat('main.min.js'))
- .pipe(map.write('../sourcemaps/'))
- .pipe(size(settings_size))
- .pipe(gulp.dest('build/js/'))
- .pipe(bs.stream())
-});
-
-gulp.task('build_js', () => {
- return gulp
- .src(['src/js/01__main.js', 'src/components/bem-blocks/**/*.js'])
- .pipe(map.init())
- .pipe(uglify())
- .pipe(babel({
- presets: ['@babel/env']
- }))
- .pipe(concat('main.min.js'))
- .pipe(map.write('../sourcemaps/'))
- .pipe(size(settings_size))
- .pipe(gulp.dest('build/js/'))
-});
-
-gulp.task('js',
- gulp.series(
- 'libs_js',
- 'dev_js'
- )
-);
-
-gulp.task('html', () => {
- return gulp
- .src(['src/**/*.html', '!src/**/_*.html'])
- .pipe(include())
- .pipe(svgInclude({
- selectors: '.include-svg',
- root: './src/svg/include'
- }))
- .pipe(size(settings_size))
- .pipe(gulp.dest('build'))
- .pipe(bs.stream())
-});
-
-gulp.task('php', () => {
- return gulp
- .src('src/**/*.php')
- .pipe(svgInclude({
- selectors: '.include-svg',
- root: './src/svg/include'
- }))
- .pipe(size(settings_size))
- .pipe(gulp.dest('build/'))
- .pipe(bs.stream())
-});
-
-gulp.task('json', function () {
- return gulp
- .src('src/**/*.json', '!src/components/**/*.json')
- .pipe(size(settings_size))
- .pipe(gulp.dest('build/'))
- .pipe(bs.stream())
-});
-
-gulp.task('svg2css', () => {
- return gulp
- .src('src/svg/css/**/*.svg')
- .pipe(svgmin(svgmin_plugins))
- .pipe(svgcss({
- fileName: '_05-svg',
- fileExt: 'scss',
- cssPrefix: '--svg__',
- addSize: false
- }))
- .pipe(gulp.dest('src/scss/global'))
- .pipe(size(settings_size));
-});
-
-gulp.task('svg2sprite', () => {
- return gulp
- .src('src/svg/sprite/**/*.svg')
- .pipe(svgmin(svgmin_plugins))
- .pipe(svgsprite({
- mode: {
- stack: {
- sprite: '../sprite.svg'
- }
- },
- }))
- .pipe(gulp.dest('src/img/'))
- .pipe(size(settings_size))
-});
-
-gulp.task('img', () => {
- return gulp
- .src('src/img/**/*.+(png|jpg|jpeg|gif|svg|ico|webp)')
- .pipe(imagemin({
- interlaced: true,
- progressive: true,
- optimizationLevel: 5,
- },
- [
- recompress({
- loops: 6,
- min: 50,
- max: 90,
- quality: 'high',
- use: [pngquant({
- quality: [0.7, 0.9],
- strip: true,
- speed: 1
- })],
- }),
- imagemin.gifsicle(),
- imagemin.optipng(),
- imagemin.svgo()
- ], ), )
- .pipe(gulp.dest('build/img'))
- .pipe(size(settings_size))
- .pipe(bs.stream())
-});
-
-gulp.task('images',
- gulp.parallel(
- 'svg2css',
- 'svg2sprite',
- 'img'
- ));
-
-gulp.task('font-woff', () => {
- return gulp
- .src('src/fonts/**/*.ttf')
- .pipe(changed('build/fonts', {
- extension: '.woff',
- hasChanged: changed.compareLastModifiedTime
- }))
- .pipe(ttf2woff())
- .pipe(gulp.dest('build/fonts/'))
-});
-
-gulp.task('font-woff2', () => {
- return gulp
- .src('src/fonts/**/*.ttf')
- .pipe(changed('build/fonts', {
- extension: '.woff2',
- hasChanged: changed.compareLastModifiedTime
- }))
- .pipe(ttf2woff2())
- .pipe(gulp.dest('build/fonts/'))
-});
-
-gulp.task('font-eot', () => {
- return gulp
- .src('src/fonts/**/*.ttf')
- .pipe(changed('build/fonts', {
- extension: '.eot',
- hasChanged: changed.compareLastModifiedTime
- }))
- .pipe(ttf2eot())
- .pipe(gulp.dest('build/fonts/'))
-});
-
-const cb = () => {}
-
-let srcFonts = 'src/scss/_local-fonts.scss';
-let appFonts = 'build/fonts/';
-
-gulp.task('fontsgen', (done) => {
- let file_content = fs.readFileSync(srcFonts);
-
- fs.writeFile(srcFonts, '', cb);
- fs.readdir(appFonts, (err, items) => {
- if (items) {
- let c_fontname;
- for (let i = 0; i < items.length; i++) {
- let fontname = items[i].split('.'),
- fontExt;
- fontExt = fontname[1];
- fontname = fontname[0];
- if (c_fontname != fontname) {
- if (fontExt == 'woff' || fontExt == 'woff2' || fontExt == 'eot') {
- fs.appendFile(srcFonts, `@include font-face("${fontname}", "${fontname}", 400);\r\n`, cb);
- console.log(`Added font ${fontname}.
-----------------------------------------------------------------------------------
-Please, move mixin call from src/scss/_local-fonts.scss to src/scss/_fonts.scss and then change it, if font from this family added ealy!
-----------------------------------------------------------------------------------`);
- }
- }
- c_fontname = fontname;
- }
- }
- })
- done();
-})
-
-gulp.task('fonts', gulp.series(
- 'font-woff2',
- 'font-woff',
- 'font-eot',
- 'fontsgen'
-));
-
-gulp.task('server_html', () => {
- bs.init({
- server: {
- baseDir: 'build/',
- host: '192.168.0.104',
- },
- browser: 'chrome',
- logPrefix: 'BS-HTML:',
- logLevel: 'info',
- open: false
- })
-});
-
-gulp.task('server_php', () => {
- bs.init({
- browser: ['chrome'],
- watch: true,
- proxy: '',
- /* set local domain of your project */
- logLevel: 'info',
- logPrefix: 'BS-PHP:',
- logConnections: true,
- logFileChanges: true,
- })
-});
-
-gulp.task('deploy', () => {
- return gulp
- .src('build/**/*.*')
- .pipe(connect.newer('html/'))
- .pipe(connect.dest('html/'))
-});
-
-gulp.task('watch_html', () => {
- gulp.watch('src/**/*.scss', gulp.parallel('dev_styles'));
- gulp.watch('src/**/*.html', gulp.parallel('html'));
- gulp.watch('src/**/*.js', gulp.parallel('dev_js'));
- gulp.watch('src/**/*.json', gulp.parallel('json', 'html'));
- gulp.watch('src/img/**/*.*', gulp.parallel('img'));
- gulp.watch('src/svg/css/**/*.svg', gulp.parallel('svg2css'));
- gulp.watch('src/svg/sprite/**/*.svg', gulp.parallel('svg2sprite'));
- gulp.watch('src/svg/include/**/*.svg', gulp.parallel('html'));
- gulp.watch('src/fonts/**/*.ttf', gulp.parallel('fonts'));
-});
-
-gulp.task('watch_php', () => {
- gulp.watch('src/**/*.scss', gulp.parallel('dev_styles'));
- gulp.watch('src/**/*.php', gulp.parallel('php'));
- gulp.watch('src/**/*.js', gulp.parallel('dev_js'));
- gulp.watch('src/**/*.json', gulp.parallel('json'));
- gulp.watch('src/img/**/*.*', gulp.parallel('img'));
- gulp.watch('src/svg/css/**/*.svg', gulp.parallel('svg2css'));
- gulp.watch('src/svg/sprite/**/*.svg', gulp.parallel('svg2sprite'));
- gulp.watch('src/svg/include/**/*.svg', gulp.parallel('php'));
- gulp.watch('src/fonts/**/*.ttf', gulp.parallel('fonts'));
-});
-
-gulp.task('default',
- gulp.parallel(
- 'style',
- 'html',
- 'js',
- 'json',
- 'images',
- 'fonts',
- 'watch_html',
- 'server_html'
- )
-);
-
-gulp.task('dev-php',
- gulp.parallel(
- 'style',
- 'php',
- 'js',
- 'json',
- 'images',
- 'fonts',
- 'watch_php',
- 'server_php'
- )
-);
+const requireDir = require('require-dir');
+const tasks = requireDir('./tasks');
+
+exports.style = tasks.style;
+exports.libs_style = tasks.libs_style;
+exports.build_js = tasks.build_js;
+exports.libs_js = tasks.libs_js;
+exports.dev_js = tasks.dev_js;
+exports.html = tasks.html;
+exports.php = tasks.php;
+exports.rastr = tasks.rastr;
+exports.webp = tasks.webp;
+exports.svg_css = tasks.svg_css;
+exports.svg_sprite = tasks.svg_sprite;
+exports.ttf = tasks.ttf;
+exports.fonts = tasks.fonts;
+exports.bs_html = tasks.bs_html;
+exports.bs_php = tasks.bs_php;
+exports.watch = tasks.watch;
+
+exports.default = gulp.parallel(
+ exports.libs_style,
+ exports.style,
+ exports.libs_js,
+ exports.dev_js,
+ exports.rastr,
+ exports.webp,
+ exports.svg_css,
+ exports.svg_sprite,
+ exports.ttf,
+ exports.fonts,
+ exports.html,
+ exports.bs_html,
+ exports.watch
+)
+exports.dev_php = gulp.parallel(
+ exports.libs_style,
+ exports.style,
+ exports.libs_js,
+ exports.dev_js,
+ exports.rastr,
+ exports.webp,
+ exports.svg_css,
+ exports.svg_sprite,
+ exports.ttf,
+ exports.fonts,
+ exports.html,
+ exports.bs_php,
+ exports.watch
+)
\ No newline at end of file
diff --git a/package.json b/package.json
index 15f26d9a..d8a68866 100644
--- a/package.json
+++ b/package.json
@@ -1,52 +1,43 @@
{
- "name": "Easy-webdev-startpack",
- "version": "2.1.1",
- "description": "HTML5+CSS3 template for easy start website project'",
- "main": "gulpfile.js",
- "devDependencies": {
- "@babel/core": "^7.12.10",
- "@babel/preset-env": "^7.12.11",
- "browser-sync": "^2.26.13",
- "fs": "^0.0.1-security",
- "gulp": "^4.0.2",
- "gulp-autoprefixer": "^7.0.1",
- "gulp-babel": "^8.0.0",
- "gulp-changed": "^4.0.2",
- "gulp-clean-css": "^4.3.0",
- "gulp-concat": "^2.6.1",
- "gulp-embed-svg": "^1.2.0",
- "gulp-file-include": "^2.3.0",
- "gulp-htmlmin": "^5.0.1",
- "gulp-imagemin": "^4.1.0",
- "gulp-rename": "^2.0.0",
- "gulp-sass": "^4.1.0",
- "gulp-sass-bulk-importer": "^3.0.1",
- "gulp-size": "^3.0.0",
- "gulp-sourcemaps": "^3.0.0",
- "gulp-svg-css-pseudo": "^1.0.4",
- "gulp-svg-sprite": "^1.5.0",
- "gulp-svgmin": "^3.0.0",
- "gulp-ttf2eot": "^1.1.2",
- "gulp-ttf2woff": "^1.1.1",
- "gulp-ttftowoff2": "^1.0.1",
- "gulp-uglify-es": "^2.0.0",
- "imagemin-jpeg-recompress": "^7.0.0",
- "imagemin-pngquant": "^9.0.1",
- "vinyl-ftp": "^0.6.1"
- },
- "dependencies": {
- "normalize.css": "^8.0.1"
- },
- "scripts": {
- "html": "gulp",
- "php": "gulp dev-php",
- "build": "gulp style && gulp html && gulp php && gulp js && gulp build_js && gulp json && gulp images && gulp fonts"
- },
- "repository": "https://github.com/budfy/Easy-webdev-startpack.git",
- "author": "Roman Serdyuk",
- "license": "ISC",
- "bugs": {
- "url": "https://github.com/budfy/Startpack_Gulp4/issues"
- },
- "homepage": "https://github.com/budfy/Startpack_Gulp4#readme"
-}
\ No newline at end of file
+ "name": "easy-webdev-startpack-new",
+ "version": "1.0.0",
+ "description": "",
+ "main": "index.js",
+ "scripts": {
+ "test": "echo \"Error: no test specified\" && exit 1"
+ },
+ "author": "",
+ "license": "ISC",
+ "devDependencies": {
+ "@babel/core": "^7.14.2",
+ "@babel/preset-env": "^7.14.2",
+ "browser-sync": "^2.26.14",
+ "chalk": "^4.1.1",
+ "gulp": "^4.0.2",
+ "gulp-autoprefixer": "^7.0.1",
+ "gulp-babel": "^8.0.0",
+ "gulp-changed": "^4.0.2",
+ "gulp-clean-css": "^4.3.0",
+ "gulp-concat": "^2.6.1",
+ "gulp-file-include": "^2.3.0",
+ "gulp-imagemin": "^7.1.0",
+ "gulp-multi-dest": "^1.3.7",
+ "gulp-plumber": "^1.2.1",
+ "gulp-sass": "^4.1.0",
+ "gulp-sass-bulk-importer": "^3.0.1",
+ "gulp-size": "^4.0.0",
+ "gulp-sourcemaps": "^3.0.0",
+ "gulp-svg-css-pseudo": "^1.0.4",
+ "gulp-svg-sprite": "^1.5.0",
+ "gulp-svgmin": "^3.0.0",
+ "gulp-ttf2woff": "^1.1.1",
+ "gulp-ttftowoff2": "^1.0.1",
+ "gulp-uglify-es": "^2.0.0",
+ "gulp-webp": "^4.0.1",
+ "imagemin-jpeg-recompress": "^7.0.0",
+ "imagemin-pngquant": "^9.0.2",
+ "require-dir": "^1.2.0",
+ "vinyl-ftp": "^0.6.1"
+ },
+ "dependencies": {}
+}
diff --git a/process.json b/process.json
new file mode 100644
index 00000000..09209cce
--- /dev/null
+++ b/process.json
@@ -0,0 +1,4028 @@
+{
+ "wysiwyg": {
+ "text": {
+ "json": {
+ "type": "doc",
+ "content": [{
+ "type": "image",
+ "attrs": {
+ "src": "https://habrastorage.org/getpro/habr/upload_files/dfc/e83/230/dfce83230ad4a258e13c235d0e89790c.jpg",
+ "title": null,
+ "customClass": "image",
+ "border": false,
+ "float": false,
+ "fullWidth": true,
+ "inserted": false,
+ "width": 843,
+ "height": 560
+ }
+ },
+ {
+ "type": "paragraph",
+ "attrs": {
+ "align": null
+ },
+ "content": [{
+ "type": "text",
+ "text": "Серьёзно и профессионально я начал заниматься вёрсткой в 2019 году, хотя до этого ещё со школы интересовался данной темой как любитель. Поэтому новичком мне себя назвать сложно, но и профессионалом с опытом 5+ лет я тоже не являюсь. Тем не менее, я успел познакомиться со сборщиком Gulp, его плагинами и сделал для себя хорошую, как по мне, сборку для работы. О её возможностях сегодня и расскажу."
+ }]
+ },
+ {
+ "type": "paragraph",
+ "attrs": {
+ "align": null
+ },
+ "content": [{
+ "type": "text",
+ "marks": [{
+ "type": "bold"
+ }],
+ "text": "ВАЖНО!"
+ },
+ {
+ "type": "text",
+ "text": " В этой статье речь пойдёт о самой последней версии сборки. Если вы пользуетесь версиями сборки, вышедшими до публикации этой статьи, информация будет для вас не релевантна, но полезна."
+ }
+ ]
+ },
+ {
+ "type": "heading",
+ "attrs": {
+ "level": 1,
+ "class": null
+ },
+ "content": [{
+ "type": "text",
+ "text": "Какие задачи решает эта сборка?"
+ }]
+ },
+ {
+ "type": "list",
+ "attrs": {
+ "order": 1,
+ "tag": "ul",
+ "type": "outer"
+ },
+ "content": [{
+ "type": "listitem",
+ "content": [{
+ "type": "paragraph",
+ "attrs": {
+ "align": null
+ },
+ "content": [{
+ "type": "text",
+ "text": "вёрстка компонентами (вам не нужно в каждую страницу копировать head, header, footer и другие повторяющиеся элементы, вплоть до кнопок или кастомных чекбоксов);"
+ }]
+ }]
+ },
+ {
+ "type": "listitem",
+ "content": [{
+ "type": "paragraph",
+ "attrs": {
+ "align": null
+ },
+ "content": [{
+ "type": "text",
+ "text": "вёрстка с препроцессорами (SASS/SCSS);"
+ }]
+ }]
+ },
+ {
+ "type": "listitem",
+ "content": [{
+ "type": "paragraph",
+ "attrs": {
+ "align": null
+ },
+ "content": [{
+ "type": "text",
+ "text": "конвертация шрифтов из ttf в eot, woff, woff2;"
+ }]
+ }]
+ },
+ {
+ "type": "listitem",
+ "content": [{
+ "type": "paragraph",
+ "attrs": {
+ "align": null
+ },
+ "content": [{
+ "type": "text",
+ "text": "лёгкое (почти автоматическое) подключение шрифтов;"
+ }]
+ }]
+ },
+ {
+ "type": "listitem",
+ "content": [{
+ "type": "paragraph",
+ "attrs": {
+ "align": null
+ },
+ "content": [{
+ "type": "text",
+ "text": "лёгкое (почти автоматическое) создание псевдоэлементов-иконок;"
+ }]
+ }]
+ },
+ {
+ "type": "listitem",
+ "content": [{
+ "type": "paragraph",
+ "attrs": {
+ "align": null
+ },
+ "content": [{
+ "type": "text",
+ "text": "обработка изображений \"на лету\";"
+ }]
+ }]
+ },
+ {
+ "type": "listitem",
+ "content": [{
+ "type": "paragraph",
+ "attrs": {
+ "align": null
+ },
+ "content": [{
+ "type": "text",
+ "text": "минификация html/css/js файлов;"
+ }]
+ }]
+ },
+ {
+ "type": "listitem",
+ "content": [{
+ "type": "paragraph",
+ "attrs": {
+ "align": null
+ },
+ "content": [{
+ "type": "text",
+ "text": "возможность вёрстки с использованием php;"
+ }]
+ }]
+ },
+ {
+ "type": "listitem",
+ "content": [{
+ "type": "paragraph",
+ "attrs": {
+ "align": null
+ },
+ "content": [{
+ "type": "text",
+ "text": "выгрузка файлов на хостинг по FTP;"
+ }]
+ }]
+ },
+ {
+ "type": "listitem",
+ "content": [{
+ "type": "paragraph",
+ "attrs": {
+ "align": null
+ },
+ "content": [{
+ "type": "text",
+ "text": "несколько мелких задач с помощью миксинов."
+ }]
+ }]
+ }
+ ]
+ },
+ {
+ "type": "spoiler",
+ "attrs": {
+ "title": "Для тех, кому лень читать и делать всё руками - сразу ссылка на сборку."
+ },
+ "content": [{
+ "type": "paragraph",
+ "attrs": {
+ "align": null
+ },
+ "content": [{
+ "type": "text",
+ "marks": [{
+ "type": "link",
+ "attrs": {
+ "href": "https://github.com/budfy/Easy-webdev-startpack"
+ }
+ }],
+ "text": "https://github.com/budfy/Easy-webdev-startpack"
+ }]
+ }]
+ },
+ {
+ "type": "heading",
+ "attrs": {
+ "level": 1,
+ "class": null
+ },
+ "content": [{
+ "type": "text",
+ "text": "Собственно создание сборки"
+ }]
+ },
+ {
+ "type": "paragraph",
+ "attrs": {
+ "align": null
+ },
+ "content": [{
+ "type": "text",
+ "text": "Начнём собирать нашу сборку (простите за тавтологию). Предварительно нам потребуется уже установленная на компьютере "
+ },
+ {
+ "type": "text",
+ "marks": [{
+ "type": "bold"
+ }],
+ "text": "LTS-версия"
+ },
+ {
+ "type": "text",
+ "text": " "
+ },
+ {
+ "type": "text",
+ "marks": [{
+ "type": "link",
+ "attrs": {
+ "href": "https://nodejs.org/dist/v14.16.1/node-v14.16.1-x64.msi"
+ }
+ }],
+ "text": "Node.js"
+ },
+ {
+ "type": "text",
+ "text": " и NPM (входит в пакет Node.js) либо "
+ },
+ {
+ "type": "text",
+ "marks": [{
+ "type": "link",
+ "attrs": {
+ "href": "https://yarnpkg.com/getting-started/install"
+ }
+ }],
+ "text": "Yarn"
+ },
+ {
+ "type": "text",
+ "text": ". Для нашей задачи не имеет значения, какой из этих пакетных менеджеров использовать, однако я буду объяснять на примере NPM, соответственно, для Yarn вам потребуется нагуглить аналоги NPM-команд."
+ }
+ ]
+ },
+ {
+ "type": "paragraph",
+ "attrs": {
+ "align": null
+ },
+ "content": [{
+ "type": "text",
+ "text": "Первое, что нам нужно сделать - это инициализировать проект. Открываем папку проекта в командной строке (очень надеюсь, вы знаете, как это делается) и вводим команду "
+ },
+ {
+ "type": "text",
+ "marks": [{
+ "type": "code"
+ }],
+ "text": "npm init."
+ }
+ ]
+ },
+ {
+ "type": "paragraph",
+ "attrs": {
+ "align": null
+ },
+ "content": [{
+ "type": "text",
+ "text": "После этого npm задаст нам неесколько стандартных вопросов по типу названия проекта, автора, версии и т.д... Отвечаем на них как душе угодно. Для нашей задачи это не имеет никакого значения."
+ }]
+ },
+ {
+ "type": "paragraph",
+ "attrs": {
+ "align": null
+ },
+ "content": [{
+ "type": "text",
+ "text": "Далее будет намного удобнее работать через "
+ },
+ {
+ "type": "text",
+ "marks": [{
+ "type": "link",
+ "attrs": {
+ "href": "https://code.visualstudio.com/"
+ }
+ }],
+ "text": "Visual Studio Code"
+ },
+ {
+ "type": "text",
+ "text": " (поскольку у него есть встроенный терминал) или любой другой удобный вам редактор + терминал."
+ }
+ ]
+ },
+ {
+ "type": "paragraph",
+ "attrs": {
+ "align": null
+ },
+ "content": [{
+ "type": "text",
+ "text": "Прежде всего, нам нужно установить сам "
+ },
+ {
+ "type": "text",
+ "marks": [{
+ "type": "link",
+ "attrs": {
+ "href": "https://gulpjs.com/"
+ }
+ }],
+ "text": "Gulp."
+ },
+ {
+ "type": "text",
+ "text": " Делается это двумя командами "
+ },
+ {
+ "type": "text",
+ "marks": [{
+ "type": "code"
+ }],
+ "text": "npm i gulp -global"
+ },
+ {
+ "type": "text",
+ "text": " - устанавливаем Gulp глобально на систему и "
+ },
+ {
+ "type": "text",
+ "marks": [{
+ "type": "code"
+ }],
+ "text": "npm i gulp --save-dev"
+ },
+ {
+ "type": "text",
+ "text": " - устанавливаем Gulp локально в проект. Ключ "
+ },
+ {
+ "type": "text",
+ "marks": [{
+ "type": "code"
+ }],
+ "text": "--save"
+ },
+ {
+ "type": "text",
+ "text": " здесь отвечает за сохранение версии плагина при дальнейшей установке (без него вам может установить более новую, несовместимую с другими плагинами версию), а ключ "
+ },
+ {
+ "type": "text",
+ "marks": [{
+ "type": "code"
+ }],
+ "text": "-dev"
+ },
+ {
+ "type": "text",
+ "text": " указывает на то, что этот пакет необходим только во время разработки проекта, а не во время его выполнения. Например, если мы устанавливаем в проект пакет Swiper, который содержит скрипты слайдера и будет отображаться на странице, мы будем устанавливать его без ключа "
+ },
+ {
+ "type": "text",
+ "marks": [{
+ "type": "code"
+ }],
+ "text": "-dev"
+ },
+ {
+ "type": "text",
+ "text": ", поскольку он нужен для выполнения, а не для разработки."
+ }
+ ]
+ },
+ {
+ "type": "paragraph",
+ "attrs": {
+ "align": null
+ },
+ "content": [{
+ "type": "text",
+ "text": "После того, как Gulp установился, имеет смысл создать в корне проекта управляющий файл gulpfile.js, в котором мы и будем писать задачи для сборщика."
+ }]
+ },
+ {
+ "type": "paragraph",
+ "attrs": {
+ "align": null
+ },
+ "content": [{
+ "type": "text",
+ "text": "После этого нам нужно подключить Gulp в нашем файле, для того чтобы он исполнялся. Это делается с помощью require:"
+ }]
+ },
+ {
+ "type": "code_block",
+ "attrs": {
+ "lang": "javascript",
+ "code": "const gulp = require('gulp');",
+ "inserted": true
+ }
+ },
+ {
+ "type": "paragraph",
+ "attrs": {
+ "align": null
+ },
+ "content": [{
+ "type": "text",
+ "text": "Далее, для каждой задачи будем использовать модули в отдельных файлах. Для того, чтобы не подключать каждый модуль отдельно, нужно установить и подключить плагин "
+ },
+ {
+ "type": "text",
+ "marks": [{
+ "type": "link",
+ "attrs": {
+ "href": "https://www.npmjs.com/package/require-dir"
+ }
+ }],
+ "text": "require-dir"
+ },
+ {
+ "type": "text",
+ "text": ". Устанавливается он всё той же командой (как и все последующие плагины, поэтому далее повторяться не буду, просто знайте, что установить - это "
+ },
+ {
+ "type": "text",
+ "marks": [{
+ "type": "code"
+ }],
+ "text": "npm i $PLUGIN-NAME$ --save-dev"
+ },
+ {
+ "type": "text",
+ "text": "). После установки подключаем его и прописываем путь к папке, в которую будем складывать модули (у меня это папка tasks):"
+ }
+ ]
+ },
+ {
+ "type": "code_block",
+ "attrs": {
+ "lang": "javascript",
+ "code": "const gulp = require('gulp');\n\nconst requireDir = require('require-dir');\nconst tasks = requireDir('./tasks');",
+ "inserted": true
+ }
+ },
+ {
+ "type": "heading",
+ "attrs": {
+ "level": 2,
+ "class": null
+ },
+ "content": [{
+ "type": "text",
+ "text": "Первая задача"
+ }]
+ },
+ {
+ "type": "paragraph",
+ "attrs": {
+ "align": null
+ },
+ "content": [{
+ "type": "text",
+ "text": "Давайте проверим, всё ли мы правильно сделали. Создадим в директории "
+ },
+ {
+ "type": "text",
+ "marks": [{
+ "type": "bold"
+ }],
+ "text": "tasks"
+ },
+ {
+ "type": "text",
+ "text": " файл модуля с именем "
+ },
+ {
+ "type": "text",
+ "marks": [{
+ "type": "bold"
+ }],
+ "text": "hello.js"
+ },
+ {
+ "type": "text",
+ "text": ". В созданном файле напишем простейшую функцию, которая будет выводить в консоль строку \"Hello Gulp!\" (можете придумать что-то менее банальное, если хотите)."
+ }
+ ]
+ },
+ {
+ "type": "code_block",
+ "attrs": {
+ "lang": "javascript",
+ "code": "module.exports = function () {\n\tconsole.log(\"Hello Gulp!\");\n}",
+ "inserted": true
+ }
+ },
+ {
+ "type": "paragraph",
+ "attrs": {
+ "align": null
+ },
+ "content": [{
+ "type": "text",
+ "text": "Теперь вернёмся в gulpfile.js и создадим там задачу hello:"
+ }]
+ },
+ {
+ "type": "code_block",
+ "attrs": {
+ "lang": "javascript",
+ "code": "const gulp = require('gulp');\n\nconst requireDir = require('require-dir');\nconst tasks = requireDir('./tasks');\n\nexports.hello = tasks.hello;",
+ "inserted": true
+ },
+ "content": [{
+ "type": "text",
+ "text": "Т"
+ }]
+ },
+ {
+ "type": "paragraph",
+ "attrs": {
+ "align": null
+ },
+ "content": [{
+ "type": "text",
+ "text": "Теперь командой "
+ },
+ {
+ "type": "text",
+ "marks": [{
+ "type": "code"
+ }],
+ "text": "gulp hello"
+ },
+ {
+ "type": "text",
+ "text": " в терминале запустим нашу задачу. Если всё сделано правильно - в терминал должно вывестись приблизительно такое сообщение:"
+ }
+ ]
+ },
+ {
+ "type": "code_block",
+ "attrs": {
+ "lang": "powershell",
+ "code": "[13:17:15] Using gulpfile D:\\Web projects\\Easy-webdev-startpack-new\\gulpfile.js\n[13:17:15] Starting 'hello'...\nHello Gulp!\n[13:17:15] The following tasks did not complete: hello\n[13:17:15] Did you forget to signal async completion?",
+ "inserted": true
+ }
+ },
+ {
+ "type": "heading",
+ "attrs": {
+ "level": 1,
+ "class": null
+ },
+ "content": [{
+ "type": "text",
+ "text": "Файловая структура"
+ }]
+ },
+ {
+ "type": "paragraph",
+ "attrs": {
+ "align": null
+ },
+ "content": [{
+ "type": "text",
+ "text": "Теперь, когда мы создали первую функцию, можно подумать о том, какая структура будет у наших проектов. Я предпочитаю использовать директорию (папку) "
+ },
+ {
+ "type": "text",
+ "marks": [{
+ "type": "bold"
+ }],
+ "text": "/src"
+ },
+ {
+ "type": "text",
+ "text": " для хранения исходных файлов и директорию "
+ },
+ {
+ "type": "text",
+ "marks": [{
+ "type": "bold"
+ }],
+ "text": "/build"
+ },
+ {
+ "type": "text",
+ "text": " для готовых файлов проекта."
+ }
+ ]
+ },
+ {
+ "type": "spoiler",
+ "attrs": {
+ "title": "В директории src/ нам понадобятся следующие поддиректории:"
+ },
+ "content": [{
+ "type": "list",
+ "attrs": {
+ "order": 1,
+ "tag": "ul",
+ "type": "outer"
+ },
+ "content": [{
+ "type": "listitem",
+ "content": [{
+ "type": "paragraph",
+ "attrs": {
+ "align": null
+ },
+ "content": [{
+ "type": "text",
+ "text": "components/ - директория для компонентов"
+ }]
+ }]
+ },
+ {
+ "type": "listitem",
+ "content": [{
+ "type": "paragraph",
+ "attrs": {
+ "align": null
+ },
+ "content": [{
+ "type": "text",
+ "text": "components/bem-blocks/ - директория для БЭМ-блоков"
+ }]
+ }]
+ },
+ {
+ "type": "listitem",
+ "content": [{
+ "type": "paragraph",
+ "attrs": {
+ "align": null
+ },
+ "content": [{
+ "type": "text",
+ "text": "components/page-blocks/ - директория для типовых блоков страницы, таких как хедер, футер и т.п."
+ }]
+ }]
+ },
+ {
+ "type": "listitem",
+ "content": [{
+ "type": "paragraph",
+ "attrs": {
+ "align": null
+ },
+ "content": [{
+ "type": "text",
+ "text": "fonts/ - директория для шрифтов"
+ }]
+ }]
+ },
+ {
+ "type": "listitem",
+ "content": [{
+ "type": "paragraph",
+ "attrs": {
+ "align": null
+ },
+ "content": [{
+ "type": "text",
+ "text": "img/ - директория для изображений"
+ }]
+ }]
+ },
+ {
+ "type": "listitem",
+ "content": [{
+ "type": "paragraph",
+ "attrs": {
+ "align": null
+ },
+ "content": [{
+ "type": "text",
+ "text": "js/ - директория для файлов JavaScript"
+ }]
+ }]
+ },
+ {
+ "type": "listitem",
+ "content": [{
+ "type": "paragraph",
+ "attrs": {
+ "align": null
+ },
+ "content": [{
+ "type": "text",
+ "text": "scss/ - директория для файлов стилей"
+ }]
+ }]
+ },
+ {
+ "type": "listitem",
+ "content": [{
+ "type": "paragraph",
+ "attrs": {
+ "align": null
+ },
+ "content": [{
+ "type": "text",
+ "text": "scss/base/ - директория для базовых стилей, которые мы изменять не будем"
+ }]
+ }]
+ },
+ {
+ "type": "listitem",
+ "content": [{
+ "type": "paragraph",
+ "attrs": {
+ "align": null
+ },
+ "content": [{
+ "type": "text",
+ "text": "svg/ - директория для файлов SVG"
+ }]
+ }]
+ },
+ {
+ "type": "listitem",
+ "content": [{
+ "type": "paragraph",
+ "attrs": {
+ "align": null
+ },
+ "content": [{
+ "type": "text",
+ "text": "svg/css/ - директория для SVG-файлов, которые будут интегрироваться в CSS"
+ }]
+ }]
+ }
+ ]
+ },
+ {
+ "type": "paragraph",
+ "attrs": {
+ "align": null
+ }
+ }
+ ]
+ },
+ {
+ "type": "paragraph",
+ "attrs": {
+ "align": null
+ },
+ "content": [{
+ "type": "text",
+ "text": " Получиться в итоге должно приблизительно следующее:"
+ }]
+ },
+ {
+ "type": "code_block",
+ "attrs": {
+ "lang": "powershell",
+ "code": "💼 project/\n├──── 📡 build/\n├──── 🧶 node-modules/\n├──┬─ 🗄 src/\n│ ├──┬─ ⚙ components/\n│ │ ├─── 🧩 bem-blocks/\n│ │ └─── 🗂 page-blocks/\n│ ├──── 🔤 fonts/\n│ ├──── 🖼 img/\n│ ├──── 📑 js/\n│ ├──┬─ 📜 scss/\n│ │ └─── 🧬 base/\n│ └──┬─ ⛱ svg/\n│ └─── 🏷 css/\n└──── 🛠 tasks/",
+ "inserted": true
+ }
+ },
+ {
+ "type": "paragraph",
+ "attrs": {
+ "align": null
+ },
+ "content": [{
+ "type": "text",
+ "marks": [{
+ "type": "bold"
+ }],
+ "text": "ВАЖНО:"
+ },
+ {
+ "type": "text",
+ "text": " в пустых директориях, таких как img/, fonts/ и т.п. перед тем как пушить в удалённый репозиторий (например на Github) нужно создать пустые файлы с именем .gitkeep. Это нужно для того, чтобы Github не удалил пустые директории из сборки."
+ }
+ ]
+ },
+ {
+ "type": "heading",
+ "attrs": {
+ "level": 1,
+ "class": null
+ },
+ "content": [{
+ "type": "text",
+ "text": "Добавление задач и настройка плагинов"
+ }]
+ },
+ {
+ "type": "paragraph",
+ "attrs": {
+ "align": null
+ },
+ "content": [{
+ "type": "text",
+ "text": "Основной целью Gulp является автоматизация рутинных действий в разработке, которые мы можем запрограммировать с помощью задач и плагинов. Первую тестовую задачу мы с вами уже написали. Давайте теперь напишем настоящие задачи, которые будут нам помогать в работе."
+ }]
+ },
+ {
+ "type": "hr",
+ "attrs": {
+ "inserted": true
+ }
+ },
+ {
+ "type": "heading",
+ "attrs": {
+ "level": 1,
+ "class": null
+ },
+ "content": [{
+ "type": "text",
+ "text": "Задачи стилей"
+ }]
+ },
+ {
+ "type": "paragraph",
+ "attrs": {
+ "align": null
+ },
+ "content": [{
+ "type": "text",
+ "text": "Для работы с scss нам нужно будет установить некоторые плагины, которые будут обрабатывать и компилировать наш scss код в готовый css. Прежде всего, установим сам плагин "
+ },
+ {
+ "type": "text",
+ "marks": [{
+ "type": "link",
+ "attrs": {
+ "href": "https://www.npmjs.com/package/gulp-sass"
+ }
+ }],
+ "text": "Gulp-sass"
+ },
+ {
+ "type": "text",
+ "text": ", который будет компилировать scss файлы в обычный css, понятный браузеру. Так же, для того, чтобы scss-файлы можно было импортировать не по одному, а целыми директориями нам понадобится "
+ },
+ {
+ "type": "text",
+ "marks": [{
+ "type": "link",
+ "attrs": {
+ "href": "https://www.npmjs.com/package/gulp-sass-bulk-importer"
+ }
+ }],
+ "text": "gulp-sass-bulk-importer"
+ },
+ {
+ "type": "text",
+ "text": ", для автоматической расстановки префиксов - "
+ },
+ {
+ "type": "text",
+ "marks": [{
+ "type": "link",
+ "attrs": {
+ "href": "https://www.npmjs.com/package/gulp-autoprefixer"
+ }
+ }],
+ "text": "gulp-autoprefixer"
+ },
+ {
+ "type": "text",
+ "text": ", для очистки лишнего css - "
+ },
+ {
+ "type": "text",
+ "marks": [{
+ "type": "link",
+ "attrs": {
+ "href": "https://www.npmjs.com/package/gulp-clean-css"
+ }
+ }],
+ "text": "gulp-clean-css"
+ },
+ {
+ "type": "text",
+ "text": " и для конкатинации (\"склеивания\" файлов вместе) - "
+ },
+ {
+ "type": "text",
+ "marks": [{
+ "type": "link",
+ "attrs": {
+ "href": "https://www.npmjs.com/package/gulp-concat"
+ }
+ }],
+ "text": "gulp-concat."
+ },
+ {
+ "type": "text",
+ "text": " Так же, для того, чтобы в DevTools было понятно, из какого файла взялись стили, установим "
+ },
+ {
+ "type": "text",
+ "marks": [{
+ "type": "link",
+ "attrs": {
+ "href": "https://www.npmjs.com/package/gulp-sourcemaps"
+ }
+ }],
+ "text": "gulp-sourcemaps"
+ },
+ {
+ "type": "text",
+ "text": ". Можно каждый раз не прописывать команду npm i в терминале, а указать перечень устанавливаемых плагинов через пробел, но ключи тогда нужно будет указать перед названиями плагинов: "
+ },
+ {
+ "type": "text",
+ "marks": [{
+ "type": "code"
+ }],
+ "text": "npm i --save-dev gulp-sass gulp-sass-bulk-importer gulp-autoprefixer gulp-clean-css gulp-concat gulp-sourcemaps"
+ }
+ ]
+ },
+ {
+ "type": "paragraph",
+ "attrs": {
+ "align": null
+ },
+ "content": [{
+ "type": "text",
+ "text": "Теперь создадим в директории tasks/ файл модуля, в котором опишем, что нужно делать галпу с scss-файлами. От gulp нам понадобятся src и dest, а остальные плагины подключим полностью:"
+ }]
+ },
+ {
+ "type": "code_block",
+ "attrs": {
+ "lang": "javascript",
+ "code": "const {\n\tsrc,\n\tdest\n} = require('gulp');\nconst sass = require('gulp-sass');\nconst bulk = require('gulp-sass-bulk-importer');\nconst prefixer = require('gulp-autoprefixer');\nconst clean = require('gulp-clean-css');\nconst concat = require('gulp-concat');\nconst map = require('gulp-sourcemap');",
+ "inserted": true
+ }
+ },
+ {
+ "type": "paragraph",
+ "attrs": {
+ "align": null
+ },
+ "content": [{
+ "type": "text",
+ "text": "Далее экспортируем безымянную функцию: "
+ }]
+ },
+ {
+ "type": "code_block",
+ "attrs": {
+ "lang": "javascript",
+ "code": "module.exports = function () {\n\treturn\n}",
+ "inserted": true
+ }
+ },
+ {
+ "type": "paragraph",
+ "attrs": {
+ "align": null
+ },
+ "content": [{
+ "type": "text",
+ "text": "В ней-то мы и будем обрабатывать наши scss файлы. Gulp выполняет последовательность действий "
+ },
+ {
+ "type": "text",
+ "marks": [{
+ "type": "code"
+ }],
+ "text": ".pipe"
+ },
+ {
+ "type": "text",
+ "text": " над объектами, указанными в модуле "
+ },
+ {
+ "type": "text",
+ "marks": [{
+ "type": "code"
+ }],
+ "text": "src"
+ },
+ {
+ "type": "text",
+ "text": ". Это похоже на конвейер, проходя по которому, код или файлы, которые мы создали в директории src/, превращаются в то, что мы хотим видеть в итоге и складываются в директорию build/. "
+ }
+ ]
+ },
+ {
+ "type": "image",
+ "attrs": {
+ "src": "https://habrastorage.org/getpro/habr/upload_files/97f/ff3/057/97fff30574c7d36097f92dba1a3cf32e.gif",
+ "title": null,
+ "customClass": "image",
+ "border": false,
+ "float": false,
+ "fullWidth": false,
+ "inserted": false,
+ "width": 320,
+ "height": 180
+ }
+ },
+ {
+ "type": "paragraph",
+ "attrs": {
+ "align": null
+ },
+ "content": [{
+ "type": "text",
+ "text": "Давайте определим порядок действий:"
+ }]
+ },
+ {
+ "type": "list",
+ "attrs": {
+ "order": 1,
+ "tag": "ol",
+ "type": "outer"
+ },
+ "content": [{
+ "type": "listitem",
+ "content": [{
+ "type": "paragraph",
+ "attrs": {
+ "align": null
+ },
+ "content": [{
+ "type": "text",
+ "text": "Взять файлы scss из директорий scss/"
+ }]
+ }]
+ },
+ {
+ "type": "listitem",
+ "content": [{
+ "type": "paragraph",
+ "attrs": {
+ "align": null
+ },
+ "content": [{
+ "type": "text",
+ "text": "Инициализировать карту исходных файлов (sourcepams)"
+ }]
+ }]
+ },
+ {
+ "type": "listitem",
+ "content": [{
+ "type": "paragraph",
+ "attrs": {
+ "align": null
+ },
+ "content": [{
+ "type": "text",
+ "text": "Скомпилировать в css"
+ }]
+ }]
+ },
+ {
+ "type": "listitem",
+ "content": [{
+ "type": "paragraph",
+ "attrs": {
+ "align": null
+ },
+ "content": [{
+ "type": "text",
+ "text": "Расставить вендорные префиксы"
+ }]
+ }]
+ },
+ {
+ "type": "listitem",
+ "content": [{
+ "type": "paragraph",
+ "attrs": {
+ "align": null
+ },
+ "content": [{
+ "type": "text",
+ "text": "Очистить от лишнего"
+ }]
+ }]
+ },
+ {
+ "type": "listitem",
+ "content": [{
+ "type": "paragraph",
+ "attrs": {
+ "align": null
+ },
+ "content": [{
+ "type": "text",
+ "text": "Склеить в единый файл style.css"
+ }]
+ }]
+ },
+ {
+ "type": "listitem",
+ "content": [{
+ "type": "paragraph",
+ "attrs": {
+ "align": null
+ },
+ "content": [{
+ "type": "text",
+ "text": "Записать карту исходных файлов в получившемся файле"
+ }]
+ }]
+ },
+ {
+ "type": "listitem",
+ "content": [{
+ "type": "paragraph",
+ "attrs": {
+ "align": null
+ },
+ "content": [{
+ "type": "text",
+ "text": "Положить его в build"
+ }]
+ }]
+ }
+ ]
+ },
+ {
+ "type": "paragraph",
+ "attrs": {
+ "align": null
+ },
+ "content": [{
+ "type": "text",
+ "text": "Итогом (return) нашей функции как раз и будет результат всей последовательности действий, которые мы определили. Это и запишем:"
+ }]
+ },
+ {
+ "type": "code_block",
+ "attrs": {
+ "lang": "javascript",
+ "code": "\treturn src('src/scss/**/*.scss')\n\t\t.pipe(map.init())\n\t\t.pipe(bulk())\n\t\t.pipe(sass())\n\t\t.pipe(prefixer())\n\t\t.pipe(clean())\n\t\t.pipe(concat('style.min.css'))\n\t\t.pipe(map.write('../sourcemaps/'))\n\t\t.pipe(dest('build/css/'))",
+ "inserted": true
+ }
+ },
+ {
+ "type": "spoiler",
+ "attrs": {
+ "title": "Пояснение каждой строки кода отдельно"
+ },
+ "content": [{
+ "type": "paragraph",
+ "attrs": {
+ "align": null
+ },
+ "content": [{
+ "type": "text",
+ "marks": [{
+ "type": "code"
+ }],
+ "text": "src('src/scss/**/*.scss')"
+ },
+ {
+ "type": "text",
+ "text": " - определяем источник исходного кода (source)"
+ }
+ ]
+ },
+ {
+ "type": "paragraph",
+ "attrs": {
+ "align": null
+ },
+ "content": [{
+ "type": "text",
+ "marks": [{
+ "type": "code"
+ }],
+ "text": ".pipe(map.init())"
+ },
+ {
+ "type": "text",
+ "text": " - инициализируем маппинг, чтобы он отслеживал включаемые файлы"
+ }
+ ]
+ },
+ {
+ "type": "paragraph",
+ "attrs": {
+ "align": null
+ },
+ "content": [{
+ "type": "text",
+ "marks": [{
+ "type": "code"
+ }],
+ "text": ".pipe(bulk())"
+ },
+ {
+ "type": "text",
+ "text": " - проводим код через плагин, который ползволяет использовать директиву @include в scss для директорий, а не только для отдельных файлов"
+ }
+ ]
+ },
+ {
+ "type": "paragraph",
+ "attrs": {
+ "align": null
+ },
+ "content": [{
+ "type": "text",
+ "marks": [{
+ "type": "code"
+ }],
+ "text": ".pipe(sass())"
+ },
+ {
+ "type": "text",
+ "text": " - проводим код через сам компиллятор sass"
+ }
+ ]
+ },
+ {
+ "type": "paragraph",
+ "attrs": {
+ "align": null
+ },
+ "content": [{
+ "type": "text",
+ "marks": [{
+ "type": "code"
+ }],
+ "text": ".pipe(prefixer())"
+ },
+ {
+ "type": "text",
+ "text": " - проводим код через префиксер, который расставит вендорные префиксы"
+ }
+ ]
+ },
+ {
+ "type": "paragraph",
+ "attrs": {
+ "align": null
+ },
+ "content": [{
+ "type": "text",
+ "marks": [{
+ "type": "code"
+ }],
+ "text": ".pipe(clean())"
+ },
+ {
+ "type": "text",
+ "text": " - проводим код через \"очиститель\" от лишнего css"
+ }
+ ]
+ },
+ {
+ "type": "paragraph",
+ "attrs": {
+ "align": null
+ },
+ "content": [{
+ "type": "text",
+ "marks": [{
+ "type": "code"
+ }],
+ "text": ".pipe(concat('style.min.css'))"
+ },
+ {
+ "type": "text",
+ "text": " - склеиваем все исходные файлы в один"
+ }
+ ]
+ },
+ {
+ "type": "paragraph",
+ "attrs": {
+ "align": null
+ },
+ "content": [{
+ "type": "text",
+ "marks": [{
+ "type": "code"
+ }],
+ "text": ".pipe(map.write('../sourcemaps/'))"
+ },
+ {
+ "type": "text",
+ "text": " - записываем \"карту\" источников полученного файла"
+ }
+ ]
+ },
+ {
+ "type": "paragraph",
+ "attrs": {
+ "align": null
+ },
+ "content": [{
+ "type": "text",
+ "marks": [{
+ "type": "code"
+ }],
+ "text": ".pipe(dest('build/css/'))"
+ },
+ {
+ "type": "text",
+ "text": " - кладём итоговый файл в директорию"
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "type": "paragraph",
+ "attrs": {
+ "align": null
+ },
+ "content": [{
+ "type": "text",
+ "text": "Единственное, чего нам здесь не хватает - настроек и методов для некоторых плагинов. Настройки для плагинов задаются в виде объектов, которые передаются аргументом в функцию плагина. Звучит страшно, а на деле выглядит примерно так: "
+ },
+ {
+ "type": "text",
+ "marks": [{
+ "type": "code"
+ }],
+ "text": ".pipe(sass({outputStyle: 'compressed'})"
+ },
+ {
+ "type": "text",
+ "text": ". Для sass я определяю степень сжатия выходного css как compressed, а так же добавляю на событие error логирование ошибки, чтобы было понятно, что не так (если вдруг). И того получаем такой пайп: "
+ }
+ ]
+ },
+ {
+ "type": "code_block",
+ "attrs": {
+ "lang": "javascript",
+ "code": ".pipe(sass({\n outputStyle: 'compressed'\n}).on('error', sass.logError))",
+ "inserted": true
+ }
+ },
+ {
+ "type": "paragraph",
+ "attrs": {
+ "align": null
+ },
+ "content": [{
+ "type": "text",
+ "text": "Для автопрефиксера возьму рекомендованые в документации параметры. Для того, чтобы задать свои значения настроек - переходите к документации каждого плагина и читайте, за что отвечает та или иная опция."
+ }]
+ },
+ {
+ "type": "code_block",
+ "attrs": {
+ "lang": "javascript",
+ "code": ".pipe(prefixer({\n overrideBrowserslist: ['last 8 versions'],\n browsers: [\n 'Android >= 4',\n 'Chrome >= 20',\n 'Firefox >= 24',\n 'Explorer >= 11',\n 'iOS >= 6',\n 'Opera >= 12',\n 'Safari >= 6',\n ],\n}))",
+ "inserted": true
+ }
+ },
+ {
+ "type": "paragraph",
+ "attrs": {
+ "align": null
+ },
+ "content": [{
+ "type": "text",
+ "text": "С клинером вообще всё просто: выставляю уровень очистки (level) в значение 2 и готово."
+ }]
+ },
+ {
+ "type": "paragraph",
+ "attrs": {
+ "align": null
+ },
+ "content": [{
+ "type": "text",
+ "marks": [{
+ "type": "bold"
+ }],
+ "text": "Что такое bs и для чего он нужен я опишу ниже, в соответствующем разделе."
+ }]
+ },
+ {
+ "type": "spoiler",
+ "attrs": {
+ "title": "Как итог, в файле tasks/style.js у нас будет следующее:"
+ },
+ "content": [{
+ "type": "code_block",
+ "attrs": {
+ "lang": "javascript",
+ "code": "const {\n\tsrc,\n\tdest\n} = require('gulp');\nconst sass = require('gulp-sass');\nconst bulk = require('gulp-sass-bulk-importer');\nconst prefixer = require('gulp-autoprefixer');\nconst clean = require('gulp-clean-css');\nconst concat = require('gulp-concat');\nconst map = require('gulp-sourcemaps');\nconst bs = require('browser-sync');\n\nmodule.exports = function () {\n\treturn src('src/scss/**/*.scss')\n\t\t.pipe(map.init())\n\t\t.pipe(bulk())\n\t\t.pipe(sass({\n\t\t\toutputStyle: 'compressed'\n\t\t}).on('error', sass.logError))\n\t\t.pipe(prefixer({\n\t\t\toverrideBrowserslist: ['last 8 versions'],\n\t\t\tbrowsers: [\n\t\t\t\t'Android >= 4',\n\t\t\t\t'Chrome >= 20',\n\t\t\t\t'Firefox >= 24',\n\t\t\t\t'Explorer >= 11',\n\t\t\t\t'iOS >= 6',\n\t\t\t\t'Opera >= 12',\n\t\t\t\t'Safari >= 6',\n\t\t\t],\n\t\t}))\n\t\t.pipe(clean({\n\t\t\tlevel: 2\n\t\t}))\n\t\t.pipe(concat('style.min.css'))\n\t\t.pipe(map.write('../sourcemaps/'))\n\t\t.pipe(dest('build/css/'))\n .pipe(bs.stream())\n}",
+ "inserted": true
+ }
+ },
+ {
+ "type": "paragraph",
+ "attrs": {
+ "align": null
+ }
+ }
+ ]
+ },
+ {
+ "type": "paragraph",
+ "attrs": {
+ "align": null
+ },
+ "content": [{
+ "type": "text",
+ "text": "Приблизитекльно аналогичным образом поступаем со стилями библиотек и плагинов, которые будем подключать к будущим проектам. Создаём константу plugins, в которой у нас будет массив файлов-источников из node_modules "
+ },
+ {
+ "type": "text",
+ "marks": [{
+ "type": "code"
+ }],
+ "text": "const plugins = [];"
+ },
+ {
+ "type": "text",
+ "text": " . Путь к файлам стилей будем писать в кавычках через запятую - получится массив строк с путями к файлам плагинов. Подключаем в файл gulp.src и gulp.dest, плагины gulp-concat и gulp-sourcemaps аналогично предыдущей задаче и прописываем наш \"конвейер\":"
+ }
+ ]
+ },
+ {
+ "type": "list",
+ "attrs": {
+ "order": 1,
+ "tag": "ol",
+ "type": "outer"
+ },
+ "content": [{
+ "type": "listitem",
+ "content": [{
+ "type": "paragraph",
+ "attrs": {
+ "align": null
+ },
+ "content": [{
+ "type": "text",
+ "text": "Взять файлы из источников (в данном случае - из константы)"
+ }]
+ }]
+ },
+ {
+ "type": "listitem",
+ "content": [{
+ "type": "paragraph",
+ "attrs": {
+ "align": null
+ },
+ "content": [{
+ "type": "text",
+ "text": "Инициализировать маппинг"
+ }]
+ }]
+ },
+ {
+ "type": "listitem",
+ "content": [{
+ "type": "paragraph",
+ "attrs": {
+ "align": null
+ },
+ "content": [{
+ "type": "text",
+ "text": "Прогнать всё через sass"
+ }]
+ }]
+ },
+ {
+ "type": "listitem",
+ "content": [{
+ "type": "paragraph",
+ "attrs": {
+ "align": null
+ },
+ "content": [{
+ "type": "text",
+ "text": "Подчистить неиспользуемый css (в библиотеках это особенно важно)"
+ }]
+ }]
+ },
+ {
+ "type": "listitem",
+ "content": [{
+ "type": "paragraph",
+ "attrs": {
+ "align": null
+ },
+ "content": [{
+ "type": "text",
+ "text": "Сконкатенировать файлы в один"
+ }]
+ }]
+ },
+ {
+ "type": "listitem",
+ "content": [{
+ "type": "paragraph",
+ "attrs": {
+ "align": null
+ },
+ "content": [{
+ "type": "text",
+ "text": "Записать маппинг"
+ }]
+ }]
+ },
+ {
+ "type": "listitem",
+ "content": [{
+ "type": "paragraph",
+ "attrs": {
+ "align": null
+ },
+ "content": [{
+ "type": "text",
+ "text": "Сложить в директорию build/css"
+ }]
+ }]
+ }
+ ]
+ },
+ {
+ "type": "paragraph",
+ "attrs": {
+ "align": null
+ },
+ "content": [{
+ "type": "text",
+ "text": "Но тут есть одна особенность - добавим условие, если длина массива plugins будет равна нулю - эта функция просто вернёт true, без выполнения других действий. Без этого, наша функция будет ругаться на отсутствие плагинов. А они нужны далеко не всегда."
+ }]
+ },
+ {
+ "type": "spoiler",
+ "attrs": {
+ "title": "В результате, файл tasks/libs_style.js будет выглядеть так:"
+ },
+ "content": [{
+ "type": "code_block",
+ "attrs": {
+ "lang": "javascript",
+ "code": "const plugins = [];\nconst {\n\tsrc,\n\tdest\n} = require('gulp');\nconst sass = require('gulp-sass');\nconst concat = require('gulp-concat');\nconst map = require('gulp-sourcemaps');\n\nmodule.exports = function () {\n\tif (plugins.length > 0) {\n\t\treturn src(plugins)\n\t\t\t.pipe(map.init())\n\t\t\t.pipe(sass({\n\t\t\t\toutputStyle: 'compressed'\n\t\t\t}).on('error', sass.logError))\n\t\t\t.pipe(concat('libs.min.css'))\n\t\t\t.pipe(map.write('../sourcemaps/'))\n\t\t\t.pipe(dest('build/css/'))\n\t} else {\n\t\treturn true;\n\t}\n}",
+ "inserted": true
+ }
+ },
+ {
+ "type": "paragraph",
+ "attrs": {
+ "align": null
+ }
+ }
+ ]
+ },
+ {
+ "type": "hr",
+ "attrs": {
+ "inserted": true
+ }
+ },
+ {
+ "type": "heading",
+ "attrs": {
+ "level": 1,
+ "class": null
+ },
+ "content": [{
+ "type": "text",
+ "text": "Задачи для JavaScript"
+ }]
+ },
+ {
+ "type": "paragraph",
+ "attrs": {
+ "align": null
+ },
+ "content": [{
+ "type": "text",
+ "text": "C JavaScript всё будет немного сложнее. Для разработки нам понадобится чистый, не редактированный и не минимизированный код. При этом, в готовом проекте мы будем прогонять код через babel, который не только приводит код к стандарту ES5, но и скоращает его, путём замены имён переменных и функций на более короткие (одно-двух символьные). Поэтому, нам потребуется три разные задачи: для нашего JS-кода, для плагинов/библиотек и для билда."
+ }]
+ },
+ {
+ "type": "paragraph",
+ "attrs": {
+ "align": null
+ },
+ "content": [{
+ "type": "text",
+ "text": "Для обработки JS нам понадобится "
+ },
+ {
+ "type": "text",
+ "marks": [{
+ "type": "link",
+ "attrs": {
+ "href": "https://www.npmjs.com/package/gulp-uglify-es"
+ }
+ }],
+ "text": "gulp-uglify-es"
+ },
+ {
+ "type": "text",
+ "text": " - для минификации JS-кода и "
+ },
+ {
+ "type": "text",
+ "marks": [{
+ "type": "link",
+ "attrs": {
+ "href": "https://www.npmjs.com/package/gulp-babel"
+ }
+ }],
+ "text": "gulp-babel"
+ },
+ {
+ "type": "text",
+ "text": " для оптимизации. С остальными плагинами, которыми мы будем обрабатывать наш код вы уже знакомы."
+ }
+ ]
+ },
+ {
+ "type": "paragraph",
+ "attrs": {
+ "align": null
+ },
+ "content": [{
+ "type": "text",
+ "text": "Для начала, опишем процесс работы с кодом:"
+ }]
+ },
+ {
+ "type": "list",
+ "attrs": {
+ "order": 1,
+ "tag": "ol",
+ "type": "outer"
+ },
+ "content": [{
+ "type": "listitem",
+ "content": [{
+ "type": "paragraph",
+ "attrs": {
+ "align": null
+ },
+ "content": [{
+ "type": "text",
+ "text": "Определить источники (sources)"
+ }]
+ }]
+ },
+ {
+ "type": "listitem",
+ "content": [{
+ "type": "paragraph",
+ "attrs": {
+ "align": null
+ },
+ "content": [{
+ "type": "text",
+ "text": "Инициализировать маппинг"
+ }]
+ }]
+ },
+ {
+ "type": "listitem",
+ "content": [{
+ "type": "paragraph",
+ "attrs": {
+ "align": null
+ },
+ "content": [{
+ "type": "text",
+ "text": "Склеить в один файл"
+ }]
+ }]
+ },
+ {
+ "type": "listitem",
+ "content": [{
+ "type": "paragraph",
+ "attrs": {
+ "align": null
+ },
+ "content": [{
+ "type": "text",
+ "text": "Минифицировать полученны файл"
+ }]
+ }]
+ },
+ {
+ "type": "listitem",
+ "content": [{
+ "type": "paragraph",
+ "attrs": {
+ "align": null
+ },
+ "content": [{
+ "type": "text",
+ "text": "Записать источники в файл"
+ }]
+ }]
+ },
+ {
+ "type": "listitem",
+ "content": [{
+ "type": "paragraph",
+ "attrs": {
+ "align": null
+ },
+ "content": [{
+ "type": "text",
+ "text": "Положить итоговый файл в build/js/"
+ }]
+ }]
+ }
+ ]
+ },
+ {
+ "type": "paragraph",
+ "attrs": {
+ "align": null
+ },
+ "content": [{
+ "type": "text",
+ "text": "Для итоговой (билдовой) версии после 4 пункта мы ещё прогоним его через babel для оптимизации. И для этого нам понадобится установить "
+ },
+ {
+ "type": "text",
+ "marks": [{
+ "type": "link",
+ "attrs": {
+ "href": "https://www.npmjs.com/package/@babel/core"
+ }
+ }],
+ "text": "@babel/core"
+ },
+ {
+ "type": "text",
+ "text": " , а так же "
+ },
+ {
+ "type": "text",
+ "marks": [{
+ "type": "link",
+ "attrs": {
+ "href": "https://www.npmjs.com/package/@babel/preset-env"
+ }
+ }],
+ "text": "@babel/preset-env"
+ },
+ {
+ "type": "text",
+ "text": ". После установки, в корне проекта нужно будет создать файл "
+ },
+ {
+ "type": "text",
+ "marks": [{
+ "type": "code"
+ }],
+ "text": ".babelrc"
+ },
+ {
+ "type": "text",
+ "text": " , который указывает на пресет настроек Babel со следующим содержимым:"
+ }
+ ]
+ },
+ {
+ "type": "code_block",
+ "attrs": {
+ "lang": "json",
+ "code": "{\n \"presets\": [\"@babel/preset-env\"]\n}",
+ "inserted": true
+ }
+ },
+ {
+ "type": "paragraph",
+ "attrs": {
+ "align": null
+ },
+ "content": [{
+ "type": "text",
+ "text": "Теперь можем приступать к написанию файлов задач. Они будут максимально похожи друг на друга, поэтому нет смысла описывать каждый отдельно. Разница будет лишь в задаче build, какк я уже писал выше."
+ }]
+ },
+ {
+ "type": "spoiler",
+ "attrs": {
+ "title": "Файл tasks/dev_js.js"
+ },
+ "content": [{
+ "type": "code_block",
+ "attrs": {
+ "lang": "javascript",
+ "code": "const {\n\tsrc,\n\tdest\n} = require('gulp');\nconst uglify = require('gulp-uglify-es').default;\nconst concat = require('gulp-concat');\nconst map = require('gulp-sourcemaps');\nconst bs = require('browser-sync');\n\nmodule.exports = function () {\n\treturn src(['src/components/bem-blocks/**/*.js', 'src/js/01_main.js'])\n\t\t.pipe(map.init())\n\t\t.pipe(uglify())\n\t\t.pipe(concat('main.min.js'))\n\t\t.pipe(map.write('../sourcemaps'))\n\t\t.pipe(dest('build/js/'))\n .pipe(bs.stream())\n}",
+ "inserted": true
+ }
+ },
+ {
+ "type": "paragraph",
+ "attrs": {
+ "align": null
+ }
+ }
+ ]
+ },
+ {
+ "type": "spoiler",
+ "attrs": {
+ "title": "Файл tasks/libs_js.js"
+ },
+ "content": [{
+ "type": "code_block",
+ "attrs": {
+ "lang": "javascript",
+ "code": "const plugins = [];\nconst {\n\tsrc,\n\tdest\n} = require('gulp');\nconst uglify = require('gulp-uglify-es').default;\nconst concat = require('gulp-concat');\nconst map = require('gulp-sourcemaps');\n\nmodule.exports = function () {\n\tif (plugins.length > 0)\n\t\treturn src(['src/js/01__main.js', 'src/components/bem-blocks/**/*.js'])\n\t\t\t.pipe(map.init())\n\t\t\t.pipe(uglify())\n\t\t\t.pipe(concat('main.min.js'))\n\t\t\t.pipe(map.write('../sourcemaps'))\n\t\t\t.pipe(dest('build/js/'))\n\telse {\n\t\treturn true;\n\t}\n}",
+ "inserted": true
+ }
+ },
+ {
+ "type": "paragraph",
+ "attrs": {
+ "align": null
+ }
+ }
+ ]
+ },
+ {
+ "type": "spoiler",
+ "attrs": {
+ "title": "Файл tasks/build__js.js"
+ },
+ "content": [{
+ "type": "code_block",
+ "attrs": {
+ "lang": "javascript",
+ "code": "const {\n\tsrc,\n\tdest\n} = require('gulp');\nconst uglify = require('gulp-uglify-es').default;\nconst babel = require('gulp-babel');\nconst concat = require('gulp-concat');\n\nmodule.exports = function () {\n\treturn src(['src/components/bem-blocks/**/*.js', 'src/js/01_main.js'])\n\t\t.pipe(uglify())\n\t\t.pipe(babel({\n\t\t\tpresets: ['@babel/env']\n\t\t}))\n\t\t.pipe(concat('main.min.js'))\n\t\t.pipe(dest('build/js/'))\n}",
+ "inserted": true
+ }
+ },
+ {
+ "type": "paragraph",
+ "attrs": {
+ "align": null
+ }
+ }
+ ]
+ },
+ {
+ "type": "paragraph",
+ "attrs": {
+ "align": null
+ },
+ "content": [{
+ "type": "text",
+ "text": "Как видим, тут есть Указанный напрямую файл "
+ },
+ {
+ "type": "text",
+ "marks": [{
+ "type": "code"
+ }],
+ "text": "src/js/01_main.js"
+ },
+ {
+ "type": "text",
+ "text": " который должен присутствовать для выполнения задач, поэтому создадим его в директории "
+ },
+ {
+ "type": "text",
+ "marks": [{
+ "type": "code"
+ }],
+ "text": "src/js"
+ },
+ {
+ "type": "text",
+ "text": ". "
+ }
+ ]
+ },
+ {
+ "type": "paragraph",
+ "attrs": {
+ "align": null
+ },
+ "content": [{
+ "type": "text",
+ "text": "Так же стоит отметить ещё пару мелких особенностей. "
+ },
+ {
+ "type": "text",
+ "marks": [{
+ "type": "italic"
+ }],
+ "text": "Во-первых,"
+ },
+ {
+ "type": "text",
+ "text": " как видите, подключение плагина gulp-uglify-es я сделал сразу с параметром (свойством) default: "
+ },
+ {
+ "type": "text",
+ "marks": [{
+ "type": "code"
+ }],
+ "text": "const uglify = require('gulp-uglify-es').default"
+ },
+ {
+ "type": "text",
+ "text": " - указание какого-либо параметра обязательно для успешной работы плагина. Я использую стандартную настройку. Вы-же можете порыться в документации к плагину и использовать другие настройки, если хотите. "
+ },
+ {
+ "type": "text",
+ "marks": [{
+ "type": "italic"
+ }],
+ "text": "Во-вторых"
+ },
+ {
+ "type": "text",
+ "text": ", в build-задаче я не использовал sourcemaps, поскольку они там и не нужны. Эта карта необходима при разработке, чтобы видеть источники кода в итоговом файле."
+ }
+ ]
+ },
+ {
+ "type": "hr",
+ "attrs": {
+ "inserted": true
+ }
+ },
+ {
+ "type": "heading",
+ "attrs": {
+ "level": 1,
+ "class": null
+ },
+ "content": [{
+ "type": "text",
+ "text": "Задачи для HTML/PHP"
+ }]
+ },
+ {
+ "type": "paragraph",
+ "attrs": {
+ "align": null
+ },
+ "content": [{
+ "type": "text",
+ "text": "Следующим этапом будет обработка HTML или PHP, в зависимости от того, на чём вы пишете. Мы не будем рассматривать препроцессоры, такие как "
+ },
+ {
+ "type": "text",
+ "marks": [{
+ "type": "link",
+ "attrs": {
+ "href": "https://pugjs.org/api/getting-started.html"
+ }
+ }],
+ "text": "Pug"
+ },
+ {
+ "type": "text",
+ "text": " или "
+ },
+ {
+ "type": "text",
+ "marks": [{
+ "type": "link",
+ "attrs": {
+ "href": "https://mozilla.github.io/nunjucks/"
+ }
+ }],
+ "text": "Nunjucks"
+ },
+ {
+ "type": "text",
+ "text": " по той простой причине, что с появлением Emmet они перестали значительно ускорять процесс разработки на HTML, а шаблоны мы вполне можем строить другим плагином: "
+ },
+ {
+ "type": "text",
+ "marks": [{
+ "type": "link",
+ "attrs": {
+ "href": "https://www.npmjs.com/package/gulp-file-include"
+ }
+ }],
+ "text": "gulp-file-include"
+ },
+ {
+ "type": "text",
+ "text": ", который умеет, как мне кажется, нечто большее - включать текстовое представление "
+ },
+ {
+ "type": "text",
+ "marks": [{
+ "type": "bold"
+ }],
+ "text": "любых"
+ },
+ {
+ "type": "text",
+ "text": " файлов в "
+ },
+ {
+ "type": "text",
+ "marks": [{
+ "type": "bold"
+ }],
+ "text": "любые"
+ },
+ {
+ "type": "text",
+ "text": " файлы. Далее, вы поймёте, почему я считаю это важным. Для разработки на PHP это вообще не имеет никакого значения. Там можно использовать require и другие возможности PHP из коробки. Если же вы привыкли пользоваться препроцессорами - вы легко сможете настроить Gulp для их обработки, я считаю."
+ }
+ ]
+ },
+ {
+ "type": "paragraph",
+ "attrs": {
+ "align": null
+ },
+ "content": [{
+ "type": "text",
+ "text": "Для минификации HTML можно использовать "
+ },
+ {
+ "type": "text",
+ "marks": [{
+ "type": "link",
+ "attrs": {
+ "href": "https://www.npmjs.com/package/gulp-htmlmin"
+ }
+ }],
+ "text": "gulp-htmlmin"
+ },
+ {
+ "type": "text",
+ "text": ", добавив его в виде "
+ },
+ {
+ "type": "text",
+ "marks": [{
+ "type": "code"
+ }],
+ "text": ".pipe(htmlmin({ collapseWhitespace: true }))"
+ },
+ {
+ "type": "text",
+ "text": " в свою задачу html. Однако, как показывает практика, вёрстка - не конечный этап разработки в 90% случаев, поэтому добавлять его в сборку я не буду. При необходимости, сможете установить и добавить в задачу по аналогией с другими плагинами."
+ }
+ ]
+ },
+ {
+ "type": "paragraph",
+ "attrs": {
+ "align": null
+ },
+ "content": [{
+ "type": "text",
+ "text": "Устанавливаем gulp-file-include и пишем задачу для html:"
+ }]
+ },
+ {
+ "type": "spoiler",
+ "attrs": {
+ "title": "Файл tasks/html.js"
+ },
+ "content": [{
+ "type": "code_block",
+ "attrs": {
+ "lang": "javascript",
+ "code": "const {\n\tsrc,\n\tdest\n} = require('gulp');\nconst include = require('gulp-file-include');\nconst bs = require('browser-sync');\n\nmodule.exports = function () {\n\treturn src(['src/**/*.html', '!src/**/_*.html'])\n\t\t.pipe(include())\n\t\t.pipe(dest('build'))\n .pipe(bs.stream())\n}",
+ "inserted": true
+ }
+ },
+ {
+ "type": "paragraph",
+ "attrs": {
+ "align": null
+ }
+ }
+ ]
+ },
+ {
+ "type": "paragraph",
+ "attrs": {
+ "align": null
+ },
+ "content": [{
+ "type": "text",
+ "text": "Здесь вы увидели немного новую конструкцию. В src аргумент стал массивом, в котором один из элементов обозначени восклицательным знаком(!) в начале. Это на языке JavaScript буквально означает \""
+ },
+ {
+ "type": "text",
+ "marks": [{
+ "type": "bold"
+ }],
+ "text": "не"
+ },
+ {
+ "type": "text",
+ "text": "\". То есть мы берём все файлы html "
+ },
+ {
+ "type": "text",
+ "marks": [{
+ "type": "code"
+ }],
+ "text": "'src/**/*.html'"
+ },
+ {
+ "type": "text",
+ "text": ", но только не те, которые "
+ },
+ {
+ "type": "text",
+ "marks": [{
+ "type": "code"
+ }],
+ "text": "'!src/**/_*.html'"
+ },
+ {
+ "type": "text",
+ "text": " начинаются с подчёркивания. В дальнейшем это позволит нам создавать файлы модулей, которые не должны попасть в build директорию, а служат, так сказать, служебными шаблонами. Об этих шаблонах мы поговорим ниже."
+ }
+ ]
+ },
+ {
+ "type": "paragraph",
+ "attrs": {
+ "align": null
+ },
+ "content": [{
+ "type": "text",
+ "text": "Создадим аналогичную задачу для php. Абсолютно такую же, один в один., кроме исключений. Они нам в этой задаче не потребуются."
+ }]
+ },
+ {
+ "type": "spoiler",
+ "attrs": {
+ "title": "Файл tasks/php.js"
+ },
+ "content": [{
+ "type": "code_block",
+ "attrs": {
+ "lang": "javascript",
+ "code": "const {\n\tsrc,\n\tdest\n} = require('gulp');\nconst include = require('gulp-file-include');\nconst bs = require('browser-sync');\n\nmodule.exports = function () {\n\treturn src('src/**/*.php')\n\t\t.pipe(include())\n\t\t.pipe(dest('build'))\n .pipe(bs.stream())\n}",
+ "inserted": true
+ }
+ },
+ {
+ "type": "paragraph",
+ "attrs": {
+ "align": null
+ }
+ }
+ ]
+ },
+ {
+ "type": "heading",
+ "attrs": {
+ "level": 1,
+ "class": null
+ },
+ "content": [{
+ "type": "text",
+ "text": "Задачи для изображений"
+ }]
+ },
+ {
+ "type": "paragraph",
+ "attrs": {
+ "align": null
+ },
+ "content": [{
+ "type": "text",
+ "text": "Здесь всё будет несколько сложнее. Нам потребуется как минимум две задачи для svg, две для растровых изображений. Что нам нужно:"
+ }]
+ },
+ {
+ "type": "list",
+ "attrs": {
+ "order": 1,
+ "tag": "ol",
+ "type": "outer"
+ },
+ "content": [{
+ "type": "listitem",
+ "content": [{
+ "type": "paragraph",
+ "attrs": {
+ "align": null
+ },
+ "content": [{
+ "type": "text",
+ "text": "Сжимать растровые изображения."
+ }]
+ }]
+ },
+ {
+ "type": "listitem",
+ "content": [{
+ "type": "paragraph",
+ "attrs": {
+ "align": null
+ },
+ "content": [{
+ "type": "text",
+ "text": "Конвертировать растровые изображения в webp."
+ }]
+ }]
+ },
+ {
+ "type": "listitem",
+ "content": [{
+ "type": "paragraph",
+ "attrs": {
+ "align": null
+ },
+ "content": [{
+ "type": "text",
+ "text": "Объединять svg в спрайт."
+ }]
+ }]
+ },
+ {
+ "type": "listitem",
+ "content": [{
+ "type": "paragraph",
+ "attrs": {
+ "align": null
+ },
+ "content": [{
+ "type": "text",
+ "text": "Включать svg в виде класа с фоном в CSS b cоздавать для svg класс с псевдоэлементами "
+ },
+ {
+ "type": "text",
+ "marks": [{
+ "type": "code"
+ }],
+ "text": "::after"
+ },
+ {
+ "type": "text",
+ "text": " и "
+ },
+ {
+ "type": "text",
+ "marks": [{
+ "type": "code"
+ }],
+ "text": "::before"
+ },
+ {
+ "type": "text",
+ "text": " "
+ }
+ ]
+ }]
+ }
+ ]
+ },
+ {
+ "type": "paragraph",
+ "attrs": {
+ "align": null
+ },
+ "content": [{
+ "type": "text",
+ "text": "Это всё мы будем делать разными задачами и тут нам понадобится просто тонна разных плагинов! Их так много, что я решил оформить их названия в отдельный список и убрать под спойлер."
+ }]
+ },
+ {
+ "type": "spoiler",
+ "attrs": {
+ "title": "Плагины для изображений"
+ },
+ "content": [{
+ "type": "list",
+ "attrs": {
+ "order": 1,
+ "tag": "ul",
+ "type": "outer"
+ },
+ "content": [{
+ "type": "listitem",
+ "content": [{
+ "type": "paragraph",
+ "attrs": {
+ "align": null
+ },
+ "content": [{
+ "type": "text",
+ "marks": [{
+ "type": "link",
+ "attrs": {
+ "href": "https://www.npmjs.com/package/gulp-changed"
+ }
+ }],
+ "text": "gulp-changed"
+ },
+ {
+ "type": "text",
+ "text": " - понадобится нам для отслеживания изменения в файле. Если файл не изменился, дальнейшие действия с ним не производятся."
+ }
+ ]
+ }]
+ },
+ {
+ "type": "listitem",
+ "content": [{
+ "type": "paragraph",
+ "attrs": {
+ "align": null
+ },
+ "content": [{
+ "type": "text",
+ "marks": [{
+ "type": "link",
+ "attrs": {
+ "href": "https://www.npmjs.com/package/gulp-multi-dest"
+ }
+ }],
+ "text": "gulp-multi-dest"
+ },
+ {
+ "type": "text",
+ "text": " - понадобится нам для складывания результатов обработки в несколько директорий."
+ }
+ ]
+ }]
+ },
+ {
+ "type": "listitem",
+ "content": [{
+ "type": "paragraph",
+ "attrs": {
+ "align": null
+ },
+ "content": [{
+ "type": "text",
+ "marks": [{
+ "type": "link",
+ "attrs": {
+ "href": "https://www.npmjs.com/package/gulp-imagemin"
+ }
+ }],
+ "text": "gulp-imagemin"
+ },
+ {
+ "type": "text",
+ "text": " - сжимает изображения"
+ }
+ ]
+ }]
+ },
+ {
+ "type": "listitem",
+ "content": [{
+ "type": "paragraph",
+ "attrs": {
+ "align": null
+ },
+ "content": [{
+ "type": "text",
+ "marks": [{
+ "type": "link",
+ "attrs": {
+ "href": "https://www.npmjs.com/package/imagemin-jpeg-recompress"
+ }
+ }],
+ "text": "imagemin-jpeg-recompress"
+ },
+ {
+ "type": "text",
+ "text": " - тоже сжимает изображения"
+ }
+ ]
+ }]
+ },
+ {
+ "type": "listitem",
+ "content": [{
+ "type": "paragraph",
+ "attrs": {
+ "align": null
+ },
+ "content": [{
+ "type": "text",
+ "marks": [{
+ "type": "link",
+ "attrs": {
+ "href": "https://www.npmjs.com/package/imagemin-pngquant"
+ }
+ }],
+ "text": "imagemin-pngquant"
+ },
+ {
+ "type": "text",
+ "text": " - и этот тоже сжимает изображения"
+ }
+ ]
+ }]
+ },
+ {
+ "type": "listitem",
+ "content": [{
+ "type": "paragraph",
+ "attrs": {
+ "align": null
+ },
+ "content": [{
+ "type": "text",
+ "marks": [{
+ "type": "link",
+ "attrs": {
+ "href": "https://www.npmjs.com/package/gulp-webp"
+ }
+ }],
+ "text": "gulp-webp"
+ },
+ {
+ "type": "text",
+ "text": " - конвертирует растровые форматы (png, jpeg) в webp"
+ }
+ ]
+ }]
+ },
+ {
+ "type": "listitem",
+ "content": [{
+ "type": "paragraph",
+ "attrs": {
+ "align": null
+ },
+ "content": [{
+ "type": "text",
+ "marks": [{
+ "type": "link",
+ "attrs": {
+ "href": "https://www.npmjs.com/package/gulp-svgmin"
+ }
+ }],
+ "text": "gulp-svgmin"
+ },
+ {
+ "type": "text",
+ "text": " - сжимает svg"
+ }
+ ]
+ }]
+ },
+ {
+ "type": "listitem",
+ "content": [{
+ "type": "paragraph",
+ "attrs": {
+ "align": null
+ },
+ "content": [{
+ "type": "text",
+ "marks": [{
+ "type": "link",
+ "attrs": {
+ "href": "https://www.npmjs.com/package/gulp-svg-css-pseudo"
+ }
+ }],
+ "text": "gulp-svg-css-pseudo"
+ },
+ {
+ "type": "text",
+ "text": " - добавляет svg фоном в css и сразу же создаёт псевдоэлементы"
+ }
+ ]
+ }]
+ },
+ {
+ "type": "listitem",
+ "content": [{
+ "type": "paragraph",
+ "attrs": {
+ "align": null
+ },
+ "content": [{
+ "type": "text",
+ "marks": [{
+ "type": "link",
+ "attrs": {
+ "href": "https://www.npmjs.com/package/gulp-svg-sprite"
+ }
+ }],
+ "text": "gulp-svg-sprite"
+ },
+ {
+ "type": "text",
+ "text": " - склеивает все svg в один спрайт. Лично я им пользуюсь крайне редко, но это ввиду особенностей проектов. Вообще весьма полезен для снижения запросов к серверу."
+ }
+ ]
+ }]
+ }
+ ]
+ },
+ {
+ "type": "paragraph",
+ "attrs": {
+ "align": null
+ }
+ }
+ ]
+ },
+ {
+ "type": "paragraph",
+ "attrs": {
+ "align": null
+ },
+ "content": [{
+ "type": "text",
+ "text": "Теперь создадим под всё это файлы задач: "
+ },
+ {
+ "type": "text",
+ "marks": [{
+ "type": "code"
+ }],
+ "text": "tasks/rastr.js"
+ },
+ {
+ "type": "text",
+ "text": ", "
+ },
+ {
+ "type": "text",
+ "marks": [{
+ "type": "code"
+ }],
+ "text": "tasks/webp.js"
+ },
+ {
+ "type": "text",
+ "text": ", "
+ },
+ {
+ "type": "text",
+ "marks": [{
+ "type": "code"
+ }],
+ "text": "tasks/svg_css.js"
+ },
+ {
+ "type": "text",
+ "text": " и "
+ },
+ {
+ "type": "text",
+ "marks": [{
+ "type": "code"
+ }],
+ "text": "tasks/svg_sprite.js"
+ },
+ {
+ "type": "text",
+ "text": ". Самая сложная задача будет для растровых изображений, ввиду того, что там много настроек, для объяснения значаний которых нужна "
+ },
+ {
+ "type": "text",
+ "marks": [{
+ "type": "link",
+ "attrs": {
+ "href": "https://habr.com/ru/post/422531/"
+ }
+ }],
+ "text": "отдельная статья"
+ },
+ {
+ "type": "text",
+ "text": ". Здесь я детали всех настроек описывать не буду. "
+ },
+ {
+ "type": "text",
+ "marks": [{
+ "type": "italic"
+ }],
+ "text": "Just belive me"
+ },
+ {
+ "type": "text",
+ "text": ", что я долго сидел подбирал эти настройки с дизайнером так, чтобы качество графики сильно снижало трафик и не сильно резало глаз. В итоге у нас получился вот такой монстр:"
+ }
+ ]
+ },
+ {
+ "type": "spoiler",
+ "attrs": {
+ "title": "tasks/rastr.js"
+ },
+ "content": [{
+ "type": "code_block",
+ "attrs": {
+ "lang": "javascript",
+ "code": "const {\n\tsrc,\n\tdest\n} = require('gulp');\nconst changed = require('gulp-changed');\nconst imagemin = require('gulp-imagemin');\nconst recompress = require('imagemin-jpeg-recompress');\nconst pngquant = require('imagemin-pngquant');\nconst bs = require('browser-sync');\n\nmodule.exports = function () {\n\treturn src('src/img/**/*.+(png|jpg|jpeg|gif|svg|ico)')\n\t\t.pipe(changed('build/img'))\n\t\t.pipe(imagemin({\n\t\t\t\tinterlaced: true,\n\t\t\t\tprogressive: true,\n\t\t\t\toptimizationLevel: 5,\n\t\t\t},\n\t\t\t[\n\t\t\t\trecompress({\n\t\t\t\t\tloops: 6,\n\t\t\t\t\tmin: 50,\n\t\t\t\t\tmax: 90,\n\t\t\t\t\tquality: 'high',\n\t\t\t\t\tuse: [pngquant({\n\t\t\t\t\t\tquality: [0.8, 1],\n\t\t\t\t\t\tstrip: true,\n\t\t\t\t\t\tspeed: 1\n\t\t\t\t\t})],\n\t\t\t\t}),\n\t\t\t\timagemin.gifsicle(),\n\t\t\t\timagemin.optipng(),\n\t\t\t\timagemin.svgo()\n\t\t\t], ), )\n\t\t.pipe(dest('build/img'))\n \t.pipe(bs.stream())\n}",
+ "inserted": true
+ }
+ },
+ {
+ "type": "paragraph",
+ "attrs": {
+ "align": null
+ }
+ }
+ ]
+ },
+ {
+ "type": "paragraph",
+ "attrs": {
+ "align": null
+ },
+ "content": [{
+ "type": "text",
+ "text": "В этой задаче мы применили фильтр по расширениям файлов таким образом, чтобы обрабатывались только конкретные расширения: "
+ },
+ {
+ "type": "text",
+ "marks": [{
+ "type": "code"
+ }],
+ "text": "src('src/img/**/*.+(png|jpg|jpeg|gif|svg|ico)')"
+ },
+ {
+ "type": "text",
+ "text": ". В данном случае прямой слеш (знак "
+ },
+ {
+ "type": "text",
+ "marks": [{
+ "type": "code"
+ }],
+ "text": "|"
+ },
+ {
+ "type": "text",
+ "text": ") означает буквально \"или\". Таким образом, при обработке эта функция выберет файлы с данными расширениями, а все остальные просто проигнорирует. Так же с помощью gulp-changed мы запретим обработку старых изображений - это ускорит выполнение задачи. Для того же, чтобы выполнение задачи не вызывало ошибку, если входящих файлов для конвертации нет - используем "
+ },
+ {
+ "type": "text",
+ "marks": [{
+ "type": "link",
+ "attrs": {
+ "href": "https://www.npmjs.com/package/gulp-plumber"
+ }
+ }],
+ "text": "gulp-plumber"
+ },
+ {
+ "type": "text",
+ "text": "."
+ }
+ ]
+ },
+ {
+ "type": "paragraph",
+ "attrs": {
+ "align": null
+ },
+ "content": [{
+ "type": "text",
+ "text": "Теперь создадим webp-дубликаты этих изображений. Эти дубликаты мы будем делать одновременно и в директорию src, и в build, банально для того, чтобы path-intellisense подсказывал нам пути к ним."
+ }]
+ },
+ {
+ "type": "spoiler",
+ "attrs": {
+ "title": "tasks/webp.js"
+ },
+ "content": [{
+ "type": "code_block",
+ "attrs": {
+ "lang": "javascript",
+ "code": "const {\n\tsrc\n} = require('gulp');\nconst webp = require('gulp-webp');\nconst changed = require('gulp-changed');\nconst multiDest = require('gulp-multi-dest');\nconst plumber = require('gulp-plumber');\n\nmodule.exports = function () {\n\treturn src('build/img/**/*.+(png|jpg|jpeg)')\n\t\t.pipe(plumber())\n\t\t.pipe(changed('build/img', {\n\t\t\textension: '.webp'\n\t\t}))\n\t\t.pipe(webp())\n\t\t.pipe(multiDest(['src/img', 'build/img']))\n}",
+ "inserted": true
+ }
+ },
+ {
+ "type": "paragraph",
+ "attrs": {
+ "align": null
+ }
+ }
+ ]
+ },
+ {
+ "type": "paragraph",
+ "attrs": {
+ "align": null
+ },
+ "content": [{
+ "type": "text",
+ "text": "Следующий этап - обработка SVG. Здесь у нас будет две задачи: добавление svg в качестве отдельного класса фоном прямо в css и создание svg-спрайта. У этих задач, хоть и похожее, но всё-же разное назначение. Я сейчас не буду вдаваться в подробности, поскольку они не предмет данной статьи. Если вам интересно, зачем нужны svg-спрайты и как этим пользоваться, то об этом писали "
+ },
+ {
+ "type": "text",
+ "marks": [{
+ "type": "link",
+ "attrs": {
+ "href": "https://habr.com/ru/sandbox/125438/"
+ }
+ }],
+ "text": "тут"
+ },
+ {
+ "type": "text",
+ "text": " и "
+ },
+ {
+ "type": "text",
+ "marks": [{
+ "type": "link",
+ "attrs": {
+ "href": "https://habr.com/ru/post/272505/"
+ }
+ }],
+ "text": "тут"
+ },
+ {
+ "type": "text",
+ "text": ". Давайте перейдём к установке нужных плагинов и написанию задач."
+ }
+ ]
+ },
+ {
+ "type": "paragraph",
+ "attrs": {
+ "align": null
+ },
+ "content": [{
+ "type": "text",
+ "text": "Прежде всего, нам нужно очистить наш svg от всего лишнего. Для этого я буду использовать "
+ },
+ {
+ "type": "text",
+ "marks": [{
+ "type": "link",
+ "attrs": {
+ "href": "https://www.npmjs.com/package/gulp-svgmin"
+ }
+ }],
+ "text": "gulp-svgmin"
+ },
+ {
+ "type": "text",
+ "text": "."
+ }
+ ]
+ },
+ {
+ "type": "paragraph",
+ "attrs": {
+ "align": null
+ },
+ "content": [{
+ "type": "text",
+ "text": "Для добавления svg в качестве background-image я буду использовать плагин "
+ },
+ {
+ "type": "text",
+ "marks": [{
+ "type": "link",
+ "attrs": {
+ "href": "https://www.npmjs.com/package/gulp-svg-css-pseudo"
+ }
+ }],
+ "text": "gulp-svg-css-pseudo"
+ },
+ {
+ "type": "text",
+ "text": ". Точнее это форк плагина "
+ },
+ {
+ "type": "text",
+ "marks": [{
+ "type": "link",
+ "attrs": {
+ "href": "https://www.npmjs.com/package/gulp-svg-css"
+ }
+ }],
+ "text": "gulp-svg-css"
+ },
+ {
+ "type": "text",
+ "text": ", который я сделал после того, как авторы оригинального плагина не добавили мой пропоуз в свой плагин. Разница между ними заключается в том, что он создаёт для каждого класса псевдоэлементы "
+ },
+ {
+ "type": "text",
+ "marks": [{
+ "type": "code"
+ }],
+ "text": "::before"
+ },
+ {
+ "type": "text",
+ "text": " и "
+ },
+ {
+ "type": "text",
+ "marks": [{
+ "type": "code"
+ }],
+ "text": "::after."
+ },
+ {
+ "type": "text",
+ "text": " Вы можете точно так же использовать оригинальный плагин, но он не будет работать с псевдоэлементами. На входе мы в определённую папку кладём файл, например myicon.svg, а на выходе получаем классы "
+ },
+ {
+ "type": "text",
+ "marks": [{
+ "type": "code"
+ }],
+ "text": "--svg__myicon"
+ },
+ {
+ "type": "text",
+ "text": ", "
+ },
+ {
+ "type": "text",
+ "marks": [{
+ "type": "code"
+ }],
+ "text": "--svg__myicon-before"
+ },
+ {
+ "type": "text",
+ "text": " и "
+ },
+ {
+ "type": "text",
+ "marks": [{
+ "type": "code"
+ }],
+ "text": "--svg__myicon-after"
+ },
+ {
+ "type": "text",
+ "text": ", у которых background-image уже задан в виде нашей картинки. Это очень просто и удобно, если картинку не нужно менять (анимировать или изменять цвет):"
+ }
+ ]
+ },
+ {
+ "type": "code_block",
+ "attrs": {
+ "lang": "css",
+ "code": ".--svg__myicon,.--svg__myicon-before::before,.--svg__myicon-after::after{\n background-image: url(\"data:image/svg+xml;charset=utf8, %3Csvg width='8' height='6' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath fill-rule='evenodd' clip-rule='evenodd' d='M.228 1.635A1 1 0 011 0h6a1 1 0 01.772 1.635L4.808 5.589a.999.999 0 01-1.616 0L.228 1.635z' fill='%232C2D2E'/%3E%3C/svg%3E\");\n}\n.--svg__myicon-before::before {\n content:'';\n}\n.--svg__myicon-after::after {\n content:'';\n}",
+ "inserted": true
+ }
+ },
+ {
+ "type": "paragraph",
+ "attrs": {
+ "align": null
+ },
+ "content": [{
+ "type": "text",
+ "text": "И так, мы будем брать файлы из директории src/svg/css и обрабатывать нашими плагинами, получая на выходе файл svg.scss и положим его в директорию src/scss/base. Полный файл этой задачи будет выглядеть так:"
+ }]
+ },
+ {
+ "type": "spoiler",
+ "attrs": {
+ "title": "tasks/svg_css.js"
+ },
+ "content": [{
+ "type": "code_block",
+ "attrs": {
+ "lang": "javascript",
+ "code": "const {\n\tsrc,\n\tdest\n} = require('gulp');\nconst svgmin = require('gulp-svgmin');\nconst svgCss = require('gulp-svg-css-pseudo');\n\nmodule.exports = function () {\n\treturn src('src/svg/css/**/*.svg')\n\t\t.pipe(svgmin({\n\t\t\tplugins: [{\n\t\t\t\t\tremoveComments: true\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tremoveEmptyContainers: true\n\t\t\t\t}\n\t\t\t]\n\t\t}))\n\t\t.pipe(svgCss({\n\t\t\tfileName: 'svg',\n\t\t\tfileExt: 'scss',\n\t\t\tcssPrefix: '--svg__',\n\t\t\taddSize: false\n\t\t}))\n\t\t.pipe(dest('src/scss/global'))\n}",
+ "inserted": true
+ }
+ },
+ {
+ "type": "paragraph",
+ "attrs": {
+ "align": null
+ }
+ }
+ ]
+ },
+ {
+ "type": "paragraph",
+ "attrs": {
+ "align": null
+ },
+ "content": [{
+ "type": "text",
+ "text": "Теперь напишем задачу для svg-спрайта. Смысл спрайтов в том, что он объединяет svg в один файл, который содержит набор векторных картинок. К этим картинкам можно обращаться по ID и вставлять их в нужное место с возможностью изменять их (например цвет) средствами css. Установим плагин "
+ },
+ {
+ "type": "text",
+ "marks": [{
+ "type": "link",
+ "attrs": {
+ "href": "https://www.npmjs.com/package/gulp-svg-sprite"
+ }
+ }],
+ "text": "gulp-svg-sprite"
+ },
+ {
+ "type": "text",
+ "text": " и создадим под него вот такую задачу:"
+ }
+ ]
+ },
+ {
+ "type": "spoiler",
+ "attrs": {
+ "title": "tasks/svg_sprite.js"
+ },
+ "content": [{
+ "type": "code_block",
+ "attrs": {
+ "lang": "javascript",
+ "code": "const {\n\tsrc,\n\tdest\n} = require('gulp');\nconst svgmin = require('gulp-svgmin');\nconst sprite = require('gulp-svg-sprite');\n\nmodule.exports = function () {\n\treturn src('src/svg/**/*.svg')\n\t\t.pipe(svgmin({\n\t\t\tplugins: [{\n\t\t\t\t\tremoveComments: true\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tremoveEmptyContainers: true\n\t\t\t\t}\n\t\t\t]\n\t\t}))\n\t\t.pipe(sprite({\n\t\t\tmode: {\n\t\t\t\tstack: {\n\t\t\t\t\tsprite: '../sprite.svg'\n\t\t\t\t}\n\t\t\t}\n\t\t}))\n\t\t.pipe(dest('src/img'))\n}",
+ "inserted": true
+ }
+ },
+ {
+ "type": "paragraph",
+ "attrs": {
+ "align": null
+ }
+ }
+ ]
+ },
+ {
+ "type": "paragraph",
+ "attrs": {
+ "align": null
+ },
+ "content": [{
+ "type": "text",
+ "text": "На выходе мы получим файл src/img/sprite.svg внутри которого и будут все наши svg. Обратиться к ним в html можно будет так: "
+ },
+ {
+ "type": "text",
+ "marks": [{
+ "type": "code"
+ }],
+ "text": ""
+ },
+ {
+ "type": "text",
+ "text": " или так:"
+ }
+ ]
+ },
+ {
+ "type": "code_block",
+ "attrs": {
+ "lang": "xml",
+ "code": "",
+ "inserted": true
+ }
+ },
+ {
+ "type": "paragraph",
+ "attrs": {
+ "align": null
+ },
+ "content": [{
+ "type": "text",
+ "text": "Если же иконка находилась во вложенной директории (например, как в нашем случае, в директории css), то перед именем файла нужно поставить имя этой директории и два дефиса. Вот так: "
+ },
+ {
+ "type": "text",
+ "marks": [{
+ "type": "code"
+ }],
+ "text": ""
+ }
+ ]
+ },
+ {
+ "type": "paragraph",
+ "attrs": {
+ "align": null
+ },
+ "content": [{
+ "type": "text",
+ "text": "Чуть позже покажу ещё и третий способ вставлять svg в html с помощью сборки. Вы можете использовать любой из этих способов или их комбинировать, в зависимости от задач, которые нужно решить."
+ }]
+ },
+ {
+ "type": "hr",
+ "attrs": {
+ "inserted": true
+ }
+ },
+ {
+ "type": "heading",
+ "attrs": {
+ "level": 1,
+ "class": null
+ },
+ "content": [{
+ "type": "text",
+ "text": "Задачи для шрифтов"
+ }]
+ },
+ {
+ "type": "paragraph",
+ "attrs": {
+ "align": null
+ },
+ "content": [{
+ "type": "text",
+ "text": "Здесь у нас будет две задачи. Первая направлена на конвертацию шрифтов из формата ttf в форматы woff и woff2. Во всех остальных форматах в 2021 году не вижу никакого смысла, поскольку поддержка формата woff простирается аж до Internet Explorer 9. Давно ли вы видели компьютеры с IE9 на борту? Конвертиролвать будем с помощью двух плагинов: "
+ },
+ {
+ "type": "text",
+ "marks": [{
+ "type": "link",
+ "attrs": {
+ "href": "https://www.npmjs.com/package/gulp-ttftowoff2"
+ }
+ }],
+ "text": "gulp-ttftowoff2"
+ },
+ {
+ "type": "text",
+ "text": " и "
+ },
+ {
+ "type": "text",
+ "marks": [{
+ "type": "link",
+ "attrs": {
+ "href": "https://www.npmjs.com/package/gulp-ttf2woff"
+ }
+ }],
+ "text": "gulp-ttftowoff"
+ },
+ {
+ "type": "text",
+ "text": ", соответственно. Файл задачи для конвертации шрифтов будет выглядеть так:"
+ }
+ ]
+ },
+ {
+ "type": "spoiler",
+ "attrs": {
+ "title": "tasks/ttf.js"
+ },
+ "content": [{
+ "type": "code_block",
+ "attrs": {
+ "lang": "javascript",
+ "code": "const {\n\tsrc,\n\tdest\n} = require('gulp');\nconst changed = require('gulp-changed');\nconst ttf2woff2 = require('gulp-ttftowoff2');\nconst ttf2woff = require('gulp-ttf2woff');\n\nmodule.exports = function (done) {\n\tsrc('src/fonts/**/*.ttf')\n\t\t.pipe(changed('build/fonts', {\n\t\t\textension: '.woff2',\n\t\t\thasChanged: changed.compareLastModifiedTime\n\t\t}))\n\t\t.pipe(ttf2woff2())\n\t\t.pipe(dest('build/fonts'))\n\n\tsrc('src/fonts/**/*.ttf')\n\t\t.pipe(changed('build/fonts', {\n\t\t\textension: 'woff',\n\t\t\thasChanged: changed.compareLastModifiedTime\n\t\t}))\n\t\t.pipe(ttf2woff())\n\t\t.pipe(dest('build/fonts'))\n\tdone();\n}",
+ "inserted": true
+ }
+ },
+ {
+ "type": "paragraph",
+ "attrs": {
+ "align": null
+ }
+ }
+ ]
+ },
+ {
+ "type": "paragraph",
+ "attrs": {
+ "align": null
+ },
+ "content": [{
+ "type": "text",
+ "text": "После того, как шрифты сконвертированы, нам нужно их подключить. Я так и не смог добиться полной автоматизации подключения шрифтов, объединения их по жирности и стилю начертания, в зависимости от имени файла и присвоения нескольких вариантов локального имени. Тем не менее, процесс подключения тоже можно немного автоматизировать. Для этого, во-первых, создадим в директороии "
+ },
+ {
+ "type": "text",
+ "marks": [{
+ "type": "code"
+ }],
+ "text": "src/scss/base"
+ },
+ {
+ "type": "text",
+ "text": " файл "
+ },
+ {
+ "type": "text",
+ "marks": [{
+ "type": "code"
+ }],
+ "text": "_mixins.scss"
+ },
+ {
+ "type": "text",
+ "text": ". В дальнейшем, он нам ещё понадобится и для других миксинов. Напишем в этом файле следующий миксин:"
+ }
+ ]
+ },
+ {
+ "type": "spoiler",
+ "attrs": {
+ "title": "src/scss/base/_mixins.scss"
+ },
+ "content": [{
+ "type": "code_block",
+ "attrs": {
+ "lang": "css",
+ "code": "@mixin font-face($name, $file, $weight: 400, $style: normal) {\n\t@font-face {\n\t\tfont-family: \"#{$name}\";\n\t\tsrc: local(\"#{$file}\"),\n\t\turl('../fonts/#{$file}.woff2') format('woff2'),\n\t\turl('../fonts/#{$file}.woff') format('woff');\n\t\tfont-weight: $weight;\n\t\tfont-style: $style;\n\t\tfont-display: swap;\n\t}\n}",
+ "inserted": true
+ }
+ },
+ {
+ "type": "paragraph",
+ "attrs": {
+ "align": null
+ }
+ }
+ ]
+ },
+ {
+ "type": "paragraph",
+ "attrs": {
+ "align": null
+ },
+ "content": [{
+ "type": "text",
+ "text": "В этот миксин нам нужно будет передать такие параметры: "
+ }]
+ },
+ {
+ "type": "list",
+ "attrs": {
+ "order": 1,
+ "tag": "ul",
+ "type": "outer"
+ },
+ "content": [{
+ "type": "listitem",
+ "content": [{
+ "type": "paragraph",
+ "attrs": {
+ "align": null
+ },
+ "content": [{
+ "type": "text",
+ "marks": [{
+ "type": "code"
+ }],
+ "text": "$name"
+ },
+ {
+ "type": "text",
+ "text": " - имя шрифтового семейства;"
+ }
+ ]
+ }]
+ },
+ {
+ "type": "listitem",
+ "content": [{
+ "type": "paragraph",
+ "attrs": {
+ "align": null
+ },
+ "content": [{
+ "type": "text",
+ "marks": [{
+ "type": "code"
+ }],
+ "text": "$file"
+ },
+ {
+ "type": "text",
+ "text": " - имя файла;"
+ }
+ ]
+ }]
+ },
+ {
+ "type": "listitem",
+ "content": [{
+ "type": "paragraph",
+ "attrs": {
+ "align": null
+ },
+ "content": [{
+ "type": "text",
+ "marks": [{
+ "type": "code"
+ }],
+ "text": "$weight"
+ },
+ {
+ "type": "text",
+ "text": " - жирность шрифта (по-умолчанию установлено значение 400, но если мы передадим параметр, то значение по-умолчанию будет проигнорировано)"
+ }
+ ]
+ }]
+ },
+ {
+ "type": "listitem",
+ "content": [{
+ "type": "paragraph",
+ "attrs": {
+ "align": null
+ },
+ "content": [{
+ "type": "text",
+ "marks": [{
+ "type": "code"
+ }],
+ "text": "$style"
+ },
+ {
+ "type": "text",
+ "text": " - стиль начертания (тоже установлен по-умолчанию normal)"
+ }
+ ]
+ }]
+ }
+ ]
+ },
+ {
+ "type": "paragraph",
+ "attrs": {
+ "align": null
+ },
+ "content": [{
+ "type": "text",
+ "text": "На выходе мы получим уже подключенный файл шрифта, но с ним потребуются некоторые манипуляции вручную, которые я опишу чуть ниже. "
+ }]
+ },
+ {
+ "type": "paragraph",
+ "attrs": {
+ "align": null
+ },
+ "content": [{
+ "type": "text",
+ "text": "Далее нам нужно написать задачу, которая будет подключать наши шрифты. По сути эта задача будет циклом проходиться по файлам, брать имя каждого файла, отсекать расширение и на основе имени генерировать код для его подключения через миксин. Для этого нам потребуется модуль nodeJS для работы с файлами, который называется fs. Он уже установлен вместе с nodejs, поэтому устанавливать его нет необходимости - нужно только подключить: "
+ },
+ {
+ "type": "text",
+ "marks": [{
+ "type": "code"
+ }],
+ "text": "const fs = require('fs');"
+ }
+ ]
+ },
+ {
+ "type": "paragraph",
+ "attrs": {
+ "align": null
+ },
+ "content": [{
+ "type": "text",
+ "text": "Далее создадим две переменные. В первую запишем, путь к файлу, который получим на выходе, а во вторую - путь к папке шрифтов, которые нам создала предыдущая задача."
+ }]
+ },
+ {
+ "type": "code_block",
+ "attrs": {
+ "lang": null,
+ "code": "let srcFonts = 'src/scss/_local-fonts.scss';\nlet appFonts = 'build/fonts/';",
+ "inserted": true
+ }
+ },
+ {
+ "type": "paragraph",
+ "attrs": {
+ "align": null
+ },
+ "content": [{
+ "type": "text",
+ "text": "Дальнейший код этой функции я честно скопировал у "
+ },
+ {
+ "type": "text",
+ "marks": [{
+ "type": "link",
+ "attrs": {
+ "href": "https://www.youtube.com/c/maxgraph/featured"
+ }
+ }],
+ "text": "Максима Васяновича"
+ },
+ {
+ "type": "text",
+ "text": " ( "
+ },
+ {
+ "type": "mention",
+ "attrs": {
+ "identity": "MaxGraph",
+ "identityType": "user",
+ "display": "@MaxGraph",
+ "link": "/users/maxgraph",
+ "class": "mention"
+ }
+ },
+ {
+ "type": "text",
+ "text": " ), с его "
+ },
+ {
+ "type": "text",
+ "marks": [{
+ "type": "link",
+ "attrs": {
+ "href": "https://github.com/maxdenaro/maxgraph-youtube-source/tree/master/Gulp%204.%20%D0%9F%D0%BE%D0%B4%D1%80%D0%BE%D0%B1%D0%BD%D0%BE%D0%B5%20%D1%80%D1%83%D0%BA%D0%BE%D0%B2%D0%BE%D0%B4%D1%81%D1%82%D0%B2%D0%BE"
+ }
+ }],
+ "text": "урока про Gulp"
+ },
+ {
+ "type": "text",
+ "text": " и немного переписал. В итоге, у нас получится вот такая функция:"
+ }
+ ]
+ },
+ {
+ "type": "spoiler",
+ "attrs": {
+ "title": "tasks/fonts.js"
+ },
+ "content": [{
+ "type": "code_block",
+ "attrs": {
+ "lang": "javascript",
+ "code": "const fs = require('fs');\n\nlet srcFonts = 'src/scss/_local-fonts.scss';\nlet appFonts = 'build/fonts/';\nmodule.exports = function (done) {\n\tfs.writeFile(srcFonts, '', () => {});\n\tfs.readdir(appFonts, (err, items) => {\n\t\tif (items) {\n\t\t\tlet c_fontname;\n\t\t\tfor (let i = 0; i < items.length; i++) {\n\t\t\t\tlet fontname = items[i].split('.'),\n\t\t\t\t\tfontExt;\n\t\t\t\tfontExt = fontname[1];\n\t\t\t\tfontname = fontname[0];\n\t\t\t\tif (c_fontname != fontname) {\n\t\t\t\t\tif (fontExt == 'woff' || fontExt == 'woff2') {\n\t\t\t\t\t\tfs.appendFile(srcFonts, `@include font-face(\"${fontname}\", \"${fontname}\", 400);\\r\\n`, () => {});\n\t\t\t\t\t\tconsole.log(`Added font ${fontname}.\n----------------------------------------------------------------------------------\nPlease, move mixin call from src/scss/_local-fonts.scss to src/scss/_fonts.scss and then change it, if font from this family added ealy!\n----------------------------------------------------------------------------------`);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tc_fontname = fontname;\n\t\t\t}\n\t\t}\n\t})\n\tdone();\n}",
+ "inserted": true
+ }
+ },
+ {
+ "type": "paragraph",
+ "attrs": {
+ "align": null
+ }
+ }
+ ]
+ },
+ {
+ "type": "paragraph",
+ "attrs": {
+ "align": null
+ },
+ "content": [{
+ "type": "text",
+ "text": "Эта функция на выходе создаст нам файл "
+ },
+ {
+ "type": "text",
+ "marks": [{
+ "type": "code"
+ }],
+ "text": "src/scss/_local-fonts.scss"
+ },
+ {
+ "type": "text",
+ "text": ", в котором под каждый файл шрифтов будет содержаться вызов миксина для его подключения. Так же, при выполнении функции в консоль будет выдаваться следующее сообщение:"
+ }
+ ]
+ },
+ {
+ "type": "code_block",
+ "attrs": {
+ "lang": "powershell",
+ "code": "Added new font: YoyrFont.\n----------------------------------------------------------------------------------\nPlease, move mixin call from src/scss/_local-fonts.scss to src/scss/_fonts.scss and then change it.\n----------------------------------------------------------------------------------",
+ "inserted": true
+ }
+ },
+ {
+ "type": "paragraph",
+ "attrs": {
+ "align": null
+ },
+ "content": [{
+ "type": "text",
+ "text": "Как видите, в сообщении написано следующее: \"Добавлен новый шрифт: "
+ },
+ {
+ "type": "text",
+ "marks": [{
+ "type": "italic"
+ }],
+ "text": "названиешрифта. "
+ },
+ {
+ "type": "text",
+ "text": "Пожалуйста, переместите вызов миксина из "
+ },
+ {
+ "type": "text",
+ "marks": [{
+ "type": "code"
+ }],
+ "text": "src/scss/_local-fonts.scss"
+ },
+ {
+ "type": "text",
+ "text": " в "
+ },
+ {
+ "type": "text",
+ "marks": [{
+ "type": "code"
+ }],
+ "text": "src/scss/_fonts.scss"
+ },
+ {
+ "type": "text",
+ "marks": [{
+ "type": "italic"
+ }],
+ "text": " "
+ },
+ {
+ "type": "text",
+ "text": "после чего измените его"
+ },
+ {
+ "type": "text",
+ "marks": [{
+ "type": "italic"
+ }],
+ "text": "\". "
+ },
+ {
+ "type": "text",
+ "text": "Давайте разберёмся, что куда нужно переместить и как изменить. Но прежде создадим тот самый файл "
+ },
+ {
+ "type": "text",
+ "marks": [{
+ "type": "code"
+ }],
+ "text": "src/scss/_fonts.scss"
+ },
+ {
+ "type": "text",
+ "text": " - он будет содержать уже реальные вызовы миксина для подключения шрифтов, которыми мы и будем пользоваться."
+ }
+ ]
+ },
+ {
+ "type": "paragraph",
+ "attrs": {
+ "align": null
+ },
+ "content": [{
+ "type": "text",
+ "text": "Когда мы добавляем шрифты, чаще всего, у нас есть отдельные файлы для разной жирности шрифта и для разных стилей начертания (наклонные и прямые, тонкие и жирные и т.п.). Каждый такой файл шрифта создаст отдельный вызов миксина и на выходе мы получим файл "
+ },
+ {
+ "type": "text",
+ "marks": [{
+ "type": "code"
+ }],
+ "text": "src/scss/_local-fonts.scss "
+ },
+ {
+ "type": "text",
+ "text": "примерно такого содержания:"
+ }
+ ]
+ },
+ {
+ "type": "code_block",
+ "attrs": {
+ "lang": "css",
+ "code": "@include font-face(\"Arial\", \"Arial\", 400);\n@include font-face(\"ArialBold\", \"ArialBold\", 400);\n@include font-face(\"ArialItalic\", \"ArialItalic\", 400);",
+ "inserted": true
+ }
+ },
+ {
+ "type": "paragraph",
+ "attrs": {
+ "align": null
+ },
+ "content": [{
+ "type": "text",
+ "text": "Как видим, мы подключаем шрифты отдельными названиями. Это неправильно с точки зрения читабельности и удобства использования шрифта, поскольку вместо того, чтобы прописывать font-weight: 700, нам каждый раз придётся указывать font-family: \"ArialBold\". Это плохая практика, поэтому нам нужно переписать эти вызовы. Как помним из миксина, который мы написали, первым параметром он принимает имя шрифтового семейства. В данном случае это \"Arial\". Второй параметр мы не меняем - это имя файла без расширения (расширения подставит миксин). Третий параметр отвечает за жирность. Изменим во второй строке 400 на 700. Четвёртый параметр, который здесь не указан, отвечает за стиль начертания. В третей строке миксин вызывается для наклонного шрифта, поэтому четвёртым параметром нужно это указать. В итоге получим:"
+ }]
+ },
+ {
+ "type": "code_block",
+ "attrs": {
+ "lang": "css",
+ "code": "@include font-face(\"Arial\", \"Arial\", 400);\n@include font-face(\"Arial\", \"ArialBold\", 700);\n@include font-face(\"Arial\", \"ArialItalic\", 400, italic);",
+ "inserted": true
+ }
+ },
+ {
+ "type": "paragraph",
+ "attrs": {
+ "align": null
+ },
+ "content": [{
+ "type": "text",
+ "text": "Теперь это всё нужно переместить в файл "
+ },
+ {
+ "type": "text",
+ "marks": [{
+ "type": "code"
+ }],
+ "text": "src/scss/_fonts.scss"
+ },
+ {
+ "type": "text",
+ "text": ", который, естественно, необходимо создать. Перемещать нужно в новый файл по той причине, что при следующем запуске эта функция перезапишет файл "
+ },
+ {
+ "type": "text",
+ "marks": [{
+ "type": "code"
+ }],
+ "text": "src/scss/_local-fonts.scss"
+ },
+ {
+ "type": "text",
+ "text": " и все наши изменения затрутся."
+ }
+ ]
+ },
+ {
+ "type": "hr",
+ "attrs": {
+ "inserted": true
+ }
+ },
+ {
+ "type": "heading",
+ "attrs": {
+ "level": 1,
+ "class": null
+ },
+ "content": [{
+ "type": "text",
+ "text": "Автоматическое обновление и синхронизация браузеров"
+ }]
+ },
+ {
+ "type": "paragraph",
+ "attrs": {
+ "align": null
+ },
+ "content": [{
+ "type": "text",
+ "text": "Нам нужно настроить Gulp таким образом, чтобы он при запуске открывал наш проект в браузере и автоматически обновлял его при каких-либо изменениях. Я для этого использую "
+ },
+ {
+ "type": "text",
+ "marks": [{
+ "type": "link",
+ "attrs": {
+ "href": "https://www.npmjs.com/package/browser-sync"
+ }
+ }],
+ "text": "browser-sync"
+ },
+ {
+ "type": "text",
+ "text": ". Этот плагин создаёт сервер средствами nodejs и позволяет подключать к нему различные браузеры. Для него у нас будет две задачи - для html-разработки и для php соответственно. Установим этот плагин и создадим два соответствующих файла задач. В первом файле мы укажем настройки для разработки html. "
+ }
+ ]
+ },
+ {
+ "type": "spoiler",
+ "attrs": {
+ "title": "tasks/bs_html.js"
+ },
+ "content": [{
+ "type": "code_block",
+ "attrs": {
+ "lang": "javascript",
+ "code": "const bs = require('browser-sync');\n\nmodule.exports = function () {\n\tbs.init({\n\t\tserver: {\n\t\t\tbaseDir: 'build/',\n\t\t\thost: '192.168.0.104',\n\t\t},\n\t\tcallbacks: {\n\t\t\tready: function (err, bs) {\n\t\t\t\tbs.addMiddleware(\"*\", function (req, res) {\n\t\t\t\t\tres.writeHead(302, {\n\t\t\t\t\t\tlocation: \"404.html\"\n\t\t\t\t\t});\n\t\t\t\t\tres.end(\"Redirecting!\");\n\t\t\t\t});\n\t\t\t}\n\t\t},\n\t\tbrowser: 'chrome',\n\t\tlogPrefix: 'BS-HTML:',\n\t\tlogLevel: 'info',\n\t\tlogConnections: true,\n\t\tlogFileChanges: true,\n\t\topen: true\n\t})\n}",
+ "inserted": true
+ }
+ },
+ {
+ "type": "paragraph",
+ "attrs": {
+ "align": null
+ }
+ }
+ ]
+ },
+ {
+ "type": "paragraph",
+ "attrs": {
+ "align": null
+ },
+ "content": [{
+ "type": "text",
+ "text": "Параметр host отвечает за IP-адрес компьютера, с которого сайт будет раздаваться на другие устройства в сети. Важно, чтобы там был указан именно IP-адрес вашего компьютера в локальной сети, если хотите иметь доступ к разрабатываемому сайту со всех устройств. Коллбэк-функция отвечает за открытие страницы 404, если она есть и если не найдена запрашиваемая (по умолчанию это страница index.html). Все остальные параметры вы вольны настраивать как вашей душе угодно, согласно документации."
+ }]
+ },
+ {
+ "type": "paragraph",
+ "attrs": {
+ "align": null
+ },
+ "content": [{
+ "type": "text",
+ "text": "Для php мы напишем гораздо более простую задачу. Она будет только обновлять браузер в случае изменений, а локальный сервер Вы будете запускать с помощью сторонней программы: openserver, wamp или любой другой."
+ }]
+ },
+ {
+ "type": "spoiler",
+ "attrs": {
+ "title": "tasks/bs_php.js"
+ },
+ "content": [{
+ "type": "code_block",
+ "attrs": {
+ "lang": "javascript",
+ "code": "\tconst bs = require('browser-sync');\n\n\tmodule.exports = function () {\n\t\tbs.init({\n\t\t\tbrowser: ['chrome'],\n\t\t\twatch: true,\n\t\t\tproxy: '',\n\t\t\tlogLevel: 'info',\n\t\t\tlogPrefix: 'BS-PHP:',\n\t\t\tlogConnections: true,\n\t\t\tlogFileChanges: true,\n\t\t})\n\t}",
+ "inserted": true
+ }
+ },
+ {
+ "type": "paragraph",
+ "attrs": {
+ "align": null
+ }
+ }
+ ]
+ },
+ {
+ "type": "paragraph",
+ "attrs": {
+ "align": null
+ },
+ "content": [{
+ "type": "text",
+ "text": "Теперь нам нужно подключить в ранее написанные задачи browser-sync и pipe для обновления браузера в конце выполнения задачи. Как подключить - вы уже знаете: так же, как и др угие плагины. А добавлять pipe мы будем следуюший: "
+ },
+ {
+ "type": "text",
+ "marks": [{
+ "type": "code"
+ }],
+ "text": ".pipe(bs.stream())"
+ },
+ {
+ "type": "text",
+ "text": "."
+ }
+ ]
+ },
+ {
+ "type": "paragraph",
+ "attrs": {
+ "align": null
+ },
+ "content": [{
+ "type": "text",
+ "text": "Перезагружать страницу нам придётся при изменении html, scss и js, php, а так же растровых картинок. Соответственно, в эти задачи я уже добавил нужный pipe."
+ }]
+ },
+ {
+ "type": "spoiler",
+ "attrs": {
+ "title": "Опционально!"
+ },
+ "content": [{
+ "type": "paragraph",
+ "attrs": {
+ "align": null
+ },
+ "content": [{
+ "type": "text",
+ "text": "Лично мне нравится, когда в консоли показывается исходный размер изображений до сжатия и итоговый - после сжатия, поэтому в сборку я добавил плагин "
+ },
+ {
+ "type": "text",
+ "marks": [{
+ "type": "link",
+ "attrs": {
+ "href": "https://www.npmjs.com/package/gulp-size"
+ }
+ }],
+ "text": "gulp-size"
+ },
+ {
+ "type": "text",
+ "text": ", но поскольку я почти не нашёл единомшленников в данном вопросе \"засорения терминала лишней инфой\", я не описывал его здесь. При желании вы можете добавить соответствующий pipe к нужным задачам или убрать его, если вы клонировали себе готовую сборку и он вас раздражает мельканием в консоли."
+ }
+ ]
+ }]
+ },
+ {
+ "type": "paragraph",
+ "attrs": {
+ "align": null
+ },
+ "content": [{
+ "type": "text",
+ "text": "Далее нам следует заняться функциями вотчинга. Вотчинг - это состояние, когда вся наша программа запущена, следит за файлами и их изменением и выполняет те или иные функции, если эти файлы изменились. И так, за чем будем следить: html, php, scss, js, json (ниже объясню, для чего), изображения в src, изображения в build (помните, что у нас webp конвертируются из уже сжатых изображений?), svg, шрифты. Давайте напишем соответствующую задачу:"
+ }]
+ },
+ {
+ "type": "spoiler",
+ "attrs": {
+ "title": "tasks/watch.js"
+ },
+ "content": [{
+ "type": "code_block",
+ "attrs": {
+ "lang": "javascript",
+ "code": "const {\n\twatch,\n\tparallel,\n\tseries\n} = require('gulp');\n\nmodule.exports = function () {\n\twatch('src/**/*.html', parallel('html'));\n\twatch('src/**/*.php', parallel('php'));\n\twatch('src/**/*.scss', parallel('style'));\n\twatch('src/**/*.js', parallel('dev_js'));\n\twatch('src/**/*.json', parallel('html'));\n\twatch('src/img/**/*.+(png|jpg|jpeg|gif|svg|ico)', parallel('rastr'));\n\twatch('build/img/**/*.+(png|jpg|jpeg)', parallel('webp'));\n\twatch('src/svg/css/**/*.svg', parallel('svg_css'));\n\twatch('src/svg/sprite/**/*.svg', parallel('svg_sprite'));\n\twatch('src/fonts/**/*.ttf', series('ttf', 'fonts'));\n}",
+ "inserted": true
+ }
+ },
+ {
+ "type": "paragraph",
+ "attrs": {
+ "align": null
+ }
+ }
+ ]
+ },
+ {
+ "type": "paragraph",
+ "attrs": {
+ "align": null
+ },
+ "content": [{
+ "type": "text",
+ "text": "Здесь мы использовали два варианта запуска и выполнения задач - parallel и series. Разница между ними в том, что в первом случае задачи запускаются одновременно и выполняются параллельно друг другу, а во втором - последовательно, одна за другой в том порядке, в котором мы указали. "
+ }]
+ },
+ {
+ "type": "paragraph",
+ "attrs": {
+ "align": null
+ },
+ "content": [{
+ "type": "text",
+ "text": "Вот и всё. Мы научили Gulp следить за файлами и выполнять те или иные задачи в тот момент, когда эти файлы изменились."
+ }]
+ },
+ {
+ "type": "hr",
+ "attrs": {
+ "inserted": true
+ }
+ },
+ {
+ "type": "heading",
+ "attrs": {
+ "level": 1,
+ "class": null
+ },
+ "content": [{
+ "type": "text",
+ "text": "Деплой на хостинг по FTP"
+ }]
+ },
+ {
+ "type": "paragraph",
+ "attrs": {
+ "align": null
+ },
+ "content": [{
+ "type": "text",
+ "text": "Для этого нам потребуется отдельная задача и плагин. Я использую vinyl-ftp, хотя есть аналоги, без проблем делающие то же самое. Установим его и напишем задачу. На хостинг будем грузить только директорию "
+ },
+ {
+ "type": "text",
+ "marks": [{
+ "type": "code"
+ }],
+ "text": "build/"
+ },
+ {
+ "type": "text",
+ "text": ", в которой находятся готовые, скомпилированные файлы проекта."
+ }
+ ]
+ },
+ {
+ "type": "spoiler",
+ "attrs": {
+ "title": "tasks/ftp.js"
+ },
+ "content": [{
+ "type": "code_block",
+ "attrs": {
+ "lang": "javascript",
+ "code": "const {\n\tsrc\n} = require('gulp');\nconst changed = require('gulp-changed');\nconst ftp = require('vinyl-ftp');\nconst connect = ftp.create({\n\thost: 'yourhosting.com',\n\tuser: 'username',\n\tpass: '*********',\n\tparallel: 10\n});\n\nmodule.exports = function () {\n\treturn src('build/**/*.*')\n\t\t.pipe(connect.changed('www/'))\n\t\t.pipe(connect.dest('www/'))\n}",
+ "inserted": true
+ }
+ },
+ {
+ "type": "paragraph",
+ "attrs": {
+ "align": null
+ }
+ }
+ ]
+ },
+ {
+ "type": "paragraph",
+ "attrs": {
+ "align": null
+ },
+ "content": [{
+ "type": "text",
+ "text": "Константа "
+ },
+ {
+ "type": "text",
+ "marks": [{
+ "type": "code"
+ }],
+ "text": "connect"
+ },
+ {
+ "type": "text",
+ "text": " - это функция, в которую аргументом передаётся объект с настройками хостинга - адрес сервера, логин, пароль. "
+ },
+ {
+ "type": "text",
+ "marks": [{
+ "type": "bold"
+ }],
+ "text": "Будьте аккуратны, выкладывая подобные данные на Github"
+ },
+ {
+ "type": "text",
+ "text": " или другое хранилище кода. Можете создать отдельный файл с настройками и добавить его в gitignore, чтобы случайно не забыть и не вывалить свои логин и пароль к хостингу на весь мир. не забудьте так же указать правильную директорию на ftp-сервере вместо "
+ },
+ {
+ "type": "text",
+ "marks": [{
+ "type": "code"
+ }],
+ "text": "www/"
+ },
+ {
+ "type": "text",
+ "text": ", которая указана в моих настройках."
+ }
+ ]
+ },
+ {
+ "type": "hr",
+ "attrs": {
+ "inserted": true
+ }
+ },
+ {
+ "type": "heading",
+ "attrs": {
+ "level": 1,
+ "class": null
+ },
+ "content": [{
+ "type": "text",
+ "text": "Написание GULP- и NPM-сценариев"
+ }]
+ },
+ {
+ "type": "paragraph",
+ "attrs": {
+ "align": null
+ }
+ },
+ {
+ "type": "hr",
+ "attrs": {
+ "inserted": true
+ }
+ },
+ {
+ "type": "heading",
+ "attrs": {
+ "level": 1,
+ "class": null
+ },
+ "content": [{
+ "type": "text",
+ "text": "Создание базовых шаблонов"
+ }]
+ },
+ {
+ "type": "paragraph",
+ "attrs": {
+ "align": null
+ }
+ },
+ {
+ "type": "hr",
+ "attrs": {
+ "inserted": true
+ }
+ },
+ {
+ "type": "heading",
+ "attrs": {
+ "level": 1,
+ "class": null
+ },
+ "content": [{
+ "type": "text",
+ "text": "Процесс разработки или краткое howto"
+ }]
+ },
+ {
+ "type": "paragraph",
+ "attrs": {
+ "align": null
+ }
+ },
+ {
+ "type": "hr",
+ "attrs": {
+ "inserted": true
+ }
+ },
+ {
+ "type": "paragraph",
+ "attrs": {
+ "align": null
+ }
+ }
+ ]
+ },
+ "textContent": "Как я сделал свою сборку Gulp для быстрой, лёгкой и приятной вёрстки.Серьёзно и профессионально я начал заниматься вёрсткой в 2019 году, хотя до этого ещё со школы интересовался данной темой как любитель. Поэтому новичком мне себя назвать сложно, но и профессионалом с опытом 5+ лет я тоже не являюсь. Тем не менее, я успел познакомиться со сборщиком Gulp, его плагинами и сделал для себя хорошую, как по мне, сборку для работы. О её возможностях сегодня и расскажу.ВАЖНО! В этой статье речь пойдёт о самой последней версии сборки. Если вы пользуетесь версиями сборки, вышедшими до публикации этой статьи, информация будет для вас не релевантна, но полезна.Какие задачи решает эта сборка?вёрстка компонентами (вам не нужно в каждую страницу копировать head, header, footer и другие повторяющиеся элементы, вплоть до кнопок или кастомных чекбоксов);вёрстка с препроцессорами (SASS/SCSS);конвертация шрифтов из ttf в eot, woff, woff2;лёгкое (почти автоматическое) подключение шрифтов;лёгкое (почти автоматическое) создание псевдоэлементов-иконок;обработка изображений \"на лету\";минификация html/css/js файлов;возможность вёрстки с использованием php;выгрузка файлов на хостинг по FTP;несколько мелких задач с помощью миксинов.https://github.com/budfy/Easy-webdev-startpackСобственно создание сборкиНачнём собирать нашу сборку (простите за тавтологию). Предварительно нам потребуется уже установленная на компьютере LTS-версия Node.js и NPM (входит в пакет Node.js) либо Yarn. Для нашей задачи не имеет значения, какой из этих пакетных менеджеров использовать, однако я буду объяснять на примере NPM, соответственно, для Yarn вам потребуется нагуглить аналоги NPM-команд.Первое, что нам нужно сделать - это инициализировать проект. Открываем папку проекта в командной строке (очень надеюсь, вы знаете, как это делается) и вводим команду npm init.После этого npm задаст нам неесколько стандартных вопросов по типу названия проекта, автора, версии и т.д... Отвечаем на них как душе угодно. Для нашей задачи это не имеет никакого значения.Далее будет намного удобнее работать через Visual Studio Code (поскольку у него есть встроенный терминал) или любой другой удобный вам редактор + терминал.Прежде всего, нам нужно установить сам Gulp. Делается это двумя командами npm i gulp -global - устанавливаем Gulp глобально на систему и npm i gulp --save-dev - устанавливаем Gulp локально в проект. Ключ --save здесь отвечает за сохранение версии плагина при дальнейшей установке (без него вам может установить более новую, несовместимую с другими плагинами версию), а ключ -dev указывает на то, что этот пакет необходим только во время разработки проекта, а не во время его выполнения. Например, если мы устанавливаем в проект пакет Swiper, который содержит скрипты слайдера и будет отображаться на странице, мы будем устанавливать его без ключа -dev, поскольку он нужен для выполнения, а не для разработки.После того, как Gulp установился, имеет смысл создать в корне проекта управляющий файл gulpfile.js, в котором мы и будем писать задачи для сборщика.После этого нам нужно подключить Gulp в нашем файле, для того чтобы он исполнялся. Это делается с помощью require:Далее, для каждой задачи будем использовать модули в отдельных файлах. Для того, чтобы не подключать каждый модуль отдельно, нужно установить и подключить плагин require-dir. Устанавливается он всё той же командой (как и все последующие плагины, поэтому далее повторяться не буду, просто знайте, что установить - это npm i $PLUGIN-NAME$ --save-dev). После установки подключаем его и прописываем путь к папке, в которую будем складывать модули (у меня это папка tasks):Первая задачаДавайте проверим, всё ли мы правильно сделали. Создадим в директории tasks файл модуля с именем hello.js. В созданном файле напишем простейшую функцию, которая будет выводить в консоль строку \"Hello Gulp!\" (можете придумать что-то менее банальное, если хотите).Теперь вернёмся в gulpfile.js и создадим там задачу hello:ТТеперь командой gulp hello в терминале запустим нашу задачу. Если всё сделано правильно - в терминал должно вывестись приблизительно такое сообщение:Файловая структураТеперь, когда мы создали первую функцию, можно подумать о том, какая структура будет у наших проектов. Я предпочитаю использовать директорию (папку) /src для хранения исходных файлов и директорию /build для готовых файлов проекта.components/ - директория для компонентовcomponents/bem-blocks/ - директория для БЭМ-блоковcomponents/page-blocks/ - директория для типовых блоков страницы, таких как хедер, футер и т.п.fonts/ - директория для шрифтовimg/ - директория для изображенийjs/ - директория для файлов JavaScriptscss/ - директория для файлов стилейscss/base/ - директория для базовых стилей, которые мы изменять не будемsvg/ - директория для файлов SVGsvg/css/ - директория для SVG-файлов, которые будут интегрироваться в CSS Получиться в итоге должно приблизительно следующее:ВАЖНО: в пустых директориях, таких как img/, fonts/ и т.п. перед тем как пушить в удалённый репозиторий (например на Github) нужно создать пустые файлы с именем .gitkeep. Это нужно для того, чтобы Github не удалил пустые директории из сборки.Добавление задач и настройка плагиновОсновной целью Gulp является автоматизация рутинных действий в разработке, которые мы можем запрограммировать с помощью задач и плагинов. Первую тестовую задачу мы с вами уже написали. Давайте теперь напишем настоящие задачи, которые будут нам помогать в работе.Задачи стилейДля работы с scss нам нужно будет установить некоторые плагины, которые будут обрабатывать и компилировать наш scss код в готовый css. Прежде всего, установим сам плагин Gulp-sass, который будет компилировать scss файлы в обычный css, понятный браузеру. Так же, для того, чтобы scss-файлы можно было импортировать не по одному, а целыми директориями нам понадобится gulp-sass-bulk-importer, для автоматической расстановки префиксов - gulp-autoprefixer, для очистки лишнего css - gulp-clean-css и для конкатинации (\"склеивания\" файлов вместе) - gulp-concat. Так же, для того, чтобы в DevTools было понятно, из какого файла взялись стили, установим gulp-sourcemaps. Можно каждый раз не прописывать команду npm i в терминале, а указать перечень устанавливаемых плагинов через пробел, но ключи тогда нужно будет указать перед названиями плагинов: npm i --save-dev gulp-sass gulp-sass-bulk-importer gulp-autoprefixer gulp-clean-css gulp-concat gulp-sourcemapsТеперь создадим в директории tasks/ файл модуля, в котором опишем, что нужно делать галпу с scss-файлами. От gulp нам понадобятся src и dest, а остальные плагины подключим полностью:Далее экспортируем безымянную функцию: В ней-то мы и будем обрабатывать наши scss файлы. Gulp выполняет последовательность действий .pipe над объектами, указанными в модуле src. Это похоже на конвейер, проходя по которому, код или файлы, которые мы создали в директории src/, превращаются в то, что мы хотим видеть в итоге и складываются в директорию build/. Давайте определим порядок действий:Взять файлы scss из директорий scss/Инициализировать карту исходных файлов (sourcepams)Скомпилировать в cssРасставить вендорные префиксыОчистить от лишнегоСклеить в единый файл style.cssЗаписать карту исходных файлов в получившемся файлеПоложить его в buildИтогом (return) нашей функции как раз и будет результат всей последовательности действий, которые мы определили. Это и запишем:src('src/scss/**/*.scss') - определяем источник исходного кода (source).pipe(map.init()) - инициализируем маппинг, чтобы он отслеживал включаемые файлы.pipe(bulk()) - проводим код через плагин, который ползволяет использовать директиву @include в scss для директорий, а не только для отдельных файлов.pipe(sass()) - проводим код через сам компиллятор sass.pipe(prefixer()) - проводим код через префиксер, который расставит вендорные префиксы.pipe(clean()) - проводим код через \"очиститель\" от лишнего css.pipe(concat('style.min.css')) - склеиваем все исходные файлы в один.pipe(map.write('../sourcemaps/')) - записываем \"карту\" источников полученного файла.pipe(dest('build/css/')) - кладём итоговый файл в директориюЕдинственное, чего нам здесь не хватает - настроек и методов для некоторых плагинов. Настройки для плагинов задаются в виде объектов, которые передаются аргументом в функцию плагина. Звучит страшно, а на деле выглядит примерно так: .pipe(sass({outputStyle: 'compressed'}). Для sass я определяю степень сжатия выходного css как compressed, а так же добавляю на событие error логирование ошибки, чтобы было понятно, что не так (если вдруг). И того получаем такой пайп: Для автопрефиксера возьму рекомендованые в документации параметры. Для того, чтобы задать свои значения настроек - переходите к документации каждого плагина и читайте, за что отвечает та или иная опция.С клинером вообще всё просто: выставляю уровень очистки (level) в значение 2 и готово.Что такое bs и для чего он нужен я опишу ниже, в соответствующем разделе.Приблизитекльно аналогичным образом поступаем со стилями библиотек и плагинов, которые будем подключать к будущим проектам. Создаём константу plugins, в которой у нас будет массив файлов-источников из node_modules const plugins = []; . Путь к файлам стилей будем писать в кавычках через запятую - получится массив строк с путями к файлам плагинов. Подключаем в файл gulp.src и gulp.dest, плагины gulp-concat и gulp-sourcemaps аналогично предыдущей задаче и прописываем наш \"конвейер\":Взять файлы из источников (в данном случае - из константы)Инициализировать маппингПрогнать всё через sassПодчистить неиспользуемый css (в библиотеках это особенно важно)Сконкатенировать файлы в одинЗаписать маппингСложить в директорию build/cssНо тут есть одна особенность - добавим условие, если длина массива plugins будет равна нулю - эта функция просто вернёт true, без выполнения других действий. Без этого, наша функция будет ругаться на отсутствие плагинов. А они нужны далеко не всегда.Задачи для JavaScriptC JavaScript всё будет немного сложнее. Для разработки нам понадобится чистый, не редактированный и не минимизированный код. При этом, в готовом проекте мы будем прогонять код через babel, который не только приводит код к стандарту ES5, но и скоращает его, путём замены имён переменных и функций на более короткие (одно-двух символьные). Поэтому, нам потребуется три разные задачи: для нашего JS-кода, для плагинов/библиотек и для билда.Для обработки JS нам понадобится gulp-uglify-es - для минификации JS-кода и gulp-babel для оптимизации. С остальными плагинами, которыми мы будем обрабатывать наш код вы уже знакомы.Для начала, опишем процесс работы с кодом:Определить источники (sources)Инициализировать маппингСклеить в один файлМинифицировать полученны файлЗаписать источники в файлПоложить итоговый файл в build/js/Для итоговой (билдовой) версии после 4 пункта мы ещё прогоним его через babel для оптимизации. И для этого нам понадобится установить @babel/core , а так же @babel/preset-env. После установки, в корне проекта нужно будет создать файл .babelrc , который указывает на пресет настроек Babel со следующим содержимым:Теперь можем приступать к написанию файлов задач. Они будут максимально похожи друг на друга, поэтому нет смысла описывать каждый отдельно. Разница будет лишь в задаче build, какк я уже писал выше.Как видим, тут есть Указанный напрямую файл src/js/01_main.js который должен присутствовать для выполнения задач, поэтому создадим его в директории src/js. Так же стоит отметить ещё пару мелких особенностей. Во-первых, как видите, подключение плагина gulp-uglify-es я сделал сразу с параметром (свойством) default: const uglify = require('gulp-uglify-es').default - указание какого-либо параметра обязательно для успешной работы плагина. Я использую стандартную настройку. Вы-же можете порыться в документации к плагину и использовать другие настройки, если хотите. Во-вторых, в build-задаче я не использовал sourcemaps, поскольку они там и не нужны. Эта карта необходима при разработке, чтобы видеть источники кода в итоговом файле.Задачи для HTML/PHPСледующим этапом будет обработка HTML или PHP, в зависимости от того, на чём вы пишете. Мы не будем рассматривать препроцессоры, такие как Pug или Nunjucks по той простой причине, что с появлением Emmet они перестали значительно ускорять процесс разработки на HTML, а шаблоны мы вполне можем строить другим плагином: gulp-file-include, который умеет, как мне кажется, нечто большее - включать текстовое представление любых файлов в любые файлы. Далее, вы поймёте, почему я считаю это важным. Для разработки на PHP это вообще не имеет никакого значения. Там можно использовать require и другие возможности PHP из коробки. Если же вы привыкли пользоваться препроцессорами - вы легко сможете настроить Gulp для их обработки, я считаю.Для минификации HTML можно использовать gulp-htmlmin, добавив его в виде .pipe(htmlmin({ collapseWhitespace: true })) в свою задачу html. Однако, как показывает практика, вёрстка - не конечный этап разработки в 90% случаев, поэтому добавлять его в сборку я не буду. При необходимости, сможете установить и добавить в задачу по аналогией с другими плагинами.Устанавливаем gulp-file-include и пишем задачу для html:Здесь вы увидели немного новую конструкцию. В src аргумент стал массивом, в котором один из элементов обозначени восклицательным знаком(!) в начале. Это на языке JavaScript буквально означает \"не\". То есть мы берём все файлы html 'src/**/*.html', но только не те, которые '!src/**/_*.html' начинаются с подчёркивания. В дальнейшем это позволит нам создавать файлы модулей, которые не должны попасть в build директорию, а служат, так сказать, служебными шаблонами. Об этих шаблонах мы поговорим ниже.Создадим аналогичную задачу для php. Абсолютно такую же, один в один., кроме исключений. Они нам в этой задаче не потребуются.Задачи для изображенийЗдесь всё будет несколько сложнее. Нам потребуется как минимум две задачи для svg, две для растровых изображений. Что нам нужно:Сжимать растровые изображения.Конвертировать растровые изображения в webp.Объединять svg в спрайт.Включать svg в виде класа с фоном в CSS b cоздавать для svg класс с псевдоэлементами ::after и ::before Это всё мы будем делать разными задачами и тут нам понадобится просто тонна разных плагинов! Их так много, что я решил оформить их названия в отдельный список и убрать под спойлер.gulp-changed - понадобится нам для отслеживания изменения в файле. Если файл не изменился, дальнейшие действия с ним не производятся.gulp-multi-dest - понадобится нам для складывания результатов обработки в несколько директорий.gulp-imagemin - сжимает изображенияimagemin-jpeg-recompress - тоже сжимает изображенияimagemin-pngquant - и этот тоже сжимает изображенияgulp-webp - конвертирует растровые форматы (png, jpeg) в webpgulp-svgmin - сжимает svggulp-svg-css-pseudo - добавляет svg фоном в css и сразу же создаёт псевдоэлементыgulp-svg-sprite - склеивает все svg в один спрайт. Лично я им пользуюсь крайне редко, но это ввиду особенностей проектов. Вообще весьма полезен для снижения запросов к серверу.Теперь создадим под всё это файлы задач: tasks/rastr.js, tasks/webp.js, tasks/svg_css.js и tasks/svg_sprite.js. Самая сложная задача будет для растровых изображений, ввиду того, что там много настроек, для объяснения значаний которых нужна отдельная статья. Здесь я детали всех настроек описывать не буду. Just belive me, что я долго сидел подбирал эти настройки с дизайнером так, чтобы качество графики сильно снижало трафик и не сильно резало глаз. В итоге у нас получился вот такой монстр:В этой задаче мы применили фильтр по расширениям файлов таким образом, чтобы обрабатывались только конкретные расширения: src('src/img/**/*.+(png|jpg|jpeg|gif|svg|ico)'). В данном случае прямой слеш (знак |) означает буквально \"или\". Таким образом, при обработке эта функция выберет файлы с данными расширениями, а все остальные просто проигнорирует. Так же с помощью gulp-changed мы запретим обработку старых изображений - это ускорит выполнение задачи. Для того же, чтобы выполнение задачи не вызывало ошибку, если входящих файлов для конвертации нет - используем gulp-plumber.Теперь создадим webp-дубликаты этих изображений. Эти дубликаты мы будем делать одновременно и в директорию src, и в build, банально для того, чтобы path-intellisense подсказывал нам пути к ним.Следующий этап - обработка SVG. Здесь у нас будет две задачи: добавление svg в качестве отдельного класса фоном прямо в css и создание svg-спрайта. У этих задач, хоть и похожее, но всё-же разное назначение. Я сейчас не буду вдаваться в подробности, поскольку они не предмет данной статьи. Если вам интересно, зачем нужны svg-спрайты и как этим пользоваться, то об этом писали тут и тут. Давайте перейдём к установке нужных плагинов и написанию задач.Прежде всего, нам нужно очистить наш svg от всего лишнего. Для этого я буду использовать gulp-svgmin.Для добавления svg в качестве background-image я буду использовать плагин gulp-svg-css-pseudo. Точнее это форк плагина gulp-svg-css, который я сделал после того, как авторы оригинального плагина не добавили мой пропоуз в свой плагин. Разница между ними заключается в том, что он создаёт для каждого класса псевдоэлементы ::before и ::after. Вы можете точно так же использовать оригинальный плагин, но он не будет работать с псевдоэлементами. На входе мы в определённую папку кладём файл, например myicon.svg, а на выходе получаем классы --svg__myicon, --svg__myicon-before и --svg__myicon-after, у которых background-image уже задан в виде нашей картинки. Это очень просто и удобно, если картинку не нужно менять (анимировать или изменять цвет):И так, мы будем брать файлы из директории src/svg/css и обрабатывать нашими плагинами, получая на выходе файл svg.scss и положим его в директорию src/scss/base. Полный файл этой задачи будет выглядеть так:Теперь напишем задачу для svg-спрайта. Смысл спрайтов в том, что он объединяет svg в один файл, который содержит набор векторных картинок. К этим картинкам можно обращаться по ID и вставлять их в нужное место с возможностью изменять их (например цвет) средствами css. Установим плагин gulp-svg-sprite и создадим под него вот такую задачу:На выходе мы получим файл src/img/sprite.svg внутри которого и будут все наши svg. Обратиться к ним в html можно будет так: или так:Если же иконка находилась во вложенной директории (например, как в нашем случае, в директории css), то перед именем файла нужно поставить имя этой директории и два дефиса. Вот так: Чуть позже покажу ещё и третий способ вставлять svg в html с помощью сборки. Вы можете использовать любой из этих способов или их комбинировать, в зависимости от задач, которые нужно решить.Задачи для шрифтовЗдесь у нас будет две задачи. Первая направлена на конвертацию шрифтов из формата ttf в форматы woff и woff2. Во всех остальных форматах в 2021 году не вижу никакого смысла, поскольку поддержка формата woff простирается аж до Internet Explorer 9. Давно ли вы видели компьютеры с IE9 на борту? Конвертиролвать будем с помощью двух плагинов: gulp-ttftowoff2 и gulp-ttftowoff, соответственно. Файл задачи для конвертации шрифтов будет выглядеть так:После того, как шрифты сконвертированы, нам нужно их подключить. Я так и не смог добиться полной автоматизации подключения шрифтов, объединения их по жирности и стилю начертания, в зависимости от имени файла и присвоения нескольких вариантов локального имени. Тем не менее, процесс подключения тоже можно немного автоматизировать. Для этого, во-первых, создадим в директороии src/scss/base файл _mixins.scss. В дальнейшем, он нам ещё понадобится и для других миксинов. Напишем в этом файле следующий миксин:В этот миксин нам нужно будет передать такие параметры: $name - имя шрифтового семейства;$file - имя файла;$weight - жирность шрифта (по-умолчанию установлено значение 400, но если мы передадим параметр, то значение по-умолчанию будет проигнорировано)$style - стиль начертания (тоже установлен по-умолчанию normal)На выходе мы получим уже подключенный файл шрифта, но с ним потребуются некоторые манипуляции вручную, которые я опишу чуть ниже. Далее нам нужно написать задачу, которая будет подключать наши шрифты. По сути эта задача будет циклом проходиться по файлам, брать имя каждого файла, отсекать расширение и на основе имени генерировать код для его подключения через миксин. Для этого нам потребуется модуль nodeJS для работы с файлами, который называется fs. Он уже установлен вместе с nodejs, поэтому устанавливать его нет необходимости - нужно только подключить: const fs = require('fs');Далее создадим две переменные. В первую запишем, путь к файлу, который получим на выходе, а во вторую - путь к папке шрифтов, которые нам создала предыдущая задача.Дальнейший код этой функции я честно скопировал у Максима Васяновича ( ), с его урока про Gulp и немного переписал. В итоге, у нас получится вот такая функция:Эта функция на выходе создаст нам файл src/scss/_local-fonts.scss, в котором под каждый файл шрифтов будет содержаться вызов миксина для его подключения. Так же, при выполнении функции в консоль будет выдаваться следующее сообщение:Как видите, в сообщении написано следующее: \"Добавлен новый шрифт: названиешрифта. Пожалуйста, переместите вызов миксина из src/scss/_local-fonts.scss в src/scss/_fonts.scss после чего измените его\". Давайте разберёмся, что куда нужно переместить и как изменить. Но прежде создадим тот самый файл src/scss/_fonts.scss - он будет содержать уже реальные вызовы миксина для подключения шрифтов, которыми мы и будем пользоваться.Когда мы добавляем шрифты, чаще всего, у нас есть отдельные файлы для разной жирности шрифта и для разных стилей начертания (наклонные и прямые, тонкие и жирные и т.п.). Каждый такой файл шрифта создаст отдельный вызов миксина и на выходе мы получим файл src/scss/_local-fonts.scss примерно такого содержания:Как видим, мы подключаем шрифты отдельными названиями. Это неправильно с точки зрения читабельности и удобства использования шрифта, поскольку вместо того, чтобы прописывать font-weight: 700, нам каждый раз придётся указывать font-family: \"ArialBold\". Это плохая практика, поэтому нам нужно переписать эти вызовы. Как помним из миксина, который мы написали, первым параметром он принимает имя шрифтового семейства. В данном случае это \"Arial\". Второй параметр мы не меняем - это имя файла без расширения (расширения подставит миксин). Третий параметр отвечает за жирность. Изменим во второй строке 400 на 700. Четвёртый параметр, который здесь не указан, отвечает за стиль начертания. В третей строке миксин вызывается для наклонного шрифта, поэтому четвёртым параметром нужно это указать. В итоге получим:Теперь это всё нужно переместить в файл src/scss/_fonts.scss, который, естественно, необходимо создать. Перемещать нужно в новый файл по той причине, что при следующем запуске эта функция перезапишет файл src/scss/_local-fonts.scss и все наши изменения затрутся.Автоматическое обновление и синхронизация браузеровНам нужно настроить Gulp таким образом, чтобы он при запуске открывал наш проект в браузере и автоматически обновлял его при каких-либо изменениях. Я для этого использую browser-sync. Этот плагин создаёт сервер средствами nodejs и позволяет подключать к нему различные браузеры. Для него у нас будет две задачи - для html-разработки и для php соответственно. Установим этот плагин и создадим два соответствующих файла задач. В первом файле мы укажем настройки для разработки html. Параметр host отвечает за IP-адрес компьютера, с которого сайт будет раздаваться на другие устройства в сети. Важно, чтобы там был указан именно IP-адрес вашего компьютера в локальной сети, если хотите иметь доступ к разрабатываемому сайту со всех устройств. Коллбэк-функция отвечает за открытие страницы 404, если она есть и если не найдена запрашиваемая (по умолчанию это страница index.html). Все остальные параметры вы вольны настраивать как вашей душе угодно, согласно документации.Для php мы напишем гораздо более простую задачу. Она будет только обновлять браузер в случае изменений, а локальный сервер Вы будете запускать с помощью сторонней программы: openserver, wamp или любой другой.Теперь нам нужно подключить в ранее написанные задачи browser-sync и pipe для обновления браузера в конце выполнения задачи. Как подключить - вы уже знаете: так же, как и др угие плагины. А добавлять pipe мы будем следуюший: .pipe(bs.stream()).Перезагружать страницу нам придётся при изменении html, scss и js, php, а так же растровых картинок. Соответственно, в эти задачи я уже добавил нужный pipe.Лично мне нравится, когда в консоли показывается исходный размер изображений до сжатия и итоговый - после сжатия, поэтому в сборку я добавил плагин gulp-size, но поскольку я почти не нашёл единомшленников в данном вопросе \"засорения терминала лишней инфой\", я не описывал его здесь. При желании вы можете добавить соответствующий pipe к нужным задачам или убрать его, если вы клонировали себе готовую сборку и он вас раздражает мельканием в консоли.Далее нам следует заняться функциями вотчинга. Вотчинг - это состояние, когда вся наша программа запущена, следит за файлами и их изменением и выполняет те или иные функции, если эти файлы изменились. И так, за чем будем следить: html, php, scss, js, json (ниже объясню, для чего), изображения в src, изображения в build (помните, что у нас webp конвертируются из уже сжатых изображений?), svg, шрифты. Давайте напишем соответствующую задачу:Здесь мы использовали два варианта запуска и выполнения задач - parallel и series. Разница между ними в том, что в первом случае задачи запускаются одновременно и выполняются параллельно друг другу, а во втором - последовательно, одна за другой в том порядке, в котором мы указали. Вот и всё. Мы научили Gulp следить за файлами и выполнять те или иные задачи в тот момент, когда эти файлы изменились.Деплой на хостинг по FTPДля этого нам потребуется отдельная задача и плагин. Я использую vinyl-ftp, хотя есть аналоги, без проблем делающие то же самое. Установим его и напишем задачу. На хостинг будем грузить только директорию build/, в которой находятся готовые, скомпилированные файлы проекта.Константа connect - это функция, в которую аргументом передаётся объект с настройками хостинга - адрес сервера, логин, пароль. Будьте аккуратны, выкладывая подобные данные на Github или другое хранилище кода. Можете создать отдельный файл с настройками и добавить его в gitignore, чтобы случайно не забыть и не вывалить свои логин и пароль к хостингу на весь мир. не забудьте так же указать правильную директорию на ftp-сервере вместо www/, которая указана в моих настройках.Написание GULP- и NPM-сценариевСоздание базовых шаблоновПроцесс разработки или краткое howto"
+ },
+ "title": "Как я сделал свою сборку Gulp для быстрой, лёгкой и приятной вёрстки."
+ },
+ "formData": {
+ "id": null,
+ "lang": "ru",
+ "type": "simple",
+ "title": null,
+ "status": null,
+ "plannedDateTime": null,
+ "isPlanned": false,
+ "socialCover": {
+ "url": "https://habrastorage.org/getpro/habr/upload_files/dfc/e83/230/dfce83230ad4a258e13c235d0e89790c.jpg",
+ "positionX": 0,
+ "positionY": 38.18181818181818,
+ "fit": "cover"
+ },
+ "isModerated": null,
+ "format": null,
+ "hubs": [],
+ "tags": [],
+ "text": null,
+ "preview": {
+ "json": {
+ "type": "doc",
+ "content": [{
+ "type": "paragraph",
+ "attrs": {
+ "align": null
+ },
+ "content": [{
+ "type": "text",
+ "text": "Серьёзно и профессионально я начал заниматься вёрсткой в 2019 году, хотя до этого интересовался данной темой как любитель. Поэтому новичком мне себя назвать сложно, но и профессионалом с опытом 5+ лет я тоже не являюсь.Тем не менее, я успел познакомиться со сборщиком Gulp, его плагинами и сделал для себя хорошую, как по мне, сборку для работы. О её возможностях сегодня и расскажу."
+ }]
+ }]
+ },
+ "textContent": "Серьёзно и профессионально я начал заниматься вёрсткой в 2019 году, хотя до этого интересовался данной темой как любитель. Поэтому новичком мне себя назвать сложно, но и профессионалом с опытом 5+ лет я тоже не являюсь.Тем не менее, я успел познакомиться со сборщиком Gulp, его плагинами и сделал для себя хорошую, как по мне, сборку для работы. О её возможностях сегодня и расскажу."
+ },
+ "leadButtonText": "Читать далее",
+ "isTranslation": false,
+ "translationSource": null,
+ "originalAuthor": null,
+ "polls": [],
+ "isTutorial": true
+ },
+ "date": "2021-05-22T23:13:06.194Z"
+}
\ No newline at end of file
diff --git a/src/svg/sprite/.gitkeep b/src/components/page-blocks/.gitkeep
similarity index 100%
rename from src/svg/sprite/.gitkeep
rename to src/components/page-blocks/.gitkeep
diff --git a/src/components/page/_1_head.html b/src/components/page/_1_head.html
deleted file mode 100644
index bd208e85..00000000
--- a/src/components/page/_1_head.html
+++ /dev/null
@@ -1,17 +0,0 @@
-
-
-
-
-
-
-
-
- @@title
-
\ No newline at end of file
diff --git a/src/components/page/_2_header.html b/src/components/page/_2_header.html
deleted file mode 100644
index 530a5a97..00000000
--- a/src/components/page/_2_header.html
+++ /dev/null
@@ -1 +0,0 @@
-
\ No newline at end of file
diff --git a/src/components/page/_3_footer.html b/src/components/page/_3_footer.html
deleted file mode 100644
index 2081a98b..00000000
--- a/src/components/page/_3_footer.html
+++ /dev/null
@@ -1 +0,0 @@
-
\ No newline at end of file
diff --git a/src/components/page/_4_scripts.html b/src/components/page/_4_scripts.html
deleted file mode 100644
index 40194529..00000000
--- a/src/components/page/_4_scripts.html
+++ /dev/null
@@ -1,2 +0,0 @@
-
-
\ No newline at end of file
diff --git a/src/index.html b/src/index.html
index a8e417d2..e69de29b 100644
--- a/src/index.html
+++ b/src/index.html
@@ -1,21 +0,0 @@
-
-
-
-@@include('components/page/_1_head.html',{
-'title': 'Easy webdev startpack - test',
-'description': 'Easy webdev startpack by budfy',
-'keywords': 'Webdev, Gulp4, html, css, js, jquery',
-'favicon': 'img/favicon.png'
-})
-
-
- @@include('components/page/_2_header.html')
-
-
-
- @@include('components/page/_3_footer.html')
- @@include('components/page/_4_scripts.html')
-
-
-
\ No newline at end of file
diff --git a/build/index.php b/src/js/.gitkeep
similarity index 100%
rename from build/index.php
rename to src/js/.gitkeep
diff --git a/src/index.php b/src/js/01_main.js
similarity index 100%
rename from src/index.php
rename to src/js/01_main.js
diff --git a/src/js/01__main.js b/src/scss/_fonts.scss
similarity index 100%
rename from src/js/01__main.js
rename to src/scss/_fonts.scss
diff --git a/src/scss/_media.scss b/src/scss/_media.scss
deleted file mode 100644
index cc02cb6f..00000000
--- a/src/scss/_media.scss
+++ /dev/null
@@ -1,49 +0,0 @@
-//MEDIA: 1679
-@media screen and (max-width: 1679px) {}
-
-//!MEDIA: 1679
-
-//MEDIA: 1439
-@media screen and (max-width: 1439px) {}
-
-//!MEDIA: 1439
-
-//MEDIA: 1365
-@media screen and (max-width: 1365px) {}
-
-//!MEDIA: 1365
-
-//MEDIA: 1359
-@media screen and (max-width: 1359px) {}
-
-//!MEDIA: 1359
-
-//MEDIA: 1199
-@media screen and (max-width: 1199px) {}
-
-//!MEDIA: 1199
-
-//MEDIA: 1023
-@media screen and (max-width: 1023px) {}
-
-//!MEDIA: 1023
-
-//MEDIA: 991
-@media screen and (max-width: 991px) {}
-
-//!MEDIA: 991
-
-//MEDIA: 767
-@media screen and (max-width: 767px) {}
-
-//!MEDIA: 767
-
-//MEDIA: 639
-@media screen and (max-width: 639px) {}
-
-//!MEDIA: 639
-
-//MEDIA: 369
-@media screen and (max-width: 369px) {}
-
-//!MEDIA: 369
\ No newline at end of file
diff --git a/src/scss/global/_02-fonts.scss b/src/scss/base/.gitkeep
similarity index 100%
rename from src/scss/global/_02-fonts.scss
rename to src/scss/base/.gitkeep
diff --git a/src/scss/base/_mixins.scss b/src/scss/base/_mixins.scss
new file mode 100644
index 00000000..e4f3b4fc
--- /dev/null
+++ b/src/scss/base/_mixins.scss
@@ -0,0 +1,11 @@
+@mixin font-face($name, $file, $weight: 400, $style: normal) {
+ @font-face {
+ font-family: "#{$name}";
+ src: local("#{$file}"),
+ url('../fonts/#{$file}.woff2') format('woff2'),
+ url('../fonts/#{$file}.woff') format('woff');
+ font-weight: $weight;
+ font-style: $style;
+ font-display: swap;
+ }
+}
\ No newline at end of file
diff --git a/src/scss/global/_01-mixins.scss b/src/scss/global/_01-mixins.scss
deleted file mode 100644
index c760c1e2..00000000
--- a/src/scss/global/_01-mixins.scss
+++ /dev/null
@@ -1,65 +0,0 @@
-@mixin bg ($size) {
- background-size: #{$size};
- background-position: center center;
- background-repeat: no-repeat;
-}
-
-@mixin centering ($dir) {
- position: absolute;
-
- @if $dir==v {
- top: 50%;
- transform: translateY(-50%);
- }
-
- @else if $dir==h {
- left: 50%;
- transform: translateX(-50%);
- }
-
- @else {
- top: 50%;
- left: 50%;
- transform: translate(-50%, -50%);
- }
-}
-
-@mixin btn_anim($scaleMax:1.05, $scaleMin:0.95) {
- transform-origin: center center;
- transition: all ease-out 240ms;
-
- &:hover {
- transform: scale($scaleMax);
- }
-
- &:focus {
- transform: scale($scaleMax) trahslateY(-5%);
- }
-
- &:active {
- transform: scale($scaleMin);
- }
-}
-
-@mixin font-face($name, $file, $weight: 400, $style: normal) {
- @font-face {
- font-family: "#{$name}";
- src: url('../fonts/#{$file}.eot');
- src: url('../fonts/#{$file}.eot?#iefix') format('embedded-opentype'),
- url('../fonts/#{$file}.woff2') format('woff2'),
- url('../fonts/#{$file}.woff') format('woff');
- font-weight: $weight;
- font-style: $style;
- font-display: swap;
- }
-}
-
-@mixin no-btn {
- padding: 0;
- margin: 0;
- border: 0;
- background-color: transparent;
- border-radius: 0;
- cursor: pointer;
- -webkit-appearance: none;
-}
diff --git a/src/scss/global/_03-vars.scss b/src/scss/global/_03-vars.scss
deleted file mode 100644
index 74a1aa7d..00000000
--- a/src/scss/global/_03-vars.scss
+++ /dev/null
@@ -1 +0,0 @@
-:root {}
\ No newline at end of file
diff --git a/src/scss/global/_04-global.scss b/src/scss/global/_04-global.scss
deleted file mode 100644
index 6598a6e1..00000000
--- a/src/scss/global/_04-global.scss
+++ /dev/null
@@ -1,70 +0,0 @@
-html {
- box-sizing: border-box;
- font-size: 16px;
-}
-
-*,
-*::before,
-*::after {
- box-sizing: inherit;
-}
-
-html,
-body {
- height: 100%;
- position: relative;
-}
-
-body {}
-
-ul,
-ol,
-li,
-p,
-h1,
-h2,
-h3,
-h4,
-h5,
-h6 {
- margin: 0;
-}
-
-button:focus,
-input:focus {
- outline: transparent;
-}
-
-button {
- @include no-btn;
-}
-
-.flex {
- display: flex;
- align-items: flex-start;
- justify-content: flex-start;
-}
-
-.--just-space {
- justify-content: space-between;
-}
-
-.--just-center {
- justify-content: center;
-}
-
-.--just-end {
- justify-content: flex-end;
-}
-
-.--align-str {
- align-items: stretch;
-}
-
-.--align-center {
- align-items: center;
-}
-
-.--align-end {
- align-items: flex-end;
-}
\ No newline at end of file
diff --git a/src/scss/global/_05-svg.scss b/src/scss/global/svg.scss
similarity index 100%
rename from src/scss/global/_05-svg.scss
rename to src/scss/global/svg.scss
diff --git a/src/scss/libs.scss b/src/scss/libs.scss
deleted file mode 100644
index 192eb9ce..00000000
--- a/src/scss/libs.scss
+++ /dev/null
@@ -1,349 +0,0 @@
-/*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */
-
-/* Document
- ========================================================================== */
-
-/**
- * 1. Correct the line height in all browsers.
- * 2. Prevent adjustments of font size after orientation changes in iOS.
- */
-
-html {
- line-height: 1.15; /* 1 */
- -webkit-text-size-adjust: 100%; /* 2 */
-}
-
-/* Sections
- ========================================================================== */
-
-/**
- * Remove the margin in all browsers.
- */
-
-body {
- margin: 0;
-}
-
-/**
- * Render the `main` element consistently in IE.
- */
-
-main {
- display: block;
-}
-
-/**
- * Correct the font size and margin on `h1` elements within `section` and
- * `article` contexts in Chrome, Firefox, and Safari.
- */
-
-h1 {
- font-size: 2em;
- margin: 0.67em 0;
-}
-
-/* Grouping content
- ========================================================================== */
-
-/**
- * 1. Add the correct box sizing in Firefox.
- * 2. Show the overflow in Edge and IE.
- */
-
-hr {
- box-sizing: content-box; /* 1 */
- height: 0; /* 1 */
- overflow: visible; /* 2 */
-}
-
-/**
- * 1. Correct the inheritance and scaling of font size in all browsers.
- * 2. Correct the odd `em` font sizing in all browsers.
- */
-
-pre {
- font-family: monospace, monospace; /* 1 */
- font-size: 1em; /* 2 */
-}
-
-/* Text-level semantics
- ========================================================================== */
-
-/**
- * Remove the gray background on active links in IE 10.
- */
-
-a {
- background-color: transparent;
-}
-
-/**
- * 1. Remove the bottom border in Chrome 57-
- * 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari.
- */
-
-abbr[title] {
- border-bottom: none; /* 1 */
- text-decoration: underline; /* 2 */
- text-decoration: underline dotted; /* 2 */
-}
-
-/**
- * Add the correct font weight in Chrome, Edge, and Safari.
- */
-
-b,
-strong {
- font-weight: bolder;
-}
-
-/**
- * 1. Correct the inheritance and scaling of font size in all browsers.
- * 2. Correct the odd `em` font sizing in all browsers.
- */
-
-code,
-kbd,
-samp {
- font-family: monospace, monospace; /* 1 */
- font-size: 1em; /* 2 */
-}
-
-/**
- * Add the correct font size in all browsers.
- */
-
-small {
- font-size: 80%;
-}
-
-/**
- * Prevent `sub` and `sup` elements from affecting the line height in
- * all browsers.
- */
-
-sub,
-sup {
- font-size: 75%;
- line-height: 0;
- position: relative;
- vertical-align: baseline;
-}
-
-sub {
- bottom: -0.25em;
-}
-
-sup {
- top: -0.5em;
-}
-
-/* Embedded content
- ========================================================================== */
-
-/**
- * Remove the border on images inside links in IE 10.
- */
-
-img {
- border-style: none;
-}
-
-/* Forms
- ========================================================================== */
-
-/**
- * 1. Change the font styles in all browsers.
- * 2. Remove the margin in Firefox and Safari.
- */
-
-button,
-input,
-optgroup,
-select,
-textarea {
- font-family: inherit; /* 1 */
- font-size: 100%; /* 1 */
- line-height: 1.15; /* 1 */
- margin: 0; /* 2 */
-}
-
-/**
- * Show the overflow in IE.
- * 1. Show the overflow in Edge.
- */
-
-button,
-input { /* 1 */
- overflow: visible;
-}
-
-/**
- * Remove the inheritance of text transform in Edge, Firefox, and IE.
- * 1. Remove the inheritance of text transform in Firefox.
- */
-
-button,
-select { /* 1 */
- text-transform: none;
-}
-
-/**
- * Correct the inability to style clickable types in iOS and Safari.
- */
-
-button,
-[type="button"],
-[type="reset"],
-[type="submit"] {
- -webkit-appearance: button;
-}
-
-/**
- * Remove the inner border and padding in Firefox.
- */
-
-button::-moz-focus-inner,
-[type="button"]::-moz-focus-inner,
-[type="reset"]::-moz-focus-inner,
-[type="submit"]::-moz-focus-inner {
- border-style: none;
- padding: 0;
-}
-
-/**
- * Restore the focus styles unset by the previous rule.
- */
-
-button:-moz-focusring,
-[type="button"]:-moz-focusring,
-[type="reset"]:-moz-focusring,
-[type="submit"]:-moz-focusring {
- outline: 1px dotted ButtonText;
-}
-
-/**
- * Correct the padding in Firefox.
- */
-
-fieldset {
- padding: 0.35em 0.75em 0.625em;
-}
-
-/**
- * 1. Correct the text wrapping in Edge and IE.
- * 2. Correct the color inheritance from `fieldset` elements in IE.
- * 3. Remove the padding so developers are not caught out when they zero out
- * `fieldset` elements in all browsers.
- */
-
-legend {
- box-sizing: border-box; /* 1 */
- color: inherit; /* 2 */
- display: table; /* 1 */
- max-width: 100%; /* 1 */
- padding: 0; /* 3 */
- white-space: normal; /* 1 */
-}
-
-/**
- * Add the correct vertical alignment in Chrome, Firefox, and Opera.
- */
-
-progress {
- vertical-align: baseline;
-}
-
-/**
- * Remove the default vertical scrollbar in IE 10+.
- */
-
-textarea {
- overflow: auto;
-}
-
-/**
- * 1. Add the correct box sizing in IE 10.
- * 2. Remove the padding in IE 10.
- */
-
-[type="checkbox"],
-[type="radio"] {
- box-sizing: border-box; /* 1 */
- padding: 0; /* 2 */
-}
-
-/**
- * Correct the cursor style of increment and decrement buttons in Chrome.
- */
-
-[type="number"]::-webkit-inner-spin-button,
-[type="number"]::-webkit-outer-spin-button {
- height: auto;
-}
-
-/**
- * 1. Correct the odd appearance in Chrome and Safari.
- * 2. Correct the outline style in Safari.
- */
-
-[type="search"] {
- -webkit-appearance: textfield; /* 1 */
- outline-offset: -2px; /* 2 */
-}
-
-/**
- * Remove the inner padding in Chrome and Safari on macOS.
- */
-
-[type="search"]::-webkit-search-decoration {
- -webkit-appearance: none;
-}
-
-/**
- * 1. Correct the inability to style clickable types in iOS and Safari.
- * 2. Change font properties to `inherit` in Safari.
- */
-
-::-webkit-file-upload-button {
- -webkit-appearance: button; /* 1 */
- font: inherit; /* 2 */
-}
-
-/* Interactive
- ========================================================================== */
-
-/*
- * Add the correct display in Edge, IE 10+, and Firefox.
- */
-
-details {
- display: block;
-}
-
-/*
- * Add the correct display in all browsers.
- */
-
-summary {
- display: list-item;
-}
-
-/* Misc
- ========================================================================== */
-
-/**
- * Add the correct display in IE 10+.
- */
-
-template {
- display: none;
-}
-
-/**
- * Add the correct display in IE 10.
- */
-
-[hidden] {
- display: none;
-}
diff --git a/src/scss/style.scss b/src/scss/style.scss
index 8a47f1ac..e69de29b 100644
--- a/src/scss/style.scss
+++ b/src/scss/style.scss
@@ -1,6 +0,0 @@
-@import 'global/*.scss';
-@import '../components/bem-blocks/**/*.scss';
-
-/* NOTE - import here your style modules */
-
-@import 'media';
\ No newline at end of file
diff --git a/src/svg/include/storm.svg b/src/svg/include/storm.svg
deleted file mode 100644
index 707c9878..00000000
--- a/src/svg/include/storm.svg
+++ /dev/null
@@ -1,3 +0,0 @@
-
diff --git a/tasks/bs_html.js b/tasks/bs_html.js
new file mode 100644
index 00000000..26ccee39
--- /dev/null
+++ b/tasks/bs_html.js
@@ -0,0 +1,26 @@
+const bs = require('browser-sync');
+
+module.exports = function bs_html() {
+ bs.init({
+ server: {
+ baseDir: 'build/',
+ host: '192.168.0.104',
+ },
+ callbacks: {
+ ready: function (err, bs) {
+ bs.addMiddleware("*", function (req, res) {
+ res.writeHead(302, {
+ location: "404.html"
+ });
+ res.end("Redirecting!");
+ });
+ }
+ },
+ browser: 'chrome',
+ logPrefix: 'BS-HTML:',
+ logLevel: 'info',
+ logConnections: true,
+ logFileChanges: true,
+ open: true
+ })
+}
\ No newline at end of file
diff --git a/tasks/bs_php.js b/tasks/bs_php.js
new file mode 100644
index 00000000..439c5687
--- /dev/null
+++ b/tasks/bs_php.js
@@ -0,0 +1,13 @@
+ const bs = require('browser-sync');
+
+ module.exports = function bs_php() {
+ bs.init({
+ browser: ['chrome'],
+ watch: true,
+ proxy: '',
+ logLevel: 'info',
+ logPrefix: 'BS-PHP:',
+ logConnections: true,
+ logFileChanges: true,
+ })
+ }
\ No newline at end of file
diff --git a/tasks/build_js.js b/tasks/build_js.js
new file mode 100644
index 00000000..5fd0af38
--- /dev/null
+++ b/tasks/build_js.js
@@ -0,0 +1,24 @@
+const {
+ src,
+ dest
+} = require('gulp');
+const uglify = require('gulp-uglify-es').default;
+const babel = require('gulp-babel');
+const concat = require('gulp-concat');
+const size = require('gulp-size');
+
+module.exports = function build_js() {
+ return src(['src/components/bem-blocks/**/*.js', 'src/js/01_main.js'])
+ .pipe(uglify())
+ .pipe(babel({
+ presets: ['@babel/env']
+ }))
+ .pipe(concat('main.min.js'))
+ .pipe(size({
+ 'gzip': true,
+ 'pretty': true,
+ 'showFiles': true,
+ 'showTotal': true
+ }))
+ .pipe(dest('build/js/'))
+}
\ No newline at end of file
diff --git a/tasks/dev_js.js b/tasks/dev_js.js
new file mode 100644
index 00000000..344d107c
--- /dev/null
+++ b/tasks/dev_js.js
@@ -0,0 +1,25 @@
+const {
+ src,
+ dest
+} = require('gulp');
+const uglify = require('gulp-uglify-es').default;
+const concat = require('gulp-concat');
+const map = require('gulp-sourcemaps');
+const bs = require('browser-sync');
+const size = require('gulp-size');
+
+module.exports = function dev_js() {
+ return src(['src/components/bem-blocks/**/*.js', 'src/js/01_main.js'])
+ .pipe(map.init())
+ .pipe(uglify())
+ .pipe(concat('main.min.js'))
+ .pipe(map.write('../sourcemaps'))
+ .pipe(size({
+ 'gzip': true,
+ 'pretty': true,
+ 'showFiles': true,
+ 'showTotal': true
+ }))
+ .pipe(dest('build/js/'))
+ .pipe(bs.stream())
+}
\ No newline at end of file
diff --git a/tasks/fonts.js b/tasks/fonts.js
new file mode 100644
index 00000000..81d535d7
--- /dev/null
+++ b/tasks/fonts.js
@@ -0,0 +1,32 @@
+const fs = require('fs');
+const chalk = require('chalk');
+
+let srcFonts = 'src/scss/_local-fonts.scss';
+let appFonts = 'build/fonts/';
+module.exports = function fonts(done) {
+ fs.writeFile(srcFonts, '', () => {});
+ fs.readdir(appFonts, (err, items) => {
+ if (items) {
+ let c_fontname;
+ for (let i = 0; i < items.length; i++) {
+ let fontname = items[i].split('.'),
+ fontExt;
+ fontExt = fontname[1];
+ fontname = fontname[0];
+ if (c_fontname != fontname) {
+ if (fontExt == 'woff' || fontExt == 'woff2') {
+ fs.appendFile(srcFonts, `@include font-face("${fontname}", "${fontname}", 400);\r\n`, () => {});
+ console.log(chalk `
+{bold {bgGray Added new font: ${fontname}.}
+----------------------------------------------------------------------------------
+{bgYellow.black Please, move mixin call from {cyan src/scss/_local-fonts.scss} to {cyan src/scss/_fonts.scss} and then change it!}}
+----------------------------------------------------------------------------------
+`);
+ }
+ }
+ c_fontname = fontname;
+ }
+ }
+ })
+ done();
+}
\ No newline at end of file
diff --git a/tasks/ftp.js b/tasks/ftp.js
new file mode 100644
index 00000000..c0fd8401
--- /dev/null
+++ b/tasks/ftp.js
@@ -0,0 +1,21 @@
+const {
+ src
+} = require('gulp');
+const ftp = require('vinyl-ftp');
+const ftpSettings = require('../tasks/ftp_settings');
+const chalk = require('chalk');
+const size = require('gulp-size');
+const connect = ftp.create(ftpSettings);
+
+module.exports = function ftp() {
+ return src('build/**/*.*')
+ .pipe(connect.newer('www/'))
+ .pipe(size({
+ 'gzip': true,
+ 'pretty': true,
+ 'showFiles': true,
+ 'showTotal': true
+ }))
+ .pipe(connect.dest('www/'))
+ .on('success', () => console.log(`Finished deploing ./build to https://${chalk.blueBright(ftpSettings.host)}`))
+}
\ No newline at end of file
diff --git a/tasks/ftp_settings.json b/tasks/ftp_settings.json
new file mode 100644
index 00000000..426eedf9
--- /dev/null
+++ b/tasks/ftp_settings.json
@@ -0,0 +1,6 @@
+{
+ "host": "yourhosting.com",
+ "user": "username",
+ "pass": "*********",
+ "parallel": 10
+}
\ No newline at end of file
diff --git a/tasks/html.js b/tasks/html.js
new file mode 100644
index 00000000..103be4af
--- /dev/null
+++ b/tasks/html.js
@@ -0,0 +1,20 @@
+const {
+ src,
+ dest
+} = require('gulp');
+const include = require('gulp-file-include');
+const bs = require('browser-sync');
+const size = require('gulp-size');
+
+module.exports = function html() {
+ return src(['src/**/*.html', '!src/**/_*.html'])
+ .pipe(include())
+ .pipe(size({
+ 'gzip': true,
+ 'pretty': true,
+ 'showFiles': true,
+ 'showTotal': true
+ }))
+ .pipe(dest('build'))
+ .pipe(bs.stream())
+}
\ No newline at end of file
diff --git a/tasks/libs_js.js b/tasks/libs_js.js
new file mode 100644
index 00000000..c8305549
--- /dev/null
+++ b/tasks/libs_js.js
@@ -0,0 +1,28 @@
+const plugins = [];
+const {
+ src,
+ dest
+} = require('gulp');
+const uglify = require('gulp-uglify-es').default;
+const concat = require('gulp-concat');
+const map = require('gulp-sourcemaps');
+const size = require('gulp-size');
+
+module.exports = function libs_js() {
+ if (plugins.length > 0)
+ return src(['src/js/01_main.js', 'src/components/bem-blocks/**/*.js'])
+ .pipe(map.init())
+ .pipe(uglify())
+ .pipe(concat('main.min.js'))
+ .pipe(map.write('../sourcemaps'))
+ .pipe(size({
+ 'gzip': true,
+ 'pretty': true,
+ 'showFiles': true,
+ 'showTotal': true
+ }))
+ .pipe(dest('build/js/'))
+ else {
+ return true;
+ }
+}
\ No newline at end of file
diff --git a/tasks/libs_style.js b/tasks/libs_style.js
new file mode 100644
index 00000000..fe235e69
--- /dev/null
+++ b/tasks/libs_style.js
@@ -0,0 +1,31 @@
+const plugins = [];
+
+const {
+ src,
+ dest
+} = require('gulp');
+const sass = require('gulp-sass');
+const concat = require('gulp-concat');
+const map = require('gulp-sourcemaps');
+const size = require('gulp-size');
+
+module.exports = function libs_style() {
+ if (plugins.length > 0) {
+ return src(plugins)
+ .pipe(map.init())
+ .pipe(sass({
+ outputStyle: 'compressed'
+ }).on('error', sass.logError))
+ .pipe(concat('libs.min.css'))
+ .pipe(map.write('../sourcemaps/'))
+ .pipe(size({
+ 'gzip': true,
+ 'pretty': true,
+ 'showFiles': true,
+ 'showTotal': true
+ }))
+ .pipe(dest('build/css/'))
+ } else {
+ return true;
+ }
+}
\ No newline at end of file
diff --git a/tasks/php.js b/tasks/php.js
new file mode 100644
index 00000000..43313d69
--- /dev/null
+++ b/tasks/php.js
@@ -0,0 +1,20 @@
+const {
+ src,
+ dest
+} = require('gulp');
+const include = require('gulp-file-include');
+const bs = require('browser-sync');
+const size = require('gulp-size');
+
+module.exports = function php() {
+ return src('src/**/*.php')
+ .pipe(include())
+ .pipe(size({
+ 'gzip': true,
+ 'pretty': true,
+ 'showFiles': true,
+ 'showTotal': true
+ }))
+ .pipe(dest('build'))
+ .pipe(bs.stream())
+}
\ No newline at end of file
diff --git a/tasks/rastr.js b/tasks/rastr.js
new file mode 100644
index 00000000..39daae11
--- /dev/null
+++ b/tasks/rastr.js
@@ -0,0 +1,46 @@
+const {
+ src,
+ dest
+} = require('gulp');
+const changed = require('gulp-changed');
+const imagemin = require('gulp-imagemin');
+const recompress = require('imagemin-jpeg-recompress');
+const pngquant = require('imagemin-pngquant');
+const size = require('gulp-size');
+const bs = require('browser-sync');
+const plumber = require('gulp-plumber');
+
+module.exports = function rastr() {
+ return src('src/img/**/*.+(png|jpg|jpeg|gif|svg|ico)')
+ .pipe(plumber())
+ .pipe(changed('build/img'))
+ .pipe(imagemin({
+ interlaced: true,
+ progressive: true,
+ optimizationLevel: 5,
+ },
+ [
+ recompress({
+ loops: 6,
+ min: 50,
+ max: 90,
+ quality: 'high',
+ use: [pngquant({
+ quality: [0.8, 1],
+ strip: true,
+ speed: 1
+ })],
+ }),
+ imagemin.gifsicle(),
+ imagemin.optipng(),
+ imagemin.svgo()
+ ], ), )
+ .pipe(size({
+ 'gzip': true,
+ 'pretty': true,
+ 'showFiles': true,
+ 'showTotal': true
+ }))
+ .pipe(dest('build/img'))
+ .pipe(bs.stream())
+}
\ No newline at end of file
diff --git a/tasks/style.js b/tasks/style.js
new file mode 100644
index 00000000..dcf7530f
--- /dev/null
+++ b/tasks/style.js
@@ -0,0 +1,46 @@
+const {
+ src,
+ dest
+} = require('gulp');
+const sass = require('gulp-sass');
+const bulk = require('gulp-sass-bulk-importer');
+const prefixer = require('gulp-autoprefixer');
+const clean = require('gulp-clean-css');
+const concat = require('gulp-concat');
+const map = require('gulp-sourcemaps');
+const bs = require('browser-sync');
+const size = require('gulp-size');
+
+module.exports = function style() {
+ return src('src/scss/**/*.scss')
+ .pipe(map.init())
+ .pipe(bulk())
+ .pipe(sass({
+ outputStyle: 'compressed'
+ }).on('error', sass.logError))
+ .pipe(prefixer({
+ overrideBrowserslist: ['last 8 versions'],
+ browsers: [
+ 'Android >= 4',
+ 'Chrome >= 20',
+ 'Firefox >= 24',
+ 'Explorer >= 11',
+ 'iOS >= 6',
+ 'Opera >= 12',
+ 'Safari >= 6',
+ ],
+ }))
+ .pipe(clean({
+ level: 2
+ }))
+ .pipe(concat('style.min.css'))
+ .pipe(map.write('../sourcemaps/'))
+ .pipe(size({
+ 'gzip': true,
+ 'pretty': true,
+ 'showFiles': true,
+ 'showTotal': true
+ }))
+ .pipe(dest('build/css/'))
+ .pipe(bs.stream())
+}
\ No newline at end of file
diff --git a/tasks/svg_css.js b/tasks/svg_css.js
new file mode 100644
index 00000000..03f79978
--- /dev/null
+++ b/tasks/svg_css.js
@@ -0,0 +1,33 @@
+const {
+ src,
+ dest
+} = require('gulp');
+const svgmin = require('gulp-svgmin');
+const svgCss = require('gulp-svg-css-pseudo');
+const size = require('gulp-size');
+
+module.exports = function svg_css() {
+ return src('src/svg/css/**/*.svg')
+ .pipe(svgmin({
+ plugins: [{
+ removeComments: true
+ },
+ {
+ removeEmptyContainers: true
+ }
+ ]
+ }))
+ .pipe(svgCss({
+ fileName: 'svg',
+ fileExt: 'scss',
+ cssPrefix: '--svg__',
+ addSize: false
+ }))
+ .pipe(size({
+ 'gzip': true,
+ 'pretty': true,
+ 'showFiles': true,
+ 'showTotal': true
+ }))
+ .pipe(dest('src/scss/global'))
+}
\ No newline at end of file
diff --git a/tasks/svg_sprite.js b/tasks/svg_sprite.js
new file mode 100644
index 00000000..c3aa2e10
--- /dev/null
+++ b/tasks/svg_sprite.js
@@ -0,0 +1,34 @@
+const {
+ src,
+ dest
+} = require('gulp');
+const svgmin = require('gulp-svgmin');
+const sprite = require('gulp-svg-sprite');
+const size = require('gulp-size');
+
+module.exports = function svg_sprite() {
+ return src('src/svg/**/*.svg')
+ .pipe(svgmin({
+ plugins: [{
+ removeComments: true
+ },
+ {
+ removeEmptyContainers: true
+ }
+ ]
+ }))
+ .pipe(sprite({
+ mode: {
+ stack: {
+ sprite: '../sprite.svg'
+ }
+ }
+ }))
+ .pipe(size({
+ 'gzip': true,
+ 'pretty': true,
+ 'showFiles': true,
+ 'showTotal': true
+ }))
+ .pipe(dest('src/img'))
+}
\ No newline at end of file
diff --git a/tasks/ttf.js b/tasks/ttf.js
new file mode 100644
index 00000000..788ed174
--- /dev/null
+++ b/tasks/ttf.js
@@ -0,0 +1,39 @@
+const {
+ src,
+ dest
+} = require('gulp');
+const changed = require('gulp-changed');
+const ttf2woff2 = require('gulp-ttftowoff2');
+const ttf2woff = require('gulp-ttf2woff');
+const size = require('gulp-size');
+
+module.exports = function ttf(done) {
+ src('src/fonts/**/*.ttf')
+ .pipe(changed('build/fonts', {
+ extension: '.woff2',
+ hasChanged: changed.compareLastModifiedTime
+ }))
+ .pipe(ttf2woff2())
+ .pipe(dest('build/fonts'))
+ .pipe(size({
+ 'gzip': true,
+ 'pretty': true,
+ 'showFiles': true,
+ 'showTotal': true
+ }))
+
+ src('src/fonts/**/*.ttf')
+ .pipe(changed('build/fonts', {
+ extension: 'woff',
+ hasChanged: changed.compareLastModifiedTime
+ }))
+ .pipe(ttf2woff())
+ .pipe(size({
+ 'gzip': true,
+ 'pretty': true,
+ 'showFiles': true,
+ 'showTotal': true
+ }))
+ .pipe(dest('build/fonts'))
+ done();
+}
\ No newline at end of file
diff --git a/tasks/watch.js b/tasks/watch.js
new file mode 100644
index 00000000..39581f4e
--- /dev/null
+++ b/tasks/watch.js
@@ -0,0 +1,18 @@
+const {
+ watch,
+ parallel,
+ series
+} = require('gulp');
+
+module.exports = function watching() {
+ watch('src/**/*.html', parallel('html'));
+ watch('src/**/*.php', parallel('php'));
+ watch('src/**/*.scss', parallel('style'));
+ watch('src/**/*.js', parallel('dev_js'));
+ watch('src/**/*.json', parallel('html'));
+ watch('src/img/**/*.+(png|jpg|jpeg|gif|svg|ico)', parallel('rastr'));
+ watch('build/img/**/*.+(png|jpg|jpeg)', parallel('webp'));
+ watch('src/svg/css/**/*.svg', series('svg_css', 'style'));
+ watch('src/svg/sprite/**/*.svg', series('svg_sprite', 'rastr'));
+ watch('src/fonts/**/*.ttf', series('ttf', 'fonts'));
+}
\ No newline at end of file
diff --git a/tasks/webp.js b/tasks/webp.js
new file mode 100644
index 00000000..f4ad5eaa
--- /dev/null
+++ b/tasks/webp.js
@@ -0,0 +1,18 @@
+const {
+ src
+} = require('gulp');
+const webpconv = require('gulp-webp');
+const changed = require('gulp-changed');
+const multiDest = require('gulp-multi-dest');
+const plumber = require('gulp-plumber');
+const size = require('gulp-size');
+
+module.exports = function webp() {
+ return src('build/img/**/*.+(png|jpg|jpeg)')
+ .pipe(plumber())
+ .pipe(changed('build/img', {
+ extension: '.webp'
+ }))
+ .pipe(webpconv())
+ .pipe(multiDest(['src/img', 'build/img']))
+}
\ No newline at end of file