webpack
As developer you want to reuse existing code.
As with node.js and web all files are already in the same language, but it is extra work to use your code with the node.js module system and the browser.
The goal of webpack
is to bundle CommonJs (and AMD) modules into javascript files which can be loaded by <script>
-tags.
Simply concatenating all required files has a disadvantage: many code to download (and execute) on page load.
Therefore webpack
uses the require.ensure
function (CommonJs/Modules/Async/A) to split your code automatically into multiple bundles which are loaded on demand.
This happens mostly transparent to the developer.
Dependencies are resolved for you.
The result is a smaller initial code download which results in faster page load.
Another common thing in web development is that some files have to be preprocessed before send to the client (ex. template files).
This introduce more complexity to the compile step.
webpack
supports loaders which process files before including them.
You as developer can use such files like any other module.
TL;DR
- bundle CommonJs and/or AMD modules for browser
- reuse server-side code (node.js) on client-side
- create multiple files which are loaded on demand (faster page load in big webapps or on mobile connections)
- dependencies managed for you, on compile time (no resolution on runtime needed)
- loaders can preprocess files
Quick start guide
var moduleA = require("module/file");
var moduleB = require("./relativeFile");
var moduleC = require("../stuff/cup.coffee");
function getTemplate(name) {
return require("./templates/" + name + ".jade");
}
require("bootstrap/less/bootstrap.less");
npm install webpack -g
webpack lib/yourEntryModule.js output/bundle.js
Goals
- make node.js and browser development similar
- minimize code size (mobile connection)
- minimize code size on initial download
- download code only on demand
- require minimal configuration, but offer a maximum
- load polyfills for node-specific things if used
- offer replacements for node buildin libraries
- support npm and jam
Examples
See example webapp.
More examples.
Simple Example
var b = require("./b");
b.stuff("It works");
exports.stuff = function(text) {
console.log(text);
}
are compiled to (reformatted)
()
({
0: function(module, exports, require) {
var b = require(1);
b.stuff("It works");
},
1: function(module, exports, require) {
exports.stuff = function(text) {
console.log(text)
}
}
})
Code Splitting
Example
var a = require("a");
var b = require("b");
require.ensure(["c"], function(require) {
require("b").xyz();
var d = require("d");
});
File 1: web.js
- code of that file
- code of module a and dependencies
- code of module b and dependencies
File 2: 1.web.js
- code of module c and dependencies (but code is not used)
- code of module d and dependencies
Initially only web.js
is included (and loaded) by your application.
1.web.js
is loaded when the call to require.ensure
happens.
After the second bundle (1.web.js
) is loaded, the callback function will be invoked.
See details for exact output.
See more examples.
Reusing node.js code
webpack
was built to support most of the code that was coded for node.js environment.
For example this works out of the box:
require("./templates/" + templateName);
require(condition ? "moduleA" : condition2 ? "moduleB" : "./localStuff");
function xyz(require) { require("text"); } xyz(function(a) { console.log(a) });
var r = require; r("./file");
with warningfunction xyz(require) { require("./file"); } xyz(require);
with warningtry { require("missingModule"); } catch(e) { console.log("missing") }
with warningvar require = function(a) { console.log(a) }; require("text");
if(condition) require("optionalModule")
with warning if missing
Browser replacements
Somethings it happens that browsers require other code than node.js do.
webpack
allow module developers to specify replacements which are used in the compile process of webpack
.
Modules in web_modules
and jam
replace modules in node_modules
.
filename.web.js
replaces filename.js
when required without file extension.
in options: alias: { "http": "http-browserify" }
in shell: --alias http=http-browserify
Contexts
If the required module is not known while compile time we get into a problem.
A solution is the method require.context
which takes a directory as parameter
and returns a function which behaves like the require
function issued from a file
in this directory (but only if used for files in that directory).
The whole directory is included while compiling, so you have access to all files in it.
Example
We have a directory full of templates, which are compiled javascript files.
A template should be loaded by template name.
var requireTemplate = require.context("./templates");
function getTemplate(templateName) {
return requireTemplate("./" + templateName);
}
In addition to that webpack
uses the require.context
function automatically
if you use variables or other not parse-able things in the require
function.
That means the following code behaves like the above:
function getTemplate(templateName) {
return require("./templates/" + templateName);
}
See details for complete example.
When try to store the require
function in another variable or try to pass it as parameter,
webpack
convert it to a require.context(".")
to be compatible.
There is a warning emitted in this case.
Warning: The complete code in the directory are included. So use it carefully.
Loaders
You can use a syntax for loader plugins to preprocess files before emitting javascript code to the bundle.
The following example loads the raw content of a file with the raw
loader:
var content = require("raw!./file.txt");
Multiple loader plugins can be prepended by separating them with !
.
The loader plugins are resolved like in normal require
call but with different default extension.
The raw
loader plugin is looked up at modules raw-webpack-web-loader
, raw-webpack-loader
, raw-web-loader
, raw-loader
, raw
and the following files are looked up: index.webpack-web-loader.js
, index.webpack-loader.js
, index.web-loader.js
, index.loader.js
, index
, index.js
.
Note that the web-
versions are omitted if loaders are used in node.js.
See example.
The following loaders are included in webpack:
raw
: Loads raw content of a file (as utf-8)json
(default at .json
): Loads file as JSONjade
(default at .jade
): Loads jade template and returns a functioncoffee
(default at .coffee
): Loads coffee-script like javascriptcss
: Loads css file with resolved imports and returns css codeless
: Loads and compiles a less file and returns css codeval
: Excutes code as module and consider exports as javascript codebundle
: Wraps request in a require.ensure
blockfile
: Emits the file into the output folder and returns the (relative) url (file/{ext}
for some extensions)style
: Adds result of javascript execution to DOMscript
: Executes a javascript file once in global context (like in script tag), requires are not parsed. Use this to include a library. ex. require("script!./jquery.min.js")
. This is synchron, so the $
variable is available after require.- (
.css
defaults to style!css
loader, so all css rules are added to DOM) - (
.less
defaults to style!css!val/cacheable!less
loader, so all less rules are added to DOM)
See docs for loader in github repo of the loader.
Bigger list of loaders
TL;DR
var a = require("a");
var b = require("./b");
require = require("enhanced-require")(module);
require.ensure([], function(require) {
var c = require("c");
var packageJson = require("../package.json");
var result = require("./template.jade")(require("./dataFrom.coffee"));
});
... and compile from the shell with:
webpack lib/input.js js/output.js
try --min
to minimize with uglify-js
.
Limitations
require
-function
require
should not be overwritten, except from polyfillrequire.ensure
should not be overwritten or called indirectrequire.context
should not be overwritten or called indirect- the argument to
require.context
should be a literal or addition of multiple literals - An indirect call of
require
should access a file in current directory: This throws an exception: var r = require; r("../file");
The following cases could result in too much code in result file if used wrong:
- indirect call of
require
: var r = require; r("./file");
. It includes the whole directory. require.context
. It includes the whole directory.- expressions in require arguments:
require(variable)
. It includes the whole directory. (except from ?:
-operator require(condition ? "a" : "b")
) - the function passed to
require.ensure
is not inlined in the call. Only requires in inlined function move into the second bundle.
node.js specific modules
As node.js specific modules like fs
will not work in browser they are not included (by default) and cause an exception.
You should replace them by own modules if you want to use them.
For some simple modules are replacements included in webpack
.
Expensive replacements are not needed by everyone, so that are not included by default.
You need to specify --alias [module]=[replacement]
to use them.
A warning saying that some module is missing is emitted in the case you use it without providing a replacement.
Some credit goes to the browserify contributors, you can use replacements provided by them.
Included simple replacements:
assert
: copy of node.js' version, small changebuffer
: copy of node-browserify's versionbuffer_ieee754
: copy of node-browserify's versionchild_process
: disabledevents
: copy of node.js' versionpath
: copy of node.js' versionpunycode
: copy of node.js' version, one line removed (http://mths.be/punycode by @mathias)querystring
: copy of node.js' versionstring_decoder
: copy of node.js' versionurl
: copy of node.js' versionutil
: copy of node.js' version
Here is a list of possible useful replacements: (intentionally not by default)
http=http-browserify
vm=vm-browserify
- TODO provide some more replacements
Usage
Shell
webpack
offers a command line interface:
after npm install webpack -g
you can use the webpack
command
if invoked without arguments it prints a usage:
Usage: webpack <options> <input> <output>
Options:
--min Minimize it with uglifyjs [boolean] [default: false]
--filenames Output Filenames Into File [boolean] [default: false]
--options Options JSON File [string]
--public-prefix Path Prefix For JavaScript Loading [string]
--libary Stores the exports into this variable [string]
--colors Output Stats with colors [boolean] [default: false]
--single Disable lazy loading [boolean] [default: false]
--json Output Stats as JSON [boolean] [default: false]
--by-size Sort modules by size in Stats [boolean] [default: false]
--verbose Output dependencies in Stats [boolean] [default: false]
--alias Set a alias name for a module. ex. http=http-browserify [string]
--debug Prints debug info to output files [boolean] [default: false]
--watch Recompiles on changes (except loaders) [boolean] [default: false]
--watch-delay Timeout to wait for the last change [string]
--progress Displays a progress while compiling [boolean] [default: false]
Programmatically Usage
webpack(context, moduleName, [options], callback)
webpack(absoluteModulePath, [options], callback)
options
You can also save this options object in a JSON file and use it with the shell command.
{
output: "out/file.js",
outputDirectory: "out/dir",
outputPostfix: ".chunk.js",
context: "/home/node/stuff",
ouputJsonpFunction: "myJsonp",
publicPrefix: "http://static.url.com/asserts/",
libary: "mylib",
includeFilenames: true,
single: false,
debug: true,
watch: true,
watchDelay: 1000,
events: new EventEmitter(),
noWrite: true,
parse: {
overwrites: {
"myglobal": "modulename-of-myglobal"
},
}
resolve: {
paths: ["/my/absolute/dirname"],
modulesDirectorys: ["xyz_modules", "node_modules"],
alias: {
"old-module": "new-module"
},
extensions: ["", ".www.js", ".js"],
loaderExtensions: [".loader.js", ".www-loader.js", "", ".js"],
loaderPostfixes: ["-loader", "-xyz", ""],
loaders: [{test: /\.generator.js/, loader: "val"}],
postprocess: {
normal: [function(filename, callback) {
if(/\.exclude\.[^\\\/]*$/.test(filename))
return callback(new Error("File is excluded"));
callback(null, filename);
}],
context: [],
}
}
postLoaders: [{test: /\.export.js$/, loader: "export"}],
preLoaders: [{test: /\.txt$|\.html$/, loader: "normalizeNLs"}],
}
callback
function(err, source / stats)
source
if options.output
is not set (DEPRECATED)
else stats
as json:
{
hash: "52bd9213...38d",
time: 1234,
chunkCount: 2,
modulesCount: 10,
modulesIncludingDuplicates: 10,
modulesFirstChunk: 3,
fileSizes: {
"output.js": 1234,
"1.output.js": 2345
},
warnings: [ "Some warning" ],
errors: [ "Some error" ],
fileModules: {
"output.js": [
{ id: 0, size: 123, filename: "/home/.../main.js", reasons: [
{ type: "main" }
]},
{ id: 1, size: 234, filename: "...", reasons: [
{ type: "require",
count: 2,
filename: "/home/.../main.js",
}
]},
...
],
"1.output.js": [...]
},
subStats: [...],
}
with grunt
see grunt-webpack.
Bonus features
File hash
You can use [hash]
in publicPrefix
, output
, outputDirectory
, outputPostfix
and in the shell parameters.
webpack
will replace it with a hash of your files, when writing.
From shell
Combine the options --colors --watch --progress
to get a pretty shell compilation.
Comparison
Feature
|
webpack/ webpack
|
jrburke/ requirejs
|
substack/ node- browserify
|
---|
sync require
|
yes
|
only wrapped
|
yes
|
sync require.resolve
|
yes
|
no
|
yes
|
define
|
yes
|
yes
|
no
|
async require
|
yes
|
yes
|
no
|
single bundle
|
yes
|
yes
|
yes
|
load each file seperate
|
no
|
yes
|
no
|
multiple bundles, Code Splitting
|
yes
|
no
|
no
|
indirect require
var r = require; r("./file");
|
in directory
|
yes (not bundled)
|
no
|
concat in require
require("./fi" + "le")
|
yes
|
yes (not bundled)
|
no
|
variables in require (local)
require("./templates/"+template)
|
yes, complete directory included
|
yes (not bundled)
|
no
|
variables in require (global)
require(moduleName)
|
no
|
yes (not bundled)
|
no
|
requirable files
|
filesystem
|
web
|
filesystem
|
plugins
|
no
|
no
|
yes
|
loaders
|
yes
|
yes
|
no
|
watch mode
|
yes
|
not needed
|
yes
|
debug support
|
yes
|
yes
|
yes
|
node buildin libs
require("path");
|
yes
|
no
|
yes
|
process polyfill
|
yes, on demand
|
no
|
yes, ever
|
global to window mapping
|
yes
|
no
|
no
|
node browser replacements
|
web_modules and .web.js
by alias config option
|
by alias config option
|
by alias config option
|
Tests
You can run the unit tests with npm test
.
You can run the browser tests:
cd test/browsertests
node build
and open test.html
in browser. There must be several OKs in the file, no FAIL and no RED boxes.
Contribution
You are welcome to contribute by writing issues or pull requests.
It would be nice if you open source your own loaders or webmodules. :)
You are also welcome to correct any spelling mistakes or any language issues, because my english is not perfect...
Future plans
- more and better polyfills for node.js buildin modules
- cache in folder and allow reuseing it
- write it into the wiki if you have more ideas...
License
Copyright (c) 2012 Tobias Koppers
MIT (http://www.opensource.org/licenses/mit-license.php)
Dependencies