1

I started using grunt in my build process. In package.json I include variables to be replaced this way:

{
  "name": "myApp",
  "variableToReplace":{
    "var1":"var1_replacement",
    "var2":"var2_replacement"
  },
  ...
}

However, I do not know how to use this variableToReplace in Gruntfile.js so that it will automatically look up all properties then replace them with the corresponding values. Is it possible using Grunt?

'string-replace': {
  dist: {
    files: {
      //A list of files
    },
    options: {
      //What to put here?
    }
  }
}
RobC
  • 22,977
  • 20
  • 73
  • 80
Loredra L
  • 1,485
  • 2
  • 16
  • 32

1 Answers1

3

Revised Answer (after comment)

... Is there anyway to loop through the key-value pair of variableToReplace and replace, for example, var1 with var1_replacement.

Yes, this can be achieved by utilizing a Custom Task. The custom task should perform the following:

  1. Read the variableToReplace object from package.json.
  2. Dynamically build the options.replacements array.
  3. Configure/set the option.replacements array using grunt.config.set
  4. Then finally run the task using grunt.task.run.

The following Gruntfile.js demonstrates this solution:

Gruntfile.js

module.exports = function(grunt) {

  grunt.loadNpmTasks('grunt-string-replace');

  grunt.initConfig({

    'string-replace': {
      dist: {
        files: [{
          expand: true,
          cwd: 'src/',
          src: '**/*.css',
          dest: 'dist/'
        }],
        options: {
          replacements: [] // <-- Intentionally empty and will be dynamically
                           //     configured via `configAndRunStringReplace`.
        }
      }
    }
  });

  /**
   *  Helper task to dynamically configure the Array of Objects for the
   * `options.replacements` property in the `dist` target of the `string-replace`
   *  task. Each property name of the `variableToReplace` Object (found in
   * `package.json`) is set as the search string, and it's respective value
   *  is set as the replacement value.
   */
  grunt.registerTask('configAndRunStringReplace', function () {

    // 1. Read the `variableToReplace` object from `package.json`.
    var replacements = grunt.file.readJSON('package.json').variableToReplace,
      config = [];

    // 2. Dynamically build the `options.replacements` array.
    for (key in replacements) {
      config.push({
        pattern: new RegExp(key, 'g'),
        replacement: replacements[key]
      });
    }

    // 3. Configure the option.replacements values.
    grunt.config.set('string-replace.dist.options.replacements', config);

    // 4. Run the task.
    grunt.task.run('string-replace:dist');
  });

  // Note: In the `default` Task we add the `configAndRunStringReplace`
  // task to the taskList array instead of `string-replace`.
  grunt.registerTask('default', ['configAndRunStringReplace']);
}

Important note regarding Regular Expressions:

The docs for grunt-string-replace states:

If the pattern is a string, only the first occurrence will be replaced...

To ensure that multiple instances of the search/find string are matched and replaced the configAndRunStringReplace custom task utilizes a Regular Expression with the global g flag.

So, any instances of the following regex special characters:

\ ^ $ * + ? . ( ) | { } [ ]

which may be used in a search word (i.e. as a key/property name in your package.json) will need to be escaped. The typical way to escape these characters in a Regex is to add a backslash \ before the character (E.g. \? or \+ etc..). However, because you're using key/property names in JSON to define your search word. You'll need to double escape any of the previously mentioned characters to ensure your JSON remains valid. For Example:

Lets say you wanted to replace question marks (?) with exclamation marks (!). Instead of defining those rules in package.json like this:

...
"variableToReplace":{
  "?": "!"
},
...

Or like this:

...
"variableToReplace":{
  "\?": "!"
},
...

You'll need to do this (i.e. use double escapes \\):

...
"variableToReplace":{
  "\\?": "!"
},
...

Original Answer

The following contrived example shows how this can be achieved:

Gruntfile.js

module.exports = function(grunt) {

  grunt.loadNpmTasks('grunt-string-replace');

  grunt.initConfig({
    pkg: grunt.file.readJSON('package.json'), // 1. Read the package.json file.

    'string-replace': {
      dist: {
        files: {
          'dist/': ['src/css/**/*.css'],
        },
        options: {
          replacements: [{
            // 2. Use grunt templates to reference the properties in package.json
            pattern: '<%= pkg.variableToReplace.var1 %>',
            replacement: '<%= pkg.variableToReplace.var2 %>',
          }]
        }
      }
    }
  });

  grunt.registerTask('default', ['string-replace']);

}

Notes

  1. Add pkg: grunt.file.readJSON('package.json') to the grunt.initConfig() section of your Gruntfile.js. This will parse the JSON data stored in package.json.

  2. Use grunt templates to access the properties of package.json. The standard JavaScript dot notation is used to access the property values, (e.g. pkg.variableToReplace.var1), and is wrapped in a leading <%= and trailing %>

  3. Using the contrived Gruntfile.js configuration above with your the package.json data (as described in your question). The following would occur:

    • Any instances of the string var1_replacement found in any of the .css files stored in the src/css/ directory will be replaced with the string var2_replacement.

    • The resultant files will be saved to the dist/ directory.

Community
  • 1
  • 1
RobC
  • 22,977
  • 20
  • 73
  • 80
  • But by your code, var1 will be replaced by var2. Is there anyway to loop through the key-value pair of variableToReplace and replace, for example, var1 with var1_replacement – Loredra L Mar 19 '18 at 18:49
  • Sorry for the late confirm update. It was really excellent answer – Loredra L Mar 22 '18 at 14:17