webpack构建后运行命令

86

我希望以 --watch 模式运行webpack,并在每次构建后运行一个shell命令,将一个文件夹同步到另一个文件夹。

我找到了这个插件,它可以在每次构建后触发一个事件。这很有效,但缺失的一块是如何从Javascript中触发一个shell命令(进行同步)。非常感谢关于如何实现这一目标的任何指针。

8个回答

168

Webpack 4

截至今天(2018年4月11日),我尝试过的大多数插件都使用已弃用的API,导致出现此警告:

DeprecationWarning: Tapable.plugin is deprecated. Use new API on `.hooks` instead

我很高兴发现您可以轻松地编写一个即席的webpack插件(文档)。

在您的webpack.config.js文件中:

const exec = require('child_process').exec;

module.exports = {

  // ... other config here ...

  plugins: [

    // ... other plugins here ...

    {
      apply: (compiler) => {
        compiler.hooks.afterEmit.tap('AfterEmitPlugin', (compilation) => {
          exec('<path to your post-build script here>', (err, stdout, stderr) => {
            if (stdout) process.stdout.write(stdout);
            if (stderr) process.stderr.write(stderr);
          });
        });
      }
    }
  ]
};

如果您更愿意使用spawn从脚本中获取实时或“活动”数据,则可以按照以下示例使用基本用法:

const spawn = require('child_process').spawn;

const child = spawn('<your script here>');
child.stdout.on('data', function (data) {
    process.stdout.write(data);
});
child.stderr.on('data', function (data) {
    process.stderr.write(data);
});

2
太好了,它也适用于手表构建,这似乎不是Yair Tavor的解决方案的情况。 - robbash
9
提醒一下,如果你在寻找钩子的 before 版本,可以尝试使用 beforeCompile。关于可用钩子的文档可以在 这里 找到。 - Gus
你确定 "<path to your post-build script here>" 是正确的吗?文档中说应该是要运行的命令:https://nodejs.org/api/child_process.html#child_process_child_process_exec_command_options_callback。当我在那里放置路径时,它会抱怨权限问题:"/bin/sh: tasks/postbuild.sh: Permission denied"。 - Andrey
1
@Andrey,您可以提供要运行的命令或具有执行权限设置的脚本文件路径。为了解决您的问题,请尝试chmod +x /path/to/script - jchook
是的,但如果webpack有子编译,它将无法工作。 - Limbo

81

我也需要这样一件事情,所以我编写了一个超级简单的插件,在每次构建之前和之后执行shell命令。

'use strict';

var exec = require('child_process').exec;

function puts(error, stdout, stderr) {
    console.log(stdout);
}

function WebpackShellPlugin(options) {
  var defaultOptions = {
    onBuildStart: [],
    onBuildEnd: []
  };

  this.options = Object.assign(defaultOptions, options);
}

WebpackShellPlugin.prototype.apply = function(compiler) {
  const options = this.options;

  compiler.plugin("compilation", compilation => {
    if(options.onBuildStart.length){
        console.log("Executing pre-build scripts");
        options.onBuildStart.forEach(script => exec(script, puts));
    }
  });

  compiler.plugin("emit", (compilation, callback) => {
    if(options.onBuildEnd.length){
        console.log("Executing post-build scripts");
        options.onBuildEnd.forEach(script => exec(script, puts));
    }
    callback();
  });
};

module.exports = WebpackShellPlugin;

然后在您的webpack配置文件中:

plugins: [
    new WebpackShellPlugin({ 
         onBuildStart: ['echo "hello world"'], 
         onBuildEnd: ['echo "goodbye world"'] 
    })
]

这很基础,不支持异步脚本。但它可以工作。随意根据您的需要进行修改。

请考虑遵循 MIT 许可证的代码。

需要 Node 4.x 或更高版本才能运行,因为我在这里使用了一些 ES6 特性。


16
如果您需要访问捆绑文件,最好使用 after-emit,因为 emit 是在 webpack 开始发出文件时触发的。 - Matt Zeunert
你能创建一个非 E6 版本吗? - Paul Preibisch
2
它已经被打包在以下网址:https://www.npmjs.com/package/webpack-shell-plugin - Maxime R.
我将其简化为仅传递单个js函数而不是字符串命令给 exec。 运行良好。 - jinglesthula

7

使用webpack-shell-plugin

