Machy8\Webloader – PHP bundler pro JS a CSS

Machy8
Člen | 59
+
+5
-

Ahoj,
protože jsem v průběhu posledních 2 měsíců často řešil načítání CSS a JS souborů a pluginy, které jsou na componette mi na to v některých případech nestačily, rozhodl jsem se vytvořit vlastní řešení.

Odkazy

Instalace

composer require machy8/webloader

Co umí

  • Vytvářet kolekce souborů
  • Sloučit více kolekcí pomocí kontejneru a vygenerovat je najednou
  • Generování pouze těch kolekcí, které jsou na dané stránce nebo všech najednou
  • Zapnout nebo vypnout cache (při vypnutém cache bude opakovaně přegenerovávat soubory při načtení stránky)
  • Konfigurovat kolekce i kontejnery v neon souborech a toto nastavení popřípadě modifikovat klidně i v šabloně
  • Pro každou kolekci vygenerovat link s atributem preload a prefetch
  • Vytvořit cestu k vygenerovanému souboru nebo ho načíst do stránky (například pro critical css)
  • Generovat elementy link, script, style s libovolnými atributy
  • Registrovat filtry a placeholdery pro cesty k souborům
  • Spouštět filtry jednou po načtení kolekce souborů nebo pro každý soubor zvlášť
  • Nastavit delimiter pro placeholdery
  • + je v repozitáři již vytvořený filtr pro url v CSS souborech a bridge pro Tracy

Ukázka
Neon

extensions:
    webloader: WebLoader\Bridges\Nette\WebLoaderExtension

webloader:
    outputDir: %wwwDir%/webtemp
	documentRoot: %wwwDir%
    filesCollections:
        critical:
            cssFiles:
                - path/to/file.css
            cssLoadContent: TRUE

        homepage:
            jsFiles:
                - path/to/file.js
			jsFilters:
				- minify

Presenter

/**
 * @var Engine
 */
private $webLoader;


public function __construct(\WebLoader\Engine $engine)
{
    $this->webLoader = $engine;
}


public function beforeRender()
{
    $this->webLoader->addJsFilter('minify', function(string $code) {
		// Minify
        return $code;
    });


    $this->template->setParameters([
        'webloaderFilesCollectionRender' => $this->webLoader->getFilesCollectionRender()
    ]);
}

Šablona

{$webloaderFilesCollectionRender->css('critical')|noescape}
{$webloaderFilesCollectionRender->js('homepage')|noescape}

Output

<style type="text/css">
	/* critical.css */
</style>

<script async type="text/javascript" src="/webtemp/homepage.js?v=1508851219"></script>

TODO?

  • Přidat možnost slučovat kolekce v kontejneru do jednoho souboru?
  • Povolit přidání filtrů i přez neon soubor (pouze pro Nette)?
  • Přidat další možnost generování link elementu s rel=„subresource“?

Budu rád za jakoukoliv připomínku, nápad či hejt :).

pitr82
Člen | 121
+
0
-

Ahoj,
@Machy8, máš někde i funkční ukázku s použitím minimalizace souborů?

Machy8
Člen | 59
+
0
-

@pitr82

  • Je to použito například zde www.lekarna.cz a www.mojalekaren.sk + to chci použít na dalších webech.
  • Soubory jsou minifikovány na základě zvoleného minifikátoru (naprosto libovolné). WebLoader soubory pouze načítá z různých míst a spojuje dohromady. Filtry pak aplikuje podle nastavení (pro celou kolekci nebo pro každý soubor zvlášť).
  • Pospojované kolekce, vygenerované html soubory a minifikované soubory, přez níže uvedené filtry, lze nalézt ve složce tests.
  • Filtry:
Hanz25
Člen | 38
+
0
-

Pěkné! Něco takového jsem už chvíli hledal, vypadá to dobře.

Jak se dá rozchodit linkování v případě, že používám XAMPP a tedy mám root aplikace přístupný z url

http://localhost/<jmenoapp>/www

To se mi ještě nepodařilo zjistit. Pořád tam přidává na začátek lomítko a odkazuje to od rootu localhostu. Pokud změním parametry outputDir nebo documentRoot (mám nastavené stejně jak je výše), tak to zas řve, že daná cesta neexistuje.

Díky

Machy8
Člen | 59
+
0
-

