Skip to content

Commit 282baff

Browse files
alan-agius4clydin
authored andcommitted
fix(@angular-devkit/build-angular): hide stacktraces from webpack errors
In many cases Webpack will output errors with stacktraces even when `errorStack` is configured to false which bloats the terminal and makes the actual error harder to find. With this change we output stacktraces only when using the `verbose` option. Before ``` $ ng build ./src/styles.scss.webpack[javascript/auto]!=!./node_modules/css-loader/dist/cjs.js??ruleSet[1].rules[5].rules[0].oneOf[0].use[1]!./node_modules/postcss-loader/dist/cjs.js??ruleSet[1].rules[5].rules[0].oneOf[0].use[2]!./node_modules/resolve-url-loader/index.js??ruleSet[1].rules[5].rules[1].use[0]!./node_modules/sass-loader/dist/cjs.js??ruleSet[1].rules[5].rules[1].use[1]!./src/styles.scss - Error: Module build failed (from ./node_modules/sass-loader/dist/cjs.js): SassError: Can't find stylesheet to import. ╷ 1 │ @import "invalid"; │ ^^^^^^^^^ ╵ src/styles.scss 1:9 root stylesheet ./src/styles.scss - Error: Module build failed (from ./node_modules/mini-css-extract-plugin/dist/loader.js): HookWebpackError: Module build failed (from ./node_modules/sass-loader/dist/cjs.js): SassError: Can't find stylesheet to import. ╷ 1 │ @import "invalid"; │ ^^^^^^^^^ ╵ src/styles.scss 1:9 root stylesheet at tryRunOrWebpackError (/usr/local/xxxxxxx/cli-reproductions/showwcase-v14-rc0/node_modules/webpack/lib/HookWebpackError.js:88:9) at __webpack_require_module__ (/usr/local/xxxxxxx/cli-reproductions/showwcase-v14-rc0/node_modules/webpack/lib/Compilation.js:5051:12) at __webpack_require__ (/usr/local/xxxxxxx/cli-reproductions/showwcase-v14-rc0/node_modules/webpack/lib/Compilation.js:5008:18) at /usr/local/xxxxxxx/cli-reproductions/showwcase-v14-rc0/node_modules/webpack/lib/Compilation.js:5079:20 at symbolIterator (/usr/local/xxxxxxx/cli-reproductions/showwcase-v14-rc0/node_modules/neo-async/async.js:3485:9) at done (/usr/local/xxxxxxx/cli-reproductions/showwcase-v14-rc0/node_modules/neo-async/async.js:3527:9) at Hook.eval [as callAsync] (eval at create (/usr/local/xxxxxxx/cli-reproductions/showwcase-v14-rc0/node_modules/tapable/lib/HookCodeFactory.js:33:10), <anonymous>:15:1) at Hook.CALL_ASYNC_DELEGATE [as _callAsync] (/usr/local/xxxxxxx/cli-reproductions/showwcase-v14-rc0/node_modules/tapable/lib/Hook.js:18:14) at /usr/local/xxxxxxx/cli-reproductions/showwcase-v14-rc0/node_modules/webpack/lib/Compilation.js:4986:43 at symbolIterator (/usr/local/xxxxxxx/cli-reproductions/showwcase-v14-rc0/node_modules/neo-async/async.js:3482:9) -- inner error -- Error: Module build failed (from ./node_modules/sass-loader/dist/cjs.js): SassError: Can't find stylesheet to import. ╷ 1 │ @import "invalid"; │ ^^^^^^^^^ ╵ src/styles.scss 1:9 root stylesheet at Object.<anonymous> (/usr/local/xxxxxxx/cli-reproductions/showwcase-v14-rc0/node_modules/css-loader/dist/cjs.js??ruleSet[1].rules[5].rules[0].oneOf[0].use[1]!/usr/local/xxxxxxx/cli-reproductions/showwcase-v14-rc0/node_modules/postcss-loader/dist/cjs.js??ruleSet[1].rules[5].rules[0].oneOf[0].use[2]!/usr/local/xxxxxxx/cli-reproductions/showwcase-v14-rc0/node_modules/resolve-url-loader/index.js??ruleSet[1].rules[5].rules[1].use[0]!/usr/local/xxxxxxx/cli-reproductions/showwcase-v14-rc0/node_modules/sass-loader/dist/cjs.js??ruleSet[1].rules[5].rules[1].use[1]!/usr/local/xxxxxxx/cli-reproductions/showwcase-v14-rc0/src/styles.scss:1:7) at /usr/local/xxxxxxx/cli-reproductions/showwcase-v14-rc0/node_modules/webpack/lib/javascript/JavascriptModulesPlugin.js:441:11 at Hook.eval [as call] (eval at create (/usr/local/xxxxxxx/cli-reproductions/showwcase-v14-rc0/node_modules/tapable/lib/HookCodeFactory.js:19:10), <anonymous>:7:1) at Hook.CALL_DELEGATE [as _call] (/usr/local/xxxxxxx/cli-reproductions/showwcase-v14-rc0/node_modules/tapable/lib/Hook.js:14:14) at /usr/local/xxxxxxx/cli-reproductions/showwcase-v14-rc0/node_modules/webpack/lib/Compilation.js:5053:39 at tryRunOrWebpackError (/usr/local/xxxxxxx/cli-reproductions/showwcase-v14-rc0/node_modules/webpack/lib/HookWebpackError.js:83:7) at __webpack_require_module__ (/usr/local/xxxxxxx/cli-reproductions/showwcase-v14-rc0/node_modules/webpack/lib/Compilation.js:5051:12) at __webpack_require__ (/usr/local/xxxxxxx/cli-reproductions/showwcase-v14-rc0/node_modules/webpack/lib/Compilation.js:5008:18) at /usr/local/xxxxxxx/cli-reproductions/showwcase-v14-rc0/node_modules/webpack/lib/Compilation.js:5079:20 at symbolIterator (/usr/local/xxxxxxx/cli-reproductions/showwcase-v14-rc0/node_modules/neo-async/async.js:3485:9) Generated code for /usr/local/xxxxxxx/cli-reproductions/showwcase-v14-rc0/node_modules/css-loader/dist/cjs.js??ruleSet[1].rules[5].rules[0].oneOf[0].use[1]!/usr/local/xxxxxxx/cli-reproductions/showwcase-v14-rc0/node_modules/postcss-loader/dist/cjs.js??ruleSet[1].rules[5].rules[0].oneOf[0].use[2]!/usr/local/xxxxxxx/cli-reproductions/showwcase-v14-rc0/node_modules/resolve-url-loader/index.js??ruleSet[1].rules[5].rules[1].use[0]!/usr/local/xxxxxxx/cli-reproductions/showwcase-v14-rc0/node_modules/sass-loader/dist/cjs.js??ruleSet[1].rules[5].rules[1].use[1]!/usr/local/xxxxxxx/cli-reproductions/showwcase-v14-rc0/src/styles.scss 1 | throw new Error("Module build failed (from ./node_modules/sass-loader/dist/cjs.js):\nSassError: Can't find stylesheet to import.\n ╷\n1 │ @import \"invalid\";\n │ ^^^^^^^^^\n ╵\n src/styles.scss 1:9 root stylesheet"); ``` After ``` $ ng build ./src/styles.scss - Error: Module build failed (from ./node_modules/sass-loader/dist/cjs.js): SassError: Can't find stylesheet to import. ╷ 1 │ @import "invalid"; │ ^^^^^^^^^ ╵ src/styles.scss 1:9 root stylesheet ./src/styles.scss - Error: Module build failed (from ./node_modules/mini-css-extract-plugin/dist/loader.js): HookWebpackError: Module build failed (from ./node_modules/sass-loader/dist/cjs.js): SassError: Can't find stylesheet to import. ╷ 1 │ @import "invalid"; │ ^^^^^^^^^ ╵ src/styles.scss 1:9 root stylesheet ``` (cherry picked from commit b40aeed)
1 parent 694b73d commit 282baff

