Permalink
Browse files

Handle a new `.tpl.jade` file extension

Putting each template in its own separate file and naming the file
after the template it contains is becoming a followed pattern among
Meteor developers. See for instance this article from Josh Owens:
http://joshowens.me/how-to-organize-your-meteor-js-app/

But as it stands today, this pattern doesn't respect the “don't
repeat yourself” (DRY) philosophy. Indeed you have to wrap your
template in a `<template name="myTemplate>` tag and saving it in a
`myTemplate.html` file, effectively writing the name of the template
twice. If those two names doesn't match Meteor will consider the name
of the `<template>` tag and will ignore the file name. So if you
follow this pattern you have to take care of keeping the file name and
the template tag name in sync (manually).

This commit solve the problem presented above for jade using a new
file extension, namely `.tpl.jade`. With this extension you can only
define one template per file and you don't need to wrap your template
in a tag. The template will be named after the file name. We handle
special `head.tpl.jade` and `body.tpl.jade` templates as expected.

Of course the `.jade` extension is still here and you can use it along
the new one, for instance for rapid prototyping (with several
templates in the same file) or for different parts of the apps (
sometimes it makes sense to define a group of templates in the same
file).
  • Loading branch information...
mquandalle committed Dec 10, 2014
1 parent 1692271 commit e7d5df8e72780962feb92ac935ea49814bb2b6be
View
@@ -21,6 +21,12 @@ Package.onTest(function (api) {
api.versionsFrom("METEOR@0.9.0");
api.use("tinytest");
api.use(["mquandalle:jade", "ui", "spacebars", "templating"]);
- api.addFiles(["tests/match.jade", "tests/match.html", "tests/runtime.jade"]);
+ api.addFiles([
+ "tests/match.jade",
+ "tests/match.html",
+ "tests/runtime.jade",
+ "tests/body.tpl.jade",
+ "tests/img_tag_here.tpl.jade"
+ ]);
api.addFiles(["tests/match.js", "tests/runtime.js"], "client");
});
@@ -1,17 +1,44 @@
-var sourceHandler = function (compileStep) {
- // Parse and compile the content
+var path = Npm.require('path');
+var codeGen = SpacebarsCompiler.codeGen;
+
+var bodyGen = function (tpl) {
+ var res = "";
+ res += "\nTemplate.body.addContent(";
+ res += codeGen(tpl, { isBody: true, sourceName: "<body>"});
+ res += ");\n";
+ res += "Meteor.startup(Template.body.renderToDocument);\n";
+ return res;
+};
+
+var templateGen = function (tree, tplName) {
+ var nameLiteral = JSON.stringify(tplName);
+ var templateDotNameLiteral = JSON.stringify("Template." + tplName);
+ var res = "";
+ res += "\nTemplate.__checkName(" + nameLiteral + ");";
+ res += "\nTemplate[" + nameLiteral + "] = new Template(";
+ res += templateDotNameLiteral + ", ";
+ res += codeGen(tree, { isTemplate: true });
+ res += ");\n";
+ return res;
+};
+
+var getCompilerResult = function (compileStep, fileMode) {
+ var content = compileStep.read().toString('utf8');
try {
- var content = compileStep.read().toString('utf8');
- var results = Jade.compile(content, {
+ return Jade.compile(content, {
filename: compileStep.inputPath,
- fileMode: true
+ fileMode: fileMode
});
} catch (err) {
return compileStep.error({
message: "Jade syntax error: " + err.message,
sourcePath: compileStep.inputPath
});
}
+}
+
+var fileModeHandler = function (compileStep) {
+ var results = getCompilerResult(compileStep, true);
// Head
if (results.head !== null) {
@@ -21,29 +48,13 @@ var sourceHandler = function (compileStep) {
});
}
- // Generate the final js file
- // XXX generate a source map
var jsContent = "";
- var codeGen = SpacebarsCompiler.codeGen;
-
- // Body
if (results.body !== null) {
- jsContent += "\nTemplate.body.addContent(";
- jsContent += codeGen(results.body, { isBody: true, sourceName: "<body>"});
- jsContent += ");\n";
- jsContent += "Meteor.startup(Template.body.renderToDocument);\n";
+ jsContent += bodyGen(results.body);
+ }
+ if (! _.isEmpty(results.templates)) {
+ jsContent += _.map(results.templates, templateGen).join("");
}
-
- // Templates
- _.forEach(results.templates, function (tree, tplName) {
- var nameLiteral = JSON.stringify(tplName);
- var templateDotNameLiteral = JSON.stringify("Template." + tplName);
- jsContent += "\nTemplate.__checkName(" + nameLiteral + ");";
- jsContent += "\nTemplate[" + nameLiteral + "] = new Template(";
- jsContent += templateDotNameLiteral + ", ";
- jsContent += codeGen(tree, { isTemplate: true });
- jsContent += ");\n";
- });
if (jsContent !== "") {
compileStep.addJavaScript({
@@ -54,7 +65,36 @@ var sourceHandler = function (compileStep) {
}
};
-Plugin.registerSourceHandler("jade", {
+var templateModeHandler = function (compileStep) {
+ var result = getCompilerResult(compileStep, false);
+ var templateName = path.basename(compileStep.inputPath, '.tpl.jade');
+ var jsContent;
+
+ if (templateName === "head") {
+ compileStep.appendDocument({
+ section: "head",
+ data: HTML.toHTML(result)
+ });
+
+ } else {
+
+ if (templateName === "body")
+ jsContent = bodyGen(result);
+ else
+ jsContent = templateGen(result, templateName);
+
+ compileStep.addJavaScript({
+ path: compileStep.inputPath + '.js',
+ sourcePath: compileStep.inputPath,
+ data: jsContent
+ });
+ }
+};
+
+var pluginOptions = {
isTemplate: true,
archMatching: "web"
-}, sourceHandler);
+};
+
+Plugin.registerSourceHandler("jade", pluginOptions, fileModeHandler);
+Plugin.registerSourceHandler("tpl.jade", pluginOptions, templateModeHandler);
@@ -0,0 +1,2 @@
+#UnwrappedTemplateUniqueIdentifier
+ +img_tag_here
@@ -0,0 +1 @@
+img
@@ -2,3 +2,8 @@ Tinytest.add('Jade - Runtime template insertion', function (test) {
test.isNotNull(document.querySelector('#myUniqueJadeIdentifier pre',
"it should insert a pre tag inside the body"));
});
+
+Tinytest.add('Jade - Runtime unwrapped template insertion', function (test) {
+ test.isNotNull(document.querySelector('#UnwrappedTemplateUniqueIdentifier img',
+ "it should insert an img tag inside the body"));
+});

0 comments on commit e7d5df8

Please sign in to comment.