Repository: serverless-heaven/serverless-webpack
Pull request #328 (2018-02-26 09:58:51)

Use chunk.forEachModules instead of deprecated chunk.modules
## What did you implement:

Webpack 4 removes the deprecated chunk.modules property. This uses the forEachModule instead. See https://github.com/webpack/webpack/blob/webpack-3/lib/Chunk.js#L473.

`forEachModule` exists since webpack v3 so this means webpack v2 is no longer supported with this change. Not sure how much we care about webpack 2 support. If we want we could check if `forEachModule` exists and fallback to `.modules`.

## How did you implement it:

Trivial, most changes were to update tests.

## How can we verify it:

- Tested in a project using webpack 4, before this change externals were broken.
- Run tests

## Todos:

- [x] Write tests (N/A)
- [x] Write documentation (N/A)
- [x] Fix linting errors
- [x] Make sure code coverage hasn't dropped
- [x] Provide verification config / commands / resources
- [x] Enable "Allow edits from maintainers" for this PR
- [x] Update the messages below

***Is this ready for review?:*** YES
***Is it a breaking change?:*** YES (if webpack 2 is still supported)



Patch:
diff --git a/.editorconfig b/.editorconfig
index 93dab0f..9fbef2a 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -1,12 +1,10 @@
-root = true
-
-[*]
-indent_style = space
-indent_size = 2
-end_of_line = lf
-
-[*.{js,jsx}]
-indent_style = space
-indent_size = 2
-end_of_line = lf
-charset = utf-8
+root = true
+
+[*]
+indent_style = space
+end_of_line = lf
+
+[*.{js,jsx}]
+indent_style = space
+end_of_line = lf
+charset = utf-8
diff --git a/README.md b/README.md
index 2c1922a..db2d26b 100644
--- a/README.md
+++ b/README.md
@@ -459,14 +459,6 @@ Plugin commands are supported by the following providers. ⁇ indicates that com
 | invoke local          |      ✔︎     |         ✔︎        |        ⁇        |            ⁇           |
 | invoke local --watch  |      ✔︎     |         ✔︎        |        ⁇        |            ⁇           |
 
-## Plugin support
-
-The following serverless plugins are explicitly supported with `serverless-webpack`
-
-| Plugin                            | NPM |
-|-----------------------------------|-----|
-| serverless-step-functions-offline | [![NPM][ico-step-functions-offline]][link-step-functions-offline] |
-
 ## For developers
 
 The plugin exposes a complete lifecycle model that can be hooked by other plugins to extend
@@ -624,8 +616,6 @@ plugin when running a command or invoked by a hook.
 [link-examples]: ./examples
 [link-serverless-offline]: https://www.npmjs.com/package/serverless-offline
 [link-serverless-dynamodb-local]: https://www.npmjs.com/package/serverless-dynamodb-local
-[link-step-functions-offline]: https://www.npmjs.com/package/serverless-step-functions-offline
-[ico-step-functions-offline]: https://img.shields.io/npm/v/serverless-step-functions-offline.svg
 
 [comment]: # (Referenced issues)
 
diff --git a/index.js b/index.js
index 3664e8c..7d99d55 100644
--- a/index.js
+++ b/index.js
@@ -12,7 +12,6 @@ const run = require('./lib/run');
 const prepareLocalInvoke = require('./lib/prepareLocalInvoke');
 const runPluginSupport = require('./lib/runPluginSupport');
 const prepareOfflineInvoke = require('./lib/prepareOfflineInvoke');
-const prepareStepOfflineInvoke = require('./lib/prepareStepOfflineInvoke');
 const packExternalModules = require('./lib/packExternalModules');
 const packageModules = require('./lib/packageModules');
 const lib = require('./lib');
@@ -45,8 +44,7 @@ class ServerlessWebpack {
       packageModules,
       prepareLocalInvoke,
       runPluginSupport,
-      prepareOfflineInvoke,
-      prepareStepOfflineInvoke
+      prepareOfflineInvoke
     );
 
     this.commands = {
@@ -165,9 +163,6 @@ class ServerlessWebpack {
         .then(this.prepareOfflineInvoke)
         .then(this.wpwatch),
 
-      'before:step-functions-offline:start': () => BbPromise.bind(this)
-         .then(this.prepareStepOfflineInvoke)
-         .then(this.compile)
     };
   }
 }
