Merge 2.x into master

This commit is contained in:
Jan Nicklas 2016-01-20 17:12:01 +01:00 committed by Jan Nicklas
commit 9e8259ae26
97 changed files with 2498 additions and 532 deletions

1
.gitignore vendored
View File

@ -1,2 +1,3 @@
/node_modules/
/dist/
npm-debug.log

View File

@ -1,95 +0,0 @@
{
// From JSHint Default Configuration File
// See http://jshint.com/docs/ for more details
"maxerr" : 50, // {int} Maximum error before stopping
// Enforcing
"bitwise" : true, // true: Prohibit bitwise operators (&, |, ^, etc.)
"camelcase" : false, // true: Identifiers must be in camelCase
"curly" : true, // true: Require {} for every new block or scope
"eqeqeq" : true, // true: Require triple equals (===) for comparison
"forin" : false, // true: Require filtering for..in loops with obj.hasOwnProperty()
"freeze" : true, // true: prohibits overwriting prototypes of native objects such as Array, Date etc.
"immed" : false, // true: Require immediate invocations to be wrapped in parens e.g. `(function () { } ());`
"indent" : 2, // {int} Number of spaces to use for indentation
"latedef" : false, // true: Require variables/functions to be defined before being used
"newcap" : false, // true: Require capitalization of all constructor functions e.g. `new F()`
"noarg" : true, // true: Prohibit use of `arguments.caller` and `arguments.callee`
"noempty" : true, // true: Prohibit use of empty blocks
"nonbsp" : true, // true: Prohibit "non-breaking whitespace" characters.
"nonew" : false, // true: Prohibit use of constructors for side-effects (without assignment)
"plusplus" : false, // true: Prohibit use of `++` & `--`
"quotmark" : false, // Quotation mark consistency:
// false : do nothing (default)
// true : ensure whatever is used is consistent
// "single" : require single quotes
// "double" : require double quotes
"undef" : true, // true: Require all non-global variables to be declared (prevents global leaks)
"unused" : true, // true: Require all defined variables be used
"strict" : true, // true: Requires all functions run in ES5 Strict Mode
"maxparams" : false, // {int} Max number of formal params allowed per function
"maxdepth" : false, // {int} Max depth of nested blocks (within functions)
"maxstatements" : false, // {int} Max number statements per function
"maxcomplexity" : false, // {int} Max cyclomatic complexity per function
"maxlen" : false, // {int} Max number of characters per line
// Relaxing
"asi" : false, // true: Tolerate Automatic Semicolon Insertion (no semicolons)
"boss" : false, // true: Tolerate assignments where comparisons would be expected
"debug" : false, // true: Allow debugger statements e.g. browser breakpoints.
"eqnull" : false, // true: Tolerate use of `== null`
"es5" : false, // true: Allow ES5 syntax (ex: getters and setters)
"esnext" : false, // true: Allow ES.next (ES6) syntax (ex: `const`)
"moz" : false, // true: Allow Mozilla specific syntax (extends and overrides esnext features)
// (ex: `for each`, multiple try/catch, function expression…)
"evil" : false, // true: Tolerate use of `eval` and `new Function()`
"expr" : false, // true: Tolerate `ExpressionStatement` as Programs
"funcscope" : false, // true: Tolerate defining variables inside control statements
"globalstrict" : false, // true: Allow global "use strict" (also enables 'strict')
"iterator" : false, // true: Tolerate using the `__iterator__` property
"lastsemic" : false, // true: Tolerate omitting a semicolon for the last statement of a 1-line block
"laxbreak" : false, // true: Tolerate possibly unsafe line breakings
"laxcomma" : false, // true: Tolerate comma-first style coding
"loopfunc" : true, // true: Tolerate functions being defined in loops
"multistr" : false, // true: Tolerate multi-line strings
"noyield" : false, // true: Tolerate generator functions with no yield statement in them.
"notypeof" : false, // true: Tolerate invalid typeof operator values
"proto" : false, // true: Tolerate using the `__proto__` property
"scripturl" : false, // true: Tolerate script-targeted URLs
"shadow" : false, // true: Allows re-define variables later in code e.g. `var x=1; x=2;`
"sub" : false, // true: Tolerate using `[]` notation when it can still be expressed in dot notation
"supernew" : false, // true: Tolerate `new function () { ... };` and `new Object;`
"validthis" : false, // true: Tolerate using this in a non-constructor function
// Environments
"browser" : true, // Web Browser (window, document, etc)
"browserify" : false, // Browserify (node.js code in the browser)
"couch" : false, // CouchDB
"devel" : true, // Development/debugging (alert, confirm, etc)
"dojo" : false, // Dojo Toolkit
"jasmine" : false, // Jasmine
"jquery" : false, // jQuery
"mocha" : true, // Mocha
"mootools" : false, // MooTools
"node" : true, // Node.js
"nonstandard" : false, // Widely adopted globals (escape, unescape, etc)
"prototypejs" : false, // Prototype and Scriptaculous
"qunit" : false, // QUnit
"rhino" : false, // Rhino
"shelljs" : false, // ShellJS
"worker" : false, // Web Workers
"wsh" : false, // Windows Scripting Host
"yui" : false, // Yahoo User Interface
// Custom Globals
"globals" : {
// jasmine helpers
"expect": false,
"describe": false,
"ddescribe": false,
"beforeEach": false,
"it": false,
"iit": false
}
}

View File

