Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Include component code location in runtime error messages #51919

Closed
wants to merge 6 commits into from

Conversation

pmvald
Copy link
Member

@pmvald pmvald commented Sep 26, 2023

Currently the runtime error messages only include the name of the Type in the error. It is up to the dev to code search and find the class. This change provides infra tools by which we can include code location info (file path and line number) to error messages. This change focuses on components, but the same can be done for directives and pipes as well. This PR uses the new infra tools to revamp the "orphan component" error message (NG01001). Subsequent PRs can migrate more error messages.

Note that this feature is only supported in full and local compilation modes for now. Supporting it in partial mode needs more discussion.

PR Checklist

Please check if your PR fulfills the following requirements:

PR Type

What kind of change does this PR introduce?

  • Bugfix
  • Feature
  • Code style update (formatting, local variables)
  • Refactoring (no functional changes, no api changes)
  • Build related changes
  • CI related changes
  • Documentation content changes
  • angular.io application / infrastructure changes
  • Other... Please describe:

What is the current behavior?

Issue Number: N/A

What is the new behavior?

Some errors contain file path and line number of the components which are part of the error

Does this PR introduce a breaking change?

  • Yes
  • No

Other information

@angular-robot angular-robot bot added the detected: feature PR contains a feature commit label Sep 26, 2023
@pmvald pmvald force-pushed the runtime-debug-info branch 2 times, most recently from ed89607 to eb48386 Compare September 27, 2023 03:01
@pmvald pmvald added action: review The PR is still awaiting reviews from at least one requested reviewer area: core Issues related to the framework runtime area: compiler Issues related to `ngc`, Angular's template compiler target: minor This PR is targeted for the next minor release PullApprove: disable labels Sep 27, 2023
@ngbot ngbot bot added this to the Backlog milestone Sep 27, 2023
@pmvald pmvald changed the title Runtime debug info Include component code location in runtime error messages Sep 27, 2023
@pmvald pmvald marked this pull request as ready for review September 27, 2023 04:58
Copy link
Member

@devversion devversion left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM overall, but some comments

packages/core/src/render3/util/stringify_utils.ts Outdated Show resolved Hide resolved
packages/core/src/render3/util/stringify_utils.ts Outdated Show resolved Hide resolved
for (const rootDir of rootDirs) {
const rel = path.relative(rootDir, filePath);
if (!rel.startsWith('..')) {
return path.join('$PROJECT_ROOT', rel);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you extract this in to a constant?

Also: Do we actually need this? I think we can just always assume paths in DebugInfo#filePath to be relative to the project root.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah removed this. For now we assume all paths are relative, unless we fail to resolve, in which case they will be absolute.

This is where g3 and 3P diverge. In g3 it makes perfect sense to replace $PROJECT_ROOT with either google3 or an addition /, and then the path will look so familiar. In 3P probably a relative path makes the most sense.

Maybe as an improvement in the future, if this debug info happened to be very popular we can add an field to options fir the path prefix, and in g3 we can set it to google3 or something and leave it undefined in 3P to just have relative paths.

@pmvald pmvald force-pushed the runtime-debug-info branch 3 times, most recently from 125d1ff to ef003eb Compare September 27, 2023 22:56


(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(MainStandalone, { className: "MainStandalone", filePath: "debug_info.ts", lineNumber: 13 }); })();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm wondering if there isn't some security risk here since we're exposing details about the local file system in the JS bundle.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Interesting. good point to talk about. In production people would not see this- especially for Angular CLI where ngDevMode is properly set to false for production builds. I know there is a risk of not setting this up properly/or using the wrong build. Notably though, source maps would impose the same risk for dev-mode (or even prod)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sourcemaps would indeed provide similar information. However, source maps are separate generated files that have explicit build options.

One area that should probably be tested here is that the file paths are always relative to the project root. Absolute paths should never be present here.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Source maps could also be inline here I'd assume. The generated code would also be stripped by the optimization build option. I agree though that the file paths should never be absolute

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Modifed to either show the relative path or null, no absolute path

@AndrewKushnir AndrewKushnir requested review from alxhub and removed request for AndrewKushnir October 2, 2023 21:18
@pmvald pmvald removed the request for review from alxhub October 3, 2023 02:26
});