使用方法:

const WebpackShellPlugin = require('webpack-shell-plugin');


    module.exports = {
      ...
      ...
      plugins: [
        new WebpackShellPlugin({onBuildStart:['echo "Webpack Start"'], onBuildEnd:['echo "Webpack End"']})
      ],
      ...
    }

注:这实际上是由@Yair Tavor在他的答案中建议的插件。 - Olga
2
@Olga 简化解决方案到必要的程度是一种实际的贡献,值得赞扬。Krishna,你可以通过解释与Yair Tavor答案的区别来完善你的答案。据我所知,Yair Tavor的解决方案已经打包在webpack-shell-plugin中,而你提出的是如何使用它。值得一提的是,这有助于其他人理解情况。 - Titou

7
基本上,您可以在整个编译过程的各个阶段(包括生成资源阶段等)钩入编译器,并按照自己的意愿运行自己的脚本或代码。我喜欢这样做 -
class CustomPlugin {
  constructor(name, command, stage = 'afterEmit') {
    this.name = name;
    this.command = command;
    this.stage = stage;
  }

  static execHandler(err, stdout, stderr) {
    if (stdout) process.stdout.write(stdout);
    if (stderr) process.stderr.write(stderr);
  }

  apply(compiler) {
    compiler.hooks[this.stage].tap(this.name, () => {
      exec(this.command, CustomPlugin.execHandler);
    });
  }
}

然后像这样使用它。
new CustomPlugin('RunTest', 'jest', 'beforeRun'),

我可以看到自己经常使用这个模式。 - Cody

5

webpack-shell-plugin-next插件

有一个 webpack-shell-plugin-next 插件:

使用插件

onAfterDone 插件 API:

onAfterDone: 配置对象,用于在完成后执行脚本。

可以使用它来实现期望的与监视相关的行为(另外,请参见下面的重要说明):

我想在--watch模式下运行webpack,并在每次构建完成后运行一个shell命令,将一个文件夹与另一个文件夹同步。

重要说明: onAfterDone 插件 API 也会影响正常的构建模式(即没有 --watch 选项的 webpack 命令)。

这里有一个相关的 GitHub issue: onDoneWatch scripts executing before bundle written · Issue #16 · s00d/webpack-shell-plugin-next

示例

刚尝试使用该插件:效果很好。

devDependencies(来自 package.json

"devDependencies": {
  "webpack": "5.3.2",
  "webpack-cli": "4.1.0",
  "webpack-shell-plugin-next": "2.0.4"
}

watch npm运行脚本(来自package.json

"scripts": {
  "watch": "webpack --config webpack.config.js --watch"
}

Webpack 配置文件 (webpack.config.js)

const WebpackShellPluginNext = require('webpack-shell-plugin-next');

module.exports = {
    plugins: [
        new WebpackShellPluginNext({
            onAfterDone: {
                scripts: ['echo "It works!"'],
                blocking: true,
                parallel: false
            }
        })
    ]
};

在观察模式下运行Webpack的命令行

npm run watch

1
非常适用于webpack 4。只需记住使用不同版本的webpack 4: webpack4 => "webpack-shell-plugin-next": "1.2.0" webpack5 => "webpack-shell-plugin-next": "2.2.2" - strix25

4
你可以使用内置的child_process模块来轻松运行任何Shell命令。此外,你还可以尝试使用一些针对Node.js的Shell库,例如Shell.js。它包装了大多数默认的Shell以便更加方便地使用。

2
谢谢,这就是我缺少的环节。不知怎么回事,我一直在打转,完全错过了这个选项。 - Monokai
20
对于那些仅仅是使用webpack的用户来说,一个示例会很有帮助。 - Titou

2

1
我在使用webpack-shell-pluginwebpack-shell-plugin-next时遇到了一些问题:尽管我使用了onDoneWatch,但脚本在新文件被发出之前就已经执行了。
这时我找到了hook-shell-script-webpack-plugin,非常好用。

已经有很多答案解决了这个问题,请添加一些额外的信息,说明你的答案有何不同。 - Fabian S.
1
其他答案指向了webpack-shell-plugin或使用webpack钩子的自定义实现。我无法让shell插件正常工作,而制作自定义插件似乎过于复杂。我发现了这个其他人没有提到的解决方案。希望它能有所帮助。 - Daniele Mazzotta

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