Skip to content
Open
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
79 changes: 63 additions & 16 deletions packages/vxrn/src/utils/createNativeDevEngine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,8 @@ function getNativePlugins(
dev: boolean
): Plugin[] {
return [
// stub CSS imports — native doesn't support CSS and rolldown removed CSS bundling
cssStubPlugin(),
// handle import.meta.glob (used by One's route system)
viteImportGlobPlugin({ root }) as any,
// strip Flow types from react-native and @react-native packages
Expand Down Expand Up @@ -223,6 +225,11 @@ export async function createNativeDevEngine(
},

treeshake: false,
// some react-native ecosystem packages import symbols that don't exist in
// the declared entry (e.g. @react-navigation/elements imports NavigationProvider
// from @react-navigation/native which doesn't export it). metro silently shims
// these — rolldown needs an explicit opt-in.
shimMissingExports: true,

moduleTypes: {
'.js': 'jsx',
Expand Down Expand Up @@ -405,6 +412,7 @@ export async function buildNativeBundle(
resolve: getNativeResolveConfig(platform),
transform: getNativeTransformConfig(dev),
treeshake: !dev,
shimMissingExports: true,
moduleTypes: { '.js': 'jsx' },
plugins: [
...getNativePlugins(root, platform, viteImportGlobPlugin, dev),
Expand Down Expand Up @@ -465,6 +473,24 @@ createApp({

// --- plugins ---

/**
* Stub CSS imports for native builds.
* Native doesn't support CSS and rolldown removed CSS bundling support.
* Without this, any `import './foo.css'` will cause a build error.
*/
function cssStubPlugin(): Plugin {
return {
name: 'vxrn:css-stub',
load: {
handler(id) {
if (/\.css$/.test(id)) {
return { code: '', moduleType: 'js' as any }
}
},
},
}
}

/**
* Strip Flow types from react-native source files.
* Uses hermes-parser which is already a dep of react-native.
Expand Down Expand Up @@ -560,16 +586,17 @@ function hermesCompatSWCPlugin(dev: boolean): Plugin {
if (id.includes('\0') || id.includes('virtual:')) return
// skip files that don't need transformation
const hasClass = code.includes('class ') || code.includes('class{')
const hasAsyncGen = code.includes('async *') || code.includes('async*')
const hasAsync = !dev && code.includes('async ')
if (!hasClass && !hasAsync) return
if (!hasClass && !hasAsync && !hasAsyncGen) return
// skip very large prebuilt files
if (code.length > 500_000) return

try {
if (!swc) swc = await import('@swc/core')

// prod builds: hermes bytecode compiler rejects class declarations/expressions
// and async functions, so we downlevel them
// hermes doesn't support async generators (even in dev) and rejects
// class declarations in prod bytecode compilation
const envIncludes = [
'transform-class-properties',
'transform-class-static-block',
Expand All @@ -578,25 +605,45 @@ function hermesCompatSWCPlugin(dev: boolean): Plugin {
...(!dev ? ['transform-classes', 'transform-async-to-generator'] : []),
]

// for files with async generators, use a lower target so SWC
// fully downlevels them (hermes doesn't support async generators)
const result = await swc.transform(code, {
filename: id,
configFile: false,
swcrc: false,
sourceMaps: false,
inputSourceMap: false,
env: {
targets: { node: 9999 },
include: envIncludes,
},
jsc: {
parser: { syntax: 'typescript', tsx: true },
transform: { react: { runtime: 'preserve' } },
externalHelpers: false,
assumptions: {
setPublicClassFields: true,
privateFieldsAsProperties: true,
},
},
// hermes supports async/await but NOT async generators or for-await-of.
// for files with those patterns, use es5 target to fully downlevel them
// (this adds regenerator overhead only for those specific files)
...(hasAsyncIter
? {
jsc: {
parser: { syntax: 'typescript', tsx: true },
target: 'es5',
transform: { react: { runtime: 'preserve' } },
externalHelpers: false,
assumptions: {
setPublicClassFields: true,
privateFieldsAsProperties: true,
},
},
}
: {
env: {
targets: { node: 9999 },
include: envIncludes,
},
jsc: {
parser: { syntax: 'typescript', tsx: true },
transform: { react: { runtime: 'preserve' } },
externalHelpers: false,
assumptions: {
setPublicClassFields: true,
privateFieldsAsProperties: true,
},
},
}),
isModule: !id.endsWith('.cjs'),
})
return { code: result.code }
Expand Down
Loading