pug.compile() cannot find template files (res.render() can) - node.js

I'm writing a small NodeJS/Express app. I set up pug as its template engine:
const app = express();
app.set('view engine', 'pug');
app.set('views', __dirname + "/public/views");
app.use(express.static(__dirname + '/public/static'));
This works fine when calling a res.render() to send HTML responses:
app.get('/', function getIndex(req, res){
res.render('index.pug');
});
But when I try to render small components and collect them in a string or an array as response to an AJAX call, I can't get it to work.
const pug = require('pug');
const compile = pug.compileFile('option.pug');
This always results in Error: ENOENT: no such file or directory, open 'option.pug'. I tried changing the path to the router's perspective (so something like ../../public/views/option.pug) but this also does not help.
I don't know why the paths are interpreted differently.
How do I refer to this template when using pug.compileFile?

From the Pug source code, the passed path is set as the filename in the options:
https://github.com/pugjs/pug/blob/926f7c720112cac76cfedb003e25e9f43d3a1767/packages/pug/lib/index.js#L354
This is then passed to handleTemplateCache to read the file:
https://github.com/pugjs/pug/blob/926f7c720112cac76cfedb003e25e9f43d3a1767/packages/pug/lib/index.js#L215
So ultimately the path is just being passed to fs.readFileSync, which treats relative paths as being relative to the current working directory, process.cwd().
You could generate the appropriate path using something like this:
const file = app.get('views') + '/option.pug';
It would be better to use path.join rather than string concatenation for building paths, https://nodejs.org/api/path.html#path_path_join_paths
const path = require('path');
const file = path.join(app.get('views'), 'option.pug');
If you don't want to (or can't) use app.get('views') you could just build up the absolute path by other means, such as using __dirname directly.
Note also that you can pass a callback to res.render which will be passed the rendered HTML instead of writing it to the response. That may allow you to avoid calling the template directly in the first place.

Related

Access Node Environment Variables in Jade file

I am trying to figure out how to optionally display text in a jade file based on the environment. I can't seem to figure out how to access the NODE_ENV variable in my jade file.
In my index.jade file I am doing:
if process.env.NODE_ENV === 'development'
h1 I am in development mode
else
h1 I am not in development mode
The problem is that process.env.NODE_env is undefined.
When I try and do: h1 #{process.env} outside of the if statement, Jade outputs [Object Object] onto the page.
When I try and do: h1 #{process.env.NODE_ENV} outside of the if statement, Jade does not output anything onto the page.
I am not rendering my Jade files on fly, rather I am building them all as "static" files whenever I start the server.
Anything you want to access in the jade template has to be in the locals object sent down from the server. For something like the process environment, you can do this right when you fire up your app:
const express = require('express');
var app = express();
app.locals.env = process.env; // though you might prefer to clone this instead of setting them equal
Then in your jade template you can do
#{env.NODE_ENV}
UPDATE
Adding for direct use, rather than in an express server.
const pug = require('pug');
// Compile the source code
const compiledFunction = pug.compileFile('template.pug');
// Render a set of data
console.log(compiledFunction(process.env));
That'll log it, but of course you could just as easily write that to an HTML file using fs utilities instead.

nodejs static content NOT using sendFile

