diff --git a/src/data/AtlasFileSource.ts b/src/data/AtlasFileSource.ts index 809546d..90c9b5b 100644 --- a/src/data/AtlasFileSource.ts +++ b/src/data/AtlasFileSource.ts @@ -83,8 +83,9 @@ export async function readAtlasEntry(filePath: string, id: number): Promise = Promise.resolve(); /** - * Add a new entry to the file. - * This is appended on a new line, so we can load the selectively. + * Add a new entry to the Atlas file. + * This function also ensures the Atlas file is ready to be written to, due to complications with Expo CLI. + * Eventually, the entry is appended on a new line, so we can load them selectively. */ export function writeAtlasEntry(filePath: string, entry: AtlasBundle) { const line = [ @@ -98,7 +99,9 @@ export function writeAtlasEntry(filePath: string, entry: AtlasBundle) { entry.serializeOptions, ]; - return (writeQueue = writeQueue.then(() => appendJsonLine(filePath, line))); + writeQueue = writeQueue.then(() => appendJsonLine(filePath, line)); + + return writeQueue; } /** The default location of the metro file */ @@ -137,3 +140,21 @@ export async function createAtlasFile(filePath: string) { await fs.promises.rm(filePath, { force: true }); await appendJsonLine(filePath, getAtlasMetdata()); } + +/** + * Create the Atlas file if it doesn't exist, or recreate it if it's incompatible. + */ +export async function ensureAtlasFileExist(filePath: string) { + try { + await validateAtlasFile(filePath); + } catch (error: any) { + if (error.code === 'ATLAS_FILE_NOT_FOUND' || error.code === 'ATLAS_FILE_INCOMPATIBLE') { + await createAtlasFile(filePath); + return false; + } + + throw error; + } + + return true; +} diff --git a/src/index.ts b/src/index.ts index 45d57b5..5a1f1ac 100644 --- a/src/index.ts +++ b/src/index.ts @@ -5,6 +5,7 @@ export { MetroGraphSource } from './data/MetroGraphSource'; export { AtlasFileSource, createAtlasFile, + ensureAtlasFileExist, validateAtlasFile, getAtlasMetdata, getAtlasPath, diff --git a/src/metro.ts b/src/metro.ts index 841b3e9..7754929 100644 --- a/src/metro.ts +++ b/src/metro.ts @@ -1,6 +1,11 @@ import { type MetroConfig } from 'metro-config'; -import { createAtlasFile, getAtlasPath, writeAtlasEntry } from './data/AtlasFileSource'; +import { + createAtlasFile, + ensureAtlasFileExist, + getAtlasPath, + writeAtlasEntry, +} from './data/AtlasFileSource'; import { convertGraph, convertMetroConfig } from './data/MetroGraphSource'; type ExpoAtlasOptions = Partial<{ @@ -36,7 +41,7 @@ export function withExpoAtlas(config: MetroConfig, options: ExpoAtlasOptions = { const metroConfig = convertMetroConfig(config); // Note(cedric): we don't have to await this, Metro would never bundle before this is finishes - createAtlasFile(atlasFile); + ensureAtlasFileExist(atlasFile); // @ts-expect-error config.serializer.customSerializer = (entryPoint, preModules, graph, serializeOptions) => { @@ -51,3 +56,13 @@ export function withExpoAtlas(config: MetroConfig, options: ExpoAtlasOptions = { return config; } + +/** + * Fully reset, or recreate, the Expo Atlas file containing all Metro information. + * This method should only be called once per exporting session, to avoid overwriting data with mutliple Metro instances. + */ +export async function resetExpoAtlasFile(projectRoot: string) { + const filePath = getAtlasPath(projectRoot); + await createAtlasFile(filePath); + return filePath; +}