@ -1,30 +1,19 @@
HTML Webpack Plugin
===================
[![npm version](https://badge.fury.io/js/html-webpack-plugin.svg)](http://badge.fury.io/js/html-webpack-plugin) [![Dependency Status](https://david-dm.org/ampedandwired/html-webpack-plugin.svg)](https://david-dm.org/ampedandwired/html-webpack-plugin) [![bitHound Score](https://www.bithound.io/github/ampedandwired/html-webpack-plugin/badges/score.svg)](https://www.bithound.io/github/ampedandwired/html-webpack-plugin) [![Build status](https://travis-ci.org/ampedandwired/html-webpack-plugin.svg)](https://travis-ci.org/ampedandwired/html-webpack-plugin)
===================
[![npm version](https://badge.fury.io/js/html-webpack-plugin.svg)](http://badge.fury.io/js/html-webpack-plugin) [![Dependency Status](https://david-dm.org/ampedandwired/html-webpack-plugin.svg)](https://david-dm.org/ampedandwired/html-webpack-plugin) [![Build status](https://travis-ci.org/ampedandwired/html-webpack-plugin.svg)](https://travis-ci.org/ampedandwired/html-webpack-plugin) [![Windows build status](https://ci.appveyor.com/api/projects/status/github/ampedandwired/html-webpack-plugin?svg=true&branch=master)](https://ci.appveyor.com/project/jantimon/html-webpack-plugin) [![js-semistandard-style](https://img.shields.io/badge/code%20style-semistandard-brightgreen.svg?style=flat-square)](https://github.com/Flet/semistandard)
[![NPM](https://nodei.co/npm/html-webpack-plugin.png?downloads=true&downloadRank=true&stars=true)](https://nodei.co/npm/html-webpack-plugin/)
This is a [webpack](http://webpack.github.io/) plugin that simplifies creation of HTML files to serve your
webpack bundles. This is especially useful for webpack bundles that include
a hash in the filename which changes every compilation. You can either let the plugin generate an HTML file for you or supply
your own template (using [blueimp templates](https://github.com/blueimp/JavaScript-Templates)).
Version 2.x
-----------
HTML Webpack Plugin 2.x is in beta right now however we are planing to release it soon.
Installation:
```shell
$ npm install html-webpack-plugin@2 --save-dev
```
Please take a look at the [HTML Webpack Plugin 2.x readme](https://github.com/ampedandwired/html-webpack-plugin/tree/feature/loaders)
a hash in the filename which changes every compilation. You can either let the plugin generate an HTML file for you, supply
your own template using lodash templates or use your own loader.
Installation
------------
Install the old version of the plugin with npm:
Install the plugin with npm:
```shell
$ npm install html-webpack-plugin --save-dev
$ npm install html-webpack-plugin@2 --save-dev
```
Basic Usage
@ -75,16 +64,17 @@ Allowed values are as follows:
- `title`: The title to use for the generated HTML document.
- `filename`: The file to write the HTML to. Defaults to `index.html`.
You can specify a subdirectory here too (eg: `assets/admin.html`).
- `template`: A html template (supports [blueimp templates](https://github.com/blueimp/JavaScript-Templates)).
- `templateContent`: A html string or a function returning the html (supports [blueimp templates](https://github.com/blueimp/JavaScript-Templates)).
- `template`: Path to the template. Supports loaders e.g. `html!./index.html`.
- `inject`: `true | 'head' | 'body' | false` Inject all assets into the given `template` or `templateContent` - When passing `true` or `'body'` all javascript resources will be placed at the bottom of the body element. `'head'` will place the scripts in the head element.
- `favicon`: Adds the given favicon path to the output html.
- `minify`: `{...} | false` Pass a [html-minifier](https://github.com/kangax/html-minifier#options-quick-reference) options object to minify the output.
- `hash`: `true | false` if `true` then append a unique webpack compilation hash to all
included scripts and css files. This is useful for cache busting.
- `cache`: `true | false` if `true` (default) try to emit the file only if it was changed.
- `showErrors`: `true | false` if `true` (default) errors details will be written into the html page.
- `chunks`: Allows you to add only some chunks (e.g. only the unit-test chunk)
- `excludeChunks`: Allows you to skip some chunks (e.g. don't add the unit-test chunk)
- `chunksSortMode`: Allows to controll how chunks should be sorted before they are included to the html. Allowed values: 'none' | 'default' | {function} - default: 'auto'
- `excludeChunks`: Allows you to skip some chunks (e.g. don't add the unit-test chunk)
Here's an example webpack config illustrating how to use these options:
```javascript
@ -135,7 +125,7 @@ and favicon files into the markup.
```javascript
plugins: [
new HtmlWebpackPlugin({
title: 'Custom template',
title: 'Custom template',
template: 'my-index.html', // Load a custom template
inject: 'body' // Inject all scripts into the body
})
@ -149,13 +139,30 @@ plugins: [
<html>
<head>
<meta http-equiv="Content-type" content="text/html; charset=utf-8"/>
<title>{%= o.htmlWebpackPlugin.options.title %}</title>
<title><%= htmlWebpackPlugin.options.title %></title>
</head>
<body>
</body>
</html>
```
If you already have a template loader, you can use it to parse the template.
```javascript
module: {
loaders: [
{ test: /\.hbs$/, loader: "handlebars" }
]
},
plugins: [
new HtmlWebpackPlugin({
title: 'Custom template using Handlebars',
template: 'my-index.hbs',
inject: 'body'
})
]
```
Alternatively, if you already have your template's content in a String, you
can pass it to the plugin using the `templateContent` option:
```javascript
@ -167,8 +174,8 @@ plugins: [
]
```
You can use the [blueimp template](https://github.com/blueimp/JavaScript-Templates) syntax out of the box.
If the `inject` feature doesn't fit your needs and you want full control over the asset placement use the [default template](https://github.com/ampedandwired/html-webpack-plugin/blob/master/default_index.html)
You can use the lodash syntax out of the box.
If the `inject` feature doesn't fit your needs and you want full control over the asset placement use the [default template](https://github.com/ampedandwired/html-webpack-plugin/blob/master/default_index.ejs)
as a starting point for writing your own.
The `templateContent` option can also be a function to use another template language like jade:
@ -260,3 +267,20 @@ plugins: [
]
```
Events
------
To allow other plugins to alter the html this plugin executes the following events:
* `html-webpack-plugin-before-html-processing`
* `html-webpack-plugin-after-html-processing`
* `html-webpack-plugin-after-emit`
Usage:
```javascript
compilation.plugin('html-webpack-plugin-before-html-processing', function(htmlPluginData, callback) {
htmlPluginData.html += 'The magic footer';
callback();
});
```

23
appveyor.yml Normal file
View File

@ -0,0 +1,23 @@
# appveyor file
# http://www.appveyor.com/docs/appveyor-yml
environment:
matrix:
- nodejs_version: 0.10
- nodejs_version: 0.12
- nodejs_version: 4
version: "{build}"
build: off
deploy: off
matrix:
fast_finish: true
install:
- ps: Install-Product node $env:nodejs_version
- npm install
test_script:
- node --version
- npm --version
- npm test

View File

@ -2,8 +2,8 @@
<html>
<head>
<meta charset="UTF-8">
<title>{%=o.htmlWebpackPlugin.options.title || 'Webpack App'%}</title>
<title><%= htmlWebpackPlugin.options.title %></title>
</head>
<body>
</body>
</html>
</html>

View File

@ -1,18 +0,0 @@
<!DOCTYPE html>
<html{% if(o.htmlWebpackPlugin.files.manifest) { %} manifest="{%= o.htmlWebpackPlugin.files.manifest %}"{% } %}>
<head>
<meta charset="UTF-8">
<title>{%=o.htmlWebpackPlugin.options.title || 'Webpack App'%}</title>
{% if (o.htmlWebpackPlugin.files.favicon) { %}
<link rel="shortcut icon" href="{%=o.htmlWebpackPlugin.files.favicon%}">
{% } %}
{% for (var css in o.htmlWebpackPlugin.files.css) { %}
<link href="{%=o.htmlWebpackPlugin.files.css[css] %}" rel="stylesheet">
{% } %}
</head>
<body>
{% for (var chunk in o.htmlWebpackPlugin.files.chunks) { %}
<script src="{%=o.htmlWebpackPlugin.files.chunks[chunk].entry %}"></script>
{% } %}
</body>
</html>

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB

60
examples/appcache/dist/bundle.js vendored Normal file
View File

@ -0,0 +1,60 @@
/******/ (function(modules) { // webpackBootstrap
/******/ // The module cache
/******/ var installedModules = {};
/******/ // The require function
/******/ function __webpack_require__(moduleId) {
/******/ // Check if module is in cache
/******/ if(installedModules[moduleId])
/******/ return installedModules[moduleId].exports;
/******/ // Create a new module (and put it into the cache)
/******/ var module = installedModules[moduleId] = {
/******/ exports: {},
/******/ id: moduleId,
/******/ loaded: false
/******/ };
/******/ // Execute the module function
/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
/******/ // Flag the module as loaded
/******/ module.loaded = true;
/******/ // Return the exports of the module
/******/ return module.exports;
/******/ }
/******/ // expose the modules object (__webpack_modules__)
/******/ __webpack_require__.m = modules;
/******/ // expose the module cache
/******/ __webpack_require__.c = installedModules;
/******/ // __webpack_public_path__
/******/ __webpack_require__.p = "";
/******/ // Load entry module and return exports
/******/ return __webpack_require__(0);
/******/ })
/************************************************************************/
/******/ ([
/* 0 */
/***/ function(module, exports, __webpack_require__) {
__webpack_require__(1);
var h1 = document.createElement('h1');
h1.innerHTML = 'Hello world!';
document.body.appendChild(h1);
/***/ },
/* 1 */
/***/ function(module, exports) {
// removed by extract-text-webpack-plugin
/***/ }
/******/ ]);

1
examples/appcache/dist/index.html vendored Normal file
View File

@ -0,0 +1 @@
<!doctype html><html lang="en" manifest="manifest.appcache"><head><meta charset="utf-8"><title>Example template</title><meta name="viewport" content="width=device-width,initial-scale=1"><link href="styles.css" rel="stylesheet"></head><body><img src="0714810ae3fb211173e2964249507195.png"><script src="bundle.js"></script></body></html>

View File

@ -0,0 +1,9 @@
CACHE MANIFEST
# 9e067d48ea3de65b9733
0714810ae3fb211173e2964249507195.png
bundle.js
styles.css
NETWORK:
*

3
examples/appcache/dist/styles.css vendored Normal file
View File

@ -0,0 +1,3 @@
body {
background: snow;
}

4
examples/appcache/example.js Executable file
View File

@ -0,0 +1,4 @@
require('./main.css');
var h1 = document.createElement('h1');
h1.innerHTML = 'Hello world!';
document.body.appendChild(h1);

BIN
examples/appcache/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB

View File

@ -0,0 +1,3 @@
body {
background: snow;
}

View File

@ -0,0 +1,11 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Example template</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>
<img src="logo.png">
</body>
</html>

View File

@ -0,0 +1,31 @@
var AppCachePlugin = require('appcache-webpack-plugin');
var HtmlWebpackPlugin = require('../..');
var ExtractTextPlugin = require('extract-text-webpack-plugin');
module.exports = {
entry: './example.js',
output: {
path: __dirname + '/dist',
publicPath: '',
filename: 'bundle.js'
},
module: {
loaders: [
{ test: /\.css$/, loader: ExtractTextPlugin.extract('style-loader', 'css-loader') },
{ test: /\.png$/, loader: 'file-loader' },
{ test: /\.html$/, loader: 'html-loader?-removeOptionalTags' }
]
},
plugins: [
new AppCachePlugin(),
new HtmlWebpackPlugin({
filename: 'index.html',
template: 'template.html',
minify: {
removeComments: true,
collapseWhitespace: true
}
}),
new ExtractTextPlugin('styles.css')
]
};

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB

60
examples/custom-template/dist/bundle.js vendored Normal file
View File

@ -0,0 +1,60 @@
/******/ (function(modules) { // webpackBootstrap
/******/ // The module cache
/******/ var installedModules = {};
/******/ // The require function
/******/ function __webpack_require__(moduleId) {
/******/ // Check if module is in cache
/******/ if(installedModules[moduleId])
/******/ return installedModules[moduleId].exports;
/******/ // Create a new module (and put it into the cache)
/******/ var module = installedModules[moduleId] = {
/******/ exports: {},
/******/ id: moduleId,
/******/ loaded: false
/******/ };
/******/ // Execute the module function
/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
/******/ // Flag the module as loaded
/******/ module.loaded = true;
/******/ // Return the exports of the module
/******/ return module.exports;
/******/ }
/******/ // expose the modules object (__webpack_modules__)
/******/ __webpack_require__.m = modules;
/******/ // expose the module cache
/******/ __webpack_require__.c = installedModules;
/******/ // __webpack_public_path__
/******/ __webpack_require__.p = "";
/******/ // Load entry module and return exports
/******/ return __webpack_require__(0);
/******/ })
/************************************************************************/
/******/ ([
/* 0 */
/***/ function(module, exports, __webpack_require__) {
__webpack_require__(1);
var h1 = document.createElement('h1');
h1.innerHTML = 'Hello world!';
document.body.appendChild(h1);
/***/ },
/* 1 */
/***/ function(module, exports) {
// removed by extract-text-webpack-plugin
/***/ }
/******/ ]);

View File

@ -0,0 +1,12 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Webpack App</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link href="styles.css" rel="stylesheet"></head>
<body>
<h2>Partial</h2>
<img src="0714810ae3fb211173e2964249507195.png">
<script src="bundle.js"></script></body>
</html>

View File

@ -0,0 +1,3 @@
body {
background: snow;
}

View File

@ -0,0 +1,4 @@
require('./main.css');
var h1 = document.createElement('h1');
h1.innerHTML = 'Hello world!';
document.body.appendChild(h1);

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB

View File

@ -0,0 +1,3 @@
body {
background: snow;
}

View File

@ -0,0 +1,2 @@
<h2>Partial</h2>
<img src="logo.png">

View File

@ -0,0 +1,4 @@
# custom template
This example uses a custom underscore template which inlines an partial using the html-loader:
`<%= require('html!./partial.html') %>`

View File

@ -0,0 +1,11 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title><%= htmlWebpackPlugin.options.title %></title>
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>
<%= require('html!./partial.html') %>
</body>
</html>

View File

@ -0,0 +1,22 @@
var HtmlWebpackPlugin = require('../..');
var ExtractTextPlugin = require('extract-text-webpack-plugin');
module.exports = {
entry: './example.js',
output: {
path: __dirname + '/dist',
publicPath: '',
filename: 'bundle.js'
},
module: {
loaders: [
{ test: /\.css$/, loader: ExtractTextPlugin.extract('style-loader', 'css-loader') },
{ test: /\.png$/, loader: 'file-loader' }
]
},
plugins: [
new HtmlWebpackPlugin({
template: 'template.html'
}),
new ExtractTextPlugin('styles.css')
]
};

404
examples/default/dist/bundle.js vendored Normal file
View File

@ -0,0 +1,404 @@
/******/ (function(modules) { // webpackBootstrap
/******/ // The module cache
/******/ var installedModules = {};
/******/ // The require function
/******/ function __webpack_require__(moduleId) {
/******/ // Check if module is in cache
/******/ if(installedModules[moduleId])
/******/ return installedModules[moduleId].exports;
/******/ // Create a new module (and put it into the cache)
/******/ var module = installedModules[moduleId] = {
/******/ exports: {},
/******/ id: moduleId,
/******/ loaded: false
/******/ };
/******/ // Execute the module function
/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
/******/ // Flag the module as loaded
/******/ module.loaded = true;
/******/ // Return the exports of the module
/******/ return module.exports;
/******/ }
/******/ // expose the modules object (__webpack_modules__)
/******/ __webpack_require__.m = modules;
/******/ // expose the module cache
/******/ __webpack_require__.c = installedModules;
/******/ // __webpack_public_path__
/******/ __webpack_require__.p = "";
/******/ // Load entry module and return exports
/******/ return __webpack_require__(0);
/******/ })
/************************************************************************/
/******/ ([
/* 0 */
/***/ function(module, exports, __webpack_require__) {
__webpack_require__(1);
var h1 = document.createElement('h1');
h1.innerHTML = 'Hello world!';
document.body.appendChild(h1);
/***/ },
/* 1 */
/***/ function(module, exports, __webpack_require__) {
// style-loader: Adds some css to the DOM by adding a <style> tag
// load the styles
var content = __webpack_require__(2);
if(typeof content === 'string') content = [[module.id, content, '']];
// add the styles to the DOM
var update = __webpack_require__(4)(content, {});
if(content.locals) module.exports = content.locals;
// Hot Module Replacement
if(false) {
// When the styles change, update the <style> tags
if(!content.locals) {
module.hot.accept("!!./../../node_modules/css-loader/index.js!./main.css", function() {
var newContent = require("!!./../../node_modules/css-loader/index.js!./main.css");
if(typeof newContent === 'string') newContent = [[module.id, newContent, '']];
update(newContent);
});
}
// When the module is disposed, remove the <style> tags
module.hot.dispose(function() { update(); });
}
/***/ },
/* 2 */
/***/ function(module, exports, __webpack_require__) {
exports = module.exports = __webpack_require__(3)();
// imports
// module
exports.push([module.id, "body {\n background: snow;\n}", ""]);
// exports
/***/ },
/* 3 */
/***/ function(module, exports) {
/*
MIT License http://www.opensource.org/licenses/mit-license.php
Author Tobias Koppers @sokra
*/
// css base code, injected by the css-loader
module.exports = function() {
var list = [];
// return the list of modules as css string
list.toString = function toString() {
var result = [];
for(var i = 0; i < this.length; i++) {
var item = this[i];
if(item[2]) {
result.push("@media " + item[2] + "{" + item[1] + "}");
} else {
result.push(item[1]);
}
}
return result.join("");
};
// import a list of modules into the list
list.i = function(modules, mediaQuery) {
if(typeof modules === "string")
modules = [[null, modules, ""]];
var alreadyImportedModules = {};
for(var i = 0; i < this.length; i++) {
var id = this[i][0];
if(typeof id === "number")
alreadyImportedModules[id] = true;
}
for(i = 0; i < modules.length; i++) {
var item = modules[i];
// skip already imported module
// this implementation is not 100% perfect for weird media query combinations
// when a module is imported multiple times with different media queries.
// I hope this will never occur (Hey this way we have smaller bundles)
if(typeof item[0] !== "number" || !alreadyImportedModules[item[0]]) {
if(mediaQuery && !item[2]) {
item[2] = mediaQuery;
} else if(mediaQuery) {
item[2] = "(" + item[2] + ") and (" + mediaQuery + ")";
}
list.push(item);
}
}
};
return list;
};
/***/ },
/* 4 */
/***/ function(module, exports, __webpack_require__) {
/*
MIT License http://www.opensource.org/licenses/mit-license.php
Author Tobias Koppers @sokra
*/
var stylesInDom = {},
memoize = function(fn) {
var memo;
return function () {
if (typeof memo === "undefined") memo = fn.apply(this, arguments);
return memo;
};
},
isOldIE = memoize(function() {
return /msie [6-9]\b/.test(window.navigator.userAgent.toLowerCase());
}),
getHeadElement = memoize(function () {
return document.head || document.getElementsByTagName("head")[0];
}),
singletonElement = null,
singletonCounter = 0,
styleElementsInsertedAtTop = [];
module.exports = function(list, options) {
if(false) {
if(typeof document !== "object") throw new Error("The style-loader cannot be used in a non-browser environment");
}
options = options || {};
// Force single-tag solution on IE6-9, which has a hard limit on the # of <style>
// tags it will allow on a page
if (typeof options.singleton === "undefined") options.singleton = isOldIE();
// By default, add <style> tags to the bottom of <head>.
if (typeof options.insertAt === "undefined") options.insertAt = "bottom";
var styles = listToStyles(list);
addStylesToDom(styles, options);
return function update(newList) {
var mayRemove = [];
for(var i = 0; i < styles.length; i++) {
var item = styles[i];
var domStyle = stylesInDom[item.id];
domStyle.refs--;
mayRemove.push(domStyle);
}
if(newList) {
var newStyles = listToStyles(newList);
addStylesToDom(newStyles, options);
}
for(var i = 0; i < mayRemove.length; i++) {
var domStyle = mayRemove[i];
if(domStyle.refs === 0) {
for(var j = 0; j < domStyle.parts.length; j++)
domStyle.parts[j]();
delete stylesInDom[domStyle.id];
}
}
};
}
function addStylesToDom(styles, options) {
for(var i = 0; i < styles.length; i++) {
var item = styles[i];
var domStyle = stylesInDom[item.id];
if(domStyle) {
domStyle.refs++;
for(var j = 0; j < domStyle.parts.length; j++) {
domStyle.parts[j](item.parts[j]);
}
for(; j < item.parts.length; j++) {
domStyle.parts.push(addStyle(item.parts[j], options));
}
} else {
var parts = [];
for(var j = 0; j < item.parts.length; j++) {
parts.push(addStyle(item.parts[j], options));
}
stylesInDom[item.id] = {id: item.id, refs: 1, parts: parts};
}
}
}
function listToStyles(list) {
var styles = [];
var newStyles = {};
for(var i = 0; i < list.length; i++) {
var item = list[i];
var id = item[0];
var css = item[1];
var media = item[2];
var sourceMap = item[3];
var part = {css: css, media: media, sourceMap: sourceMap};
if(!newStyles[id])
styles.push(newStyles[id] = {id: id, parts: [part]});
else
newStyles[id].parts.push(part);
}
return styles;
}
function insertStyleElement(options, styleElement) {
var head = getHeadElement();
var lastStyleElementInsertedAtTop = styleElementsInsertedAtTop[styleElementsInsertedAtTop.length - 1];
if (options.insertAt === "top") {
if(!lastStyleElementInsertedAtTop) {
head.insertBefore(styleElement, head.firstChild);
} else if(lastStyleElementInsertedAtTop.nextSibling) {
head.insertBefore(styleElement, lastStyleElementInsertedAtTop.nextSibling);
} else {
head.appendChild(styleElement);
}
styleElementsInsertedAtTop.push(styleElement);
} else if (options.insertAt === "bottom") {
head.appendChild(styleElement);
} else {
throw new Error("Invalid value for parameter 'insertAt'. Must be 'top' or 'bottom'.");
}
}
function removeStyleElement(styleElement) {
styleElement.parentNode.removeChild(styleElement);
var idx = styleElementsInsertedAtTop.indexOf(styleElement);
if(idx >= 0) {
styleElementsInsertedAtTop.splice(idx, 1);
}
}
function createStyleElement(options) {
var styleElement = document.createElement("style");
styleElement.type = "text/css";
insertStyleElement(options, styleElement);
return styleElement;
}
function createLinkElement(options) {
var linkElement = document.createElement("link");
linkElement.rel = "stylesheet";
insertStyleElement(options, linkElement);
return linkElement;
}
function addStyle(obj, options) {
var styleElement, update, remove;
if (options.singleton) {
var styleIndex = singletonCounter++;
styleElement = singletonElement || (singletonElement = createStyleElement(options));
update = applyToSingletonTag.bind(null, styleElement, styleIndex, false);
remove = applyToSingletonTag.bind(null, styleElement, styleIndex, true);
} else if(obj.sourceMap &&
typeof URL === "function" &&
typeof URL.createObjectURL === "function" &&
typeof URL.revokeObjectURL === "function" &&
typeof Blob === "function" &&
typeof btoa === "function") {
styleElement = createLinkElement(options);
update = updateLink.bind(null, styleElement);
remove = function() {
removeStyleElement(styleElement);
if(styleElement.href)
URL.revokeObjectURL(styleElement.href);
};
} else {
styleElement = createStyleElement(options);
update = applyToTag.bind(null, styleElement);
remove = function() {
removeStyleElement(styleElement);
};
}
update(obj);
return function updateStyle(newObj) {
if(newObj) {
if(newObj.css === obj.css && newObj.media === obj.media && newObj.sourceMap === obj.sourceMap)
return;
update(obj = newObj);
} else {
remove();
}
};
}
var replaceText = (function () {
var textStore = [];
return function (index, replacement) {
textStore[index] = replacement;
return textStore.filter(Boolean).join('\n');
};
})();
function applyToSingletonTag(styleElement, index, remove, obj) {
var css = remove ? "" : obj.css;
if (styleElement.styleSheet) {
styleElement.styleSheet.cssText = replaceText(index, css);
} else {
var cssNode = document.createTextNode(css);
var childNodes = styleElement.childNodes;
if (childNodes[index]) styleElement.removeChild(childNodes[index]);
if (childNodes.length) {
styleElement.insertBefore(cssNode, childNodes[index]);
} else {
styleElement.appendChild(cssNode);
}
}
}
function applyToTag(styleElement, obj) {
var css = obj.css;
var media = obj.media;
var sourceMap = obj.sourceMap;
if(media) {
styleElement.setAttribute("media", media)
}
if(styleElement.styleSheet) {
styleElement.styleSheet.cssText = css;
} else {
while(styleElement.firstChild) {
styleElement.removeChild(styleElement.firstChild);
}
styleElement.appendChild(document.createTextNode(css));
}
}
function updateLink(linkElement, obj) {
var css = obj.css;
var media = obj.media;
var sourceMap = obj.sourceMap;
if(sourceMap) {
// http://stackoverflow.com/a/26603875
css += "\n/*# sourceMappingURL=data:application/json;base64," + btoa(unescape(encodeURIComponent(JSON.stringify(sourceMap)))) + " */";
}
var blob = new Blob([css], { type: "text/css" });
var oldSrc = linkElement.href;
linkElement.href = URL.createObjectURL(blob);
if(oldSrc)
URL.revokeObjectURL(oldSrc);
}
/***/ }
/******/ ]);

9
examples/default/dist/index.html vendored Normal file
View File

@ -0,0 +1,9 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Webpack App</title>
</head>
<body>
<script src="bundle.js"></script></body>
</html>

4
examples/default/example.js Executable file
View File

@ -0,0 +1,4 @@
require('./main.css');
var h1 = document.createElement('h1');
h1.innerHTML = 'Hello world!';
document.body.appendChild(h1);

View File

@ -0,0 +1,3 @@
body {
background: snow;
}

View File

@ -0,0 +1,3 @@
# zero-config example
in this example only the default configuration is used

View File

@ -0,0 +1,18 @@
var HtmlWebpackPlugin = require('../..');
module.exports = {
entry: './example.js',
output: {
path: __dirname + '/dist',
publicPath: '',
filename: 'bundle.js'
},
module: {
loaders: [
{ test: /\.css$/, loader: 'style-loader!css-loader' },
{ test: /\.png$/, loader: 'file-loader' }
]
},
plugins: [
new HtmlWebpackPlugin()
]
};

60
examples/favicon/dist/bundle.js vendored Normal file
View File

@ -0,0 +1,60 @@
/******/ (function(modules) { // webpackBootstrap
/******/ // The module cache
/******/ var installedModules = {};
/******/ // The require function
/******/ function __webpack_require__(moduleId) {
/******/ // Check if module is in cache
/******/ if(installedModules[moduleId])
/******/ return installedModules[moduleId].exports;
/******/ // Create a new module (and put it into the cache)
/******/ var module = installedModules[moduleId] = {
/******/ exports: {},
/******/ id: moduleId,
/******/ loaded: false
/******/ };
/******/ // Execute the module function
/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
/******/ // Flag the module as loaded
/******/ module.loaded = true;
/******/ // Return the exports of the module
/******/ return module.exports;
/******/ }
/******/ // expose the modules object (__webpack_modules__)
/******/ __webpack_require__.m = modules;
/******/ // expose the module cache
/******/ __webpack_require__.c = installedModules;
/******/ // __webpack_public_path__
/******/ __webpack_require__.p = "";
/******/ // Load entry module and return exports
/******/ return __webpack_require__(0);
/******/ })
/************************************************************************/
/******/ ([
/* 0 */
/***/ function(module, exports, __webpack_require__) {
__webpack_require__(1);
var h1 = document.createElement('h1');
h1.innerHTML = 'Hello world!';
document.body.appendChild(h1);
/***/ },
/* 1 */
/***/ function(module, exports) {
// removed by extract-text-webpack-plugin
/***/ }
/******/ ]);

9
examples/favicon/dist/favicon.html vendored Normal file
View File

@ -0,0 +1,9 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>HtmlWebpackPlugin example</title>
<link rel="shortcut icon" href="favicon.ico"><link href="styles.css" rel="stylesheet"></head>
<body>
<script src="bundle.js"></script></body>
</html>

BIN
examples/favicon/dist/favicon.ico vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 766 B

3
examples/favicon/dist/styles.css vendored Normal file
View File

@ -0,0 +1,3 @@
body {
background: snow;
}

4
examples/favicon/example.js Executable file
View File

@ -0,0 +1,4 @@
require('./main.css');
var h1 = document.createElement('h1');
h1.innerHTML = 'Hello world!';
document.body.appendChild(h1);

Binary file not shown.

After

Width:  |  Height:  |  Size: 766 B

View File

@ -0,0 +1,3 @@
body {
background: snow;
}

View File

@ -0,0 +1,11 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Example template</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>
<img src="logo.png">
</body>
</html>

View File

@ -0,0 +1,24 @@
var HtmlWebpackPlugin = require('../..');
var ExtractTextPlugin = require('extract-text-webpack-plugin');
module.exports = {
entry: './example.js',
output: {
path: __dirname + '/dist',
publicPath: '',
filename: 'bundle.js'
},
module: {
loaders: [
{ test: /\.css$/, loader: ExtractTextPlugin.extract('style-loader', 'css-loader') },
{ test: /\.png$/, loader: 'file-loader' }
]
},
plugins: [
new HtmlWebpackPlugin({
title: 'HtmlWebpackPlugin example',
favicon: 'favicon.ico',
filename: 'favicon.html'
}),
new ExtractTextPlugin('styles.css')
]
};

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB

11
examples/html-loader/dist/about.html vendored Normal file
View File

@ -0,0 +1,11 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Example template</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="shortcut icon" href="favicon.ico"><link href="styles.css" rel="stylesheet"></head>
<body>
<img src="0714810ae3fb211173e2964249507195.png">
<script src="bundle.js"></script></body>
</html>

60
examples/html-loader/dist/bundle.js vendored Normal file
View File

@ -0,0 +1,60 @@
/******/ (function(modules) { // webpackBootstrap
/******/ // The module cache
/******/ var installedModules = {};
/******/ // The require function
/******/ function __webpack_require__(moduleId) {
/******/ // Check if module is in cache
/******/ if(installedModules[moduleId])
/******/ return installedModules[moduleId].exports;
/******/ // Create a new module (and put it into the cache)
/******/ var module = installedModules[moduleId] = {
/******/ exports: {},
/******/ id: moduleId,
/******/ loaded: false
/******/ };
/******/ // Execute the module function
/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
/******/ // Flag the module as loaded
/******/ module.loaded = true;
/******/ // Return the exports of the module
/******/ return module.exports;
/******/ }
/******/ // expose the modules object (__webpack_modules__)
/******/ __webpack_require__.m = modules;
/******/ // expose the module cache
/******/ __webpack_require__.c = installedModules;
/******/ // __webpack_public_path__
/******/ __webpack_require__.p = "";
/******/ // Load entry module and return exports
/******/ return __webpack_require__(0);
/******/ })
/************************************************************************/
/******/ ([
/* 0 */
/***/ function(module, exports, __webpack_require__) {
__webpack_require__(1);
var h1 = document.createElement('h1');
h1.innerHTML = 'Hello world!';
document.body.appendChild(h1);
/***/ },
/* 1 */
/***/ function(module, exports) {
// removed by extract-text-webpack-plugin
/***/ }
/******/ ]);

BIN
examples/html-loader/dist/favicon.ico vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 766 B

11
examples/html-loader/dist/index.html vendored Normal file
View File

@ -0,0 +1,11 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Example template</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="shortcut icon" href="favicon.ico"><link href="styles.css" rel="stylesheet"></head>
<body>
<img src="0714810ae3fb211173e2964249507195.png">
<script src="bundle.js"></script></body>
</html>

3
examples/html-loader/dist/styles.css vendored Normal file
View File

@ -0,0 +1,3 @@
body {
background: snow;
}

View File

@ -0,0 +1,4 @@
require('./main.css');
var h1 = document.createElement('h1');
h1.innerHTML = 'Hello world!';
document.body.appendChild(h1);

Binary file not shown.

After

Width:  |  Height:  |  Size: 766 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB

View File

@ -0,0 +1,3 @@
body {
background: snow;
}

View File

@ -0,0 +1,11 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Example template</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>
<img src="logo.png">
</body>
</html>

View File

@ -0,0 +1,30 @@
var HtmlWebpackPlugin = require('../..');
var ExtractTextPlugin = require('extract-text-webpack-plugin');
module.exports = {
entry: './example.js',
output: {
path: __dirname + '/dist',
publicPath: '',
filename: 'bundle.js'
},
module: {
loaders: [
{ test: /\.css$/, loader: ExtractTextPlugin.extract('style-loader', 'css-loader') },
{ test: /\.png$/, loader: 'file-loader' },
{ test: /\.html$/, loader: 'html-loader' }
]
},
plugins: [
new HtmlWebpackPlugin({
filename: 'index.html',
favicon: 'favicon.ico',
template: 'template.html'
}),
new HtmlWebpackPlugin({
filename: 'about.html',
favicon: 'favicon.ico',
template: 'template.html'
}),
new ExtractTextPlugin('styles.css')
]
};

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB

347
examples/jade-loader/dist/bundle.js vendored Normal file
View File

@ -0,0 +1,347 @@
/******/ (function(modules) { // webpackBootstrap
/******/ // The module cache
/******/ var installedModules = {};
/******/ // The require function
/******/ function __webpack_require__(moduleId) {
/******/ // Check if module is in cache
/******/ if(installedModules[moduleId])
/******/ return installedModules[moduleId].exports;
/******/ // Create a new module (and put it into the cache)
/******/ var module = installedModules[moduleId] = {
/******/ exports: {},
/******/ id: moduleId,
/******/ loaded: false
/******/ };
/******/ // Execute the module function
/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
/******/ // Flag the module as loaded
/******/ module.loaded = true;
/******/ // Return the exports of the module
/******/ return module.exports;
/******/ }
/******/ // expose the modules object (__webpack_modules__)
/******/ __webpack_require__.m = modules;
/******/ // expose the module cache
/******/ __webpack_require__.c = installedModules;
/******/ // __webpack_public_path__
/******/ __webpack_require__.p = "";
/******/ // Load entry module and return exports
/******/ return __webpack_require__(0);
/******/ })
/************************************************************************/
/******/ ([
/* 0 */
/***/ function(module, exports, __webpack_require__) {
'use strict';
__webpack_require__(1);
// Use the same template for the frontend code
var template = __webpack_require__(5);
setInterval(function () {
var div = document.getElementById('main');
div.innerHTML = template({ time: new Date() });
div.style.color = 'navy';
}, 1000);
/***/ },
/* 1 */
/***/ function(module, exports) {
// removed by extract-text-webpack-plugin
/***/ },
/* 2 */,
/* 3 */,
/* 4 */,
/* 5 */
/***/ function(module, exports, __webpack_require__) {
var jade = __webpack_require__(6);
module.exports = function template(locals) {
var buf = [];
var jade_mixins = {};
var jade_interp;
;var locals_for_with = (locals || {});(function (time) {
buf.push("<!-- this partial is used for frontend and backend--><div class=\"time\"> <b>Current time</b><p>" + (jade.escape((jade_interp = time.toISOString()) == null ? '' : jade_interp)) + "</p></div><img" + (jade.attr("src", "" + (__webpack_require__(8)) + "", true, true)) + ">");}.call(this,"time" in locals_for_with?locals_for_with.time:typeof time!=="undefined"?time:undefined));;return buf.join("");
}
/***/ },
/* 6 */
/***/ function(module, exports, __webpack_require__) {
'use strict';
/**
* Merge two attribute objects giving precedence
* to values in object `b`. Classes are special-cased
* allowing for arrays and merging/joining appropriately
* resulting in a string.
*
* @param {Object} a
* @param {Object} b
* @return {Object} a
* @api private
*/
exports.merge = function merge(a, b) {
if (arguments.length === 1) {
var attrs = a[0];
for (var i = 1; i < a.length; i++) {
attrs = merge(attrs, a[i]);
}
return attrs;
}
var ac = a['class'];
var bc = b['class'];
if (ac || bc) {
ac = ac || [];
bc = bc || [];
if (!Array.isArray(ac)) ac = [ac];
if (!Array.isArray(bc)) bc = [bc];
a['class'] = ac.concat(bc).filter(nulls);
}
for (var key in b) {
if (key != 'class') {
a[key] = b[key];
}
}
return a;
};
/**
* Filter null `val`s.
*
* @param {*} val
* @return {Boolean}
* @api private
*/
function nulls(val) {
return val != null && val !== '';
}
/**
* join array as classes.
*
* @param {*} val
* @return {String}
*/
exports.joinClasses = joinClasses;
function joinClasses(val) {
return (Array.isArray(val) ? val.map(joinClasses) :
(val && typeof val === 'object') ? Object.keys(val).filter(function (key) { return val[key]; }) :
[val]).filter(nulls).join(' ');
}
/**
* Render the given classes.
*
* @param {Array} classes
* @param {Array.<Boolean>} escaped
* @return {String}
*/
exports.cls = function cls(classes, escaped) {
var buf = [];
for (var i = 0; i < classes.length; i++) {
if (escaped && escaped[i]) {
buf.push(exports.escape(joinClasses([classes[i]])));
} else {
buf.push(joinClasses(classes[i]));
}
}
var text = joinClasses(buf);
if (text.length) {
return ' class="' + text + '"';
} else {
return '';
}
};
exports.style = function (val) {
if (val && typeof val === 'object') {
return Object.keys(val).map(function (style) {
return style + ':' + val[style];
}).join(';');
} else {
return val;
}
};
/**
* Render the given attribute.
*
* @param {String} key
* @param {String} val
* @param {Boolean} escaped
* @param {Boolean} terse
* @return {String}
*/
exports.attr = function attr(key, val, escaped, terse) {
if (key === 'style') {
val = exports.style(val);
}
if ('boolean' == typeof val || null == val) {
if (val) {
return ' ' + (terse ? key : key + '="' + key + '"');
} else {
return '';
}
} else if (0 == key.indexOf('data') && 'string' != typeof val) {
if (JSON.stringify(val).indexOf('&') !== -1) {
console.warn('Since Jade 2.0.0, ampersands (`&`) in data attributes ' +
'will be escaped to `&amp;`');
};
if (val && typeof val.toISOString === 'function') {
console.warn('Jade will eliminate the double quotes around dates in ' +
'ISO form after 2.0.0');
}
return ' ' + key + "='" + JSON.stringify(val).replace(/'/g, '&apos;') + "'";
} else if (escaped) {
if (val && typeof val.toISOString === 'function') {
console.warn('Jade will stringify dates in ISO form after 2.0.0');
}
return ' ' + key + '="' + exports.escape(val) + '"';
} else {
if (val && typeof val.toISOString === 'function') {
console.warn('Jade will stringify dates in ISO form after 2.0.0');
}
return ' ' + key + '="' + val + '"';
}
};
/**
* Render the given attributes object.
*
* @param {Object} obj
* @param {Object} escaped
* @return {String}
*/
exports.attrs = function attrs(obj, terse){
var buf = [];
var keys = Object.keys(obj);
if (keys.length) {
for (var i = 0; i < keys.length; ++i) {
var key = keys[i]
, val = obj[key];
if ('class' == key) {
if (val = joinClasses(val)) {
buf.push(' ' + key + '="' + val + '"');
}
} else {
buf.push(exports.attr(key, val, false, terse));
}
}
}
return buf.join('');
};
/**
* Escape the given string of `html`.
*
* @param {String} html
* @return {String}
* @api private
*/
var jade_encode_html_rules = {
'&': '&amp;',
'<': '&lt;',
'>': '&gt;',
'"': '&quot;'
};
var jade_match_html = /[&<>"]/g;
function jade_encode_char(c) {
return jade_encode_html_rules[c] || c;
}
exports.escape = jade_escape;
function jade_escape(html){
var result = String(html).replace(jade_match_html, jade_encode_char);
if (result === '' + html) return html;
else return result;
};
/**
* Re-throw the given `err` in context to the
* the jade in `filename` at the given `lineno`.
*
* @param {Error} err
* @param {String} filename
* @param {String} lineno
* @api private
*/
exports.rethrow = function rethrow(err, filename, lineno, str){
if (!(err instanceof Error)) throw err;
if ((typeof window != 'undefined' || !filename) && !str) {
err.message += ' on line ' + lineno;
throw err;
}
try {
str = str || __webpack_require__(7).readFileSync(filename, 'utf8')
} catch (ex) {
rethrow(err, null, lineno)
}
var context = 3
, lines = str.split('\n')
, start = Math.max(lineno - context, 0)
, end = Math.min(lines.length, lineno + context);
// Error context
var context = lines.slice(start, end).map(function(line, i){
var curr = i + start + 1;
return (curr == lineno ? ' > ' : ' ')
+ curr
+ '| '
+ line;
}).join('\n');
// Alter exception message
err.path = filename;
err.message = (filename || 'Jade') + ':' + lineno
+ '\n' + context + '\n\n' + err.message;
throw err;
};
exports.DebugItem = function DebugItem(lineno, filename) {
this.lineno = lineno;
this.filename = filename;
}
/***/ },
/* 7 */
/***/ function(module, exports) {
/* (ignored) */
/***/ },
/* 8 */
/***/ function(module, exports, __webpack_require__) {
module.exports = __webpack_require__.p + "0714810ae3fb211173e2964249507195.png";
/***/ }
/******/ ]);

BIN
examples/jade-loader/dist/favicon.ico vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 766 B

1
examples/jade-loader/dist/index.html vendored Normal file
View File

@ -0,0 +1 @@
<!DOCTYPE html><html><head><title>Jade demo</title><link rel="shortcut icon" href="favicon.ico"><link href="styles.css" rel="stylesheet"></head><body><div id="main"><!-- this partial is used for frontend and backend--><div class="time"> <b>Current time</b><p>1998-12-31T23:00:00.000Z</p></div><img src="0714810ae3fb211173e2964249507195.png"></div><script src="bundle.js"></script></body></html>

3
examples/jade-loader/dist/styles.css vendored Normal file
View File

@ -0,0 +1,3 @@
body {
background: snow;
}

10
examples/jade-loader/example.js Executable file
View File

@ -0,0 +1,10 @@
'use strict';
require('./main.css');
// Use the same template for the frontend code
var template = require('./time.jade');
setInterval(function () {
var div = document.getElementById('main');
div.innerHTML = template({ time: new Date() });
div.style.color = 'navy';
}, 1000);

Binary file not shown.

After

Width:  |  Height:  |  Size: 766 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB

View File

@ -0,0 +1,3 @@
body {
background: snow;
}

View File

@ -0,0 +1,4 @@
# isomorphic jade example
This example shows how to use a different template engine (in this case jade)
to load the `time.jade` template on the backend and frontend.

View File

@ -0,0 +1,8 @@
doctype html
html
head
title= htmlWebpackPlugin.options.title
body
#main
- locals.time = new Date('01 01, 1999');
include ./time.jade

View File

@ -0,0 +1,6 @@
// this partial is used for frontend and backend
.time
b Current time
p #{time.toISOString()}
img(src="#{require('./logo.png')}")

View File

@ -0,0 +1,26 @@
var HtmlWebpackPlugin = require('../..');
var ExtractTextPlugin = require('extract-text-webpack-plugin');
module.exports = {
entry: './example.js',
output: {
path: __dirname + '/dist',
publicPath: '',
filename: 'bundle.js'
},
module: {
loaders: [
{ test: /\.css$/, loader: ExtractTextPlugin.extract('style-loader', 'css-loader') },
{ test: /\.png$/, loader: 'file-loader' },
{ test: /\.jade$/, loader: 'jade' }
]
},
plugins: [
new HtmlWebpackPlugin({
filename: 'index.html',
favicon: 'favicon.ico',
template: 'template.jade',
title: 'Jade demo'
}),
new ExtractTextPlugin('styles.css')
]
};

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB

65
examples/javascript/dist/bundle.js vendored Normal file
View File

@ -0,0 +1,65 @@
/******/ (function(modules) { // webpackBootstrap
/******/ // The module cache
/******/ var installedModules = {};
/******/ // The require function
/******/ function __webpack_require__(moduleId) {
/******/ // Check if module is in cache
/******/ if(installedModules[moduleId])
/******/ return installedModules[moduleId].exports;
/******/ // Create a new module (and put it into the cache)
/******/ var module = installedModules[moduleId] = {
/******/ exports: {},
/******/ id: moduleId,
/******/ loaded: false
/******/ };
/******/ // Execute the module function
/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
/******/ // Flag the module as loaded
/******/ module.loaded = true;
/******/ // Return the exports of the module
/******/ return module.exports;
/******/ }
/******/ // expose the modules object (__webpack_modules__)
/******/ __webpack_require__.m = modules;
/******/ // expose the module cache
/******/ __webpack_require__.c = installedModules;
/******/ // __webpack_public_path__
/******/ __webpack_require__.p = "";
/******/ // Load entry module and return exports
/******/ return __webpack_require__(0);
/******/ })
/************************************************************************/
/******/ ([
/* 0 */
/***/ function(module, exports, __webpack_require__) {
eval("__webpack_require__(1);\n\nvar universal = __webpack_require__(5);\nvar h1 = document.createElement('h1');\nh1.innerHTML = universal();\n\ndocument.body.appendChild(h1);\n\n\n/*****************\n ** WEBPACK FOOTER\n ** ./example.js\n ** module id = 0\n ** module chunks = 0\n **/\n//# sourceURL=webpack:///./example.js?");
/***/ },
/* 1 */
/***/ function(module, exports) {
eval("// removed by extract-text-webpack-plugin\n\n/*****************\n ** WEBPACK FOOTER\n ** ./main.css\n ** module id = 1\n ** module chunks = 0\n **/\n//# sourceURL=webpack:///./main.css?");
/***/ },
/* 2 */,
/* 3 */,
/* 4 */,
/* 5 */
/***/ function(module, exports) {
eval("// This file is used for frontend and backend\n'use strict';\n\n// If compiled by the html-webpack-plugin\n// HTML_WEBPACK_PLUGIN is set to true:\nvar backend = typeof HTML_WEBPACK_PLUGIN !== 'undefined';\n\nmodule.exports = function () {\n return 'Hello World from ' + (backend ? 'backend' : 'frontend');\n};\n\n\n/*****************\n ** WEBPACK FOOTER\n ** ./universial.js\n ** module id = 5\n ** module chunks = 0\n **/\n//# sourceURL=webpack:///./universial.js?");
/***/ }
/******/ ]);

2
examples/javascript/dist/index.html vendored Normal file
View File

@ -0,0 +1,2 @@
<head><link href="styles.css" rel="stylesheet"></head>Hello World from backend2016-01-20T15:46:57.631Z<h2>Partial</h2>
<img src="0714810ae3fb211173e2964249507195.png"><script src="bundle.js"></script>

3
examples/javascript/dist/styles.css vendored Normal file
View File

@ -0,0 +1,3 @@
body {
background: snow;
}

View File

@ -0,0 +1,7 @@
require('./main.css');
var universal = require('./universial.js');
var h1 = document.createElement('h1');
h1.innerHTML = universal();
document.body.appendChild(h1);

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB

View File

@ -0,0 +1,3 @@
body {
background: snow;
}

View File

@ -0,0 +1,2 @@
<h2>Partial</h2>
<img src="logo.png">

View File

@ -0,0 +1,5 @@
# isomorphic javascript example
This example shows how to generate a template on the fly using javascript.
The best way to debug the compilation result is `devTool:eval`

View File

@ -0,0 +1,6 @@
// Webpack require:
var partial = require('./partial.html');
var universal = require('./universial.js');
// Export a function / promise / or a string:
module.exports = universal() + new Date().toISOString() + partial;

View File

@ -0,0 +1,10 @@
// This file is used for frontend and backend
'use strict';
// If compiled by the html-webpack-plugin
// HTML_WEBPACK_PLUGIN is set to true:
var backend = typeof HTML_WEBPACK_PLUGIN !== 'undefined';
module.exports = function () {
return 'Hello World from ' + (backend ? 'backend' : 'frontend');
};

View File

@ -0,0 +1,24 @@
var HtmlWebpackPlugin = require('../..');
var ExtractTextPlugin = require('extract-text-webpack-plugin');
module.exports = {
entry: './example.js',
output: {
path: __dirname + '/dist',
publicPath: '',
filename: 'bundle.js'
},
module: {
loaders: [
{ test: /\.css$/, loader: ExtractTextPlugin.extract('style-loader', 'css-loader') },
{ test: /\.png$/, loader: 'file-loader' },
{ test: /\.html$/, loader: 'html-loader' }
]
},
devtool: 'eval',
plugins: [
new HtmlWebpackPlugin({
template: 'template.js'
}),
new ExtractTextPlugin('styles.css')
]
};

502
index.js
View File

@ -1,182 +1,284 @@
'use strict';
var vm = require('vm');
var fs = require('fs');
var path = require('path');
var urlModule = require('url');
var _ = require('lodash');
var tmpl = require('blueimp-tmpl').tmpl;
var Promise = require('bluebird');
var path = require('path');
var childCompiler = require('./lib/compiler.js');
var prettyError = require('./lib/errors.js');
Promise.promisifyAll(fs);
function HtmlWebpackPlugin(options) {
this.options = options || {};
function HtmlWebpackPlugin (options) {
// Default options
this.options = _.extend({
template: __dirname + '/default_index.ejs',
filename: 'index.html',
hash: false,
inject: true,
compile: true,
favicon: false,
minify: false,
cache: true,
showErrors: true,
chunks: 'all',
excludeChunks: [],
title: 'Webpack App'
}, options);
}
HtmlWebpackPlugin.prototype.apply = function(compiler) {
HtmlWebpackPlugin.prototype.apply = function (compiler) {
var self = this;
compiler.plugin('emit', function(compilation, compileCallback) {
var webpackStatsJson = compilation.getStats().toJson();
var outputFilename = self.options.filename || 'index.html';
var isCompilationCached = false;
var compilationPromise;
this.options.template = this.getFullTemplatePath(this.options.template, compiler.context);
compiler.plugin('make', function (compilation, callback) {
// Compile the template (queued)
compilationPromise = childCompiler.compileTemplate(self.options.template, compiler.context, self.options.filename, compilation)
.catch(function (err) {
compilation.errors.push(prettyError(err, compiler.context).toString());
return {
content: self.options.showErrors ? prettyError(err, compiler.context).toJsonHtml() : 'ERROR'
};
})
.then(function (compilationResult) {
// If the compilation change didnt change the cache is valid
isCompilationCached = compilationResult.hash && self.hash === compilationResult.hash;
self.hash = compilation.hash;
callback();
return compilationResult.content;
});
});
compiler.plugin('after-compile', function (compilation, callback) {
// Clear the compilation queue
delete compiler.HtmlWebpackPluginQueue;
callback();
});
compiler.plugin('emit', function (compilation, callback) {
var applyPluginsAsyncWaterfall = Promise.promisify(compilation.applyPluginsAsyncWaterfall, {context: compilation});
// Get all chunks
var chunks = self.filterChunks(compilation.getStats().toJson(), self.options.chunks, self.options.excludeChunks);
// Sort chunks
chunks = self.sortChunks(chunks, self.options.chunksSortMode);
// Get assets
var assets = self.htmlWebpackPluginAssets(compilation, chunks);
// If the template and the assets did not change we don't have to emit the html
var assetJson = JSON.stringify(assets);
if (isCompilationCached && self.options.cache && assetJson === self.assetJson) {
return callback();
} else {
self.assetJson = assetJson;
}
Promise.resolve()
// Add the favicon
.then(function(callback) {
// Favicon
.then(function () {
if (self.options.favicon) {
return self.addFileToAssets(compilation, self.options.favicon, callback);
return self.addFileToAssets(self.options.favicon, compilation)
.then(function (faviconBasename) {
assets.favicon = faviconBasename;
});
}
})
// Generate the html
.then(function() {
var templateParams = {
webpack: webpackStatsJson,
webpackConfig: compilation.options,
htmlWebpackPlugin: {
files: self.htmlWebpackPluginAssets(compilation, webpackStatsJson, self.options.chunks, self.options.excludeChunks),
options: self.options,
}
};
// Deprecate templateParams.htmlWebpackPlugin.assets
var assets = self.htmlWebpackPluginLegacyAssets(compilation, webpackStatsJson);
Object.defineProperty(templateParams.htmlWebpackPlugin, 'assets', {
get: function() {
compilation.warnings.push(new Error('HtmlWebPackPlugin: htmlWebpackPlugin.assets is deprecated - please use inject or htmlWebpackPlugin.files instead' +
'\nsee: https://github.com/ampedandwired/html-webpack-plugin/issues/52'));
return assets;
}
});
// Get/generate html
return self.getTemplateContent(compilation, templateParams)
.then(function(htmlTemplateContent) {
// Compile and add html to compilation
return self.emitHtml(compilation, htmlTemplateContent, templateParams, outputFilename);
});
// Wait for the compilation to finish
.then(function () {
return compilationPromise;
})
// In case anything went wrong let the user know
.catch(function(err) {
compilation.errors.push(err);
compilation.assets[outputFilename] = {
source: function() {
return err.toString();
.then(function (compiledTemplate) {
// Allow to use a custom function / string instead
if (self.options.templateContent) {
return self.options.templateContent;
}
// Once everything is compiled evaluate the html factory
// and replace it with its content
return self.evaluateCompilationResult(compilation, compiledTemplate);
})
// Execute the template
.then(function (compilationResult) {
// If the loader result is a function execute it to retreive the html
// otherwise use the returned html
return typeof compilationResult !== 'function'
? compilationResult
: self.executeTemplate(compilationResult, chunks, assets, compilation);
})
// Allow plugins to change the html before assets are injected
.then(function (html) {
var pluginArgs = {html: html, assets: assets, plugin: self};
return applyPluginsAsyncWaterfall('html-webpack-plugin-before-html-processing', pluginArgs)
.then(function () {
return pluginArgs.html;
});
})
.then(function (html) {
// Add the stylesheets, scripts and so on to the resulting html
return self.postProcessHtml(html, assets);
})
// Allow plugins to change the html after assets are injected
.then(function (html) {
var pluginArgs = {html: html, assets: assets, plugin: self};
return applyPluginsAsyncWaterfall('html-webpack-plugin-after-html-processing', pluginArgs)
.then(function () {
return pluginArgs.html;
});
})
.catch(function (err) {
// In case anything went wrong the promise is resolved
// with the error message and an error is logged
compilation.errors.push(prettyError(err, compiler.context).toString());
// Prevent caching
self.hash = null;
return self.options.showErrors ? prettyError(err, compiler.context).toHtml() : 'ERROR';
})
.then(function (html) {
// Replace the compilation result with the evaluated html code
compilation.assets[self.options.filename] = {
source: function () {
return html;
},
size: function() {
return err.toString().length;
size: function () {
return html.length;
}
};
})
// Tell the compiler to proceed
.finally(compileCallback);
.then(function () {
// Let other plugins know that we are done:
return applyPluginsAsyncWaterfall('html-webpack-plugin-after-emit', {
html: compilation.assets[self.options.filename],
plugin: self
});
})
// Let webpack continue with it
.finally(function () {
callback();
// Tell blue bird that we don't want to wait for callback.
// Fixes "Warning: a promise was created in a handler but none were returned from it"
// https://github.com/petkaantonov/bluebird/blob/master/docs/docs/warning-explanations.md#warning-a-promise-was-created-in-a-handler-but-none-were-returned-from-it
return null;
});
});
};
/**
* Retrieves the html source depending on `this.options`.
* Supports:
* + options.fileContent as string
* + options.fileContent as sync function
* + options.fileContent as async function
* + options.template as template path
* Returns a Promise
* Evaluates the child compilation result
* Returns a promise
*/
HtmlWebpackPlugin.prototype.getTemplateContent = function(compilation, templateParams) {
HtmlWebpackPlugin.prototype.evaluateCompilationResult = function (compilation, source) {
if (!source) {
return Promise.reject('The child compilation didn\'t provide a result');
}
// The LibraryTemplatePlugin stores the template result in a local variable.
// To extract the result during the evaluation this part has to be removed.
source = source.replace('var HTML_WEBPACK_PLUGIN_RESULT =', '');
// Evaluate code and cast to string
var newSource;
try {
newSource = vm.runInNewContext(source, {HTML_WEBPACK_PLUGIN: true}, {filename: 'html-plugin-evaluation'});
} catch (e) {
return Promise.reject(e);
}
return typeof newSource === 'string' || typeof newSource === 'function'
? Promise.resolve(newSource)
: Promise.reject('The loader "' + this.options.template + '" didn\'t return html.');
};
/**
* Html post processing
*
* Returns a promise
*/
HtmlWebpackPlugin.prototype.executeTemplate = function (templateFunction, chunks, assets, compilation) {
var self = this;
// If config is invalid
if (self.options.templateContent && self.options.template) {
return Promise.reject(new Error('HtmlWebpackPlugin: cannot specify both template and templateContent options'));
}
// If a function is passed
if (typeof self.options.templateContent === 'function') {
return Promise.fromNode(function(callback) {
// allow to specify a sync or an async function to generate the template content
var result = self.options.templateContent(templateParams, compilation, callback);
// if it returns a result expect it to be sync
if (result !== undefined) {
callback(null, result);
return Promise.resolve()
// Template processing
.then(function () {
var templateParams = {
webpack: compilation.getStats().toJson(),
webpackConfig: compilation.options,
htmlWebpackPlugin: {
files: assets,
options: self.options
}
};
var html = '';
try {
html = templateFunction(templateParams);
} catch (e) {
compilation.errors.push(new Error('Template execution failed: ' + e));
return Promise.reject(e);
}
});
}
// If a string is passed
if (self.options.templateContent) {
return Promise.resolve(self.options.templateContent);
}
// If templateContent is empty use the template option
var templateFile = self.options.template;
if (!templateFile) {
// Use a special index file to prevent double script / style injection if the `inject` option is truthy
templateFile = path.join(__dirname, self.options.inject ? 'default_inject_index.html' : 'default_index.html');
} else {
templateFile = path.normalize(templateFile);
}
compilation.fileDependencies.push(templateFile);
return fs.readFileAsync(templateFile, 'utf8')
// If the file could not be read log a error
.catch(function() {
return Promise.reject(new Error('HtmlWebpackPlugin: Unable to read HTML template "' + templateFile + '"'));
return html;
});
};
/*
* Compile the html template and push the result to the compilation assets
/**
* Html post processing
*
* Returns a promise
*/
HtmlWebpackPlugin.prototype.emitHtml = function(compilation, htmlTemplateContent, templateParams, outputFilename) {
var html;
// blueimp-tmpl processing
try {
html = tmpl(htmlTemplateContent, templateParams);
} catch(e) {
return Promise.reject(new Error('HtmlWebpackPlugin: template error ' + e));
HtmlWebpackPlugin.prototype.postProcessHtml = function (html, assets) {
var self = this;
if (typeof html !== 'string') {
return Promise.reject('Expected html to be a string but got ' + JSON.stringify(html));
}
// Inject link and script elements into an existing html file
if (this.options.inject) {
html = this.injectAssetsIntoHtml(html, templateParams);
}
// Minify the html output
if (this.options.minify) {
var minify = require('html-minifier').minify;
html = minify(html, this.options.minify);
}
compilation.assets[outputFilename] = {
source: function() {
return Promise.resolve()
// Inject
.then(function () {
if (self.options.inject) {
return self.injectAssetsIntoHtml(html, assets);
} else {
return html;
}
})
// Minify
.then(function (html) {
if (self.options.minify) {
var minify = require('html-minifier').minify;
return minify(html, self.options.minify);
}
return html;
},
size: function() {
return html.length;
}
};
});
};
/*
* Pushes the content of the given filename to the compilation assets
*/
HtmlWebpackPlugin.prototype.addFileToAssets = function(compilation, filename) {
HtmlWebpackPlugin.prototype.addFileToAssets = function (filename, compilation) {
filename = path.resolve(compilation.compiler.context, filename);
return Promise.props({
size: fs.statAsync(filename),
source: fs.readFileAsync(filename)
})
.catch(function() {
.catch(function () {
return Promise.reject(new Error('HtmlWebpackPlugin: could not load file ' + filename));
})
.then(function(results) {
.then(function (results) {
var basename = path.basename(filename);
compilation.fileDependencies.push(filename);
compilation.assets[path.basename(filename)] = {
source: function() {
compilation.assets[basename] = {
source: function () {
return results.source;
},
size: function() {
return results.size;
size: function () {
return results.size.size;
}
};
return basename;
});
};
/**
* Helper to sort chunks
*/
HtmlWebpackPlugin.prototype.sortChunks = function(chunks, sortMode) {
// Sort mode auto by default:
HtmlWebpackPlugin.prototype.sortChunks = function (chunks, sortMode) {
// Sort mode auto by default:
if (typeof sortMode === 'undefined' || sortMode === 'auto') {
return chunks.sort(function orderEntryLast(a, b) {
return chunks.sort(function orderEntryLast (a, b) {
if (a.entry !== b.entry) {
return b.entry ? 1 : -1;
} else {
@ -196,16 +298,44 @@ HtmlWebpackPlugin.prototype.sortChunks = function(chunks, sortMode) {
throw new Error('"' + sortMode + '" is not a valid chunk sort mode');
};
HtmlWebpackPlugin.prototype.htmlWebpackPluginAssets = function(compilation, webpackStatsJson, includedChunks, excludedChunks) {
/**
* Return all chunks from the compilation result which match the exclude and include filters
*/
HtmlWebpackPlugin.prototype.filterChunks = function (webpackStatsJson, includedChunks, excludedChunks) {
return webpackStatsJson.chunks.filter(function (chunk) {
var chunkName = chunk.names[0];
// This chunk doesn't have a name. This script can't handled it.
if (chunkName === undefined) {
return false;
}
// Skip if the chunk should be lazy loaded
if (!chunk.initial) {
return false;
}
// Skip if the chunks should be filtered and the given chunk was not added explicity
if (Array.isArray(includedChunks) && includedChunks.indexOf(chunkName) === -1) {
return false;
}
// Skip if the chunks should be filtered and the given chunk was excluded explicity
if (Array.isArray(excludedChunks) && excludedChunks.indexOf(chunkName) !== -1) {
return false;
}
// Add otherwise
return true;
});
};
HtmlWebpackPlugin.prototype.htmlWebpackPluginAssets = function (compilation, chunks) {
var self = this;
var webpackStatsJson = compilation.getStats().toJson();
// Use the configured public path or build a relative path
var publicPath = typeof compilation.options.output.publicPath !== 'undefined' ?
compilation.mainTemplate.getPublicPath({hash: webpackStatsJson.hash}) :
path.relative(path.dirname(self.options.filename), '.');
var publicPath = typeof compilation.options.output.publicPath !== 'undefined'
? compilation.options.output.publicPath
: path.relative(path.dirname(self.options.filename), '.').split(path.sep).join('/');
if (publicPath.length && publicPath.substr(-1, 1) !== '/') {
publicPath = path.join(urlModule.resolve(publicPath + '/', '.'), '/');
publicPath += '/';
}
var assets = {
@ -215,10 +345,8 @@ HtmlWebpackPlugin.prototype.htmlWebpackPluginAssets = function(compilation, webp
js: [],
// Will contain all css files
css: [],
// Will contain the path to the favicon if it exists
favicon: self.options.favicon ? publicPath + path.basename(self.options.favicon): undefined,
// Will contain the html5 appcache manifest files if it exists
manifest: Object.keys(compilation.assets).filter(function(assetFile){
manifest: Object.keys(compilation.assets).filter(function (assetFile) {
return path.extname(assetFile) === '.appcache';
})[0]
};
@ -229,42 +357,20 @@ HtmlWebpackPlugin.prototype.htmlWebpackPluginAssets = function(compilation, webp
assets.favicon = self.appendHash(assets.favicon, webpackStatsJson.hash);
}
// Get sorted chunks
var chunks = HtmlWebpackPlugin.prototype.sortChunks(webpackStatsJson.chunks, this.options.chunksSortMode);
for (var i = 0; i < chunks.length; i++) {
var chunk = chunks[i];
var chunkName = chunk.names[0];
// This chunk doesn't have a name. This script can't handled it.
if(chunkName === undefined) {
continue;
}
// Skip not initial chunks
if (!chunk.initial) {
continue;
}
// Skip if the chunks should be filtered and the given chunk was not added explicity
if (Array.isArray(includedChunks) && includedChunks.indexOf(chunkName) === -1) {
continue;
}
// Skip if the chunks should be filtered and the given chunk was excluded explicity
if (Array.isArray(excludedChunks) && excludedChunks.indexOf(chunkName) !== -1) {
continue;
}
assets.chunks[chunkName] = {};
// Prepend the public path to all chunk files
var chunkFiles = [].concat(chunk.files).map(function(chunkFile) {
var chunkFiles = [].concat(chunk.files).map(function (chunkFile) {
return publicPath + chunkFile;
});
// Append a hash for cache busting
if (this.options.hash) {
chunkFiles = chunkFiles.map(function(chunkFile) {
chunkFiles = chunkFiles.map(function (chunkFile) {
return self.appendHash(chunkFile, webpackStatsJson.hash);
});
}
@ -277,7 +383,7 @@ HtmlWebpackPlugin.prototype.htmlWebpackPluginAssets = function(compilation, webp
assets.js.push(entry);
// Gather all css files
var css = chunkFiles.filter(function(chunkFile){
var css = chunkFiles.filter(function (chunkFile) {
// Some chunks may contain content hash in their names, for ex. 'main.css?1e7cac4e4d8b52fd5ccd2541146ef03f'.
// We must proper handle such cases, so we use regexp testing here
return /^.css($|\?)/.test(path.extname(chunkFile));
@ -296,28 +402,21 @@ HtmlWebpackPlugin.prototype.htmlWebpackPluginAssets = function(compilation, webp
/**
* Injects the assets into the given html string
*/
HtmlWebpackPlugin.prototype.injectAssetsIntoHtml = function(html, templateParams) {
var assets = templateParams.htmlWebpackPlugin.files;
var chunks = Object.keys(assets.chunks);
// Gather all css and script files
var styles = [];
var scripts = [];
chunks.forEach(function(chunkName) {
styles = styles.concat(assets.chunks[chunkName].css);
scripts.push(assets.chunks[chunkName].entry);
});
HtmlWebpackPlugin.prototype.injectAssetsIntoHtml = function (html, assets) {
// Turn script files into script tags
scripts = scripts.map(function(scriptPath) {
var scripts = assets.js.map(function (scriptPath) {
return '<script src="' + scriptPath + '"></script>';
});
// Turn css files into link tags
styles = styles.map(function(stylePath) {
var styles = assets.css.map(function (stylePath) {
return '<link href="' + stylePath + '" rel="stylesheet">';
});
// Injections
var htmlRegExp = /(<html[^>]*>)/i;
var head = [];
var headRegExp = /(<\/head>)/i;
var body = [];
var bodyRegExp = /(<\/body>)/i;
// If there is a favicon present, add it to the head
if (assets.favicon) {
@ -331,14 +430,37 @@ HtmlWebpackPlugin.prototype.injectAssetsIntoHtml = function(html, templateParams
} else {
body = body.concat(scripts);
}
// Append assets to head element
html = html.replace(/(<\/head>)/i, function (match) {
return head.join('') + match;
});
// Append assets to body element
html = html.replace(/(<\/body>)/i, function (match) {
return body.join('') + match;
if (body.length) {
if (bodyRegExp.test(html)) {
// Append assets to body element
html = html.replace(bodyRegExp, function (match) {
return body.join('') + match;
});
} else {
// Append scripts to the end of the file if no <body> element exists:
html += body.join('');
}
}
if (head.length) {
// Create a head tag if none exists
if (!headRegExp.test(html)) {
if (!htmlRegExp.test(html)) {
html = '<head></head>' + html;
} else {
html = html.replace(htmlRegExp, function (match) {
return match + '<head></head>';
});
}
}
// Append assets to head element
html = html.replace(headRegExp, function (match) {
return head.join('') + match;
});
}
// Inject manifest into the opening html tag
if (assets.manifest) {
html = html.replace(/(<html[^>]*)(>)/i, function (match, start, end) {
@ -352,18 +474,6 @@ HtmlWebpackPlugin.prototype.injectAssetsIntoHtml = function(html, templateParams
return html;
};
/**
* A helper to support the templates written for html-webpack-plugin <= 1.1.0
*/
HtmlWebpackPlugin.prototype.htmlWebpackPluginLegacyAssets = function(compilation, webpackStatsJson) {
var assets = this.htmlWebpackPluginAssets(compilation, webpackStatsJson);
var legacyAssets = {};
Object.keys(assets.chunks).forEach(function(chunkName){
legacyAssets[chunkName] = assets.chunks[chunkName].entry;
});
return legacyAssets;
};
/**
* Appends a cache busting hash
*/
@ -374,5 +484,17 @@ HtmlWebpackPlugin.prototype.appendHash = function (url, hash) {
return url + (url.indexOf('?') === -1 ? '?' : '&') + hash;
};
HtmlWebpackPlugin.prototype.getFullTemplatePath = function (template, context) {
// If the template doesn't use a loader use the lodash template loader
if (template.indexOf('!') === -1) {
template = require.resolve('./lib/loader.js') + '!' + path.resolve(context, template);
}
// Resolve template path
return template.replace(
/(\!)([^\/\\][^\!\?]+|[^\/\\!?])($|\?.+$)/,
function (match, prefix, filepath, postfix) {
return prefix + path.resolve(filepath) + postfix;
});
};
module.exports = HtmlWebpackPlugin;

100
lib/compiler.js Normal file
View File

@ -0,0 +1,100 @@
/*
* This file uses webpack to compile a template with a child compiler.
*
* [TEMPLATE] -> [JAVASCRIPT]
*
*/
'use strict';
var Promise = require('bluebird');
var path = require('path');
var NodeTemplatePlugin = require('webpack/lib/node/NodeTemplatePlugin');
var NodeTargetPlugin = require('webpack/lib/node/NodeTargetPlugin');
var LoaderTargetPlugin = require('webpack/lib/LoaderTargetPlugin');
var LibraryTemplatePlugin = require('webpack/lib/LibraryTemplatePlugin');
var SingleEntryPlugin = require('webpack/lib/SingleEntryPlugin');
/**
* Compiles the template into a nodejs factory, adds its to the compilation.assets
* and returns a promise of the result asset object.
*
* @param template relative path to the template file
* @param context path context
* @param outputFilename the file name
* @param compilation The webpack compilation object
*
* Returns an object:
* {
* hash: {String} - Base64 hash of the file
* content: {String} - Javascript executable code of the template
* }
*
*/
module.exports.compileTemplate = function compileTemplate (template, context, outputFilename, compilation) {
// The entry file is just an empty helper as the dynamic template
// require is added in "loader.js"
var outputOptions = {
filename: outputFilename,
publicPath: compilation.outputOptions.publicPath
};
var cachedAsset = compilation.assets[outputOptions.filename];
// Create an additional child compiler which takes the template
// and turns it into an Node.JS html factory.
// This allows us to use loaders during the compilation
var compilerName = getCompilerName(context, outputFilename);
var childCompiler = compilation.createChildCompiler(compilerName, outputOptions);
childCompiler.context = context;
childCompiler.apply(
new NodeTemplatePlugin(outputOptions),
new NodeTargetPlugin(),
new LibraryTemplatePlugin('HTML_WEBPACK_PLUGIN_RESULT', 'var'),
new SingleEntryPlugin(this.context, template),
new LoaderTargetPlugin('node')
);
// Fix for "Uncaught TypeError: __webpack_require__(...) is not a function"
// Hot module replacement requires that every child compiler has its own
// cache. @see https://github.com/ampedandwired/html-webpack-plugin/pull/179
childCompiler.plugin('compilation', function (compilation) {
if (compilation.cache) {
if (!compilation.cache[compilerName]) {
compilation.cache[compilerName] = {};
}
compilation.cache = compilation.cache[compilerName];
}
});
// Compile and return a promise
return new Promise(function (resolve, reject) {
childCompiler.runAsChild(function (err, entries, childCompilation) {
compilation.assets[outputOptions.filename] = cachedAsset;
if (cachedAsset === undefined) {
delete compilation.assets[outputOptions.filename];
}
// Resolve / reject the promise
if (childCompilation.errors && childCompilation.errors.length) {
var errorDetails = childCompilation.errors.map(function (error) {
return error.message + (error.error ? ':\n' + error.error : '');
}).join('\n');
reject(new Error('Child compilation failed:\n' + errorDetails));
} else if (err) {
reject(err);
} else {
resolve({
// Hash of the template entry point
hash: entries[0].hash,
// Compiled code
content: childCompilation.assets[outputOptions.filename].source()
});
}
});
});
};
/**
* Returns the child compiler name e.g. 'html-webpack-plugin for "index.html"'
*/
function getCompilerName (context, filename) {
var absolutePath = path.resolve(context, filename);
var relativePath = path.relative(context, absolutePath);
return 'html-webpack-plugin for "' + (absolutePath.length < relativePath.length ? absolutePath : relativePath) + '"';
}

23
lib/errors.js Normal file
View File

@ -0,0 +1,23 @@
'use strict';
var PrettyError = require('pretty-error');
var prettyError = new PrettyError();
prettyError.withoutColors();
prettyError.skipPackage(['html-plugin-evaluation']);
prettyError.skipNodeFiles();
prettyError.skip(function (traceLine) {
return traceLine.path === 'html-plugin-evaluation';
});
module.exports = function (err, context) {
return {
toHtml: function () {
return 'Html Webpack Plugin:\n<pre>\n' + this.toString() + '</pre>';
},
toJsonHtml: function () {
return JSON.stringify(this.toHtml());
},
toString: function () {
return prettyError.render(err).replace(/webpack:\/\/\/\./g, context);
}
};
};

26
lib/loader.js Normal file
View File

@ -0,0 +1,26 @@
/* This loader renders the template with underscore if no other loader was found */
'use strict';
var _ = require('lodash');
var loaderUtils = require('loader-utils');
module.exports = function (source) {
if (this.cacheable) {
this.cacheable();
}
var allLoadersButThisOne = this.loaders.filter(function (loader) {
return loader.module !== module.exports;
});
// This loader shouldn't kick in if there is any other loader
if (allLoadersButThisOne.length > 0) {
return source;
}
// Skip .js files
if (/\.js$/.test(this.request)) {
return source;
}
// Use underscore for a minimalistic loader
var options = loaderUtils.parseQuery(this.query);
var template = _.template(source, options);
return 'var _ = require(' + loaderUtils.stringifyRequest(this, require.resolve('lodash')) + ');module.exports = ' + template;
};

View File

@ -1,15 +1,15 @@
{
"name": "html-webpack-plugin",
"version": "1.7.0",
"version": "2.7.1",
"description": "Simplifies creation of HTML files to serve your webpack bundles",
"main": "index.js",
"files": [
"index.js",
"default_index.html",
"default_inject_index.html"
"default_index.ejs",
"lib/"
],
"scripts": {
"pretest": "jshint *.js spec",
"pretest": "semistandard",
"test": "jasmine-node --captureExceptions spec"
},
"repository": {
@ -20,7 +20,7 @@
"webpack",
"plugin",
"html",
"generate"
"html-webpack-plugin"
],
"author": "Charles Blaxland <charles.blaxland@gmail.com> (https://github.com/ampedandwired)",
"license": "MIT",
@ -29,21 +29,27 @@
},
"homepage": "https://github.com/ampedandwired/html-webpack-plugin",
"devDependencies": {
"css-loader": "^0.23.0",
"appcache-webpack-plugin": "^1.2.1",
"css-loader": "^0.23.1",
"dir-compare": "0.0.2",
"es6-promise": "^3.0.2",
"extract-text-webpack-plugin": "^0.9.1",
"file-loader": "^0.8.4",
"extract-text-webpack-plugin": "^1.0.1",
"file-loader": "^0.8.5",
"html-loader": "^0.4.0",
"jade-loader": "^0.8.0",
"jasmine-node": "^1.14.5",
"jshint": "^2.9.1-rc1",
"rimraf": "^2.4.4",
"rimraf": "^2.5.0",
"semistandard": "^7.0.5",
"style-loader": "^0.13.0",
"url-loader": "^0.5.6",
"webpack": "^1.12.8"
"underscore-template-loader": "^0.5.2",
"url-loader": "^0.5.7",
"webpack": "^1.12.10"
},
"dependencies": {
"bluebird": "^3.0.5",
"blueimp-tmpl": "^2.5.5",
"html-minifier": "^1.0.0",
"lodash": "^3.10.1"
"bluebird": "^3.1.1",
"html-minifier": "^1.1.1",
"loader-utils": "^0.2.12",
"lodash": "^3.10.1",
"pretty-error": "^2.0.0"
}
}

70
spec/ExampleSpec.js Normal file
View File

@ -0,0 +1,70 @@
/* global describe, it, expect */
'use strict';
// Workaround for css-loader issue
// https://github.com/webpack/css-loader/issues/144
if (!global.Promise) {
require('es6-promise').polyfill();
}
var path = require('path');
var webpack = require('webpack');
var rm_rf = require('rimraf');
var fs = require('fs');
var OUTPUT_DIR = path.join(__dirname, '../dist');
function runExample (exampleName, done) {
var examplePath = path.resolve(__dirname, '..', 'examples', exampleName);
var exampleOutput = path.join(OUTPUT_DIR, exampleName);
rm_rf(exampleOutput, function () {
var options = require(path.join(examplePath, 'webpack.config.js'));
options.context = examplePath;
options.output.path = exampleOutput;
webpack(options, function (err) {
var dircompare = require('dir-compare');
var res = dircompare.compareSync(path.join(examplePath, 'dist'), exampleOutput, {compareSize: true});
res.diffSet.filter(function (diff) {
return diff.state === 'distinct';
}).forEach(function (diff) {
expect(fs.readFileSync(path.join(diff.path1, diff.name1)).toString())
.toBe(fs.readFileSync(path.join(diff.path2, diff.name2)).toString());
});
expect(err).toBeFalsy();
expect(res.same).toBe(true);
done();
});
});
}
describe('HtmlWebpackPlugin Examples', function () {
it('appcache example', function (done) {
runExample('appcache', done);
});
it('custom-template example', function (done) {
runExample('custom-template', done);
});
it('default example', function (done) {
runExample('default', done);
});
it('favicon example', function (done) {
runExample('favicon', done);
});
it('html-loader example', function (done) {
runExample('html-loader', done);
});
it('jade-loader example', function (done) {
runExample('jade-loader', done);
});
it('javascript example', function (done) {
runExample('javascript', done);
});
});

View File

@ -1,6 +1,7 @@
/* global describe, it, beforeEach, expect */
'use strict';
// Workaround for css-loader issue
// Workaround for css-loader issue
// https://github.com/webpack/css-loader/issues/144
if (!global.Promise) {
require('es6-promise').polyfill();
@ -10,14 +11,14 @@ var path = require('path');
var fs = require('fs');
var webpack = require('webpack');
var rm_rf = require('rimraf');
var CommonsChunkPlugin = require("webpack/lib/optimize/CommonsChunkPlugin");
var CommonsChunkPlugin = require('webpack/lib/optimize/CommonsChunkPlugin');
var HtmlWebpackPlugin = require('../index.js');
var OUTPUT_DIR = path.join(__dirname, '../dist');
function testHtmlPlugin(webpackConfig, expectedResults, outputFile, done, expectErrors, expectWarnings) {
function testHtmlPlugin (webpackConfig, expectedResults, outputFile, done, expectErrors, expectWarnings) {
outputFile = outputFile || 'index.html';
webpack(webpackConfig, function(err, stats) {
webpack(webpackConfig, function (err, stats) {
expect(err).toBeFalsy();
var compilationErrors = (stats.compilation.errors || []).join('\n');
if (expectErrors) {
@ -31,6 +32,11 @@ function testHtmlPlugin(webpackConfig, expectedResults, outputFile, done, expect
} else {
expect(compilationWarnings).toBe('');
}
var outputFileExists = fs.existsSync(path.join(OUTPUT_DIR, outputFile));
expect(outputFileExists).toBe(true);
if (!outputFileExists) {
return done();
}
var htmlContent = fs.readFileSync(path.join(OUTPUT_DIR, outputFile)).toString();
for (var i = 0; i < expectedResults.length; i++) {
@ -45,12 +51,12 @@ function testHtmlPlugin(webpackConfig, expectedResults, outputFile, done, expect
});
}
describe('HtmlWebpackPlugin', function() {
beforeEach(function(done) {
describe('HtmlWebpackPlugin', function () {
beforeEach(function (done) {
rm_rf(OUTPUT_DIR, done);
});
it('generates a default index.html file for a single entry point', function(done) {
it('generates a default index.html file for a single entry point', function (done) {
testHtmlPlugin({
entry: path.join(__dirname, 'fixtures/index.js'),
output: {
@ -59,10 +65,9 @@ describe('HtmlWebpackPlugin', function() {
},
plugins: [new HtmlWebpackPlugin()]
}, [/<body>[\s]*<script src="index_bundle.js"><\/script>[\s]*<\/body>/], null, done);
});
it('generates a default index.html file with multiple entry points', function(done) {
it('generates a default index.html file with multiple entry points', function (done) {
testHtmlPlugin({
entry: {
util: path.join(__dirname, 'fixtures/util.js'),
@ -76,7 +81,7 @@ describe('HtmlWebpackPlugin', function() {
}, ['<script src="util_bundle.js"', '<script src="app_bundle.js"'], null, done);
});
it('allows you to specify your own HTML template file', function(done) {
it('allows you to specify a custom loader', function (done) {
testHtmlPlugin({
entry: {
app: path.join(__dirname, 'fixtures/index.js')
@ -85,23 +90,85 @@ describe('HtmlWebpackPlugin', function() {
path: OUTPUT_DIR,
filename: '[name]_bundle.js'
},
plugins: [new HtmlWebpackPlugin({template: path.join(__dirname, 'fixtures/test.html')})]
plugins: [new HtmlWebpackPlugin({
inject: false,
template: 'underscore-template-loader!' + path.join(__dirname, 'fixtures/test.html')
})]
},
['<script src="app_bundle.js', 'Some unique text'], null, done);
});
it('allows you to specify your own HTML template string', function(done) {
it('should pass through loader errors', function (done) {
testHtmlPlugin({
entry: {app: path.join(__dirname, 'fixtures/index.js')},
entry: {
app: path.join(__dirname, 'fixtures/index.js')
},
output: {
path: OUTPUT_DIR,
filename: 'app_bundle.js'
filename: '[name]_bundle.js'
},
plugins: [new HtmlWebpackPlugin({
templateContent: fs.readFileSync(path.join(__dirname, 'fixtures/test.html'), 'utf8')
inject: false,
template: path.join(__dirname, 'fixtures/invalid.html')
})]
},
['<script src="app_bundle.js'], null, done);
['ReferenceError: foo is not defined'], null, done, true);
});
it('uses a custom loader from webpacks config', function (done) {
testHtmlPlugin({
entry: {
app: path.join(__dirname, 'fixtures/index.js')
},
module: {
loaders: [
{test: /\.html$/, loader: 'underscore-template-loader'}
]
},
output: {
path: OUTPUT_DIR,
filename: '[name]_bundle.js'
},
plugins: [new HtmlWebpackPlugin({
inject: false,
template: path.join(__dirname, 'fixtures/test.html')
})]
},
['<script src="app_bundle.js', 'Some unique text'], null, done);
});
it('works when using html-loader', function (done) {
testHtmlPlugin({
entry: {
app: path.join(__dirname, 'fixtures/index.js')
},
output: {
path: OUTPUT_DIR,
filename: '[name]_bundle.js'
},
plugins: [new HtmlWebpackPlugin({
inject: true,
template: 'html-loader!' + path.join(__dirname, 'fixtures/plain.html')
})]
},
['<script src="app_bundle.js"'], null, done);
});
it('allows you to specify your own HTML template file', function (done) {
testHtmlPlugin({
entry: {
app: path.join(__dirname, 'fixtures/index.js')
},
output: {
path: OUTPUT_DIR,
filename: '[name]_bundle.js'
},
plugins: [new HtmlWebpackPlugin({
template: path.join(__dirname, 'fixtures/test.html'),
inject: false
})]
},
['<script src="app_bundle.js', 'Some unique text'], null, done);
});
it('allows you to inject the assets into a given html file', function (done) {
@ -121,23 +188,6 @@ describe('HtmlWebpackPlugin', function() {
}, ['<script src="util_bundle.js"', '<script src="app_bundle.js"'], null, done);
});
it('allows to specify windows/*nix paths to template', function (done) {
testHtmlPlugin({
entry: {
util: path.join(__dirname, 'fixtures/util.js'),
app: path.join(__dirname, 'fixtures/index.js')
},
output: {
path: OUTPUT_DIR,
filename: '[name]_bundle.js'
},
plugins: [new HtmlWebpackPlugin({
inject: true,
template: path.join(__dirname, 'fixtures') + '/plain.html'
})]
}, ['<script src="util_bundle.js"', '<script src="app_bundle.js"'], null, done);
});
it('allows you to inject the assets into the body of the given template', function (done) {
testHtmlPlugin({
entry: {
@ -172,24 +222,6 @@ describe('HtmlWebpackPlugin', function() {
}, ['<script src="util_bundle.js"', '<script src="app_bundle.js"'], null, done);
});
it('allows you to inject the assets into a html string', function (done) {
testHtmlPlugin({
entry: {
util: path.join(__dirname, 'fixtures/util.js'),
app: path.join(__dirname, 'fixtures/index.js')
},
output: {
path: OUTPUT_DIR,
filename: '[name]_bundle.js'
},
plugins: [new HtmlWebpackPlugin({
inject: true,
chunks: ['util', 'app'],
templateContent: fs.readFileSync(path.join(__dirname, 'fixtures/plain.html'), 'utf8')
})]
}, ['<script src="util_bundle.js"', '<script src="app_bundle.js"'], null, done);
});
it('allows you to inject a specified asset into a given html file', function (done) {
testHtmlPlugin({
entry: {
@ -226,35 +258,24 @@ describe('HtmlWebpackPlugin', function() {
}, ['<script src="app_bundle.js"'], null, done);
});
it('allows you to use the deprecated assets object', function (done) {
it('allows you to disable injection', function (done) {
testHtmlPlugin({
entry: {
app: path.join(__dirname, 'fixtures/index.js')
},
output: {
path: OUTPUT_DIR,
filename: '[name]_bundle.js'
},
plugins: [new HtmlWebpackPlugin({template: path.join(__dirname, 'fixtures/legacy.html')})]
entry: {
util: path.join(__dirname, 'fixtures/util.js'),
app: path.join(__dirname, 'fixtures/index.js')
},
['<script src="app_bundle.js', 'Some unique text'], null, done, false, true);
output: {
path: OUTPUT_DIR,
filename: '[name]_bundle.js'
},
plugins: [new HtmlWebpackPlugin({
inject: false,
template: path.join(__dirname, 'fixtures/plain.html')
})]
}, ['<body>\n</body>'], null, done);
});
it('allows you to use a deprecated legacy_index template', function (done) {
testHtmlPlugin({
entry: {
app: path.join(__dirname, 'fixtures/index.js')
},
output: {
path: OUTPUT_DIR,
filename: '[name]_bundle.js'
},
plugins: [new HtmlWebpackPlugin({template: path.join(__dirname, 'fixtures/legacy_default_index.html')})]
},
['<script src="app_bundle.js'], null, done, false, true);
});
it('allows you to specify your own HTML template function', function(done) {
it('allows you to specify your own HTML template function', function (done) {
testHtmlPlugin({
entry: {app: path.join(__dirname, 'fixtures/index.js')},
output: {
@ -262,33 +283,15 @@ describe('HtmlWebpackPlugin', function() {
filename: 'app_bundle.js'
},
plugins: [new HtmlWebpackPlugin({
templateContent: function() {
return fs.readFileSync(path.join(__dirname, 'fixtures/test.html'), 'utf8');
templateContent: function () {
return fs.readFileSync(path.join(__dirname, 'fixtures/plain.html'), 'utf8');
}
})]
},
['<script src="app_bundle.js"'], null, done);
});
it('registers a webpack error both template and template content are specified', function(done) {
webpack({
entry: path.join(__dirname, 'fixtures/index.js'),
output: {
path: OUTPUT_DIR,
filename: 'index_bundle.js'
},
plugins: [new HtmlWebpackPlugin({
template: path.join(__dirname, 'fixtures/test.html'),
templateContent: 'whatever'
})]
}, function(err, stats) {
expect(stats.hasErrors()).toBe(true);
expect(stats.toJson().errors[0]).toContain('HtmlWebpackPlugin');
done();
});
});
it('works with source maps', function(done) {
it('works with source maps', function (done) {
testHtmlPlugin({
devtool: 'sourcemap',
entry: path.join(__dirname, 'fixtures/index.js'),
@ -300,7 +303,7 @@ describe('HtmlWebpackPlugin', function() {
}, ['<script src="index_bundle.js"'], null, done);
});
it('handles hashes in bundle filenames', function(done) {
it('handles hashes in bundle filenames', function (done) {
testHtmlPlugin({
entry: path.join(__dirname, 'fixtures/index.js'),
output: {
@ -308,22 +311,10 @@ describe('HtmlWebpackPlugin', function() {
filename: 'index_bundle_[hash].js'
},
plugins: [new HtmlWebpackPlugin()]
}, [/<script src="index_bundle_[0-9a-f]+\.js/], null, done);
}, [/<script src="index_bundle_[0-9a-f]+\.js"*/], null, done);
});
it('handles hashes in publicPath', function(done) {
testHtmlPlugin({
entry: path.join(__dirname, 'fixtures/index.js'),
output: {
path: OUTPUT_DIR,
filename: 'index_bundle.js',
publicPath: 'assets/[hash]/'
},
plugins: [new HtmlWebpackPlugin()]
}, [/<script src="assets\/[0-9a-f]+\/index_bundle\.js/], null, done);
});
it('allows to append hashes to the assets', function(done) {
it('allows to append hashes to the assets', function (done) {
testHtmlPlugin({
entry: path.join(__dirname, 'fixtures/index.js'),
output: {
@ -334,7 +325,7 @@ describe('HtmlWebpackPlugin', function() {
}, ['<script src="index_bundle.js?%hash%"'], null, done);
});
it('allows to append hashes to the assets', function(done) {
it('allows to append hashes to the assets', function (done) {
testHtmlPlugin({
entry: path.join(__dirname, 'fixtures/index.js'),
output: {
@ -346,7 +337,7 @@ describe('HtmlWebpackPlugin', function() {
});
it('should work with the css extract plugin', function (done) {
var ExtractTextPlugin = require("extract-text-webpack-plugin");
var ExtractTextPlugin = require('extract-text-webpack-plugin');
testHtmlPlugin({
entry: path.join(__dirname, 'fixtures/theme.js'),
output: {
@ -360,13 +351,13 @@ describe('HtmlWebpackPlugin', function() {
},
plugins: [
new HtmlWebpackPlugin(),
new ExtractTextPlugin("styles.css")
new ExtractTextPlugin('styles.css')
]
}, ['<link href="styles.css"'], null, done);
});
it('should allow to add cache hashes to with the css assets', function (done) {
var ExtractTextPlugin = require("extract-text-webpack-plugin");
var ExtractTextPlugin = require('extract-text-webpack-plugin');
testHtmlPlugin({
entry: path.join(__dirname, 'fixtures/theme.js'),
output: {
@ -380,13 +371,13 @@ describe('HtmlWebpackPlugin', function() {
},
plugins: [
new HtmlWebpackPlugin({hash: true}),
new ExtractTextPlugin("styles.css")
new ExtractTextPlugin('styles.css')
]
}, ['<link href="styles.css?%hash%"'], null, done);
});
it('should inject css files when using the extract text plugin', function (done) {
var ExtractTextPlugin = require("extract-text-webpack-plugin");
var ExtractTextPlugin = require('extract-text-webpack-plugin');
testHtmlPlugin({
entry: path.join(__dirname, 'fixtures/theme.js'),
output: {
@ -400,13 +391,13 @@ describe('HtmlWebpackPlugin', function() {
},
plugins: [
new HtmlWebpackPlugin({inject: true}),
new ExtractTextPlugin("styles.css")
new ExtractTextPlugin('styles.css')
]
}, ['<link href="styles.css"'], null, done);
});
it('should allow to add cache hashes to with injected css assets', function (done) {
var ExtractTextPlugin = require("extract-text-webpack-plugin");
var ExtractTextPlugin = require('extract-text-webpack-plugin');
testHtmlPlugin({
entry: path.join(__dirname, 'fixtures/theme.js'),
output: {
@ -420,12 +411,12 @@ describe('HtmlWebpackPlugin', function() {
},
plugins: [
new HtmlWebpackPlugin({hash: true, inject: true}),
new ExtractTextPlugin("styles.css")
new ExtractTextPlugin('styles.css')
]
}, ['<link href="styles.css?%hash%"'], null, done);
});
it('prepends the webpack public path to script src', function(done) {
it('prepends the webpack public path to script src', function (done) {
testHtmlPlugin({
entry: path.join(__dirname, 'fixtures/index.js'),
output: {
@ -437,7 +428,7 @@ describe('HtmlWebpackPlugin', function() {
}, ['<script src="http://cdn.example.com/assets/index_bundle.js"'], null, done);
});
it('handles subdirectories in the webpack output bundles', function(done) {
it('handles subdirectories in the webpack output bundles', function (done) {
testHtmlPlugin({
entry: path.join(__dirname, 'fixtures/index.js'),
output: {
@ -448,7 +439,7 @@ describe('HtmlWebpackPlugin', function() {
}, ['<script src="assets/index_bundle.js"'], null, done);
});
it('handles subdirectories in the webpack output bundles along with a public path', function(done) {
it('handles subdirectories in the webpack output bundles along with a public path', function (done) {
testHtmlPlugin({
entry: path.join(__dirname, 'fixtures/index.js'),
output: {
@ -460,7 +451,7 @@ describe('HtmlWebpackPlugin', function() {
}, ['<script src="http://cdn.example.com/assets/index_bundle.js"'], null, done);
});
it('allows you to configure the title of the generated HTML page', function(done) {
it('allows you to configure the title of the generated HTML page', function (done) {
testHtmlPlugin({
entry: path.join(__dirname, 'fixtures/index.js'),
output: {
@ -471,7 +462,7 @@ describe('HtmlWebpackPlugin', function() {
}, ['<title>My Cool App</title>'], null, done);
});
it('allows you to configure the output filename', function(done) {
it('allows you to configure the output filename', function (done) {
testHtmlPlugin({
entry: path.join(__dirname, 'fixtures/index.js'),
output: {
@ -482,7 +473,7 @@ describe('HtmlWebpackPlugin', function() {
}, ['<script src="index_bundle.js"'], 'test.html', done);
});
it('will try to use a relative name if the filename is in a subdirectory', function(done) {
it('will try to use a relative name if the filename is in a subdirectory', function (done) {
testHtmlPlugin({
entry: path.join(__dirname, 'fixtures/index.js'),
output: {
@ -493,7 +484,7 @@ describe('HtmlWebpackPlugin', function() {
}, ['<script src="../index_bundle.js"'], 'assets/test.html', done);
});
it('will try to use a relative name if the filename and the script are in a subdirectory', function(done) {
it('will try to use a relative name if the filename and the script are in a subdirectory', function (done) {
testHtmlPlugin({
entry: path.join(__dirname, 'fixtures/index.js'),
output: {
@ -504,7 +495,7 @@ describe('HtmlWebpackPlugin', function() {
}, ['<script src="../../assets/index_bundle.js"'], 'assets/demo/test.html', done);
});
it('allows you write multiple HTML files', function(done) {
it('allows you write multiple HTML files', function (done) {
testHtmlPlugin({
entry: {
app: path.join(__dirname, 'fixtures/index.js')
@ -516,31 +507,42 @@ describe('HtmlWebpackPlugin', function() {
plugins: [
new HtmlWebpackPlugin(),
new HtmlWebpackPlugin({
filename: 'test.html',
filename: 'second-file.html',
template: path.join(__dirname, 'fixtures/test.html')
}),
new HtmlWebpackPlugin({
filename: 'third-file.html',
template: path.join(__dirname, 'fixtures/test.html')
})
]
}, ['<script src="index_bundle.js"'], null, done);
expect(fs.existsSync(path.join(__dirname, 'fixtures/test.html'))).toBe(true);
});
it('registers a webpack error if the template cannot be opened', function(done) {
webpack({
entry: path.join(__dirname, 'fixtures/index.js'),
output: {
path: OUTPUT_DIR,
filename: 'index_bundle.js'
},
plugins: [new HtmlWebpackPlugin({template: 'fixtures/does_not_exist.html'})]
}, function(err, stats) {
expect(stats.hasErrors()).toBe(true);
expect(stats.toJson().errors[0]).toContain('HtmlWebpackPlugin');
}, ['<script src="index_bundle.js"'], null, function () {
expect(fs.existsSync(path.join(OUTPUT_DIR, 'second-file.html'))).toBe(true);
expect(fs.existsSync(path.join(OUTPUT_DIR, 'third-file.html'))).toBe(true);
done();
});
});
it('exposes the webpack configuration to templates', function(done) {
it('should inject js css files even if the html file is incomplete', function (done) {
var ExtractTextPlugin = require('extract-text-webpack-plugin');
testHtmlPlugin({
entry: path.join(__dirname, 'fixtures/theme.js'),
output: {
path: OUTPUT_DIR,
filename: 'index_bundle.js'
},
module: {
loaders: [
{ test: /\.css$/, loader: ExtractTextPlugin.extract('style-loader', 'css-loader') }
]
},
plugins: [
new HtmlWebpackPlugin({template: path.join(__dirname, 'fixtures/empty_html.html')}),
new ExtractTextPlugin('styles.css')
]
}, ['<link href="styles.css"', '<script src="index_bundle.js"'], null, done);
});
it('exposes the webpack configuration to templates', function (done) {
testHtmlPlugin({
entry: {
app: path.join(__dirname, 'fixtures/index.js')
@ -555,7 +557,160 @@ describe('HtmlWebpackPlugin', function() {
['Public path is https://cdn.com'], null, done);
});
it('works with commons chunk plugin', function(done) {
it('fires the html-webpack-plugin-before-html-processing event', function (done) {
var eventFired = false;
var examplePlugin = {
apply: function (compiler) {
compiler.plugin('compilation', function (compilation) {
compilation.plugin('html-webpack-plugin-before-html-processing', function (object, callback) {
eventFired = true;
callback();
});
});
}
};
testHtmlPlugin({
entry: {
app: path.join(__dirname, 'fixtures/index.js')
},
output: {
path: OUTPUT_DIR,
filename: '[name]_bundle.js'
},
plugins: [
new HtmlWebpackPlugin(),
examplePlugin
]
}, [], null, function () {
expect(eventFired).toBe(true);
done();
});
});
it('fires the html-webpack-plugin-after-html-processing event', function (done) {
var eventFired = false;
var examplePlugin = {
apply: function (compiler) {
compiler.plugin('compilation', function (compilation) {
compilation.plugin('html-webpack-plugin-after-html-processing', function (object, callback) {
eventFired = true;
callback();
});
});
}
};
testHtmlPlugin({
entry: {
app: path.join(__dirname, 'fixtures/index.js')
},
output: {
path: OUTPUT_DIR,
filename: '[name]_bundle.js'
},
plugins: [
new HtmlWebpackPlugin(),
examplePlugin
]
}, [], null, function () {
expect(eventFired).toBe(true);
done();
});
});
it('fires the html-webpack-plugin-after-emit event', function (done) {
var eventFired = false;
var examplePlugin = {
apply: function (compiler) {
compiler.plugin('compilation', function (compilation) {
compilation.plugin('html-webpack-plugin-after-emit', function (object, callback) {
eventFired = true;
callback();
});
});
}
};
testHtmlPlugin({
entry: {
app: path.join(__dirname, 'fixtures/index.js')
},
output: {
path: OUTPUT_DIR,
filename: '[name]_bundle.js'
},
plugins: [
new HtmlWebpackPlugin(),
examplePlugin
]
}, [], null, function () {
expect(eventFired).toBe(true);
done();
});
});
it('allows to modify the html during html-webpack-plugin-after-html-processing event', function (done) {
var eventFired = false;
var examplePlugin = {
apply: function (compiler) {
compiler.plugin('compilation', function (compilation) {
compilation.plugin('html-webpack-plugin-after-html-processing', function (object, callback) {
eventFired = true;
object.html += 'Injected by plugin';
callback();
});
});
}
};
testHtmlPlugin({
entry: {
app: path.join(__dirname, 'fixtures/index.js')
},
output: {
path: OUTPUT_DIR,
filename: '[name]_bundle.js'
},
plugins: [
new HtmlWebpackPlugin(),
examplePlugin
]
}, ['Injected by plugin'], null, function () {
expect(eventFired).toBe(true);
done();
});
});
it('allows to modify the html during html-webpack-plugin-before-html-processing event', function (done) {
var eventFired = false;
var examplePlugin = {
apply: function (compiler) {
compiler.plugin('compilation', function (compilation) {
compilation.plugin('html-webpack-plugin-before-html-processing', function (object, callback) {
eventFired = true;
object.assets.js.push('funky-script.js');
object.html += 'Injected by plugin';
callback();
});
});
}
};
testHtmlPlugin({
entry: {
app: path.join(__dirname, 'fixtures/index.js')
},
output: {
path: OUTPUT_DIR,
filename: '[name]_bundle.js'
},
plugins: [
new HtmlWebpackPlugin(),
examplePlugin
]
}, ['Injected by plugin', '<script src="funky-script.js"'], null, function () {
expect(eventFired).toBe(true);
done();
});
});
it('works with commons chunk plugin', function (done) {
testHtmlPlugin({
debug: true,
verbose: true,
@ -570,7 +725,7 @@ describe('HtmlWebpackPlugin', function() {
plugins: [
new CommonsChunkPlugin({
name: 'common',
filename: "common_bundle.js",
filename: 'common_bundle.js'
}),
new HtmlWebpackPlugin()
]
@ -579,7 +734,7 @@ describe('HtmlWebpackPlugin', function() {
/<script src="common_bundle.js"[\s\S]*<script src="index_bundle.js">/], null, done);
});
it('adds a favicon', function(done) {
it('adds a favicon', function (done) {
testHtmlPlugin({
entry: path.join(__dirname, 'fixtures/index.js'),
output: {
@ -594,7 +749,7 @@ describe('HtmlWebpackPlugin', function() {
}, [/<link rel="shortcut icon" href="[^"]+\.ico">/], null, done);
});
it('adds a favicon with inject enabled', function(done) {
it('adds a favicon with inject enabled', function (done) {
testHtmlPlugin({
entry: path.join(__dirname, 'fixtures/index.js'),
output: {
@ -610,7 +765,7 @@ describe('HtmlWebpackPlugin', function() {
}, [/<link rel="shortcut icon" href="[^"]+\.ico">/], null, done);
});
it('shows an error if the favicon could not be load', function(done) {
it('shows an error if the favicon could not be load', function (done) {
testHtmlPlugin({
entry: path.join(__dirname, 'fixtures/index.js'),
output: {
@ -626,7 +781,68 @@ describe('HtmlWebpackPlugin', function() {
}, ['Error: HtmlWebpackPlugin: could not load file'], null, done, true);
});
it('should short the chunks', function(done) {
it('adds a manifest', function (done) {
var AppCachePlugin = require('appcache-webpack-plugin');
testHtmlPlugin({
entry: path.join(__dirname, 'fixtures/index.js'),
output: {
path: OUTPUT_DIR,
filename: 'index_bundle.js'
},
plugins: [
new AppCachePlugin({settings: ['prefer-online']}),
new HtmlWebpackPlugin()
]
}, ['<html manifest="manifest.appcache">'], null, done);
});
it('does not add a manifest if already present', function (done) {
var AppCachePlugin = require('appcache-webpack-plugin');
testHtmlPlugin({
entry: path.join(__dirname, 'fixtures/index.js'),
output: {
path: OUTPUT_DIR,
filename: 'index_bundle.js'
},
plugins: [
new AppCachePlugin({settings: ['prefer-online']}),
new HtmlWebpackPlugin({
template: path.join(__dirname, 'fixtures/plain.html')
})
]
}, ['<html lang="en" manifest="foo.appcache">'], null, done);
});
it('works with webpack bannerplugin', function (done) {
testHtmlPlugin({
entry: path.join(__dirname, 'fixtures/index.js'),
output: {
path: OUTPUT_DIR,
filename: 'index_bundle.js'
},
plugins: [
new webpack.BannerPlugin('Copyright and such.'),
new HtmlWebpackPlugin()
]
}, ['<html'], null, done);
});
it('shows an error when a template fails to load', function (done) {
testHtmlPlugin({
entry: path.join(__dirname, 'fixtures/index.js'),
output: {
path: OUTPUT_DIR,
filename: 'index_bundle.js'
},
plugins: [
new HtmlWebpackPlugin({
template: path.join(__dirname, 'fixtures/non-existing-template.html')
})
]
}, ["Child compilation failed:\n Entry module not found: Error: Cannot resolve 'file' or 'directory'"], null, done, true);
});
it('should short the chunks', function (done) {
testHtmlPlugin({
entry: {
util: path.join(__dirname, 'fixtures/util.js'),
@ -639,17 +855,17 @@ describe('HtmlWebpackPlugin', function() {
plugins: [
new CommonsChunkPlugin({
name: 'common',
filename: "common_bundle.js",
filename: 'common_bundle.js'
}),
new HtmlWebpackPlugin({
chunksSortMode: 'auto'
})
]
}, [
/<script src="common_bundle.js">[\s\S]+<script src="util_bundle.js">[\s\S]+<script src="index_bundle.js">/], null, done);
/<script src="common_bundle.js">.+<script src="util_bundle.js">.+<script src="index_bundle.js">/], null, done);
});
it('should short the chunks with custom (alphabetical) order', function(done) {
it('should short the chunks with custom (alphabetical) order', function (done) {
testHtmlPlugin({
entry: {
b: path.join(__dirname, 'fixtures/index.js'),
@ -662,14 +878,17 @@ describe('HtmlWebpackPlugin', function() {
},
plugins: [
new HtmlWebpackPlugin({
chunksSortMode: function(a, b) {
if(a.name < b.name) {return 1;}
if(a.name > b.name) {return -1;}
chunksSortMode: function (a, b) {
if (a.name < b.name) {
return 1;
}
if (a.name > b.name) {
return -1;
}
return 0;
}
})
]
}, [/<script src="a_bundle.js">[\s\S]+<script src="b_bundle.js">[\s\S]+<script src="c_bundle.js">/], null, done);
}, [/<script src="a_bundle.js">.+<script src="b_bundle.js">.+<script src="c_bundle.js">/], null, done);
});
});

View File

@ -1,3 +1,2 @@
'use strict';
module.exports = "async";
module.exports = 'async';

View File

@ -1,3 +1,3 @@
'use strict';
module.exports = "common";
module.exports = 'common';

0
spec/fixtures/empty_html.html vendored Normal file
View File

View File

@ -2,8 +2,8 @@
require('./common');
require.ensure([], function() {
require.ensure([], function () {
require('./async');
});
document.body.innerHTML = document.body.innerHTML + "<p>index.js</p>";
document.body.innerHTML = document.body.innerHTML + '<p>index.js</p>';

11
spec/fixtures/invalid.html vendored Normal file
View File

@ -0,0 +1,11 @@
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-type" content="text/html; charset=utf-8"/>
<title>Test</title>
</head>
<body>
<p>Some unique text</p>
<script src="<%=foo.bar%>"></script>
</body>
</html>

View File

@ -1,12 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>{%=o.htmlWebpackPlugin.options.title || 'Webpack App'%}</title>
</head>
<body>
{% for (var chunk in o.htmlWebpackPlugin.assets) { %}
<script src="{%=o.htmlWebpackPlugin.assets[chunk]%}"></script>
{% } %}
</body>
</html>

View File

@ -1,5 +1,5 @@
<!doctype html>
<html lang="en">
<html lang="en" manifest="foo.appcache">
<head>
<meta charset="utf-8">
<title>Example Plain file</title>
@ -7,4 +7,4 @@
</head>
<body>
</body>
</html>
</html>

View File

@ -6,6 +6,6 @@
</head>
<body>
<p>Some unique text</p>
<script src="{%=o.htmlWebpackPlugin.files.chunks.app.entry%}"></script>
<script src="<%=htmlWebpackPlugin.files.chunks.app.entry%>"></script>
</body>
</html>

View File

@ -1,4 +1,4 @@
'use strict';
require('./main.css');
require('./index.js');
require('./index.js');

View File

@ -1,3 +1,4 @@
'use strict';
require('./common');
document.body.innerHTML = document.body.innerHTML + "<p>util.js</p>";
document.body.innerHTML = document.body.innerHTML + '<p>util.js</p>';

View File

@ -5,7 +5,7 @@
<title>Test</title>
</head>
<body>
<p>Public path is {%=o.webpackConfig.output.publicPath%}</p>
<script src="{%=o.htmlWebpackPlugin.files.chunks.app.entry%}"></script>
<p>Public path is <%= webpackConfig.output.publicPath %></p>
<script src="<%= htmlWebpackPlugin.files.chunks.app.entry %>"></script>
</body>
</html>