Skip to content

fix(browser): Ensure lazyLoadIntegration works in NPM mode #11673

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

Merged
merged 2 commits into from
Apr 23, 2024

Conversation

mydea
Copy link
Member

@mydea mydea commented Apr 18, 2024

As noticed by @ryan953 here: #11621 (comment), this was not actually working properly in NPM-mode. I added a proper test for this and fixed this, so hopefully should be all good now.

Copy link
Contributor

github-actions bot commented Apr 18, 2024

size-limit report 📦

Path Size
@sentry/browser 21.67 KB (0%)
@sentry/browser (incl. Tracing) 31.46 KB (0%)
@sentry/browser (incl. Tracing, Replay) 66.79 KB (0%)
@sentry/browser (incl. Tracing, Replay) - with treeshaking flags 60.19 KB (0%)
@sentry/browser (incl. Tracing, Replay with Canvas) 70.62 KB (0%)
@sentry/browser (incl. Tracing, Replay, Feedback) 80.5 KB (0%)
@sentry/browser (incl. Feedback) 35.25 KB (0%)
@sentry/browser (incl. Feedback, Feedback Modal) 35.25 KB (0%)
@sentry/browser (incl. Feedback, Feedback Modal, Feedback Screenshot) 37.28 KB (0%)
@sentry/browser (incl. sendFeedback) 26.46 KB (0%)
@sentry/react 24.35 KB (-0.01% 🔽)
@sentry/react (incl. Tracing) 34.37 KB (0%)
@sentry/vue 25.2 KB (0%)
@sentry/vue (incl. Tracing) 33.18 KB (0%)
@sentry/svelte 21.79 KB (0%)
CDN Bundle 24.03 KB (+0.02% 🔺)
CDN Bundle (incl. Tracing) 32.77 KB (+0.03% 🔺)
CDN Bundle (incl. Tracing, Replay) 66.4 KB (+0.01% 🔺)
CDN Bundle (incl. Tracing, Replay, Feedback) 82.61 KB (+0.02% 🔺)
CDN Bundle - uncompressed 70.86 KB (-0.01% 🔽)
CDN Bundle (incl. Tracing) - uncompressed 97.59 KB (-0.01% 🔽)
CDN Bundle (incl. Tracing, Replay) - uncompressed 207.25 KB (-0.01% 🔽)
@sentry/nextjs (client) 33.7 KB (0%)
@sentry/sveltekit (client) 31.96 KB (0%)
@sentry/node 155.86 KB (+0.01% 🔺)

