diff --git a/packages/core/package.json b/packages/core/package.json index bb1c632f..540051b6 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -52,11 +52,11 @@ }, "peerDependencies": { "@babel/types": "^7.25.0", - "metro": "^0.82.1", - "metro-config": "^0.82.1", - "metro-file-map": "^0.82.1", - "metro-resolver": "^0.82.1", - "metro-source-map": "^0.82.1", + "metro": "^0.82.1 || ^0.83.0", + "metro-config": "^0.82.1 || ^0.83.0", + "metro-file-map": "^0.82.1 || ^0.83.0", + "metro-resolver": "^0.82.1 || ^0.83.0", + "metro-source-map": "^0.82.1 || ^0.83.0", "react": ">=19.0.0", "react-native": ">=0.79.0" }, diff --git a/packages/core/src/commands/bundle-host/index.ts b/packages/core/src/commands/bundle-host/index.ts index 60fa7899..60c1f882 100644 --- a/packages/core/src/commands/bundle-host/index.ts +++ b/packages/core/src/commands/bundle-host/index.ts @@ -1,9 +1,8 @@ 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, Server } from '../../utils/metro-compat'; import type { Config } from '../types'; import { createResolver } from '../utils/create-resolver'; import { getCommunityCliPlugin } from '../utils/get-community-plugin'; @@ -54,7 +53,10 @@ async function bundleFederatedHost( communityCliPlugin.unstable_buildBundleWithConfig; return buildBundleWithConfig(args, config, { - build: async (server: Server, requestOpts: RequestOptions) => { + build: async ( + server: InstanceType, + requestOpts: RequestOptions + ) => { // setup enhance middleware to trigger virtual modules setup config.server.enhanceMiddleware(server.processRequest, server); const resolver = await createResolver(server, args.platform); diff --git a/packages/core/src/commands/bundle-remote/index.ts b/packages/core/src/commands/bundle-remote/index.ts index 775dcd53..025c4ebf 100644 --- a/packages/core/src/commands/bundle-remote/index.ts +++ b/packages/core/src/commands/bundle-remote/index.ts @@ -3,10 +3,13 @@ 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, + type RequestOptions, + Server, +} from '../../utils/metro-compat'; import type { Config } from '../types'; import { createModulePathRemapper } from '../utils/create-module-path-remapper'; import { createResolver } from '../utils/create-resolver'; @@ -39,7 +42,10 @@ interface BundleRequestOptions extends RequestOptions { sourceUrl: string; } -async function buildBundle(server: Server, requestOpts: BundleRequestOptions) { +async function buildBundle( + server: InstanceType, + requestOpts: BundleRequestOptions +) { const bundle = await server.build({ ...Server.DEFAULT_BUNDLE_OPTIONS, ...requestOpts, diff --git a/packages/core/src/commands/utils/create-resolver.ts b/packages/core/src/commands/utils/create-resolver.ts index f12574a2..3553ebf5 100644 --- a/packages/core/src/commands/utils/create-resolver.ts +++ b/packages/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. @@ -14,7 +14,10 @@ import type Server from 'metro/src/Server'; * @param platform - The target platform for resolution (e.g., 'ios', 'android', null for default) * @returns The resolver object with a resolve method that takes source and target paths */ -export async function createResolver(server: Server, platform: string | null) { +export async function createResolver( + server: InstanceType, + platform: string | null +) { const bundler = server.getBundler().getBundler(); const depGraph = await bundler.getDependencyGraph(); diff --git a/packages/core/src/commands/utils/get-community-plugin.ts b/packages/core/src/commands/utils/get-community-plugin.ts index 5cfa6dc5..30ba5fe2 100644 --- a/packages/core/src/commands/utils/get-community-plugin.ts +++ b/packages/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; @@ -21,7 +24,7 @@ interface CommunityCliPlugin { config: ConfigT, bundleImpl: { build: ( - server: Server, + server: InstanceType, requestOpts: RequestOptions ) => Promise<{ code: string; map: string }>; save: ( diff --git a/packages/core/src/commands/utils/save-bundle-and-map.ts b/packages/core/src/commands/utils/save-bundle-and-map.ts index 9af40eef..6c3b1da4 100644 --- a/packages/core/src/commands/utils/save-bundle-and-map.ts +++ b/packages/core/src/commands/utils/save-bundle-and-map.ts @@ -1,8 +1,10 @@ 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, + relativizeSourceMapInline, +} from '../../utils/metro-compat'; function relativizeSerializedMap( map: string, diff --git a/packages/core/src/plugin/serializer.ts b/packages/core/src/plugin/serializer.ts index 7742bcd8..77a5b949 100644 --- a/packages/core/src/plugin/serializer.ts +++ b/packages/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']; diff --git a/packages/core/src/utils/metro-compat.ts b/packages/core/src/utils/metro-compat.ts new file mode 100644 index 00000000..515b3a41 --- /dev/null +++ b/packages/core/src/utils/metro-compat.ts @@ -0,0 +1,55 @@ +/** + * 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. + */ + +/** + * Attempts to import from Metro 0.83 format first, falls back to 0.82 format + */ +function tryImport(metro83Path: string, metro82Path: string) { + try { + return require(metro83Path); + } catch { + return require(metro82Path); + } +} + +// Server class +export const Server = tryImport( + 'metro/private/Server', + 'metro/src/Server' +).default; + +// DeltaBundler Serializers +export const baseJSBundle = tryImport( + 'metro/private/DeltaBundler/Serializers/baseJSBundle', + 'metro/src/DeltaBundler/Serializers/baseJSBundle' +).default; + +// Utility classes +export const CountingSet = tryImport( + 'metro/private/lib/CountingSet', + 'metro/src/lib/CountingSet' +).default; + +// Bundle utilities +export const bundleToString = tryImport( + 'metro/private/lib/bundleToString', + 'metro/src/lib/bundleToString' +).default; + +export const relativizeSourceMapInline = tryImport( + 'metro/private/lib/relativizeSourceMap', + 'metro/src/lib/relativizeSourceMap' +).relativizeSourceMapInline; + +// 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/core/src/utils/vm-manager.ts b/packages/core/src/utils/vm-manager.ts index 497f4a5e..dfeb40e6 100644 --- a/packages/core/src/utils/vm-manager.ts +++ b/packages/core/src/utils/vm-manager.ts @@ -6,11 +6,13 @@ 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']; -type Bundler = ReturnType['getBundler']>; +type Bundler = ReturnType< + ReturnType['getBundler']>['getBundler'] +>; export class VirtualModuleManager { private setupFinished: Promise | null = null; @@ -69,8 +71,8 @@ export class VirtualModuleManager { setup(bundler: Bundler) { this.setupFinished = (async () => { const graph = await bundler.getDependencyGraph(); - // @ts-expect-error incomplete types - this.ensureFileSystemPatched(graph._fileSystem); + // eslint-disable-next-line @typescript-eslint/no-explicit-any + this.ensureFileSystemPatched((graph as any)._fileSystem); this.ensureBundlerPatched(bundler); return true; })(); @@ -104,7 +106,11 @@ export class VirtualModuleManager { } const transformFile = bundler.transformFile.bind(bundler); - bundler.transformFile = async (filePath, transformOptions, fileBuffer) => { + bundler.transformFile = async ( + filePath: string, + transformOptions: any, + fileBuffer?: Buffer + ) => { let buffer = fileBuffer; const virtualModule = this.virtualModules.get(filePath);