简体   繁体   中英

Using webpack in Visual Studio's pre-build

I have two commands:

npm run build - calls webpack to compile all my .js

npm run dev - calls webpack -w , compiles all my .js and stays in watch mode, looking for changes.

I want to integrate it with Visual Studio's build, so I went into Properties -> Build Events -> Pre-build

if $(ConfigurationName) == Debug (
  npm --prefix ../ run dev
) ELSE (
  npm --prefix ../ run build
)

This logic works. If I'm at release mode, it will simple bundle my files and the server will run. But the problem is at debug mode, since the webpack -w doesn't end, the build also never ends, it's expecting an exit code....

So I tried to outsmart Visual Studio and start a new cmd process that would not block the build's start:

start cmd /K npm --prefix ../ run dev

Unfortunately, Visual Studio is too smart for me.

So the question is: is there any smart way to make the visual studio simply run what I want in the pre-build command and not wait for it to finish?

I know there's a proper place for this called task runner, but I couldn't configure it properly, it doesn't recognize any commands from my package.json. Also, I don't want to active it manually after/before running my server, ideally I want it to be integrated with the server startup so that's why I went for the pre-build. But if there's a smarter way to do this, feel free to point it to me.

Thanks in advance.

To answer this question:

So the question is: is there any smart way to make the visual studio simply run what I want in the pre-build command and not wait for it to finish?

The answer is yes, there is a smart way that I figured out.

In your prebuild script, you need to use NodeJS to spawn a new process not connected to its parent. You would call something like this:

node spawner.js fork \"npm --prefix ../ run dev\"

Then you need the spawner.js script in your project root

 /**
 * Spawns a new forked child process
 *
 * The process.argv property returns an array containing the command line arguments
 * passed when the Node.js process was launched. The first element will be process.execPath.
 * See process.argv0 if access to the original value of argv[0] is needed. The second element
 * will be the path to the JavaScript file being executed. The remaining elements will be any
 * additional command line arguments.
 * See: https://nodejs.org/docs/latest/api/process.html#process_process_argv
 *
 */


/**
 * Function: run()
 * This method runs the command using child_proces.exec only
 * Does the same as function fork(), but in a different implementation.
 */
module.exports = {


   /**
    * Function: fork()
    * This method runs the command using child_proces.fork and child_process.exec
    * Does the same as function run(), but in a different implementation.
    * Forks a new NodeJS process of the same codebase in a new V8 instance.
    */
   fork: function (command) {

      console.log('Begin Forking New Process');
      console.log(command);

      var cp = require('child_process');
      var child = cp.fork('./forked-child.js', [command]);

      /**
       * child.unref()
       * Causes the parent's (this) event loop to not include the child (spawned-child.js)
       * in its reference count, allowing the parent to exit independently of the child,
       * unless there is an established IPC channel between the child and parent.
       */
      child.unref();

   },

   runTsNode: function (command) {

      console.log('Begin Running ts-node Script');

      require('child_process').exec(
         // terminating command prompt is /c - persistent command prompt is /k
         'ts-node ' + command + '"',
         function () {
            console.log('Received Command: ' + command);
         });

      /**
       * Debug the arguments received on command line.
       */
      var args = process.argv.slice(2);
      args.forEach((val, index) => {
         console.log(`${index}: ${val}`);
      });

      /**
       * Kill the child process after 2 seconds, once the detached cmd.exe process has spawned
       */
      setTimeout(function () {
         console.log('Done Spawning');
         process.exit(0);
      }, 2000);

   },

   runNode: function (command) {

      console.log('Begin Running Node Script');

      require('child_process').exec(
         // terminating command prompt is /c - persistent command prompt is /k
         'node ' + command + '"',
         function () {
            console.log('Received Command: ' + command);
         });

      /**
       * Debug the arguments received on command line.
       */
      var args = process.argv.slice(2);
      args.forEach((val, index) => {
         console.log(`${index}: ${val}`);
      });

      /**
       * Kill the child process after 2 seconds, once the detached cmd.exe process has spawned
       */
      setTimeout(function () {
         console.log('Done Spawning');
         process.exit(0);
      }, 2000);

   },

   runCommand: function (command) {

      console.log('Begin Running Command Line Script');

      require('child_process').exec(
         // terminating command prompt is /c - persistent command prompt is /k
         'cmd.exe @cmd /k "' + command + '"',
         function () {
            console.log('Received Command: ' + command);
         });

      /**
       * Debug the arguments received on command line.
       */
      var args = process.argv.slice(2);
      args.forEach((val, index) => {
         console.log(`${index}: ${val}`);
      });

      /**
       * Kill the child process after 2 seconds, once the detached cmd.exe process has spawned
       */
      setTimeout(function () {
         console.log('Done Spawning');
         process.exit(0);
      }, 2000);

   },


};

