Added logging, changed some directory structure

This commit is contained in:
2018-01-13 21:33:40 -05:00
parent f079a5f067
commit 8e72ffb917
73656 changed files with 35284 additions and 53718 deletions

View File

@@ -0,0 +1,264 @@
Change History
==============
v2.28.0
---
* Backport 3.x void tag for plugin authors
v2.27.1
---
* Revert 2.25.0 loader resolving
v2.27.0
---
* Fix a chunksorter webpack 2 issue (#569)
* Fix template path resolving(#542)
v2.26.0
---
* Allow plugins to add attributes without values to the `<script>` and `<link>` tags
v2.25.0
---
* Clearer loader output
* Add basic support for webpack 2
v2.24.1
---
* Hide event deprecated warning of 'applyPluginsAsyncWaterfall' for html-webpack-plugin-after-emit and improve the warning message.
v2.24.0
---
* Update dependencies
* Add deprecate warning for plugins not returning a result
* Add [path] for favicons
v2.23.0
---
* Update dependencies
* Stop automated tests for webpack 2 beta because of #401
v2.22.0
---
* Update dependencies
v2.21.1
---
* Better error handling (#354)
v2.21.0
----
* Add `html-webpack-plugin-alter-asset-tags` event to allow plugins to adjust the script/link tags
v2.20.0
----
* Exclude chunks works now even if combined with dependency sort
v2.19.0
----
* Add `html-webpack-plugin-alter-chunks` event for custom chunk sorting and interpolation
v2.18.0
----
* Updated all dependencies
v2.17.0
----
* Add `type` attribute to `script` element to prevent issues in Safari 9.1.1
v2.16.2
----
* Fix bug introduced by 2.16.2. Fixes #315
v2.16.1
----
* Fix hot module replacement for webpack 2.x
v2.16.0
----
* Add support for dynamic filenames like index[hash].html
v2.15.0
----
* Add full unit test coverage for the webpack 2 beta version
* For webpack 2 the default sort will be 'dependency' instead of 'id'
* Upgrade dependencies
v2.14.0
----
* Export publicPath to the template
* Add example for inlining css and js
v2.13.0
----
* Add support for absolute output file names
* Add support for relative file names outside the output path
v2.12.0
----
* Basic Webpack 2.x support #225
v2.11.0
----
* Add `xhtml` option which is turned of by default. When activated it will inject self closed `<link href=".." />` tags instead of unclosed `<link href="..">` tags. https://github.com/ampedandwired/html-webpack-plugin/pull/255
* Add support for webpack placeholders inside the public path e.g. `'/dist/[hash]/'`. https://github.com/ampedandwired/html-webpack-plugin/pull/249
v2.10.0
----
* Add `hash` field to the chunk object
* Add `compilation` field to the templateParam object (fixes https://github.com/ampedandwired/html-webpack-plugin/issues/237)
* Add `html-webpack-plugin-before-html-generation` event
* Improve error messages
v2.9.0
----
* Fix favicon path (fixes https://github.com/ampedandwired/html-webpack-plugin/issues/185, https://github.com/ampedandwired/html-webpack-plugin/issues/208, https://github.com/ampedandwired/html-webpack-plugin/pull/215 )
v2.8.2
----
* Support relative URLs on Windows (fixes https://github.com/ampedandwired/html-webpack-plugin/issues/205 )
v2.8.1
----
* Caching improvements (fixes https://github.com/ampedandwired/html-webpack-plugin/issues/204 )
v2.8.0
----
* Add `dependency` mode for `chunksSortMode` to sort chunks based on their dependencies with each other
v2.7.2
----
* Add support for require in js templates
v2.7.1
----
* Refactoring
* Fix relative windows path
v2.6.5
----
* Minor refactoring
v2.6.4
----
* Fix for `"Uncaught TypeError: __webpack_require__(...) is not a function"`
* Fix incomplete cache modules causing "HtmlWebpackPlugin Error: No source available"
* Fix some issues on Windows
v2.6.3
----
* Prevent parsing the base template with the html-loader
v2.6.2
----
* Fix `lodash` resolve error (fixes https://github.com/ampedandwired/html-webpack-plugin/issues/172 )
v2.6.1
----
* Fix missing module (fixes https://github.com/ampedandwired/html-webpack-plugin/issues/164 )
v2.6.0
----
* Move compiler to its own file
* Improve error messages
* Fix global HTML_WEBPACK_PLUGIN variable
v2.5.0
----
* Support `lodash` template's HTML _"escape"_ delimiter (`<%- %>`)
* Fix bluebird warning (fixes https://github.com/ampedandwired/html-webpack-plugin/issues/130 )
* Fix an issue where incomplete cache modules were used
v2.4.0
----
* Don't recompile if the assets didn't change
v2.3.0
----
* Add events `html-webpack-plugin-before-html-processing`, `html-webpack-plugin-after-html-processing`, `html-webpack-plugin-after-emit` to allow other plugins to alter the html this plugin executes
v2.2.0
----
* Inject css and js even if the html file is incomplete (fixes https://github.com/ampedandwired/html-webpack-plugin/issues/135 )
* Update dependencies
v2.1.0
----
* Synchronize with the stable `@1` version
v2.0.4
----
* Fix `minify` option
* Fix missing hash interpolation in publicPath
v2.0.3
----
* Add support for webpack.BannerPlugin
v2.0.2
----
* Add support for loaders in templates (fixes https://github.com/ampedandwired/html-webpack-plugin/pull/41 )
* Remove `templateContent` option from configuration
* Better error messages
* Update dependencies
v1.7.0
----
* Add `chunksSortMode` option to configuration to control how chunks should be sorted before they are included to the html
* Don't insert async chunks into html (fixes https://github.com/ampedandwired/html-webpack-plugin/issues/95 )
* Update dependencies
v1.6.2
----
* Fix paths on Windows
* Fix missing hash interpolation in publicPath
* Allow only `false` or `object` in `minify` configuration option
v1.6.1
----
* Add `size` field to the chunk object
* Fix stylesheet `<link>`s being discarded when used with `"inject: 'head'"`
* Update dependencies
v1.6.0
----
* Support placing templates in subfolders
* Don't include chunks with undefined name (fixes https://github.com/ampedandwired/html-webpack-plugin/pull/60 )
* Don't include async chunks
v1.5.2
----
* Update dependencies (lodash)
v1.5.1
----
* Fix error when manifest is specified (fixes https://github.com/ampedandwired/html-webpack-plugin/issues/56 )
v1.5.0
----
* Allow to inject javascript files into the head of the html page
* Fix error reporting
v1.4.0
----
* Add `favicon.ico` option
* Add html minifcation
v1.2.0
------
* Set charset using HTML5 meta attribute
* Reload upon change when using webpack watch mode
* Generate manifest attribute when using
[appcache-webpack-plugin](https://github.com/lettertwo/appcache-webpack-plugin)
* Optionally add webpack hash as a query string to resources included in the HTML
(`hash: true`) for cache busting
* CSS files generated using webpack (for example, by using the
[extract-text-webpack-plugin](https://github.com/webpack/extract-text-webpack-plugin))
are now automatically included into the generated HTML
* More detailed information about the files generated by webpack is now available
to templates in the `o.htmlWebpackPlugin.files` attribute. See readme for more
details. This new attribute deprecates the old `o.htmlWebpackPlugin.assets` attribute.
* The `templateContent` option can now be a function that returns the template string to use
* Expose webpack configuration to templates (`o.webpackConfig`)
* Sort chunks to honour dependencies between them (useful for use with CommonsChunkPlugin).

View File

@@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2014 Charles Blaxland
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -0,0 +1,322 @@
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/jantimon/html-webpack-plugin.svg)](https://david-dm.org/jantimon/html-webpack-plugin) [![Build status](https://travis-ci.org/jantimon/html-webpack-plugin.svg)](https://travis-ci.org/jantimon/html-webpack-plugin) [![Windows build status](https://ci.appveyor.com/api/projects/status/github/jantimon/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) [![bitHound Dependencies](https://www.bithound.io/github/jantimon/html-webpack-plugin/badges/dependencies.svg)](https://www.bithound.io/github/jantimon/html-webpack-plugin/master/dependencies/npm) [![license](https://img.shields.io/github/license/mashape/apistatus.svg?maxAge=2592000)]()
[![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, supply
your own template using lodash templates or use your own loader.
Maintainer: Jan Nicklas [@jantimon](https://twitter.com/jantimon)
Installation
------------
Install the plugin with npm:
```shell
$ npm install html-webpack-plugin --save-dev
```
Third party addons:
-------------
The html-webpack-plugin provides [hooks](https://github.com/jantimon/html-webpack-plugin#events) to extend it to your needs.
There are already some really powerful plugins which can be integrated with zero configuration:
* [webpack-subresource-integrity](https://www.npmjs.com/package/webpack-subresource-integrity) for enhanced asset security
* [appcache-webpack-plugin](https://github.com/lettertwo/appcache-webpack-plugin) for iOS and Android offline usage
* [favicons-webpack-plugin](https://github.com/jantimon/favicons-webpack-plugin) which generates favicons and icons for iOS, Android and desktop browsers
* [html-webpack-harddisk-plugin](https://github.com/jantimon/html-webpack-harddisk-plugin) can be used to always write to disk the html file, useful when webpack-dev-server / HMR are being used
* [html-webpack-inline-source-plugin](https://github.com/DustinJackson/html-webpack-inline-source-plugin) to inline your assets in the resulting HTML file
* [html-webpack-exclude-assets-plugin](https://github.com/jamesjieye/html-webpack-exclude-assets-plugin) for excluding assets using regular expressions
* [html-webpack-include-assets-plugin](https://github.com/jharris4/html-webpack-include-assets-plugin) for including lists of js or css file paths (such as those copied by the copy-webpack-plugin).
* [script-ext-html-webpack-plugin](https://github.com/numical/script-ext-html-webpack-plugin) to add `async`, `defer` or `module` attributes to your`<script>` elements, or even in-line them
* [style-ext-html-webpack-plugin](https://github.com/numical/style-ext-html-webpack-plugin) to convert your `<link>`s to external stylesheets into `<style>` elements containing internal CSS
* [resource-hints-webpack-plugin](https://github.com/jantimon/resource-hints-webpack-plugin) to add resource hints for faster initial page loads using `<link rel='preload'>` and `<link rel='prefetch'>`
* [preload-webpack-plugin](https://github.com/GoogleChrome/preload-webpack-plugin) for automatically wiring up asynchronous (and other types) of JavaScript chunks using `<link rel='preload'>` helping with lazy-loading
* [link-media-html-webpack-plugin](https://github.com/yaycmyk/link-media-html-webpack-plugin) allows for injected stylesheet `<link />` tags to have their media attribute set automatically; useful for providing specific desktop/mobile/print etc. stylesheets that the browser will conditionally download
* [inline-chunk-manifest-html-webpack-plugin](https://github.com/jouni-kantola/inline-chunk-manifest-html-webpack-plugin) for inlining webpack's chunk manifest. Default extracts manifest and inlines in `<head>`.
Basic Usage
-----------
The plugin will generate an HTML5 file for you that includes all your webpack
bundles in the body using `script` tags. Just add the plugin to your webpack
config as follows:
```javascript
var HtmlWebpackPlugin = require('html-webpack-plugin');
var webpackConfig = {
entry: 'index.js',
output: {
path: __dirname + '/dist',
filename: 'index_bundle.js'
},
plugins: [new HtmlWebpackPlugin()]
};
```
This will generate a file `dist/index.html` containing the following:
```html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Webpack App</title>
</head>
<body>
<script src="index_bundle.js"></script>
</body>
</html>
```
If you have multiple webpack entry points, they will all be included with `script`
tags in the generated HTML.
If you have any CSS assets in webpack's output (for example, CSS extracted
with the [ExtractTextPlugin](https://github.com/webpack/extract-text-webpack-plugin))
then these will be included with `<link>` tags in the HTML head.
Configuration
-------------
You can pass a hash of configuration options to `HtmlWebpackPlugin`.
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`: Webpack require path to the template. Please see the [docs](https://github.com/jantimon/html-webpack-plugin/blob/master/docs/template-option.md) for details.
- `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 [html-minifier](https://github.com/kangax/html-minifier#options-quick-reference)'s options as 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)
- `chunksSortMode`: Allows to control how chunks should be sorted before they are included to the html. Allowed values: 'none' | 'auto' | 'dependency' | {function} - default: 'auto'
- `excludeChunks`: Allows you to skip some chunks (e.g. don't add the unit-test chunk)
- `xhtml`: `true | false` If `true` render the `link` tags as self-closing, XHTML compliant. Default is `false`
Here's an example webpack config illustrating how to use these options:
```javascript
{
entry: 'index.js',
output: {
path: __dirname + '/dist',
filename: 'index_bundle.js'
},
plugins: [
new HtmlWebpackPlugin({
title: 'My App',
filename: 'assets/admin.html'
})
]
}
```
FAQ
----
* [Why is my HTML minified?](https://github.com/jantimon/html-webpack-plugin/blob/master/docs/template-option.md)
* [Why is my `<% ... %>` template not working?](https://github.com/jantimon/html-webpack-plugin/blob/master/docs/template-option.md)
* [How can I use handlebars/pug/ejs as template engine](https://github.com/jantimon/html-webpack-plugin/blob/master/docs/template-option.md)
Generating Multiple HTML Files
------------------------------
To generate more than one HTML file, declare the plugin more than
once in your plugins array:
```javascript
{
entry: 'index.js',
output: {
path: __dirname + '/dist',
filename: 'index_bundle.js'
},
plugins: [
new HtmlWebpackPlugin(), // Generates default index.html
new HtmlWebpackPlugin({ // Also generate a test.html
filename: 'test.html',
template: 'src/assets/test.html'
})
]
}
```
Writing Your Own Templates
--------------------------
If the default generated HTML doesn't meet your needs you can supply
your own template. The easiest way is to use the `template` option and pass a custom HTML file.
The html-webpack-plugin will automatically inject all necessary CSS, JS, manifest
and favicon files into the markup.
```javascript
plugins: [
new HtmlWebpackPlugin({
title: 'Custom template',
template: 'my-index.ejs', // Load a custom template (ejs by default see the FAQ for details)
})
]
```
`my-index.ejs`:
```html
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-type" content="text/html; charset=utf-8"/>
<title><%= htmlWebpackPlugin.options.title %></title>
</head>
<body>
</body>
</html>
```
If you already have a template loader, you can use it to parse the template.
Please note that this will also happen if you specifiy the html-loader and use `.html` file as template.
```javascript
module: {
loaders: [
{ test: /\.hbs$/, loader: "handlebars" }
]
},
plugins: [
new HtmlWebpackPlugin({
title: 'Custom template using Handlebars',
template: 'my-index.hbs'
})
]
```
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/jaketrent/html-webpack-template/blob/86f285d5c790a6c15263f5cc50fd666d51f974fd/index.html) of the [html-webpack-template project](https://github.com/jaketrent/html-webpack-template) as a starting point for writing your own.
The following variables are available in the template:
- `htmlWebpackPlugin`: data specific to this plugin
- `htmlWebpackPlugin.files`: a massaged representation of the
`assetsByChunkName` attribute of webpack's [stats](https://github.com/webpack/docs/wiki/node.js-api#stats)
object. It contains a mapping from entry point name to the bundle filename, eg:
```json
"htmlWebpackPlugin": {
"files": {
"css": [ "main.css" ],
"js": [ "assets/head_bundle.js", "assets/main_bundle.js"],
"chunks": {
"head": {
"entry": "assets/head_bundle.js",
"css": [ "main.css" ]
},
"main": {
"entry": "assets/main_bundle.js",
"css": []
},
}
}
}
```
If you've set a publicPath in your webpack config this will be reflected
correctly in this assets hash.
- `htmlWebpackPlugin.options`: the options hash that was passed to
the plugin. In addition to the options actually used by this plugin,
you can use this hash to pass arbitrary data through to your template.
- `webpack`: the webpack [stats](https://github.com/webpack/docs/wiki/node.js-api#stats)
object. Note that this is the stats object as it was at the time the HTML template
was emitted and as such may not have the full set of stats that are available
after the webpack run is complete.
- `webpackConfig`: the webpack configuration that was used for this compilation. This
can be used, for example, to get the `publicPath` (`webpackConfig.output.publicPath`).
Filtering chunks
----------------
To include only certain chunks you can limit the chunks being used:
```javascript
plugins: [
new HtmlWebpackPlugin({
chunks: ['app']
})
]
```
It is also possible to exclude certain chunks by setting the `excludeChunks` option:
```javascript
plugins: [
new HtmlWebpackPlugin({
excludeChunks: ['dev-helper']
})
]
```
Events
------
To allow other [plugins](https://github.com/webpack/docs/wiki/plugins) to alter the HTML this plugin executes the following events:
Async:
* `html-webpack-plugin-before-html-generation`
* `html-webpack-plugin-before-html-processing`
* `html-webpack-plugin-alter-asset-tags`
* `html-webpack-plugin-after-html-processing`
* `html-webpack-plugin-after-emit`
Sync:
* `html-webpack-plugin-alter-chunks`
Example implementation: [html-webpack-harddisk-plugin](https://github.com/jantimon/html-webpack-harddisk-plugin)
Usage:
```javascript
// MyPlugin.js
function MyPlugin(options) {
// Configure your plugin with options...
}
MyPlugin.prototype.apply = function(compiler) {
// ...
compiler.plugin('compilation', function(compilation) {
console.log('The compiler is starting a new compilation...');
compilation.plugin('html-webpack-plugin-before-html-processing', function(htmlPluginData, callback) {
htmlPluginData.html += 'The magic footer';
callback(null, htmlPluginData);
});
});
};
module.exports = MyPlugin;
```
Then in `webpack.config.js`
```javascript
plugins: [
new MyPlugin({options: ''})
]
```
Note that the callback must be passed the htmlPluginData in order to pass this onto any other plugins listening on the same `html-webpack-plugin-before-html-processing` event.
# Contribution
You're free to contribute to this project by submitting [issues](https://github.com/jantimon/html-webpack-plugin/issues) and/or [pull requests](https://github.com/jantimon/html-webpack-plugin/pulls). This project is test-driven, so keep in mind that every change and new feature should be covered by tests.
This project uses the [semistandard code style](https://github.com/Flet/semistandard).
Before running the tests, make sure to execute `yarn link` and `yarn link html-webpack-plugin` (or the npm variant of this).
# License
This project is licensed under [MIT](https://github.com/jantimon/html-webpack-plugin/blob/master/LICENSE).

View File

@@ -0,0 +1,9 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title><%= htmlWebpackPlugin.options.title %></title>
</head>
<body>
</body>
</html>

View File

@@ -0,0 +1,651 @@
'use strict';
var vm = require('vm');
var fs = require('fs');
var _ = require('lodash');
var Promise = require('bluebird');
var path = require('path');
var childCompiler = require('./lib/compiler.js');
var prettyError = require('./lib/errors.js');
var chunkSorter = require('./lib/chunksorter.js');
Promise.promisifyAll(fs);
function HtmlWebpackPlugin (options) {
// Default options
this.options = _.extend({
template: path.join(__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',
xhtml: false
}, options);
}
HtmlWebpackPlugin.prototype.apply = function (compiler) {
var self = this;
var isCompilationCached = false;
var compilationPromise;
this.options.template = this.getFullTemplatePath(this.options.template, compiler.context);
// convert absolute filename into relative so that webpack can
// generate it at correct location
var filename = this.options.filename;
if (path.resolve(filename) === path.normalize(filename)) {
this.options.filename = path.relative(compiler.options.output.path, filename);
}
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',
outputName: self.options.filename
};
})
.then(function (compilationResult) {
// If the compilation change didnt change the cache is valid
isCompilationCached = compilationResult.hash && self.childCompilerHash === compilationResult.hash;
self.childCompilerHash = compilationResult.hash;
self.childCompilationOutputName = compilationResult.outputName;
callback();
return compilationResult.content;
});
});
compiler.plugin('emit', function (compilation, callback) {
var applyPluginsAsyncWaterfall = self.applyPluginsAsyncWaterfall(compilation);
// Get all chunks
var allChunks = compilation.getStats().toJson().chunks;
// Filter chunks (options.chunks and options.excludeCHunks)
var chunks = self.filterChunks(allChunks, self.options.chunks, self.options.excludeChunks);
// Sort chunks
chunks = self.sortChunks(chunks, self.options.chunksSortMode);
// Let plugins alter the chunks and the chunk sorting
chunks = compilation.applyPluginsWaterfall('html-webpack-plugin-alter-chunks', chunks, { plugin: self });
// Get assets
var assets = self.htmlWebpackPluginAssets(compilation, chunks);
// If this is a hot update compilation, move on!
// This solves a problem where an `index.html` file is generated for hot-update js files
// It only happens in Webpack 2, where hot updates are emitted separately before the full bundle
if (self.isHotUpdateCompilation(assets)) {
return callback();
}
// If the template and the assets did not change we don't have to emit the html
var assetJson = JSON.stringify(self.getAssetFiles(assets));
if (isCompilationCached && self.options.cache && assetJson === self.assetJson) {
return callback();
} else {
self.assetJson = assetJson;
}
Promise.resolve()
// Favicon
.then(function () {
if (self.options.favicon) {
return self.addFileToAssets(self.options.favicon, compilation)
.then(function (faviconBasename) {
var publicPath = compilation.mainTemplate.getPublicPath({hash: compilation.hash}) || '';
if (publicPath && publicPath.substr(-1) !== '/') {
publicPath += '/';
}
assets.favicon = publicPath + faviconBasename;
});
}
})
// Wait for the compilation to finish
.then(function () {
return compilationPromise;
})
.then(function (compiledTemplate) {
// Allow to use a custom function / string instead
if (self.options.templateContent !== undefined) {
return self.options.templateContent;
}
// Once everything is compiled evaluate the html factory
// and replace it with its content
return self.evaluateCompilationResult(compilation, compiledTemplate);
})
// Allow plugins to make changes to the assets before invoking the template
// This only makes sense to use if `inject` is `false`
.then(function (compilationResult) {
return applyPluginsAsyncWaterfall('html-webpack-plugin-before-html-generation', false, {
assets: assets,
outputName: self.childCompilationOutputName,
plugin: self
})
.then(function () {
return compilationResult;
});
})
// Execute the template
.then(function (compilationResult) {
// If the loader result is a function execute it to retrieve 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, outputName: self.childCompilationOutputName};
return applyPluginsAsyncWaterfall('html-webpack-plugin-before-html-processing', true, pluginArgs);
})
.then(function (result) {
var html = result.html;
var assets = result.assets;
// Prepare script and link tags
var assetTags = self.generateAssetTags(assets);
var pluginArgs = {head: assetTags.head, body: assetTags.body, plugin: self, chunks: chunks, outputName: self.childCompilationOutputName};
// Allow plugins to change the assetTag definitions
return applyPluginsAsyncWaterfall('html-webpack-plugin-alter-asset-tags', true, pluginArgs)
.then(function (result) {
// Add the stylesheets, scripts and so on to the resulting html
return self.postProcessHtml(html, assets, { body: result.body, head: result.head })
.then(function (html) {
return _.extend(result, {html: html, assets: assets});
});
});
})
// Allow plugins to change the html after assets are injected
.then(function (result) {
var html = result.html;
var assets = result.assets;
var pluginArgs = {html: html, assets: assets, plugin: self, outputName: self.childCompilationOutputName};
return applyPluginsAsyncWaterfall('html-webpack-plugin-after-html-processing', true, pluginArgs)
.then(function (result) {
return result.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.childCompilationOutputName] = {
source: function () {
return html;
},
size: function () {
return html.length;
}
};
})
.then(function () {
// Let other plugins know that we are done:
return applyPluginsAsyncWaterfall('html-webpack-plugin-after-emit', false, {
html: compilation.assets[self.childCompilationOutputName],
outputName: self.childCompilationOutputName,
plugin: self
}).catch(function (err) {
console.error(err);
return null;
}).then(function () {
return null;
});
})
// 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;
});
});
};
/**
* Evaluates the child compilation result
* Returns a promise
*/
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 =', '');
var template = this.options.template.replace(/^.+!/, '').replace(/\?.+$/, '');
var vmContext = vm.createContext(_.extend({HTML_WEBPACK_PLUGIN: true, require: require}, global));
var vmScript = new vm.Script(source, {filename: template});
// Evaluate code and cast to string
var newSource;
try {
newSource = vmScript.runInContext(vmContext);
} catch (e) {
return Promise.reject(e);
}
if (typeof newSource === 'object' && newSource.__esModule && newSource.default) {
newSource = newSource.default;
}
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;
return Promise.resolve()
// Template processing
.then(function () {
var templateParams = {
compilation: compilation,
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);
}
return html;
});
};
/**
* Html post processing
*
* Returns a promise
*/
HtmlWebpackPlugin.prototype.postProcessHtml = function (html, assets, assetTags) {
var self = this;
if (typeof html !== 'string') {
return Promise.reject('Expected html to be a string but got ' + JSON.stringify(html));
}
return Promise.resolve()
// Inject
.then(function () {
if (self.options.inject) {
return self.injectAssetsIntoHtml(html, assets, assetTags);
} 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;
});
};
/*
* Pushes the content of the given filename to the compilation assets
*/
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 () {
return Promise.reject(new Error('HtmlWebpackPlugin: could not load file ' + filename));
})
.then(function (results) {
var basename = path.basename(filename);
compilation.fileDependencies.push(filename);
compilation.assets[basename] = {
source: function () {
return results.source;
},
size: function () {
return results.size.size;
}
};
return basename;
});
};
/**
* Helper to sort chunks
*/
HtmlWebpackPlugin.prototype.sortChunks = function (chunks, sortMode) {
// Sort mode auto by default:
if (typeof sortMode === 'undefined') {
sortMode = 'auto';
}
// Custom function
if (typeof sortMode === 'function') {
return chunks.sort(sortMode);
}
// Disabled sorting:
if (sortMode === 'none') {
return chunkSorter.none(chunks);
}
// Check if the given sort mode is a valid chunkSorter sort mode
if (typeof chunkSorter[sortMode] !== 'undefined') {
return chunkSorter[sortMode](chunks);
}
throw new Error('"' + sortMode + '" is not a valid chunk sort mode');
};
/**
* Return all chunks from the compilation result which match the exclude and include filters
*/
HtmlWebpackPlugin.prototype.filterChunks = function (chunks, includedChunks, excludedChunks) {
return 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.isHotUpdateCompilation = function (assets) {
return assets.js.length && assets.js.every(function (name) {
return /\.hot-update\.js$/.test(name);
});
};
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'
// If a hard coded public path exists use it
? compilation.mainTemplate.getPublicPath({hash: webpackStatsJson.hash})
// If no public path was set get a relative url path
: path.relative(path.resolve(compilation.options.output.path, path.dirname(self.childCompilationOutputName)), compilation.options.output.path)
.split(path.sep).join('/');
if (publicPath.length && publicPath.substr(-1, 1) !== '/') {
publicPath += '/';
}
var assets = {
// The public path
publicPath: publicPath,
// Will contain all js & css files by chunk
chunks: {},
// Will contain all js files
js: [],
// Will contain all css files
css: [],
// Will contain the html5 appcache manifest files if it exists
manifest: Object.keys(compilation.assets).filter(function (assetFile) {
return path.extname(assetFile) === '.appcache';
})[0]
};
// Append a hash for cache busting
if (this.options.hash) {
assets.manifest = self.appendHash(assets.manifest, webpackStatsJson.hash);
assets.favicon = self.appendHash(assets.favicon, webpackStatsJson.hash);
}
for (var i = 0; i < chunks.length; i++) {
var chunk = chunks[i];
var chunkName = chunk.names[0];
assets.chunks[chunkName] = {};
// Prepend the public path to all chunk files
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) {
return self.appendHash(chunkFile, webpackStatsJson.hash);
});
}
// Webpack outputs an array for each chunk when using sourcemaps
// But we need only the entry file
var entry = chunkFiles[0];
assets.chunks[chunkName].size = chunk.size;
assets.chunks[chunkName].entry = entry;
assets.chunks[chunkName].hash = chunk.hash;
assets.js.push(entry);
// Gather all css files
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(chunkFile);
});
assets.chunks[chunkName].css = css;
assets.css = assets.css.concat(css);
}
// Duplicate css assets can occur on occasion if more than one chunk
// requires the same css.
assets.css = _.uniq(assets.css);
return assets;
};
/**
* Injects the assets into the given html string
*/
HtmlWebpackPlugin.prototype.generateAssetTags = function (assets) {
// Turn script files into script tags
var scripts = assets.js.map(function (scriptPath) {
return {
tagName: 'script',
closeTag: true,
attributes: {
type: 'text/javascript',
src: scriptPath
}
};
});
// Make tags self-closing in case of xhtml
var selfClosingTag = !!this.options.xhtml;
// Turn css files into link tags
var styles = assets.css.map(function (stylePath) {
return {
tagName: 'link',
selfClosingTag: selfClosingTag,
attributes: {
href: stylePath,
rel: 'stylesheet'
}
};
});
// Injection targets
var head = [];
var body = [];
// If there is a favicon present, add it to the head
if (assets.favicon) {
head.push({
tagName: 'link',
selfClosingTag: selfClosingTag,
attributes: {
rel: 'shortcut icon',
href: assets.favicon
}
});
}
// Add styles to the head
head = head.concat(styles);
// Add scripts to body or head
if (this.options.inject === 'head') {
head = head.concat(scripts);
} else {
body = body.concat(scripts);
}
return {head: head, body: body};
};
/**
* Injects the assets into the given html string
*/
HtmlWebpackPlugin.prototype.injectAssetsIntoHtml = function (html, assets, assetTags) {
var htmlRegExp = /(<html[^>]*>)/i;
var headRegExp = /(<\/head>)/i;
var bodyRegExp = /(<\/body>)/i;
var body = assetTags.body.map(this.createHtmlTag);
var head = assetTags.head.map(this.createHtmlTag);
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) {
// Append the manifest only if no manifest was specified
if (/\smanifest\s*=/.test(match)) {
return match;
}
return start + ' manifest="' + assets.manifest + '"' + end;
});
}
return html;
};
/**
* Appends a cache busting hash
*/
HtmlWebpackPlugin.prototype.appendHash = function (url, hash) {
if (!url) {
return url;
}
return url + (url.indexOf('?') === -1 ? '?' : '&') + hash;
};
/**
* Turn a tag definition into a html string
*/
HtmlWebpackPlugin.prototype.createHtmlTag = function (tagDefinition) {
var attributes = Object.keys(tagDefinition.attributes || {})
.filter(function (attributeName) {
return tagDefinition.attributes[attributeName] !== false;
})
.map(function (attributeName) {
if (tagDefinition.attributes[attributeName] === true) {
return attributeName;
}
return attributeName + '="' + tagDefinition.attributes[attributeName] + '"';
});
// Backport of 3.x void tag definition
var voidTag = tagDefinition.voidTag !== undefined ? tagDefinition.voidTag : !tagDefinition.closeTag;
var selfClosingTag = tagDefinition.voidTag !== undefined ? tagDefinition.voidTag && this.options.xhtml : tagDefinition.selfClosingTag;
return '<' + [tagDefinition.tagName].concat(attributes).join(' ') + (selfClosingTag ? '/' : '') + '>' +
(tagDefinition.innerHTML || '') +
(voidTag ? '' : '</' + tagDefinition.tagName + '>');
};
/**
* Helper to return the absolute template path with a fallback loader
*/
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(
/([!])([^/\\][^!?]+|[^/\\!?])($|\?[^!?\n]+$)/,
function (match, prefix, filepath, postfix) {
return prefix + path.resolve(filepath) + postfix;
});
};
/**
* Helper to return a sorted unique array of all asset files out of the
* asset object
*/
HtmlWebpackPlugin.prototype.getAssetFiles = function (assets) {
var files = _.uniq(Object.keys(assets).filter(function (assetType) {
return assetType !== 'chunks' && assets[assetType];
}).reduce(function (files, assetType) {
return files.concat(assets[assetType]);
}, []));
files.sort();
return files;
};
/**
* Helper to promisify compilation.applyPluginsAsyncWaterfall that returns
* a function that helps to merge given plugin arguments with processed ones
*/
HtmlWebpackPlugin.prototype.applyPluginsAsyncWaterfall = function (compilation) {
var promisedApplyPluginsAsyncWaterfall = Promise.promisify(compilation.applyPluginsAsyncWaterfall, {context: compilation});
return function (eventName, requiresResult, pluginArgs) {
return promisedApplyPluginsAsyncWaterfall(eventName, pluginArgs)
.then(function (result) {
if (requiresResult && !result) {
compilation.warnings.push(new Error('Using ' + eventName + ' without returning a result is deprecated.'));
}
return _.extend(pluginArgs, result);
});
};
};
module.exports = HtmlWebpackPlugin;

View File

@@ -0,0 +1,93 @@
'use strict';
var toposort = require('toposort');
var _ = require('lodash');
/*
Sorts dependencies between chunks by their "parents" attribute.
This function sorts chunks based on their dependencies with each other.
The parent relation between chunks as generated by Webpack for each chunk
is used to define a directed (and hopefully acyclic) graph, which is then
topologically sorted in order to retrieve the correct order in which
chunks need to be embedded into HTML. A directed edge in this graph is
describing a "is parent of" relationship from a chunk to another (distinct)
chunk. Thus topological sorting orders chunks from bottom-layer chunks to
highest level chunks that use the lower-level chunks.
@param {Array} chunks an array of chunks as generated by the html-webpack-plugin.
It is assumed that each entry contains at least the properties "id"
(containing the chunk id) and "parents" (array containing the ids of the
parent chunks).
@return {Array} A topologically sorted version of the input chunks
*/
module.exports.dependency = function (chunks) {
if (!chunks) {
return chunks;
}
// We build a map (chunk-id -> chunk) for faster access during graph building.
var nodeMap = {};
chunks.forEach(function (chunk) {
nodeMap[chunk.id] = chunk;
});
// Next, we add an edge for each parent relationship into the graph
var edges = [];
chunks.forEach(function (chunk) {
if (chunk.parents) {
// Add an edge for each parent (parent -> child)
chunk.parents.forEach(function (parentId) {
// webpack2 chunk.parents are chunks instead of string id(s)
var parentChunk = _.isObject(parentId) ? parentId : nodeMap[parentId];
// If the parent chunk does not exist (e.g. because of an excluded chunk)
// we ignore that parent
if (parentChunk) {
edges.push([parentChunk, chunk]);
}
});
}
});
// We now perform a topological sorting on the input chunks and built edges
return toposort.array(chunks, edges);
};
/**
* Sorts the chunks based on the chunk id.
*
* @param {Array} chunks the list of chunks to sort
* @return {Array} The sorted list of chunks
*/
module.exports.id = function (chunks) {
return chunks.sort(function orderEntryLast (a, b) {
if (a.entry !== b.entry) {
return b.entry ? 1 : -1;
} else {
return b.id - a.id;
}
});
};
/**
* Performs identity mapping (no-sort).
* @param {Array} chunks the chunks to sort
* @return {Array} The sorted chunks
*/
module.exports.none = function (chunks) {
return chunks;
};
/**
* Defines the default sorter.
*/
module.exports.auto = module.exports.id;
// In webpack 2 the ids have been flipped.
// Therefore the id sort doesn't work the same way as it did for webpack 1
// Luckily the dependency sort is working as expected
if (Number(require('webpack/package.json').version.split('.')[0]) > 1) {
module.exports.auto = module.exports.dependency;
}

View File

@@ -0,0 +1,112 @@
/*
* This file uses webpack to compile a template with a child compiler.
*
* [TEMPLATE] -> [JAVASCRIPT]
*
*/
'use strict';
var Promise = require('bluebird');
var _ = require('lodash');
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
};
// Store the result of the parent compilation before we start the child compilation
var assetsBeforeCompilation = _.assign({}, 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) {
// Resolve / reject the promise
if (childCompilation && 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 {
// Replace [hash] placeholders in filename
var outputName = compilation.mainTemplate.applyPluginsWaterfall('asset-path', outputOptions.filename, {
hash: childCompilation.hash,
chunk: entries[0]
});
// Restore the parent compilation to the state like it
// was before the child compilation
compilation.assets[outputName] = assetsBeforeCompilation[outputName];
if (assetsBeforeCompilation[outputName] === undefined) {
// If it wasn't there - delete it
delete compilation.assets[outputName];
}
resolve({
// Hash of the template entry point
hash: entries[0].hash,
// Output name
outputName: outputName,
// Compiled code
content: childCompilation.assets[outputName].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) + '"';
}

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);
}
};
};

View File

@@ -0,0 +1,51 @@
/* 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) {
// Loader API changed from `loader.module` to `loader.normal` in Webpack 2.
return (loader.module || loader.normal) !== 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.resourcePath)) {
return source;
}
// The following part renders the tempalte with lodash as aminimalistic loader
//
// Get templating options
var options = loaderUtils.parseQuery(this.query);
// Webpack 2 does not allow with() statements, which lodash templates use to unwrap
// the parameters passed to the compiled template inside the scope. We therefore
// need to unwrap them ourselves here. This is essentially what lodash does internally
// To tell lodash it should not use with we set a variable
var template = _.template(source, _.defaults(options, { variable: 'data' }));
// All templateVariables which should be available
// @see HtmlWebpackPlugin.prototype.executeTemplate
var templateVariables = [
'compilation',
'webpack',
'webpackConfig',
'htmlWebpackPlugin'
];
return 'var _ = require(' + loaderUtils.stringifyRequest(this, require.resolve('lodash')) + ');' +
'module.exports = function (templateParams) {' +
// Declare the template variables in the outer scope of the
// lodash template to unwrap them
templateVariables.map(function (variableName) {
return 'var ' + variableName + ' = templateParams.' + variableName;
}).join(';') + ';' +
// Execute the lodash template
'return (' + template.source + ')();' +
'}';
};

View File

@@ -0,0 +1,22 @@
(The MIT License)
Copyright (c) 2012 - 2015 Tobias Koppers
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
'Software'), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@@ -0,0 +1,217 @@
# loader-utils
## Methods
### `getLoaderConfig`
Recommended way to retrieve the loader config:
```javascript
// inside your loader
config = loaderUtils.getLoaderConfig(this, "myLoader");
```
Tries to read the loader config from the `webpack.config.js` under the given property name (`"myLoader"` in this case) and merges the result with the loader query. For example, if your `webpack.config.js` had this property...
```javascript
cheesecakeLoader: {
type: "delicious",
slices: 4
}
```
...and your loader was called with `?slices=8`, `getLoaderConfig(this, "cheesecakeLoader")` would return
```javascript
{
type: "delicious",
slices: 8
}
```
It is recommended that you use the camelCased loader name as your default config property name.
### `parseQuery`
``` javascript
var query = loaderUtils.parseQuery(this.query);
assert(typeof query == "object");
if(query.flag)
// ...
```
``` text
null -> {}
? -> {}
?flag -> { flag: true }
?+flag -> { flag: true }
?-flag -> { flag: false }
?xyz=test -> { xyz: "test" }
?xyz[]=a -> { xyz: ["a"] }
?flag1&flag2 -> { flag1: true, flag2: true }
?+flag1,-flag2 -> { flag1: true, flag2: false }
?xyz[]=a,xyz[]=b -> { xyz: ["a", "b"] }
?a%2C%26b=c%2C%26d -> { "a,&b": "c,&d" }
?{json:5,data:{a:1}} -> { json: 5, data: { a: 1 } }
```
### `stringifyRequest`
Turns a request into a string that can be used inside `require()` or `import` while avoiding absolute paths.
Use it instead of `JSON.stringify(...)` if you're generating code inside a loader.
**Why is this necessary?** Since webpack calculates the hash before module paths are translated into module ids, we must avoid absolute paths to ensure
consistent hashes across different compilations.
This function:
- resolves absolute requests into relative requests if the request and the module are on the same hard drive
- replaces `\` with `/` if the request and the module are on the same hard drive
- won't change the path at all if the request and the module are on different hard drives
- applies `JSON.stringify` to the result
```javascript
loaderUtils.stringifyRequest(this, "./test.js");
// "\"./test.js\""
loaderUtils.stringifyRequest(this, ".\\test.js");
// "\"./test.js\""
loaderUtils.stringifyRequest(this, "test");
// "\"test\""
loaderUtils.stringifyRequest(this, "test/lib/index.js");
// "\"test/lib/index.js\""
loaderUtils.stringifyRequest(this, "otherLoader?andConfig!test?someConfig");
// "\"otherLoader?andConfig!test?someConfig\""
loaderUtils.stringifyRequest(this, require.resolve("test"));
// "\"../node_modules/some-loader/lib/test.js\""
loaderUtils.stringifyRequest(this, "C:\\module\\test.js");
// "\"../../test.js\"" (on Windows, in case the module and the request are on the same drive)
loaderUtils.stringifyRequest(this, "C:\\module\\test.js");
// "\"C:\\module\\test.js\"" (on Windows, in case the module and the request are on different drives)
loaderUtils.stringifyRequest(this, "\\\\network-drive\\test.js");
// "\"\\\\network-drive\\\\test.js\"" (on Windows, in case the module and the request are on different drives)
```
### `urlToRequest`
Converts some resource URL to a webpack module request.
```javascript
var url = "path/to/module.js";
var request = loaderUtils.urlToRequest(url); // "./path/to/module.js"
```
#### Module URLs
Any URL containing a `~` will be interpreted as a module request. Anything after the `~` will be considered the request path.
```javascript
var url = "~path/to/module.js";
var request = loaderUtils.urlToRequest(url); // "path/to/module.js"
```
#### Root-relative URLs
URLs that are root-relative (start with `/`) can be resolved relative to some arbitrary path by using the `root` parameter:
```javascript
var url = "/path/to/module.js";
var root = "./root";
var request = loaderUtils.urlToRequest(url, root); // "./root/path/to/module.js"
```
To convert a root-relative URL into a module URL, specify a `root` value that starts with `~`:
```javascript
var url = "/path/to/module.js";
var root = "~";
var request = loaderUtils.urlToRequest(url, root); // "path/to/module.js"
```
### `interpolateName`
Interpolates a filename template using multiple placeholders and/or a regular expression.
The template and regular expression are set as query params called `name` and `regExp` on the current loader's context.
```javascript
var interpolatedName = loaderUtils.interpolateName(loaderContext, name, options);
```
The following tokens are replaced in the `name` parameter:
* `[ext]` the extension of the resource
* `[name]` the basename of the resource
* `[path]` the path of the resource relative to the `context` query parameter or option.
* `[folder]` the folder of the resource is in.
* `[emoji]` a random emoji representation of `options.content`
* `[emoji:<length>]` same as above, but with a customizable number of emojis
* `[hash]` the hash of `options.content` (Buffer) (by default it's the hex digest of the md5 hash)
* `[<hashType>:hash:<digestType>:<length>]` optionally one can configure
* other `hashType`s, i. e. `sha1`, `md5`, `sha256`, `sha512`
* other `digestType`s, i. e. `hex`, `base26`, `base32`, `base36`, `base49`, `base52`, `base58`, `base62`, `base64`
* and `length` the length in chars
* `[N]` the N-th match obtained from matching the current file name against `options.regExp`
Examples
``` javascript
// loaderContext.resourcePath = "/app/js/javascript.js"
loaderUtils.interpolateName(loaderContext, "js/[hash].script.[ext]", { content: ... });
// => js/9473fdd0d880a43c21b7778d34872157.script.js
// loaderContext.resourcePath = "/app/page.html"
loaderUtils.interpolateName(loaderContext, "html-[hash:6].html", { content: ... });
// => html-9473fd.html
// loaderContext.resourcePath = "/app/flash.txt"
loaderUtils.interpolateName(loaderContext, "[hash]", { content: ... });
// => c31e9820c001c9c4a86bce33ce43b679
// loaderContext.resourcePath = "/app/img/image.gif"
loaderUtils.interpolateName(loaderContext, "[emoji]", { content: ... });
// => 👍
// loaderContext.resourcePath = "/app/img/image.gif"
loaderUtils.interpolateName(loaderContext, "[emoji:4]", { content: ... });
// => 🙍🏢📤🐝
// loaderContext.resourcePath = "/app/img/image.png"
loaderUtils.interpolateName(loaderContext, "[sha512:hash:base64:7].[ext]", { content: ... });
// => 2BKDTjl.png
// use sha512 hash instead of md5 and with only 7 chars of base64
// loaderContext.resourcePath = "/app/img/myself.png"
// loaderContext.query.name =
loaderUtils.interpolateName(loaderContext, "picture.png");
// => picture.png
// loaderContext.resourcePath = "/app/dir/file.png"
loaderUtils.interpolateName(loaderContext, "[path][name].[ext]?[hash]", { content: ... });
// => /app/dir/file.png?9473fdd0d880a43c21b7778d34872157
// loaderContext.resourcePath = "/app/js/page-home.js"
loaderUtils.interpolateName(loaderContext, "script-[1].[ext]", { regExp: "page-(.*)\\.js", content: ... });
// => script-home.js
```
### `getHashDigest`
``` javascript
var digestString = loaderUtils.getHashDigest(buffer, hashType, digestType, maxLength);
```
* `buffer` the content that should be hashed
* `hashType` one of `sha1`, `md5`, `sha256`, `sha512` or any other node.js supported hash type
* `digestType` one of `hex`, `base26`, `base32`, `base36`, `base49`, `base52`, `base58`, `base62`, `base64`
* `maxLength` the maximum length in chars
## License
MIT (http://www.opensource.org/licenses/mit-license.php)

View File

@@ -0,0 +1,331 @@
var JSON5 = require("json5");
var path = require("path");
var util = require("util");
var os = require("os");
var assign = require("object-assign");
var emojiRegex = /[\uD800-\uDFFF]./;
var emojiList = require("emojis-list").filter(function(emoji) {
return emojiRegex.test(emoji)
});
var matchAbsolutePath = /^\/|^[A-Z]:[/\\]|^\\\\/i; // node 0.10 does not support path.isAbsolute()
var matchAbsoluteWin32Path = /^[A-Z]:[/\\]|^\\\\/i;
var matchRelativePath = /^\.\.?[/\\]/;
var baseEncodeTables = {
26: "abcdefghijklmnopqrstuvwxyz",
32: "123456789abcdefghjkmnpqrstuvwxyz", // no 0lio
36: "0123456789abcdefghijklmnopqrstuvwxyz",
49: "abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ", // no lIO
52: "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ",
58: "123456789abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ", // no 0lIO
62: "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ",
64: "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-_"
};
var emojiCache = {};
var parseQueryDeprecationWarning = util.deprecate(function() {},
"loaderUtils.parseQuery() received a non-string value which can be problematic, " +
"see https://github.com/webpack/loader-utils/issues/56" + os.EOL +
"parseQuery() will be replaced with getOptions() in the next major version of loader-utils."
);
function encodeStringToEmoji(content, length) {
if (emojiCache[content]) return emojiCache[content];
length = length || 1;
var emojis = [];
do {
var index = Math.floor(Math.random() * emojiList.length);
emojis.push(emojiList[index]);
emojiList.splice(index, 1);
} while (--length > 0);
var emojiEncoding = emojis.join('');
emojiCache[content] = emojiEncoding;
return emojiEncoding;
}
function encodeBufferToBase(buffer, base) {
var encodeTable = baseEncodeTables[base];
if (!encodeTable) throw new Error("Unknown encoding base" + base);
var readLength = buffer.length;
var Big = require('big.js');
Big.RM = Big.DP = 0;
var b = new Big(0);
for (var i = readLength - 1; i >= 0; i--) {
b = b.times(256).plus(buffer[i]);
}
var output = "";
while (b.gt(0)) {
output = encodeTable[b.mod(base)] + output;
b = b.div(base);
}
Big.DP = 20;
Big.RM = 1;
return output;
}
exports.parseQuery = function parseQuery(query) {
var specialValues = {
'null': null,
'true': true,
'false': false
};
if(!query) return {};
if(typeof query !== "string") {
parseQueryDeprecationWarning();
return query;
}
if(query.substr(0, 1) !== "?")
throw new Error("a valid query string passed to parseQuery should begin with '?'");
query = query.substr(1);
var queryLength = query.length;
if(query.substr(0, 1) === "{" && query.substr(-1) === "}") {
return JSON5.parse(query);
}
var queryArgs = query.split(/[,\&]/g);
var result = {};
queryArgs.forEach(function(arg) {
var idx = arg.indexOf("=");
if(idx >= 0) {
var name = arg.substr(0, idx);
var value = decodeURIComponent(arg.substr(idx+1));
if (specialValues.hasOwnProperty(value)) {
value = specialValues[value];
}
if(name.substr(-2) === "[]") {
name = decodeURIComponent(name.substr(0, name.length-2));
if(!Array.isArray(result[name]))
result[name] = [];
result[name].push(value);
} else {
name = decodeURIComponent(name);
result[name] = value;
}
} else {
if(arg.substr(0, 1) === "-") {
result[decodeURIComponent(arg.substr(1))] = false;
} else if(arg.substr(0, 1) === "+") {
result[decodeURIComponent(arg.substr(1))] = true;
} else {
result[decodeURIComponent(arg)] = true;
}
}
});
return result;
};
exports.getLoaderConfig = function(loaderContext, defaultConfigKey) {
var query = exports.parseQuery(loaderContext.query);
var configKey = query.config || defaultConfigKey;
if (configKey) {
var config = loaderContext.options[configKey] || {};
delete query.config;
return assign({}, config, query);
}
return query;
};
exports.stringifyRequest = function(loaderContext, request) {
var splitted = request.split("!");
var context = loaderContext.context || (loaderContext.options && loaderContext.options.context);
return JSON.stringify(splitted.map(function(part) {
// First, separate singlePath from query, because the query might contain paths again
var splittedPart = part.match(/^(.*?)(\?.*)/);
var singlePath = splittedPart ? splittedPart[1] : part;
var query = splittedPart ? splittedPart[2] : "";
if(matchAbsolutePath.test(singlePath) && context) {
singlePath = path.relative(context, singlePath);
if(matchAbsolutePath.test(singlePath)) {
// If singlePath still matches an absolute path, singlePath was on a different drive than context.
// In this case, we leave the path platform-specific without replacing any separators.
// @see https://github.com/webpack/loader-utils/pull/14
return singlePath + query;
}
if(matchRelativePath.test(singlePath) === false) {
// Ensure that the relative path starts at least with ./ otherwise it would be a request into the modules directory (like node_modules).
singlePath = "./" + singlePath;
}
}
return singlePath.replace(/\\/g, "/") + query;
}).join("!"));
};
function dotRequest(obj) {
return obj.request;
}
exports.getRemainingRequest = function(loaderContext) {
if(loaderContext.remainingRequest)
return loaderContext.remainingRequest;
var request = loaderContext.loaders.slice(loaderContext.loaderIndex+1).map(dotRequest).concat([loaderContext.resource]);
return request.join("!");
};
exports.getCurrentRequest = function(loaderContext) {
if(loaderContext.currentRequest)
return loaderContext.currentRequest;
var request = loaderContext.loaders.slice(loaderContext.loaderIndex).map(dotRequest).concat([loaderContext.resource]);
return request.join("!");
};
exports.isUrlRequest = function(url, root) {
// An URL is not an request if
// 1. it's a Data Url
// 2. it's an absolute url or and protocol-relative
// 3. it's some kind of url for a template
if(/^data:|^chrome-extension:|^(https?:)?\/\/|^[\{\}\[\]#*;,'§\$%&\(=?`´\^°<>]/.test(url)) return false;
// 4. It's also not an request if root isn't set and it's a root-relative url
if((root === undefined || root === false) && /^\//.test(url)) return false;
return true;
};
exports.urlToRequest = function(url, root) {
var moduleRequestRegex = /^[^?]*~/;
var request;
if(matchAbsoluteWin32Path.test(url)) {
// absolute windows path, keep it
request = url;
} else if(root !== undefined && root !== false && /^\//.test(url)) {
// if root is set and the url is root-relative
switch(typeof root) {
// 1. root is a string: root is prefixed to the url
case "string":
// special case: `~` roots convert to module request
if (moduleRequestRegex.test(root)) {
request = root.replace(/([^~\/])$/, "$1/") + url.slice(1);
} else {
request = root + url;
}
break;
// 2. root is `true`: absolute paths are allowed
// *nix only, windows-style absolute paths are always allowed as they doesn't start with a `/`
case "boolean":
request = url;
break;
default:
throw new Error("Unexpected parameters to loader-utils 'urlToRequest': url = " + url + ", root = " + root + ".");
}
} else if(/^\.\.?\//.test(url)) {
// A relative url stays
request = url;
} else {
// every other url is threaded like a relative url
request = "./" + url;
}
// A `~` makes the url an module
if (moduleRequestRegex.test(request)) {
request = request.replace(moduleRequestRegex, "");
}
return request;
};
exports.parseString = function parseString(str) {
try {
if(str[0] === '"') return JSON.parse(str);
if(str[0] === "'" && str.substr(str.length - 1) === "'") {
return parseString(str.replace(/\\.|"/g, function(x) {
if(x === '"') return '\\"';
return x;
}).replace(/^'|'$/g, '"'));
}
return JSON.parse('"' + str + '"');
} catch(e) {
return str;
}
};
exports.getHashDigest = function getHashDigest(buffer, hashType, digestType, maxLength) {
hashType = hashType || "md5";
maxLength = maxLength || 9999;
var hash = require("crypto").createHash(hashType);
hash.update(buffer);
if (digestType === "base26" || digestType === "base32" || digestType === "base36" ||
digestType === "base49" || digestType === "base52" || digestType === "base58" ||
digestType === "base62" || digestType === "base64") {
return encodeBufferToBase(hash.digest(), digestType.substr(4)).substr(0, maxLength);
} else {
return hash.digest(digestType || "hex").substr(0, maxLength);
}
};
exports.interpolateName = function interpolateName(loaderContext, name, options) {
var filename;
if (typeof name === "function") {
filename = name(loaderContext.resourcePath);
} else {
filename = name || "[hash].[ext]";
}
var context = options.context;
var content = options.content;
var regExp = options.regExp;
var ext = "bin";
var basename = "file";
var directory = "";
var folder = "";
if(loaderContext.resourcePath) {
var resourcePath = loaderContext.resourcePath;
var idx = resourcePath.lastIndexOf(".");
var i = resourcePath.lastIndexOf("\\");
var j = resourcePath.lastIndexOf("/");
var p = i < 0 ? j : j < 0 ? i : i < j ? i : j;
if(idx >= 0) {
ext = resourcePath.substr(idx+1);
resourcePath = resourcePath.substr(0, idx);
}
if(p >= 0) {
basename = resourcePath.substr(p+1);
resourcePath = resourcePath.substr(0, p+1);
}
if (typeof context !== 'undefined') {
directory = path.relative(context, resourcePath + "_").replace(/\\/g, "/").replace(/\.\.(\/)?/g, "_$1");
directory = directory.substr(0, directory.length-1);
}
else {
directory = resourcePath.replace(/\\/g, "/").replace(/\.\.(\/)?/g, "_$1");
}
if (directory.length === 1) {
directory = "";
} else if (directory.length > 1) {
folder = path.basename(directory);
}
}
var url = filename;
if(content) {
// Match hash template
url = url.replace(/\[(?:(\w+):)?hash(?::([a-z]+\d*))?(?::(\d+))?\]/ig, function() {
return exports.getHashDigest(content, arguments[1], arguments[2], parseInt(arguments[3], 10));
}).replace(/\[emoji(?::(\d+))?\]/ig, function() {
return encodeStringToEmoji(content, arguments[1]);
});
}
url = url.replace(/\[ext\]/ig, function() {
return ext;
}).replace(/\[name\]/ig, function() {
return basename;
}).replace(/\[path\]/ig, function() {
return directory;
}).replace(/\[folder\]/ig, function() {
return folder;
});
if(regExp && loaderContext.resourcePath) {
var re = new RegExp(regExp);
var match = loaderContext.resourcePath.match(re);
if(match) {
for (var i = 0; i < match.length; i++) {
var re = new RegExp("\\[" + i + "\\]", "ig");
url = url.replace(re, match[i]);
}
}
}
if(typeof loaderContext.options === "object" && typeof loaderContext.options.customInterpolateName === "function") {
url = loaderContext.options.customInterpolateName.call(loaderContext, url, name, options);
}
return url;
};

View File

@@ -0,0 +1,65 @@
{
"_args": [
[
"loader-utils@0.2.17",
"C:\\Users\\deranjer\\go\\src\\github.com\\deranjer\\goTorrent\\torrent-project"
]
],
"_from": "loader-utils@0.2.17",
"_id": "loader-utils@0.2.17",
"_inBundle": false,
"_integrity": "sha1-+G5jdNQyBabmxg6RlvF8Apm/s0g=",
"_location": "/react-scripts/html-webpack-plugin/loader-utils",
"_phantomChildren": {},
"_requested": {
"type": "version",
"registry": true,
"raw": "loader-utils@0.2.17",
"name": "loader-utils",
"escapedName": "loader-utils",
"rawSpec": "0.2.17",
"saveSpec": null,
"fetchSpec": "0.2.17"
},
"_requiredBy": [
"/react-scripts/html-webpack-plugin"
],
"_resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-0.2.17.tgz",
"_spec": "0.2.17",
"_where": "C:\\Users\\deranjer\\go\\src\\github.com\\deranjer\\goTorrent\\torrent-project",
"author": {
"name": "Tobias Koppers @sokra"
},
"bugs": {
"url": "https://github.com/webpack/loader-utils/issues"
},
"dependencies": {
"big.js": "^3.1.3",
"emojis-list": "^2.0.0",
"json5": "^0.5.0",
"object-assign": "^4.0.1"
},
"description": "utils for webpack loaders",
"devDependencies": {
"coveralls": "^2.11.2",
"istanbul": "^0.3.14",
"mocha": "^1.21.4"
},
"files": [
"index.js"
],
"homepage": "https://github.com/webpack/loader-utils#readme",
"license": "MIT",
"name": "loader-utils",
"repository": {
"type": "git",
"url": "git+https://github.com/webpack/loader-utils.git"
},
"scripts": {
"cover": "istanbul cover -x *.runtime.js node_modules/mocha/bin/_mocha",
"publish-patch": "mocha && npm version patch && git push && git push --tags && npm publish",
"test": "mocha",
"travis": "npm run cover -- --report lcovonly"
},
"version": "0.2.17"
}

View File

@@ -0,0 +1,105 @@
{
"_args": [
[
"html-webpack-plugin@2.29.0",
"C:\\Users\\deranjer\\go\\src\\github.com\\deranjer\\goTorrent\\torrent-project"
]
],
"_from": "html-webpack-plugin@2.29.0",
"_id": "html-webpack-plugin@2.29.0",
"_inBundle": false,
"_integrity": "sha1-6Yf0IYU9O2k4yMTIFxhC5f0XryM=",
"_location": "/react-scripts/html-webpack-plugin",
"_phantomChildren": {
"big.js": "3.2.0",
"emojis-list": "2.1.0",
"json5": "0.5.1",
"object-assign": "4.1.1"
},
"_requested": {
"type": "version",
"registry": true,
"raw": "html-webpack-plugin@2.29.0",
"name": "html-webpack-plugin",
"escapedName": "html-webpack-plugin",
"rawSpec": "2.29.0",
"saveSpec": null,
"fetchSpec": "2.29.0"
},
"_requiredBy": [
"/react-scripts"
],
"_resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-2.29.0.tgz",
"_spec": "2.29.0",
"_where": "C:\\Users\\deranjer\\go\\src\\github.com\\deranjer\\goTorrent\\torrent-project",
"author": {
"name": "Charles Blaxland",
"email": "charles.blaxland@gmail.com",
"url": "https://github.com/ampedandwired"
},
"bugs": {
"url": "https://github.com/jantimon/html-webpack-plugin/issues"
},
"dependencies": {
"bluebird": "^3.4.7",
"html-minifier": "^3.2.3",
"loader-utils": "^0.2.16",
"lodash": "^4.17.3",
"pretty-error": "^2.0.2",
"toposort": "^1.0.0"
},
"description": "Simplifies creation of HTML files to serve your webpack bundles",
"devDependencies": {
"appcache-webpack-plugin": "^1.3.0",
"css-loader": "^0.26.1",
"dir-compare": "1.3.0",
"es6-promise": "^4.0.5",
"extract-text-webpack-plugin": "^1.0.1",
"file-loader": "^0.9.0",
"html-loader": "^0.4.4",
"jade": "^1.11.0",
"jade-loader": "^0.8.0",
"jasmine": "^2.5.2",
"rimraf": "^2.5.4",
"semistandard": "8.0.0",
"style-loader": "^0.13.1",
"underscore-template-loader": "^0.7.3",
"url-loader": "^0.5.7",
"webpack": "^1.14.0",
"webpack-recompilation-simulator": "^1.3.0"
},
"files": [
"index.js",
"default_index.ejs",
"lib/"
],
"homepage": "https://github.com/jantimon/html-webpack-plugin",
"keywords": [
"webpack",
"plugin",
"html",
"html-webpack-plugin"
],
"license": "MIT",
"main": "index.js",
"name": "html-webpack-plugin",
"peerDependencies": {
"webpack": "1 || ^2 || ^2.1.0-beta || ^2.2.0-rc || ^3"
},
"repository": {
"type": "git",
"url": "git+https://github.com/jantimon/html-webpack-plugin.git"
},
"scripts": {
"build-examples": "node examples/build-examples.js",
"prepublish": "npm run test",
"pretest": "semistandard",
"test": "jasmine"
},
"semistandard": {
"ignore": [
"examples/*/dist/**/*.*"
]
},
"version": "2.29.0"
}