Vytváření vlastních pluginů na Gruntu je překvapivě snadné. Možná se ptáte proč to dělat? Odpovědí mohou být různé, pro mě jsou důvody následující:
- používám Grunt ve svém vývojovém prostředí (pro účely kódování je plně dostačující)
- potřebuji specifické úpravy souborů, které neumožňuje žádný jiný plugin
- líbí se mi myšlenka vytvoření vlastního pluginu, který si může stáhnout a používat kdokoliv na světě
- jednoduchost
A protože jsem čerstvě během pár hodin vytvořil vlastní funkční plugin, rozhodl jsem se o tom podělit na svém blogu. Třeba se někomu bude hodit.
Jste připravení? Dáme se do práce!
Inicializace a instalace grunt pluginu
Začneme oficiálním návodem od Gruntu, který popisuje pár jednoduchých kroku pro tvorbu pluginu. V této fázi nic vymýšlet nebudeme a držíme se návodu.
- Nainstalujte
grunt-init
do vašeho počítače příkazemnpm install -g grunt-init
- Nainstalujte gruntplugin šablonu příkazem
git clone git://github.com/gruntjs/grunt-init-gruntplugin.git ~/.grunt-init/gruntplugin
(
%USERPROFILE%\.grunt-init\gruntplugin
pro uživatele Windows) - Spusťte příkaz
grunt-init gruntplugin
v prázdné složce. Spustí se průvodce, který vám položí sadu otázek. Po dokončení průvodce se vytvoří všechny potřebné soubory. - Spusťte příkaz
npm install
Tadááá. Právě jsme vytvořili a nainstalovali svůj první Grunt plugin. Nyní když spustíte příkaz grunt
, pak proběhne výchozí build grunt pluginu, a pokud uvidíte zelený Done, pak vše funguje správně.
TIP: pokud nejdříve vytvoříte GIT repozitař pluginu (například v Github nebo Gitlab) a projekt inicializujete v této složce, pak inicializace Grunt pluginu je mnohem jednodušší, protože průvodce sám předvyplní všechny otázky a vy mačkáte jenom ENTER.
Rozbor a pochopení souborů
Když se podíváme co se vlastně stalo, tak v naši prázdné složce se objevili různé soubory. Nejvíce nás budou zajímat jen Gruntfile.js – ve kterém nastavujeme především soubory, které chceme prohnat skrz náš plugin a tasks/{nazev-projektu}.js – ve kterém probíhá veškerá logika a operace našeho pluginu. Nejefektivnější způsob pochopení je právě tento soubor, ve kterém si zkuste logiku pozměnit, spustit grunt
a sledovat co se děje.
Vytváříme vlastní logiku
Jelikož já jsem převážně frontend kodér a předpokládám, že vy taky, tak vytvoříme jednoduchý Grunt plugin, který bude pracovat s HTML soubory a nějakým způsobem je upravovat.
Nejdříve potřebujeme nainstalovat knihovnu jsdom, která nám umožní číst obsah html souborů a pracovat s nim. Do příkazové řádky vložte.
npm i jsdom
Nyní, když máme nainstalováno, vytvoříme soubor index.html ve složce test a jeho obsah bude následující:
<html>
<body>
<p>hello world</p>
<p>this is my first grunt plugin</p>
<img src="images/image.jpg" alt="">
<img src="images/test.png" alt="">
</body>
</html>
Nyní řekneme Gruntfile.js aby procházel tento náš soubor. Uděláme to jednoduše úpravou našeho tasku. Já svůj plugin pojmenoval „my-first-plugin“, vy ho možná pojmenovali jinak, tak si to upravte podle sebe. Zároveň vytvoříme jedno nastavení copyText, které uživatele pluginu budou moci měnit dle vlastních potřeb.
my_first_plugin: {
main: {
options: {
copyText: 'Copyright: My first grunt plugin'
},
files: {
'tmp/index.html': 'test/index.html'
}
},
},
Dostáváme se k logice našeho pluginu, který jak už víme, píšeme v souboru tasks/my_first_plugin.js.
'use strict';
module.exports = function(grunt) {
var jsdom = require('jsdom');
var JSDOM = jsdom.JSDOM;
// Please see the Grunt documentation for more information regarding task
// creation: http://gruntjs.com/creating-tasks
grunt.registerMultiTask('my_first_plugin', 'The best Grunt plugin ever.', function() {
// Merge task-specific and/or target-specific options with these defaults.
var options = this.options({
copyText: ''
});
// Iterate over all specified file groups.
this.files.forEach(function(f) {
// Check that the source file exists
if (f.src.length === 0) {
// Print a success message.
grunt.log.warn('File "' + f.dest + '" does not exist.');
return;
}
// init dom
var dom = new JSDOM(grunt.file.read(f.src));
var doc = dom.window.document;
grunt.log.write(('Reading: ').green + f.src.toString());
//calculate content length
var paragraphs = doc.querySelectorAll('p');
Array.prototype.forEach.call(paragraphs, function(el, i){
var contentLength = el.textContent.length;
el.setAttribute('data-content-length', contentLength);
});
//lazyload images
var images = doc.querySelectorAll('img');
Array.prototype.forEach.call(images, function(el, i){
el.setAttribute('data-src', el.getAttribute('src'));
el.classList.add('js-lazyload');
el.setAttribute('src', 'data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7');
});
//append copyright to body
var copy = doc.createElement("p");
copy.textContent = options.copyText;
doc.body.append(copy);
var modifiedHtml = dom.serialize();
// Write the destination file.
grunt.file.write(f.dest, modifiedHtml);
grunt.log.write('... ' + ('ok').green);
});
});
};
Nyní když do příkazového řádku zadám grunt my_first_plugin
, tak proběhnou veškeré operace, ale když zadám jen grunt
, tak grunt zařve chybu. Proč? Protože grunt provádí testy, které jsou vidět na tomto řádku v gruntfile.js
grunt.registerTask('test', ['clean', 'my_first_plugin', 'nodeunit']);
.
Opravit to můžeme dvěma způsoby:
- jednodušší varianta je smazat nežádoucí kroky. Čili odmazat
nodeunit
z pole - složitější a pracnější způsob je napsat testy správně, které jsou ve složce test, v mém případě je to soubor test/my_first_plugin_test.js. Když soubor otevřete tak uvidíte, že grunt porovnává jestli obsah vstupního a výstupního souboru prohnaného gruntem jsou stejné. Jelikož do souboru provádíme úpravy tak stejné být nesmí. Mírně pozměníme logiku a vše bude fungovat správně.
exports.my_first_plugin = {
setUp: function(done) {
// setup here if necessary
done();
},
main: function(test) {
test.expect(1);
var actual = grunt.file.read('tmp/index.html');
var expected = grunt.file.read('test/index.html');
test.notEqual(actual, expected, 'should describe what the default behavior is.');
test.done();
}
};
Hotovo! Nyní když zadáme grunt
do příkazového řádku, veškeré procesy proběhnou a grunt napíše „Done“. Když se podíváme do výstupního souboru, který se nachází v tmp/index.html tak vypadá následně:
<html>
<body>
<p data-content-length="11">hello world</p>
<p data-content-length="29">this is my first grunt plugin</p>
<img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" alt="" data-src="images/image.jpg" class="js-lazyload">
<img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" alt="" data-src="images/test.png" class="js-lazyload">
<p>Copyright: My first grunt plugin</p>
</body>
</html>
Při porovnání html souborů vidíme provedené operace, jsou to:
- najdeme všechny paragrafy, spočítáme počet znaků v každém z nich a vložíme to do
data-content-length
atributu - projdeme všechny obrázky, přesuneme
src
dodata-src
atributu, přidáme třídujs-lazyload
a dosrc
vložíme base64 placeholder - před
</body>
vložíme copyright text, který bude možné měnit skrze nastavení v gruntu
Jsou to jen příklady, které mě napadly. Možností máte neomezené množství a můžete se soubory provádět cokoliv co vás napadne a co vám javascript dovolí 🙂
Publikace do světa
Nyní, když máme funkční plugin připravený k publikaci do světa, do příkazové řádky vepíšeme npm publish
. Tímto příkazem se vytvoří veřejný npm balíček, který si kdokoliv může stáhnout a používat včetně vás.
Pokud vám článek přišel užitečný neváhejte se pochlubit co jste udobřili v komentářích.