简体   繁体   中英

Grunt: Access Sequence/List Data from External YAML File

I am trying to access values from an external YAML file in my Gruntfile using:

external = grunt.file.readYAML('_config.yml');

The _config.yml file has the following example data:

computer:
  parts:
    - name: brand1
      type: cpu
    - name: brand2
      type: gpu
    - name: brand3
      type: hd

I've been trying to access the multi-level YAML data using <%= %> grunt templating to get the different name and type values.

module.exports = {
  concat: {
    src: ['htdocs/<%= external.computer.parts['type'] %>/<%= external.computer.parts['name'] %>/*.js'],
    dest: 'htdocs/output.js'
  }
};

The main goal has been to concat files from different directories this way into one, but I can't seem to access data from the _config.yml file beyond external.computer.parts . FYI, the structure of the _config.yml file has to remained unchanged.

How do you access a sequence/list with different properties this way?

Below are a couple of solutions to consider. However, firstly let's understand what using grunt.file.readYAML() to parse your _config.yml file does. It essentially produces the following object:

{
  computer: {
    parts: [
      {
        name: 'brand1',
        type: 'cpu'
      },
      {
        name: 'brand2',
        type: 'gpu'
      },
      {
        name: 'brand3',
        type: 'hd'
      }
    ]
  }
}

Note how the value of parts is an array of objects.


Solution 1:

Given that you want to utilize grunt templates (ie <%= %> ) to obtain the different name and type values, consider configuring your concat task in your Gruntfile.js as follows:

Gruntfile.js

module.exports = function (grunt) {

  grunt.loadNpmTasks('grunt-contrib-concat');

  grunt.initConfig({
    external: grunt.file.readYAML('_config.yml'),

    concat: {
      dist: {
        options: {
          // ...
        },
        src: [
          'htdocs/<%= external.computer.parts[0].type %>/<%= external.computer.parts[0].name %>/*.js',
          'htdocs/<%= external.computer.parts[1].type %>/<%= external.computer.parts[1].name %>/*.js',
          'htdocs/<%= external.computer.parts[2].type %>/<%= external.computer.parts[2].name %>/*.js'
        ],
        dest: 'htdocs/output.js'
      }
    }
    // ...
  });

  grunt.registerTask('default', [ 'concat' ]);

};

Notes:

  1. The value of the external property of the object passed into thegrunt.initConfig method is essentially the aforementioned object, ie it's the result of utiizing grunt.file.readYAML() to parse your _config.yml .

  2. The value of the src property of the dist target , (which is associated with the concat task ), is an array. Each item of this array is where we utilize the <%... %> notation to reference the parts from your .yml file.

    Note how we reference each object in the external.computer.parts array by it's index, ie [0] , [1] , [2]

     'htdocs/<%= external.computer.parts[0].type %>/<%= external.computer.parts[0].name %>/*.js' ^ ^

Solution 2:

Another way to achieve your requirement is to not utilize grunt templates, ie <%... %> , at all. Consider the following solution:

Gruntfile.js

module.exports = function (grunt) {

  grunt.loadNpmTasks('grunt-contrib-concat');

  var external = grunt.file.readYAML('_config.yml');

  grunt.initConfig({
    concat: {
      dist: {
        options: {
          // ...
        },
        src: external.computer.parts.map(function(part) {
          return 'htdocs/' + part.type + '/' + part.name + '/*.js'
        }),
        dest: 'htdocs/output.js'
      }
    }
    // ...
  });

  grunt.registerTask('default', [ 'concat' ]);

};

Notes:

  1. This time we assign the result of parsing your _config.yml file to a variable named external :

     var external = grunt.file.readYAML('_config.yml');
  2. The value of the src property is computed by utilizing the map() method. Here we create a new array of glob patterns .

     src: external.computer.parts.map(function(part) { return 'htdocs/' + part.type + '/' + part.name + '/*.js' }),

Benefits:

One of the key benefits that Solution 2 has over Solution 1 is:

If we need to add a new part ( name and tyoe ) to _config.yml . For example:

computer:
  parts:
    - name: brand1
      type: cpu
    - name: brand2
      type: gpu
    - name: brand3
      type: hd
    - name: brand4      <-------
      type: foo         <-------

With Solution 1 we will need to add it to the src configuration in the Gruntfile.js . For example:

src: [
  'htdocs/<%= external.computer.parts[0].type %>/<%= external.computer.parts[0].name %>/*.js',
  'htdocs/<%= external.computer.parts[1].type %>/<%= external.computer.parts[1].name %>/*.js',
  'htdocs/<%= external.computer.parts[2].type %>/<%= external.computer.parts[2].name %>/*.js',

   // Newly added...
  'htdocs/<%= external.computer.parts[3].type %>/<%= external.computer.parts[3].name %>/*.js'
],

With Solution 2 we don't have to change the src configuration in the Gruntfile.js at all.


Edit:

If you're using a fairly recent version of node.js then you can also refactor Solution 2 as follows:

Gruntfile.js

module.exports = function (grunt) {

  grunt.loadNpmTasks('grunt-contrib-concat');

  const { computer: { parts } } = grunt.file.readYAML('_config.yml');

  grunt.initConfig({
    concat: {
      dist: {
        options: {
          // ...
        },
        src: parts.map(({ type, name }) => `htdocs/${type}/${name}/*.js`),
        dest: 'htdocs/output.js'
      }
    }
    // ...
  });

  grunt.registerTask('default', [ 'concat' ]);

};

Please ignore StackOverflow's inability to syntax highlight the above example correctly.

Notes:

This refactored version utilizes some ES6 features as follows:

  • Object destructuring is used to unpack the parts property/value from the parsed _config.yml , into a parts variable:

     var { computer: { parts } } = grunt.file.readYAML('_config.yml');
  • The value of the src property is computed using an Arrow function with the map() method, and Template Literals are used instead of the plus operator ( + ) for string concatenation.

     src: parts.map(({ type, name }) => `htdocs/${type}/${name}/*.js`),

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM