Skip to content

Commit e294953

Browse files
authored
fix(content): prune stale glob() entries when a collection becomes empty (#17937)
1 parent 8eaa4f2 commit e294953

3 files changed

Lines changed: 44 additions & 2 deletions

File tree

.changeset/two-experts-flash.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'astro': patch
3+
---
4+
5+
Fixes a bug where the `glob()` content loader kept stale entries in the data store after the last file in a collection was deleted. Empty collections are now pruned correctly, and the file watcher is registered in dev so the first file added to an empty collection is picked up without a restart.

packages/astro/src/content/loaders/glob.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -292,7 +292,6 @@ export function glob(globOptions: GlobOptions & { [secretLegacyFlag]?: boolean }
292292
logger.warn(
293293
`No files found matching "${globOptions.pattern}" in directory "${relativePath}"`,
294294
);
295-
return;
296295
}
297296

298297
function configForFile(file: string) {

packages/astro/test/units/content-layer/glob-loader.test.ts

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { strict as assert } from 'node:assert';
2-
import { mkdirSync, writeFileSync } from 'node:fs';
2+
import { mkdirSync, rmSync, writeFileSync } from 'node:fs';
33
import { join } from 'node:path';
44
import { describe, it } from 'node:test';
55
import { fileURLToPath } from 'node:url';
@@ -513,6 +513,44 @@ describe('Glob Loader', () => {
513513
assert.ok(warnings.some((w) => w.includes('No files found matching')));
514514
});
515515

516+
it('prunes stale entries when the last file in a collection is deleted', async () => {
517+
const tempDir = createTempDir();
518+
const contentDir = join(fileURLToPath(tempDir), 'src', 'content', 'posts');
519+
mkdirSync(contentDir, { recursive: true });
520+
writeFileSync(join(contentDir, 'post.md'), '---\ntitle: Post MD\n---\nContent MD');
521+
522+
const store = new MutableDataStore();
523+
const settings = createMinimalSettings(tempDir, {
524+
contentEntryTypes: [createMarkdownEntryType()],
525+
});
526+
const logger = new AstroLogger({
527+
destination: { write: () => true },
528+
level: 'silent',
529+
});
530+
531+
const collections = {
532+
posts: defineCollection({
533+
loader: glob({ pattern: '*.md', base: 'src/content/posts' }),
534+
}),
535+
};
536+
537+
const contentLayer = new ContentLayer({
538+
settings,
539+
logger,
540+
store,
541+
contentConfigObserver: createTestConfigObserver(collections),
542+
});
543+
544+
await contentLayer.sync();
545+
assert.equal(store.values('posts').length, 1);
546+
547+
// Delete the only file, leaving the (still existing) directory in place
548+
rmSync(join(contentDir, 'post.md'));
549+
550+
await contentLayer.sync();
551+
assert.equal(store.values('posts').length, 0);
552+
});
553+
516554
it('throws on duplicate IDs when prerenderConflictBehavior is error', async () => {
517555
const tempDir = createTempDir();
518556
const contentDir = join(fileURLToPath(tempDir), 'src', 'content', 'posts');

0 commit comments

Comments
 (0)