How can I dynamically generate a list of filenames for use in a task with Grunt?

gruntjs, javascript, node.js

Solution

Basic Principle

Here's a way to do it that uses Grunt's file matching capabilities to get a list of files. The following code will seek templates in a subdirectory named `templates`. You just need to put your `php` files there and the script will find it. Note I've omitted the use of `load-grunt-config` since it is not a factor in the specific problem of getting a list of files.

The key is to use `grunt.file.expand` to get the files.

module.exports = function (grunt) {

    // List all files in the templates directory.
    var templates = grunt.file.expand({filter: "isFile", cwd: "templates"},
                                      ["*"]);

    // Make actual choices out of them that grunt-prompt can use.
    var choices = templates.map(function (t) {
        return { name: t, checked: false};
    });

    grunt.initConfig({
        prompt: {
            init: {
                options: {
                    questions: [{
                        // Set the authors name
                        config: 'init.author.name',
                        type: 'input',
                        message: 'What is your name?'
                    }, {
                        // Set the name of the project
                        config: 'init.project.name',
                        type: 'input',
                        message: 'What is the name of your project?'
                    }, {
                        // Select templates to be used
                        config: 'init.php.templates',
                        type: 'checkbox',
                        message: 'Which templates do you want to use?',
                        choices: choices
                    }]
                }
            }
        }
    });

    grunt.task.loadNpmTasks("grunt-prompt");
    grunt.registerTask("default", ["prompt"]);
};

You could use something more sophisticated than `"*"` as a pattern. For instance, if you are going to have other types of files there that you don't want to list `"*.php"` would be indicated. I also use `isFile` for the `filter` option to avoid listing directories. And I use `cwd` to change the working directory to `templates` before listing the files, which means the file names returned do not include `templates/` in their name. It would also be possible to do this instead:

var templates = grunt.file.expand({filter: "isFile"}, ["templates/*"]);

and get a list of files that include the `templates/` directory in their name.

With `load-grunt-config`

By default, `load-grunt-config` wants a `package.json` file (because it calls `load-grunt-tasks`). This is what I've used:

{
  "dependencies": {
    "load-grunt-config": "^0.8.0",
    "grunt-prompt": "^1.1.0",
    "grunt": "^0.4.4"
  }
}

The `Gruntfile.js` becomes:

module.exports = function (grunt) {

    grunt.registerTask("default", ["prompt"]);
    require('load-grunt-config')(grunt);
};

And then in `grunt/prompt.js` you need this:

module.exports = function(grunt) {
    // List all files in the templates directory.
    var templates = grunt.file.expand({filter: "isFile", cwd: "templates"},
                                      ["*"]);

    // Make actual choices out of them that grunt-prompt can use.
    var choices = templates.map(function (t) {
        return { name: t, checked: false};
    });

    return {
        init: {
            options: {
                questions: [{
                    // Set the authors name
                    config: 'init.author.name',
                    type: 'input',
                    message: 'What is your name?'
                }, {
                    // Set the name of the project
                    config: 'init.project.name',
                    type: 'input',
                    message: 'What is the name of your project?'
                }, {
                    // Select templates to be used
                    config: 'init.php.templates',
                    type: 'checkbox',
                    message: 'Which templates do you want to use?',
                    choices: choices
                }]
            }
        }
    };
};

Problem

I'm using load-grunt-config and grunt-prompt, and I'm developing an init task, which copies some php templates between two folders. Right now the template filenames are hardcoded, but I'd rather have grunt scan the right folder and provide the filenames dynamically. I've tried using grunt.file.expand, but I'm unable to get it to work. Is it possible to scan a folder and return an array (or object, not sure what you'd call it) of filenames in the format that grunt-prompt expects? ``` // ------------------------------------- // Grunt prompt // ------------------------------------- module.exports = { // ----- Initialization prompt ----- // init: { options: { questions: [{ // Set the authors name config: 'init.author.name', type: 'input', message: 'What is your name?' }, { // Set the name of the project config: 'init.project.name', type: 'input', message: 'What is the name of your project?' }, { // Select templates to be used config: 'init.php.templates', type: 'checkbox', message: 'Which templates do you want to use?', choices: [{ name: '404.php', checked: false }, { name: 'archive.php', checked: false }, { name: 'comments.php', checked: false }] }] } } }; ``` By the way, I have found this answer: https://stackoverflow.com/a/22270703/1694077, which relates to the problem. But it does not go into detail about how one would specifically address this problem. Also, I need more specific syntax than just an array of filenames: ``` [{ name: '404.php' }, { name: 'archive.php' }] ```

Original source