Skip to content

Commit 6fdf914

Browse files
committed
fix(misc): address comments
1 parent 459f429 commit 6fdf914

3 files changed

Lines changed: 44 additions & 37 deletions

File tree

Lines changed: 25 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,15 @@
11
/**
2-
* Hermes Compatibility Test
2+
* Hermes + CSP Compatibility Test
33
*
4-
* Asserts dist/index.cjs contains no bare import() expressions.
5-
* hermesc (React Native bytecode compiler) rejects import() at parse time —
6-
* before dead-code elimination — so the syntax must be physically absent.
7-
* The only allowed occurrence is inside a new Function() string body, which
8-
* is opaque to all static parsers including hermesc.
4+
* Asserts dist/index.cjs contains:
5+
* - No import() expressions -> hermesc (React Native bytecode compiler) rejects
6+
* import() at parse time, before dead-code elimination.
7+
* - No new Function() calls -> browsers with a strict Content-Security-Policy
8+
* (no 'unsafe-eval') block new Function() identically to eval() at runtime.
9+
*
10+
* Both constraints are satisfied by pointing the CJS bundle at the TypeScript
11+
* CommonJS-compiled output of @supabase/tracing (dist/main/), where import()
12+
* has already been downcompiled to require() by tsc.
913
*
1014
* Run with: node test/bundle-hermes-compat.test.cjs
1115
*/
@@ -14,31 +18,25 @@ const assert = require('assert')
1418
const fs = require('fs')
1519
const path = require('path')
1620

17-
console.log('Testing Hermes (React Native) compatibility...\n')
21+
console.log('Testing Hermes + CSP compatibility of dist/index.cjs...\n')
1822

1923
const cjsPath = path.join(__dirname, '../dist/index.cjs')
2024
const cjs = fs.readFileSync(cjsPath, 'utf8')
21-
const lines = cjs.split('\n')
2225

23-
let bareImportCount = 0
24-
for (let i = 0; i < lines.length; i++) {
25-
const line = lines[i]
26-
if (!line.includes('import(')) continue
27-
if (!line.includes('new Function(')) {
28-
bareImportCount++
29-
console.error(
30-
`❌ Bare import() at line ${i + 1}:\n ${line.trim()}\n` +
31-
` This breaks hermesc (Hermes bytecode compiler for React Native).`
32-
)
33-
}
34-
}
26+
// Check 1: no import() expressions (breaks hermesc / React Native)
27+
assert.ok(
28+
!cjs.includes('import('),
29+
'dist/index.cjs contains import() — breaks hermesc (Hermes bytecode compiler for React Native)'
30+
)
31+
console.log('1. No import() expressions in dist/index.cjs')
32+
console.log(' Hermes-safe (React Native compatible)\n')
3533

36-
assert.strictEqual(
37-
bareImportCount,
38-
0,
39-
`${bareImportCount} bare import() expression(s) in dist/index.cjs — breaks React Native Hermes builds.`
34+
// Check 2: no new Function() (breaks browser strict CSP)
35+
assert.ok(
36+
!cjs.includes('new Function('),
37+
'dist/index.cjs contains new Function() — breaks browser strict Content-Security-Policy (unsafe-eval)'
4038
)
39+
console.log('2. No new Function() in dist/index.cjs')
40+
console.log(' CSP-safe (no unsafe-eval required)\n')
4141

42-
console.log('1. No bare import() expressions in dist/index.cjs')
43-
console.log(' ✅ Hermes-safe (React Native compatible)\n')
44-
console.log('🎉 Hermes compatibility test passed!')
42+
console.log('All Hermes + CSP compatibility checks passed.')

packages/core/supabase-js/tsdown.config.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { defineConfig } from 'tsdown'
2+
import { createRequire } from 'module'
23
import { fileURLToPath } from 'url'
34
import { dirname, join } from 'path'
45

@@ -26,6 +27,23 @@ export default defineConfig([
2627
fixedExtension: true,
2728
hash: false,
2829
target: 'es2017',
30+
inputOptions: (_options, format) => {
31+
if (format === 'cjs') {
32+
// Rolldown always resolves @supabase/tracing via its 'import' export
33+
// condition (ESM source) even for CJS output — so import() ends up in
34+
// index.cjs. hermesc (Hermes bytecode compiler for React Native) rejects
35+
// import() at parse time; browser strict CSP blocks new Function() at
36+
// runtime. Aliasing to the package's 'main' field (dist/main/) gives us
37+
// TypeScript's CJS-compiled require() — safe for both constraints.
38+
return {
39+
resolve: {
40+
alias: {
41+
'@supabase/tracing': createRequire(import.meta.url).resolve('@supabase/tracing'),
42+
},
43+
},
44+
}
45+
}
46+
},
2947
},
3048
// IIFE build for CDN (jsdelivr/unpkg) - bundles EVERYTHING
3149
{

packages/shared/tracing/src/extract.ts

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -14,18 +14,9 @@ let otelModulePromise: Promise<any | null> | null = null
1414
// be installed in the consumer app; we look it up purely at runtime.
1515
const OTEL_PKG = '@opentelemetry/api'
1616

17-
// Wrap import() in a Function constructor so the import() expression is
18-
// never present as syntax in the bundled output. hermesc (the Hermes bytecode
19-
// compiler used by React Native release builds) rejects import() at parse
20-
// time — before any dead-code elimination — so the syntax must be physically
21-
// absent from the bundle. new Function() bodies are opaque strings to all
22-
// static parsers, including hermesc.
23-
// eslint-disable-next-line no-new-func
24-
const _dynamicImport = new Function('p', 'return import(p)') as (pkg: string) => Promise<any>
25-
2617
function loadOtel(): Promise<any | null> {
2718
if (otelModulePromise === null) {
28-
otelModulePromise = _dynamicImport(OTEL_PKG).catch(() => null)
19+
otelModulePromise = (import(OTEL_PKG) as Promise<any>).catch(() => null)
2920
}
3021
return otelModulePromise
3122
}

0 commit comments

Comments
 (0)