Skip to content

fix(misc): hide dynamic import from hermesc#2381

Merged
mandarini merged 11 commits into
masterfrom
fix/otel-import-issue
May 20, 2026
Merged

fix(misc): hide dynamic import from hermesc#2381
mandarini merged 11 commits into
masterfrom
fix/otel-import-issue

Conversation

@mandarini

@mandarini mandarini commented May 19, 2026

Copy link
Copy Markdown
Contributor

Problem

v2.106.0 introduced @supabase/tracing with a dynamic import('@opentelemetry/api'). Rolldown bundles @supabase/tracing's ESM source into dist/index.cjs of supabase-js (it prefers ESM for tree-shaking), so the literal import() ends up in the CJS bundle. hermesc — the Hermes bytecode compiler used for every React Native release build — rejects import() at parse time, before any dead-code elimination, breaking every RN release build with Invalid expression encountered.

The ESM build of supabase-js has a parallel problem: when downstream consumers build through Turbopack / webpack / Vite without @opentelemetry/api installed, those bundlers try to statically resolve the specifier and fail with Module not found: Can't resolve '@opentelemetry/api'. Magic comments suppress this — but Rolldown drops them, and Turbopack only honors them in a specific shape.

Fixes #2380.

Fix

Two independent changes in packages/core/supabase-js/tsdown.config.ts:

1. CJS bundle — alias @supabase/tracing to its CJS main file

inputOptions: (_options, format) => {
  if (format === 'cjs') {
    return {
      resolve: {
        alias: {
          '@supabase/tracing': createRequire(import.meta.url).resolve('@supabase/tracing'),
        },
      },
    }
  }
}

tsc's CJS emit (packages/shared/tracing/dist/main/index.js) already lowers import() to require(). The alias bypasses the import export condition Rolldown's platform: 'node' default would otherwise pick. Result: dist/index.cjs ends up with require() (Hermes-safe; also CSP-safe — no new Function()). dist/index.mjs keeps native import() (valid ESM; Metro resolves the require condition for React Native and never sees the ESM bundle).

2. ESM bundle — inject canonical bundler-ignore comments after rolldown's printer runs

const injectBundlerIgnoreComments = (): Plugin => ({
  name: 'inject-bundler-ignore-comments',
  generateBundle(_options, bundle) {
    for (const [fileName, chunk] of Object.entries(bundle)) {
      if (chunk.type !== 'chunk' || !fileName.endsWith('.mjs')) continue
      chunk.code = chunk.code.replace(
        /import\(\s*([A-Za-z_$][\w$]*)\s*\)/g,
        'import(/* webpackIgnore: true */ /* turbopackIgnore: true */ /* @vite-ignore */ $1)'
      )
    }
  },
})

Rolldown's printer only preserves comments containing @vite-ignore, @__PURE__, or @__NO_SIDE_EFFECTS__ — standalone /* webpackIgnore */ and /* turbopackIgnore */ blocks get stripped. Turbopack only honors /* turbopackIgnore: true */ when it occupies its own block (every Next.js internal usage is single-purpose; multi-keyword blocks are silently ignored — this is what broke 2.106.1-beta.2 on Vercel). The two constraints cannot be satisfied from source.

The plugin runs after rolldown's printer and rewrites every import() in dist/index.mjs into the canonical single-purpose shape that all three bundlers accept. extract.ts is correspondingly reverted to a bare import(OTEL_PKG) — source-side magic comments are dead weight.

Regression test

