【问题标题】:How can I dynamically generate a list of filenames for use in a task with Grunt?如何使用 Grunt 动态生成用于任务的文件名列表?
【发布时间】:2015-12-24 01:52:44
【问题描述】:

我正在使用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;你的意思是手动提供文件列表?好吧,我已经在这样做了(正如您在代码中看到的那样),但它相当脆弱,因为有人可能会不小心删除文件或添加文件。这会导致提示和任何后续的 grunt 任务出现意外行为。
  • @Vinz243;你是什​​么意思?这已经是一项艰巨的任务了。
  • 供您扫描。或者我没有真正理解你的问题。
  • @Vinz243;啊对。好吧,据我所知,没有一个繁重的任务可以做到这一点。我认为有一个内置函数可以执行此操作(grunt.file.expand)。但我不知道在这种情况下如何使用它..

标签: javascript node.js gruntjs


【解决方案1】:

基本原理

这是一种使用 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 before 列出文件,这意味着返回的文件名 在其名称中包含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 将不起作用(该插件还省略了 task-name 和 initconfig 行并将任务分隔为单独的文件)。可以使用此插件注册一个单独的功能,但我无法做到这一点。
  • 我已经编辑了我的答案以添加一个涵盖 load-grunt-config 的部分。
【解决方案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
      }]
    }
  }
};

【讨论】:

    【解决方案3】:

    还有另一种方法可以使用不同于已接受答案中列出的 Grunt 实用程序方法。值得一提的是,它公开了 Grunt 内部使用的“glob”npm 包的选项对象。

    有关您的 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
      );
    

    这是一个示例块,展示了我如何在玉石加工中使用它,我似乎记得由于某种原因,它本身并不处理扩展块。选项:

    • 允许通配符匹配以点为前缀的文件
    • 为非通配符 glob 模式路径节点禁用不必要的统计调用
    • 禁用排序,因为我不关心输入顺序
    • 禁用重复数据删除,因为我知道我的模式只匹配每个文件一次
    • 如果没有文件匹配,则请求一个空数组而不是一个数组

      var filesObj = { }; var configObj = { 建造: { 选项: { 漂亮:真的, 数据: { titleStr: '这是来自数据的标题' } }, 文件:filesObj } };

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

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

      grunt.file.glob( appConfig.source.client + '/**/*.jade', {同步:真,统计:假 严格:真,点:假, nonull:假,nodir:真, nosort: true, nounique: true } ).forEach(addJadeFile);

      返回配置对象;

    附: grunt.file.glob() 和 grunt.file.expand() 通常都优于 fs 包中的遍历方法,因为 glob 保持一个遍历缓存。

    如果您正在查找可能已由上一个构建步骤创建的文件,但在其他任务首先遍历其创建位置和填充该缓存。我还没有遇到过这种情况,但请注意它,以防您需要了解如何在这种极端情况下清除或忽略缓存。

    【讨论】:

      猜你喜欢
      • 2014-06-17
      • 2014-04-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-12-10
      相关资源
      最近更新 更多