Skip to content

Commit

Permalink
refactor(core): add a new stringifier for runtime errors which includ…
Browse files Browse the repository at this point in the history
…es debug info such as the file path and line number (#51919)

The current error stringifier only includes the class name. In this change a new stringifier is added which returns a more helpful string which includes the file path and line number. Note that this is only the case with components, and for other class types (directive, pipes) it will fallback to the current stringifier. Subsequent changes can cover the case of directive and pipes as well.

PR Close #51919
  • Loading branch information
pmvald authored and atscott committed Oct 9, 2023
1 parent 9e7d243 commit f12e1ef
Show file tree
Hide file tree
Showing 2 changed files with 80 additions and 0 deletions.
31 changes: 31 additions & 0 deletions packages/core/src/render3/util/stringify_utils.ts
Expand Up @@ -6,6 +6,9 @@
* found in the LICENSE file at https://angular.io/license
*/

import {Type} from '../../interface/type';
import {NG_COMP_DEF} from '../fields';

/**
* Used for stringify render output in Ivy.
* Important! This function is very performance-sensitive and we should
Expand All @@ -23,6 +26,7 @@ export function renderStringify(value: any): string {

/**
* Used to stringify a value so that it can be displayed in an error message.
*
* Important! This function contains a megamorphic read and should only be
* used for error messages.
*/
Expand All @@ -34,3 +38,30 @@ export function stringifyForError(value: any): string {

return renderStringify(value);
}

/**
* Used to stringify a `Type` and including the file path and line number in which it is defined, if
* possible, for better debugging experience.
*
* Important! This function contains a megamorphic read and should only be used for error messages.
*/
export function debugStringifyTypeForError(type: Type<any>): string {
// TODO(pmvald): Do some refactoring so that we can use getComponentDef here without creating
// circular deps.
let componentDef = (type as any)[NG_COMP_DEF] || null;
if (componentDef !== null && componentDef.debugInfo) {
return stringifyTypeFromDebugInfo(componentDef.debugInfo);
}

return stringifyForError(type);
}

// TODO(pmvald): Do some refactoring so that we can use the type ClassDebugInfo for the param
// debugInfo here without creating circular deps.
function stringifyTypeFromDebugInfo(debugInfo: any): string {
if (!debugInfo.filePath || !debugInfo.lineNumber) {
return debugInfo.className;
} else {
return `${debugInfo.className} (at ${debugInfo.filePath}:${debugInfo.lineNumber})`;
}
}
49 changes: 49 additions & 0 deletions packages/core/test/render3/util/stringify_util_spec.ts
@@ -0,0 +1,49 @@
/**
* @license
* Copyright Google LLC All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/

import {ɵsetClassDebugInfo, ɵɵdefineComponent} from '@angular/core/src/render3';
import {debugStringifyTypeForError} from '@angular/core/src/render3/util/stringify_utils';

describe('stringify utils', () => {
describe('stringifyTypeForError util', () => {
it('should include the file path and line number for component if debug info includes them',
() => {
class Comp {
static ɵcmp = ɵɵdefineComponent({type: Comp, decls: 0, vars: 0, template: () => ''});
}
ɵsetClassDebugInfo(Comp, {
className: 'Comp',
filePath: 'comp.ts',
lineNumber: 11,
});

expect(debugStringifyTypeForError(Comp)).toBe('Comp (at comp.ts:11)');
});

it('should include only the class name if debug info does not contain file path', () => {
class Comp {
static ɵcmp = ɵɵdefineComponent({type: Comp, decls: 0, vars: 0, template: () => ''});
}
ɵsetClassDebugInfo(Comp, {
className: 'Comp',
lineNumber: 11,
});

expect(debugStringifyTypeForError(Comp)).toBe('Comp');
});

it('should default to showing just the class name for component if debug info is not available',
() => {
class Comp {
static ɵcmp = ɵɵdefineComponent({type: Comp, decls: 0, vars: 0, template: () => ''});
}

expect(debugStringifyTypeForError(Comp)).toBe('Comp');
});
});
});

0 comments on commit f12e1ef

Please sign in to comment.