Decoding JET's Grunt Scripts
If you’ve scaffolded out a JET QuickStart project using the Yeoman generator, you may have noticed some Grunt tooling in a scripts
folder in the root of your project. If you haven’t, give it a try at the command prompt (assuming you already have Yeoman installed):
# this gets the JET generator installed once for all time
npm install -g generator-oraclejet
# this generates a JET application with QuickStart_Basic template into a yoJET directory
yo oraclejet yoJET --template=basic
After this runs, a message even tempts you regarding your new Grunt powers with the hint that you can change to the yoJET directory and run grunt build
and grunt serve
.
So we know there are at least two Grunt tasks delivered in the Oracle JET Yeoman generator. But did you know they run differently in development mode vs. release mode? What other coolness lurks in the JET Grunt implementation?
A leaned-down Gruntfile.js
In the root of your generated yoJET application is a Gruntfile.js
file. This is Grunt’s most important file, and in most projects it can get very long and cluttered with many lines of configuration for all of the Grunt tasks you want to set up.
But in the generated version, you’ll see that things are lean and tidy. In fact, there’s none of the normal grunt.loadNpmTasks()
calls nor endless configuration settings. Instead, the JET Grunt setups lean on grunt-load-config and jit-grunt to implement the configs and load everything on the fly.
Gruntfile.js
/**
* Copyright (c) 2014, 2016, Oracle and/or its affiliates.
* The Universal Permissive License (UPL), Version 1.0
*/
"use strict";
var path = require("path");
/*
* Currently the tooling uses load-grunt-config to manage it's tasks.
* In future grunt plugin will be created for better management
*/
module.exports = function (grunt)
{
require("jit-grunt")(grunt, {});
require("load-grunt-config")(grunt,
{
configPath: path.join(process.cwd(), "scripts/grunt/config"),
jitGrunt:
{
customTasksDir: "scripts/grunt/tasks"
},
data:
{
oraclejet:
{
ports:
{
server: 8000,
livereload: 35729
}
}
}
});
};
Notice that the configPath
is being redirected to the scripts/grunt/config
folder in our project. There’s a whole folder structure with Grunt settings created by the Yeoman generator:
What’s up with the Scripts folder
This structure is how load-grunt-config
likes to think. By separating each Grunt task’s configs into their own files it keeps things orderly and easy to edit. Meanwhile, jit-grunt
takes care of loading the node_modules plugins based upon the task names you use instead of loading them all up at the beginning of your Gruntfile.
The scripts/grunt/config
files fall into the proper naming pattern and contain the configurations that would normally be in the Gruntfile’s grunt.initConfig()
. A module’s configs are stuffed into a module.exports
object. However, note that the module’s config object notation changes ever so slightly: you don’t need the eponymous outer object named after the modules and can instead just export the meat of the Grunt module’s config. Compare, for example, the generated scripts/grunt/config/clean.js
export with a sample Gruntfile config for grunt-contrib-clean — there’s no outer clean: {...}
object in the JET-generated case.
The scripts/grunt/common
folder contains importable variables used by the tasks and configs, such as build.js
and bowercopy.js.
These imports are more modular than the global values set in the Gruntfile’s data: {}
object.
Finally, under the scripts/grunt/tasks
folder you’ll find our two seeded Grunt tasks: build.js
and serve.js
. These tasks can call the other modules with various targets or even set options based upon which target you call the task with.
Out-of-the-box Grunt commands
So the fresh-from-the-generator setup for a JET project includes at least the following Grunt tasks:
grunt serve
(same asgrunt serve:dev
) – spins up a webserver pointing at your project code, watches for changes to your files, and uses liveReload to refresh the browser upon changesgrunt build
(same asgrunt build:dev
) – doesn’t do anything. Even though the:dev
target is the default, there isn’t anything to “build” since the dev files are served directly out of the project rootgrunt build:release
– runs a boatload of tasks which create a/release
folder in the root of the project and then copy the code and assets needed to deploy your JET project. Notice that the tasks that run include uglifying your JavaScript and moving copies of the Bower-sourced libraries into placegrunt serve:release
– spins up a webserver that points at that/release
folder so you can verify that everything built correctly before deploying to a production webservergrunt bowercopy
– an interesting task that copies specified files from the bower_components directory into the js/libs directory. The Yeoman generator accomplishes this when it scaffolds out your project, as you can see (for example inside ofmain.js
) that everything points to the supporting files in the/js/libs/
folders. But if you later usebower install
to add an additional library of your own to your project, you’ll want to update thescripts/grunt/config/bowercopy.js
file and then rerungrunt bowercopy
so that your new dependencies are “inside” your project instead of referencing them straight out of thebower_components
folders
Understanding the way JET leverages load-grunt-config
and how the files in the scripts/grunt
folders work together opens up the possibility of tweaking the Grunt tasks to match our build process, and also gives us the knowledge needed to add additional Grunt modules to our projects.