@Hanz25 děkuju za připomínku! Tato možnost zde zatím není, protože jsem ji doposud nevyužil. Všechny soubory mám většinou postahované u sebe. Vytvořil jsem na to issue a zvážím, jestli tuto funkcionalitu přidat.

Prozatím je potřeba soubory načítat přes root nebo vytvořit patch na soubor Compiler.php, kde bude zakomentovaná / odstraněná podmínka na kontrolu existence souboru (r. 469–471).

Edit
Když nad tím tak přemýšlím, tak v podstatě nepotřebuješ načítat soubory z jiné url (tato funkcionalita ale chybí, takže ji doplním). Jediné co potřebuješ, je do webloaderu předat outputDir a documentRoot, stejně jako je v ukázce. Webloader nemá funkcionalitu (a nevím, jestli ji mám přidávat) na linkování souboru z něco://**.

webloader:
	documentRoot: %wwwDir%
	outputDir: %wwwDir%/webtemp

Editoval Machy8 (30. 10. 2017 20:05)

Hanz25
Člen | 38
+
+1
-

@Machy8 zatím mám nad tím replace a přepisuju to

ale teoreticky by tam mělo jít dát něco jako basePath ne? To nastavení co uvádíš tam přesně mám, ale v css mi to nalinkuje /webtemp/collection.css místo <jmenoapp>/www/webtemp/collection.css pak by to znamenalo, že pokud bych chtěl přesunout web do nějaké podsložky, musel bych přesně nastavit i cestu mimo aplikační adresář. Jestli to dobře chápu, tak to asi bug je.

byl by super nějaký full config example. Chtěl bych co nejvíce věcí nastavit z configu a v presenteru upravovat jen to nejnutnější a pro ty možnosti a jejich názvy musím jít až do WebLoaderExtension

Jak se to chová k url v css souborech? Vypadá to, že je nemodifikuje, takže pokud chci zachovat správné linkování tak musím držet ty generované soubory na stejné úrovni jako psané css? A pokud se rozhodnu, že mi vygenerovaný soubor v podsložce nevyhovuje a rozhodnu si to vypsat do stránky, musím zároveň zaktualizovat i linky v css souborech?

koukal jsem na ty placeholdery, ale ty, co jsem pochopil a zkoušel, fungují jen v definicích cest k souborům a ne uvnitř samotného css souboru

díky za odpověď

Machy8
Člen | 59
+
0
-

@Hanz25 nějak nechápu o co přesně se snažíš

  • Base path je generována následovně.
  • Mám li document root například /var/www/html/www a k tomu pak webtemp adresář s cestou /var/www/html/www/webtemp, tak výsledná basePath je /webtemp a vygenerovaný element bude vypadat následovně
<link rel="stylesheet" href="/webtemp/collection.css">
  • Pokud máš v Nette root /www, pak nenalinkuješ nic, co je za rootem a jedině by to šlo „přímo“ přez url, což WebLoader neumí
  • Mohl bych tě poprosit o příklad kódu, o který ti jde?
pitr82
Člen | 121
+
0
-

Ahoj, @Machy8
Planuješ podporu pro NEON config s využitím Nette/utils/finder, abych nemusel vypisovat všechny soubory ručně ?

Machy8
Člen | 59
+
0
-

Ahoj, @pitr82
Zatím jsem nad tím nepřemýšlel. Ty soubory se do konfiguráku přidají jen jednou. Je nějaký speciální důvod na to používat finder? O kolika souborech se bavíme?

pitr82
Člen | 121
+
0
-

Machy8 napsal(a):

Ahoj, @pitr82
Zatím jsem nad tím nepřemýšlel. Ty soubory se do konfiguráku přidají jen jednou. Je nějaký speciální důvod na to používat finder? O kolika souborech se bavíme?

Jan mě to napadlo, jako ulehčení, včetně exclude, když nechci načítat podadresáře.

Dá se nějak donutit webloader , aby kontroloval soubory na změnu?
Při každé změně css musím vymazat webtemp. Pro vývoj by tato možnost nebyla vůbec špatná.

GEpic
Člen | 562
+
0
-

pitr82 napsal(a):

Ahoj, @Machy8
Planuješ podporu pro NEON config s využitím Nette/utils/finder, abych nemusel vypisovat všechny soubory ručně ?

To nechceš, co když máš natažený bootstrap např přes bower a čerstvě zkompilovaný? V dist se ti vytvoří jak .css, tak .min.css, tak theme.css i theme.min.css a to prostě nechceš načítat vše. To nemluvím o dalších knihovnách. Toto je lepší mít přímo definované.

