Skip to content

Commit b736099

Browse files
fix: source map generation for multiple minify functions
1 parent c545405 commit b736099

13 files changed

+523
-185
lines changed

src/index.js

Lines changed: 82 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ class CssMinimizerPlugin {
5353
);
5454
}
5555

56-
static buildError(error, name, requestShortener, sourceMap) {
56+
static buildError(error, name, sourceMap, requestShortener) {
5757
let builtError;
5858

5959
if (error.line) {
@@ -148,7 +148,17 @@ class CssMinimizerPlugin {
148148
return null;
149149
}
150150

151-
return `Css Minimizer Plugin: ${warningMessage} ${locationMessage}`;
151+
const builtWarning = new Error(
152+
`Css Minimizer Plugin: ${warningMessage}${
153+
locationMessage ? ` ${locationMessage}` : ""
154+
}`
155+
);
156+
157+
builtWarning.name = "Warning";
158+
builtWarning.hideStack = true;
159+
builtWarning.file = file;
160+
161+
return builtWarning;
152162
}
153163

154164
static getAvailableNumberOfCores(parallel) {
@@ -266,8 +276,6 @@ class CssMinimizerPlugin {
266276
if (CssMinimizerPlugin.isSourceMap(map)) {
267277
inputSourceMap = map;
268278
} else {
269-
inputSourceMap = map;
270-
271279
compilation.warnings.push(
272280
new Error(`${name} contains invalid source map`)
273281
);
@@ -286,76 +294,103 @@ class CssMinimizerPlugin {
286294
minifyOptions: this.options.minimizerOptions,
287295
};
288296

297+
let result;
298+
289299
try {
290-
output = await (getWorker
300+
result = await (getWorker
291301
? getWorker().transform(serialize(options))
292302
: minifyFn(options));
293303
} catch (error) {
304+
const hasSourceMap =
305+
inputSourceMap &&
306+
CssMinimizerPlugin.isSourceMap(inputSourceMap);
307+
294308
compilation.errors.push(
295309
CssMinimizerPlugin.buildError(
296310
error,
297311
name,
298-
compilation.requestShortener,
299-
inputSourceMap &&
300-
CssMinimizerPlugin.isSourceMap(inputSourceMap)
312+
hasSourceMap
301313
? new SourceMapConsumer(inputSourceMap)
302-
: null
314+
: // eslint-disable-next-line no-undefined
315+
undefined,
316+
// eslint-disable-next-line no-undefined
317+
hasSourceMap ? compilation.requestShortener : undefined
303318
)
304319
);
305320

306321
return;
307322
}
308323

309-
if (output.map) {
310-
output.source = new SourceMapSource(
311-
output.code,
312-
name,
313-
output.map,
314-
input,
315-
inputSourceMap,
316-
true
317-
);
318-
} else {
319-
output.source = new RawSource(output.code);
324+
output = { warnings: [], errors: [] };
325+
326+
for (const item of result.outputs) {
327+
if (item.map) {
328+
let originalSource;
329+
let innerSourceMap;
330+
331+
if (output.source) {
332+
({ source: originalSource, map: innerSourceMap } =
333+
output.source.sourceAndMap());
334+
} else {
335+
originalSource = input;
336+
innerSourceMap = inputSourceMap;
337+
}
338+
339+
// TODO need API for merging source maps in `webpack-source`
340+
output.source = new SourceMapSource(
341+
item.code,
342+
name,
343+
item.map,
344+
originalSource,
345+
innerSourceMap,
346+
true
347+
);
348+
} else {
349+
output.source = new RawSource(item.code);
350+
}
320351
}
321352

322-
if (output.warnings && output.warnings.length > 0) {
323-
output.warnings = output.warnings
324-
.map((warning) =>
325-
CssMinimizerPlugin.buildWarning(
326-
warning,
327-
name,
328-
inputSourceMap &&
329-
CssMinimizerPlugin.isSourceMap(inputSourceMap)
330-
? new SourceMapConsumer(inputSourceMap)
331-
: null,
332-
compilation.requestShortener,
333-
this.options.warningsFilter
334-
)
335-
)
336-
.filter(Boolean);
353+
if (result.warnings && result.warnings.length > 0) {
354+
const hasSourceMap =
355+
inputSourceMap &&
356+
CssMinimizerPlugin.isSourceMap(inputSourceMap);
357+
358+
for (const warning of result.warnings) {
359+
const buildWarning = CssMinimizerPlugin.buildWarning(
360+
warning,
361+
name,
362+
hasSourceMap
363+
? new SourceMapConsumer(inputSourceMap)
364+
: // eslint-disable-next-line no-undefined
365+
undefined,
366+
// eslint-disable-next-line no-undefined
367+
hasSourceMap ? compilation.requestShortener : undefined,
368+
this.options.warningsFilter
369+
);
370+
371+
if (buildWarning) {
372+
output.warnings.push(buildWarning);
373+
}
374+
}
337375
}
338376

339377
await cacheItem.storePromise({
340378
source: output.source,
341379
warnings: output.warnings,
380+
errors: output.errors,
342381
});
343382
}
344383

345384
if (output.warnings && output.warnings.length > 0) {
346-
output.warnings.forEach((warning) => {
347-
const Warning = class Warning extends Error {
348-
constructor(message) {
349-
super(message);
350-
351-
this.name = "Warning";
352-
this.hideStack = true;
353-
this.file = name;
354-
}
355-
};
385+
for (const warning of output.warnings) {
386+
compilation.warnings.push(warning);
387+
}
388+
}
356389

357-
compilation.warnings.push(new Warning(warning));
358-
});
390+
if (output.errors && output.errors.length > 0) {
391+
for (const error of output.errors) {
392+
compilation.errors.push(error);
393+
}
359394
}
360395

361396
const newInfo = { minimized: true };

src/minify.js

Lines changed: 33 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,31 +2,52 @@ const minify = async (options) => {
22
const minifyFns =
33
typeof options.minify === "function" ? [options.minify] : options.minify;
44

5-
const result = {
6-
code: options.input,
7-
map: options.inputSourceMap,
8-
warnings: [],
9-
};
5+
const result = { outputs: [], warnings: [], errors: [] };
6+
7+
let needSourceMap = false;
108

119
for (let i = 0; i <= minifyFns.length - 1; i++) {
1210
const minifyFn = minifyFns[i];
1311
const minifyOptions = Array.isArray(options.minifyOptions)
1412
? options.minifyOptions[i]
1513
: options.minifyOptions;
14+
const prevResult =
15+
result.outputs.length > 0
16+
? result.outputs[result.outputs.length - 1]
17+
: { code: options.input, map: options.inputSourceMap };
18+
const { code, map } = prevResult;
1619
// eslint-disable-next-line no-await-in-loop
1720
const minifyResult = await minifyFn(
18-
{ [options.name]: result.code },
19-
result.map,
21+
{ [options.name]: code },
22+
map,
2023
minifyOptions
2124
);
2225

23-
result.code = minifyResult.code;
24-
result.map = minifyResult.map;
25-
result.warnings = result.warnings.concat(minifyResult.warnings || []);
26+
if (!minifyResult.code) {
27+
throw new Error("Minimizer function doesn't return result");
28+
}
29+
30+
if (minifyResult.map) {
31+
needSourceMap = true;
32+
}
33+
34+
if (minifyResult.errors) {
35+
result.errors = result.errors.concat(
36+
minifyResult.errors.map((error) => error.toString())
37+
);
38+
}
39+
40+
if (minifyResult.warnings) {
41+
result.warnings = result.warnings.concat(
42+
minifyResult.warnings.map((warning) => warning.toString())
43+
);
44+
}
45+
46+
result.outputs.push({ code: minifyResult.code, map: minifyResult.map });
2647
}
2748

28-
if (result.warnings.length > 0) {
29-
result.warnings = result.warnings.map((warning) => warning.toString());
49+
if (!needSourceMap) {
50+
result.outputs = [result.outputs[result.outputs.length - 1]];
3051
}
3152

3253
return result;

src/utils.js

Lines changed: 31 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,34 @@ async function cssnanoMinify(
44
inputSourceMap,
55
minimizerOptions = { preset: "default" }
66
) {
7+
const load = async (module) => {
8+
let exports;
9+
10+
try {
11+
// eslint-disable-next-line import/no-dynamic-require, global-require
12+
exports = require(module);
13+
14+
return exports;
15+
} catch (requireError) {
16+
let importESM;
17+
18+
try {
19+
// eslint-disable-next-line no-new-func
20+
importESM = new Function("id", "return import(id);");
21+
} catch (e) {
22+
importESM = null;
23+
}
24+
25+
if (requireError.code === "ERR_REQUIRE_ESM" && importESM) {
26+
exports = await importESM(module);
27+
28+
return exports.default;
29+
}
30+
31+
throw requireError;
32+
}
33+
};
34+
735
const [[name, input]] = Object.entries(data);
836
const postcssOptions = {
937
to: name,
@@ -42,10 +70,7 @@ async function cssnanoMinify(
4270
}
4371

4472
if (inputSourceMap) {
45-
postcssOptions.map = {
46-
annotation: false,
47-
prev: inputSourceMap,
48-
};
73+
postcssOptions.map = { annotation: false };
4974
}
5075

5176
// eslint-disable-next-line global-require
@@ -62,56 +87,19 @@ async function cssnanoMinify(
6287
map: result.map && result.map.toString(),
6388
warnings: result.warnings().map(String),
6489
};
65-
66-
async function load(module) {
67-
let exports;
68-
69-
try {
70-
// eslint-disable-next-line import/no-dynamic-require, global-require
71-
exports = require(module);
72-
73-
return exports;
74-
} catch (requireError) {
75-
let importESM;
76-
77-
try {
78-
// eslint-disable-next-line no-new-func
79-
importESM = new Function("id", "return import(id);");
80-
} catch (e) {
81-
importESM = null;
82-
}
83-
84-
if (requireError.code === "ERR_REQUIRE_ESM" && importESM) {
85-
exports = await importESM(module);
86-
87-
return exports.default;
88-
}
89-
90-
throw requireError;
91-
}
92-
}
9390
}
9491

9592
/* istanbul ignore next */
9693
async function cssoMinify(data, inputSourceMap, minimizerOptions) {
9794
// eslint-disable-next-line global-require,import/no-extraneous-dependencies
9895
const csso = require("csso");
99-
// eslint-disable-next-line global-require
100-
const sourcemap = require("source-map");
10196
const [[filename, input]] = Object.entries(data);
10297
const result = csso.minify(input, {
10398
filename,
104-
sourceMap: inputSourceMap,
99+
sourceMap: Boolean(inputSourceMap),
105100
...minimizerOptions,
106101
});
107102

108-
if (inputSourceMap) {
109-
result.map.applySourceMap(
110-
new sourcemap.SourceMapConsumer(inputSourceMap),
111-
filename
112-
);
113-
}
114-
115103
return {
116104
code: result.css,
117105
map: result.map && result.map.toJSON(),
@@ -124,7 +112,7 @@ async function cleanCssMinify(data, inputSourceMap, minimizerOptions) {
124112
const CleanCSS = require("clean-css");
125113
const [[name, input]] = Object.entries(data);
126114
const result = await new CleanCSS({
127-
sourceMap: inputSourceMap,
115+
sourceMap: Boolean(inputSourceMap),
128116
...minimizerOptions,
129117
}).minify({ [name]: { styles: input } });
130118

test/CssMinimizerPlugin.test.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -167,8 +167,8 @@ describe("CssMinimizerPlugin", () => {
167167
CssMinimizerPlugin.buildError(
168168
otherErrorWithLineAndCol,
169169
"test.css",
170-
new RequestShortener("/example.com/www/js/"),
171-
new SourceMapConsumer(rawSourceMap)
170+
new SourceMapConsumer(rawSourceMap),
171+
new RequestShortener("/example.com/www/js/")
172172
)
173173
).toMatchSnapshot();
174174

0 commit comments

Comments
 (0)