File tree

3 files changed

+92
-9
lines changed

3 files changed

+92
-9
lines changed

packages/angular_devkit/build_angular/src/builders/browser/tests/options/verbose_spec.ts

+54
Original file line numberDiff line numberDiff line change
@@ -102,5 +102,59 @@ describeBuilder(buildWebpackBrowser, BROWSER_BUILDER_INFO, (harness) => {
102102
)
103103
.toPromise();
104104
});
105+
106+
it('should not include error stacktraces when false', async () => {
107+
harness.useTarget('build', {
108+
...BASE_OPTIONS,
109+
verbose: false,
110+
styles: ['./src/styles.scss'],
111+
});
112+
113+
// Create a compilatation error.
114+
await harness.writeFile('./src/styles.scss', `@import 'invalid-module';`);
115+
116+
const { result, logs } = await harness.executeOnce({ outputLogsOnFailure: false });
117+
expect(result?.success).toBeFalse();
118+
expect(logs).toContain(
119+
jasmine.objectContaining<logging.LogEntry>({
120+
message: jasmine.stringMatching(`SassError: Can't find stylesheet to import.`),
121+
}),
122+
);
123+
expect(logs).not.toContain(
124+
jasmine.objectContaining<logging.LogEntry>({
125+
message: jasmine.stringMatching('styles.scss.webpack'),
126+
}),
127+
);
128+
expect(logs).not.toContain(
129+
jasmine.objectContaining<logging.LogEntry>({
130+
message: jasmine.stringMatching('at Object.loader'),
131+
}),
132+
);
133+
});
134+
135+
it('should include error stacktraces when true', async () => {
136+
harness.useTarget('build', {
137+
...BASE_OPTIONS,
138+
verbose: true,
139+
styles: ['./src/styles.scss'],
140+
});
141+
142+
// Create a compilatation error.
143+
await harness.writeFile('./src/styles.scss', `@import 'invalid-module';`);
144+
145+
const { result, logs } = await harness.executeOnce({ outputLogsOnFailure: false });
146+
expect(result?.success).toBeFalse();
147+
148+
expect(logs).toContain(
149+
jasmine.objectContaining<logging.LogEntry>({
150+
message: jasmine.stringMatching('styles.scss.webpack'),
151+
}),
152+
);
153+
expect(logs).toContain(
154+
jasmine.objectContaining<logging.LogEntry>({
155+
message: jasmine.stringMatching('at Object.loader'),
156+
}),
157+
);
158+
});
105159
});
106160
});