diff --git a/lib/packExternalModules.js b/lib/packExternalModules.js
index c9b3356..92c6cb4 100644
--- a/lib/packExternalModules.js
+++ b/lib/packExternalModules.js
@@ -154,7 +154,7 @@ function getExternalModules(stats) {
 
   _.forEach(stats.compilation.chunks, chunk => {
     // Explore each module within the chunk (built inputs):
-    _.forEach(chunk.modules, module => {
+    chunk.forEachModule(module => {
       if (isExternalModule(module)) {
         externals.add({
           origin: _.get(findExternalOrigin(module.issuer), 'rawRequest'),
@@ -285,7 +285,7 @@ module.exports = {
             /**
              * We should not be modifying 'package-lock.json'
              * because this file should be treat as internal to npm.
-             * 
+             *
              * Rebase package-lock is a temporary workaround and must be
              * removed as soon as https://github.com/npm/npm/issues/19183 gets fixed.
              */
diff --git a/lib/prepareStepOfflineInvoke.js b/lib/prepareStepOfflineInvoke.js
deleted file mode 100644
index ddc12fe..0000000
--- a/lib/prepareStepOfflineInvoke.js
+++ /dev/null
@@ -1,24 +0,0 @@
-'use strict';
-
-const _ = require('lodash');
-const path = require('path');
-
-/**
- * Special settings for use with serverless-step-functions-offline.
- */
-
-module.exports = {
-  prepareStepOfflineInvoke() {
-    _.set(this.serverless, 'service.package.individually', false);
-
-    return this.serverless.pluginManager.spawn('webpack:validate')
-      .then(() => {
-        if (!this.options.location && !_.get(this.serverless, 'service.custom.stepFunctionsOffline.location')) {
-          _.set(this.serverless, 'service.custom.stepFunctionsOffline.location',
-            path.relative(this.serverless.config.servicePath, path.join(this.webpackOutputPath, 'service'))
-          );
-        }
-        return null;
-      });
-  }
-};
diff --git a/package.json b/package.json
index 646abdb..1b1678a 100644
--- a/package.json
+++ b/package.json
@@ -65,6 +65,6 @@
     "sinon-chai": "^2.14.0"
   },
   "peerDependencies": {
-    "webpack": ">= 3.0.0 < 5"
+    "webpack": "*"
   }
 }


Test patch:
diff --git a/tests/all.js b/tests/all.js
index e9d3d5b..b68fc27 100644
--- a/tests/all.js
+++ b/tests/all.js
@@ -9,5 +9,4 @@ describe('serverless-webpack', () => {
   require('./cleanup.test');
   require('./wpwatch.test');
   require('./runPluginSupport.test');
-  require('./prepareStepOfflineInvoke.test');
 });
diff --git a/tests/data/stats-peerdeps.js b/tests/data/stats-peerdeps.js
deleted file mode 100644
index ff9d190..0000000
--- a/tests/data/stats-peerdeps.js
+++ /dev/null
@@ -1,37 +0,0 @@
-const _ = require('lodash');
-
-module.exports = {
-  stats: [
-    {
-      compilation: {
-        chunks: [
-          {
-            modules: [
-              {
-                identifier: _.constant('"crypto"')
-              },
-              {
-                identifier: _.constant('"uuid/v4"')
-              },
-              {
-                identifier: _.constant('"mockery"')
-              },
-              {
-                identifier: _.constant('"@scoped/vendor/module1"')
-              },
-              {
-                identifier: _.constant('external "bluebird"')
-              },
-              {
-                identifier: _.constant('external "request-promise"')
-              }
-            ]
-          }
-        ],
-        compiler: {
-          outputPath: '/my/Service/Path/.webpack/service'
-        }
-      }
-    }
-  ]
-};
diff --git a/tests/packExternalModules.test.js b/tests/packExternalModules.test.js
index 0c9a042..36d1074 100644
--- a/tests/packExternalModules.test.js
+++ b/tests/packExternalModules.test.js
@@ -19,6 +19,16 @@ chai.use(require('sinon-chai'));
 
 const expect = chai.expect;
 
+class ChunkMock {
+  constructor(modules) {
+    this._modules = modules;
+  }
+
+  forEachModule(fn) {
+    _.forEach(this._modules, fn);
+  }
+}
+
 describe('packExternalModules', () => {
   let sandbox;
   let baseModule;
@@ -90,34 +100,32 @@ describe('packExternalModules', () => {
         {
           compilation: {
             chunks: [
-              {
-                modules: [
-                  {
-                    identifier: _.constant('"crypto"')
-                  },
-                  {
-                    identifier: _.constant('"uuid/v4"')
-                  },
-                  {
-                    identifier: _.constant('external "eslint"')
-                  },
-                  {
-                    identifier: _.constant('"mockery"')
-                  },
-                  {
-                    identifier: _.constant('"@scoped/vendor/module1"')
-                  },
-                  {
-                    identifier: _.constant('external "@scoped/vendor/module2"')
-                  },
-                  {
-                    identifier: _.constant('external "uuid/v4"')
-                  },
-                  {
-                    identifier: _.constant('external "bluebird"')
-                  },
-                ]
-              }
+              new ChunkMock([
+                {
+                  identifier: _.constant('"crypto"')
+                },
+                {
+                  identifier: _.constant('"uuid/v4"')
+                },
+                {
+                  identifier: _.constant('external "eslint"')
+                },
+                {
+                  identifier: _.constant('"mockery"')
+                },
+                {
+                  identifier: _.constant('"@scoped/vendor/module1"')
+                },
+                {
+                  identifier: _.constant('external "@scoped/vendor/module2"')
+                },
+                {
+                  identifier: _.constant('external "uuid/v4"')
+                },
+                {
+                  identifier: _.constant('external "bluebird"')
+                },
+              ])
             ],
             compiler: {
               outputPath: '/my/Service/Path/.webpack/service'
@@ -131,22 +139,20 @@ describe('packExternalModules', () => {
         {
           compilation: {
             chunks: [
-              {
-                modules: [
-                  {
-                    identifier: _.constant('"crypto"')
-                  },
-                  {
-                    identifier: _.constant('"uuid/v4"')
-                  },
-                  {
-                    identifier: _.constant('"mockery"')
-                  },
-                  {
-                    identifier: _.constant('"@scoped/vendor/module1"')
-                  },
-                ]
-              }
+              new ChunkMock([
+                {
+                  identifier: _.constant('"crypto"')
+                },
+                {
+                  identifier: _.constant('"uuid/v4"')
+                },
+                {
+                  identifier: _.constant('"mockery"')
+                },
+                {
+                  identifier: _.constant('"@scoped/vendor/module1"')
+                },
+              ])
             ],
             compiler: {
               outputPath: '/my/Service/Path/.webpack/service'
@@ -160,37 +166,35 @@ describe('packExternalModules', () => {
         {
           compilation: {
             chunks: [
-              {
-                modules: [
-                  {
-                    identifier: _.constant('"crypto"')
-                  },
-                  {
-                    identifier: _.constant('"uuid/v4"')
-                  },
-                  {
-                    identifier: _.constant('external "eslint"')
-                  },
-                  {
-                    identifier: _.constant('"mockery"')
-                  },
-                  {
-                    identifier: _.constant('"@scoped/vendor/module1"')
-                  },
-                  {
-                    identifier: _.constant('external "@scoped/vendor/module2"')
-                  },
-                  {
-                    identifier: _.constant('external "uuid/v4"')
-                  },
-                  {
-                    identifier: _.constant('external "localmodule"')
-                  },
-                  {
-                    identifier: _.constant('external "bluebird"')
-                  },
-                ]
-              }
+              new ChunkMock([
+                {
+                  identifier: _.constant('"crypto"')
+                },
+                {
+                  identifier: _.constant('"uuid/v4"')
+                },
+                {
+                  identifier: _.constant('external "eslint"')
+                },
+                {
+                  identifier: _.constant('"mockery"')
+                },
+                {
+                  identifier: _.constant('"@scoped/vendor/module1"')
+                },
+                {
+                  identifier: _.constant('external "@scoped/vendor/module2"')
+                },
+                {
+                  identifier: _.constant('external "uuid/v4"')
+                },
+                {
+                  identifier: _.constant('external "localmodule"')
+                },
+                {
+                  identifier: _.constant('external "bluebird"')
+                },
+              ])
             ],
             compiler: {
               outputPath: '/my/Service/Path/.webpack/service'
@@ -332,7 +336,7 @@ describe('packExternalModules', () => {
 
       sandbox.stub(process, 'cwd').returns(path.join('/my/Service/Path'));
       mockery.registerMock(path.join(process.cwd(), 'locals', 'package.json'), packageLocalRefMock);
-      
+
       return expect(module.packExternalModules()).to.be.fulfilled
       .then(() => BbPromise.all([
         // The module package JSON and the composite one should have been stored
@@ -859,7 +863,39 @@ describe('packExternalModules', () => {
         };
 
         const dependencyGraph = require('./data/npm-ls-peerdeps.json');
-        const peerDepStats = require('./data/stats-peerdeps.js');
+        const peerDepStats = {
+          stats: [
+            {
+              compilation: {
+                chunks: [
+                  new ChunkMock([
+                    {
+                      identifier: _.constant('"crypto"')
+                    },
+                    {
+                      identifier: _.constant('"uuid/v4"')
+                    },
+                    {
+                      identifier: _.constant('"mockery"')
+                    },
+                    {
+                      identifier: _.constant('"@scoped/vendor/module1"')
+                    },
+                    {
+                      identifier: _.constant('external "bluebird"')
+                    },
+                    {
+                      identifier: _.constant('external "request-promise"')
+                    }
+                  ])
+                ],
+                compiler: {
+                  outputPath: '/my/Service/Path/.webpack/service'
+                }
+              }
+            }
+          ]
+        };
 
         module.webpackOutputPath = 'outputPath';
         fsExtraMock.pathExists.yields(null, false);
diff --git a/tests/prepareStepOfflineInvoke.test.js b/tests/prepareStepOfflineInvoke.test.js
deleted file mode 100644
index 91c651e..0000000
--- a/tests/prepareStepOfflineInvoke.test.js
+++ /dev/null
@@ -1,132 +0,0 @@
-'use strict';
-
-const _ = require('lodash');
-const BbPromise = require('bluebird');
-const chai = require('chai');
-const sinon = require('sinon');
-const Serverless = require('serverless');
-
-chai.use(require('chai-as-promised'));
-chai.use(require('sinon-chai'));
-
-const expect = chai.expect;
-
-describe('prepareStepOfflineInvoke', () => {
-  let serverless;
-  let baseModule;
-  let module;
-  let sandbox;
-
-  before(() => {
-    sandbox = sinon.createSandbox();
-    sandbox.usingPromise(BbPromise.Promise);
-
-    baseModule = require('../lib/prepareStepOfflineInvoke');
-    Object.freeze(baseModule);
-  });
-
-  beforeEach(() => {
-    serverless = new Serverless();
-    serverless.cli = {
-      log: sandbox.stub()
-    };
-    sandbox.stub(serverless.pluginManager, 'spawn');
-    module = _.assign({
-      serverless,
-      options: {},
-    }, baseModule);
-  });
-
-  afterEach(() => {
-    sandbox.restore();
-  });
-
-  it('should set service packaging explicitly', () => {
-    serverless.pluginManager.spawn.resolves();
-    serverless.config.servicePath = 'myPath';
-    module.webpackOutputPath = '.';
-    module.serverless.service.package = {};
-
-    return expect(module.prepareStepOfflineInvoke()).to.be.fulfilled
-    .then(() => {
-      expect(module.serverless.service.package).to.have.a.property('individually').that.is.false;
-      return null;
-    });
-  });
-
-  it('should switch to service packaging', () => {
-    serverless.pluginManager.spawn.resolves();
-    serverless.config.servicePath = 'myPath';
-    module.webpackOutputPath = '.';
-    module.serverless.service.package = {
-      individually: true
-    };
-
-    return expect(module.prepareStepOfflineInvoke()).to.be.fulfilled
-    .then(() => {
-      expect(module.serverless.service.package).to.have.a.property('individually').that.is.false;
-      return null;
-    });
-  });
-
-  it('should spawn webpack:validate', () => {
-    serverless.pluginManager.spawn.resolves();
-    serverless.config.servicePath = 'myPath';
-    module.webpackOutputPath = '.';
-
-    return expect(module.prepareStepOfflineInvoke()).to.be.fulfilled
-    .then(() => {
-      expect(serverless.pluginManager.spawn).to.have.been.calledOnce;
-      expect(serverless.pluginManager.spawn).to.have.been.calledWithExactly('webpack:validate');
-      return null;
-    });
-  });
-
-  it('should reject if spawn rejects', () => {
-    serverless.pluginManager.spawn.returns(BbPromise.reject(new Error('spawn failed')));
-    serverless.config.servicePath = 'myPath';
-    module.webpackOutputPath = '.';
-
-    return expect(module.prepareStepOfflineInvoke()).to.be.rejectedWith('spawn failed');
-  });
-
-  it('should set location if not given by user', () => {
-    serverless.pluginManager.spawn.resolves();
-    serverless.config.servicePath = '.';
-    module.webpackOutputPath = '.';
-
-    return expect(module.prepareStepOfflineInvoke()).to.be.fulfilled
-    .then(() => {
-      expect(serverless.service).to.have.a.nested.property('custom.stepFunctionsOffline.location', 'service');
-      return null;
-    });
-  });
-
-  it('should keep location if set in service config', () => {
-    serverless.pluginManager.spawn.resolves();
-    serverless.config.servicePath = '.';
-    module.webpackOutputPath = '.';
-    _.set(module.serverless, 'service.custom.stepFunctionsOffline.location', 'myLocation');
-
-    return expect(module.prepareStepOfflineInvoke()).to.be.fulfilled
-    .then(() => {
-      expect(serverless.service).to.have.a.nested.property('custom.stepFunctionsOffline.location', 'myLocation');
-      return null;
-    });
-  });
-
-  it('should keep location if set in options', () => {
-    serverless.pluginManager.spawn.resolves();
-    serverless.config.servicePath = '.';
-    module.webpackOutputPath = '.';
-    module.options = {
-      location: 'myLocation'
-    };
-
-    return expect(module.prepareStepOfflineInvoke()).to.be.fulfilled
-    .then(() => {
-      expect(serverless.service).to.not.have.a.nested.property('custom.stepFunctionsOffline.location');
-      return null;
-    });
-  });
-});