Grunt任务以JSON中的文件名汇编JSON文件

时间:2018-07-26 22:44:18

标签: javascript json gruntjs

我有一个类似JSON文件:

{
    "someRandomStuff": "myRandomStuff",
    "includeNodesFromFiles": {
        "item1": "item1.json",
        "item2": "item2.json",
        "item3": "item3.json"
    }
}

现在我想用每个文件中的JSON内容替换item1,item2和item3,因此目标文件如下所示:

{
    "someRandomStuff": "myRandomStuff",
    "includeNodesFromFiles": {
        "item1": {"whatever": "...whatever was in item1.json"},
        "item2": {"whatever": "...whatever was in item2.json"},
        "item3": {"whatever": "...whatever was in item3.json"},
    }
}

或类似地对于数组:

{
    "someRandomStuff": "myRandomStuff",
    "includeNodesFromFiles": [
        "item1.json",
        "item2.json",
        "item3.json"
    ]
}

收件人:

{
    "someRandomStuff": "myRandomStuff",
    "includeNodesFromFiles": [
        {"whatever": "...whatever was in item1.json"},
        {"whatever": "...whatever was in item2.json"},
        {"whatever": "...whatever was in item3.json"}
    ]
}

我如何用Grunt做到这一点?到目前为止,我还没有找到可以立即完成的Grunt任务。

Grunt的新手,所以请忍受我。

3 个答案:

答案 0 :(得分:2)

简短的回答:这是一个非常自定义的要求,并且我不知道现有的grunt插件可以实现这一目标。


解决方案:

