I am working on building a Sails.js app. I am quite new to Sails.js, Node.js environment and HTML templating engines. I would like to use Handlebars as my templating engine. While the basic rendering "works" (that is, data passed from the server is rendered appropriately and the base layout works), I cannot get the sails-linker to inject my client-side scripts and styles into the templates. I have searched, looked at Sail's documentation, and tried various settings but to no avail.
This is my pipeline file:
/**
* grunt/pipeline.js
*
* The order in which your css, javascript, and template files should be
* compiled and linked from your views and static HTML files.
*
* (Note that you can take advantage of Grunt-style wildcard/glob/splat expressions
* for matching multiple files, and ! in front of an expression to ignore files.)
*
* For more information see:
* https://github.com/balderdashy/sails-docs/blob/master/anatomy/myApp/tasks/pipeline.js.md
*/
// CSS files to inject in order
//
// (if you're using LESS with the built-in default config, you'll want
// to change `assets/styles/importer.less` instead.)
var cssFilesToInject = [
'styles/src/**/*.scss'
];
// Client-side javascript files to inject in order
// (uses Grunt-style wildcard/glob/splat expressions)
var jsFilesToInject = [
// Load sails.io before everything else
'js/dependencies/sails.io.js',
// Dependencies like jQuery, or Angular are brought in here
'js/dependencies/**/*.js',
// All of the rest of your client-side js files
// will be injected here in no particular order.
'js/**/*.js'
];
// Client-side HTML templates are injected using the sources below
// The ordering of these templates shouldn't matter.
// (uses Grunt-style wildcard/glob/splat expressions)
//
// By default, Sails uses JST templates and precompiles them into
// functions for you. If you want to use jade, handlebars, dust, etc.,
// with the linker, no problem-- you'll just want to make sure the precompiled
// templates get spit out to the same file. Be sure and check out `tasks/README.md`
// for information on customizing and installing new tasks.
var templateFilesToInject = [
'**/*.handlebars'
];
// Default path for public folder (see documentation for more information)
var tmpPath = '.tmp/public/';
// Prefix relative paths to source files so they point to the proper locations
// (i.e. where the other Grunt tasks spit them out, or in some cases, where
// they reside in the first place)
module.exports.cssFilesToInject = cssFilesToInject.map(function(cssPath) {
// If we're ignoring the file, make sure the ! is at the beginning of the path
if (cssPath[0] === '!') {
return require('path').join('!.tmp/public/', cssPath.substr(1));
}
return require('path').join('.tmp/public/', cssPath);
});
module.exports.jsFilesToInject = jsFilesToInject.map(function(jsPath) {
// If we're ignoring the file, make sure the ! is at the beginning of the path
if (jsPath[0] === '!') {
return require('path').join('!.tmp/public/', jsPath.substr(1));
}
return require('path').join('.tmp/public/', jsPath);
});
module.exports.templateFilesToInject = templateFilesToInject.map(function(tplPath) {
// If we're ignoring the file, make sure the ! is at the beginning of the path
if (tplPath[0] === '!') {
return require('path').join('views/', tplPath.substr(1));
}
return require('path').join('views/', tplPath);
});
I have added a handlebars.js task underneath tasks/config
// tasks/config/handlebars.js
// --------------------------------
// handlebar task configuration.
module.exports = function(grunt) {
// We use the grunt.config api's set method to configure an
// object to the defined string. In this case the task
// 'handlebars' will be configured based on the object below.
grunt.config.set('handlebars', {
dev: {
// We will define which template files to inject
// in tasks/pipeline.js
files: {
'.tmp/public/templates.js': require('../pipeline').templateFilesToInject
}
}
});
// load npm module for handlebars.
grunt.loadNpmTasks('grunt-contrib-handlebars');
}
My sails-linker.js file (also underneath tasks/config):
/**
* `sails-linker`
*
* ---------------------------------------------------------------
*
* Automatically inject <script> tags and <link> tags into the specified
* specified HTML and/or EJS files. The specified delimiters (`startTag`
* and `endTag`) determine the insertion points.
*
* #### Development (default)
* By default, tags will be injected for your app's client-side JavaScript files,
* CSS stylesheets, and precompiled client-side HTML templates in the `templates/`
* directory (see the `jst` task for more info on that). In addition, if a LESS
* stylesheet exists at `assets/styles/importer.less`, it will be compiled to CSS
* and a `<link>` tag will be inserted for it. Similarly, if any Coffeescript
* files exists in `assets/js/`, they will be compiled into JavaScript and injected
* as well.
*
* #### Production (`NODE_ENV=production`)
* In production, all stylesheets are minified into a single `.css` file (see
* `tasks/config/cssmin.js` task) and all client-side scripts are minified into
* a single `.js` file (see `tasks/config/uglify.js` task). Any client-side HTML
* templates, CoffeeScript, or LESS files are bundled into these same two minified
* files as well.
*
* For usage docs see:
* https://github.com/Zolmeister/grunt-sails-linker
*
*/
module.exports = function(grunt) {
grunt.config.set('sails-linker', {
devJs: {
options: {
startTag: '<!--SCRIPTS-->',
endTag: '<!--SCRIPTS END-->',
fileTmpl: '<script src="%s"></script>',
appRoot: '.tmp/public'
},
files: {
'.tmp/public/**/*.html': require('../pipeline').jsFilesToInject,
'views/**/*.html': require('../pipeline').jsFilesToInject,
'.tmp/public/templates.js': require('../pipeline').jsFilesToInject,
'views/**/*.handlebars': require('../pipeline').jsFilesToInject
}
},
devJsRelative: {
options: {
startTag: '<!--SCRIPTS-->',
endTag: '<!--SCRIPTS END-->',
fileTmpl: '<script src="%s"></script>',
appRoot: '.tmp/public',
relative: true
},
files: {
'.tmp/public/**/*.html': require('../pipeline').jsFilesToInject,
'views/**/*.html': require('../pipeline').jsFilesToInject,
'.tmp/public/templates.js': require('../pipeline').jsFilesToInject,
'views/**/*.handlebars': require('../pipeline').jsFilesToInject
}
},
prodJs: {
options: {
startTag: '<!--SCRIPTS-->',
endTag: '<!--SCRIPTS END-->',
fileTmpl: '<script src="%s"></script>',
appRoot: '.tmp/public'
},
files: {
'.tmp/public/**/*.html': ['.tmp/public/min/production.min.js'],
'views/**/*.html': ['.tmp/public/min/production.min.js'],
'views/**/*.handlebars': ['.tmp/public/min/production.min.js']
}
},
prodJsRelative: {
options: {
startTag: '<!--SCRIPTS-->',
endTag: '<!--SCRIPTS END-->',
fileTmpl: '<script src="%s"></script>',
appRoot: '.tmp/public',
relative: true
},
files: {
'.tmp/public/**/*.html': ['.tmp/public/min/production.min.js'],
'views/**/*.html': ['.tmp/public/min/production.min.js'],
'views/**/*.handlebars': ['.tmp/public/min/production.min.js']
}
},
devStyles: {
options: {
startTag: '<!--STYLES-->',
endTag: '<!--STYLES END-->',
fileTmpl: '<link rel="stylesheet" href="%s">',
appRoot: '.tmp/public'
},
files: {
'.tmp/public/**/*.html': require('../pipeline').cssFilesToInject,
'views/**/*.html': require('../pipeline').cssFilesToInject,
'.tmp/public/templates.js': require('../pipeline').jsFilesToInject,
'views/**/*.handlebars': require('../pipeline').cssFilesToInject
}
},
devStylesRelative: {
options: {
startTag: '<!--STYLES-->',
endTag: '<!--STYLES END-->',
fileTmpl: '<link rel="stylesheet" href="%s">',
appRoot: '.tmp/public',
relative: true
},
files: {
'.tmp/public/**/*.html': require('../pipeline').cssFilesToInject,
'views/**/*.html': require('../pipeline').cssFilesToInject,
'.tmp/public/templates.js': require('../pipeline').jsFilesToInject,
'views/**/*.handlebars': require('../pipeline').cssFilesToInject
}
},
prodStyles: {
options: {
startTag: '<!--STYLES-->',
endTag: '<!--STYLES END-->',
fileTmpl: '<link rel="stylesheet" href="%s">',
appRoot: '.tmp/public'
},
files: {
'.tmp/public/index.html': ['.tmp/public/min/production.min.css'],
'views/**/*.html': ['.tmp/public/min/production.min.css'],
'views/**/*.handlebars': ['.tmp/public/min/production.min.css']
}
},
prodStylesRelative: {
options: {
startTag: '<!--STYLES-->',
endTag: '<!--STYLES END-->',
fileTmpl: '<link rel="stylesheet" href="%s">',
appRoot: '.tmp/public',
relative: true
},
files: {
'.tmp/public/index.html': ['.tmp/public/min/production.min.css'],
'views/**/*.html': ['.tmp/public/min/production.min.css'],
'views/**/*.handlebars': ['.tmp/public/min/production.min.css']
}
},
// Bring in JST template object
devTpl: {
options: {
startTag: '<!--TEMPLATES-->',
endTag: '<!--TEMPLATES END-->',
fileTmpl: '<script type="text/javascript" src="%s"></script>',
appRoot: '.tmp/public'
},
files: {
'.tmp/public/index.html': ['.tmp/public/jst.js'],
'views/**/*.html': ['.tmp/public/jst.js'],
'views/**/*.handlebars': ['.tmp/public/jst.js']
}
},
devJsJade: {
options: {
startTag: '// SCRIPTS',
endTag: '// SCRIPTS END',
fileTmpl: 'script(src="%s")',
appRoot: '.tmp/public'
},
files: {
'views/**/*.jade': require('../pipeline').jsFilesToInject
}
},
devJsRelativeJade: {
options: {
startTag: '// SCRIPTS',
endTag: '// SCRIPTS END',
fileTmpl: 'script(src="%s")',
appRoot: '.tmp/public',
relative: true
},
files: {
'views/**/*.jade': require('../pipeline').jsFilesToInject
}
},
prodJsJade: {
options: {
startTag: '// SCRIPTS',
endTag: '// SCRIPTS END',
fileTmpl: 'script(src="%s")',
appRoot: '.tmp/public'
},
files: {
'views/**/*.jade': ['.tmp/public/min/production.min.js']
}
},
prodJsRelativeJade: {
options: {
startTag: '// SCRIPTS',
endTag: '// SCRIPTS END',
fileTmpl: 'script(src="%s")',
appRoot: '.tmp/public',
relative: true
},
files: {
'views/**/*.jade': ['.tmp/public/min/production.min.js']
}
},
devStylesJade: {
options: {
startTag: '// STYLES',
endTag: '// STYLES END',
fileTmpl: 'link(rel="stylesheet", href="%s")',
appRoot: '.tmp/public'
},
files: {
'views/**/*.jade': require('../pipeline').cssFilesToInject
}
},
devStylesRelativeJade: {
options: {
startTag: '// STYLES',
endTag: '// STYLES END',
fileTmpl: 'link(rel="stylesheet", href="%s")',
appRoot: '.tmp/public',
relative: true
},
files: {
'views/**/*.jade': require('../pipeline').cssFilesToInject
}
},
prodStylesJade: {
options: {
startTag: '// STYLES',
endTag: '// STYLES END',
fileTmpl: 'link(rel="stylesheet", href="%s")',
appRoot: '.tmp/public'
},
files: {
'views/**/*.jade': ['.tmp/public/min/production.min.css']
}
},
prodStylesRelativeJade: {
options: {
startTag: '// STYLES',
endTag: '// STYLES END',
fileTmpl: 'link(rel="stylesheet", href="%s")',
appRoot: '.tmp/public',
relative: true
},
files: {
'views/**/*.jade': ['.tmp/public/min/production.min.css']
}
},
// Bring in JST template object
devTplJade: {
options: {
startTag: '// TEMPLATES',
endTag: '// TEMPLATES END',
fileTmpl: 'script(type="text/javascript", src="%s")',
appRoot: '.tmp/public'
},
files: {
'views/**/*.jade': ['.tmp/public/jst.js']
}
}
});
grunt.loadNpmTasks('grunt-sails-linker');
};
Lastly, my compileAssets.js file
/**
* `compileAssets`
*
* ---------------------------------------------------------------
*
* This Grunt tasklist is not designed to be used directly-- rather
* it is a helper called by the `default`, `prod`, `build`, and
* `buildProd` tasklists.
*
* For more information see:
* http://sailsjs.org/documentation/anatomy/my-app/tasks/register/compile-assets-js
*
*/
module.exports = function(grunt) {
grunt.registerTask('compileAssets', [
'clean:dev',
'copy:dev',
"handlebars:dev",
"sass:dev",
"ts:dev",
]);
};
I have several .handlebars files underneath the views directory including layout.handlebars. The content is being rendered correctly; my style and scripts tags are simply not being injected.
Thanks much for any guidance.