Repository: serverless-heaven/serverless-webpack
Pull request #1004 (2021-11-19 14:47:00)

Fix command resolution when watch is involved
When testing the plugin with the Serverless Framework, I've discovered that the plugin finalizes the given processing hook (and in turn command)  while leaving some command-specific logic working in the background.

Such configuration introduces some erroneous side effects.  Framework assumes that command was finalized and pursues some cleanup operations. Command seems to operate without issues only because Framework doesn't imply a hard exit on the process.

It's especially important to have it fixed before v3 of the Framework is released, as it comes with new logging interfaces, and e.g. on command finalization, all progress bars are permanently cleared, leaving this feature no further operable (while it's a valuable feature for watching as configured in this plugin)


medikoo: > Could you just rebase against the master so the branch will be up to date? Thanks

Sure, rebased
j0k3r: @medikoo could again rebase against the master? Thanks

Patch:
diff --git a/lib/run.js b/lib/run.js
index fb964aa..fbb8bf4 100644
--- a/lib/run.js
+++ b/lib/run.js
@@ -21,29 +21,32 @@ module.exports = {
       this.serverless.cli.log(`Enabled polling (${watchOptions.poll} ms)`);
     }
 
-    compiler.watch(watchOptions, (err /*, stats */) => {
-      if (err) {
-        throw err;
-      }
-      // eslint-disable-next-line promise/catch-or-return, promise/no-promise-in-callback
-      BbPromise.try(() => {
-        if (this.originalServicePath) {
-          process.chdir(this.originalServicePath);
-          this.serverless.config.servicePath = this.originalServicePath;
+    return new BbPromise((resolve, reject) => {
+      compiler.watch(watchOptions, (err /*, stats */) => {
+        if (err) {
+          reject(err);
+          return;
         }
+        // eslint-disable-next-line promise/catch-or-return, promise/no-promise-in-callback
+        BbPromise.try(() => {
+          if (this.originalServicePath) {
+            process.chdir(this.originalServicePath);
+            this.serverless.config.servicePath = this.originalServicePath;
+          }
 
-        if (!this.isWatching) {
-          this.isWatching = true;
-          return BbPromise.resolve();
-        }
+          if (!this.isWatching) {
+            this.isWatching = true;
+            return BbPromise.resolve();
+          }
 
-        this.serverless.cli.log('Sources changed.');
-        if (_.isFunction(command)) {
-          return command();
-        }
-        this.options.verbose && this.serverless.cli.log(`Invoke ${command}`);
-        return this.serverless.pluginManager.spawn(command);
-      }).then(() => this.serverless.cli.log('Waiting for changes ...'));
+          this.serverless.cli.log('Sources changed.');
+          if (_.isFunction(command)) {
+            return command();
+          }
+          this.options.verbose && this.serverless.cli.log(`Invoke ${command}`);
+          return this.serverless.pluginManager.spawn(command);
+        }).then(() => this.serverless.cli.log('Waiting for changes ...'), reject);
+      });
     });
   }
 };


Test patch:
diff --git a/tests/run.test.js b/tests/run.test.js
index 5efa2b8..7cc191b 100644
--- a/tests/run.test.js
+++ b/tests/run.test.js
@@ -76,7 +76,7 @@ describe('run', () => {
       const watch = module.watch.bind(module);
       webpackMock.compilerMock.watch = sandbox.stub().yields(new Error('Failed'));
 
-      expect(() => watch()).to.throw('Failed');
+      expect(watch()).to.eventually.be.rejectedWith('Failed');
     });
 
     it('should not spawn invoke local on first run', () => {