Editoval GEpic (6. 11. 2017 9:37)

Machy8
Člen | 59
+
0
-

@pitr82
Zkoušel jsi vypnout cache v config.local.neon?

webloader:
	disableCache: TRUE
Hanz25
Člen | 38
+
+2
-

Machy8 napsal(a):

  • Mohl bych tě poprosit o příklad kódu, o který ti jde?

teď to mám v layoutu implementované takto

{$webLoader->css('base')|noescape|replace:"/webloader":$basePath."/webloader"}

jde mi o to, abych do té vygenerované adresy (už přímo v kódu) přidal $basePath

protože když vyvíjím na localhostu a celý projekt mi funguje v nějaké podsložce, přistupuji k němu přes url (localhost/<jmenoprojektu>/). Takže když všechny vygenerované linky začínají vnějším rootem „/“ tak mi to nesedí.

Hanz25
Člen | 38
+
0
-

A ještě by mě zajímalo, jak by se mělo pracovat s tou keší na lokálu/produkci.

Zatím tedy mám v lokálním configu

webloader:
    disableCache: TRUE

s tím, že mi to asi přegeneruje vše vždy že?

A když nahraju novou verzi na produkci? Se smazáním nette keše v temp/ to asi nic nemá že? Takže musím po nahrání smazat i ty vygenerované soubory, abych vynutil jejich přegenerování?

Editoval Hanz25 (23. 1. 2018 12:59)

Hanz25
Člen | 38
+
0
-

Nevím jestli s těmi cestami pracuji správně, ale stejně se mi tam zdá něco divného. Jakoby to občas bralo z rootu a občas z absolutní pozice ve které právě je… Načrtnu situaci a co očekávám, třeba to fakt špatně používám…

lokální adresa: http://localhost/mujprojekt/www
produkční adresa: http://mujprojekt.cz/
nastavení neonu:

webloader:
    documentRoot: %wwwDir%
    outputDir: %wwwDir%/webloader
$webloader->css()

vygeneruje

<link type="text/css" rel="stylesheet" href="/webloader/base.css?v=1516261067">

ovšem na localhostu potřebuji

<link type="text/css" rel="stylesheet" href="/mujprojekt/www/webloader/base.css?v=1516261067">

($basePath v nette to umí poznat)

Když spustím testy
WebLoader\Exception: Given output dir "C:\xampp\htdocs\mujprojekt\tests\cases\presenter\MujModule\presenters/webloader" doesn't exists or is not a directory.

Díky za odpověď :)

Editoval Hanz25 (23. 1. 2018 16:53)

Machy8
Člen | 59
+
0
-

@Hanz25 To máš nějaký složitý :D. WebLoader ti to generuje špatně, protože v něm máš nastavený stejný root, leč cesta je jiná.

WebLoader vždy vychází z rootu.
Tudíž když je root /var/www/html/www
a output má jít do složky /var/www/html/www/webtemp
tak se odebere /var/www/html/www a vygenerovaná cesta bude například /webtemp/file.css.

Zkusil bych třeba na localhostu v presenteru pozměnit documentRoot nebo si nakonfigurovat jiná nastavení v config.neon a config.local.neon.

Editoval Machy8 (25. 1. 2018 19:58)

pitr82
Člen | 121
+
0
-

Ahoj @Machy8 ,
umí webloader, vygenerovat z kolekce jen jeden soubor ?

Machy8
Člen | 59
+
0
-

@pitr82

  • Umí. Pomocí $webLoader->getCompiler() získáš kompiler kolekcí, který obsahuje metody jako compileAllFilesCollections (vygeneruje vše, vhodné pro read only deployment), compileCssFilesCollection, compileJsFilesCollection, compileFilesCollectionByType.
  • Díky za poznámku, doplním to do dokumentace
pitr82
Člen | 121
+
0
-

@Machy8
To jsme si úplně neporozuměli, manuální kompilace je super :-), určitě stojí za zmínku do dokumentace.
Spíš jsem myslel, když renderuješ CollectionContainer, tak se mi zvlášť vypisují jednotlivé kontejnery.
Obvykle mám Homepage a HomepageToMinify – a chtěl jsem, aby tohle vygenerovalo jeden soubor.

Machy8 napsal(a):

