import { CyclicDependencies , PluginOptions as CyclicDetectorOptions , } from "@rnx-kit/metro-plugin-cyclic-dependencies-detector" ; import { DuplicateDependencies , Options as DuplicateDetectorOptions , } from "@rnx-kit/metro-plugin-duplicates-checker" ; import { MetroPlugin , MetroSerializer } from "@rnx-kit/metro-serializer" ; import { esbuildTransformerConfig , MetroSerializer as MetroSerializerEsbuild , } from "@rnx-kit/metro-serializer-esbuild" ; import { AllPlatforms } from "@rnx-kit/tools-react-native/platform" ; import { Project } from "@rnx-kit/typescript-service" ; import { createProjectCache } from "./typescript/project-cache" ; import type { TypeScriptValidationOptions } from "./types" ; import type { DeltaResult , Graph } from "metro" ; import type { InputConfigT , SerializerConfigT } from "metro-config" ; param options returns function createSerializerHook ( options ) { const projectCache = createProjectCache ( options . print ) ; const patternNodeModules = /[/\\]node_modules[/\\]/ ; const excludeNodeModules = ( p ) => ! patternNodeModules . test ( p ) ; const hook = ( graph , delta ) : void => { const platform = graph . transformOptions . platform as AllPlatforms ; if ( platform ) { if ( delta . reset ) { projectCache . clearPlatform ( platform ) ; } const adds = Array . from ( delta . added . values ( ) , ( module ) => module . path ) . filter ( excludeNodeModules ) ; const updates = Array . from ( delta . modified . values ( ) , ( module ) => module . path ) . filter ( excludeNodeModules ) ; const deletes = Array . from ( delta . deleted . values ( ) ) . filter ( excludeNodeModules ) ; const tsprojectsToValidate = new Set ( ) ; adds . concat ( updates ) . forEach ( ( sourceFile ) => { const { tsproject , tssourceFiles } = projectCache . getProjectInfo ( sourceFile , platform ) ; if ( tssourceFiles . has ( sourceFile ) ) { tsproject . setFile ( sourceFile ) ; tsprojectsToValidate . add ( tsproject ) ; } } ) ; deletes . forEach ( ( sourceFile ) => { const { tsproject } = projectCache . getProjectInfo ( sourceFile , platform ) ; tsproject . removeFile ( sourceFile ) ; tsprojectsToValidate . add ( tsproject ) ; } ) ; let isValid = true ; tsprojectsToValidate . forEach ( ( p ) => { if ( ! p . validate ( ) ) { isValid = false ; } } ) ; if ( ! isValid && options . throwOnError ) { throw new Error ( "Type validation failed" ) ; } } } ; return hook ; } const emptySerializerHook = ( _graph , _delta ) : void => { } ; param metroConfigReadonly param detectCyclicDependencies param detectDuplicateDependencies param typescriptValidation param experimental_treeShake export function customizeMetroConfig ( metroConfigReadonly , detectCyclicDependencies , detectDuplicateDependencies , typescriptValidation , experimental_treeShake ) { const metroConfig = metroConfigReadonly as InputConfigT ; const plugins = [ ] ; if ( typeof detectDuplicateDependencies === "object" ) { plugins . push ( DuplicateDependencies ( detectDuplicateDependencies ) ) ; } else if ( detectDuplicateDependencies !== false ) { plugins . push ( DuplicateDependencies ( ) ) ; } if ( typeof detectCyclicDependencies === "object" ) { plugins . push ( CyclicDependencies ( detectCyclicDependencies ) ) ; } else if ( detectCyclicDependencies !== false ) { plugins . push ( CyclicDependencies ( ) ) ; } if ( experimental_treeShake ) { metroConfig . serializer . customSerializer = MetroSerializerEsbuild ( plugins ) ; Object . assign ( metroConfig . transformer , esbuildTransformerConfig ) ; } else if ( plugins . length > 0 ) { metroConfig . serializer . customSerializer = MetroSerializer ( plugins ) as SerializerConfigT [ "customSerializer" ] ; } else { delete metroConfig . serializer . customSerializer ; } let hook = emptySerializerHook ; if ( typeof typescriptValidation === "object" ) { hook = createSerializerHook ( typescriptValidation ) ; } else if ( typescriptValidation !== false ) { hook = createSerializerHook ( { } ) ; } metroConfig . serializer . experimentalSerializerHook = hook ; }