Config DustJS (ServerSide) do not escape new line? - node.js

I'm using DustJS with ExpressJS with NodeJS.
When ExpressJS renders, it auto escape the new line and white space in my dustJS; while since I'm putting JavaScript into my DustJS templates, I want to leave the white spaces as it is.
How to achieve that?
Thanks a lot.
Update: here's some further explanation:
In my template.dust, I have something like:
<script type='template' id="tempVO">{.vo|s}</script>
<script> {! Block to compile the dustJS into cache !}
$(function($){
dust.compile($("#tempVO").html(),"vo");
// register helper
dust.helpers.justToString = function(chunk, ctx, body, param){
console.log(param);
}
})();
The default behaviour of DustJS, when used with expressJS by doing:
app.get("/",function(req,res){
res.render("ajaxTemplate",xmlService.templates);
});
would escape all the white spaces so that it turns out something like:
$(function($){dust.compile($("#tempVO").html(),"vo");// register helperdust.helpers.justToString = function(chunk, ctx, body, param){console.log(param);}})();// AJAX Callback</script><script>$(function(){$(".tipNeeded").tooltip();$(".tabsNeeded").tabs();});
Which is certainly not what I want.]
Although I can just use some external JavaScript Import, like:
<script src="my/own/beautiful.js"></script>
Still I'd like to know how to put in-HTML script by not-escaping the white space when render my dustJS files.

From the Dust Tutorial on controlling whitespace suppression, the exact method would vary based upon the version you are using. If you are on v1.2.6 (the latest as of this writing) you would add this line to your code:
dust.optimizers.format = function(ctx, node) { return node };
To quickly test it, add that line immediately after your require for the dustjs-linkedin module:
var dust = require('dustjs-linkedin');
Edit:
From version 1.2.0 to 1.2.5, the compile function took three arguments:
dust.compile = function(source, name, strip)
If strip was set to false then whitespace would be preserved. However, it looks like this argument was removed in 1.2.6. Even with a version that has the strip argument, when using express, you aren't calling dust.compile directly. You might be able to use something like consolidate.js as a basis to write two render calls, one with whitespace and one without...but I don't see a plainly simple way.

Related

gulp-inject: missing end tag for start tag when using pug

I have a rather specific problem: I am trying to build a complex stack using Sails.js with machinepack-sailsgulpify.
In order to inject assets into my templates I use gulp-inject plugin, as the machinepack suggests. The problem is that for anything other than html and ejs the injector doesn't work. It simply doesn't change anything. No errors, nothing.
My task looks like this:
gulp.task('sails-linker-gulp:devViews', function() {
return gulp.src('views/**/*.?(html|ejs|jade|pug|haml|slim|dust)') // Read templates
.pipe(
plugins.inject(
gulp.src(require('../pipeline').jsFilesToInject, {read: false}), // Link the javaScript
{
starttag: generateScriptStartTag,
endtag: generateScriptEndTag,
ignorePath: '.tmp/public',
transform: (filepath, file, i, length) => {
return `script(src="${filepath}")`;
}
}
)
)
.pipe(
plugins.inject(
gulp.src(require('../pipeline').cssFilesToInject, {read: false}), // Link the styles
{
starttag: generateStyleStartTag,
endtag: generateStyleEndTag,
ignorePath: '.tmp/public'
}
)
)
.pipe(
plugins.inject(
gulp.src( ['.tmp/public/jst.js'], {read: false}), // Link the JST Templates
{
starttag: generateTemplateStartTag,
endtag: generateTemplateEndTag,
ignorePath: '.tmp/public'
}
)
)
.pipe(gulp.dest('views/'))// Write modified files...
Don't worry about the generateScriptStartTag and such functions, they are just there for control and I am 1000% sure they work correctly, tested a lot. They generate the tags kind of like this:
//- SCRIPTS
//- SCRIPTS END
depending on the template language.
Adding custom transform function did not work. If I use ejs or html or really anything that resembles html syntax it works fine.
Now, about Sails: I can NOT add a gulp task to compile the template before injecting because Sails renders templates on request in development, it doesn't actually pre-compile them into any directory. And honestly: why should I? The injection is just adding lines to my .jade/.pug files in views, the files are there already, so I don't see why there's a problem there. Can someone advise?
UPDATE:
Rather frustrating inspection of the code revealed that the 'matches' property when running the inject function of has length 0 and when inspecting the content of the stream in node inspector, I did not see the comments, they were stripped away, despite the fact that they are clearly there in the file.
UPDATE #2:
It appears that I was wrong about ejs. ONLY HTML files are getting processed. Also it works OK when it doesn't detect the injection comments. However if it does the end event simply never emits for that file and nothing gets injected. This is true for ALL templating engines, only static HTML files have injection working fine.
UPDATE #3:
After another 5 hours of debugging I found the problem, however my understanding of streams isn't good enough to get me any closer to the solution. The problem is that in inject function of the plugin there's a loop that doesn't quit properly, and while it perfectly injects the required tags into the stream, it then runs that loop again on the same stream (with injected tags), and throws an error.
Why that error never showed up in any console I don't know but there you go. Can someone please help? I am completely lost with this... Is it a bug in the plugin?
I had to figure this out on my own.
It is a bug in gulp-inject. The regex that this plugin generates to test against the injection tags does not match the whole line, it's simply matches the first occurrence. This means that if I have my tags set like so:
//SCRIPTS
//SCRIPTS END
The regex will match the starttag: //SCRIPTS twice:
And the end tag will only be matched once. This was causing the second faulty loop with the error for missing end tag.
A workaround is to avoid repeating start of tags.
//SCRIPTS
//END SCRIPTS
That's not a solution, however. A solution would be to alter the regex so that it only allows whitespace and newline characters in order to match the tag, when using an indent-based template language.
Something like this would work: /\/\/-\s*SCRIPTS(?=\s*\n|$)/ig
Can't believe nobody has stumbled upon this until now, seems like it would be a more common problem...

How to properly use dustjs & create helpers in sailsjs?

I'm using sailsjs to build my website but decided not to use its default EJS for templating.
In ./config/views.js, I have set the the following(after npm install dustjs-linkedin & dustjs-helpers):
engine: 'dust',
In my HomeController ./api/controllers/HomeController.js, I need to do the following to get the language text from ./config/locales/en.json:
title: res.i18n('title'),
//or
siteName: res.__('siteName')
In my home template ./views/home/index.dust, I can access those string with:
<h1>{title}</h1>
<p>{siteName}</p>
However, as the number of strings grow, it will be redundant as I have to set each of the variables in controller before I can use it in my template.
So my questions are:
is there a better or correct way to use the text variables in my template from ./config/locales/en.json without having to define each of them in my controller file?
how to access & use the dustjs module in my controller so that..
I can create custom dustjs helpers? (how to create dustjs helpers?)
also, it seems I cannot configure the settings for i18n in ./config/i18n.js (objectNotation: true not working) Is this the correct place to configure i18n?
Its a long ques. I'll answer the part: I can create custom dustjs helpers? (how to create dustjs helpers?)
Ans. Yes you can. Here's how (in sailsjs):
We can create a globally accessible service in api/services/MultiplyBy2.js
MultiplyBy2.js
module.exports = {
myFunc: function (chunk, context, bodies, params) {
var number = params.num;
number = number*2;
return chunk.write(number);
}
};
And then you can use this in a dustjs template like so:
<p>10 * 2 = {#sails.services.multiplyby2.myFunc num=10/}</p>
Note: The file name (MultiplyBy2) will be lowercase. But the function name will be the same as defined (myFunc);
Hope this helps.
I also had this problem. I further dug into this and figured that it's too much hassle to resolve this issue, and that's when I switched to Handlebars on Sails.js.
Handlebars' i18n's syntax in Sails.js is {{__ 'Welcome' }} .

Node.js and Express - Regarding rendering pages in a directory with one call

Hopefully I'm not duplicating a question here, but I looked around and didn't see anything that addressed this specific issue.
I'm constructing some pages by creating a bunch of unique content blocks that I store in a folder - things like this:
h4 Content Example
p.description.
Lorem ipsum dolor sample text etc etc
in, say, pages\contentBlocks\Example\contentExample.jade.
These content blocks are added to a mixin (pages\mixins.jade) that looks like this:
mixin contentBlock(title)
.content-block(id=title.toLowerCase())
h3 #{title}
if block
.content
block
and I call this mixin within a larger page (pages\portfolio.jade) like this:
.subsection
//- Expects a list of objects of the format { title, html }
each val in pageBlocks
+contentBlock(val.title)
!= val.html
(the intention is to later enhance this with other variables that can be passed down into the mixin for in-place formatting, like background images)
I would like to make use of the ability of Express to automatically render all of the pages in a directory in a single call. Like so (index.js):
var pageBlocks = [];
res.render('pages/contentBlocks/Example/*', function (err, html) {
if (err) throw err;
pageBlocks.push({ html: html });
});
However, I'd also like to be able to reference the title of the file, for example for setting the class of the block that I put it in. Ideally, it would look something like this:
res.render('pages/contentBlocks/Example/*', function (err, html) {
if (err) throw err;
pageBlocks.push({ title:functionToGetFileName(), html: html });
});
Is there any way to do this without manually iterating through the files in that directory?
Or, since this method seems needlessly convoluted (given the presence of mixins, includes, extends, and partials, it feels like I ought to be able to do this without extra render calls), is there a better way to do this?
Edit: I've done a bit more looking around and code testing, and now I'm unsure. Can Express implicitly render all of the files in a directory? Based on this question, I assumed it could, but I might have misread. The formatting of res.render('path/to/directory/', callback) doesn't seem to be working - I get the following error:
Error: Failed to lookup view "path/to/directory/" in views directory "/path/to/app/views"
For a temporary solution, I am using the following pattern:
exampleBlocks = [];
eb_dir = "pages/contentBlocks/Example";
fs.readdirSync("./views/" + eb_dir).forEach(function(file) {
res.render(eb_dir + "/" + file, function (err, html) {
if (err) throw err;
exampleBlocks.push({title: file, html:html})
});
});
Which is concise enough for my tastes, though calling res.render() so many times rustles my jimmies. I'd rather it be a single batch render, or better yet, solved through clever use of jade structures - but I still haven't thought of a way to do the latter.
Edit:
I decided I'd rather not simply enumerate the files in the folder - I want more control of the order. Instead of using an each loop in my page, I'm simply going to call the mixin with included content, like this (pages\portfolio.jade in my previous examples):
.subsection
+contentBlock("Block 1")
include blocks/block1
+contentBlock("Block 2")
include blocks/block2
I will leave the question here, however, as others may encounter a similar issue.

How do I escape EJS template code in node.js to be evaluated on the client side?

I use node.js/ejs on the server side and backbone.js on the client side.
Both server side and client side use the same templating style.
So the problem is, if I put template code meant for the client inside a template it still get's parsed on the server side.
If found out that something like this works:
<%- "<%= done ? 'done' : '' %\>" %>
However, IMHO this uglifies the code in a way which makes the whole point of using templates useless.
How would you approach this?
Is there a way to define blocks of code inside EJS-templates which do not get parsed like a {literal}-tag used in other templating languages?
Update: For now I use backbone's _.templateSettings to use different delimiters on the client side.
Update: Here's a similar solution in a JSP context: Underscore.js Templates Within JSP
The way I have dealt with this is to override the opening and closing tags on node so that the 2 instances of ejs are lookgin for different tags.
On node you can pass in options
{open:'<%',close:'%>'}
In my case I use <% and <# for my two versions. Then in node ejs template I have something like this (where name is from backbone and everyauth obviously from node):
<% if(everyauth.loggedIn) %><h1><#= name #></h1><% } %>
I assume the question could be read as following because it was this thread Google provide me at first link:
“How I can escape EJS template code delimiter tag only for limited items?”
tl;dr:
Use <%# %> to break the original parsing code (e.g. <<%# %>%= done ? 'done' : '' %<%# %>> will done the following unparsed code <%= done ? 'done' : '' %>)
Long explanation
Imagine a case I decide to change % by ? using { delimiter: '?' } option (that could be the case here, because we want not to use the same has Backbone.js).
Great, that solves your problem. Imagine now later, for some reason, you use your templating system to generate an XML. This XML will start with <?xml version="1.0" encoding="UTF-8"?>.
You will facing the same issue again. What do? You will change the delimiter again? And after that, you will change again? etc. No, for punctual escaping, what we should is just to be capable to say “Not parse this part of the document as EJS”.
So a trick is to avoid EJS understand it's an EJS delimiter parser. So avoid it (in our current case) parse <? (or <% in an original case).
So by simply adding <?# ?> to break the parsing, you will add nothing (the # item is for EJS comment) and you will avoid parser to understand <<?# ?>?xml version="1.0" encoding="UTF-8"?<?# ?>>. The output will be <?xml version="1.0" encoding="UTF-8"?>
Conclusion
In a punctual necessity to avoid EJS parsing, you can just trick the parser to produce the output you need by using <%# %> as a delimiter tag breaker.
For sure, probably in your case, you can just use the marked answer because you will use the EJS tag in a lot of cases.
Well, the way that I currently approach this is to use require.js with the text plugin; this allows me to include the templates using AJAX during development time and have them all compiled into an optimized/minified single file bundle during deploy time.
Of course, if you don't use require.js for dependency management of the rest of your JS code this doesn't work nearly as well, but I can't stand to do javascript dev without require.js anymore now that I'm used to it anyway.
Alternately, there may be other similar technologies that you could use to solve the same problem.
I use backbone.layout.manager on both the client and server side, because I want my templates to be exactly the same.
The way I solved the template delimiter issue was to render the page on the server side, then inject the raw backbone templates.
With new ejs you can add a custom delimiter at client side :
https://github.com/mde/ejs#custom-delimiters
eg :
Custom delimiters can be applied on a per-template basis, or globally:
var ejs = require('ejs'),
users = ['geddy', 'neil', 'alex'];
// Just one template
ejs.render('<?= users.join(" | "); ?>', {users: users}, {delimiter: '?'});
// => 'geddy | neil | alex'
// Or globally
ejs.delimiter = '$';
ejs.render('<$= users.join(" | "); $>', {users: users});
// => 'geddy | neil | alex'

Passing raw Markdown text to Jade

I'm playing around with my first Node.js Express application, and as every programmer knows, the first thing you should build when testing out a new framework is a blog! Anyway, I'd like to write the articles in Markdown and then render it in the view. I saw that Jade allows for this to be done inside the view itself, using filters, but I can't get that working.
To simplify the situation, here's an example of what I'm talking about.
//app.js
res.render("article", {
md : "Hello World!\n\n*Woo*"
});
//article.jade
section
:markdown
#{md}
But, that outputs this: <section><h1>{md}</h1></section>... it isn't substituting in the variables I've passed to it.
Then I tried this:
//article.jade
section
:markdown
!{md}
And the output is this:
<section><p>Hello World!
*Woo*</p></section>
So, now it's not parsing the markdown!
I have been able to get this to work by parsing the markdown in the app.js file and then passing the HTML to the view to display, but I don't know, that seems a bit messier.
Is there a way to pass variables into Jade filters?
You can do this with a function passed in to jade from node:
var md = require("node-markdown").Markdown;
Then pass it into the view as a local:
res.render('view', { md:md, markdownContent:data });
Then render it in the jade view by calling the function:
!= md(markdownContent)
The node module node-markdown is deprecated. The marked is advanced new version. You can try like this
var md = require('marked');
Inside your router
res.render('template', { md: md });
Inside your jade template
div!= md(note.string)
I don't think jade can do this out of the box. One way to accomplish it that might feel slightly cleaner than pre-rendering the markdown is to create a helper function called markdown that takes a markdown string and returns HTML. Then you could do something like
section
!= markdown(md)
The markdown function should be included in the locals data when you render the jade template and can directly use a markdown library to convert the markdown syntax to HTML.
If you are using Scalate's Jade support you can enter:
section
:&markdown
#{md}
You can also import external files with:
section
:&markdown
#{include("MyFile.md")}

Resources