:)
Alright!
I some how managed to stumble on a decent solution that does what I want - automatically inject all the front end dependencies that I installed using a front end dependency manager so that when I run:
sails lift
My layout.ejs or index.html (whatever you want to use) automatically contains:
<script src="../bower_components/jquery/dist/jquery.js"></script>
<script src="../bower_components/underscore/underscore.js"></script>
<script src="../bower_components/backbone/backbone.js"></script>
In the correct location.
Hopefully this helps others who are on the same boat as me.
So I got it working using the following guides:
Important Note
Before anything else, please ensure your .sailsrc
file looks like this:
{
"generators": {
"modules": {}
},
"hooks": {
}
}
and NOT like this:
{
"generators": {
"modules": {}
},
"hooks": {
"grunt": false // this line needs to be deleted
}
}
That line prevents Grunt from doing its thing.
Stuff To Install First
So some important tools we'll need to install first:
- Bower (http://bower.io/)
- Grunt-Wiredep (https://github.com/stephenplusplus/grunt-wiredep)
Bower
Bower is the front end dependency manager that will fetch and install our front end libraries like jQuery and Backbone.
Install this first using:
[sudo] npm install -g bower
You might or might not need to use the sudo
command in front of npm install
depending on your computer setup.
The important thing to do now is to do:
bower init
This will take you through a command line setup of your bower.json file and generate it. I just press enter a few times to certain questions and choose 'y' for certain answers like automatically add commonly ignore files.
One last important bower step we need to do. Create a new file called .bowerrc
and add this code:
{
"directory": "assets/bower_components"
}
This tells bower where to install the "bower_components" folder when you run the command bower install
.
The thing with SailsJS is the actual javascript, css and images are served from the .tmp/public
folder, so if we had bower_components folder outside of the assets
folder, our bower_components
will not be copied to the .tmp/public
folder. We'll get 404 Not Found errors later if we omit this step.
Grunt-Wiredep
Grunt-Wiredep is the tool that will help us inject the installed bower components (front end libraries like jQuery and Backbone) into our HTML templates layout files
We install this next after setting up the Bower manager. Install Grunt-Wiredep using:
[sudo] npm install --save-dev grunt-wiredep
Installing Our Frontend Javascript Libraries
OK, so now with Bower, we can install our javascript front end libraries using command:
bower install jquery --save
and
bower install backbone --save
This will install jQuery and Backbone libraries into the bower_components
folder.
Our bower.json
file should show:
{
...
"license": "MIT",
"ignore": [
"**/.*",
"node_modules",
"bower_components",
"test",
"tests"
],
"dependencies": {
"jquery": "~2.1.4",
"backbone": "~1.2.1"
}
}
Notice how the dependencies section has two new entries for jquery and backbone respectively.
Bower Injection Point
Rightio, so all our important tools are installed. We now need to tell Wiredep where to inject our jQuery and Backbone tags.
In my case, I have a file called layout.ejs
and I tell bower to inject the javascript libraries using:
...
<!-- bower:js -->
<!-- endbower -->
<!--SCRIPTS-->
<!--SCRIPTS END-->
</body>
</html>
Please note the order of the injection placeholders which is important. Here, I've specified to inject our "bower" libraries like jQuery and Backbone BEFORE Sails default "jsToInject" files.
When I write my Javascript files, I put them inside the assets/js
folder. I like to think of these javascript files as my "front-end logic javascripts" and NOT the javascript libraries like jQuery and Backbone.
So if I were to put the bower script placeholder after Sails's default SCRIPTS placeholder, I would get errors if I tried to use jQuery before the jQuery library was included:
Adding WireDep Grunt Task
I don't know how to explain what Grunt is, as far as I understand, it's what SailsJS use to automate tasks like copy javascript files, images, css to the appropriate folders.
One of the Grunt task we want SailsJS to do is inject the installed Bower components from the bower_components
folder into our HTML files for us automatically, rather us manually typing it in.
So, first thing is create a new file called wiredep.js
inside the tasks/config
folder. My wiredep.js
looks like this:
module.exports = function(grunt) {
grunt.config.set('wiredep', {
task: {
// Point to the files that should be updated when
// you run 'grunt wiredep'
src: [
'views/**/*.ejs', // .html support...
],
// we need this line so injection is correct path
ignorePath: '../assets',
options: {
// See wiredep's configuration documentation for the options
// you may pass:
// https://github.com/taptapship/wiredep#configuration
}
}
});
grunt.loadNpmTasks('grunt-wiredep');
};
The important line in the above grunt task file is:
ignorePath: '../assets',
Wiredep searches for the location of the "bower_components" folder and uses that as the prefix for injecting our javascript libraries.
Recall earlier we told bower to install the bower_components folder in the assets
folder so that Sails pipeline.js
copies the content in the assets folder to the .tmp/public
folder but this conflicts with Wiredep's default injection path prefix.
i.e. The default Wiredep inject will become:
// Sails does not serve files from the assets folder!
<script src="../assets/bower_components/backbone/backbone.js"></script>
We need it to become:
// bower_components is in the .tmp/public folder where Sails serves content
<script src="/bower_components/backbone/backbone.js"></script>
Grunt-Wiredep Injection
At this point, we can test to see if wiredep is injecting.
In my Mac terminal, when I run (at the root of my sailsjs project):
grunt wiredep
I can see in my layout.ejs
(my Sailsjs template file), I have
<!-- bower:js -->
<script src="/bower_components/jquery/dist/jquery.js"></script>
<script src="/bower_components/underscore/underscore.js"></script>
<script src="/bower_components/backbone/backbone.js"></script>
<!-- endbower -->
Which is what we want.
If you delete the three lines of <script></script>
tags that was injected in the above code and try running:
sails lift
You'll probably notice wiredep isn't injecting the javascript libraries. We definitely don't want to have to type grunt wiredep
before we run sails lift
every time.
Automate Grunt Wiredep Command
The last step is to automate grunt wiredep
command so wiredep injects the javascript libraries automagically when we run sails lift
.
We do this by registering the task in the file:
tasks/register/default.js
My default.js
looks like this:
module.exports = function (grunt) {
grunt.registerTask('default', ['wiredep', 'compileAssets', 'linkAssets', 'watch']);
};
Now after adding wiredep
to the default tasks, when we run:
sails lift
We can inspect our layout.ejs
file and see, once again, our javascript libraries are automagically injected into our HTML.