您需要创建自己的grunt插件来处理此类要求。以下步骤描述了如何实现此目的:

  1. 首先如下创建一个插件文件。让我们将文件命名为json-replace.js

    json-replace.js

    /**
     * Custom grunt plugin replaces JSON values (filepatha) with JSON file content.
     */
    module.exports = function(grunt) {
    
      'use strict';
    
      var path = require('path');
    
      /**
       * Custom grunt multi task to replace values in JSON.
       */
      grunt.registerMultiTask('jsonReplace', 'Replace values in JSON', function() {
    
        // Read the configuration values.
        var src = this.data.src;
        var dest = this.data.dest;
        var keyName = this.data.key;
    
        var baseDir = path.dirname(src);
    
        // Default options
        var opts = this.options({
          indent: 2
        });
    
        /**
         * Determines whether the passed value is an Array.
         * @param {*} value - A reference to the value to check.
         * @returns {Boolean} - true if the value is an Array, otherwise false.
         */
        function isArray(value) {
          return Array.isArray(value);
        }
    
        /**
         * Determines whether the passed value is an Object.
         * @param {*} value - A reference to the value to check.
         * @returns {Boolean} - true if the value is an Object, otherwise false.
         */
        function isObject(value) {
          return Object.prototype.toString.call(value) === '[object Object]';
        }
    
        /**
         * Reads a file's contents, parsing the data as JSON.
         * @param {String} srcPath - The filepath to the JSON file to parse.
         * @returns {Object}- The parsed JSON data.
         */
        function readJson(srcPath) {
          return grunt.file.readJSON(srcPath);
        }
    
        /**
         * Writes JSON data to a file.
         * @param {String} destPath - A filepath for where to save the file.
         * @param {Object|Array} data - Value to covert to JSON and saved to file.
         * @param {Number} [indent=2] - The no. of spaces to indent the JSON.
         */
        function writeJson(destPath, data, indent) {
          indent = (typeof indent !== 'undefined') ? indent : 2;
          grunt.file.write(destPath, JSON.stringify(data, null, indent));
          grunt.log.writeln('Saved \x1b[96m1\x1b[0m file');
        }
    
        /**
         * Checks whether a file exists and logs any missing files to console.
         * @param {String} filePath - The filepath to check for its existence.
         * @returns {Boolean} - true if the filepath exists, otherwise false.
         */
        function fileExists(filePath) {
          if (!grunt.file.exists(filePath)) {
            grunt.fail.warn('Unable to read \"' + filePath + '\"');
            return false;
          }
          return true;
        }
    
        /**
         * Checks whether type of value is a string and logs an error if not.
         * @param {*} value - The value to check
         * @returns {Boolean} - true if type of value is 'string', otherwise false.
         */
        function isString(value) {
          if (typeof value !== 'string') {
            grunt.fail.warn('Value type must be a string: found \"' + value + '\"');
            return false;
          }
          return true;
        }
    
        /**
         * Processes each Array item for a given key.
         * @param {Object} data - The parsed JSON data to process.
         * @param {String} keyName - Name of key whose Array values to process.
         * @param {String} baseDir - Base directory path of the source json file.
         */
        function processArrayItems(data, keyName, baseDir) {
          var replacement = [];
    
          data[keyName].forEach(function(item) {
            var fullPath = path.join(baseDir, item);
    
            if (isString(item) && fileExists(fullPath)) {
              replacement.push(readJson(fullPath));
            }
          });
          data[keyName] = replacement;
          writeJson(dest, data, opts.indent);
        }
    
        /**
         * Processes an Objects key/value pair for a given Object.
         * @param {Object} data - The parsed JSON data to process.
         * @param {String} keyName - Name of key whose property values to process.
         * @param {String} baseDir - Base directory path of the source json file.
         */
        function processObjectValues(data, keyName, baseDir) {
          var replacement = {};
    
          Object.keys(data[keyName]).forEach(function(key) {
            var accessor = data[keyName][key];
            var fullPath = path.join(baseDir, accessor);
    
            if (isString(accessor) && fileExists(fullPath)) {
              replacement[key] = readJson(fullPath);
            }
          });
    
          data[keyName] = replacement;
          writeJson(dest, data, opts.indent);
        }
    
        // Read the source JSON file
        var srcData = readJson(src);
    
        // Check if the `key` provided exists in source JSON.
        if (!srcData[keyName]) {
          grunt.fail.warn('Missing given key "' + keyName + '" in ' + src);
        }
    
        // Invoke the appropriate processing for key value.
        if (isArray(srcData[keyName])) {
          processArrayItems(srcData, keyName, baseDir);
        } else if (isObject(srcData[keyName])) {
          processObjectValues(srcData, keyName, baseDir);
        } else {
          grunt.fail.warn('Value for "' + keyName + '" must be object or array');
        }
    
      });
    };
    
  2. json-replace.js保存在项目根目录(即与custom-grunt-tasksGruntfile.js相同级别的名为package.json的文件夹中。例如:

    .
    ├── Gruntfile.js
    ├── custom-grunt-tasks    <---
    │   └── json-replace.js   <---
    ├── node_modules
    │   └── ...
    ├── package.json
    └── ...
    
  3. 将以下任务添加到您的Gruntfile.js

    Gruntfile.js

    module.exports = function(grunt) {
    
      grunt.loadTasks('custom-grunt-tasks');
    
      grunt.initConfig({
        jsonReplace: {    // <-- Task
          targetA: {      // <-- Target
            src: 'path/to/source.json',
            dest: 'path/to/output/file.json',
            key: 'includeNodesFromFiles'
          }
        }
        // ...
      });
    
      grunt.registerTask('default', ['jsonReplace']);
    }
    

    注释:(关于上述 Gruntfile.js 的配置)

    • 读取grunt.loadTasks('custom-grunt-tasks');的行从名为json-replace.js的目录中加载自定义插件(即custom-grunt-tasks)。

    • 名为jsonReplace的任务被添加到grunt.initConfig({...}),其中包含一个任意命名为targetA的{​​{3}}。

    • src属性的值应替换为指向您的源.json文件的有效文件路径。

    • 应将dest属性的值替换为保存新.json文件的文件路径。

    • key的值应替换为有效的键名。所提供密钥的名称应针对包含.json文件路径的对象或数组的密钥(例如,如您的两个示例中所述的includeNodesFromFiles


其他信息:

  1. json-replace.jsTarget,这基本上意味着您可以根据需要在jsonReplace任务中配置多个目标。例如:

    // ...
    jsonReplace: {    // <-- Task
      targetA: {      // <-- Target
        src: 'path/to/source.json',
        dest: 'path/to/output/file.json',
        key: 'includeNodesFromFiles'
      },
      targetB: {      // <-- Another Target
        src: 'path/to/another/source.json',
        dest: 'path/to/output/another/file.json',
        key: 'anotherKeyName'
      }
    }
    // ...
    

    如果要处理多个.json文件,多个目标可能很有用。

  2. json-replace.js期望key的值(例如includeNodesFromFiles)为以下任一值:

    • 具有嵌套键/值对的对象(如您的第一个示例所示),其中每个嵌套键都有一个现有.json文件的文件路径值。
    • 或者是一个数组(如您的第二个示例所示),其中数组的每个项目都是现有.json文件的文件路径值。

    • 注意:如果给定JSON key的结构与上述两个结构中的任何一个都匹配,则会向控制台记录错误。

  3. json-replace.js默认将生成的.json文件的内容缩进为两个空格。但是,如果要更改此设置,可以使用indent选项。例如,以下任务配置会将生成的.json文件缩进四个空格:

    // ...
    jsonReplace: {
      options: {
        indent: 4  // <-- Custom indent option
      },
      targetA: {
        src: 'path/to/source.json',
        dest: 'path/to/output/file.json',
        key: 'includeNodesFromFiles'
      }
    }
    // ... 
    

  

重要在源.json文件(例如item1.jsonitem2.json等)中定义文件路径值时,此解决方案希望它们是相对于源.json文件本身。

答案 1 :(得分:0)

这是一个非常简单的任务,只需加载文件并更改对象中的值(所有文件路径都与gruntfile.js相关):

    grunt.registerTask('mytask', 'My super task', () => {
        // file with json with file paths
        //{"someRandomStuff":"myRandomStuff","includeNodesFromFiles":{"item1":"item1.json","item2":"item2.json","item3":"item3.json"}}
        let main = JSON.parse(fs.readFileSync('myjson.json', 'utf8'));
        Object.keys(main.includeNodesFromFiles).forEach((key) => {
            main.includeNodesFromFiles[key] = JSON.parse(fs.readFileSync(main.includeNodesFromFiles[key], 'utf8'));
        });
        //... do some stuff
        grunt.log.writeln(JSON.stringify(main)); //{"someRandomStuff":"myRandomStuff","includeNodesFromFiles":{"item1":{},"item2":{},"item3":{}}}
});

答案 2 :(得分:0)

这是我想出的解决方案。如果文件名存在,它将用文件递归替换文件名。还接受要包含的文件的基本URL:

## </code to remove>