Skip to content

Commit 8f3fadb

Browse files
perf: fix
1 parent 5d54128 commit 8f3fadb

File tree

4 files changed

+291
-90
lines changed

4 files changed

+291
-90
lines changed

src/index.js

+76-81
Original file line numberDiff line numberDiff line change
@@ -104,86 +104,72 @@ class CompressionPlugin {
104104

105105
async compress(compiler, compilation, assets) {
106106
const cache = compilation.getCache("CompressionWebpackPlugin");
107-
const assetsForMinify = await Promise.all(
108-
Object.keys(assets).reduce((accumulator, name) => {
109-
const { info, source } = compilation.getAsset(name);
107+
const assetsForMinify = (
108+
await Promise.all(
109+
Object.keys(assets).map(async (name) => {
110+
const { info, source } = compilation.getAsset(name);
110111

111-
// Skip double minimize assets from child compilation
112-
if (info.compressed) {
113-
return accumulator;
114-
}
115-
116-
if (
117-
!compiler.webpack.ModuleFilenameHelpers.matchObject.bind(
118-
// eslint-disable-next-line no-undefined
119-
undefined,
120-
this.options
121-
)(name)
122-
) {
123-
return accumulator;
124-
}
125-
126-
let input = source.source();
112+
if (info.compressed) {
113+
return false;
114+
}
127115

128-
if (!Buffer.isBuffer(input)) {
129-
input = Buffer.from(input);
130-
}
116+
if (
117+
!compiler.webpack.ModuleFilenameHelpers.matchObject.bind(
118+
// eslint-disable-next-line no-undefined
119+
undefined,
120+
this.options
121+
)(name)
122+
) {
123+
return false;
124+
}
131125

132-
if (input.length < this.options.threshold) {
133-
return accumulator;
134-
}
126+
let relatedName;
135127

136-
let relatedName;
128+
if (typeof this.options.algorithm === "function") {
129+
let filenameForRelatedName = this.options.filename;
137130

138-
if (typeof this.options.algorithm === "function") {
139-
let filenameForRelatedName = this.options.filename;
131+
const index = filenameForRelatedName.indexOf("?");
140132

141-
const index = filenameForRelatedName.lastIndexOf("?");
133+
if (index >= 0) {
134+
filenameForRelatedName = filenameForRelatedName.substr(0, index);
135+
}
142136

143-
if (index >= 0) {
144-
filenameForRelatedName = filenameForRelatedName.substr(0, index);
137+
relatedName = `${path.extname(filenameForRelatedName).slice(1)}ed`;
138+
} else if (this.options.algorithm === "gzip") {
139+
relatedName = "gzipped";
140+
} else {
141+
relatedName = `${this.options.algorithm}ed`;
145142
}
146143

147-
relatedName = `${path.extname(filenameForRelatedName).slice(1)}ed`;
148-
} else if (this.options.algorithm === "gzip") {
149-
relatedName = "gzipped";
150-
} else {
151-
relatedName = `${this.options.algorithm}ed`;
152-
}
144+
if (info.related && info.related[relatedName]) {
145+
return false;
146+
}
153147

154-
if (info.related && info.related[relatedName]) {
155-
return accumulator;
156-
}
148+
const cacheItem = cache.getItemCache(
149+
serialize({
150+
name,
151+
algorithm: this.options.algorithm,
152+
compressionOptions: this.options.compressionOptions,
153+
}),
154+
cache.getLazyHashedEtag(source)
155+
);
156+
const output = (await cacheItem.getPromise()) || {};
157157

158-
const eTag = cache.getLazyHashedEtag(source);
159-
const cacheItem = cache.getItemCache(
160-
serialize({
161-
name,
162-
algorithm: this.options.algorithm,
163-
compressionOptions: this.options.compressionOptions,
164-
}),
165-
eTag
166-
);
158+
let buffer;
167159

168-
accumulator.push(
169-
(async () => {
170-
const output = await cacheItem.getPromise();
160+
// No need original buffer for cached files
161+
if (!output.source) {
162+
buffer = source.buffer();
171163

172-
return {
173-
name,
174-
inputSource: source,
175-
info,
176-
input,
177-
output,
178-
cacheItem,
179-
relatedName,
180-
};
181-
})()
182-
);
164+
if (buffer.length < this.options.threshold) {
165+
return false;
166+
}
167+
}
183168

184-
return accumulator;
185-
}, [])
186-
);
169+
return { name, source, info, buffer, output, cacheItem, relatedName };
170+
})
171+
)
172+
).filter((assetForMinify) => Boolean(assetForMinify));
187173

188174
const { RawSource } = compiler.webpack.sources;
189175
const scheduledTasks = [];
@@ -193,28 +179,37 @@ class CompressionPlugin {
193179
(async () => {
194180
const {
195181
name,
196-
inputSource,
197-
input,
182+
source,
183+
buffer,
184+
output,
198185
cacheItem,
199186
info,
200187
relatedName,
201188
} = asset;
202-
let { output } = asset;
203189

204-
if (!output) {
205-
try {
206-
output = new RawSource(await this.runCompressionAlgorithm(input));
207-
} catch (error) {
208-
compilation.errors.push(error);
190+
if (!output.source) {
191+
if (!output.compressed) {
192+
try {
193+
output.compressed = await this.runCompressionAlgorithm(buffer);
194+
} catch (error) {
195+
compilation.errors.push(error);
196+
197+
return;
198+
}
199+
}
200+
201+
if (
202+
output.compressed.length / buffer.length >
203+
this.options.minRatio
204+
) {
205+
await cacheItem.storePromise({ compressed: output.compressed });
209206

210207
return;
211208
}
212209

213-
await cacheItem.storePromise(output);
214-
}
210+
output.source = new RawSource(output.compressed);
215211

216-
if (output.source().length / input.length > this.options.minRatio) {
217-
return;
212+
await cacheItem.storePromise(output);
218213
}
219214

220215
const match = /^([^?#]*)(\?[^#]*)?(#.*)?$/.exec(name);
@@ -260,19 +255,19 @@ class CompressionPlugin {
260255

261256
if (this.options.deleteOriginalAssets) {
262257
if (this.options.deleteOriginalAssets === "keep-source-map") {
263-
compilation.updateAsset(name, inputSource, {
258+
compilation.updateAsset(name, source, {
264259
related: { sourceMap: null },
265260
});
266261
}
267262

268263
compilation.deleteAsset(name);
269264
} else {
270-
compilation.updateAsset(name, inputSource, {
265+
compilation.updateAsset(name, source, {
271266
related: { [relatedName]: newName },
272267
});
273268
}
274269

275-
compilation.emitAsset(newName, output, newInfo);
270+
compilation.emitAsset(newName, output.source, newInfo);
276271
})()
277272
);
278273
}

test/CompressionPlugin.test.js

+50-1
Original file line numberDiff line numberDiff line change
@@ -294,7 +294,56 @@ describe("CompressionPlugin", () => {
294294
expect(getWarnings(stats)).toMatchSnapshot("warnings");
295295
expect(getErrors(stats)).toMatchSnapshot("errors");
296296

297-
new ModifyExistingAsset({ name: "main.js" }).apply(compiler);
297+
new ModifyExistingAsset({
298+
name: "main.js",
299+
content: "function changed() { /*! CHANGED */ }",
300+
}).apply(compiler);
301+
302+
await new Promise(async (resolve) => {
303+
const newStats = await compile(compiler);
304+
305+
expect(newStats.compilation.emittedAssets.size).toBe(2);
306+
307+
expect(getAssetsNameAndSize(newStats, compiler)).toMatchSnapshot(
308+
"assets"
309+
);
310+
expect(getWarnings(newStats)).toMatchSnapshot("errors");
311+
expect(getErrors(newStats)).toMatchSnapshot("warnings");
312+
313+
resolve();
314+
});
315+
});
316+
317+
it('should work and use memory cache when the "cache" option is "true" and the asset has been changed which filtered by the "minRation" option', async () => {
318+
const compiler = getCompiler(
319+
"./entry.js",
320+
{
321+
name: "[name].[ext]",
322+
},
323+
{
324+
cache: true,
325+
output: {
326+
path: path.resolve(__dirname, "./outputs"),
327+
filename: "[name].js",
328+
chunkFilename: "[id].js",
329+
},
330+
}
331+
);
332+
333+
new CompressionPlugin().apply(compiler);
334+
335+
const stats = await compile(compiler);
336+
337+
expect(stats.compilation.emittedAssets.size).toBe(7);
338+
339+
expect(getAssetsNameAndSize(stats, compiler)).toMatchSnapshot("assets");
340+
expect(getWarnings(stats)).toMatchSnapshot("warnings");
341+
expect(getErrors(stats)).toMatchSnapshot("errors");
342+
343+
new ModifyExistingAsset({
344+
name: "icon.png",
345+
content: `1q!Q2w@W3e#e4r$r`.repeat(1000),
346+
}).apply(compiler);
298347

299348
await new Promise(async (resolve) => {
300349
const newStats = await compile(compiler);

0 commit comments

Comments
 (0)