const fnCall = o.importExpr(R3.setClassDebugInfo).callFn([
debugInfo.type,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The value reference here is going to cause code splitting and optimization issues at build time. The ngDevMode global is not effective in this regard since it is replaced after those decisions have been made.

Either an extra transformation will need to be added to the application build pipeline to manually remove each of these lines for each build or an additional compiler-cli option will be needed to control the class debug info emit so they will not be present during bundling.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can use some field in NgCompilerOptions - I would prefer to use a flag indicating that we are compiling in dev mode. Do we have such a flag currently in NgCompilerOptions ? Or we want to have a flag specifically for debug info?

Also which interface you recommend to place the new flag, InternalOptions ?

Copy link
Member

@devversion devversion Oct 4, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We had some issues with setClassMetadata and the defer block which caused eager imports of code that should be deferred. However, if this new construct is always in the same module, i think it should be alright. We should test internally though to ensure expected behavior with the build pipeline.

However, from a more general perspective, generating code that is then immediately removed, i feel, seems counterproductive.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree with all said- for the generating code that is immediately removed. I think we are not doing anything new here. i.e. JIT class metadata TestBed is fully based around a similar principle

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So for now lets follow the existing pattern. But I'm open to the idea that we define dev mode (and other modes) in the cli compiler instead of runtime flags such as ngDevMode, and the compiler basically does not generate the unnecessary code in the first place.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One of the reasons we always emit the ngDevMode output is fully compiled code may be used in other places as well. e.g. a dev-mode application might import the full compilation output from a separate target. I think keeping these as runtime checks simplifies this and, ultimately, optimizers/bundlers have the ability to strip off the dev-mode checks globally, or in dev-mode all compilation output artifacts integrate nicely. But yes, we can revisit in the future.

const debugInfoObject = mapLiteral({
className: debugInfo.className,
filePath: debugInfo.filePath,
lineNumber: debugInfo.lineNumber,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A note that the line number may not always be accurate may be useful here.
The file may have been modified within the build process prior to being given to the TS/NG compiler.

Copy link
Member

@devversion devversion left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, capturing some things we talked about:

  • in the future, we may need/want the same for partial compilation output. i.e. mostly libraries from npm
  • we are running with the idea that there are no security concerns with capturing the file path, as this is guarded by ngDevMode and paths are also relative. Somewhat related to source-maps which could also expose file paths and even source code.

A new utility function `compileClassDebugInfo` is introduced which creates compile result necessary to generate statement for attaching some useful debug info into angular classes. An example of teh new statement would be:

```
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(Main, { className: "Main", filePath: "$PROJECT_ROOT/src/main.ts", lineNumber: 8 }); })();
```

Currently, the debug info contains:
 - the class name
 - the file path in which it is defined
 - the line number in which it is defined

 The debug info will be used in runtime to generate more helpful error messages.
A new statement will be generated for components which will attach some useful debug info to them to be used in runtime error handling. Currently this only happens in full and local compilation modes.
…DebugInfo

This runtime will set the runtime debug info for the given angular class
A new field `debugInfo` is added to the component definition. Now the runtime  ɵsetClassDebugInfo stores the debug info for components in this new field.
…es debug info such as the file path and line number

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.
… include full component info

The error message now contains the code location of the component. It now looks like: "Error: NG01001: Orphan component found! Trying to render the component Main (at $PROJECT_ROOT/src/main.ts:8) without first loading the NgModule ..."
@pmvald pmvald requested a review from crisbeto October 5, 2023 18:21
@pmvald pmvald mentioned this pull request Oct 6, 2023
14 tasks
@pmvald pmvald added action: merge The PR is ready for merge by the caretaker and removed action: review The PR is still awaiting reviews from at least one requested reviewer labels Oct 6, 2023
@atscott
Copy link
Contributor

atscott commented Oct 9, 2023

This PR was merged into the repository by commit 68ba798.

@atscott atscott closed this in 421d790 Oct 9, 2023
atscott pushed a commit that referenced this pull request Oct 9, 2023
A new statement will be generated for components which will attach some useful debug info to them to be used in runtime error handling. Currently this only happens in full and local compilation modes.

PR Close #51919
atscott pushed a commit that referenced this pull request Oct 9, 2023
…DebugInfo (#51919)

This runtime will set the runtime debug info for the given angular class

PR Close #51919
atscott pushed a commit that referenced this pull request Oct 9, 2023
…ts (#51919)

A new field `debugInfo` is added to the component definition. Now the runtime  ɵsetClassDebugInfo stores the debug info for components in this new field.

PR Close #51919
atscott pushed a commit that referenced this pull request Oct 9, 2023
…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
atscott pushed a commit that referenced this pull request Oct 9, 2023
… include full component info (#51919)

The error message now contains the code location of the component. It now looks like: "Error: NG01001: Orphan component found! Trying to render the component Main (at $PROJECT_ROOT/src/main.ts:8) without first loading the NgModule ..."

PR Close #51919
@angular-automatic-lock-bot
Copy link

This issue has been automatically locked due to inactivity.
Please file a new issue if you are encountering a similar or related problem.

Read more about our automatic conversation locking policy.

This action has been performed automatically by a bot.

@angular-automatic-lock-bot angular-automatic-lock-bot bot locked and limited conversation to collaborators Nov 9, 2023
ChellappanRajan pushed a commit to ChellappanRajan/angular that referenced this pull request Jan 23, 2024
…1919)

A new utility function `compileClassDebugInfo` is introduced which creates compile result necessary to generate statement for attaching some useful debug info into angular classes. An example of teh new statement would be:

```
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(Main, { className: "Main", filePath: "$PROJECT_ROOT/src/main.ts", lineNumber: 8 }); })();
```

Currently, the debug info contains:
 - the class name
 - the file path in which it is defined
 - the line number in which it is defined

 The debug info will be used in runtime to generate more helpful error messages.

PR Close angular#51919
ChellappanRajan pushed a commit to ChellappanRajan/angular that referenced this pull request Jan 23, 2024
A new statement will be generated for components which will attach some useful debug info to them to be used in runtime error handling. Currently this only happens in full and local compilation modes.

PR Close angular#51919
ChellappanRajan pushed a commit to ChellappanRajan/angular that referenced this pull request Jan 23, 2024
…DebugInfo (angular#51919)

This runtime will set the runtime debug info for the given angular class

PR Close angular#51919
ChellappanRajan pushed a commit to ChellappanRajan/angular that referenced this pull request Jan 23, 2024
…ts (angular#51919)

A new field `debugInfo` is added to the component definition. Now the runtime  ɵsetClassDebugInfo stores the debug info for components in this new field.

PR Close angular#51919
ChellappanRajan pushed a commit to ChellappanRajan/angular that referenced this pull request Jan 23, 2024
…es debug info such as the file path and line number (angular#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 angular#51919
ChellappanRajan pushed a commit to ChellappanRajan/angular that referenced this pull request Jan 23, 2024
… include full component info (angular#51919)

The error message now contains the code location of the component. It now looks like: "Error: NG01001: Orphan component found! Trying to render the component Main (at $PROJECT_ROOT/src/main.ts:8) without first loading the NgModule ..."

PR Close angular#51919
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
action: merge The PR is ready for merge by the caretaker area: compiler Issues related to `ngc`, Angular's template compiler area: core Issues related to the framework runtime detected: feature PR contains a feature commit PullApprove: disable target: minor This PR is targeted for the next minor release
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

5 participants