Skip to content

Commit

Permalink
Stop polyfilling console, 'domain', process, and internal `stream…
Browse files Browse the repository at this point in the history
…` modules by default; rename `includeAliases` to `additionalAliases`, and allow ignoring the defaults with `onlyAliases`

Fixes #40, fixes #52, fixes #18, fixes #44, fixes #20

Signed-off-by: Richie Bendall <richiebendall@gmail.com>
  • Loading branch information
Richienb committed May 24, 2024
1 parent 6b4feb8 commit 08f793b
Show file tree
Hide file tree
Showing 4 changed files with 118 additions and 34 deletions.
17 changes: 11 additions & 6 deletions index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<IncludeOptions, ExcludeOptions>;
export type Options = MergeExclusive<OnlyAliasesOptions, AdditionalExcludeAliasesOptions>;
}

declare class NodePolyfillPlugin {
Expand Down
63 changes: 52 additions & 11 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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');
}
}

Expand Down
46 changes: 39 additions & 7 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -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'],
})
]
};
```
Expand All @@ -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');
Expand All @@ -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');
Expand All @@ -63,7 +95,7 @@ module.exports = {
// Other rules...
plugins: [
new NodePolyfillPlugin({
includeAliases: ['console']
onlyAliases: ['console'],
})
]
};
Expand Down
26 changes: 16 additions & 10 deletions test.js
Original file line number Diff line number Diff line change
@@ -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');
Expand All @@ -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 => {
Expand All @@ -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});
});

0 comments on commit 08f793b

Please sign in to comment.