问题
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?
回答1:
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 (i.e. <%= %>
) 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:
The value of the
external
property of the object passed into the grunt.initConfig method is essentially the aforementioned object, i.e. it's the result of utiizinggrunt.file.readYAML()
to parse your_config.yml
.The value of the
src
property of thedist
target, (which is associated with theconcat
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, i.e.[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, i.e. <% ... %>
, 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:
This time we assign the result of parsing your
_config.yml
file to a variable namedexternal
:var external = grunt.file.readYAML('_config.yml');
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 aparts
variable:var { computer: { parts } } = grunt.file.readYAML('_config.yml');
The value of the
src
property is computed using an Arrow function with themap()
method, and Template Literals are used instead of the plus operator (+
) for string concatenation.src: parts.map(({ type, name }) => `htdocs/${type}/${name}/*.js`),
来源:https://stackoverflow.com/questions/61400150/grunt-access-sequence-list-data-from-external-yaml-file