How can I make nodejs serve up static content without having to use sendFile? The problem with sendFile is it opens another tab as you navigate from page to page, here is a snippet...
app.use(express.static(__dirname + '/public'));
app.use(bodyParser.urlencoded({ extended: true }));
app.post('/verify', function(request, response) {
var oneTimeCode = request.body.oneTimeCode;
var timeEntry = Date.now();
if (oneTimeCode == 'abc123') {
response.sendFile('/home/ubuntu/form.html');
} else {......
Can I use something like this in place of sendFile?
app.use(express.static(__dirname + '/home/ubuntu/form.html'));
express.static is not a good option to serve an individual file.
It is intended to serve all static assets inside a folder using the url for file name resolution as described here.
sendFile() shouldn't be responsible for opening new tabs. May be you have a wrong target on the anchor html element?
If you want to send static content without using sendFile() you can always open the static asset file and transfer the content over the response stream. This is what sendFile() does. You can review the source code and take ideas from there.

using parameters in url causing included files to be downloaded from same url

I want to get parameters from url and display content based on the parameters.
I am using express 4 here is my code
app.set('views', __dirname + '/views');
app.set('view engine', 'ejs');
app.use(express.static(__dirname + '/public'));
app.get('/showcontent/:book/:page',
function(req, res) {
console.log("app.get parameters");
res.render('bookPage', { book: req.params.book,page: req.params.page });
}
);
in my public folder there are css and js files. when i use url with parameters like
mydomain.com/showcontent/book/page123
it tries to get css and js files from
mydomain.com/showcontent/book/page123/css/style.css
mydomain.com/showcontent/book/page123/js/script.js
while it should get the files from public folder from url
mydomain.com/css/style.css
mydomain.com/js/script.js
everything works fine if I use url format like this
/showcontent?book=1234&page=12345
but i dont want to use this format, I think some thing is missing in my code please someone help me solve to get parameters from url using forward slash format and still get css and js files from public directory

Express + Consolidate + Hogan: ` Error: cannot find module 'hogan' ` with code straight from consolidate docs

I'm getting an Error: cannot find module 'hogan' when I send a request to the node.js server implemented in Coffeescript here:
https://gist.github.com/wmayner/306c89d7f8fbeed3f098
I've installed the dependencies hogan.js, consolidate, and express.
I've reproduced the example code from consolidate's documentation (reproduced below) almost exactly, so I'm having trouble seeing where this error is coming from. It looks like it should work.
From the consolidate docs:
var express = require('express')
, cons = require('consolidate')
, app = express();
// assign the swig engine to .html files
app.engine('html', cons.swig);
// set .html as the default extension
app.set('view engine', 'html');
app.set('views', __dirname + '/views');
I've also tried declaring `hogan = require('hogan.js')' as a dependency.
Anyone have an idea why this is happening?
Note: The gist above differs from the consolidate docs in that I'm setting the view engine to hogan rather than html. This is because I'd rather use .hogan than .html for my template file extensions (I've tried .html and I get the same error).
Your gist sets hogan as view engine, but that should be html like in the Consolidate docs:
// tell Express to use Consolidates 'hogan' renderer for .html templates
engines = require 'consolidate'
engine = 'hogan'
app.engine 'html', engines[engine]
// tell Express to use '.html' as extension to find views with .render()
app.set 'view engine', 'html'
EDIT: realizing that perhaps you want to use .hogan as extension for your template files, you could use this instead:
app.engine 'hogan', engines[engine]
app.set 'view engine', 'hogan'

using dustjs-linkedin templates in node.js and express3.x

I can't figure out how to use the dustjs-linkedin templates for express 3.x
#app.js
var dust = require('dustjs-linkedin');
app.set('view engine', 'dust');
app.get('/test1', routes.test1);
#./routes/test.js
exports.test1 = function(req, res){
res.locals.session = req.session;
res.render('test1', { title: 'Test 1' } );
};
#./views/test1.dust
{+base.dust/}
{<main}
Child Content
{/main}
#./views/base.dust
{+main}
Base Content
{/main}
I get the following error when going to /test1
500 Error: Cannot find module 'dust'
I had the same problems as you. And to ease the use of dustjs-linkedin together with express 3.x i put together the small library klei-dust. The library is simple to setup and you can set the root folder for views, which applies to base-templates and partials.
So if you have a views folder at views/ with home.dust and base.dust templates, the home.dust can look like this:
{>base/}
{<main}
Hello world
{/main}
So there's no need to write views/base.dust for it to work.
I've managed to get a working version of dustjs-linkedin with consolidate module.
https://github.com/chovy/express-template-demo
FYI, the layout has to be double quoted...that was a major gotcha for me, and its relative to app.js file, and it needs a trailing /
{+"views/base.dust"/}
<p>Page content here</p>
I will explain u how you should use express 3.x with dustjs-linkedin.
1) express has 2 config to set. 'view engine' and app.engine
"view engine" just sets the default and that app.engine just maps what engine to use for a given file extension.
so you should do something like this:
app.set('view engine', 'dustjs-linkedin');
app.set('views', __dirname + '/views');
app.engine('dust', dust.compileFromPath);
There is only one problem with this is that the method compileFromPath doesn't exist in dust :p.
You should add a method in the dust object with this signature that Express expects: (path, options, callback)
you can read more about this here: http://expressjs.com/api.html#app.engine.
Another option would be to use consolidate (http://spalatnik.com/blog/?p=54) but unfortunately Consolidate doesn't support the dustjs-linkedin version it only support the old dust version.

Resources