From 7b0cb852f6336c0f9cc65bd044864004e759d810 Mon Sep 17 00:00:00 2001 From: Matt Kane Date: Fri, 20 Dec 2024 15:08:43 +0000 Subject: [PATCH] fix: better logs for invalid content config (#12798) Co-authored-by: Chris Swithinbank --- .changeset/afraid-sloths-shake.md | 5 ++++ packages/astro/src/content/content-layer.ts | 9 ++++++- packages/astro/src/content/loaders/glob.ts | 20 +++++++++++++- packages/astro/src/content/runtime.ts | 2 +- packages/astro/test/content-layer.test.js | 27 ++++++++++++++++++- .../content-layer/src/content.config.ts | 10 +++++++ 6 files changed, 69 insertions(+), 4 deletions(-) create mode 100644 .changeset/afraid-sloths-shake.md diff --git a/.changeset/afraid-sloths-shake.md b/.changeset/afraid-sloths-shake.md new file mode 100644 index 000000000000..98c8efb37206 --- /dev/null +++ b/.changeset/afraid-sloths-shake.md @@ -0,0 +1,5 @@ +--- +'astro': patch +--- + +Improves warning logs for invalid content collection configuration diff --git a/packages/astro/src/content/content-layer.ts b/packages/astro/src/content/content-layer.ts index c0395295367b..4e7d364841e7 100644 --- a/packages/astro/src/content/content-layer.ts +++ b/packages/astro/src/content/content-layer.ts @@ -138,8 +138,15 @@ export class ContentLayer { async #doSync(options: RefreshContentOptions) { const contentConfig = globalContentConfigObserver.get(); const logger = this.#logger.forkIntegrationLogger('content'); + + if (contentConfig?.status === 'error') { + logger.error(`Error loading content config. Skipping sync.\n${contentConfig.error.message}`); + return; + } + + // It shows as loaded with no collections even if there's no config if (contentConfig?.status !== 'loaded') { - logger.debug('Content config not loaded, skipping sync'); + logger.error('Content config not loaded, skipping sync'); return; } diff --git a/packages/astro/src/content/loaders/glob.ts b/packages/astro/src/content/loaders/glob.ts index f96ab22b7015..6069ad8386c5 100644 --- a/packages/astro/src/content/loaders/glob.ts +++ b/packages/astro/src/content/loaders/glob.ts @@ -1,4 +1,5 @@ -import { promises as fs } from 'node:fs'; +import { promises as fs, existsSync } from 'node:fs'; +import { relative } from 'node:path'; import { fileURLToPath, pathToFileURL } from 'node:url'; import fastGlob from 'fast-glob'; import { bold, green } from 'kleur/colors'; @@ -215,10 +216,27 @@ export function glob(globOptions: GlobOptions): Loader { baseDir.pathname = `${baseDir.pathname}/`; } + const filePath = fileURLToPath(baseDir); + const relativePath = relative(fileURLToPath(config.root), filePath); + + const exists = existsSync(baseDir); + + if (!exists) { + // We warn and don't return because we will still set up the watcher in case the directory is created later + logger.warn(`The base directory "${fileURLToPath(baseDir)}" does not exist.`); + } + const files = await fastGlob(globOptions.pattern, { cwd: fileURLToPath(baseDir), }); + if (exists && files.length === 0) { + logger.warn( + `No files found matching "${globOptions.pattern}" in directory "${relativePath}"`, + ); + return; + } + function configForFile(file: string) { const ext = file.split('.').at(-1); if (!ext) { diff --git a/packages/astro/src/content/runtime.ts b/packages/astro/src/content/runtime.ts index 81590da8b325..cf6537609ef1 100644 --- a/packages/astro/src/content/runtime.ts +++ b/packages/astro/src/content/runtime.ts @@ -111,7 +111,7 @@ export function createGetCollection({ console.warn( `The collection ${JSON.stringify( collection, - )} does not exist or is empty. Ensure a collection directory with this name exists.`, + )} does not exist or is empty. Please check your content config file for errors.`, ); return []; } diff --git a/packages/astro/test/content-layer.test.js b/packages/astro/test/content-layer.test.js index 0a7e2b289297..225025b3069e 100644 --- a/packages/astro/test/content-layer.test.js +++ b/packages/astro/test/content-layer.test.js @@ -2,9 +2,11 @@ import assert from 'node:assert/strict'; import { promises as fs, existsSync } from 'node:fs'; import { sep } from 'node:path'; import { sep as posixSep } from 'node:path/posix'; +import { Writable } from 'node:stream'; import { after, before, describe, it } from 'node:test'; import * as cheerio from 'cheerio'; import * as devalue from 'devalue'; +import { Logger } from '../dist/core/logger/core.js'; import { loadFixture } from './test-utils.js'; describe('Content Layer', () => { @@ -316,8 +318,21 @@ describe('Content Layer', () => { describe('Dev', () => { let devServer; let json; + const logs = []; before(async () => { - devServer = await fixture.startDevServer({ force: true }); + devServer = await fixture.startDevServer({ + force: true, + logger: new Logger({ + level: 'warn', + dest: new Writable({ + objectMode: true, + write(event, _, callback) { + logs.push(event); + callback(); + }, + }), + }), + }); // Vite may not have noticed the saved data store yet. Wait a little just in case. await fixture.onNextDataStoreChange(1000).catch(() => { // Ignore timeout, because it may have saved before we get here. @@ -331,6 +346,16 @@ describe('Content Layer', () => { devServer?.stop(); }); + it("warns about missing directory in glob() loader's path", async () => { + assert.ok(logs.find((log) => log.level === 'warn' && log.message.includes('does not exist'))); + }); + + it("warns about missing files in glob() loader's path", async () => { + assert.ok( + logs.find((log) => log.level === 'warn' && log.message.includes('No files found matching')), + ); + }); + it('Generates content types files', async () => { assert.ok(existsSync(new URL('./.astro/content.d.ts', fixture.config.root))); const data = await fs.readFile(new URL('./.astro/types.d.ts', fixture.config.root), 'utf-8'); diff --git a/packages/astro/test/fixtures/content-layer/src/content.config.ts b/packages/astro/test/fixtures/content-layer/src/content.config.ts index 82228f61daa0..b0e97225659f 100644 --- a/packages/astro/test/fixtures/content-layer/src/content.config.ts +++ b/packages/astro/test/fixtures/content-layer/src/content.config.ts @@ -177,6 +177,13 @@ const numbers = defineCollection({ loader: glob({ pattern: 'src/data/glob-data/*', base: '.' }), }); +const notADirectory = defineCollection({ + loader: glob({ pattern: '*', base: 'src/nonexistent' }), +}); + +const nothingMatches = defineCollection({ + loader: glob({ pattern: 'nothingmatches/*', base: 'src/data' }), +}); const images = defineCollection({ loader: () => [ { @@ -259,4 +266,7 @@ export const collections = { songs, probes, rodents, + notADirectory, + nothingMatches }; +