diff --git a/.changeset/metro-083-compat.md b/.changeset/metro-083-compat.md new file mode 100644 index 00000000000..242776d53d7 --- /dev/null +++ b/.changeset/metro-083-compat.md @@ -0,0 +1,5 @@ +--- +"@module-federation/metro": patch +--- + +Add Metro 0.83 compatibility layer. Metro 0.83 introduced a restrictive `exports` field that only allows `metro/private/*` paths instead of direct `metro/src/*` imports. This adds a `metro-compat` utility that dynamically resolves the correct import path, ensuring compatibility with both Metro 0.82 and 0.83+. diff --git a/apps/metro-example-host/Gemfile.lock b/apps/metro-example-host/Gemfile.lock index 46cb5c886c6..6f58f7d9b75 100644 --- a/apps/metro-example-host/Gemfile.lock +++ b/apps/metro-example-host/Gemfile.lock @@ -1,31 +1,29 @@ GEM remote: https://rubygems.org/ specs: - CFPropertyList (3.0.7) - base64 - nkf - rexml - activesupport (7.2.2.1) + CFPropertyList (3.0.9) + activesupport (7.1.6) base64 benchmark (>= 0.3) bigdecimal - concurrent-ruby (~> 1.0, >= 1.3.1) + concurrent-ruby (~> 1.0, >= 1.0.2) connection_pool (>= 2.2.5) drb i18n (>= 1.6, < 2) logger (>= 1.4.2) minitest (>= 5.1) + mutex_m securerandom (>= 0.3) - tzinfo (~> 2.0, >= 2.0.5) - addressable (2.8.7) - public_suffix (>= 2.0.2, < 7.0) + tzinfo (~> 2.0) + addressable (2.8.8) + public_suffix (>= 2.0.2, < 8.0) algoliasearch (1.27.5) httpclient (~> 2.8, >= 2.8.3) json (>= 1.5.1) atomos (0.1.3) - base64 (0.2.0) - benchmark (0.4.0) - bigdecimal (3.1.9) + base64 (0.3.0) + benchmark (0.5.0) + bigdecimal (4.0.1) claide (1.1.0) cocoapods (1.15.2) addressable (~> 2.8) @@ -66,34 +64,33 @@ GEM cocoapods-try (1.2.0) colored2 (3.1.2) concurrent-ruby (1.3.3) - connection_pool (2.5.3) - drb (2.2.1) + connection_pool (2.5.5) + drb (2.2.3) escape (0.0.4) - ethon (0.16.0) + ethon (0.15.0) ffi (>= 1.15.0) - ffi (1.17.2) + ffi (1.17.3) fourflusher (2.3.1) fuzzy_match (2.0.4) gh_inspector (1.1.3) httpclient (2.9.0) mutex_m - i18n (1.14.7) + i18n (1.14.8) concurrent-ruby (~> 1.0) - json (2.11.3) + json (2.18.1) logger (1.7.0) - minitest (5.25.5) + minitest (5.26.1) molinillo (0.8.0) mutex_m (0.3.0) nanaimo (0.3.0) nap (1.1.0) netrc (0.11.0) - nkf (0.2.0) public_suffix (4.0.7) - rexml (3.4.1) + rexml (3.4.4) ruby-macho (2.5.1) - securerandom (0.4.1) - typhoeus (1.4.1) - ethon (>= 0.9.0) + securerandom (0.3.2) + typhoeus (1.5.0) + ethon (>= 0.9.0, < 0.16.0) tzinfo (2.0.6) concurrent-ruby (~> 1.0) xcodeproj (1.25.1) @@ -118,7 +115,7 @@ DEPENDENCIES xcodeproj (< 1.26.0) RUBY VERSION - ruby 3.1.0p0 + ruby 2.7.6p219 BUNDLED WITH - 2.6.1 + 2.1.4 diff --git a/apps/metro-example-host/ios/Podfile.lock b/apps/metro-example-host/ios/Podfile.lock index 33788d401f3..e884a7c0a15 100644 --- a/apps/metro-example-host/ios/Podfile.lock +++ b/apps/metro-example-host/ios/Podfile.lock @@ -2422,11 +2422,11 @@ SPEC CHECKSUMS: React-timing: a275a1c2e6112dba17f8f7dd496d439213bbea0d React-utils: 449a6e1fd53886510e284e80bdbb1b1c6db29452 ReactAppDependencyProvider: 3267432b637c9b38e86961b287f784ee1b08dde0 - ReactCodegen: a1a6d7288d6a5fc86f109e46149c35d707932702 + ReactCodegen: d308d08c58717331dcf82d0129efa8b73e28a64c ReactCommon: b028d09a66e60ebd83ca59d8cc9a1216360db147 SocketRocket: d4aabe649be1e368d1318fdf28a022d714d65748 - Yoga: 0c4b7d2aacc910a1f702694fa86be830386f4ceb + Yoga: 395b5d614cd7cbbfd76b05d01bd67230a6ad004e -PODFILE CHECKSUM: a8134080201cda3c42e54a89f48d0930861e3c58 +PODFILE CHECKSUM: d7ddd7fd39d49e0dd5d1205cb5dc83483092e5fb -COCOAPODS: 1.15.2 \ No newline at end of file +COCOAPODS: 1.15.2 diff --git a/apps/metro-example-host/metro.config.js b/apps/metro-example-host/metro.config.js index b51dcf27fa9..ec49dd20eb5 100644 --- a/apps/metro-example-host/metro.config.js +++ b/apps/metro-example-host/metro.config.js @@ -11,10 +11,15 @@ const {withModuleFederation} = require('@module-federation/metro'); */ const config = { - resolver: {useWatchman: false}, + resolver: { + extraNodeModules: { + '@babel/runtime': path.resolve(__dirname, 'node_modules/@babel/runtime'), + }, + useWatchman: false, + }, watchFolders: [ path.resolve(__dirname, '../../node_modules'), - path.resolve(__dirname, '../../packages/core'), + path.resolve(__dirname, '../../packages'), ], }; diff --git a/apps/metro-example-mini/Gemfile.lock b/apps/metro-example-mini/Gemfile.lock index 5af6b649c94..6f58f7d9b75 100644 --- a/apps/metro-example-mini/Gemfile.lock +++ b/apps/metro-example-mini/Gemfile.lock @@ -1,31 +1,29 @@ GEM remote: https://rubygems.org/ specs: - CFPropertyList (3.0.7) - base64 - nkf - rexml - activesupport (7.2.2.1) + CFPropertyList (3.0.9) + activesupport (7.1.6) base64 benchmark (>= 0.3) bigdecimal - concurrent-ruby (~> 1.0, >= 1.3.1) + concurrent-ruby (~> 1.0, >= 1.0.2) connection_pool (>= 2.2.5) drb i18n (>= 1.6, < 2) logger (>= 1.4.2) minitest (>= 5.1) + mutex_m securerandom (>= 0.3) - tzinfo (~> 2.0, >= 2.0.5) - addressable (2.8.7) - public_suffix (>= 2.0.2, < 7.0) + tzinfo (~> 2.0) + addressable (2.8.8) + public_suffix (>= 2.0.2, < 8.0) algoliasearch (1.27.5) httpclient (~> 2.8, >= 2.8.3) json (>= 1.5.1) atomos (0.1.3) base64 (0.3.0) - benchmark (0.4.1) - bigdecimal (3.2.2) + benchmark (0.5.0) + bigdecimal (4.0.1) claide (1.1.0) cocoapods (1.15.2) addressable (~> 2.8) @@ -66,34 +64,33 @@ GEM cocoapods-try (1.2.0) colored2 (3.1.2) concurrent-ruby (1.3.3) - connection_pool (2.5.3) + connection_pool (2.5.5) drb (2.2.3) escape (0.0.4) - ethon (0.16.0) + ethon (0.15.0) ffi (>= 1.15.0) - ffi (1.17.2) + ffi (1.17.3) fourflusher (2.3.1) fuzzy_match (2.0.4) gh_inspector (1.1.3) httpclient (2.9.0) mutex_m - i18n (1.14.7) + i18n (1.14.8) concurrent-ruby (~> 1.0) - json (2.12.2) + json (2.18.1) logger (1.7.0) - minitest (5.25.5) + minitest (5.26.1) molinillo (0.8.0) mutex_m (0.3.0) nanaimo (0.3.0) nap (1.1.0) netrc (0.11.0) - nkf (0.2.0) public_suffix (4.0.7) - rexml (3.4.2) + rexml (3.4.4) ruby-macho (2.5.1) - securerandom (0.4.1) - typhoeus (1.4.1) - ethon (>= 0.9.0) + securerandom (0.3.2) + typhoeus (1.5.0) + ethon (>= 0.9.0, < 0.16.0) tzinfo (2.0.6) concurrent-ruby (~> 1.0) xcodeproj (1.25.1) @@ -118,7 +115,7 @@ DEPENDENCIES xcodeproj (< 1.26.0) RUBY VERSION - ruby 3.1.0p0 + ruby 2.7.6p219 BUNDLED WITH - 2.6.1 + 2.1.4 diff --git a/apps/metro-example-mini/ios/Podfile.lock b/apps/metro-example-mini/ios/Podfile.lock index 3b3b7029bb4..779df101702 100644 --- a/apps/metro-example-mini/ios/Podfile.lock +++ b/apps/metro-example-mini/ios/Podfile.lock @@ -2422,10 +2422,10 @@ SPEC CHECKSUMS: React-timing: a275a1c2e6112dba17f8f7dd496d439213bbea0d React-utils: 449a6e1fd53886510e284e80bdbb1b1c6db29452 ReactAppDependencyProvider: 3267432b637c9b38e86961b287f784ee1b08dde0 - ReactCodegen: a1a6d7288d6a5fc86f109e46149c35d707932702 + ReactCodegen: d308d08c58717331dcf82d0129efa8b73e28a64c ReactCommon: b028d09a66e60ebd83ca59d8cc9a1216360db147 SocketRocket: d4aabe649be1e368d1318fdf28a022d714d65748 - Yoga: 0c4b7d2aacc910a1f702694fa86be830386f4ceb + Yoga: 395b5d614cd7cbbfd76b05d01bd67230a6ad004e PODFILE CHECKSUM: a8134080201cda3c42e54a89f48d0930861e3c58 diff --git a/apps/metro-example-mini/metro.config.js b/apps/metro-example-mini/metro.config.js index 412ecc34f0b..bd86ea7af45 100644 --- a/apps/metro-example-mini/metro.config.js +++ b/apps/metro-example-mini/metro.config.js @@ -10,10 +10,15 @@ const {withModuleFederation} = require('@module-federation/metro'); * @type {import('@react-native/metro-config').MetroConfig} */ const config = { - resolver: {useWatchman: false}, + resolver: { + extraNodeModules: { + '@babel/runtime': path.resolve(__dirname, 'node_modules/@babel/runtime'), + }, + useWatchman: false, + }, watchFolders: [ path.resolve(__dirname, '../../node_modules'), - path.resolve(__dirname, '../../packages/core'), + path.resolve(__dirname, '../../packages'), ], }; diff --git a/apps/metro-example-nested-mini/Gemfile.lock b/apps/metro-example-nested-mini/Gemfile.lock index 792397b7eb9..6f58f7d9b75 100644 --- a/apps/metro-example-nested-mini/Gemfile.lock +++ b/apps/metro-example-nested-mini/Gemfile.lock @@ -1,31 +1,29 @@ GEM remote: https://rubygems.org/ specs: - CFPropertyList (3.0.7) - base64 - nkf - rexml - activesupport (7.2.2.1) + CFPropertyList (3.0.9) + activesupport (7.1.6) base64 benchmark (>= 0.3) bigdecimal - concurrent-ruby (~> 1.0, >= 1.3.1) + concurrent-ruby (~> 1.0, >= 1.0.2) connection_pool (>= 2.2.5) drb i18n (>= 1.6, < 2) logger (>= 1.4.2) minitest (>= 5.1) + mutex_m securerandom (>= 0.3) - tzinfo (~> 2.0, >= 2.0.5) - addressable (2.8.7) - public_suffix (>= 2.0.2, < 7.0) + tzinfo (~> 2.0) + addressable (2.8.8) + public_suffix (>= 2.0.2, < 8.0) algoliasearch (1.27.5) httpclient (~> 2.8, >= 2.8.3) json (>= 1.5.1) atomos (0.1.3) base64 (0.3.0) - benchmark (0.4.1) - bigdecimal (3.2.2) + benchmark (0.5.0) + bigdecimal (4.0.1) claide (1.1.0) cocoapods (1.15.2) addressable (~> 2.8) @@ -66,34 +64,33 @@ GEM cocoapods-try (1.2.0) colored2 (3.1.2) concurrent-ruby (1.3.3) - connection_pool (2.5.3) + connection_pool (2.5.5) drb (2.2.3) escape (0.0.4) - ethon (0.16.0) + ethon (0.15.0) ffi (>= 1.15.0) - ffi (1.17.2) + ffi (1.17.3) fourflusher (2.3.1) fuzzy_match (2.0.4) gh_inspector (1.1.3) httpclient (2.9.0) mutex_m - i18n (1.14.7) + i18n (1.14.8) concurrent-ruby (~> 1.0) - json (2.12.2) + json (2.18.1) logger (1.7.0) - minitest (5.25.5) + minitest (5.26.1) molinillo (0.8.0) mutex_m (0.3.0) nanaimo (0.3.0) nap (1.1.0) netrc (0.11.0) - nkf (0.2.0) public_suffix (4.0.7) - rexml (3.4.1) + rexml (3.4.4) ruby-macho (2.5.1) - securerandom (0.4.1) - typhoeus (1.4.1) - ethon (>= 0.9.0) + securerandom (0.3.2) + typhoeus (1.5.0) + ethon (>= 0.9.0, < 0.16.0) tzinfo (2.0.6) concurrent-ruby (~> 1.0) xcodeproj (1.25.1) @@ -118,7 +115,7 @@ DEPENDENCIES xcodeproj (< 1.26.0) RUBY VERSION - ruby 3.1.0p0 + ruby 2.7.6p219 BUNDLED WITH - 2.6.1 + 2.1.4 diff --git a/apps/metro-example-nested-mini/ios/Podfile.lock b/apps/metro-example-nested-mini/ios/Podfile.lock index 3b3b7029bb4..779df101702 100644 --- a/apps/metro-example-nested-mini/ios/Podfile.lock +++ b/apps/metro-example-nested-mini/ios/Podfile.lock @@ -2422,10 +2422,10 @@ SPEC CHECKSUMS: React-timing: a275a1c2e6112dba17f8f7dd496d439213bbea0d React-utils: 449a6e1fd53886510e284e80bdbb1b1c6db29452 ReactAppDependencyProvider: 3267432b637c9b38e86961b287f784ee1b08dde0 - ReactCodegen: a1a6d7288d6a5fc86f109e46149c35d707932702 + ReactCodegen: d308d08c58717331dcf82d0129efa8b73e28a64c ReactCommon: b028d09a66e60ebd83ca59d8cc9a1216360db147 SocketRocket: d4aabe649be1e368d1318fdf28a022d714d65748 - Yoga: 0c4b7d2aacc910a1f702694fa86be830386f4ceb + Yoga: 395b5d614cd7cbbfd76b05d01bd67230a6ad004e PODFILE CHECKSUM: a8134080201cda3c42e54a89f48d0930861e3c58 diff --git a/apps/metro-example-nested-mini/metro.config.js b/apps/metro-example-nested-mini/metro.config.js index 44fc2deeca4..d4c55a5ce72 100644 --- a/apps/metro-example-nested-mini/metro.config.js +++ b/apps/metro-example-nested-mini/metro.config.js @@ -10,10 +10,15 @@ const {withModuleFederation} = require('@module-federation/metro'); * @type {import('@react-native/metro-config').MetroConfig} */ const config = { - resolver: {useWatchman: false}, + resolver: { + extraNodeModules: { + '@babel/runtime': path.resolve(__dirname, 'node_modules/@babel/runtime'), + }, + useWatchman: false, + }, watchFolders: [ path.resolve(__dirname, '../../node_modules'), - path.resolve(__dirname, '../../packages/core'), + path.resolve(__dirname, '../../packages'), ], }; diff --git a/packages/metro-core/src/commands/bundle-host/index.ts b/packages/metro-core/src/commands/bundle-host/index.ts index dfe3908d604..b9d76dcc690 100644 --- a/packages/metro-core/src/commands/bundle-host/index.ts +++ b/packages/metro-core/src/commands/bundle-host/index.ts @@ -1,9 +1,9 @@ import path from 'node:path'; import util from 'node:util'; -import Server from 'metro/src/Server'; -import type { RequestOptions } from 'metro/src/shared/types'; import type { ModuleFederationConfigNormalized } from '../../types'; import { CLIError } from '../../utils/errors'; +import type { RequestOptions } from '../../utils/metro-compat'; +import { Server } from '../../utils/metro-compat'; import type { Config } from '../types'; import { createResolver } from '../utils/create-resolver'; import { getCommunityCliPlugin } from '../utils/get-community-plugin'; diff --git a/packages/metro-core/src/commands/bundle-remote/index.ts b/packages/metro-core/src/commands/bundle-remote/index.ts index 946eac4eb4a..8a43859324f 100644 --- a/packages/metro-core/src/commands/bundle-remote/index.ts +++ b/packages/metro-core/src/commands/bundle-remote/index.ts @@ -3,10 +3,10 @@ import path from 'node:path'; import { pathToFileURL } from 'node:url'; import util from 'node:util'; import { mergeConfig } from 'metro'; -import Server from 'metro/src/Server'; -import type { OutputOptions, RequestOptions } from 'metro/src/shared/types'; import type { ModuleFederationConfigNormalized } from '../../types'; import { CLIError } from '../../utils/errors'; +import type { OutputOptions, RequestOptions } from '../../utils/metro-compat'; +import { Server } from '../../utils/metro-compat'; import type { Config } from '../types'; import { createModulePathRemapper } from '../utils/create-module-path-remapper'; import { createResolver } from '../utils/create-resolver'; diff --git a/packages/metro-core/src/commands/utils/create-resolver.ts b/packages/metro-core/src/commands/utils/create-resolver.ts index f12574a2cad..232807d0fc2 100644 --- a/packages/metro-core/src/commands/utils/create-resolver.ts +++ b/packages/metro-core/src/commands/utils/create-resolver.ts @@ -1,4 +1,4 @@ -import type Server from 'metro/src/Server'; +import type { Server } from '../../utils/metro-compat'; /** * Creates a resolver utility that mirrors Metro's bundling resolution behavior. diff --git a/packages/metro-core/src/commands/utils/get-community-plugin.ts b/packages/metro-core/src/commands/utils/get-community-plugin.ts index 7a1e73be00a..7df6e9c31fd 100644 --- a/packages/metro-core/src/commands/utils/get-community-plugin.ts +++ b/packages/metro-core/src/commands/utils/get-community-plugin.ts @@ -1,7 +1,10 @@ import type { ConfigT } from 'metro-config'; -import type Server from 'metro/src/Server'; -import type { OutputOptions, RequestOptions } from 'metro/src/shared/types'; import { CLIError } from '../../utils/errors'; +import type { + OutputOptions, + RequestOptions, + Server, +} from '../../utils/metro-compat'; interface Command { name: string; @@ -35,15 +38,14 @@ interface CommunityCliPlugin { } export function getCommunityCliPlugin(reactNativePath?: string) { - let communityCliPlugin: CommunityCliPlugin; + let communityCliPluginPath: string; try { - const communityCliPluginPath = require.resolve( + communityCliPluginPath = require.resolve( '@react-native/community-cli-plugin', { paths: [reactNativePath ?? require.resolve('react-native')] }, ); - communityCliPlugin = require(communityCliPluginPath); } catch { throw new CLIError('Community CLI plugin is not installed.'); } - return communityCliPlugin; + return require(communityCliPluginPath) as CommunityCliPlugin; } diff --git a/packages/metro-core/src/commands/utils/save-bundle-and-map.ts b/packages/metro-core/src/commands/utils/save-bundle-and-map.ts index c059a28bf83..ea42161797b 100644 --- a/packages/metro-core/src/commands/utils/save-bundle-and-map.ts +++ b/packages/metro-core/src/commands/utils/save-bundle-and-map.ts @@ -1,8 +1,8 @@ import { promises as fs } from 'node:fs'; import util from 'node:util'; import type { MixedSourceMap } from 'metro-source-map'; -import relativizeSourceMapInline from 'metro/src/lib/relativizeSourceMap'; -import type { OutputOptions } from 'metro/src/shared/types'; +import type { OutputOptions } from '../../utils/metro-compat'; +import { relativizeSourceMapInline } from '../../utils/metro-compat'; function relativizeSerializedMap( map: string, diff --git a/packages/metro-core/src/plugin/serializer.ts b/packages/metro-core/src/plugin/serializer.ts index a84f94e35df..cec91e81fdd 100644 --- a/packages/metro-core/src/plugin/serializer.ts +++ b/packages/metro-core/src/plugin/serializer.ts @@ -1,11 +1,13 @@ import path from 'node:path'; import type { Module, ReadOnlyGraph, SerializerOptions } from 'metro'; import type { SerializerConfigT } from 'metro-config'; -import baseJSBundle from 'metro/src/DeltaBundler/Serializers/baseJSBundle'; -import CountingSet from 'metro/src/lib/CountingSet'; -import bundleToString from 'metro/src/lib/bundleToString'; import type { ModuleFederationConfigNormalized, Shared } from '../types'; import { ConfigError } from '../utils/errors'; +import { + CountingSet, + baseJSBundle, + bundleToString, +} from '../utils/metro-compat'; type CustomSerializer = SerializerConfigT['customSerializer']; @@ -141,7 +143,7 @@ function generateVirtualModule(name: string, code: string): Module { return { dependencies: new Map(), getSource: (): Buffer => Buffer.from(code), - inverseDependencies: new CountingSet(), + inverseDependencies: new CountingSet(), path: name, output: [ { diff --git a/packages/metro-core/src/types/metro/baseJSBundle.d.ts b/packages/metro-core/src/types/metro/baseJSBundle.d.ts index 3bbb9660be9..4ed4414e466 100644 --- a/packages/metro-core/src/types/metro/baseJSBundle.d.ts +++ b/packages/metro-core/src/types/metro/baseJSBundle.d.ts @@ -1,10 +1,10 @@ -import type { - Module, - ReadOnlyGraph, - SerializerOptions, -} from 'metro/src/DeltaBundler/types'; - declare module 'metro/src/DeltaBundler/Serializers/baseJSBundle' { + import type { + Module, + ReadOnlyGraph, + SerializerOptions, + } from 'metro/src/DeltaBundler/types'; + interface Bundle { modules: readonly [number, string][]; post: string; diff --git a/packages/metro-core/src/types/metro/getAppendScripts.d.ts b/packages/metro-core/src/types/metro/getAppendScripts.d.ts index 1514bf44284..a56aec403a0 100644 --- a/packages/metro-core/src/types/metro/getAppendScripts.d.ts +++ b/packages/metro-core/src/types/metro/getAppendScripts.d.ts @@ -1,6 +1,5 @@ -import type { Module } from 'metro/src/DeltaBundler/types'; - declare module 'metro/src/lib/getAppendScripts' { + import type { Module } from 'metro/src/DeltaBundler/types'; interface Options { asyncRequireModulePath: string; createModuleId: (path: string) => T; diff --git a/packages/metro-core/src/types/metro/processModules.d.ts b/packages/metro-core/src/types/metro/processModules.d.ts index 5b414a46afe..a7de84c74c1 100644 --- a/packages/metro-core/src/types/metro/processModules.d.ts +++ b/packages/metro-core/src/types/metro/processModules.d.ts @@ -1,6 +1,5 @@ -import type { Module } from 'metro/src/DeltaBundler/types'; - declare module 'metro/src/DeltaBundler/Serializers/helpers/processModules' { + import type { Module } from 'metro/src/DeltaBundler/types'; interface Options { filter?: (module: Module) => boolean; createModuleId: (path: string) => number; diff --git a/packages/metro-core/src/types/metro/relativizeSourceMap.d.ts b/packages/metro-core/src/types/metro/relativizeSourceMap.d.ts index 335944e4688..043eac5ad95 100644 --- a/packages/metro-core/src/types/metro/relativizeSourceMap.d.ts +++ b/packages/metro-core/src/types/metro/relativizeSourceMap.d.ts @@ -1,8 +1,13 @@ -import type { MixedSourceMap } from 'metro-source-map'; - declare module 'metro/src/lib/relativizeSourceMap' { + import type { MixedSourceMap } from 'metro-source-map'; + export default function relativizeSourceMap( sourceMap: MixedSourceMap, sourcesRoot: string, ): void; + + export function relativizeSourceMapInline( + sourceMap: MixedSourceMap, + sourcesRoot: string, + ): void; } diff --git a/packages/metro-core/src/utils/metro-compat.ts b/packages/metro-core/src/utils/metro-compat.ts new file mode 100644 index 00000000000..fe2e3e9d171 --- /dev/null +++ b/packages/metro-core/src/utils/metro-compat.ts @@ -0,0 +1,94 @@ +/** + * Metro Compatibility Layer + * + * Provides backwards-compatible imports for Metro 0.82 and 0.83+ + * + * Metro 0.83 introduced a restrictive `exports` field that only allows + * `metro/private/*` paths instead of direct `metro/src/*` imports. + * + * This module dynamically resolves the correct import path based on the + * installed Metro version, ensuring compatibility with both versions. + */ + +// Type-only imports — resolved at compile-time via metro/src/*.d.ts, erased at runtime +import type DefaultServer from 'metro/src/Server'; +import type DefaultBaseJSBundle from 'metro/src/DeltaBundler/Serializers/baseJSBundle'; +import type DefaultCountingSet from 'metro/src/lib/CountingSet'; +import type DefaultBundleToString from 'metro/src/lib/bundleToString'; +import type { MixedSourceMap } from 'metro-source-map'; + +/** + * Resolves and imports a Metro module, trying 0.83 path first then 0.82 fallback. + * Uses require.resolve() before require() so resolution failures (wrong path) + * are separated from load errors (broken module), enabling better diagnostics. + */ +function resolveAndImport(metro83Path: string, metro82Path: string) { + let resolvedPath: string; + try { + resolvedPath = require.resolve(metro83Path); + } catch { + try { + resolvedPath = require.resolve(metro82Path); + } catch { + throw new Error( + `Could not resolve 'metro' module. Tried:\n` + + ` - ${metro83Path}\n` + + ` - ${metro82Path}\n` + + `Ensure 'metro' is installed.`, + ); + } + } + return require(resolvedPath); +} + +function getDefaultExport(mod: any) { + if (mod != null && typeof mod === 'object' && 'default' in mod) { + return mod.default; + } + return mod; +} + +// Server class +export const Server = getDefaultExport( + resolveAndImport('metro/private/Server', 'metro/src/Server'), +) as typeof DefaultServer; +export type Server = DefaultServer; + +// DeltaBundler Serializers +export const baseJSBundle = getDefaultExport( + resolveAndImport( + 'metro/private/DeltaBundler/Serializers/baseJSBundle', + 'metro/src/DeltaBundler/Serializers/baseJSBundle', + ), +) as typeof DefaultBaseJSBundle; + +// Utility classes +export const CountingSet = getDefaultExport( + resolveAndImport( + 'metro/private/lib/CountingSet', + 'metro/src/lib/CountingSet', + ), +) as typeof DefaultCountingSet; + +// Bundle utilities +export const bundleToString = getDefaultExport( + resolveAndImport( + 'metro/private/lib/bundleToString', + 'metro/src/lib/bundleToString', + ), +) as typeof DefaultBundleToString; + +const relativizeSourceMapModule = resolveAndImport( + 'metro/private/lib/relativizeSourceMap', + 'metro/src/lib/relativizeSourceMap', +); +export const relativizeSourceMapInline: ( + sourceMap: MixedSourceMap, + sourcesRoot: string, +) => void = + relativizeSourceMapModule.relativizeSourceMapInline || + getDefaultExport(relativizeSourceMapModule); + +// Re-export types - these come from metro/src/shared/types in both versions +// The types themselves are available through the main 'metro' package export +export type { RequestOptions, OutputOptions } from 'metro/src/shared/types'; diff --git a/packages/metro-core/src/utils/vm-manager.ts b/packages/metro-core/src/utils/vm-manager.ts index 7babfca544a..836d9ffb36c 100644 --- a/packages/metro-core/src/utils/vm-manager.ts +++ b/packages/metro-core/src/utils/vm-manager.ts @@ -6,7 +6,7 @@ import type { TransformerConfigT, } from 'metro-config'; import type { FileSystem } from 'metro-file-map'; -import type MetroServer from 'metro/src/Server'; +import type { Server as MetroServer } from './metro-compat'; type EnhanceMiddleware = ServerConfigT['enhanceMiddleware']; type GetTransformOptions = TransformerConfigT['getTransformOptions']; diff --git a/packages/metro-core/tsconfig.json b/packages/metro-core/tsconfig.json index 798324680dc..2c0aa558177 100644 --- a/packages/metro-core/tsconfig.json +++ b/packages/metro-core/tsconfig.json @@ -2,6 +2,9 @@ "extends": "../../tsconfig.base.json", "include": ["src"], "compilerOptions": { - "rootDir": "src" + "rootDir": "src", + "paths": { + "metro/private/*": ["metro/src/*"] + } } }