@pitr82

  • Umí. Pomocí $webLoader->getCompiler() získáš kompiler kolekcí, který obsahuje metody jako compileAllFilesCollections (vygeneruje vše, vhodné pro read only deployment), compileCssFilesCollection, compileJsFilesCollection, compileFilesCollectionByType.
  • Díky za poznámku, doplním to do dokumentace
Machy8
Člen | 59
+
0
-

@pitr82 jakože bys chtěl sloučit kolekce z kontejneru do jednoho souboru?

pitr82
Člen | 121
+
0
-

Machy8 napsal(a):

@pitr82 jakože bys chtěl sloučit kolekce z kontejneru do jednoho souboru?

Přesně tak :-)

Machy8
Člen | 59
+
0
-

@pitr82 To neumí. Od toho jsou kolekce. Kontejner je umí jen vypsat najednou. Můžeš mi prosím říct, k čemu přesně to potřebuješ združovat až do takovéto úrovně?

Editoval Machy8 (21. 3. 2018 12:15)

pitr82
Člen | 121
+
0
-

@Machy8 vidím, že máš v plánu i načítání externích souborů. Kdy to asi budeš realizovat?

Machy8
Člen | 59
+
0
-

@pitr82 v brzké době. Budeme to za chvíli potřebovat na lékárně.cz, takže to moc dlouho otálet nebude 🙂.

pitr82
Člen | 121
+
0
-

Ahoj @Machy8

mam dotaz k

<?php
$this->webLoader->addJsFilter('minify', function(string $code) {
        // Minify
        return $code;
    });
?>

pokud pouziju parametr forEachfile = true , tak mi projede soubory po jednom = OK.
Pokud ale pouziju false, načte mi kompletně všechny soubory z zpracuje, pak se ale ještě zavola se zpracovaným obsahem tolikrát, kolik jsem zpracovával souborů.

A ještě jedna věc. když je mezi minimalizovanými soubory netteForms.min.js tak rozšíření tedivm/jshrink mi zkončí ve smyčce.

Danny
Člen | 146
+
+1
-

@Hanz25

Ahoj, můžu se zeptat jak si ten problém s cestami vyřešil? Řeším stejný problém, mám projekt zde /var/www/html/projects/nazev-projektu/

mám nastavené cesty

webloader:
    outputDir: %wwwDir%/webtemp
    documentRoot: %wwwDir%

a webloader mi generuje cestu

<link type="text/css" rel="stylesheet" href="/webtemp/core.css?v=1563905846">

A já bych naopak potřeboval

<link type="text/css" rel="stylesheet" href="/projects/vehicles/www/webtemp/core.css?v=1563905846">

Přijde mi že webloader používá tu cestu správně ale jenom pro generování css/js do webtemp ale to cestu k tomu souboru pak vygeneruje špatně.

Nebo @Machy8

Díky za info.

Editoval Danny (24. 7. 2019 19:47)

mrfazolka
Člen | 24
+
0
-

Žije ještě tenhle projekt @Machy8 ?:)

Je možné nastavit outputDir i pro danou kolekci? Nebo něco jako podsložku v outputDir, nastavitelnou pro danou css/js kolekci bundelu?

Příklad:

webloader:
    outputDir: %wwwDir%/webtemp
    documentRoot: %wwwDir%
    disableCache: TRUE
    filesCollections:
        testTinyMceBundle:
                jsFiles:
                    - https://cdnjs.cloudflare.com/ajax/libs/tinymce/5.7.1/tinymce.min.js
                    - https://cdnjs.cloudflare.com/ajax/libs/tinymce/5.7.1/themes/silver/theme.min.js
                    - https://cdnjs.cloudflare.com/ajax/libs/tinymce/5.7.1/icons/default/icons.min.js
                jsFilters:
                    #- minify
                jsOutputDir:
					- tinymce

                cssFiles:
                    - https://cdnjs.cloudflare.com/ajax/libs/tinymce/5.7.1/skins/ui/oxide/skin.min.css
                    - https://cdnjs.cloudflare.com/ajax/libs/tinymce/5.7.1/skins/ui/oxide/content.inline.min.css
                cssOutputDir:
					- tinymce/skins/ui/oxide

Jde mi o použití něčeho jako jsOutputDir a cssOutputDir, které jsem si v příkladu domyslel, pro demonstraci možnosti nastavení cest generovaných souborů jednotlivých js/css kolekcí.