"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
This comment has been minimized.
use like so:
app-template.mustache