【问题标题】:How to run a Grunt task after my Yeoman generator finishes installing?我的 Yeoman 生成器安装完成后如何运行 Grunt 任务?
【发布时间】:2013-09-17 04:24:45
【问题描述】:

我正在构建一个自定义 Yeoman 生成器,它安装了许多预处理语言编译器,例如 CoffeeScript、LESS 和 Jade。在我的生成器创建的 Gruntfile 中,我有一个编译所有内容的构建任务。但是,在该构建任务至少运行一次之前,已编译的 HTML、CSS 和 Javascript 文件不存在,如果我在新搭建脚手架后尝试运行 grunt watch/connect 服务器,这可能会令人困惑。

让我的生成器在安装结束时运行 Grunt 构建步骤的最佳方法是什么?已经用于调用this.installDependenciesend 事件似乎是执行此操作的正确位置,但我应该如何与 Grunt 通信?

【问题讨论】:

    标签: javascript gruntjs yeoman yeoman-generator


    【解决方案1】:

    如果您遵循堆栈,this.installDependencies 最终会向下运行到 https://github.com/yeoman/generator/blob/45258c0a48edfb917ecf915e842b091a26d17f3e/lib/actions/install.js#L36

    this.spawnCommand(installer, args, cb)
      .on('error', cb)
      .on('exit', this.emit.bind(this, installer + 'Install:end', paths))
      .on('exit', function (err) {
        if (err === 127) {
          this.log.error('Could not find ' + installer + '. Please install with ' +
                              '`npm install -g ' + installer + '`.');
        }
        cb(err);
      }.bind(this));
    

    进一步追究,this.spawnCommand 来自https://github.com/yeoman/generator/blob/master/lib/actions/spawn_command.js

    var spawn = require('child_process').spawn;
    var win32 = process.platform === 'win32';
    
    /**
     * Normalize a command across OS and spawn it.
     *
     * @param {String} command
     * @param {Array} args
     */
    
    module.exports = function spawnCommand(command, args) {
      var winCommand = win32 ? 'cmd' : command;
      var winArgs = win32 ? ['/c'].concat(command, args) : args;
    
      return spawn(winCommand, winArgs, { stdio: 'inherit' });
    };
    

    换句话说,在您的生成器代码中,您可以随时调用this.spawnCommand,并将您希望终端运行的参数传递给它。如this.spawnCommand('grunt', ['build'])

    那么下一个问题是你把它放在哪里?线性思考,你只能相信grunt build 在你的所有依赖项都安装好后才能工作。

    来自https://github.com/yeoman/generator/blob/45258c0a48edfb917ecf915e842b091a26d17f3e/lib/actions/install.js#L67-69this.installDependencies 接受回调,因此您的代码可能如下所示:

    this.on('end', function () {
      this.installDependencies({
        skipInstall: this.options['skip-install'],
        callback: function () {
          this.spawnCommand('grunt', ['build']);
        }.bind(this) // bind the callback to the parent scope
      });
    });
    

    试一试!如果一切顺利,您应该在新的this.spawnCommand 调用之上添加一些错误处理以确保安全。

    【讨论】:

    • 值得一提的是,在回调函数this内部失去了作用域。我最初创建了一个变量以在正确的范围内缓存this,并用它来调用spawnCommand。但是,我决定改用bind(),因为它会更干净。我已经编辑了答案以反映这一点。
    • 自动安装依赖失败怎么办?
    【解决方案2】:

    我使用了 Stephen 的出色答案,通过自定义事件以以下方式实现以保持整洁。

    MyGenerator = module.exports = function MyGenerator(args, options, config) {
    
        this.on('end', function () {
            this.installDependencies({
                skipInstall: options['skip-install'],
                callback: function() {
                    // Emit a new event - dependencies installed
                    this.emit('dependenciesInstalled');
                }.bind(this)
            });
        });
    
        // Now you can bind to the dependencies installed event
        this.on('dependenciesInstalled', function() {
            this.spawnCommand('grunt', ['build']);
        });
    
    };
    

    【讨论】:

    • 我也在考虑这一点,但现在我更想将它改装到我的发电机中。它使相关的安装步骤始终保持在同一缩进水平上,并提供更清晰的划分。
    • 不需要这个自定义回调,因为 Yeoman 生成器有自己的事件:github.com/yeoman/yeoman-app/blob/main/docs/events.md。假设你使用 NPM,你可以将 "this.on('dependenciesInstalled', ...)" 替换为 "this.on('npmInstall:end', ...)"
    【解决方案3】:

    这个问题已经有点老了,但如果有人错过了,我仍然想补充一下。安装后流程现在更容易实施。查看run loop 并使用end 方法,您可以在其中运行所有安装后的内容。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-06-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-05-24
      • 2015-04-15
      • 1970-01-01
      相关资源
      最近更新 更多