Created
January 30, 2017 20:28
-
-
Save MagicDuck/f57ea85bc859d873158c5a8bc12b7466 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
"use strict"; | |
const Mustache = require("mustache"); | |
const path = require("path"); | |
/** | |
* This plugin is used to generate an html file from a mustache template. | |
* @param {object} options | |
* - enabled {boolean} whether plugin is enabled | |
* - outputFile {string} the relative path to the html file result | |
* - templateFile {string} the absolute path to the html file result | |
* - templateVars: {object} mustache view | |
* - assetMatchers {object map str -> Regex} an object where each key K points to a regular expression that | |
* is used to match asset files output by webpack during the build. We then set: | |
* templateVars.assets[K] = <matched urls, made relative to the html file> {undefined | string | string[]} | |
*/ | |
var HtmlPlugin = module.exports = function(options) { | |
this.options = Object.assign({ | |
enabled: true, | |
templateVars: {}, | |
assetMatchers: {} | |
}, options); | |
if (typeof this.options.outputFile !== "string") { | |
throw new Error("HtmlPlugin: options.outputFile of type <string> is required!"); | |
} | |
this.options.outputFile = this.options.outputFile.replace(/\\/g, '/'); | |
if (typeof this.options.templateFile !== "string") { | |
throw new Error("HtmlPlugin: options.templateFile of type <string> is required!"); | |
} | |
}; | |
HtmlPlugin.prototype.apply = function(compiler) { | |
var options = this.options; | |
if (options.enabled === false) { | |
return; | |
} | |
compiler.plugin('emit', function(compilation, callback) { | |
compilation.fileDependencies.push(options.templateFile); // note: those get deduped internally | |
Promise.resolve().then(function() { | |
return new Promise(function(resolve, reject) { | |
compiler.inputFileSystem.stat(options.templateFile, function(err, statInfo) { | |
// Check template file exists | |
if(err) { | |
return reject(err); | |
} | |
if (this._old_template_mtime && (statInfo.mtime === this._old_template_mtime)) { | |
// use cached version | |
return resolve(this._outputFileAsset); | |
} | |
compiler.inputFileSystem.readFile(options.templateFile, function(err, templateContent) { | |
if(err) { | |
return reject(err); | |
} | |
// TODO: generate relative paths | |
var assetUrls = Object.keys(compilation.assets); | |
var assets = Object.keys(options.assetMatchers).reduce((assetsMap, assetKey) => { | |
var assetMatcher = options.assetMatchers[assetKey]; | |
var matchedUrls = assetUrls.filter(url => assetMatcher.test(url)) | |
.map(url => path.relative(path.dirname(options.outputFile), url).replace(/\\/g, '/')); | |
assetsMap[assetKey] = matchedUrls.length > 1 ? matchedUrls : matchedUrls[0]; | |
return assetsMap; | |
}, {}); | |
var outputContent; | |
try { | |
outputContent = Mustache.render(templateContent.toString(), Object.assign({}, options.templateVars, { | |
assets: assets | |
})); | |
} catch(e) { | |
e.message = "Cannot render mustache template: " + e.message; | |
return reject(e); | |
} | |
this._old_template_mtime = statInfo.mtime; | |
return resolve({ | |
_content: outputContent, | |
source: function() { | |
return this._content; | |
}, | |
size: function() { | |
return this._content.length; | |
} | |
}); | |
}.bind(this)); | |
}.bind(this)); | |
}.bind(this)); | |
}.bind(this)).then(function(assetSource) { | |
this._outputFileAsset = assetSource; | |
compilation.assets[options.outputFile] = assetSource; | |
callback(); | |
}.bind(this)).catch(function(err) { | |
callback(err); | |
}); | |
}.bind(this)); | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
use like so:
app-template.mustache