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

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

175 lines
4.7 KiB
JavaScript
Raw Normal View History

"use strict";
const Filter = require("broccoli-filter");
const Handlebars = require("handlebars");
const RawHandlebars = Handlebars.create();
function buildPath(blk, args) {
2021-01-27 19:39:20 +08:00
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) {
2021-01-27 19:39:20 +08:00
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) {
2021-01-27 19:39:20 +08:00
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) {
2021-01-27 19:39:20 +08:00
let ast = Handlebars.parse(value);
replaceGet(ast);
2021-01-27 19:39:20 +08:00
let options = {
knownHelpers: {
get: true,
},
data: true,
stringParams: true,
};
asObject = asObject === undefined ? true : asObject;
2021-01-27 19:39:20 +08:00
let environment = new RawHandlebars.Compiler().compile(ast, options);
return new RawHandlebars.JavaScriptCompiler().compile(
environment,
options,
undefined,
asObject
);
};
RawHandlebars.compile = function (string) {
2021-01-27 19:39:20 +08:00
let ast = Handlebars.parse(string);
replaceGet(ast);
// this forces us to rewrite helpers
2021-01-27 19:39:20 +08:00
let options = { data: true, stringParams: true };
let environment = new RawHandlebars.Compiler().compile(ast, options);
let templateSpec = new RawHandlebars.JavaScriptCompiler().compile(
environment,
options,
undefined,
true
);
2021-01-27 19:39:20 +08:00
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\/)?raw-templates\//, "javascripts/");
} else {
filename = relativePath.replace(/^raw-templates\//, "");
}
filename = filename.replace(/\.hbr$/, "");
const hasModernReplacement = string.includes(
"{{!-- has-modern-replacement --}}"
);
return `
import { template as compiler } from "discourse-common/lib/raw-handlebars";
import { addRawTemplate } from "discourse-common/lib/raw-templates";
let template = compiler(${this.precompile(string, false)});
addRawTemplate("${filename}", template, {
core: ${!pluginName},
pluginName: ${JSON.stringify(pluginName)},
hasModernReplacement: ${hasModernReplacement},
});
export default template;
`;
};
TemplateCompiler.prototype.precompile = function (value, asObject) {
return RawHandlebars.precompile(value, asObject);
};
module.exports = TemplateCompiler;