diff --git a/CONTEXT.md b/CONTEXT.md index 09fdcee2..bdcfe76b 100644 --- a/CONTEXT.md +++ b/CONTEXT.md @@ -113,6 +113,7 @@ Metro integration: - `withUniwindConfig(config, uniwindConfig)` patches Metro graph support for uncached modules. - Metro adds `css` as source extension and removes it from asset extensions. - Metro transformer handles the configured CSS entry file specially. +- Metro transformer worker selection is lazy, cached per Expo/non-Expo config type, and follows Expo transformer paths or Expo-specific config markers. - Native platform CSS transforms into a JS module that calls `Uniwind.__reinit(...)`. - Web platform CSS transforms into CSS plus web runtime setup. - Resolver swaps React Native component imports to Uniwind-aware implementations where needed. diff --git a/packages/uniwind/src/bundler/adapters/metro/metro.ts b/packages/uniwind/src/bundler/adapters/metro/metro.ts index 106868bb..7216c737 100644 --- a/packages/uniwind/src/bundler/adapters/metro/metro.ts +++ b/packages/uniwind/src/bundler/adapters/metro/metro.ts @@ -9,6 +9,29 @@ import { nativeResolver, webResolver } from './resolvers' const isUniwindRequest = (moduleName: string) => moduleName === 'uniwind' || moduleName.startsWith('uniwind/') +type ExpoTransformerConfig = NonNullable & { + _expoRelativeProjectRoot?: string + _expoRouterPath?: string + expo_customTransformerPath?: string | false + postcssHash?: string | null +} + +const isExpoMetroConfig = (config: MetroConfig) => { + const transformerPath = config.transformerPath + const transformer = config.transformer as ExpoTransformerConfig | undefined + const hasExpoTransformerField = transformer + ? '_expoRelativeProjectRoot' in transformer + || '_expoRouterPath' in transformer + || 'expo_customTransformerPath' in transformer + || 'postcssHash' in transformer + : false + + return Boolean( + transformerPath?.includes('@expo/metro-config') + || hasExpoTransformerField, + ) +} + export const withUniwindConfig = ( config: T, uniwindConfig: UniwindConfig, @@ -24,7 +47,10 @@ export const withUniwindConfig = ( transformerPath: require.resolve('./transformer.cjs'), transformer: { ...config.transformer, - uniwind: bundlerConfig.toMetroConfig(), + uniwind: { + ...bundlerConfig.toMetroConfig(), + isExpoProject: isExpoMetroConfig(config), + }, }, resolver: { ...config.resolver, diff --git a/packages/uniwind/src/bundler/adapters/metro/transformer.ts b/packages/uniwind/src/bundler/adapters/metro/transformer.ts index 1addaf5a..e645bdc4 100644 --- a/packages/uniwind/src/bundler/adapters/metro/transformer.ts +++ b/packages/uniwind/src/bundler/adapters/metro/transformer.ts @@ -9,18 +9,31 @@ import path from 'path' const cssArtifactPath = path.resolve(__dirname, '../../uniwind.css') -let worker: typeof MetroTransformWorker +const workerCache = new Map() -try { - try { - const { unstable_transformerPath } = require('@expo/metro-config') as typeof ExpoMetroConfig +const getTransformWorker = (isExpoProject?: boolean): typeof MetroTransformWorker => { + const cacheKey = Boolean(isExpoProject) + const cachedWorker = workerCache.get(cacheKey) - worker = require(unstable_transformerPath) - } catch { - worker = require('@expo/metro-config/build/transform-worker/transform-worker.js') + if (cachedWorker) { + return cachedWorker } -} catch { - worker = require('metro-transform-worker') + + const resolvedWorker: typeof MetroTransformWorker = cacheKey + ? (() => { + try { + const { unstable_transformerPath } = require('@expo/metro-config') as typeof ExpoMetroConfig + + return require(unstable_transformerPath) + } catch { + return require('@expo/metro-config/build/transform-worker/transform-worker.js') + } + })() + : require('metro-transform-worker') + + workerCache.set(cacheKey, resolvedWorker) + + return resolvedWorker } export const transform = async ( @@ -32,6 +45,7 @@ export const transform = async ( data: Buffer, options: JsTransformOptions, ) => { + const worker = getTransformWorker(config.uniwind.isExpoProject) const isCss = options.type !== 'asset' && path.join(process.cwd(), config.uniwind.cssEntryFile) === path.join(projectRoot, filePath) if (filePath.endsWith('/components/web/metro-injected.js')) { diff --git a/packages/uniwind/src/bundler/types.ts b/packages/uniwind/src/bundler/types.ts index 704c7e60..5f7d831a 100644 --- a/packages/uniwind/src/bundler/types.ts +++ b/packages/uniwind/src/bundler/types.ts @@ -11,5 +11,6 @@ export type Polyfills = { export type UniwindMetroConfig = UniwindConfig & { polyfills?: Polyfills debug?: boolean + isExpoProject?: boolean isTV?: boolean }