require('make-runnable'); // must be at the END of the file

It requires make-runnable so make sure to run npm i make-runnable -D

Along with spawner.js , this script uses forked-child.js which should also be placed in your project root.

 /**
 * Spawns a new forked child process
 *
 * The process.argv property returns an array containing the command line arguments
 * passed when the Node.js process was launched. The first element will be process.execPath.
 * See process.argv0 if access to the original value of argv[0] is needed. The second element
 * will be the path to the JavaScript file being executed. The remaining elements will be any
 * additional command line arguments.
 * See: https://nodejs.org/docs/latest/api/process.html#process_process_argv
 *
 */

// Window only until its updated for cross platform
require('child_process')
// terminating command prompt is /c - persistent command prompt is /k
   .exec('start cmd.exe @cmd /k "' + process.argv[2] + '"',
      function () {
         console.log('Received Command: ' + process.argv[2]);
      });


/**
 * Debug the arguments received on command line.
 */
process.argv.forEach((val, index) => {
   console.log(`${index}: ${val}`);
});

/**
 * Kill the child process after 2 seconds, once the detached cmd.exe process has spawned
 */
setTimeout(function () {
   console.log('Done Spawning');
   process.exit(0);
}, 2000);

I have included other methods in the spanwer.js script that might come in useful. For this example it's calling the fork method.

Now just run your build, and it should execute

  1. node spawner.js fork \\"npm --prefix ../ run dev\\" , which calls
  2. forked-child.js , which receives your command as an argument and
  3. opens that in a new detached process

Because it is detached from its parent via forking, Visual Studio's build process will continue running even while this runs in its own never-ending terminal instance.

So that is how I solved this exact problem with webpack-dev-server and visual studio. I hope that helps.

I have recently had a similar situation where I want to run Webpack in watch mode after building my project in debug mode. I was running into the same issue where the prebuild step would hang indefinitely when I ran my project.

I came up with the following solution which runs the .bat file by opening a separate cmd window which doesn't stop the build from completing and works like a charm.

Step 1 - Create a .bat file in the root of the project, I have called this WebpackWatch.bat with the following code, this will run webpack and put it into watch mode in the opened cmd window

webpack --progress --profile --watch --mode development

Step 2 - In the post build event, I have added the following, this will run the powershell command to open the .bat file, and then continue on with the build, for release mode, it calls the script build:prod in my packages config

if $(ConfigurationName) == Debug powershell start-process $(ProjectDir)WebpackWatch.bat
if $(ConfigurationName) == Release npm run build:prod

For information purposes my package.json file has the following script for the release build event, notice this does not have --watch in the build process so this completes file with the build step.

 "scripts": {
    "build:prod": "webpack --progress --profile --mode production"
  },

cd [基于bin文件夹的WebPack Package.json路径] npx webpack

I actually figured out a way to do this with a pre-build command. You can start webpack to the side at the same time you start your application in Visual Studio without waiting for the command to finish.

You can use

powershell start-process "cmd ""/K npm run start"""

For example, this is my pre-build event command line in Build Events under the ASP.Net MVC 5 Project's properties:

cd "$(ProjectDir)React"
if $(ConfigurationName) == Debug (
  call npm run build-dev
  powershell start-process "cmd ""/K npm run start"""
)
if $(ConfigurationName) == Release (
  call npm run build-prod
)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM