From 61e4425494d7894cd99eca37f0ae758af7da3592 Mon Sep 17 00:00:00 2001 From: bholmesdev Date: Fri, 17 Mar 2023 18:32:49 -0400 Subject: [PATCH] fix: wait for in-flight entry resolution --- .../content/vite-plugin-content-imports.ts | 39 ++++++++++++++++--- 1 file changed, 33 insertions(+), 6 deletions(-) diff --git a/packages/astro/src/content/vite-plugin-content-imports.ts b/packages/astro/src/content/vite-plugin-content-imports.ts index bc445c9d1..19136446c 100644 --- a/packages/astro/src/content/vite-plugin-content-imports.ts +++ b/packages/astro/src/content/vite-plugin-content-imports.ts @@ -45,16 +45,13 @@ export function astroContentImportPlugin({ } } - // Used by the `render-module` plugin to avoid double-parsing your schema - const contentEntryModuleByIdCache = new Map(); - const plugins: Plugin[] = [ { name: 'astro:content-imports', async load(viteId) { if (isContentFlagImport(viteId, contentEntryExts)) { const { fileId } = getFileInfo(viteId, settings.config); - const { id, slug, collection, body, data, _internal } = await getContentEntryModule({ + const { id, slug, collection, body, data, _internal } = await setContentEntryModuleCache({ fileId, pluginContext: this, }); @@ -114,7 +111,9 @@ export const _internal = { const { fileId } = getFileInfo(viteId, settings.config); for (const contentEntryType of settings.contentEntryTypes) { if (contentEntryType.getRenderModule) { - const entry = await contentEntryModuleByIdCache.get(fileId); + const entry = await getContentEntryModuleFromCache(fileId); + // Cached entry must exist (or be in-flight) when importing the module via content collections. + // This is ensured by the `astro:content-imports` plugin. if (!entry) throw new AstroError({ ...AstroErrorData.UnknownContentCollectionError, @@ -130,13 +129,34 @@ export const _internal = { }); } - async function getContentEntryModule({ + // Used by the `render-module` plugin to avoid double-parsing your schema + const contentEntryModuleByIdCache = new Map(); + const awaitingCacheById = new Map void)[]>(); + function getContentEntryModuleFromCache(id: string) { + const value = contentEntryModuleByIdCache.get(id); + // It's possible for Vite to load modules that depend on this cache + // before the cache is populated. In that case, we queue a promise + // to be resolved by `setContentEntryModuleCache`. + if (value === 'loading') { + return new Promise((resolve, reject) => { + const awaiting = awaitingCacheById.get(id) ?? []; + awaiting.push(resolve); + awaitingCacheById.set(id, awaiting); + }); + } else if (value) { + return Promise.resolve(value); + } + return Promise.resolve(undefined); + } + + async function setContentEntryModuleCache({ fileId, pluginContext, }: { fileId: string; pluginContext: PluginContext; }): Promise { + contentEntryModuleByIdCache.set(fileId, 'loading'); const observable = globalContentConfigObserver.get(); // Content config should be loaded before this plugin is used @@ -211,6 +231,13 @@ export const _internal = { body: info.body, }; contentEntryModuleByIdCache.set(fileId, contentEntryModule); + const awaiting = awaitingCacheById.get(fileId); + if (awaiting) { + for (const resolve of awaiting) { + resolve(contentEntryModule); + } + awaitingCacheById.delete(fileId); + } return contentEntryModule; }