@@ -43,20 +43,38 @@ describe('sentryAssertionBabelPlugin', () => {
4343 // without relying on framesToPop or the in_app heuristic. It goes through a
4444 // hoisted global-`Error` alias so a call-site shadow can't break it.
4545 const out = transform ( `invariant(total >= 0, 'bad total');` ) ;
46- expect ( out ) . toMatch ( / v a r _ E r r o r \d * = E r r o r ; / ) ;
46+ expect ( out ) . toMatch ( / v a r _ E r r o r \d * = t y p e o f g l o b a l T h i s ! = = [ " ' ] u n d e f i n e d [ " ' ] \? g l o b a l T h i s \. E r r o r : E r r o r ; / ) ;
4747 expect ( out ) . toMatch ( / e r r o r : n e w _ E r r o r \d * \( \) / ) ;
4848 } ) ;
4949
5050 it ( 'is immune to a call-site `Error` shadow (hoisted alias captures the global)' , ( ) => {
5151 // A parameter named `Error` shadows the global at the call site. The hoisted
52- // `var _Error = Error;` at program top captured the real constructor first,
53- // so the injected `new _Error()` never resolves to the shadow (which would
54- // throw `TypeError: Error is not a constructor` when the assertion fires).
52+ // alias at program top captured the real constructor first, so the injected
53+ // `new _Error()` never resolves to the shadow (which would throw
54+ // `TypeError: Error is not a constructor` when the assertion fires).
5555 const out = transform ( `function f(Error) {\n invariant(ok);\n}` ) ;
56- expect ( out ) . toMatch ( / v a r _ E r r o r \d * = E r r o r ; / ) ;
56+ expect ( out ) . toMatch ( / v a r _ E r r o r \d * = t y p e o f g l o b a l T h i s ! = = [ " ' ] u n d e f i n e d [ " ' ] \? g l o b a l T h i s \. E r r o r : E r r o r ; / ) ;
5757 expect ( out ) . toMatch ( / e r r o r : n e w _ E r r o r \d * \( \) / ) ;
5858 } ) ;
5959
60+ it ( 'is immune to a module-level `Error` shadow (alias reads globalThis.Error)' , ( ) => {
61+ // A module-scope `const Error` would put the bare `Error` identifier in its
62+ // TDZ at program top, so `var _Error = Error;` would throw at load time. The
63+ // alias reads `globalThis.Error` instead, which the lexical shadow can't
64+ // capture.
65+ const out = transform ( `const Error = 1;\ninvariant(ok);` ) ;
66+ expect ( out ) . toMatch ( / v a r _ E r r o r \d * = t y p e o f g l o b a l T h i s ! = = [ " ' ] u n d e f i n e d [ " ' ] \? g l o b a l T h i s \. E r r o r : E r r o r ; / ) ;
67+ expect ( out ) . toMatch ( / e r r o r : n e w _ E r r o r \d * \( \) / ) ;
68+ } ) ;
69+
70+ it ( 'emits a `__proto__` value as a computed key, not a prototype setter' , ( ) => {
71+ // `{ __proto__: v }` sets the prototype and throws for a non-object value;
72+ // the computed form `{ ['__proto__']: v }` keeps it an own data property.
73+ const out = transform ( `const __proto__ = 1;\ninvariant(__proto__ > 0);` ) ;
74+ expect ( out ) . toMatch ( / \[ [ " ' ] _ _ p r o t o _ _ [ " ' ] \] : _ _ p r o t o _ _ / ) ;
75+ expect ( out ) . not . toMatch ( / \{ \s * _ _ p r o t o _ _ : _ _ p r o t o _ _ / ) ;
76+ } ) ;
77+
6078 it ( 'forwards variadic substitution args as messageArgs for interpolation' , ( ) => {
6179 // RN's Dimensions invariant is `invariant(dims, 'No dimension set for key %s',
6280 // dimension)` — the extra arg must reach the reporter so `%s` interpolates.
@@ -250,6 +268,18 @@ describe('sentryAssertionBabelPlugin', () => {
250268 expect ( out ) . toMatch ( / i n v a r i a n t \( o k \) / ) ;
251269 } ) ;
252270
271+ it ( 'never instruments `@sentry-internal` packages (SDK transitive deps)' , ( ) => {
272+ // `@sentry-internal/*` packages are dependencies of `@sentry/react-native`;
273+ // instrumenting them injects a require of the SDK into its own dependency
274+ // graph, creating a circular require that can leave the reporter undefined.
275+ const out = transform ( `import invariant from 'invariant';\ninvariant(ok);` , {
276+ filename : '/proj/node_modules/@sentry-internal/browser-utils/index.js' ,
277+ options : { includeNodeModules : true } ,
278+ } ) ;
279+ expect ( out ) . not . toContain ( '_captureAssertionViolation' ) ;
280+ expect ( out ) . toMatch ( / i n v a r i a n t \( o k \) / ) ;
281+ } ) ;
282+
253283 it ( 'never instruments the Sentry SDK’s own source (monorepo symlink path)' , ( ) => {
254284 // The dev symlink resolves the SDK through a path with no node_modules/@sentry
255285 // segment, so it must be excluded by the packages/ marker too.
0 commit comments