Skip to content

Commit 0a21502

Browse files
Add the ability to exlcude certain plugins or loaders, if causing problems
1 parent 7e64686 commit 0a21502

File tree

5 files changed

+12213
-8963
lines changed

5 files changed

+12213
-8963
lines changed

README.md

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,32 @@ const webpackConfig = smp.wrap({
123123
});
124124
```
125125

126+
### `options.excludedPlugins`
127+
128+
Type: `Array<String|Function|RegExp>`<br>
129+
Default: `[]`
130+
131+
Excludes plugins from SMP's proxy wrapping. Entries can be:
132+
133+
- a plugin name
134+
- a plugin constructor
135+
- a regular expression matched against plugin names
136+
137+
Name matching checks both `options.pluginNames` aliases and constructor names. E.g.
138+
139+
```javascript
140+
const smp = new SpeedMeasurePlugin({
141+
pluginNames: {
142+
customReactRefresh: reactRefreshPlugin,
143+
},
144+
excludedPlugins: [
145+
"customReactRefresh",
146+
MiniCssExtractPlugin,
147+
/SomeLegacyPlugin$/,
148+
],
149+
});
150+
```
151+
126152
### `options.loaderTopFiles`
127153

128154
Type: `Number`<br>
@@ -168,6 +194,23 @@ This flag is _experimental_. Some loaders will have inaccurate results:
168194

169195
We will find solutions to these issues before removing the _(experimental)_ flag on this option.
170196

197+
### `options.excludedLoaders`
198+
199+
Type: `Array<String|RegExp>`<br>
200+
Default: `[]`
201+
202+
When `granularLoaderData` is enabled, SMP prepends its timing loader to each matching
203+
loader rule. Use this option to skip rules whose loader chain contains a matching
204+
loader name or regular expression. String matching checks both the original loader
205+
request and the normalized package name. E.g.
206+
207+
```javascript
208+
const smp = new SpeedMeasurePlugin({
209+
granularLoaderData: true,
210+
excludedLoaders: ["thread-loader", /mini-css-extract-plugin/],
211+
});
212+
```
213+
171214
## FAQ
172215

173216
### What does general output time mean?

index.js

Lines changed: 60 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,18 @@ const {
1818

1919
const NS = path.dirname(fs.realpathSync(__filename));
2020

21+
const resetRegex = (pattern) => {
22+
pattern.lastIndex = 0;
23+
return pattern;
24+
};
25+
26+
const matchesName = (names, matcher) => {
27+
if (typeof matcher === "string") return names.includes(matcher);
28+
if (matcher instanceof RegExp)
29+
return names.some((name) => resetRegex(matcher).test(name));
30+
return false;
31+
};
32+
2133
module.exports = class SpeedMeasurePlugin {
2234
constructor(options) {
2335
this.options = options || {};
@@ -35,32 +47,67 @@ module.exports = class SpeedMeasurePlugin {
3547
);
3648
}
3749

50+
shouldExcludePlugin(plugin) {
51+
const excludedPlugins = this.options.excludedPlugins || [];
52+
const alias = Object.keys(this.options.pluginNames || {}).find(
53+
(pluginName) => plugin === this.options.pluginNames[pluginName]
54+
);
55+
const pluginNames = [
56+
alias,
57+
plugin.constructor && plugin.constructor.name,
58+
].filter((name, index, names) => name && names.indexOf(name) === index);
59+
60+
return excludedPlugins.some(
61+
(matcher) =>
62+
matchesName(pluginNames, matcher) ||
63+
(typeof matcher === "function" &&
64+
(plugin === matcher || plugin.constructor === matcher))
65+
);
66+
}
67+
68+
shouldExcludeLoader(loader) {
69+
const excludedLoaders = this.options.excludedLoaders || [];
70+
const loaderNames = [loader.loader || loader]
71+
.concat(getLoaderNames([loader]))
72+
.filter((name, index, names) => name && names.indexOf(name) === index);
73+
74+
return excludedLoaders.some((matcher) => matchesName(loaderNames, matcher));
75+
}
76+
77+
wrapPlugin(plugin) {
78+
if (!plugin || typeof plugin.apply !== "function") return plugin;
79+
80+
const pluginName =
81+
Object.keys(this.options.pluginNames || {}).find(
82+
(pluginName) => plugin === this.options.pluginNames[pluginName]
83+
) ||
84+
(plugin.constructor && plugin.constructor.name) ||
85+
"(unable to deduce plugin name)";
86+
if (this.shouldExcludePlugin(plugin)) return plugin;
87+
88+
return new WrappedPlugin(plugin, pluginName, this);
89+
}
90+
3891
wrap(config) {
3992
if (this.options.disable) return config;
4093
if (Array.isArray(config)) return config.map(this.wrap);
4194
if (typeof config === "function")
4295
return (...args) => this.wrap(config(...args));
4396

44-
config.plugins = (config.plugins || []).map((plugin) => {
45-
const pluginName =
46-
Object.keys(this.options.pluginNames || {}).find(
47-
(pluginName) => plugin === this.options.pluginNames[pluginName]
48-
) ||
49-
(plugin.constructor && plugin.constructor.name) ||
50-
"(unable to deduce plugin name)";
51-
return new WrappedPlugin(plugin, pluginName, this);
52-
});
97+
config.plugins = (config.plugins || []).map((plugin) =>
98+
this.wrapPlugin(plugin)
99+
);
53100

54101
if (config.optimization && config.optimization.minimizer) {
55102
config.optimization.minimizer = config.optimization.minimizer.map(
56-
(plugin) => {
57-
return new WrappedPlugin(plugin, plugin.constructor.name, this);
58-
}
103+
(plugin) => this.wrapPlugin(plugin)
59104
);
60105
}
61106

62107
if (config.module && this.options.granularLoaderData) {
63-
config.module = prependLoader(config.module);
108+
config.module = prependLoader(config.module, (loader) =>
109+
this.shouldExcludeLoader(loader)
110+
);
64111
}
65112

66113
if (!this.smpPluginAdded) {

index.test.js

Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
const SpeedMeasurePlugin = require("./index");
2+
3+
class IncludedPlugin {
4+
apply() {}
5+
}
6+
7+
class ExcludedPlugin {
8+
apply() {}
9+
}
10+
11+
class IncludedMinimizer {
12+
apply() {}
13+
}
14+
15+
class ExcludedMinimizer {
16+
apply() {}
17+
}
18+
19+
describe("wrap", () => {
20+
it("should exclude named plugins and minimizers from wrapping", () => {
21+
const includedPlugin = new IncludedPlugin();
22+
const excludedPlugin = new ExcludedPlugin();
23+
const includedMinimizer = new IncludedMinimizer();
24+
const excludedMinimizer = new ExcludedMinimizer();
25+
const smp = new SpeedMeasurePlugin({
26+
excludedPlugins: ["ExcludedPlugin", "ExcludedMinimizer"],
27+
});
28+
29+
const wrappedConfig = smp.wrap({
30+
plugins: [includedPlugin, excludedPlugin],
31+
optimization: {
32+
minimizer: [includedMinimizer, excludedMinimizer],
33+
},
34+
});
35+
36+
expect(wrappedConfig.plugins[0]).not.toBe(includedPlugin);
37+
expect(wrappedConfig.plugins[1]).toBe(excludedPlugin);
38+
expect(wrappedConfig.optimization.minimizer[0]).not.toBe(includedMinimizer);
39+
expect(wrappedConfig.optimization.minimizer[1]).toBe(excludedMinimizer);
40+
});
41+
42+
it("should allow excluding plugins by constructor", () => {
43+
const includedPlugin = new IncludedPlugin();
44+
const excludedPlugin = new ExcludedPlugin();
45+
const smp = new SpeedMeasurePlugin({
46+
excludedPlugins: [ExcludedPlugin],
47+
});
48+
49+
const wrappedConfig = smp.wrap({
50+
plugins: [includedPlugin, excludedPlugin],
51+
});
52+
53+
expect(wrappedConfig.plugins[0]).not.toBe(includedPlugin);
54+
expect(wrappedConfig.plugins[1]).toBe(excludedPlugin);
55+
});
56+
57+
it("should allow excluding plugins by regex", () => {
58+
const includedPlugin = new IncludedPlugin();
59+
const excludedPlugin = new ExcludedPlugin();
60+
const smp = new SpeedMeasurePlugin({
61+
excludedPlugins: [/Excluded/],
62+
});
63+
64+
const wrappedConfig = smp.wrap({
65+
plugins: [includedPlugin, excludedPlugin],
66+
});
67+
68+
expect(wrappedConfig.plugins[0]).not.toBe(includedPlugin);
69+
expect(wrappedConfig.plugins[1]).toBe(excludedPlugin);
70+
});
71+
72+
it("should allow excluding plugins by custom pluginNames alias", () => {
73+
const aliasedPlugin = new ExcludedPlugin();
74+
const smp = new SpeedMeasurePlugin({
75+
pluginNames: {
76+
customPluginName: aliasedPlugin,
77+
},
78+
excludedPlugins: ["customPluginName"],
79+
});
80+
81+
const wrappedConfig = smp.wrap({
82+
plugins: [aliasedPlugin],
83+
});
84+
85+
expect(wrappedConfig.plugins[0]).toBe(aliasedPlugin);
86+
});
87+
88+
it("should skip injecting granular timing for excluded loaders by name", () => {
89+
const smp = new SpeedMeasurePlugin({
90+
granularLoaderData: true,
91+
excludedLoaders: ["babel-loader"],
92+
});
93+
94+
const wrappedConfig = smp.wrap({
95+
module: {
96+
rules: [{ use: ["babel-loader"] }, { use: ["file-loader"] }],
97+
},
98+
});
99+
100+
expect(wrappedConfig.module.rules[0].use).toEqual(["babel-loader"]);
101+
expect(wrappedConfig.module.rules[1].use).toEqual([
102+
"speed-measure-webpack-plugin/loader",
103+
"file-loader",
104+
]);
105+
});
106+
107+
it("should skip injecting granular timing for excluded loaders by regex", () => {
108+
const smp = new SpeedMeasurePlugin({
109+
granularLoaderData: true,
110+
excludedLoaders: [/css-loader/],
111+
});
112+
113+
const wrappedConfig = smp.wrap({
114+
module: {
115+
rules: [
116+
{
117+
use: ["style-loader", "/tmp/node_modules/css-loader/dist/cjs.js"],
118+
},
119+
{ use: ["file-loader"] },
120+
],
121+
},
122+
});
123+
124+
expect(wrappedConfig.module.rules[1].use).toEqual([
125+
"speed-measure-webpack-plugin/loader",
126+
"file-loader",
127+
]);
128+
expect(wrappedConfig.module.rules[0].use).toEqual([
129+
"style-loader",
130+
"/tmp/node_modules/css-loader/dist/cjs.js",
131+
]);
132+
});
133+
134+
it('should leave the webpack 5 "..." minimizer sentinel untouched', () => {
135+
const smp = new SpeedMeasurePlugin();
136+
137+
const wrappedConfig = smp.wrap({
138+
optimization: {
139+
minimizer: ["..."],
140+
},
141+
});
142+
143+
expect(wrappedConfig.optimization.minimizer).toEqual(["..."]);
144+
});
145+
});

0 commit comments

Comments
 (0)