Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions packages/core/supabase-js/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@
"types": "dist/index.d.cts",
"exports": {
".": {
"react-native": {
"types": "./dist/index.d.cts",
"default": "./dist/index.cjs"
},
"import": {
"types": "./dist/index.d.mts",
"default": "./dist/index.mjs"
Expand All @@ -30,6 +34,10 @@
}
},
"./cors": {
"react-native": {
"types": "./dist/cors.d.cts",
"default": "./dist/cors.cjs"
},
"import": {
"types": "./dist/cors.d.mts",
"default": "./dist/cors.mjs"
Expand Down
42 changes: 42 additions & 0 deletions packages/core/supabase-js/test/bundle-hermes-compat.test.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,13 @@
* plugin in tsdown.config.ts — source-side magic comments don't survive
* rolldown's printer.
*
* 4. `package.json` `exports` has a `"react-native"` condition for both `.`
* and `./cors`, resolving to the Hermes-safe CJS bundle. Metro (React
* Native's bundler) checks `"react-native"` before `"import"`/`"require"`
* by default. Without this condition, Metro with `unstable_enablePackageExports`
* enabled (the default on Expo SDK 55+) resolves the ESM bundle and ships
* its `import()` to hermesc, which fails at parse time. See issue #2380.
*
* Run with: node test/bundle-hermes-compat.test.cjs
*/

Expand Down Expand Up @@ -88,4 +95,39 @@ console.log(
' Each bundler (webpack / turbopack / vite) sees its directive in a single-purpose block\n'
)

// Check 4: package.json `exports` has a `"react-native"` condition that
// resolves to the Hermes-safe CJS bundle. Without this, Metro on Expo SDK 55+
// (with unstable_enablePackageExports enabled by default) resolves the ESM
// bundle and ships its `import()` to hermesc, which fails at parse time.
const pkg = require('../package.json')
for (const entry of ['.', './cors']) {
const conds = pkg.exports[entry]
assert.ok(
conds && typeof conds === 'object',
`package.json exports["${entry}"] must be a conditions object`
)
assert.ok(
conds['react-native'],
`package.json exports["${entry}"] is missing the "react-native" condition — ` +
`Metro resolves the ESM bundle by default, which contains import() and breaks hermesc. See issue #2380.`
)
const rnKeys = Object.keys(conds)
const rnIdx = rnKeys.indexOf('react-native')
const importIdx = rnKeys.indexOf('import')
const requireIdx = rnKeys.indexOf('require')
assert.ok(
rnIdx < importIdx && rnIdx < requireIdx,
`package.json exports["${entry}"]: "react-native" must appear before "import" and "require" — ` +
`Node-style conditional exports resolve in key order.`
)
const rnTarget = conds['react-native'].default || conds['react-native']
assert.ok(
typeof rnTarget === 'string' && rnTarget.endsWith('.cjs'),
`package.json exports["${entry}"]["react-native"] must resolve to a .cjs file (Hermes-safe). ` +
`Got: ${JSON.stringify(rnTarget)}`
)
}
console.log('4. package.json exports has "react-native" condition for "." and "./cors"')
console.log(' Metro resolves dist/*.cjs (Hermes-safe) instead of dist/*.mjs\n')

console.log('All bundle compatibility checks passed.')
6 changes: 4 additions & 2 deletions packages/core/supabase-js/tsdown.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,10 @@ export default defineConfig([
// bundle to tsc's CJS output (dist/main/index.js), where the
// dynamic `import()` has been lowered to a runtime `require()`.
// dist/index.mjs intentionally keeps the native `import()` — it's
// valid ESM, isn't blocked by browser CSP, and Hermes never sees
// the ESM bundle (Metro pulls the `require` condition).
// valid ESM and isn't blocked by browser CSP. React Native bundlers
// (Metro) avoid the ESM bundle via the `react-native` export
// condition in supabase-js's package.json, which points to
// dist/index.cjs (Hermes-safe). See issue #2380.
return {
resolve: {
alias: {
Expand Down
Loading