discourse/app/assets/javascripts/discourse-hbr/raw-handlebars-compiler.js

169 lines
4.5 KiB
JavaScript

"use strict";
const Filter = require("broccoli-filter");
const Handlebars = require("handlebars");
const RawHandlebars = Handlebars.create();
function buildPath(blk, args) {
let result = {
type: "PathExpression",
data: false,
depth: blk.path.depth,
loc: blk.path.loc,
};
// Server side precompile doesn't have jquery.extend
Object.keys(args).forEach(function (a) {
result[a] = args[a];
});
return result;
}
function replaceGet(ast) {
let visitor = new Handlebars.Visitor();
visitor.mutating = true;
visitor.MustacheStatement = function (mustache) {
if (!(mustache.params.length || mustache.hash)) {
mustache.params[0] = mustache.path;
mustache.path = buildPath(mustache, {
parts: ["get"],
original: "get",
strict: true,
falsy: true,
});
}
return Handlebars.Visitor.prototype.MustacheStatement.call(this, mustache);
};
// rewrite `each x as |y|` as each y in x`
// This allows us to use the same syntax in all templates
visitor.BlockStatement = function (block) {
if (block.path.original === "each" && block.params.length === 1) {
let paramName = block.program.blockParams[0];
block.params = [
buildPath(block, { original: paramName }),
{ type: "CommentStatement", value: "in" },
block.params[0],
];
delete block.program.blockParams;
}
return Handlebars.Visitor.prototype.BlockStatement.call(this, block);
};
visitor.accept(ast);
}
RawHandlebars.Compiler = function () {};
RawHandlebars.Compiler.prototype = Object.create(Handlebars.Compiler.prototype);
RawHandlebars.Compiler.prototype.compiler = RawHandlebars.Compiler;
RawHandlebars.JavaScriptCompiler = function () {};
RawHandlebars.JavaScriptCompiler.prototype = Object.create(
Handlebars.JavaScriptCompiler.prototype
);
RawHandlebars.JavaScriptCompiler.prototype.compiler =
RawHandlebars.JavaScriptCompiler;
RawHandlebars.JavaScriptCompiler.prototype.namespace = "RawHandlebars";
RawHandlebars.precompile = function (value, asObject) {
let ast = Handlebars.parse(value);
replaceGet(ast);
let options = {
knownHelpers: {
get: true,
},
data: true,
stringParams: true,
};
asObject = asObject === undefined ? true : asObject;
let environment = new RawHandlebars.Compiler().compile(ast, options);
return new RawHandlebars.JavaScriptCompiler().compile(
environment,
options,
undefined,
asObject
);
};
RawHandlebars.compile = function (string) {
let ast = Handlebars.parse(string);
replaceGet(ast);
// this forces us to rewrite helpers
let options = { data: true, stringParams: true };
let environment = new RawHandlebars.Compiler().compile(ast, options);
let templateSpec = new RawHandlebars.JavaScriptCompiler().compile(
environment,
options,
undefined,
true
);
let t = RawHandlebars.template(templateSpec);
t.isMethod = false;
return t;
};
function TemplateCompiler(inputTree, options) {
if (!(this instanceof TemplateCompiler)) {
return new TemplateCompiler(inputTree, options);
}
Filter.call(this, inputTree, options); // this._super()
this.options = options || {};
this.inputTree = inputTree;
}
TemplateCompiler.prototype = Object.create(Filter.prototype);
TemplateCompiler.prototype.constructor = TemplateCompiler;
TemplateCompiler.prototype.extensions = ["hbr"];
TemplateCompiler.prototype.targetExtension = "js";
TemplateCompiler.prototype.registerPlugins = function registerPlugins() {};
TemplateCompiler.prototype.initializeFeatures =
function initializeFeatures() {};
TemplateCompiler.prototype.processString = function (string, relativePath) {
let filename;
const pluginName = relativePath.match(/^discourse\/plugins\/([^\/]+)\//)?.[1];
if (pluginName) {
filename = relativePath
.replace(`discourse/plugins/${pluginName}/`, "")
.replace(/^(discourse\/)?templates\//, "javascripts/");
} else {
filename = relativePath.replace(/^templates\//, "");
}
filename = filename.replace(/\.hbr$/, "");
return (
'import { template as compiler } from "discourse-common/lib/raw-handlebars";\n' +
'import { addRawTemplate } from "discourse-common/lib/raw-templates";\n\n' +
"let template = compiler(" +
this.precompile(string, false) +
");\n\n" +
'addRawTemplate("' +
filename +
'", template, { core: true });\n' +
"export default template;"
);
};
TemplateCompiler.prototype.precompile = function (value, asObject) {
return RawHandlebars.precompile(value, asObject);
};
module.exports = TemplateCompiler;