diff --git a/index.d.ts b/index.d.ts index 3e9e3c7..f3b3132 100644 --- a/index.d.ts +++ b/index.d.ts @@ -33,21 +33,26 @@ declare namespace NodePolyfillPlugin { | 'vm' | 'zlib'; - export type IncludeOptions = { + export type OnlyAliasesOptions = { /** - By default, the modules that were polyfilled in Webpack 4 are mirrored over. However, you can choose to only include certain aliases. For example, you can only have `console` polyfilled. + You can choose to only include certain aliases, ignoring the defaults. For example, you can have only `console` polyfilled. */ - includeAliases?: readonly Alias[]; + onlyAliases?: readonly Alias[]; }; - export type ExcludeOptions = { + export type AdditionalExcludeAliasesOptions = { /** - By default, the modules that were polyfilled in Webpack 4 are mirrored over. However, if you don't want a module like `console` to be polyfilled you can specify alises to be skipped here. + You can choose to add certain aliases to the list of polyfilled modules. For example, you can choose to polyfill `console`. + */ + additionalAliases?: readonly Alias[]; + + /** + If you don't want a module to be polyfilled, you can specify aliases to be skipped here. */ excludeAliases?: readonly Alias[]; }; - export type Options = MergeExclusive; + export type Options = MergeExclusive; } declare class NodePolyfillPlugin { diff --git a/index.js b/index.js index 2c997e7..8506497 100644 --- a/index.js +++ b/index.js @@ -20,31 +20,72 @@ function includeKeys(object, predicate) { return result; } -// https://github.com/sindresorhus/filter-obj/blob/58086b537bb622166387216bfb7da6e8184996ba/index.js#L27-L34 -function excludeKeys(object, keys) { - const set = new Set(keys); +const defaultPolyfills = new Set([ + 'assert', + 'buffer', + 'Buffer', + 'constants', + 'crypto', + 'events', + 'http', + 'https', + 'os', + 'path', + 'querystring', + 'stream', + 'string_decoder', + 'sys', + 'timers', + 'tty', + 'url', + 'util', + 'vm', + 'zlib', +]); - return includeKeys(object, key => !set.has(key)); +function createAliasFilter({excludeAliases, onlyAliases, additionalAliases}) { + if (onlyAliases.length > 0) { + return object => includeKeys(object, onlyAliases); + } + + if (additionalAliases.length > 0) { + return object => includeKeys(object, key => (defaultPolyfills.has(key) && !excludeAliases.includes(key)) || additionalAliases.includes(key)); + } + + return object => includeKeys(object, key => defaultPolyfills.has(key) && !excludeAliases.includes(key)); } -function createAliasFilter({includeAliases, excludeAliases}) { - if (includeAliases.length > 0) { - return object => includeKeys(object, includeAliases); +function areItemsUnique(...iterables) { + const seen = new Set(); + + for (const iterable of iterables) { + for (const item of iterable) { + if (seen.has(item)) { + return false; + } + + seen.add(item); + } } - return object => excludeKeys(object, excludeAliases); + return true; } module.exports = class NodePolyfillPlugin { constructor(options = {}) { this.options = { excludeAliases: [], - includeAliases: [], + onlyAliases: [], + additionalAliases: [], ...options, }; - if (this.options.includeAliases.length > 0 && this.options.excludeAliases.length > 0) { - throw new Error('excludeAliases and includeAliases are mutually exclusive'); + if (this.options.onlyAliases.length > 0) { + if (this.options.excludeAliases.length > 0 || this.options.additionalAliases.length > 0) { + throw new Error('onlyAliases is mutually exclusive with excludeAliases and additionalAliases'); + } + } else if (!areItemsUnique(this.options.excludeAliases, this.options.additionalAliases)) { + throw new Error('excludeAliases and additionalAliases must not include the same items'); } } diff --git a/readme.md b/readme.md index 58469cf..6967014 100644 --- a/readme.md +++ b/readme.md @@ -20,7 +20,22 @@ const NodePolyfillPlugin = require('node-polyfill-webpack-plugin'); module.exports = { // Other rules... plugins: [ - new NodePolyfillPlugin() + new NodePolyfillPlugin(), + ] +}; +``` + +`console`, `process`, and most deprecated/internal Node.js core modules are not polyfilled by default. If you still need to polyfill them, you can use the `additionalAliases` option: + +```js +const NodePolyfillPlugin = require('node-polyfill-webpack-plugin'); + +module.exports = { + // Other rules... + plugins: [ + new NodePolyfillPlugin({ + additionalAliases: ['process', 'punycode'], + }) ] }; ``` @@ -33,11 +48,28 @@ module.exports = { Type: `object` -`excludeAliases` and `includeAliases` are mutually exclusive. +`onlyAliases` is mutually exclusive with `excludeAliases` and `additionalAliases`. #### excludeAliases -By default, the modules that were polyfilled in Webpack 4 are mirrored over. However, if you don't want a module like `console` to be polyfilled you can specify alises to be skipped here. +If you don't want a module to be polyfilled, you can specify aliases to be skipped here. + +```js +const NodePolyfillPlugin = require('node-polyfill-webpack-plugin'); + +module.exports = { + // Other rules... + plugins: [ + new NodePolyfillPlugin({ + excludeAliases: ['console'], + }) + ] +}; +``` + +#### additionalAliases + +Alternatively, you can choose to add certain aliases to the list of polyfilled modules. For example, you can choose to polyfill `console`. ```js const NodePolyfillPlugin = require('node-polyfill-webpack-plugin'); @@ -46,15 +78,15 @@ module.exports = { // Other rules... plugins: [ new NodePolyfillPlugin({ - excludeAliases: ['console'] + additionalAliases: ['console'], }) ] }; ``` -#### includeAliases +#### onlyAliases -Alternatively, you can choose to only include certain aliases. For example, you can only have `console` polyfilled. +You can also choose to only include certain aliases, ignoring the defaults. For example, you can have only `console` polyfilled. ```js const NodePolyfillPlugin = require('node-polyfill-webpack-plugin'); @@ -63,7 +95,7 @@ module.exports = { // Other rules... plugins: [ new NodePolyfillPlugin({ - includeAliases: ['console'] + onlyAliases: ['console'], }) ] }; diff --git a/test.js b/test.js index 204f2ae..06af981 100644 --- a/test.js +++ b/test.js @@ -1,4 +1,4 @@ -const fs = require('node:fs'); +const {promises: fs} = require('node:fs'); const test = require('ava'); const webpack = require('p-webpack'); const NodePolyfillPlugin = require('./index.js'); @@ -14,18 +14,21 @@ test('main', async t => { }, plugins: [ new NodePolyfillPlugin({ - excludeAliases: ['console'], + additionalAliases: ['assert', 'buffer'], + // ExcludeAliases: ['console'], }), ], }); t.is(require('./dist/1.js'), 'Hello World'); + const output = await fs.readFile('./dist/1.js', {encoding: 'utf8'}); + // https://github.com/browserify/console-browserify/blob/f7eefc7c908c29d2e94954e5c6c1098e8c1028b4/index.js#L63 - t.false(fs.readFileSync('./dist/1.js').toString().includes('No such label: ')); + t.false(output.includes('No such label: ')); - // https://github.com/feross/buffer/blob/master/index.js#L80 - t.true(fs.readFileSync('./dist/1.js').toString().includes('is invalid for option "size"')); + // https://github.com/feross/buffer/blob/5857e295f4d37e3ad02c3abcbf7e8e5ef51f3be6/index.js#L101 + t.true(output.includes('is invalid for option "size"')); }); test('includeAliases', async t => { @@ -39,23 +42,26 @@ test('includeAliases', async t => { }, plugins: [ new NodePolyfillPlugin({ - includeAliases: ['console'], + additionalAliases: ['console', 'assert'], + excludeAliases: ['buffer', 'Buffer'], }), ], }); t.is(require('./dist/2.js'), 'Hello World'); + const output = await fs.readFile('./dist/2.js', {encoding: 'utf8'}); + // https://github.com/browserify/console-browserify/blob/f7eefc7c908c29d2e94954e5c6c1098e8c1028b4/index.js#L63 - t.true(fs.readFileSync('./dist/2.js').toString().includes('No such label: ')); + t.true(output.includes('No such label: ')); // https://github.com/feross/buffer/blob/master/index.js#L80 - t.false(fs.readFileSync('./dist/2.js').toString().includes('is invalid for option "size"')); + t.false(output.includes('is invalid for option "size"')); }); -test('includeAliases and excludeAliases used at the same time', t => { +test('onlyAliases and excludeAliases used at the same time', t => { t.throws(() => new NodePolyfillPlugin({ - includeAliases: ['console'], + onlyAliases: ['console'], excludeAliases: ['crypto'], }), {instanceOf: Error}); });