From 0d38532a8aadb81e728f271a3cfe3dbdd273f174 Mon Sep 17 00:00:00 2001 From: Artem Butusov Date: Sat, 30 Mar 2019 12:58:55 -0400 Subject: [PATCH] Added dedupe option to prevent bundling the same package multiple times --- .gitignore | 1 + README.md | 5 ++++ index.d.ts | 6 ++++ src/index.js | 7 ++++- test/node_modules/react-consumer/index.js | 3 ++ .../react-consumer/node_modules/.gitkeep | 0 .../node_modules/react/index.js | 1 + test/node_modules/react/index.js | 1 + test/samples/react-app/main.js | 4 +++ test/test.js | 30 +++++++++++++++++++ 10 files changed, 57 insertions(+), 1 deletion(-) create mode 100644 test/node_modules/react-consumer/index.js create mode 100644 test/node_modules/react-consumer/node_modules/.gitkeep create mode 100644 test/node_modules/react-consumer/node_modules/react/index.js create mode 100644 test/node_modules/react/index.js create mode 100644 test/samples/react-app/main.js diff --git a/.gitignore b/.gitignore index 08625d3..7bb0182 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ node_modules dist .gobble* !test/node_modules +!test/node_modules/react-consumer/node_modules diff --git a/README.md b/README.md index 75878b0..b419aa5 100644 --- a/README.md +++ b/README.md @@ -65,6 +65,11 @@ export default { // ES2015 modules modulesOnly: true, // Default: false + // Force resolving for these modules to root's node_modules that helps + // to prevent bundling the same package multiple times if package is + // imported from dependencies. + dedupe: [ 'react', 'react-dom' ], // Default: [] + // Any additional options that should be passed through // to node-resolve customResolveOptions: { diff --git a/index.d.ts b/index.d.ts index b1d3faa..adc18bd 100644 --- a/index.d.ts +++ b/index.d.ts @@ -60,6 +60,12 @@ interface RollupNodeResolveOptions { * @default false */ modulesOnly?: boolean; + /** + * Force resolving for these modules to root's node_modules that helps + * to prevent bundling the same package multiple times if package is + * imported from dependencies. + */ + dedupe?: string[]; /** * Any additional options that should be passed through * to node-resolve diff --git a/src/index.js b/src/index.js index 6dc093e..6e5a4a2 100644 --- a/src/index.js +++ b/src/index.js @@ -1,4 +1,4 @@ -import {dirname, extname, normalize, resolve, sep} from 'path'; +import {dirname, extname, normalize, resolve, sep, join} from 'path'; import builtins from 'builtin-modules'; import resolveId from 'resolve'; import isModule from 'is-module'; @@ -40,6 +40,7 @@ function cachedIsFile (file, cb) { const resolveIdAsync = (file, opts) => new Promise((fulfil, reject) => resolveId(file, opts, (err, contents) => err ? reject(err) : fulfil(contents))); export default function nodeResolve ( options = {} ) { + const dedupe = options.dedupe || []; const useModule = options.module !== false; const useMain = options.main !== false; const useJsnext = options.jsnext === true; @@ -82,6 +83,10 @@ export default function nodeResolve ( options = {} ) { const basedir = importer ? dirname( importer ) : process.cwd(); + if (dedupe.indexOf(importee) !== -1) { + importee = join(process.cwd(), 'node_modules', importee); + } + // https://github.com/defunctzombie/package-browser-field-spec if (options.browser && browserMapCache[importer]) { const resolvedImportee = resolve( basedir, importee ); diff --git a/test/node_modules/react-consumer/index.js b/test/node_modules/react-consumer/index.js new file mode 100644 index 0000000..bfc70f0 --- /dev/null +++ b/test/node_modules/react-consumer/index.js @@ -0,0 +1,3 @@ +import React from 'react' + +export default 'react-consumer:' + React diff --git a/test/node_modules/react-consumer/node_modules/.gitkeep b/test/node_modules/react-consumer/node_modules/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/test/node_modules/react-consumer/node_modules/react/index.js b/test/node_modules/react-consumer/node_modules/react/index.js new file mode 100644 index 0000000..8bfd7ab --- /dev/null +++ b/test/node_modules/react-consumer/node_modules/react/index.js @@ -0,0 +1 @@ +export default 'react:child' \ No newline at end of file diff --git a/test/node_modules/react/index.js b/test/node_modules/react/index.js new file mode 100644 index 0000000..6fd06a9 --- /dev/null +++ b/test/node_modules/react/index.js @@ -0,0 +1 @@ +export default 'react:root' diff --git a/test/samples/react-app/main.js b/test/samples/react-app/main.js new file mode 100644 index 0000000..1b39a2a --- /dev/null +++ b/test/samples/react-app/main.js @@ -0,0 +1,4 @@ +import React from 'react' +import ReactConsumer from 'react-consumer' + +export { React, ReactConsumer } diff --git a/test/test.js b/test/test.js index 75baa96..09b4cda 100644 --- a/test/test.js +++ b/test/test.js @@ -761,4 +761,34 @@ describe( 'rollup-plugin-node-resolve', function () { }); }); + it( 'single module version is bundle if dedupe is set', () => { + return rollup.rollup({ + input: 'samples/react-app/main.js', + plugins: [ + nodeResolve({ + dedupe: [ 'react' ] + }) + ] + }).then( executeBundle ).then( module => { + assert.deepEqual(module.exports, { + React: 'react:root', + ReactConsumer: 'react-consumer:react:root' + }); + }); + }); + + it( 'multiple module versions are bundled if dedupe is not set', () => { + return rollup.rollup({ + input: 'samples/react-app/main.js', + plugins: [ + nodeResolve() + ] + }).then( executeBundle ).then( module => { + assert.deepEqual(module.exports, { + React: 'react:root', + ReactConsumer: 'react-consumer:react:child' + }); + }); + }); + });