packages/core/supabase-js/test/bundle-hermes-compat.test.cjs (wired into the module-resolution CI job) asserts three properties of the built dist files:

  1. dist/index.cjs contains no import( expression — Hermes-safe / React Native compatible.
  2. dist/index.cjs contains no new Function( — browser strict-CSP safe.
  3. Every import( in dist/index.mjs is in the canonical shape: /* webpackIgnore: true */ /* turbopackIgnore: true */ /* @vite-ignore */ <ident>, each directive in its own block. Multi-keyword blocks fail this check explicitly.

Verification

  • 2.106.1-beta.0 confirmed working for Hermes/RN by @sbs44, @ryanshepps, @7ttp.
  • Spencer's hermesc repro (expo export:embed + hermesc) exits 0.
  • Next beta (post-merge) needs validation against Turbopack on the multiplayer.dev Vercel branch that surfaced the Turbopack regression.

@pkg-pr-new

pkg-pr-new Bot commented May 19, 2026

Copy link
Copy Markdown

Open in StackBlitz

@supabase/auth-js

npm i https://pkg.pr.new/@supabase/auth-js@2381

@supabase/functions-js

npm i https://pkg.pr.new/@supabase/functions-js@2381

@supabase/postgrest-js

npm i https://pkg.pr.new/@supabase/postgrest-js@2381

@supabase/realtime-js

npm i https://pkg.pr.new/@supabase/realtime-js@2381

@supabase/storage-js

npm i https://pkg.pr.new/@supabase/storage-js@2381

@supabase/supabase-js

npm i https://pkg.pr.new/@supabase/supabase-js@2381

commit: beae416

@mandarini mandarini marked this pull request as ready for review May 19, 2026 13:24
@mandarini mandarini requested review from a team as code owners May 19, 2026 13:24
@mandarini mandarini changed the title fix(misc): otel import issue fix(misc): hide dynamic import from hermesc May 19, 2026
@mandarini mandarini self-assigned this May 19, 2026
@mandarini

Copy link
Copy Markdown
Contributor Author

Published as beta under 2.106.1-beta.0

Comment thread packages/shared/tracing/src/extract.ts Outdated
mandarini and others added 8 commits May 20, 2026 12:53
Rolldown's printer only preserves block comments containing
@__PURE__, @__NO_SIDE_EFFECTS__, or @vite-ignore. Standalone
webpackIgnore / turbopackIgnore blocks were stripped from
dist/index.mjs, leaving the surviving @vite-ignore alone and
breaking Next.js Edge / Turbopack builds. Merging all three
directives into a single @vite-ignore-bearing comment lets
rolldown keep the entire block; webpack and turbopack both scan
for their keyword anywhere in adjacent comments.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@mandarini mandarini force-pushed the fix/otel-import-issue branch from 9a0a319 to 1a3f34b Compare May 20, 2026 10:09
Comment thread packages/core/supabase-js/tsdown.config.ts Fixed
@mandarini

Copy link
Copy Markdown
Contributor Author

Beta testing works, tests pass across all dogfed repos

@mandarini mandarini merged commit 3f9628a into master May 20, 2026
55 of 57 checks passed
@mandarini mandarini deleted the fix/otel-import-issue branch May 20, 2026 12:12
mandarini pushed a commit to supabase/ssr that referenced this pull request May 20, 2026
This PR updates `@supabase/supabase-js` to v2.106.1.

**Source**: supabase-js-stable-release

---

## Release Notes

## v2.106.1

## 2.106.1 (2026-05-20)

### 🩹 Fixes

- **auth:** encode client-id in oauth requests
([#2383](supabase/supabase-js#2383))
- **misc:** hide dynamic import from hermesc
([#2381](supabase/supabase-js#2381))

### ❤️ Thank You

- Etienne Stalmans @staaldraad
- Katerina Skroumpelou @mandarini

This PR was created automatically.

Co-authored-by: supabase-workflow-trigger[bot] <266661614+supabase-workflow-trigger[bot]@users.noreply.github.com>
@Zaitnoori

Copy link
Copy Markdown

Gm

mandarini pushed a commit to supabase/supabase that referenced this pull request May 25, 2026
This PR updates @supabase/*-js libraries to version 2.106.2.

**Source**: supabase-js-stable-release

**Changes**:
- Updated @supabase/supabase-js to 2.106.2
- Updated @supabase/auth-js to 2.106.2
- Updated @supabase/realtime-js to 2.106.2
- Updated @supabase/postgest-js to 2.106.2
- Refreshed pnpm-lock.yaml

---

## Release Notes

## v2.106.2

## 2.106.2 (2026-05-25)

### 🩹 Fixes

- **auth:** restore signup user response
([#2391](supabase/supabase-js#2391))
- **misc:** add react-native export condition for Hermes-safe resolution
([#2393](supabase/supabase-js#2393))

### ❤️ Thank You

- Myroslav Hryhschenko @BLOCKMATERIAL
- Vaibhav @7ttp
## v2.106.1

## 2.106.1 (2026-05-20)

### 🩹 Fixes

- **auth:** encode client-id in oauth requests
([#2383](supabase/supabase-js#2383))
- **misc:** hide dynamic import from hermesc
([#2381](supabase/supabase-js#2381))

### ❤️ Thank You

- Etienne Stalmans @staaldraad
- Katerina Skroumpelou @mandarini

This PR was created automatically.

Co-authored-by: supabase-workflow-trigger[bot] <266661614+supabase-workflow-trigger[bot]@users.noreply.github.com>
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.

v2.106.0: dynamic import('@opentelemetry/api') breaks React Native / Hermes builds

4 participants