-
Notifications
You must be signed in to change notification settings - Fork 50
fix(react-native): Enhance fragment detection for indirect references #767
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
fix(react-native): Enhance fragment detection for indirect references #767
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
overall approach seem good to me. Anecdotally traversing imports usually is not that expensive, but it might be nice to do a sanity check benchmark.
Program: { | ||
enter(path, state) { | ||
const fragmentContext = collectFragmentContext(path); | ||
state['sentryFragmentContext'] = fragmentContext; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should update AnnotationPluginPass
to strongly type sentryFragmentContext
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good idea 👍 Updated with b44fcc8
@@ -151,7 +166,8 @@ function functionBodyPushAttributes( | |||
componentName: string, | |||
sourceFileName: string | undefined, | |||
attributeNames: string[], | |||
ignoredComponents: string[] | |||
ignoredComponents: string[], | |||
fragmentContext?: FragmentContext |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Might be time to convert this to take an object instead of plain args (and also add a jsdoc to document)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated with d728ba5
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sounds reasonable to me -- thanks for adding all the tests!
Besides Abhi's feedback, could you take a look at the integration test? Perhaps we can add a fragment or two there as well (but happy to leave this up to you. If it's too much of a hassle that's fine as well)
Thank you both for the reviews and feedback. I really appreciate this 🙇 |
Thanks again for your feedback 🙇 This is now ready for another pass.
@AbhiPrasad I've added some performance tests with 4063095
@Lms24 I think this is a good idea 👍 I'm facing some issues running the integration tests (unmodified) localy (see log). I'll investigate this further and iterate on this or a follow up PR. Any feedback is welcome 🙏 log
|
expect(fragmentResults.avg).toBeLessThan(300); | ||
}); | ||
|
||
it("should not consume excessive memory", () => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This may have contaminated data since it's running other tests in parallel.
Suggestion: Use spawnSync
to invoke node and run this specific test.
the stdout from the test should return the memory used by the project.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we can never run benchmarks in test processes, there is too much risk of variance.
I would much prefer using something like https://github.com/RafaelGSS/bench-node and making it a separate CI step. We can do this in a follow-up PR though.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sounds good. I removed benchmarks for now with 29e996f and will iterate separately. In any case I think they served as a sanity check for now.
): void { | ||
if (!jsxNode) { | ||
return; | ||
} | ||
|
||
// Use provided componentName or fall back to context componentName | ||
const currentComponentName = componentName !== undefined ? componentName : context.componentName; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Will there be any cases where componentName
is null? If so, do we want to fall back to the context data instead of setting null?
If true, we can use the following suggestion:
const currentComponentName = componentName !== undefined ? componentName : context.componentName; | |
const currentComponentName = componentName ?? context.componentName; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually from the code below you do set componentName
as null in some function calls, on that context, I believe we should give it a chance for the context.componentName to be set
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point @lucas-zimerman 👍
Updated with ea68863
following up with it sounds fine to me, thanks! I just tried running them locally and for me, all 73 pass (i do get a shit ton of console logs and warnings from the plugins though). Does the one you want to adjust/modify fail? Otherwise, I'd try to ignore it as best as possible for now if you don't find out what's causing the fails 😅 You can also cd into the integration tests dir, first run |
// import { Fragment } from 'react' -> Fragment | ||
// import { Fragment as F } from 'react' -> F | ||
if (spec.imported.name === "Fragment") { | ||
fragmentAliases.add(spec.local.name); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Will the Alias be applied globally?
Asking in case of edge cases where someone alias Fragment as F but on other page they alias something else as F
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's a good question @lucas-zimerman and @alwx 👍
Regarding same aliases in different files I think this should already be covered since babel processes files one by one.
Regarding aliases on the same source file I added a test case with 4dcfbf5
I think the correct thing to do is to not add the data-sentry-element
in that case since it may result in a crash if it is a fragment.
I'll be happy to iterate if you have another case in mind 🙏
if (spec.type === "ImportSpecifier" && spec.imported.type === "Identifier") { | ||
// import { Fragment } from 'react' -> Fragment | ||
// import { Fragment as F } from 'react' -> F | ||
if (spec.imported.name === "Fragment") { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Another question, should we safeguard that it's also
spec.source.value === "react"
so we know Fragment is actually from react and not some user code?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this should be covered with the outer check if (source === "react" || source === "React")
Co-authored-by: LucasZF <[email protected]>
Co-authored-by: LucasZF <[email protected]>
sourceFileName: string | undefined, | ||
attributeNames: string[], | ||
ignoredComponents: string[] | ||
componentName?: string | null |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I feel slightly confused that componentName
could be a string, undefined
and null
now — in which situations it could be null
and undefined
and what's the difference between these two cases?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks all for your feedback 🙇 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks good to me, thanks for making all the changes. Tests seem to cover a lot of cases, so I'm fine with merging. Let's wait until EOD for others to chime in and then
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good, thanks for handling that!
| datasource | package | from | to | | ---------- | ------------------- | ----- | ----- | | npm | @sentry/vite-plugin | 3.6.1 | 4.1.1 | ## [v4.1.1](https://github.com/getsentry/sentry-javascript-bundler-plugins/blob/HEAD/CHANGELOG.md#411) - fix(react-native): Enhance fragment detection for indirect references ([#767](getsentry/sentry-javascript-bundler-plugins#767)) ## [v4.1.0](https://github.com/getsentry/sentry-javascript-bundler-plugins/blob/HEAD/CHANGELOG.md#410) - feat(deps): Bump [@sentry/cli](https://github.com/sentry/cli) to 2.51.0 [#786](getsentry/sentry-javascript-bundler-plugins#786) - feat(core): Add flag for disabling sourcemaps upload [#785](getsentry/sentry-javascript-bundler-plugins#785) - fix(debugId): Add guards for injected code to avoid errors [#783](getsentry/sentry-javascript-bundler-plugins#783) - docs(options): Improve JSDoc for options [#781](getsentry/sentry-javascript-bundler-plugins#781) - feat(core): Expose method for injecting debug Ids from plugin manager [#784](getsentry/sentry-javascript-bundler-plugins#784) ## [v4.0.2](https://github.com/getsentry/sentry-javascript-bundler-plugins/blob/HEAD/CHANGELOG.md#402) - fix(core): Make `moduleMetadata` injection snippet ES5-compliant ([#774](getsentry/sentry-javascript-bundler-plugins#774)) ## [v4.0.1](https://github.com/getsentry/sentry-javascript-bundler-plugins/blob/HEAD/CHANGELOG.md#401) - fix(core): Make plugin inject ES5-friendly code ([#770](getsentry/sentry-javascript-bundler-plugins#770)) - fix(core): Use `renderChunk` for release injection for Rollup/Rolldown/Vite ([#761](getsentry/sentry-javascript-bundler-plugins#761)) Work in this release was contributed by [@grushetsky](https://github.com/grushetsky). Thank you for your contribution! ## [v4.0.0](https://github.com/getsentry/sentry-javascript-bundler-plugins/blob/HEAD/CHANGELOG.md#400) ##### Breaking Changes - (Type change) Vite plugin now returns `VitePlugin` type instead of `any` - Deprecated function `getBuildInformation` has been removed ##### List of Changes - feat(core)!: Remove `getBuildInformation` export ([#765](getsentry/sentry-javascript-bundler-plugins#765)) - feat(vite)!: Update return type of vite plugin ([#728](getsentry/sentry-javascript-bundler-plugins#728))
Fixes getsentry/sentry-react-native#4902