Skip to content

Commit caf5621

Browse files
authored
fix: correctly handle duplicate id checks in glob loader (#15064)
* fix: correctly handle duplicate id checks in glob loader * Check if file exists to avoid race condition * Fix test
1 parent 7aa7022 commit caf5621

File tree

5 files changed

+83
-4
lines changed

5 files changed

+83
-4
lines changed

.changeset/bright-worlds-juggle.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 that caused incorrect warnings of duplicate entries to be logged by the glob loader when editing a file

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

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -142,14 +142,20 @@ export function glob(globOptions: GlobOptions): Loader {
142142
data,
143143
filePath,
144144
});
145-
if (entryType.getRenderFunction) {
146-
let render = renderFunctionByContentType.get(entryType);
147145

148-
if (store.has(id)) {
146+
if (existingEntry && existingEntry.filePath && existingEntry.filePath !== relativePath) {
147+
// Check the old file still exists - if not, this is likely a rename and
148+
// the unlink event just hasn't been processed yet
149+
const oldFilePath = new URL(existingEntry.filePath, config.root);
150+
if (existsSync(oldFilePath)) {
149151
logger.warn(
150152
`Duplicate id "${id}" found in ${filePath}. Later items with the same id will overwrite earlier ones.`,
151153
);
152154
}
155+
}
156+
157+
if (entryType.getRenderFunction) {
158+
let render = renderFunctionByContentType.get(entryType);
153159

154160
if (!render) {
155161
render = await entryType.getRenderFunction(config);

packages/astro/test/content-layer.test.js

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ describe('Content Layer', () => {
9999
it('handles negative matches in glob() loader', async () => {
100100
assert.ok(json.hasOwnProperty('probes'));
101101
assert.ok(Array.isArray(json.probes));
102-
assert.equal(json.probes.length, 5);
102+
assert.equal(json.probes.length, 6);
103103
assert.ok(
104104
json.probes.every(({ id }) => !id.startsWith('voyager')),
105105
'Voyager probes should not be included',
@@ -434,6 +434,12 @@ describe('Content Layer', () => {
434434
);
435435
});
436436

437+
it('warns about duplicate IDs in glob() loader', () => {
438+
assert.ok(
439+
logs.find((log) => log.level === 'warn' && log.message.includes('Duplicate id "cassini"')),
440+
);
441+
});
442+
437443
it("warns about missing files in glob() loader's path", async () => {
438444
assert.ok(
439445
logs.find((log) => log.level === 'warn' && log.message.includes('No files found matching')),
@@ -581,6 +587,41 @@ describe('Content Layer', () => {
581587
await fixture.resetAllFiles();
582588
});
583589

590+
it('does not warn about duplicate IDs when a file is edited', async () => {
591+
logs.length = 0;
592+
593+
await fixture.editFile('/src/content/space/endeavour.md', (prev) => {
594+
return prev.replace('learn about the', 'Learn about the');
595+
});
596+
597+
await fixture.onNextDataStoreChange();
598+
599+
const duplicateWarning = logs.find((log) => log.message.includes('Duplicate id "endeavour"'));
600+
assert.ok(!duplicateWarning, 'Should not warn about duplicate ID when editing same file');
601+
602+
await fixture.resetAllFiles();
603+
});
604+
605+
it('does not warn about duplicate IDs when a file with a slug is renamed', async () => {
606+
logs.length = 0;
607+
608+
// dawn.md has slug: dawn-mission - renaming should not cause duplicate warning
609+
const oldPath = new URL('./data/space-probes/dawn.md', fixture.config.srcDir);
610+
const newPath = new URL('./data/space-probes/dawn-renamed.md', fixture.config.srcDir);
611+
612+
await fs.rename(oldPath, newPath);
613+
await fixture.onNextDataStoreChange();
614+
615+
try {
616+
const duplicateWarning = logs.find((log) =>
617+
log.message.includes('Duplicate id "dawn-mission"'),
618+
);
619+
assert.ok(!duplicateWarning, 'Should not warn about duplicate ID when renaming file');
620+
} finally {
621+
await fs.rename(newPath, oldPath);
622+
}
623+
});
624+
584625
it('returns an error if we render an undefined entry', async () => {
585626
const res = await fixture.fetch('/missing');
586627
const text = await res.text();
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
---
2+
slug: cassini
3+
name: Cassini Duplicate
4+
type: Space Probe
5+
launch_date: 1997-10-15
6+
status: Decommissioned
7+
destination: Saturn
8+
operator: NASA/ESA/ASI
9+
notable_discoveries:
10+
- This is a duplicate entry for testing
11+
---
12+
13+
This file has the same slug as cassini.md to test duplicate ID detection.
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
---
2+
slug: dawn-mission
3+
name: Dawn
4+
type: Space Probe
5+
launch_date: 2007-09-27
6+
status: Decommissioned
7+
destination: Asteroid Belt
8+
operator: NASA
9+
notable_discoveries:
10+
- Ceres' bright spots
11+
- Vesta's geological features
12+
---
13+
14+
Dawn was a NASA space probe launched to study the asteroid belt objects Vesta and Ceres. It was the first spacecraft to orbit two extraterrestrial bodies and provided detailed data about the early solar system.

0 commit comments

Comments
 (0)