diff --git a/src/index.ts b/src/index.ts index a6f840b..acff7c4 100644 --- a/src/index.ts +++ b/src/index.ts @@ -8,3 +8,4 @@ export { AtlasError, AtlasValidationError } from './utils/errors'; export { createAtlasMiddleware } from './utils/middleware'; export { fuzzyFilterModules } from './utils/search'; export { createStatsFile, validateStatsFile, getStatsMetdata, getStatsPath } from './utils/stats'; +export { attachMetroSerializer } from './utils/metro'; diff --git a/src/metro.ts b/src/metro.ts index b4eea86..e8900aa 100644 --- a/src/metro.ts +++ b/src/metro.ts @@ -2,6 +2,7 @@ import { type MetroConfig } from 'metro-config'; import { convertGraph } from './data/MetroGraphSource'; import { writeStatsEntry } from './data/StatsFileSource'; +import { attachMetroSerializer } from './utils/metro'; import { createStatsFile, getStatsPath } from './utils/stats'; type ExpoAtlasOptions = Partial<{ @@ -27,7 +28,6 @@ type ExpoAtlasOptions = Partial<{ */ export function withExpoAtlas(config: MetroConfig, options: ExpoAtlasOptions = {}) { const projectRoot = config.projectRoot; - const originalSerializer = config.serializer?.customSerializer ?? (() => {}); if (!projectRoot) { throw new Error('No "projectRoot" configured in Metro config.'); @@ -42,16 +42,13 @@ export function withExpoAtlas(config: MetroConfig, options: ExpoAtlasOptions = { // Note(cedric): we don't have to await this, Metro would never bundle before this is finisheds createStatsFile(statsFile); - // @ts-expect-error - config.serializer.customSerializer = (entryPoint, preModules, graph, options) => { + attachMetroSerializer(config, (entryPoint, preModules, graph, options) => { // Note(cedric): we don't have to await this, it has a built-in write queue writeStatsEntry( statsFile, convertGraph({ projectRoot, entryPoint, preModules, graph, options, extensions }) ); - - return originalSerializer(entryPoint, preModules, graph, options); - }; + }); return config; } diff --git a/src/utils/metro.ts b/src/utils/metro.ts new file mode 100644 index 0000000..5265351 --- /dev/null +++ b/src/utils/metro.ts @@ -0,0 +1,36 @@ +import type { ConfigT as MetroConfig, InputConfigT as MetroInputConfig } from 'metro-config'; + +const atlasSerializerSymbol = Symbol('expo-atlas-serializer'); +const originalSerializerSymbol = Symbol('expo-atlas-original-serializer'); + +type MetroSerializerParams = Parameters> +type AtlasSerializer = (...params: MetroSerializerParams) => void; + +/** + * Attach a custom serializer, marked as being an Expo Atlas serializer. + * If there was a previous serializer, it will be overwritten to avoid conflicts. + */ +export function attachMetroSerializer( + config: MetroConfig | MetroInputConfig, + serializer: AtlasSerializer +) { + // @ts-expect-error + if (!config.serializer) config.serializer = {}; + + let prevSerializer = config.serializer?.customSerializer; + + // Already attached, overwrite the serializer + if (prevSerializer?.[atlasSerializerSymbol]) { + prevSerializer = prevSerializer[originalSerializerSymbol]; + } + + // @ts-expect-error + config.serializer.customSerializer = (entryPoint, preModules, graph, options) => { + serializer(entryPoint, preModules, graph, options); + return prevSerializer?.(entryPoint, preModules, graph, options); + }; + + // Mark this serializer as being atlas + config.serializer.customSerializer![atlasSerializerSymbol] = true; + config.serializer.customSerializer![originalSerializerSymbol] = prevSerializer; +}