Bundeling js files
Have fun!
What is webpack
From the docs:
Webpack is a popular module bundler, a tool for bundling application source code in convenient chunks and for loading that code from a server into a browser.
Websites and applications contain
We need
This results in larger amounts of code on the client side. Generally speaking, utilising a module system should enhance not only the development but also the user experience. If you happen to be working with a large codebase or just researching, you will no doubt understand it is critical to keep your code organised. One of the best ways to handle the organisation of JavaScript it to break your code into modules. When it comes to module systems there are a few popular ones which you might of either used or heard about like Browserify or RequireJS. Both of these are extremely helpful and do a great job but webpack does some extra radness.
Webpack enables professional and maintainable JavaScript programming
Webpack enables us to write JavaScript code into maintainable modules while taking care of packaging and bundeling it for runtime use.
Webpack supports:
Webpack roams over your application source code, looking for import statements, building a dependency graph, and emitting one (or more) bundles.
With plugin "loaders" Webpack can preprocess and minify different non-JavaScript files such as TypeScript, SASS, and LESS files.
A bundle is a JavaScript file that incorporate assets that belong together and should be served to the client in a response to a single file request.
A bundle can include JavaScript, CSS styles, HTML, and almost any other kind of file.
Webpack works with a JavaScript configuration file(s)
webpack.config.js
You just have to call it:
webpack
Defining the least supported setup
npm install webpack --save
webpack ./test.js bundle.js
Version: webpack 1.12.11 Time: 51ms Asset Size Chunks Chunk Names bundle.js 1.42 kB 0 [emitted] main chunk {0} bundle.js (main) 28 bytes [rendered] [0] ./test.js 28 bytes {0} [built]
Create the inital setup and test it
Webpack can be configured to start with a setup javascript file
The config file is not JSON but a JavaScript file so it can contain executable script. Lets look at an example:
module.exports = { entry: "./entry.js", output: { path: __dirname, filename: "bundle.js" }, module: { loaders: [ { test: /\.css$/, loader: "style!css" } ] } };
Some handy configuration options to remember
webpack // for building once for development webpack -p // for building once for production (minification) webpack --watch // for continuous incremental build webpack -d // for including source maps webpack --colors // for making things pretty
Create the config file setup and test it
Split and merge configurations
More complex scenario's require different setups:
npm i webpack-merge --save-dev
Configs are JavaScript files, so we can code in them
var output = merge(object1, object2, object3, ...); // smarter merging for loaders, see below var output = merge.smart(object1, object2, object3, ...);
Webpack-merge tries to be smart about merging loaders when merge smart is used.
Loaders with matching tests will be merged into a single loader value.
More info: https://www.npmjs.com/package/webpack-merge
Merging configurations in
Use switches to set different settings for different environments, export the merge result of the common and specific configurations
Example
const merge = require('webpack-merge'); const common = { entry: { app: 'app' }, output: { path: 'build', filename: '[name].js' }, plugins: [ ... ] }; // Detect how npm is run and branch based on that // u can use npm run 'script' to set this value switch(process.env.npm_lifecycle_event) { case 'build': module.exports = merge(common, { /*specifics*/}); break; default: module.exports = merge(common, { /*specifics*/}); }
Use a common setting for the basic shared configuration
Use a configuration file for each specific settings, for example:
Merge the common configuration into the specific configuration. Example:
module.exports = require('./config/webpack.dev.js');
var webpack = require('webpack'); module.exports = { entry: { 'polyfills': './src/polyfills.ts', 'vendor': './src/vendor.ts', 'app': './src/main.ts' }, /* other settings and common config */ resolve: { ... }, module: { ... }, plugins: [ ... ] };
var webpackMerge = require('webpack-merge'); var commonConfig = require('./webpack.common.js'); module.exports = webpackMerge(commonConfig, { devtool: 'cheap-module-eval-source-map', output: { path: helpers.root('dist'), publicPath: 'http://localhost:8080/', filename: '[name].js', chunkFilename: '[id].chunk.js' }, plugins: [ ... ], devServer: { ... } });
Merge multiple configurations
Using webpack-validator
To make it easier to develop our configuration, we can integrate a tool known as webpack-validator to our project. It will validate the configuration against a schema and warn if we are trying to do something not sensible. This takes some pain out of learning and using Webpack.
Install:
npm i webpack-validator --save-dev
Integrating it to our project is straight-forward:
webpack.config.js
const path = require('path'); const HtmlWebpackPlugin = require('html-webpack-plugin'); const merge = require('webpack-merge'); const validate = require('webpack-validator'); ... module.exports = validate(config);
If you break your Webpack configuration somehow after doing this, the validator will likely notice and give you a nice validation error to fix.
Test a configuration with webpack-validator, you're up next bro
File preprocessors
Loaders allow you to preprocess files as you require() or “load” them. Loaders can transform files from a different language like
Loaders even allow you to do things like require() css files right in your JavaScript!
To tell Webpack to transform a module with a loader, you can specify the loader in the module request
var moduleWithOneLoader = require("my-loader!./my-awesome-module");
Or with a relative path
require("./loaders/my-loader!./my-awesome-module");
A lot of prebuilt loaders in the following categories
complete list
npm install typescript-loader --save-dev
module: { loaders: [ { test: /\.ts$/, loader: 'typescript-loader' } ] },
npm install typescript-loader --save-dev
// webpack.config.js module.exports = { module: { loaders: [ { // Use *.json.js extension test: /\.json\.js/, loader: 'tojson' } ] } },
// file: data.json.js var fs = require('fs') var readme = fs.readFileSync(__dirname + '/../../Readme.md', 'utf8') readme = readme.split('\n')[0] // (just grab header for demo) // any other dependencies that are only used in here won't be included in bundle var tape = require('tape') // some random dependency // whatever the value of module.exports is will be serialised to JSON module.exports = { readme: readme, tape: tape, // tape happens to be a function so it won't serialise. random: Math.random(), // will be fixed to whatever value is generated at compile-time }
// file: index.js console.log(require('./data.json.js'))
... /******/ ([ /* 0 */ /***/ function(module, exports, __webpack_require__) { console.log(__webpack_require__(1)) /***/ }, /* 1 */ /***/ function(module, exports) { module.exports = {"readme":"# tojson-loader","random":0.5418716457206756} /***/ } /******/ ]);
Loaders can be also be chained together
require("style-loader!css-loader!less-loader!./my-styles.less");
Loaders can accept query parameters
require("loader?with=parameter!./file");
We do not want to specify loaders in each module request
Apply loaders to different file types in config!
{ module: { loaders: [ { test: /\.coffee$/, loader: "coffee-loader" } ], preLoaders: [ { test: /\.coffee$/, loader: "coffee-hint-loader" } ] } };
For each file, loaders are executed in the following order
preloaders from config loaders from config loaders specified in request (e.g. require('raw!./file.js')) postLoaders from configYou can override the config loader order in the module request to suit special cases.
Disable configured preLoaders
require("!raw!./script.coffee")
Disable all loaders specified in the configuration
require("!!raw!./script.coffee")
Disable configured preLoaders and loaders but not the postLoaders
require("-!raw!./script.coffee")
Using loaders in webpack
You can write your own loaders
A loader is just a file that exports a function. The compiler calls this function and passes the result of the previous loader or the resource file into it
The -this- context of the function is filled-in with useful methods that allow:
An optional SourceMap result (as JSON object) may also be passed.
Errors can be thrown in sync mode or the this.callback can be called with the error.
Sync loader returns results synchronously
module.exports = function(content) { return someSyncOperation(content); };
Async loader returns value through callback
module.exports = function(content) { var callback = this.async(); if(!callback) return someSyncOperation(content); someAsyncOperation(content, function(err, result) { if(err) return callback(err); callback(null, result); }); };
By default the resource file is passed as String(utf-8) to the loader
By setting raw to true the loader is passed the raw Buffer
Every loader is allowed to deliver its result as String or as Buffer
The compiler converts them between loaders
module.exports = function(content) { assert(content instanceof Buffer); return someSyncOperation(content); // return value can be a Buffer too // This is also allowed if loader is not "raw" }; module.exports.raw = true;
If a loader returns result in the pitch method the process skips the remaining loaders continuing with the calls to the more left loaders
It's like the two phases of event bubbling...
a!b!c!resource
When a loader return something in the pitch phase the process continues with the normal phase of the next loader...
module.exports = function(contnt) { return someSyncOp(contnt, this.data.value); }; module.exports.pitch = function(remainReq, preceedReq, data) { if(someCondition()) { // fast exit return "module.exports = require(" + JSON.stringify("-!" + remainReq) + ");"; } data.value = 42; };
Writing a custom loader in webpack
Hot Module Replacement (HMR) exchanges, adds, or removes modules while an application is running without a page reload.
Using Plugins: http://webpack.github.io/docs/using-plugins.html Code Splitting: http://webpack.github.io/docs/code-splitting.html webpack-dev-server: http://webpack.github.io/docs/webpack-dev-server.html
Webpacks adds a small HMR runtime to the bundle, during the build process, that runs inside your app. When the build completes, Webpack does not exit but stays active, watching the source files for changes. If Webpack detects a source file change, it rebuilds only the changed module(s).
Depending on the settings, Webpack will either send a signal to the HMR runtime, or the HMR runtime will poll webpack for changes. Either way, the changed module is sent to the HMR runtime which then tries to apply the hot update. First it checks whether the updated module can self-accept. If not, it checks those modules that have required the updated module.
If these too do not accept the update, it bubbles up another level, to the modules that required the modules that required the changed module. This bubbling-up will continue until either the update is accepted, or the app entry point is reached, in which case the hot update fails.
The app code asks the HMR runtime to check for updates. The HMR runtime downloads the updates (async) and tells the app code that an update is available. The app code asks the HMR runtime to apply updates. The HMR runtime applies the update (sync). The app code may or may not require user interaction in this process (you decide).
Compiler needs to emit the "Update", which contains:
In addition to the normal assets, the compiler needs to emit the "Update" to allow updating the previous version to the current version.
The "Update" contains two parts:
The manifest contains the new compilation hash and a list of all update chunks (2.).
The update chunks contains code for all updated modules in this chunk (or a flag if a module was removed).
The compiler also makes sure that module and chunk ids are consistent between these builds. It uses a "records" json file to store them between builds (or it stores them in memory).
Hot Module Replacement (HMR) exchanges, adds, or removes modules while an application is running without a page reload.
Using Plugins: http://webpack.github.io/docs/using-plugins.html Code Splitting: http://webpack.github.io/docs/code-splitting.html webpack-dev-server: http://webpack.github.io/docs/webpack-dev-server.html
Webpacks adds a small HMR runtime to the bundle, during the build process, that runs inside your app. When the build completes, Webpack does not exit but stays active, watching the source files for changes. If Webpack detects a source file change, it rebuilds only the changed module(s).
Depending on the settings, Webpack will either send a signal to the HMR runtime, or the HMR runtime will poll webpack for changes. Either way, the changed module is sent to the HMR runtime which then tries to apply the hot update. First it checks whether the updated module can self-accept. If not, it checks those modules that have required the updated module.
If these too do not accept the update, it bubbles up another level, to the modules that required the modules that required the changed module. This bubbling-up will continue until either the update is accepted, or the app entry point is reached, in which case the hot update fails.
The app code asks the HMR runtime to check for updates. The HMR runtime downloads the updates (async) and tells the app code that an update is available. The app code asks the HMR runtime to apply updates. The HMR runtime applies the update (sync). The app code may or may not require user interaction in this process (you decide).
Compiler needs to emit the "Update", which contains:
In addition to the normal assets, the compiler needs to emit the "Update" to allow updating the previous version to the current version.
The "Update" contains two parts:
The manifest contains the new compilation hash and a list of all update chunks (2.).
The update chunks contains code for all updated modules in this chunk (or a flag if a module was removed).
The compiler also makes sure that module and chunk ids are consistent between these builds. It uses a "records" json file to store them between builds (or it stores them in memory).
sdsds