Grunt:从外部 YAML 文件访问 Sequence/List 数据

Grunt: Access Sequence/List Data from External YAML File

我正在尝试使用以下方法从我的 Gruntfile 中的外部 YAML 文件访问值:

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

_config.yml 文件具有以下示例数据:

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

我一直在尝试使用 <%= %> grunt 模板访问多级 YAML 数据以获取不同的名称和类型值。

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

主要目标是通过这种方式将不同目录中的文件合并为一个目录,但我似乎无法访问 _config.yml 文件中 external.computer.parts 之外的数据。仅供参考,_config.yml 文件的结构必须保持不变。

如何通过这种方式访问​​具有不同属性的 sequence/list?

下面是一些可供考虑的解决方案。但是,首先让我们了解使用 grunt.file.readYAML() 来解析 _config.yml 文件的作用。它本质上产生以下对象:

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

注意 parts 的值是一个对象数组。


解决方案一:

鉴于您希望利用 grunt templates(即 <%= %>)获得不同的 nametype 值,请考虑配置您的 concat 任务在你的 Gruntfile.js 中如下:

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' ]);

};

备注:

  1. 传入grunt.initConfig方法的对象的external属性的值本质上就是上述对象,即利用grunt.file.readYAML() 来解析你的 _config.yml.

  2. src属性disttarget, (which is associated with the concat task)的值是一个数组。该数组的每一项都是我们使用 <% ... %> 符号来引用您的 .yml 文件中的部分的地方。

    请注意我们如何通过索引引用 external.computer.parts 数组中的每个对象,即 [0][1][2]

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

方案二:

实现您的要求的另一种方法是完全不使用 grunt 模板,即 <% ... %>。考虑以下解决方案:

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' ]);

};

备注:

  1. 这次我们将解析您的 _config.yml 文件的结果分配给一个名为 external:

    的变量
    var external = grunt.file.readYAML('_config.yml');
    
  2. src属性的值是利用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'
    }),
    

好处:

解决方案 2 优于 解决方案 1 的主要优势之一是:

如果我们需要向 _config.yml 添加新部分(nametyoe)。例如:

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

对于解决方案 1,我们需要将其添加到 Gruntfile.js 中的 src 配置中。例如:

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'
],

使用解决方案 2 我们不必更改 Gruntfile.js 中的 src 配置全部.


编辑:

如果您使用的是 node.js 的较新版本,那么您还可以重构 解决方案 2,如下所示:

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' ]);

};

请忽略 Whosebug 无法正确突出显示上述示例的语法。

备注:

此重构版本利用了一些 ES6 功能,如下所示:

  • Object destructuring用于将解析后的_config.ymlpartsproperty/value解压成parts变量:

    var { computer: { parts } } = grunt.file.readYAML('_config.yml');
    
  • src 属性 的值是使用 Arrow function with the map() method, and Template Literals 计算的,而不是用于字符串连接的加号运算符 (+) .

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