如何在Grunt任务中动态生成文件名列表?

7
我正在使用 load-grunt-configgrunt-prompt,并且我正在开发一个 init 任务,将一些 PHP 模板从一个文件夹复制到另一个文件夹。
目前,模板文件名是硬编码的,但我更希望 grunt 扫描正确的文件夹并动态提供文件名。
我尝试过使用 grunt.file.expand,但我无法让它工作。是否有可能扫描文件夹并以 grunt-prompt 要求的格式返回文件名数组(或对象,不确定应该称之为什么)?
// -------------------------------------
// 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
        }]
      }]
    }
  }
};

顺便提一下,我找到了这个答案:https://stackoverflow.com/a/22270703/1694077,它与这个问题有关。但它没有详细说明如何具体解决这个问题。此外,我需要比文件名数组更具体的语法:
[{
  name: '404.php'
}, {
  name: 'archive.php'
}]

为什么你不自己做呢? - Vinz243
@Vinz243,你是指手动提供文件列表吗?嗯,我已经在做了(正如你在代码中看到的那样),但这种方法相当脆弱,因为有人可能会意外删除或添加一个文件。这将导致提示和任何后续的 grunt 任务出现意外行为。 - user1694077
@Vinz243,你是什么意思?这已经是一个Grunt任务了。 - user1694077
如果您使用 fs.readdir - Vinz243
@Vinz243:我研究过这个问题(以及fs.readdirSync)。我只是认为Grunt本地函数会更好一些。但是,任何解决方案都可以吧。我只是不知道如何实现它以使其运行。 - user1694077
显示剩余2条评论
3个回答

12

基本原理

以下是一种使用 Grunt 文件匹配功能获取文件列表的方法。下面的代码将在名为 templates 的子目录中查找模板文件,只需将您的 php 文件放在那里,脚本就能够找到它。请注意,由于获取文件列表不属于使用 load-grunt-config 的问题,因此我省略了它。

关键是使用 grunt.file.expand 来获取文件列表。

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"]);
};

您可以使用比"*"更复杂的模式来进行匹配。例如,如果您将在其中有其他类型的文件不想列出,可以使用"*.php"。我还使用isFile作为filter选项以避免列出目录。并且我使用cwd将工作目录更改为templates 之前列出文件,这意味着返回的文件名不包含templates/。也可以这样做:
var templates = grunt.file.expand({filter: "isFile"}, ["templates/*"]);

获取包含templates/目录在文件名中的文件列表。

使用load-grunt-config

默认情况下,load-grunt-config需要一个package.json文件(因为它调用load-grunt-tasks)。这是我使用的方式:

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

Gruntfile.js变成了:

module.exports = function (grunt) {

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

然后在 grunt/prompt.js 中需要加入以下内容:

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
                }]
            }
        }
    };
};

看起来不错!但是这个解决方案有一个小问题,就是需要进行一些调整才能与load-grunt-config配合使用。因为当你将module.exports =行更改为module.exports = function (grunt)时,load-grunt-config将无法工作(该插件还省略了任务名称和initconfig行,并将任务分成单独的文件)。虽然可以向该插件注册一个单独的函数,但我无法使其正常工作。 - user1694077
1
我已经编辑了我的答案,添加了一个涵盖load-grunt-config的部分。 - Louis

2

这是一个简短的代码,用于列出目录中的文件:

var fs = require("fs")
var files = [];
var list = function (path) {
  fs.readdirSync(path).forEach(function (file) {
    if(fs.lstatSync(path + '/' +file).isDirectory())
      list(path + '/' +file);
    else
      files.push({name: file});
  });
}
list(YOUR_PATH)
console.log(files)

在你的例子中:
var fs = require("fs")
var files = [];
var list = function (path) {
  fs.readdirSync(path).forEach(function (file) {
    if(fs.lstatSync(path + '/' +file).isDirectory())
      list(path + '/' +file);
    else
      files.push({name: file});
  });
}
list(YOUR_PATH)
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: files
      }]
    }
  }
};

0

有一种使用不同的Grunt实用程序方法来执行此操作的另一种方法,而不是列在接受的答案中的方法。值得一提的是,它公开了“glob” npm包的选项对象,该包是Grunt内部使用的。

有关您的Grunt版本使用的glob版本的更多信息,请参见README.md(检查其package.json文件)。我最初查看了最新的glob,并对Grunt没有grunt.file.globSync()方法感到困惑。最终我意识到Grunt正在使用一个较早的glob版本,在此之后,{sync: true}从选项对象中删除,并且我一直在寻找的globSync()函数代替了它。

注意:grunt.file.glob()不像grunt.file.expand()那样接受src数组,但是您可以使用大括号来有效地编码数组:

var options = { ... };
var results = 
  grunt.file.glob(
     '{first/path/**/*,second/path/**/*}', 
     options
  );

这里是一个示例块,展示了我如何在jade处理中使用它。我记得某些原因下jade不能本地处理扩展块。选项:

  • 允许通配符匹配以点开头的文件
  • 对于非通配符全局模式路径节点,禁用不必要的stat调用
  • 禁用排序,因为我不关心输入顺序
  • 禁用去重,因为我知道我的模式只匹配每个文件一次
  • 如果没有文件匹配,则请求一个空数组

    var filesObj = { }; var configObj = { build: { options: { pretty: true, data: { titleStr: '这是来自数据的标题' } }, files: filesObj } };

    // 从配置中获取源和构建输出目录根目录 var srcPrefix = new RegExp('^' + grunt.config.get('sourceAssets')); var buildPrefix = grunt.config.get('buildAssets');

    // 每个glob匹配调用一次以填充文件对象。 function addJadeFile(srcPath) { // 通过规范化路径处理Windows上的glob输出。 srcPath = srcPath.replace(/\/g, '/'); // 提取公共路径后缀并更改文件扩展名 var relPath = srcPath.replace(srcPrefix, '..').replace(/.jade$/, '.html'); // 在config对象的files子对象中添加目标路径前缀和属性。 filesObj[buildPath + relPath] = srcPath; }

    grunt.file.glob( appConfig.source.client + '/**/*.jade', { sync: true, stat: false strict: true, dot: false, nonull: false, nodir: true, nosort: true, nounique: true } ).forEach(addJadeFile);

    return configObj;

附言:通常情况下,grunt.file.glob() 和 grunt.file.expand() 优于使用 fs 包中的遍历方法,因为 glob 会保留遍历缓存。

如果你正在寻找可能由先前的构建步骤创建但在其他任务首次遍历其创建位置并填充该缓存之前尚未创建的文件,则了解 glob 的遍历缓存也是很好的。我还没有遇到过这种情况,但在这种极端情况下,需要查找如何清除或忽略缓存时,请注意此问题。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接