Using Gulp to Manage All Your Static Assets
The web development community continues its march towards more advanced methods and processes. CSS preprocessing, JavaScript testing and linting, image compression, and much more make up the web development landscape. Where does that leave you, humble Salesforce developer? Your JavaScript, css and images all get loaded into static resources and that’s it. Surely there is no way to integrate the Salesforce way with all these cool, new tools, right? Prepare to be mildly interested.
Now, before we get started, I am going to make a number of assumptions:
-
You are semi-comfortable with the command line
-
You have your own development environment. For reference, check Jesse Altman’s ideal explanation and for more depth, read the other articles he has in the series
-
You have your Salesforce code and metadata pulled down to your local machine for development
-
You have some way to deploy code (MavensMate, Force.com IDE, Ant migration tools, etc.)
Initial Gulp setup
The first part will be getting Gulp set up. To begin, you will have to download and install Node.js for whatever system you are using. After that is all set up, you are going to want to go into your Salesforce project and make a sibling folder to src. This is where we are going to load all of our assets, so name it appropriately. After that, make a folder for each asset type you are going to work with (i.e. js, css, img) and if you are going to use something like Sass or Less, then add a folder for that.
Next we are going to open the asset folder in a command line and enter the following commands:
npm init
npm install --global gulp
npm install --save-dev gulp
The first command will bring up a series of prompts where you can enter some information on your project. The next two actually install Gulp.
Next we need to create gulpfile.js
in the same folder and add these few lines
var gulp = require('gulp');
var connect = require('gulp-connect');
gulp.task('webserver', function() {
connect.server({
livereload: true
});
});
gulp.task('default', ['webserver'])
That file will set up the server (for now) so that typing the command gulp
and navigating to localhost:8080 in a browser will show your asset directory.
Managing your dependencies
If you depend on a number of third party libraries, you may want to consider a package manager. If you don’t want this, it is not necessary for the article but it can be a huge help. I will be using a tool called Bower. Bower lets you search and download dependencies, much like npm, but with a focus on the front end. To get started with Bower, first we must run
npm install --global bower
What you actually install at this point is up to you, but for example I will install jQuery and Normalize.css. Like with npm, Bower has an init command that will prompt you for various values.
bower init
bower install jquery --save
bower install normalize.css --save
Local files, now with more cloud
Now that we have our files served up, we can use a bit of trickery to load from our little web server when developing locally, but then use the static resources in all the other environments. The basic premise of this is using a flag in a custom setting, something like Use Local
, and either rendering a component with all the local assets, or the static resources. There are a lot of ways to actually lay this out, but for me, I will be using two sets of components so my CSS loads in the head and my JavaScript loads in the footer.
Before we even get to writing the components, we need a few things in place. First is the custom setting so you can turn the feature on and off. Next is some dummy static resources. Now this depends heavily on what you are going to do with your assets but for me, I plan on concatenating and minifying my JavaScript and CSS. I plan on zipping third party assets into their own file. For my purposes I will create a blank js file, css file, and zip file and upload it to the static resources. Once I get that done it is on to the components.
<apex:component >
<c:HeaderAssetsLocal rendered="{!$Setup.Static_Resources__c.Use_Local__c}"/>
<c:HeaderAssetsResource rendered="{!NOT($Setup.Static_Resources__c.Use_Local__c)}"/>
</apex:component>
<apex:component >
<apex:stylesheet value="http://localhost:8080/bower_components/normalize.css/normalize.css" />
<apex:stylesheet value="http://localhost:8080/css/style.css" />
</apex:component>
<apex:component >
<apex:stylesheet value="{!URLFOR($Resource.Vendor, 'normalize.css/normalize.css')}" />
<apex:stylesheet value="{!URLFOR($Resource.AppStyles)}" />
</apex:component>
<apex:component>
<c:FooterAssetsLocal rendered="{!$Setup.Static_Resources__c.Use_Local__c}"/>
<c:FooterAssetsResource rendered="{!NOT($Setup.Static_Resources__c.Use_Local__c)}"/>
</apex:component>
<apex:component>
<script src="http://localhost:8080/bower_components/jquery/dist/jquery.js" ></script>
<script src="http://localhost:8080/js/app.js" ></script>
</apex:component>
<apex:component>
<script src="{!URLFOR($Resource.Vendor, 'jquery/dist/jquery.min.js')}" ></script>
<script src="{!URLFOR($Resource.AppJavaScript)}" ></script>
</apex:component>
This design was originally from this GitHub repository for a Visualforce AngularJs Seed. The design was later refined by Peter Knole.
The story so far
If you have been following along you have all the bits and pieces set up. Now you need to put it together in a page and write up your CSS and JavaScript. The page can be anything, so long as it has the components for the CSS and JavaScript at the top and bottom respectively. Don’t forget to set the custom setting to true so you use your local files. If everything went well, then viewing your page should work. If it doesn’t, check that your browser isn’t blocking mixed content . If you really want to test, make a change to your CSS, save, then reload and see that your page changed without any long save times.
Building for now
You now can view your work in your own sandbox just fine, but what about everyone else? So this tutorial doesn’t become a colossal pain, we will be setting the custom setting to false to simulate the remote environment. Now, viewing the page shouldn’t look so good. That is because we haven’t done anything about the static resources. We are going to fix that, but we need a bit of help. First grab a few more dependencies:
npm install --save-dev gulp-zip
npm install --save-dev gulp-minify-css
npm install --save-dev gulp-concat
npm install --save-dev gulp-uglify
Now for the script
var gulp = require('gulp');
var connect = require('gulp-connect');
var zip = require('gulp-zip');
var minifyCss = require('gulp-minify-css');
var concat = require('gulp-concat');
var uglify = require('gulp-uglify');
gulp.task('webserver', function() {
connect.server({
livereload: true
});
});
gulp.task('zip-vendor', function () {
return gulp.src('bower_components/**/*.*')
.pipe(zip('Vendor.resource'))
.pipe(gulp.dest('../src/staticresources'));
});
gulp.task('process-css', function() {
return gulp.src('css/*.css')
.pipe(concat('AppStyles.resource'))
.pipe(minifyCss({compatibility: 'ie8'}))
.pipe(gulp.dest('../src/staticresources'));
});
gulp.task('process-js', function() {
return gulp.src('js/app.js')
.pipe(concat('AppJavaScript.resource'))
.pipe(uglify())
.pipe(gulp.dest('../src/staticresources'));
});
gulp.task('default', ['webserver'])
gulp.task('build', ['zip-vendor', 'process-css', 'process-js'])
To break that down, lets look at the tasks that were just added. The zip-vendor
task gets every file and directory in the bower_components folder, zips it up and puts it in the static resources. The process-css
, and process-js
task gets all our CSS and JavaScript files, concatenates them into their own files, minifies them, and then places them in the static resources. Once that is all set, run gulp build
to rebuild the static resources. After that, you just need to get the files to the server however you are comfortable. For example, if you are using MavensMate, just run the compile.
Building for the future
There is so much more you can do than what I laid out. The tools and techniques of web development are constantly changing and shifting. This article was done so you can see a possibility. Nothing I have done is set in stone or even scratches the surface. The makers of jsForce, for instance, have a post I took inspiration from for deploying assets right from Gulp. There are also a wealth of tutorials on these tools as well as many others that can be added to your workflow.
I say to you now, welcome to the present age of web development, we’ve been expecting you.