@@ -33,12 +33,15 @@ const WindowWithMaybeIntegration = WINDOW as {
export async function lazyLoadIntegration(name: keyof typeof LazyLoadableIntegrations): Promise<IntegrationFn> {
const bundle = LazyLoadableIntegrations[name];

if (!bundle || !WindowWithMaybeIntegration.Sentry) {
// `window.Sentry` is only set when using a CDN bundle, but this method can also be used via the NPM package
Copy link
Member

Choose a reason for hiding this comment

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

Maybe I'm missing some context here but since we never set window.Sentry in NPM mode, should we really start doing it now? Just wondering since we have the __SENTRY__ carrier object anyway 🤔

Copy link
Member Author

Choose a reason for hiding this comment

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

The problem is that the integration CDN bundles always add to window.Sentry, so we can't really influence this here (unless we change the logic in our CDN bundles, which is also possible of course 🤔 )

Copy link
Member

@Lms24 Lms24 Apr 22, 2024

Choose a reason for hiding this comment

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

Yeah I see the dilemma 😅

unless we change the logic in our CDN bundles, which is also possible of course

I wouldn't do that - CDN users definitely expect window.Sentry APIs and probably also integrations being available this way.

So I guess the lesser of the two evils is to also create a window.Sentry object in NPM mode. I still have the feeling that this might bite us into the back but we can worry about that if it comes up. I guess the one concrete concern would be users who create a window.Sentry object themselves. I think I've seen this before in some unsupported SSR framework but I didn't find the issue/discussion that I had in mind. And even in this case, we only add a property to the object and wouldn't overwrite it if it was there previously. So 🤞

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, but even then we'd put the integration onto that object, which is not ideal but should generally also not break stuff (hopefully xD)

throw new Error(`Cannot lazy load integration: ${name}`);
}

// Bail if the integration already exists
const existing = WindowWithMaybeIntegration.Sentry[name];
const existing = sentryOnWindow[name];
Copy link
Member

Choose a reason for hiding this comment

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

related to my comment above: Assuming we already installed the integration in NPM mode during the normal Sentry.init call, it wouldn't have been registered on window.Sentry so we wouldn't actually bail here.

Again, I feel like I lack some context so feel free to disregard!

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 this case specifically I thought about too, my idea was that we can/should check for this outside of this method - my reasoning was that otherwise we'd have to also pass the integration name (e.g. HttpClient ) to the function because we'd need this to lookup via getIntegrationByName() 🤔 No strong feelings, we can also extend the function signature and check in here?

Copy link
Member Author

Choose a reason for hiding this comment

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

cc @ryan953 also relates to your comment. So to clarify we have to options IMHO:

  1. Keep this function "lean" so it does not check integration name - this will keep the bundle size slightly smaller, and instead at call site you have to do something like this:
function maybeLoadFeedback() {
  if(client.getIntegrationByName('Feedback')) {
    return;
  }
  return lazyLoadIntegration('feedbackIntegration');
}
  1. Do the check in the integration: For this we have to options, either requiring to pass in both function name as well as integration name, or adding this to the map somehow. The latter has a nicer DX, but will increase bundle size:

2.a: lazyLoadIntegration('feedbackIntegration', 'Feedback') <-- smaller bundle size, but not-so-nice usage
2.b: lazyLoadIntegration('feedbackIntegration') and keep feedbackIntegration > Feedback in the map somehow

For me, the main reason I went with 1. was that if a user uses this, they probably don't need this check (think in the future we want users to use this when using the loader to add pluggable integrations), and thus we can keep the API surface & bundle size slightly smaller. But I'm also totally fine with adding this additional check.

Copy link
Member

@ryan953 ryan953 Apr 19, 2024

Choose a reason for hiding this comment

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

I like 1. for what it's worth.
In the case of an npm bundle the user could easily have feedbackIntegration or something included in the bundle, but not added at runtime, therefore getIntegrationByName would not return and we'd still inject a script tag to load the integration again. For this reason I think it makes sense to avoid the extra check inside the method.

Copy link
Member

Choose a reason for hiding this comment

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

Ok yes, that makes sense, let's go with 1 then. Thx for explaining!

@mydea mydea force-pushed the fn/lazyLoadIntegrationNpm branch from 0625084 to 11f8d45 Compare April 18, 2024 09:44
@ryan953
Copy link
Member

ryan953 commented Apr 18, 2024

In terms of window.Sentry vs __SENTRY__ i'm indifferent to the implementation because i don't know the details of each. But the concept of loading the integrations up and stashing them onto some kind of window.$var member seems reasonable to me.

It looks like this method is very much a "given a valid integration name: 1. ensure a script tag is on the page to load it 2. return the integration to the callsite"
If could be the case in an npm build that the integration code is already bundled somewhere, but not stashed into window.Sentry (or whatever the impl) yet. In that case we will add the script tag and have a 2nd copy of the js parsed and loaded into memory; but i think that's ok. It's the responsibility of the callsite to only call this when needed.
I'll run the code to test it directly too now. Looking good when i pull the code down and test it in an npm build.

Copy link
Member

@billyvg billyvg left a comment

Choose a reason for hiding this comment

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

lgtm from a code standpoint, I don't really have an opinion with the sentry global

@mydea mydea requested a review from Lms24 April 22, 2024 07:56
@mydea mydea merged commit ca5cb14 into develop Apr 23, 2024
92 checks passed
@mydea mydea deleted the fn/lazyLoadIntegrationNpm branch April 23, 2024 07:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants