From 47443411820434386970e5e737422e77abf61808 Mon Sep 17 00:00:00 2001 From: Janic Duplessis Date: Wed, 21 Oct 2020 14:52:38 -0400 Subject: [PATCH] Fix external modules with webpack 5 --- lib/packExternalModules.js | 46 ++-- tests/packExternalModules.test.js | 391 +++++++++++++----------------- 2 files changed, 193 insertions(+), 244 deletions(-) diff --git a/lib/packExternalModules.js b/lib/packExternalModules.js index 96b4a4c43..d3bf4a8d3 100644 --- a/lib/packExternalModules.js +++ b/lib/packExternalModules.js @@ -130,7 +130,7 @@ function getProdModules(externalModules, packagePath, dependencyGraph, forceExcl const originInfo = _.get(dependencyGraph, 'dependencies', {})[module.origin] || {}; moduleVersion = _.get(_.get(originInfo, 'dependencies', {})[module.external], 'version'); if (!moduleVersion) { - // eslint-disable-next-line lodash/path-style + // eslint-disable-next-line lodash/path-style moduleVersion = _.get(dependencyGraph, [ 'dependencies', module.external, 'version' ]); } if (!moduleVersion) { @@ -182,36 +182,42 @@ function isExternalModule(module) { return _.startsWith(module.identifier(), 'external ') && !isBuiltinModule(getExternalModuleName(module)); } +/** + * Gets the module issuer. The ModuleGraph api does not exists in webpack@4 + * so falls back to using module.issuer. + */ +function getIssuerCompat(moduleGraph, module) { + if (moduleGraph) { + return moduleGraph.getIssuer(module); + } + + return module.issuer; +} + /** * Find the original module that required the transient dependency. Returns * undefined if the module is a first level dependency. + * @param {Object} moduleGraph - Webpack module graph * @param {Object} issuer - Module issuer */ -function findExternalOrigin(issuer) { +function findExternalOrigin(moduleGraph, issuer) { if (!_.isNil(issuer) && _.startsWith(issuer.rawRequest, './')) { - return findExternalOrigin(issuer.issuer); + return findExternalOrigin(moduleGraph, getIssuerCompat(moduleGraph, issuer)); } return issuer; } -function getExternalModules(stats) { - if (!stats.compilation.chunks) { - return []; - } +function getExternalModules({ compilation }) { const externals = new Set(); - for (const chunk of stats.compilation.chunks) { - if (!chunk.modulesIterable) { - continue; - } - - // Explore each module within the chunk (built inputs): - for (const module of chunk.modulesIterable) { - if (isExternalModule(module)) { - externals.add({ - origin: _.get(findExternalOrigin(module.issuer), 'rawRequest'), - external: getExternalModuleName(module) - }); - } + for (const module of compilation.modules) { + if (isExternalModule(module)) { + externals.add({ + origin: _.get( + findExternalOrigin(compilation.moduleGraph, getIssuerCompat(compilation.moduleGraph, module)), + 'rawRequest' + ), + external: getExternalModuleName(module) + }); } } return Array.from(externals); diff --git a/tests/packExternalModules.test.js b/tests/packExternalModules.test.js index 69999943a..c80a441a5 100644 --- a/tests/packExternalModules.test.js +++ b/tests/packExternalModules.test.js @@ -20,19 +20,19 @@ chai.use(require('sinon-chai')); const expect = chai.expect; -class ChunkMock { - constructor(modules) { - this._modules = modules; - } - - get modulesIterable() { - return this._modules; +class WebpackModuleGraphMock { + getIssuer(module) { + return module.issuer; } } -class ChunkMockNoModulesIterable { +class WebpackCompilationMock { constructor(modules) { - this._modules = modules; + this.moduleGraph = new WebpackModuleGraphMock(); + this.modules = modules; + this.compiler = { + outputPath: '/my/Service/Path/.webpack/service' + }; } } @@ -137,187 +137,151 @@ describe('packExternalModules', () => { const stats = { stats: [ { - compilation: { - chunks: [ - new ChunkMock([ - { - identifier: _.constant('"crypto"') - }, - { - identifier: _.constant('"uuid/v4"') - }, - { - identifier: _.constant('"mockery"') - }, - { - identifier: _.constant('"@scoped/vendor/module1"') - }, - { - identifier: _.constant('external "@scoped/vendor/module2"') - }, - { - identifier: _.constant('external "uuid/v4"') - }, - { - identifier: _.constant('external "bluebird"') - } - ]), - new ChunkMockNoModulesIterable([]) - ], - compiler: { - outputPath: '/my/Service/Path/.webpack/service' + compilation: new WebpackCompilationMock([ + { + identifier: _.constant('"crypto"') + }, + { + identifier: _.constant('"uuid/v4"') + }, + { + identifier: _.constant('"mockery"') + }, + { + identifier: _.constant('"@scoped/vendor/module1"') + }, + { + identifier: _.constant('external "@scoped/vendor/module2"') + }, + { + identifier: _.constant('external "uuid/v4"') + }, + { + identifier: _.constant('external "bluebird"') } - } + ]) } ] }; const noExtStats = { stats: [ { - compilation: { - chunks: [ - new ChunkMock([ - { - identifier: _.constant('"crypto"') - }, - { - identifier: _.constant('"uuid/v4"') - }, - { - identifier: _.constant('"mockery"') - }, - { - identifier: _.constant('"@scoped/vendor/module1"') - } - ]) - ], - compiler: { - outputPath: '/my/Service/Path/.webpack/service' + compilation: new WebpackCompilationMock([ + { + identifier: _.constant('"crypto"') + }, + { + identifier: _.constant('"uuid/v4"') + }, + { + identifier: _.constant('"mockery"') + }, + { + identifier: _.constant('"@scoped/vendor/module1"') } - } + ]) } ] }; const statsWithFileRef = { stats: [ { - compilation: { - chunks: [ - new ChunkMock([ - { - identifier: _.constant('"crypto"') - }, - { - identifier: _.constant('"uuid/v4"') - }, - { - 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' + compilation: new WebpackCompilationMock([ + { + identifier: _.constant('"crypto"') + }, + { + identifier: _.constant('"uuid/v4"') + }, + { + 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"') } - } + ]) } ] }; const statsWithDevDependency = { stats: [ { - compilation: { - chunks: [ - 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' + compilation: new WebpackCompilationMock([ + { + 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"') } - } + ]) } ] }; const statsWithIgnoredDevDependency = { stats: [ { - compilation: { - chunks: [ - new ChunkMock([ - { - identifier: _.constant('"crypto"') - }, - { - identifier: _.constant('"uuid/v4"') - }, - { - 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"') - }, - { - identifier: _.constant('external "aws-sdk"') - } - ]) - ], - compiler: { - outputPath: '/my/Service/Path/.webpack/service' + compilation: new WebpackCompilationMock([ + { + identifier: _.constant('"crypto"') + }, + { + identifier: _.constant('"uuid/v4"') + }, + { + 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"') + }, + { + identifier: _.constant('external "aws-sdk"') } - } + ]) } ] }; @@ -1184,33 +1148,26 @@ describe('packExternalModules', () => { 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' + compilation: new WebpackCompilationMock([ + { + identifier: _.constant('"crypto"') + }, + { + identifier: _.constant('"uuid/v4"') + }, + { + identifier: _.constant('"mockery"') + }, + { + identifier: _.constant('"@scoped/vendor/module1"') + }, + { + identifier: _.constant('external "bluebird"') + }, + { + identifier: _.constant('external "request-promise"') } - } + ]) } ] }; @@ -1291,33 +1248,26 @@ describe('packExternalModules', () => { 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' + compilation: new WebpackCompilationMock([ + { + identifier: _.constant('"crypto"') + }, + { + identifier: _.constant('"uuid/v4"') + }, + { + identifier: _.constant('"mockery"') + }, + { + identifier: _.constant('"@scoped/vendor/module1"') + }, + { + identifier: _.constant('external "bluebird"') + }, + { + identifier: _.constant('external "request-promise"') } - } + ]) } ] }; @@ -1397,18 +1347,11 @@ describe('packExternalModules', () => { const transitiveDepStats = { stats: [ { - compilation: { - chunks: [ - new ChunkMock([ - { - identifier: _.constant('external "classnames"') - } - ]) - ], - compiler: { - outputPath: '/my/Service/Path/.webpack/service' + compilation: new WebpackCompilationMock([ + { + identifier: _.constant('external "classnames"') } - } + ]) } ] };