@webdiscus/pug-loader
Version:
Pug loader renders Pug files into HTML or compiles them into a template function.
177 lines (153 loc) • 5.19 kB
JavaScript
const VMScript = require('../VMScript');
const Resolver = require('../Resolver');
const { ScriptCollection } = require('../Modules');
const { isRequireableScript, hmrFile } = require('../Utils');
/**
* Render into HTML and export a JS module.
*/
class RenderMethod {
constructor({ templateFile, templateName, esModule }) {
const loaderRequire = this.loaderRequire.bind(this);
const loaderRequireScript = this.loaderRequireScript.bind(this);
const loaderRequireStyle = this.loaderRequireStyle.bind(this);
this.templateFile = templateFile;
this.exportCode = esModule ? 'export default ' : 'module.exports=';
this.vmscript = new VMScript({
templateName,
loaderRequire,
loaderRequireScript,
loaderRequireStyle,
});
}
/**
* Encode reserved HTML chars.
*
* @param {string} str
* @return {string}
*/
encodeReservedChars(str) {
if (str.indexOf('?') < 0) return str;
const match = /[&'"]/g;
const replacements = { '&': '\\u0026', "'": '\\u0060', '"': '\\u0060' };
const replacer = (value) => replacements[value];
return str.replace(match, replacer);
}
/**
* Decode reserved HTML chars.
*
* @param {string} str
* @return {string}
*/
decodeReservedChars(str) {
const match = /('|\\u0026|\\u0027|\\u0060|\n|\r|\\)/g;
const replacements = {
'\\u0026': '&',
'\\u0027': "'",
'\\u0060': "\\'",
"'": "\\'",
'\n': '\\n',
'\r': '\\r',
'\\': '\\\\',
};
const replacer = (value) => replacements[value];
return str.replace(match, replacer);
}
/**
* Resolve resource file after compilation of source code.
* At this stage the filename is interpolated in VM.
*
* @param {string} file The required file.
* @param {string} issuer The issuer of required file.
* @return {string}
*/
loaderRequire(file, issuer) {
let resolvedFile = Resolver.resolve(file, issuer);
if (isRequireableScript(resolvedFile)) return require(resolvedFile);
resolvedFile = this.encodeReservedChars(resolvedFile);
return `\\u0027 + require(\\u0027${resolvedFile}\\u0027) + \\u0027`;
}
/**
* Resolve script file after compilation of source code.
*
* @param {string} file The required file.
* @param {string} issuer The issuer of required file.
* @return {string}
*/
loaderRequireScript(file, issuer) {
const resolvedFile = Resolver.resolve(file, issuer, 'script');
ScriptCollection.add(resolvedFile, this.templateFile);
return `\\u0027 + require(\\u0027${resolvedFile}\\u0027) + \\u0027`;
}
/**
* Resolve style file after compilation of source code.
*
* @param {string} file The required file.
* @param {string} issuer The issuer of required file.
* @return {string}
*/
loaderRequireStyle(file, issuer) {
const resolvedFile = Resolver.resolve(file, issuer, 'style');
return `\\u0027 + require(\\u0027${resolvedFile}\\u0027) + \\u0027`;
}
/**
* Returns the expression with the name of the handler function in the template source code,
* which will be called when this template is compiled in the VM.
*
* @param {string} file The required file.
* @param {string} issuer The issuer of required file.
* @return {string}
*/
require(file, issuer) {
return this.vmscript.require(file, issuer);
}
/**
* Returns the expression with the name of the handler function in the template source code,
* which will be called when this template is compiled in the VM.
* The filename from the script tag will be stored for usage in the plugin.
*
* @param {string} file The required file.
* @param {string} issuer The issuer of required file.
* @return {string}
*/
requireScript(file, issuer) {
return this.vmscript.require(file, issuer, 'script');
}
/**
* Returns the expression with the name of the handler function in the template source code,
* which will be called when this template is compiled in the VM.
*
* @param {string} file The required file.
* @param {string} issuer The issuer of required file.
* @return {string}
*/
requireStyle(file, issuer) {
return this.vmscript.require(file, issuer, 'style');
}
/**
* Export template code with rendered HTML.
*
* @param {string} source The template source code.
* @param {{}} locals The variables passed in template function.
* @return {string}
*/
export(source, locals) {
const result = this.vmscript.run(this.templateFile, source, locals);
return this.exportCode + "'" + this.decodeReservedChars(result) + "';";
}
/**
* Export code with error message.
*
* @param {Error} error
* @param {Function} getErrorMessage
* @param {string} issuer
* @return {string}
*/
exportError(error, getErrorMessage, issuer) {
const requireHmrScript = `' + require('${hmrFile}') + '`;
const errStr = error.toString().replace(/'/g, "\\'");
const message = getErrorMessage.call(null, errStr, requireHmrScript);
ScriptCollection.add(hmrFile, this.templateFile);
return this.exportCode + "'" + message + "';";
}
}
module.exports = RenderMethod;