Uvod
Prednosti i mane modularnog programiranje
Organizacija koda samo u jednom fajlu, čini programiranje veoma komplikovanim. Iz tog razloga se u JavaScript-u kao i u mnogim drugim programskim jezicima, programski kod deli u manje celine tzv. module. Podela se vrši prema odredjenoj šemi, koja omogućava dobru organizaciju i olakšava vizuelnu i strukturnu preglednost.
Pri deljenju na manje delove (module), dešava se da jedan modul zavisi od koda koji se nalazi u drugom modulu. To se prevazilazi “uvoženjem” (eng. import) koda iz jednog modula u drugi. Sve do pojave ES2015 standarda nije postojla sintaksa za module, pa se koristila eksterna sintaksa CommonJS ili AMD. Webpack podrazumevano koristi “CommonJS” sintaksu, pa se tako u okviru webpack.config.js fajla mogu naći ključne reči require() i module.export. Medjutim ako se u projekat implementira “Babel”, preporuka je da se koristi JavaScript sintaksa ugradjena u sam jezik prema ES2015 standardu.
OBJAŠNJENJE:
Externe sintakse nisu JavaScript biblioteke, već odgovarajuće specifikacije i konvencije za definisanje modula. Eksterna sintaksa može da se koristi jedino uz pomoć modul loader-a ili bundler-a koji joj “udahnu život”. Kada modul loader/bundler podržava sintaksu, to znači da zna da primeni ugradjene metode koje su prethodno zamišljene da se koriste kroz odredjenu sintaksu. Više ovome pogledajte u članku Modularno programiranje – eksterna sintaksa (AMD & CommonJS)
JavaScript modul od kojeg zavisi rad drugog modula se zove “zavisnost” (eng. “dependency”). Podela koda na više manjih modula ima svojih prednosti medjutim pri tome se javljaju sledeći problemi koji treba da se prevaziđu:
- povećan broja request-a za učitavanje tih fajlova ka serveru.
- organizovanje redosleda učitavanja modula, i svih njihovi zavisnosti (eng. dependencies)
Namena bundler-a
“Modul bundler” rešava probleme nastale usitnjavanjem koda, tako što kompajlira sve module u jedan fajl, pri čemu vodi računa da moduli koji su zavisnost drugome modulu budu učitani pre modula kome trebaju.
Najpoznatiji modul bundler-i su:
-
Browserify – implementira CommonJS sintaksu u browser okruženju. Može da se nadogradjuje raznim plugin-ima. Uz pomoć task runner-a (Gulp ili Grunt) može da završi različite zadatke vezane za obradu programskog koda.
-
Webpack – učitavati module u bilo kom popularnom formatu (CommonJS, UMD, AMD, ES6). Dolazi kao jedan paket i nisu mu potrebni dodatni plugin. Oficijelna stranica za webpack je https://webpack.github.io/, dok se dokumentacija za webpack 2 nalazi na https://webpack.js.org/
OBJAŠNJENJE:
Glavna namena “webpack-a” je da objedini sve module u jedan ili više krupnijih fajlova, dok dodatne funkcionalnosti webpack dobija kroz “webpack loader-e” ili “webpack plugin-e”.
Instalacija webpack-a
Pošto se instalacija vrši preko package manager-a, potrebno je imati instaliran node.js i odgovarajući package manager. Nakon toga koristeći “package manager” instaliramo i sam webpack:
1 |
npm install --save-dev webpack |
1 |
yarn add --dev webpack |
NAPOMENA:
Svi primeri iz članka objedinjeni su u jedan projekat. Završna verzija projekta je objavljena na GitHub-u
https://github.com/choslee/webprogramiranje-webpackOsnove.
Osnovna konfiguracija webpack-a
Konfiguraciju webpacka definišemo u okviru webpack.congfig.js fajla. U njemu se koristi common.js sintaksa, pa se primećuju ključne reči require() i module.exports = {…}. Više o common.js pogledajete u članku Modularno programiranje sa eksternom sintaksom
NAPOMENA:
Kad god se promeni webpack.config.js, potrebno je da se ponovo generišu (“build-uju”) fajlovi. Ukoliko se koristi “webpack –watch” opcija, onda je potrebno prekinuti proces sa CTRL + C i ponovo gs uključiti.
Definisanje ulaznog i generisanje izaznog fajla
Pošto smo već pri generisanju package.json fajla definisali šta je ulazna tačka (npr. “index.js”), sada tu činjenicu možemo da iskoristimo da definišemo ulaznu tačku i za webpack. Početni i izlazni fajl za webpack definišemo u okviru webpack.config.js:
1 2 3 4 5 6 |
module.exports = { entry: "./index.js", // Ulazni .js fajl output: { filename: "bundle.js", // Naziv fajla koji se generiše } } |
Prethodna konfiguracija podrazumeva da će se fajl bundle.js generisati u root-u projekta, medjutim ukoliko želimo da se bundle.js generiše na drugom mestu potrebno je dodati svojstvu output novo svojstvo path. Vrednost svojstva path je apsolutna putanja, stoga koristimo node.js promenjivu __dirname koja predstavlja apsolutnu putanju foldera u kome se nalazi modul.
1 2 3 4 5 6 7 |
module.exports = { entry: "./src/index.js", // Putanja do glavnog početnog JavaScript fajla. output: { filename: "bundle.js", // Naziv fajla koji se generiše path: __dirname + "/dist" // Putanja fajla koji se generiše } } |
Umesto path: __dirname + "/dist" možemo da koristimo sa potpuno istim rezultatom i node.js metodu path.resolve([...paths]). Da bi koristili path.resolve() metodu u okviru webpack.config.js fajla moramo da uvezemo modul require ('path') na vrhu fajla.
1 2 3 4 5 6 7 8 9 |
const path = require ('path'); module.exports = { entry: "./src/index.js", // Putanja do glavnog početnog JavaScript fajla. output: { filename: "bundle.js", // Naziv fajla koji se generiše path: path.resolve(__dirname, "dist") // Putanja fajla koji se generiše } } |
Definisanje višestrukih ulaznih i generisanje više izlaznih fajlova
U dosadašnjem delu je kroz “entry” i “output” svojstva definisan samo po jedan ulazni i jedan krajnji javascript fajl, što je najčešća konfiguracija za “Single Page Application” (tzv. SPA). Medjutim ako vaša aplikacija ima više stranica velika šansa je da će biti zahteva se na svakoj učitava drugi JavaScript bundle fajl. WebPack obezbedjuje i tu funkcionalnost, samo je potrebno da se u konfiguaracionom fajlu definiše više ulaznih (entry) fajlova i da se napiše kod koji dinamički generiše više krajnjih (“outputh”) JavaScript fajlova:
1 2 3 4 5 6 7 8 |
entry: { indexJS : "./src/js/index.js", drugaJS : "./src/js/druga.js" }, output: { filename: "[name].bundle.js", path: __dirname + "/dist" } |
Ovako definisani ulazni i izlazni fajlovi će generisati dva nova JavaScript bundle fajla: “index.bundle.js” i “druga.bundle.js”. Nazivi entry svojstva indexJS i drugaJS su “chunks” imena, koja se koriste kasnije kod HtmlWebpackPlugin plugina. Na ovaj način smo omogućili da dve različite skripte mogu biti ugradjene na dve različite stranice.
Pored prethodno opisanog definisanja višestrukih ulaza i izlaza, često se javlja potreba za razdvajenjem javascript koda na naš kod i tzv. “third party dependencies”. Ovaj postupak se malo razlikuje od opisanog i za njega je potrebno koristiti plugin koji sprečava dupliranje koda. O ovome možete pročitati u sekciji “CommonsChunkPlugin”.
Pokretanje webpack-a
Standardno pokretanje
Pokretanje webacka je isto kao pokretanje bilo kog drugog npm paketa.
1 |
npm run webpack |
1 |
yarn run webpack |
Webpack watch
Da bi izbegli neprestano pozivanje naredbe npm run webpack koja build-uje nove promene, postoji mogućnost da napišemo skriptu u okviru package.json fajla koja uz flag --watch omogućava build-ovanje nakon čuvanja promena unutar projekta.
1 2 3 |
"scripts": { "watch": "webpack --watch" } |
Posled definisanja skripte u package.json možemo u terminalu pokrenuti naredbu koja će pratiti promene:
1 |
npm run watch |
1 |
yarn watch |
Za prekidanje praćenja promena u terminalu koristimo prečicu:
1 |
Ctrl + c |
Nakon čega se od nas zahteva potvrdimo prekid i upišemo Y.
Webpack dev server
Webpack obezbedjuje i jednostavni web server koji nam daje mogućnost da automatski učitatvamo promene koda u browseru tzv. “live reloading“, nakon čuvanja promena u editoru.
Instalacija web servera
1 |
npm install --save-dev webpack-dev-server |
1 |
yarn add --dev webpack-dev-server |
Konfiguracija webpack-a za dev server
Osnovna konfiguracija se svodi na definisanje foldera koji treba da prikazuje, stoga je potrebno unutar “webpack.config.js” fajla ispod dela gde je konfigurisan “devtool” dodati deo:
1 2 3 |
devServer: { contentBase: './dist' } |
Pokretanje dev servera
Za jednostavno pokretanje servera je potrebno definisati skriptu u okviru “package.json” fajla
1 |
"dev": "webpack-dev-server --open" |
Ovo nam omogućava da iz terminala jednostavnom naredbom podignemo server i prikažemo sve iz “dist” foldera. Flag --open nam omogućava da se taj prikaz izvrši u novom tabu u okviru browsera.
1 |
npm run dev |
1 |
yarn dev |
Webpack dev server će nakon svake promene JS ili CSS fajla, automatski ponovno učitati promene u browseru. Za prekidanje rada servera u terminalu koristimo prečicu:
1 |
CTRL + C |
Nakon čega se od nas zahteva potvrdimo prekid i upišemo Y.
Više o webpack dev serveru možete pogledati na WebPack oficijelnoj stranici u sekciji “Using webpack dev server”
BrowserSync & Webpack dev server
BrowserSync je takodje webserver i može isto kao WebPack dev server samostalno da automatski učitava promene u browser tzv. “Live reloading”. Pored ove mogućnosti BrowserSync omogućava istovremeno sinhronizovano testiranje na više različitih uredjaja (mobilini, tablet…) dok su na lokalnom serveru pod uslovom da se nalaze na istoj mreži. Ovo podrazumeva da se jedna ista radnja (npr. klik na dugme, skrol stranice…) istovremeno izvodi na svim aktivnim uredjajima.
Kada se pored Webpack dev servera instalira BrowserSync onda imamo aktivna dva servera, ali u tom slučaju se BrowserSync ponaša kao medijator (proxy server). Na ovaj način smo u mogućnosti da koristimo najbolje funkcije od ta dva.
Instalacija Browsersync
Browsersync app se samostalno instalira (pogledajte na oficijelnom sajtu browsersync.io/)
1 |
npm install browser-sync --save-dev |
1 |
yarn add browser-sync --dev |
Kada se koristi uz webpack instalira se kao plugin.
1 |
npm install --save-dev browser-sync-webpack-plugin |
1 |
yarn add --dev browser-sync-webpack-plugin |
Konfiguracija BrowserSync plugina
Potrebno je na vrhu webpack.config.js fajla importovati plugin:
1 |
var BrowserSyncPlugin = require('browser-sync-webpack-plugin'); |
A u delu zaduženom za plugine dodati:
1 2 3 4 5 6 7 8 |
new BrowserSyncPlugin({ host: 'localhost', port: 3000, // url adresa za vreme developmenta http://localhost:3000/ proxy: 'http://localhost:8080/' // proxy endpoint WebpackDev Servera na http://localhost:8080 }, { reload: false // Sprečava da BrowserSync reloaduje stranu, da bi to uradio WebpackDev Server }) |
Pored ovoga potrebno je isključiti opciju da webpack dev server otvara novi tab, jer se takodje otvara i preko BrowserSynca-a pa bi došlo do dupliranja. Zbog toga je neophodno da obrišemo --open na mestu gde smo prethodno definisali script u okviru package.json fajla.
Startovanje BrowserSync
BrowserSync se sam startuje kada je Webpack u “watch” modu! Nakon startovanja BrowserSync će na kraju ispisa u terminalu dostaviti url adrese za sinhronizovano pristupanje projektu sa dva ili više uredjaja. Dovoljno je da se u vaš mobilni unese obeležena adresa i uredjaji će biti povezani na vašem projektu. Sve što se radi na jednom uredjaju paralelno se izvršava i na drugom. Više o ovome možete pročitati na oficijlnom sajtu “BrowserSync for webpack”.
Webpack loaders
Namena
Glavna namena Webpacka je da bude bundler tj. da okupi sve JavaScript fajlove u jedan veliki fajl prema odgovarajućem redosledu koji uvažava njihove zavisnosti. Medjutim pre pomenutih radnji mogu da se izrvše odredjene “predradnje” nad tim fajlovima, a za to su zaduženi webpack loaders. Loaderi preprocesuiraju fajlove pre nego što ih sam webpack obradi. Loader mogu:
- da integrišu CSS fajlove u JavaScript
- da transformišu fajlove sa drugog jezika u JavaScript (transpiliraju), npr. Typescript u JavaScript ili ES6+ u JavaScript.
- da minifikuju fajlove (CSS/JavaScript) u JavaScript
- da transformišu inline slike u base64-encoded URL string koji tako mogu da budu dostupni JavaScript-u, nakon čega on može da ih ubacuje na stranu.
- da učitavaju i kompajliraju cele framework-e (npr.Vue.js, Angular.js…)
- da testiraju kod (npr. Mocha…)
- da lintuju kod (npr. ESlint…)
- da učitaju i kompajliraju templating sistem u HTML (npr. Handlebar…)
Loaderi mogu da se nadovezuju u serijama (chain together). Listu postojećih loader-a možete pogledati na oficijelnom sajtu webpack.js.org/loaders/
CSS loader
Instalacija paketa za CSS
Ako javascript radi obradu CSS-a (minifikovanje, transpiliranje…) potrebno je uvesti taj CSS fajl u javascript modul koristeći sintaksu
import '../css/main.css'. Da bi javascript prepoznao ovu sintaksu potrebno je koristiti “css-loader”. CSS loader “hendluje” importovanje CSS koda tako što ga generiše kao string.
Ukoliko pored toga želimo i da ugradimo CSS u sam Javascript modul, onda koristimo “style-loader”. Style-loader integriše CSS string u <style> tag koji zatim ugradjuje u “head” HTML stranice. Instalacija ovih paketa se vrši sa naredbom:
1 |
npm install --save-dev style-loader css-loader |
1 |
yarn add --dev style-loader css-loader |
Konfiguracija wabpack-a za CSS loadere
Unutar “webpack.congfig.js” fajla na postojeću konfiguraciju treba dodati sledeći kod:
1 2 3 4 5 6 7 8 9 10 11 |
module: { rules: [ { test: /\.css$/, use: [ {loader :'style-loader'}, // Ubacuje stilove u vidu <style> taga u <head> tag, koristeći JS modul {loader :'css-loader'} // Zna da obradi importovanje ako se koristi ključna reč import i da prebaci CSS u običan string da bi JavaScript mogao da manipuliše sa njim ] } ] } |
NAPOMENA:
Obratiti pažnju da se u ovom nizu loaderi primenjuju u redosledu od poslednjeg ka prvom, stoga “style-loader”, treba da bude pre “css-loader”-a.
Webpack koristi regular expression definisano pod svojstvom “test” sa kojim pronalazi sve CSS fajlove, dok svojstvo “use” definiše koje sve loadere da koristi i kojim redosledom. Više o ovome pogledajte ona oficijelnoj stranici asset menagmente/css loaders. Ukoliko ipak ne želimo da ugradimo obradjeni CSS u JavaScript modul, to možemo postići preko webpack plugina pod nazivom ExtractTextWebpackPlugin
SASS loader
Instalacija paketa za SASS
Ukoliko koristimo SASS unutar nekog JavaScript modula (npr. import './scss/main.scss';), onda je potrebno instalirati pored neophodnih paketa vezanih za CSS: “style-loader” i “css-loader” i paket “sass-loader” koji transpilira SASS u CSS. Pored toga je potrebno instalirati i bibloteku (eng. library) za node.js vezanu za SASS node-sass:
1 |
npm install style-loader css-loader sass-loader node-sass --save-dev |
1 |
yarn add sass-loader node-sass --dev |
Konfiguracija webpack-a za SASS loader
Kod konfiguracije SASS-a se i dalje koristi css-loader da parsira dobijeni css nakon transpiliranja SASS-a kao i style-loader koji ubacuje u takav css u style tag. Takodje se koristi sass-loader za transpilovanje:
1 2 3 4 5 6 7 8 9 10 11 12 |
module: { rules: [ { test: /\.scss$/, use: [ {loader : 'style-loader'}, // Ubacuje stilove u vidu <style> taga u <head> tag, koristeći JS modul {loader : 'css-loader'}, // Zna da obradi importovanje ako se koristi ključna reč import i da prebaci CSS u običan string da bi JavaScript mogao da manipuliše sa njim {loader : 'sass-loader'} // Transpilira Sass u CSS ] } ] } |
Više o ovome možete pogledati na oficijelnoj stranici sass-loader
PostCSS
PostCSS je alat koji može da dodatno obradjuje i transformiše CSS ili SASS fajlove.
Instalacija PostCSS-a
1 |
npm install--save-dev postcss-loader |
1 |
yarn add --dev postcss-loader |
Webpack konfiguracija za postCSS
Nakon instalacije je potrebno dodati 'postcss-loader' u wepack.config.js fajl, kao na sledećem primeru:
1 2 3 4 5 6 7 8 9 10 11 |
rules: [ { test: /\.scss$/, use: [ {loader : 'style-loader'}, // Ubacuje stilove u vidu <style> taga u <head> tag, koristeći JS modul {loader : 'css-loader'}, // Zna da obradi importovanje ako se koristi ključna reč import i da prebaci CSS u običan string da bi JavaScript mogao da manipuliše sa njim {loader : 'postcss-loader'}, // Primenjuje sve funkcionalnosti koje dolaze sa PostCSS loader-om {loader : 'sass-loader'} // Transpilira Sass u CSS ] } ] |
NAPOMENA:
Pošto se u nizu “use” loaderi primenjuju u redosledu od poslednjeg ka prvom, stoga je potrebno postcss-loader staviti posle sass-loader.
PostCSS dodaci
Dodavanje funkcionalnosti postCSS loaderu je omogućeno kroz instalaciju dodataka tzv. plugin-a. Postoji više od 200 regularnih plugina koji mogu da rade različite zadatke, neki od često korišćenih plugina su:
- autoprefixer – dodaje prefikse vezne za odgovarajuće browser-e koristeći bazu podataka sajta Can I Use.
1npm install --save-dev autoprefixer
1yarn add --dev autoprefixer
- cssnext – omogućava da možemo da koristimo najnoviju CSS sintaksu, tako što transformiše novu CSS sintaksu u staru koja je kompatibilna sa starijim browserima. Tako da nije potrebno čekati podršku browsera. Postoji mogućnost da se instalira paket “cssnext”, medjutim prema zvaničnoj dokumentaciji uz “postcss” je preporučeno da se instalira ovako:
1npm install postcss postcss-import postcss-url postcss-cssnext postcss-browser-reporter postcss-reporter --save-dev
1yarn add postcss postcss-import postcss-url postcss-cssnext postcss-browser-reporter postcss-reporter --dev
- cssnano – minifikuje i formatira CSS kroz više optimizacija fa bi dobio što manju veličinu fajla za produkciju.
1npm install cssnano --save-dev
1yarn add --dev cssnano
- rucksack – dodaje nove mogućnosti za rad sa CSS-om, kao npr. responsive typography (font-size: responsive;), shorthand positioning syntax (position: absolute 0 20px;), native clearfix (clear: fix;), automatic font src generation (font-path: ‘/path/to/font/file’;), quantity pseudo-selectors (li:between(4,6))…
1npm install rucksack-css --save-dev
1yarn add --dev rucksack-css
Listu ponudjenih plugina možete pogledati na oficijelnom sajtu kao https://github.com/postcss ili na postcss.parts katalogu.h
“postcss.config.js”
U okviru odvojenog fajla postcss.config.js se učitavju i konfigurišu instalirani dodaci:
1 2 3 4 5 6 7 8 |
plugins: [ require('autoprefixer'), require('cssnano')({ preset: 'default', }), require('rucksack-css'), require('postcss-cssnext') ] |
Babel
Babel je JavaScript transpiler, koji od novih verzija JavaScripta “pravi” kod prema ES5 standardu. Sastoji se od:
- babel-core je glavni babel-ov npm package
- babel-loader je modul koji služi da poveže “Babel” i Webpack
Instalacija paketa za Babel
Instalacija webpack-a se vrši sledećim naredbama:
1 |
npm install --save-dev babel-loader babel-core |
1 |
yarn add --dev babel-loader babel-core |
Više o instalaciji Babel-a možete pogledati na oficijelnoj stranici babeljs.io
Konfiguracija webpack-a za babel
1 2 3 4 5 6 7 8 9 |
module: { rules: [ { test: /\.js$/, exclude: /node_modules/, loader: "babel-loader" } ] } |
Babel preset
Da bi smo mogli da koristimo pripremljena setovanja (eng. preset), potrebno je da ih instaliramo.
Na ovome primeru ćemo da koristimo “env” preset paket koji se instalira sa
babel-preset-env, a koristi se da transpilira javascript kod pisan prema verziji koja je aktuelna u trenutnoj godini (ES2017). Možemo da izaberemo i drugi preset za standard definisan u drugoj godini (npr. ES2016), ali je potrebno da koristimo odgovarajući paket za instaliranje
babel-preset-es2016. Više o ovim paketima možete pogledati u dokumentaciji na oficijelnom sajtu babeljs.io/docs/plugins/#presets-official-presets
1 |
npm install babel-preset-env --save-dev |
1 |
yarn add babel-preset-env --dev |
Pored instalacije potrebno je napraviti fajl pod nazivom .babelrc, koji se stavlja u root projekta. Ovaj fajl je zadužen da čuva babel-ova podešavanja, stoga je potrebno unutar tog fajla ubaciti sledjeći konfiguracioni JSON:
1 2 3 |
{ "presets": ["env"] } |
Browser list
Pored ove jednostavne konfiguracije babel preset-a, preporuka je da se koristi “inteligentni” način transpiliranja, koji uzima u obzir da noviji browseri podržavaju nove ES standarde. Ovaj inteligentni način selekcije omogućava da se ne vrši transpiliranje u ES5 ako to nije potrebno, jer to povećava veličinu koda. Stoga je preporuka kondicionalno transpilirati JavaScript u zavisnosti na kome browseru se kompajlira JavaScript. Idealno bi bilo kada bi targetirali tačno one browsere koje podržava naš kod. Dovoljno je programtično izabrati browsere:
1 2 3 |
"targets": { "browsers": ["last 2 versions", "safari >= 7"] } |
Browser List je babel-ova biblioteka koja nam omogućava izbor browsera koristeći neke uslove (npr. "last 2 versions"). Više o Browserlist bibloteci pogledajte na oficijelnim stranicama “BrowserList”. Za dobar odabir uslova koristite sledeću online stranicu “browserl.ist”.
Primer: Philipa Waltona
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
{ presets: [ ['env', { modules: false, useBuiltIns: true, targets: { browsers: [ 'Chrome >= 60', 'Safari >= 10.1', 'iOS >= 10.3', 'Firefox >= 54', 'Edge >= 15', ], }, }], ] } |
Kada se startuje babel, ovakva konfiguracija će izbaciti dva production-ready javascript fajla:
- main.js (the syntax will be ES2015+)
- main-legacy.js (the syntax will be ES5)
Stoga je nakaon ovoga potrebno ubaciti ove skript u željeni HTML fajl:
1 2 3 4 5 6 |
<!-- Browsers with ES module support load this file. --> <script type="module" src="main.js"></script> <!-- Older browsers load this file (and module-supporting --> <!-- browsers know *not* to load this file). --> <script nomodule src="main-legacy.js"></script> |
Više o ovome pogledajte u članku Philipa Waltona Deploying ES2015+ Code in Production Today a primenu ovoga na njegovom GitHub nalogu “Webpack ESNext Boilerplate”
Image loader
U HTML dokumentu browser povlači slike sa servera tek kada “naleti” na <img> tag (ili ne element koji ima svojstvo “background-image”). Koristeći webpack možemo optimizovati slike pa čak ih i sačuvati kao binarni podatak unutar JavaScript-a. Slike ubačene u javascript možemo da preload-ujemo pa ih browser neće morati da ih preuzme sa dodatnim request-om.
Instalacija paketa za img loader
1 |
npm install file-loader image-webpack-loader url-loader --save-dev |
1 |
yarn add file-loader image-webpack-loader url-loader --dev |
Konfiguracija webpacka za image loader
“image-webpack-loader” kompresuje sliku, nakon čega “url-loader” takvoj kompresovanoj slici proverava veličinu. Ukoliko je slika manja od željene veličine (veličina se definiše kroz options), “url-loader” je sa “Base64 encoding”s prebacuje u šifrovani binarni padatak koji takav ubacuje direktno u JavaScript modul. Ukoliko je kompresovana slika ipak veća od definisane, onda je webpack ubacuje u dist folder kao odvojeni fajl.
1 2 3 4 5 6 7 8 9 10 11 12 |
rules: [ { test: /\.(png|jpe?g|gif|svg)$/, use: [ { loader: 'url-loader', options: { limit: 10000 } // Konvertuje sliku u binarni podataka ako zadovolji uslov < 10k (kao base64 strings) }, { loader: 'image-webpac-loader',} ] } ] |
Korišćenje
Za korišćenje slika u JavaScript modulu je potrebno da ih importujemo:
1 2 |
import malaSlika from "../../assets/mala-slika.jpg"; import velikaSlika from "../../assets/velika-slika.jpg"; |
Kao što se vidi slike su smeštene u assets folderu, koji se nalazi u root-u projekta. Nakon importa možemo da sa slikama radimo šta god je potreba, npr. jednostavno prikazivanje:
1 2 3 4 5 6 7 |
const mala = document.createElement('img'); mala.src = malaSlika; document.body.appendChild(mala); const velika = document.createElement('img'); velika.src = velikaSlika; document.body.appendChild(velika); |
Nakon build-ovanja projekta sa webpack-om u sklopu dist foldera za produkciju se pojavljuje samo velika slika (jer nije prošla test limit 10000), dok je mala slika ugradjena kao binarni string u JavaScript modul.
eslint-loader
ESlinter je jedan od najpopularnijih lintera koda, standardizuje izgled JavaScript koda i nalazi greške odmah pri kompajliranju.
Instalacija
Potrebno je prvo instalirati “eslint core”:
1 |
npm install eslint --save-dev |
1 |
yarn add eslint --dev |
Nakon čega možemo da instaliramo i “eslint-loader”:
1 |
npm install eslint-loader --save-dev |
1 |
yarn add eslint-loader --dev |
Više o eslint-loader možete pročitati na oficijelnoj stranici plugina github.com/MoOx/eslint-loader
Konfiguracija u webpack.config.a
1 2 3 4 5 6 7 8 9 10 |
rules: [ { test: /\.js$/, exclude: /node_modules/, use: [ "babel-loader", "eslint-loader", ], }, ], |
NAPOMENA:
Obratite pažnju na redosled učitavanja loadera, potrebno je da ovaj loader bude pre (čitaj: “ispod”) babel-loader inače će lintovanje biti nad transpilovanim kodom. Postoji mogućnost da se doda properties
enforce: "pre" koji mu omogućava da ovaj loader prvi učita bez obzira na mesto u kodu.
Konfiguracija .eslintrc.json
Potrebno je kofigurisati i sam eslint core. Konfigurisanje se čuva u .eslintrc.json fajlu.
Primer konfigurcije
1 2 3 4 5 6 7 8 9 10 11 12 |
{ "env": { "browser": true, "commonjs": true, "es6": true, "node": true }, "extends": "eslint:recommended", "rules": { //ovde ide overide nekih pravila } } |
Sekcija “extends” definiše koji set pravila želimo da primenimo. Možemo da koristimo preporučeni set od eslint-a “eslint:recommended” ili instalirati neki drugi npr. “airbnb”
U sekciji “rules” može da se pojedinačno promeni neko od pravila. Listu svih pravila možete pronaći na oficijelnom sajtu u sekciji “rules”. Dovoljno je izabrati neko pravilo i promeniti mu konfiguraciju.
Primer – deaktiviranje pravila
Ukoliko ne želimo da bude primenjeno neko pravilo dovoljno je da u sekciji “rules” ubacimo kod "nazivPravila": 0, pa ako ne želimo da aktiviramo proveru pravila “no-console”:
1 2 3 |
"rules": { "no-console":0 }, |
Enabling sourcemap
SourceMap je fajl unutar koga je mapirana je veza izmedju koda u krajnjem fajlu prikazanog u browseru i originalnih ne-bundlovanih fajlova (koji još nisu transpilovani, minifikoavani, kompresovani,…). Sourcemap omogućava pregledaču da rekonstruiše početni izvor i prikaže rekonstruisan originalni kod u debugeru, iako se on ustvari nalazi okviru krajnjeg minifikovanog i bundl-ovanog fajla.
JavaScript sourcemap
Za aktiviranje ove funkcionalnosti nije potrebno instaliranje nekog paketa, već je dovoljno definisati odredjene parametre u sklopu webpack.config.js fajla:
1 |
devtool: 'inline-source-map', |
Nakon ovoga u inspektoru web browser-a će svaki kod biti linkovan sa svojim početnim (originalnim) fajlom pre bundl-ovanja, a ne za bundle.js. Više o ovome možete pročitati na oficijelnom sajtu pod sekcijom using source maps.
CSS & SCSS sourcemap
Najčešća organizacija u radu sa SASS-om je da se svi fajlovi importuju u jedan Da bi aktivirali ovu mogućnost za CSS ili SCSS fajlove, potrebno je dodati kod SCSS/CSS loadera options:{sourceMap: true} kod svakog loadera:
1 2 3 4 5 |
use: [ {loader : 'css-loader', options : {sourceMap: true}}, {loader : 'postcss-loader', options : {sourceMap: true}}, {loader : 'sass-loader', options : {sourceMap: true}} ] |
Nakon ovoga u inspektoru web browser-a će svaki elemenat biti linkovan sa svojim početnim (originalnim) SCSS fajlom pre bundl-ovanja, a ne za main.css.
Webpack dodaci (plugin)
Webpack ima svoje plugin-e preko kojih može da proširi svoje funkcionalnosti. Listu dostupnih plugin-a za webpack 2 možete pogledati na oficijenoj stranici https://webpack.js.org/plugins/.
ExtractTextWebpackPlugin
Već smo u predthodnom delu videli kako se CSS fajl jednostavno integriše u JavaScript, a ova tehnika je odlična kada želimo da pravimo “reusable” komponente koje bi se više puta ugradjivale u različite delove programa. Medjutim nije dobro primeniti ugradjivanje stilova u javascript za sve stilove aplikacije, jer bi stilovi bili učitani kada i javascript a to je najčešće tek nakon učitavanja DOM-a. To je neprihvatljivo sa stanovišta “user experience”, jer bi korisniku u jednom trenutku bio prikazan nestilizovani sadržaj.
ExtractTextWebpackPlugin je webpack plugin koji omogućava da se obradjeni CSS (transpilovan, minifikovan…) ipak izdvoji u zasebni fajl. Tako izdvojeni CSS fajl može da se jednostavno ugradi u vrh HTML-a kroz link tag, što će nam obezbediti da se stilovi učitaju pre DOM-a. Ovakva tehnika nam omogućava da korisnik odmah nakon učitavanja
DOM-a vidi stilizovan sadržaj.
Instalacija ExtractTextWebpackPlugin
1 |
npm install --save-dev extract-text-webpack-plugin |
1 |
yarn add --dev extract-text-webpack-plugin |
Konfiguracija webpack-a za ExtractTextWebpackPlugin
Nakon instalacije potrebno je ažurirati konfiguracioni fajl webpack.config.js. Prvo je potrebno na vrhu fajla importovati plugin modul:
1 |
const ExtractTextPlugin = require("extract-text-webpack-plugin"); |
Nakon čega je potrebno editovati deo vezan za CSS i zameniti “style-loader” sa pluginom:
1 2 3 4 5 6 7 |
use: ExtractTextPlugin.extract({ fallback: "style-loader", use: [ {loader :'css-loader'}, // Zna da obradi importovanje ako se koristi ključna reč import i da prebaci CSS u običan string da bi JavaScript mogao da manipuliše sa njim {loader :'postcss-loader'} // Primenjuje sve funkcionalnosti koje dolaze sa PostCSS loader-om ] }) |
Takodje je slično potrebno uraditi kod SCSS dela:
1 2 3 4 5 6 7 8 9 |
test: /\.scss$/, use: ExtractTextPlugin.extract({ fallback: "style-loader", use: [ {loader : 'css-loader'}, // Zna da obradi importovanje ako se koristi ključna reč import i da prebaci CSS u običan string da bi JavaScript mogao da manipuliše sa njim {loader : 'postcss-loader'}, // Primenjuje sve funkcionalnosti koje dolaze sa PostCSS loader-om {loader : 'sass-loader'} // Transpilira Sass u CSS ] })} |
Pored navedenih izmena potrebno je dodati novu sekciju u webpack.config.js vezanu za plugin, koja “obaveštava” webpack koji plugin koristimo i u koji fajl želimo da izdvojimo CSS iz JavaScript fajla bundle.js.
1 2 3 |
plugins: [ new ExtractTextPlugin("styles.css"), ] |
Nakon startovanja webpack-a, pojaviće se novi fajl “style.css”, koji je potrebno ubaciti u našu HTML stranicu:
1 |
<link rel="stylesheet" href="styles.css"> |
HtmlWebpackPlugin
Namena ovog dodatka je da generiše novi HTML fajl na osnovu nekog template-a, koji se dinamički povezuje sa generisanim “bundle.js” fajlom.
Instalacija HtmlWebpackPlugin
1 |
npm install html-webpack-plugin --save-dev |
1 |
yarn add html-webpack-plugin --dev |
Konfiguracija webpacka za HtmlWebpackPlugin
Potrebno je na vrhu fajla importovati zahtevani plugin:
1 |
const HtmlWebpackPlugin = require('html-webpack-plugin'); |
A zatim u delu za vezan za plugine pored prethodno ubačenog ExtractTextPlugin, dodati i ovaj:
1 2 3 |
plugins: [ new HtmlWebpackPlugin() ] |
Nakon startovanja webpacka-a on će generisati novi HTML fajl index.html, koji u sebi već ima ubačene veze do dva takodje generisana javascript fajla:
1 2 3 4 5 6 7 8 9 10 11 |
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <link href="styles.css" rel="stylesheet"></head> <body> <script type="text/javascript" src="index.bundle.js"></script> <script type="text/javascript" src="druga.bundle.js"></script> </body> </html> |
Generisanje HTML-a na osnovu template-a
Prethodno generisani HTML fajl osim što ima inkludovane skripte i nije naručito interesantan. Stoga je potrebno napraviti HTML template sa odgovarajućim sadržajem, koji ćemo da iskoristimo za generisanje novog HTML-a. Povezivanje templeta i odgovarajućeg JavaScript fajla se vrši definisanjem objekta koji se prosledjuje kao parametar HtmlWebpackPlugin pluginu. Kroz taj objekat se definiše ime i putanja HTML template-a koji se koristi, kao i naziv odgovarajućeg bundlovanog JavaScript dela.
1 2 3 4 5 |
new HtmlWebpackPlugin({ filename: 'index.html', // Naziv HTML template template: 'src/index.html', // Putanja do template chunks: ['indexJS'] // Naziv jednog od dva JavaScript-a definisana u "entry" sekciji }) |
Medjutim najverovatnije je da se aplikacija ne sastoji samo iz jednog HTML fajla tj. stranice, nego iz više njih, stoga je potrebno da drugačije organizujemo projekat i promenimo konfiguraciju plugina.
Napravićemo novi folder u okviru src foldera koji ćemo da nazovemo “views”, a koji je zadužen da se u njemu čuvaju template HTML stranice. Unutar njega možemo da napravimo dve HTML stranice koje ćemo nazvati “index.html” i “druga.html”. Nakon ovoga je potrebno da promenimo trenutnu konfiguraciju za HtmlWebpackPlugin u okviru webpack.config.js fajla:
1 2 3 4 5 6 7 8 9 10 |
new HtmlWebpackPlugin({ filename: 'index.html', template: 'src/views/index.html', chunks: ['indexJS'] }), new HtmlWebpackPlugin({ filename: 'druga.html', template: 'src/views/druga.html', chunks: ['drugaJS'] }) |
Više o ovome pluginu pogledajte na webpack sajtu pod sekcijom “Setting up htmlwebpackplugin”, ili na samoj stranici plugina “Html webpack plugin”, dok se o gotovim templejtima možete pročitati na https://github.com/jaketrent/html-webpack-template.
CleanWebpackPlugin (čišćenje /dist foldera)
Pri svakom menjanju imena fajlova koji treba da se generišu, u folderu za produkciju će se generisati novi preimenovani fajlovi. Medjutim fajl sa starim nazivom neće nestati, iako je nepotreban, tako da se pravi gomila nepotrebnih fajlova u produkcijskom folderu. Iz toga razloga nam je potrebno brišemo stare fajlove pre generisanja novih, pa ćemo da koristimo plugin pod nazivom “clean-webpack-plugin”. Ovaj dodatak pri svakom stratovanju webpack-a, prvo briše sadržaj celog produkciskog foldera “dist”, a zatim generiše nove fajlove u njega.
Instalacija CleanWebpackPlugin
1 |
npm install clean-webpack-plugin --save-dev |
1 |
yarn add clean-webpack-plugin --dev |
Konfiguracija CleanWebpackPlugin
Prvo je potrebno na vrhu stranice importovati instalirani plugin
1 |
const CleanWebpackPlugin = require('clean-webpack-plugin'); |
A zatim u delu za vezan za plugine dodati i ovaj:
1 2 3 |
plugins: [ new CleanWebpackPlugin(['dist']) // Definiše se folder koji se briše ] |
Više o ovome plugin-u pogledajte na oficijelnoj stranici pod sekcijom Cleaning up the dist folder.
CommonsChunkPlugin
Ukoliko aplikacija ima dosta “third party dependencies”, preporuka vezana za optimizaciju aplikacije je da se “third party dependencies” izdvoje u tzv. “Vendor Chunk”. Ovo se radi iz razloga što želimo da browseru omogućimo da kešira ove podatke jer se oni za razliku od našeg koda retko ažuriraju. Ovaj postupak smo već obradili u sekciji “Definisanje višestrukih ulaznih i generisanje više izlaznih fajlova”, stim što ćemo u sada u drugi bundle file da ga nazovemo “vendor” a u njega ćemo da stavimo sve “third party dependencies” a koji se nalaze u package.json fajlu pod “dependencies”. Više o ovome pročitajte na oficijelnom sajtu u sekciji “explicit-vendor-chunk”.
Primer
U ovome primeru je generisan jednostavni Vue.js projekat webpack-simple, u sklopu koga je “vue” kao third party dependencies.
1 2 3 4 5 6 7 8 |
entry: { app: './src/main.js', vendor: ["vue"] }, output: { path: path.resolve(__dirname, './dist'), filename: "[name].bundle.js", }, |
Prethodni kod će da generiše dva fajla: app.bundle.js i vendor.bundle.js, pa će browser moći da kešira vendor.bundle.js. Medjutim pri ovom postupku se pojavio probem da su sada ta dva fajla u zbiru veća nego početni bundle.js fajl pre razdvajanja. Ovo se dešava jer je u ulaznom fajlu index.js na vrhu fajla “ručno” importvan neki od “third party dependencies”, pa je došlo do dupliranja.
Rešenje problema dupliranja je plugin pod nazivom “CommonsChunkPlugin”.
Konfiguracija CommonsChunkPlugin
Ovaj plugin je već ugradjen u sam webpack stoga ga ne treba instalirati, potrebno ga je samo konfigurisati.
1 2 3 |
new webpack.optimize.CommonsChunkPlugin({ names: ["vendor"] }) |
Ovakva konfiguracija govori webpack-u da proveri da li unutar “entry” fajlova (u ovome slučjau “app” i “vendor”) postoji neki paket koji se nalazi u oba fajla, i ako postoji da ga stavi samo u onaj pod nazivom “vendor”.
UPDATE:
Webpack 4 je ukinuo “CommonsChunkPlugin” a umesto njega koristi druge dve opcije “optimization.splitChunks” i “optimization.runtimeChunk”, više o ovome pogledajte u članku “RIP CommonsChunkPlugin”.
UglifyjsWebpackPlugin
Glavna namena ovoga plugina je minifikacija JavaScript-a.
Instalacija
1 |
npm install --save-dev uglifyjs-webpack-plugin |
1 |
yarn add --dev uglifyjs-webpack-plugin |
Konfiguracija
Da možemo da konfigurišemo, prvo je potrebno da importujemo plugin na vrhu webpack.config.js fajl.
1 |
const UglifyJsPlugin = require('uglifyjs-webpack-plugin') |
Nakon čega možemo u sekciji zaduženoj za plugine dodamo i ovaj plugin:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
new UglifyJsPlugin({ test: /\.js($|\?)/i, uglifyOptions: { debug: true, minimize: true, sourceMap: false, output: { comments: false }, compress: { warnings: false } } }) |
Prethodna konfiguracija će biti stalno aktivna, medjutim šta ako želimo da se minifikuje samo kada se build-uje za produkciju – onda se koristi uslov:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
if (process.env.NODE_ENV.trim() === 'production') { module.exports.plugins = (module.exports.plugins || []).concat([ new UglifyJsPlugin({ test: /\.js($|\?)/i, uglifyOptions: { debug: true, minimize: true, sourceMap: false, output: { comments: false }, compress: { warnings: false } } }) ]) } |
Više o ovome pluginu pročitajte na oficijelnom sajtu u sekciji UglifyjsWebpackPlugin, a o setovanju process.env.NODE_ENV promenjive pogledajte u istoimenoj sekciji “process.env.NODE_ENV”
Dinamičko učitavanje JavaScript modula
Dinamičko učitavanje modula podrazumeva da se u okviru jednog JavaScript fajla, dinamički (nakon akcije korisnika) poziva izvršavanje drugog JavaScript fajla. Ceo sistem se zasniva na specifičnom delu koda koji se nalazi u okviru početne stranice iz koje se zahteva učitavanje novog modula, a bavi se dinamičim učitavanjem modula:
1 |
System.import("relativnaPutanja_do_modulaKojiSeUcitava"); |
Nakon izvršavanja ovog dela koda u stranicu se učitava i dodani JavaScript modul.
Sintaksa “System.import()” nam omogućava dinamičko i kondiciono učitavanje jednog modula. Pisana je prema ES2015 standardu i bazirana je na Promisima stoga vraća Promise.
1 2 3 4 5 |
System.import("./dinamickiModul") .then(module => console.log(module)) .catch(error => { console.log("Imamo problema sa učitavanjem prosledjenog modula kroz Promise"); }); |
Više o ovome možete pročitati u specifikaciji ES2015 standarda.
process.env.NODE_ENV
Kada koristite neki od framework-a tokom razvoja aplikacije, framework je stalno na oprezu i vrši česte provere da bi vam pružio potrebna upozorenja kako bi vam pomogao u pogledu uobičajenih grešaka i zamki. Međutim, to postaje beskorisno u krajnjoj produkciji a povećava veličinu aplikacije pri učitavanju i samo opterećuje aplikaciju. Produkciski režim u framework-u se definiše preko promenjive “process.env.NODE_ENV”, stoga je bitno da u wepack-u definišemo ovu promenjivu kada želimo da pošeljemo aplikaciju na produkciju. Za ovu namenu se koristi webpack-ov DefinePlugin, jer on omogućava da se setuju promenjive operativnog sistema. Da bi mogli da koristimo ovaj webpack-ov plugin u okviru “webpack.config.js” fajla, neophodnoje da ga prvo importujemo u fajl:
1 |
const path = require ('path'); |
Zatim da preko ovog plugina da definišemo promenjivu ‘process.env.NODE_ENV’:
1 2 3 4 5 |
new webpack.DefinePlugin({ 'process.env': { NODE_ENV: '"production"' } }) |
Medjutim nepotrebno je da i pri radu u lokalu sa dev serverom, promenjiva “process.env.NODE_ENV” bude setovana na “production”, stoga je potrebno promenjivu definisati programabilno. To se postiže tako što drugačije konfigurišemo DefinePlugin:
1 2 3 |
new webpack.DefinePlugin({ 'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV) }) |
Sa prethodnim kodom smo rekli webpack-u da će sistemska promenjiva “process.env.NODE_ENV” biti ista kao što je promenjiva u našem lokalnom sistemu. A definisanje te promenjive u lokalnom sistemu se vrši konfiguracijom kroz “scripts” svojstvo u okviru fajla “package.json”:
1 2 3 4 |
"scripts": { "dev" : "SET NODE_ENV=development & webpack-dev-server", "build" : "SET NODE_ENV=production & webpack" }, |
Ukoliko ne koristimo “UglifyjsWebpackPlugin” možemo da monifikujemo kod produkcije dodavanjem flag-a -p:
1 2 3 4 |
"scripts": { "dev" : "SET NODE_ENV=development & webpack-dev-server", "build" : "SET NODE_ENV=production & webpack -p" } |
Završna verzija projekta uz članak
Svi primeri iz članka objedinjeni su u jedan projekat. Završna verzija projekta je objavljena na GitHub-u
https://github.com/choslee/webprogramiranje-webpackOsnove.
webpack.config.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 |
const path = require ('path'); const ExtractTextPlugin = require("extract-text-webpack-plugin"); const HtmlWebpackPlugin = require('html-webpack-plugin'); const CleanWebpackPlugin = require('clean-webpack-plugin'); const BrowserSyncPlugin = require('browser-sync-webpack-plugin'); const UglifyJsPlugin = require('uglifyjs-webpack-plugin'); const webpack = require('webpack'); module.exports = { entry: { indexJS: "./src/js/index.js", drugaJS: "./src/js/druga.js", vendor: ["jquery"] }, output: { path: path.resolve(__dirname, "dist"), // Putanja fajla koji se generiše filename: "[name].bundle.js" }, devtool: 'inline-source-map', devServer: { contentBase: './dist' }, module: { rules: [ { test: /\.scss$/, use: ExtractTextPlugin.extract({ fallback: "style-loader", use: [ {loader: 'css-loader', options: {sourceMap: true}}, // translates CSS into CommonJS {loader: 'postcss-loader', options: {sourceMap: true}}, // postcss plugin loader {loader: 'sass-loader', options: {sourceMap: true}} // compiles Sass to CSS ] }) }, { test: /\.js$/, exclude: /node_modules/, loader: "babel-loader" }, { test: /\.(png|jpe?g|gif|svg)$/, use: [ { loader: 'url-loader', options: { limit: 40000 } // Convert images < 40k to base64 strings }, { loader: 'image-webpack-loader',} ] }, { test: /\.js$/, exclude: /node_modules/, loader: "eslint-loader", options: { // eslint options (if necessary) } } ] }, plugins: [ new ExtractTextPlugin("styles.css"), new HtmlWebpackPlugin({ filename: 'index.html', template: 'src/views/index.html', chunks: ['indexJS', "vendor"] }), new HtmlWebpackPlugin({ filename: 'druga.html', template: 'src/views/druga.html', chunks: ['drugaJS', "vendor"] }), new CleanWebpackPlugin(['dist']), new BrowserSyncPlugin({ host: 'localhost', port: 3000, // url adresa za vreme developmenta http://localhost:3000/ proxy: 'http://localhost:8080/' // proxy endpoint WebpackDev Servera na http://localhost:8080 }, { reload: false // Sprečava da BrowserSync reloaduje stranu, da bi to uradio WebpackDev Server } ), new webpack.DefinePlugin({ 'process.env.NODE_ENV': JSON.stringify("process.env.NODE_ENV") }), new webpack.optimize.CommonsChunkPlugin({ names: ["vendor"], minChunks: Infinity }), ] }; if (process.env.NODE_ENV.trim() === 'production') { module.exports.plugins = (module.exports.plugins || []).concat([ new UglifyJsPlugin({ test: /\.js($|\?)/i, uglifyOptions: { debug: true, minimize: true, sourceMap: false, output: { comments: false }, compress: { warnings: false } } }) ]) } |
package.json
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
{ "name": "js-proba-webpack", "version": "1.0.0", "description": "Proba instaliranja webpacka", "main": "index.js", "scripts": { "dev": "SET NODE_ENV=development & webpack-dev-server", "build": "SET NODE_ENV=production & webpack" }, "author": "Dragoljub Zivanovic", "license": "ISC", "devDependencies": { "autoprefixer": "^7.1.6", "babel-core": "^6.26.0", "babel-loader": "^7.1.2", "babel-preset-env": "^1.6.1", "browser-sync": "^2.18.13", "browser-sync-webpack-plugin": "^1.2.0", "clean-webpack-plugin": "^0.1.17", "css-loader": "^0.28.7", "eslint": "^4.12.0", "eslint-loader": "^1.9.0", "extract-text-webpack-plugin": "^3.0.2", "file-loader": "^1.1.5", "html-webpack-plugin": "^2.30.1", "image-webpack-loader": "^3.4.2", "node-sass": "^4.5.3", "postcss-cssnext": "^3.0.2", "postcss-loader": "^2.0.8", "rucksack-css": "^1.0.2", "sass-loader": "^6.0.6", "style-loader": "^0.19.0", "stylelint": "^8.2.0", "stylelint-webpack-plugin": "^0.9.0", "uglifyjs-webpack-plugin": "^1.1.1", "url-loader": "^0.6.2", "webpack": "^3.8.1", "webpack-dev-server": "^2.9.4" }, "dependencies": { "jquery": "^3.2.1" } } |