fix: wait for in-flight entry resolution

This commit is contained in:
bholmesdev 2023-03-17 18:32:49 -04:00
parent acbb8b7e6d
commit 61e4425494

View file

@ -45,16 +45,13 @@ export function astroContentImportPlugin({
} }
} }
// Used by the `render-module` plugin to avoid double-parsing your schema
const contentEntryModuleByIdCache = new Map<string, ContentEntryModule>();
const plugins: Plugin[] = [ const plugins: Plugin[] = [
{ {
name: 'astro:content-imports', name: 'astro:content-imports',
async load(viteId) { async load(viteId) {
if (isContentFlagImport(viteId, contentEntryExts)) { if (isContentFlagImport(viteId, contentEntryExts)) {
const { fileId } = getFileInfo(viteId, settings.config); 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, fileId,
pluginContext: this, pluginContext: this,
}); });
@ -114,7 +111,9 @@ export const _internal = {
const { fileId } = getFileInfo(viteId, settings.config); const { fileId } = getFileInfo(viteId, settings.config);
for (const contentEntryType of settings.contentEntryTypes) { for (const contentEntryType of settings.contentEntryTypes) {
if (contentEntryType.getRenderModule) { 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) if (!entry)
throw new AstroError({ throw new AstroError({
...AstroErrorData.UnknownContentCollectionError, ...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<string, ContentEntryModule | 'loading'>();
const awaitingCacheById = new Map<string, ((val: ContentEntryModule) => 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<ContentEntryModule>((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, fileId,
pluginContext, pluginContext,
}: { }: {
fileId: string; fileId: string;
pluginContext: PluginContext; pluginContext: PluginContext;
}): Promise<ContentEntryModule> { }): Promise<ContentEntryModule> {
contentEntryModuleByIdCache.set(fileId, 'loading');
const observable = globalContentConfigObserver.get(); const observable = globalContentConfigObserver.get();
// Content config should be loaded before this plugin is used // Content config should be loaded before this plugin is used
@ -211,6 +231,13 @@ export const _internal = {
body: info.body, body: info.body,
}; };
contentEntryModuleByIdCache.set(fileId, contentEntryModule); contentEntryModuleByIdCache.set(fileId, contentEntryModule);
const awaiting = awaitingCacheById.get(fileId);
if (awaiting) {
for (const resolve of awaiting) {
resolve(contentEntryModule);
}
awaitingCacheById.delete(fileId);
}
return contentEntryModule; return contentEntryModule;
} }