packages/angular_devkit/build_angular/src/webpack/utils/helpers.ts

+3-1
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ export interface HashFormat {
2929
script: string;
3030
}
3131

32+
export type WebpackStatsOptions = Exclude<Configuration['stats'], string | boolean | undefined>;
33+
3234
export function getOutputHashFormat(outputHashing = OutputHashing.None, length = 20): HashFormat {
3335
const hashTemplate = `.[contenthash:${length}]`;
3436

@@ -276,7 +278,6 @@ export function externalizePackages(
276278
}
277279
}
278280

279-
type WebpackStatsOptions = Exclude<Configuration['stats'], string | boolean>;
280281
export function getStatsOptions(verbose = false): WebpackStatsOptions {
281282
const webpackOutputOptions: WebpackStatsOptions = {
282283
all: false, // Fallback value for stats options when an option is not defined. It has precedence over local webpack defaults.
@@ -306,6 +307,7 @@ export function getStatsOptions(verbose = false): WebpackStatsOptions {
306307
version: true,
307308
chunkModules: true,
308309
errorDetails: true,
310+
errorStack: true,
309311
moduleTrace: true,
310312
logging: 'verbose',
311313
modulesSpace: Infinity,

packages/angular_devkit/build_angular/src/webpack/utils/stats.ts

+35-8
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import { Schema as BrowserBuilderOptions } from '../../builders/browser/schema';
1515
import { BudgetCalculatorResult } from '../../utils/bundle-calculator';
1616
import { colors as ansiColors, removeColor } from '../../utils/color';
1717
import { markAsyncChunksNonInitial } from './async-chunks';
18-
import { getStatsOptions, normalizeExtraEntryPoints } from './helpers';
18+
import { WebpackStatsOptions, getStatsOptions, normalizeExtraEntryPoints } from './helpers';
1919

2020
export function formatSize(size: number): string {
2121
if (size <= 0) {
@@ -317,8 +317,10 @@ function statsToString(
317317
}
318318
}
319319

320-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
321-
export function statsWarningsToString(json: StatsCompilation, statsConfig: any): string {
320+
export function statsWarningsToString(
321+
json: StatsCompilation,
322+
statsConfig: WebpackStatsOptions,
323+
): string {
322324
const colors = statsConfig.colors;
323325
const c = (x: string) => (colors ? ansiColors.reset.cyan(x) : x);
324326
const y = (x: string) => (colors ? ansiColors.reset.yellow(x) : x);
@@ -352,8 +354,10 @@ export function statsWarningsToString(json: StatsCompilation, statsConfig: any):
352354
return output ? '\n' + output : output;
353355
}
354356

355-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
356-
export function statsErrorsToString(json: StatsCompilation, statsConfig: any): string {
357+
export function statsErrorsToString(
358+
json: StatsCompilation,
359+
statsConfig: WebpackStatsOptions,
360+
): string {
357361
const colors = statsConfig.colors;
358362
const c = (x: string) => (colors ? ansiColors.reset.cyan(x) : x);
359363
const yb = (x: string) => (colors ? ansiColors.reset.yellowBright(x) : x);
@@ -369,18 +373,36 @@ export function statsErrorsToString(json: StatsCompilation, statsConfig: any): s
369373
if (typeof error === 'string') {
370374
output += r(`Error: ${error}\n\n`);
371375
} else {
372-
const file = error.file || error.moduleName;
376+
let file = error.file || error.moduleName;
377+
// Clean up error paths
378+
// Ex: ./src/app/styles.scss.webpack[javascript/auto]!=!./node_modules/css-loader/dist/cjs.js....
379+
// to ./src/app/styles.scss.webpack
380+
if (file && !statsConfig.errorDetails) {
381+
const webpackPathIndex = file.indexOf('.webpack[');
382+
if (webpackPathIndex !== -1) {
383+
file = file.substring(0, webpackPathIndex);
384+
}
385+
}
386+
373387
if (file) {
374388
output += c(file);
375389
if (error.loc) {
376390
output += ':' + yb(error.loc);
377391
}
378392
output += ' - ';
379393
}
380-
if (!/^error/i.test(error.message)) {
394+
395+
// In most cases webpack will add stack traces to error messages.
396+
// This below cleans up the error from stacks.
397+
// See: https://github.com/webpack/webpack/issues/15980
398+
const message = statsConfig.errorStack
399+
? error.message
400+
: /[\s\S]+?(?=[\n\s]+at)/.exec(error.message)?.[0] ?? error.message;
401+
402+
if (!/^error/i.test(message)) {
381403
output += r('Error: ');
382404
}
383-
output += `${error.message}\n\n`;
405+
output += `${message}\n\n`;
384406
}
385407
}
386408

@@ -428,9 +450,14 @@ export function webpackStatsLogger(
428450
): void {
429451
logger.info(statsToString(json, config.stats, budgetFailures));
430452

453+
if (typeof config.stats !== 'object') {
454+
throw new Error('Invalid Webpack stats configuration.');
455+
}
456+
431457
if (statsHasWarnings(json)) {
432458
logger.warn(statsWarningsToString(json, config.stats));
433459
}
460+
434461
if (statsHasErrors(json)) {
435462
logger.error(statsErrorsToString(json, config.stats));
436463
}

0 commit comments

Comments
 (0)