6

I have a couple grunt tasks and I am trying to share global variables across those tasks and I am running into issues.

I have written a some custom tasks which set the proper output path depending on the build type. This seems to be setting things correctly.

// Set Mode (local or build)
grunt.registerTask("setBuildType", "Set the build type. Either build or local", function (val) {
  // grunt.log.writeln(val + " :setBuildType val");
  global.buildType = val;
});

// SetOutput location
grunt.registerTask("setOutput", "Set the output folder for the build.", function () {
  if (global.buildType === "tfs") {
    global.outputPath = MACHINE_PATH;
  }
  if (global.buildType === "local") {
    global.outputPath = LOCAL_PATH;
  }
  if (global.buildType === "release") {
    global.outputPath = RELEASE_PATH;
  }
  if (grunt.option("target")) {
    global.outputPath = grunt.option("target");
  }
  grunt.log.writeln("Output folder: " + global.outputPath);
});

grunt.registerTask("globalReadout", function () {
  grunt.log.writeln(global.outputPath);
});

So, I'm trying to then reference global.outputPath in a subsequent task, and running into errors.

If I call grunt test from the command line, it outputs the correct path no problem.

However, if I have a task like this: clean: { release: { src: global.outputPath } }

It throws the following error: Warning: Cannot call method 'indexOf' of undefined Use --force to continue.

Also, my constants in the setOutput task are set at the top of my Gruntfile.js

Any thoughts? Am I doing something wrong here?

SimplGy
  • 20,079
  • 15
  • 107
  • 144
ThePuzzleMaster
  • 920
  • 2
  • 10
  • 18
  • I'm thinking it might be related to the global.outputPath being set outside of grunt.initConfig({}), while, I'm trying to access the variable inside grunt.initConfig({}) – ThePuzzleMaster Mar 26 '13 at 21:19

3 Answers3

13

So, I was on the right path. The issue is that the module exports before those global variables get set, so they are all undefined in subsequent tasks defined within the initConfig() task.

The solution I came up with, although, there may be better, is to overwrite a grunt.option value.

I have an optional option for my task --target

working solution looks like this:

grunt.registerTask("setOutput", "Set the output folder for the build.", function () {
  if (global.buildType === "tfs") {
    global.outputPath = MACHINE_PATH;
  }
  if (global.buildType === "local") {
    global.outputPath = LOCAL_PATH;
  }
  if (global.buildType === "release") {
    global.outputPath = RELEASE_PATH;
  }
  if (grunt.option("target")) {
    global.outputPath = grunt.option("target");
  }

  grunt.option("target", global.outputPath);
  grunt.log.writeln("Output path: " + grunt.option("target"));
});

And the task defined in initConfig() looked like this:

clean: {
  build: {
    src: ["<%= grunt.option(\"target\") %>"]
  }
}

Feel free to chime in if you have a better solution. Otherwise, perhaps this may help someone else.

ThePuzzleMaster
  • 920
  • 2
  • 10
  • 18
  • 1
    I like the idea of setting the target directory of the build globally. The alternative is defining separate rules for each process just because the destination is different. (eg: `sass:dev`, `coffee:dev`, `sass:dist`, `coffee:dist`). It's a pain and not very DRY. Thanks for doing the leg work on this! – SimplGy Aug 23 '13 at 16:49
  • I'm confused by the bit where you're setting `global.Outputpath` equal to `grunt.option('target')` if it's defined, then setting `grunt.option('target')` to the value of `global.Outputpath`. What's your intention there? – SimplGy Aug 23 '13 at 17:06
  • Ahhh. Looking at it now, the better way would be to put a return on the first line of that function if the option("target") was set at run time. Otherwise, I'm setting global.Outputpath to grunt.option("target") so that if they pass in a target at run time, it overrides all the defaults. – ThePuzzleMaster Aug 24 '13 at 03:47
  • Oh, I understand now! You want folks to be able to specify it externally. Cool! – SimplGy Aug 24 '13 at 05:52
  • i know this is super old, but i was working on an old project & trying to use grunt.config to no avail. Your answer saved me...thanks! – gonzofish Jul 26 '16 at 12:42
4

I have a way to do this that allows you to specify the output path using values like --dev. So far it's working very well, I quite like it. Thought I'd share it, as someone else may like it, too.

    # Enum for target switching behavior
    TARGETS =
      dev: 'dev'
      dist: 'dist'

    # Configurable paths and globs
    buildConfig =
      dist: "dist"
      dev: '.devServer'
      timestamp: grunt.template.today('mm-dd_HHMM')

    grunt.initConfig
        cfg: buildConfig
        cssmin:
            crunch:
                options: report: 'min'
                files: "<%= grunt.option('target') %>/all-min.css": "/**/*.css"

    # Set the output path for built files.
    # Most tasks will key off this so it is a prerequisite
    setPath = ->
      if grunt.option 'dev'
        grunt.option 'target', buildConfig.dev
      else if grunt.option 'dist'
        grunt.option 'target', "#{buildConfig.dist}/#{buildConfig.timestamp}"
      else # Default path
        grunt.option 'target', buildConfig.dev
      grunt.log.writeln "Output path set to: `#{grunt.option 'target'}`"
      grunt.log.writeln "Possible targets:"
      grunt.log.writeln target for target of TARGETS

    setPath()

With this setup, you can run commands like:

grunt cssmin --dist #sent to dist target
grunt cssmin --dev #sent to dev target
grunt cssmin --dev #sent to default target (dev)
SimplGy
  • 20,079
  • 15
  • 107
  • 144
0

This is an older question, I just thought to throw in my 5 cents.

If you need config variable to be accessible from any task, just define it in your main (the one that you'll always load) config file like this:

module.exports = function(grunt)
{
    //
    // Common project configuration
    //
    var config = 
    {
        pkg: grunt.file.readJSON('package.json'),

        options: // for 'project'
        {
            dist:
            {
                outputPath: '<%= process.cwd() %>/lib',
            },
            dev:
            {
                outputPath: '<%= process.cwd() %>/build',
            },
        },
    }

    grunt.config.merge( config )
}

Then you can simply access value like this:

  • in config file(s)

... my_thingie: [ ends_up_here: '<%= options.dev.outputPath %>/bundle', ], ...

  • in tasks

// as raw value grunt.config.data.options.dist.outputPath // after (eventual) templates have been processed grunt.config('options.dist.outputPath')

I used key options here just to be in line with convention, but you can use anything as long as you remember not to register a task named 'options' or whatever you used for the key :)

nidalpres
  • 3,778
  • 1
  • 18
  • 12