using execution directory as Gruntfile's file base

382 Views Asked by At

I'm trying to use Grunt to clean up a large project. For this specific example, I am trying to run unit tests and want to do so only for paths under the current grunt execution directory (i.e., the result of pwd).

I want one Gruntfile at the project root. I know grunt will find and execute this with no problem from any subdirectory. If I define my test runner options to look in "test/", it only runs tests under {project root/}test/. Is there a way to tell a project-level Gruntfile to make its paths (in all or in part) relative to the executing location?

Notes:

  • I don't need to be told "Why would you do this? Grunt should manage your whole project!" This is a retrofit, and until that halcyon day when it all works, I want/need it piecemeal.
  • To reiterate, "**/test/" isn't the answer, because I want only the tests under the current grunt execution directory.
  • --base also won't work, because Grunt will look for the Node packages at the base location.
  • I have, for similar situations, used a shared configuration JSON file that I've imported with grunt.config.merge(grunt.file.readJSON("../grunt-shared.json"));. However, that requires Gruntfiles in subfolders, as well as a hard-coded path to the shared file (e.g., ../), which seems tenuous.
  • I could write code to do some directory climbing and path building, but I'd like to make that a last resort.
1

There are 1 best solutions below

0
On BEST ANSWER

Here's the solution I came up with (H/T to @firstdoit, https://stackoverflow.com/a/28763634/356016):

  • Create a single, shared JavaScript file at the root of the project to centralize Grunt behavior.
  • Each "sub-project" directory has a minimal, boilerplate Gruntfile.js.
  • Manually adjust Grunt's file base in the shared file to load from one node_modules source.

Gruntfile.js

/**
 * This Gruntfile is largely just to establish a file path base for this
 * directory. In all but the rarest cases, it can simply allow Grunt to
 * "pass-through" to the project-level Gruntfile.
 */
module.exports = function (grunt)
{
    var PATH_TO_ROOT = "../";

    // If customization is needed...
    // grunt.config.init({});
    // grunt.config.merge(require(PATH_TO_ROOT + "grunt-shared.js")(grunt));

    // Otherwise, just use the root...
    grunt.config.init(require(PATH_TO_ROOT + "grunt-shared.js")(grunt));
};

Using a var for PATH_TO_ROOT is largely unnecessary, but it provides a single focus point for using this boilerplate file across sub-projects.

{ROOT}/grunt-shared.js

module.exports = function (grunt)
{
    // load needed Node modules
    var path = require("path");

    var processBase = process.cwd();
    var rootBase    = path.dirname(module.filename);

    /*
     * Normally, load-grunt-config also provides the functionality
     * of load-grunt-tasks. However, because of our "root modules"
     * setup, we need the task configurations to happen at a different
     * file base than task (module) loading. We could pass the base
     * for tasks to each task, but it is better to centralize it here.
     *
     * Set the base to the project root, load the modules/tasks, then
     * reset the base and process the configurations.
     *
     * WARNING: This is only compatible with the default base. An explicit base will be lost.
     */
    grunt.file.setBase(rootBase);
    require("load-grunt-tasks")(grunt);

    // Restore file path base.
    grunt.file.setBase(processBase);

    // Read every config file in {rootBase}/grunt/ into Grunt's config.
    var configObj = require("load-grunt-config")(grunt, {
        configPath: path.join(rootBase, "grunt"),
        loadGruntTasks: false
    });

    return configObj;
};