From 3f02d5f12b167514fff6eb9693b4e25c668e7a31 Mon Sep 17 00:00:00 2001
From: Matt Kane <m@mk.gg>
Date: Thu, 21 Nov 2024 10:45:02 +0000
Subject: [PATCH] feat: support new location for content config (#12475)

* feat: support new location for content config

* Test fixes

* Handle missing dir

* Handle missing content dir

* chore: changes from review

* Revert legacy fixtures

* Clarify changeset
---
 .changeset/thirty-clocks-jump.md              |  7 ++++++
 .../{content/config.ts => content.config.ts}  |  0
 .../{content/config.ts => content.config.ts}  |  0
 .../{content/config.ts => content.config.ts}  |  0
 .../{content/config.ts => content.config.ts}  |  0
 packages/astro/src/content/runtime.ts         | 14 ++++++++++--
 .../astro/src/content/server-listeners.ts     | 12 ++++++++--
 packages/astro/src/content/types-generator.ts |  7 +++---
 packages/astro/src/content/utils.ts           | 22 ++++++++++++-------
 packages/astro/src/core/errors/errors-data.ts | 11 +++++-----
 packages/astro/src/core/sync/index.ts         | 17 +++++++++++---
 packages/astro/src/types/public/config.ts     |  2 +-
 packages/astro/test/astro-sync.test.js        |  2 +-
 packages/astro/test/content-layer.test.js     |  2 +-
 .../{content/config.ts => content.config.ts}  |  0
 .../{content/config.ts => content.config.ts}  |  0
 .../{content/config.ts => content.config.ts}  |  0
 .../{content/config.ts => content.config.ts}  |  0
 .../{content/config.ts => content.config.ts}  |  0
 .../{content/config.ts => content.config.ts}  |  0
 .../{content/config.ts => content.config.ts}  |  0
 .../{content/config.ts => content.config.ts}  |  0
 .../{content/config.ts => content.config.ts}  |  0
 .../{content/config.ts => content.config.ts}  |  0
 .../{content/config.ts => content.config.ts}  |  0
 .../{content/config.ts => content.config.ts}  |  0
 .../{content/config.ts => content.config.ts}  |  0
 .../{content/config.ts => content.config.ts}  |  2 +-
 .../{content/config.ts => content.config.ts}  |  4 ++--
 .../{content/config.ts => content.config.ts}  |  0
 .../{content/config.ts => content.config.ts}  |  0
 .../{content/config.ts => content.config.ts}  |  0
 .../{content/config.ts => content.config.ts}  |  0
 .../{content/config.ts => content.config.ts}  |  0
 .../{content/config.ts => content.config.ts}  |  0
 .../content-collections/frontmatter.test.js   |  2 +-
 .../get-entry-type.test.js                    |  2 +-
 .../collections-mixed-content-errors.test.js  |  6 ++---
 .../units/dev/collections-renderentry.test.js |  4 ++--
 39 files changed, 79 insertions(+), 37 deletions(-)
 create mode 100644 .changeset/thirty-clocks-jump.md
 rename examples/blog/src/{content/config.ts => content.config.ts} (100%)
 rename examples/portfolio/src/{content/config.ts => content.config.ts} (100%)
 rename examples/starlog/src/{content/config.ts => content.config.ts} (100%)
 rename examples/with-markdoc/src/{content/config.ts => content.config.ts} (100%)
 rename packages/astro/test/fixtures/astro-assets-dir/src/{content/config.ts => content.config.ts} (100%)
 rename packages/astro/test/fixtures/astro-assets-prefix/src/{content/config.ts => content.config.ts} (100%)
 rename packages/astro/test/fixtures/astro-env-content-collections/src/{content/config.ts => content.config.ts} (100%)
 rename packages/astro/test/fixtures/content with spaces in folder name/src/{content/config.ts => content.config.ts} (100%)
 rename packages/astro/test/fixtures/content-collection-references/src/{content/config.ts => content.config.ts} (100%)
 rename packages/astro/test/fixtures/content-collections-base/src/{content/config.ts => content.config.ts} (100%)
 rename packages/astro/test/fixtures/content-collections-cache-invalidation/src/{content/config.ts => content.config.ts} (100%)
 rename packages/astro/test/fixtures/content-collections-empty-dir/src/{content/config.ts => content.config.ts} (100%)
 rename packages/astro/test/fixtures/content-collections-mutation/src/{content/config.ts => content.config.ts} (100%)
 rename packages/astro/test/fixtures/content-collections-same-contents/src/{content/config.ts => content.config.ts} (100%)
 rename packages/astro/test/fixtures/content-collections/src/{content/config.ts => content.config.ts} (100%)
 rename packages/astro/test/fixtures/content-intellisense/src/{content/config.ts => content.config.ts} (100%)
 rename packages/astro/test/fixtures/content-layer-markdoc/src/{content/config.ts => content.config.ts} (100%)
 rename packages/astro/test/fixtures/content-layer-rendering/src/{content/config.ts => content.config.ts} (84%)
 rename packages/astro/test/fixtures/content-layer/src/{content/config.ts => content.config.ts} (98%)
 rename packages/astro/test/fixtures/content-static-paths-integration/src/{content/config.ts => content.config.ts} (100%)
 rename packages/astro/test/fixtures/core-image-base/src/{content/config.ts => content.config.ts} (100%)
 rename packages/astro/test/fixtures/core-image-deletion/src/{content/config.ts => content.config.ts} (100%)
 rename packages/astro/test/fixtures/core-image-ssg/src/{content/config.ts => content.config.ts} (100%)
 rename packages/astro/test/fixtures/data-collections-schema/src/{content/config.ts => content.config.ts} (100%)
 rename packages/astro/test/fixtures/data-collections/src/{content/config.ts => content.config.ts} (100%)

diff --git a/.changeset/thirty-clocks-jump.md b/.changeset/thirty-clocks-jump.md
new file mode 100644
index 000000000000..39864b2ecbb8
--- /dev/null
+++ b/.changeset/thirty-clocks-jump.md
@@ -0,0 +1,7 @@
+---
+'astro': minor
+---
+
+Changes the default content config location from `src/content/config.*` to `src/content.config.*`.
+
+The previous location is still supported, and is required if the `legacy.collections` flag is enabled.
diff --git a/examples/blog/src/content/config.ts b/examples/blog/src/content.config.ts
similarity index 100%
rename from examples/blog/src/content/config.ts
rename to examples/blog/src/content.config.ts
diff --git a/examples/portfolio/src/content/config.ts b/examples/portfolio/src/content.config.ts
similarity index 100%
rename from examples/portfolio/src/content/config.ts
rename to examples/portfolio/src/content.config.ts
diff --git a/examples/starlog/src/content/config.ts b/examples/starlog/src/content.config.ts
similarity index 100%
rename from examples/starlog/src/content/config.ts
rename to examples/starlog/src/content.config.ts
diff --git a/examples/with-markdoc/src/content/config.ts b/examples/with-markdoc/src/content.config.ts
similarity index 100%
rename from examples/with-markdoc/src/content/config.ts
rename to examples/with-markdoc/src/content.config.ts
diff --git a/packages/astro/src/content/runtime.ts b/packages/astro/src/content/runtime.ts
index 6a8d3f214a3b..81590da8b325 100644
--- a/packages/astro/src/content/runtime.ts
+++ b/packages/astro/src/content/runtime.ts
@@ -26,12 +26,22 @@ type GlobResult = Record<string, LazyImport>;
 type CollectionToEntryMap = Record<string, GlobResult>;
 type GetEntryImport = (collection: string, lookupId: string) => Promise<LazyImport>;
 
+export function getImporterFilename() {
+	// The 4th line in the stack trace should be the importer filename
+	const stackLine = new Error().stack?.split('\n')?.[3];
+	if (!stackLine) {
+		return null;
+	}
+	// Extract the relative path from the stack line
+	const match = /\/(src\/.*?):\d+:\d+/.exec(stackLine);
+	return match?.[1] ?? null;
+}
+
 export function defineCollection(config: any) {
 	if ('loader' in config) {
 		if (config.type && config.type !== CONTENT_LAYER_TYPE) {
 			throw new AstroUserError(
-				'Collections that use the Content Layer API must have a `loader` defined and no `type` set.',
-				"Check your collection definitions in `src/content/config.*`.'",
+				`Collections that use the Content Layer API must have a \`loader\` defined and no \`type\` set. Check your collection definitions in ${getImporterFilename() ?? 'your content config file'}.`,
 			);
 		}
 		config.type = CONTENT_LAYER_TYPE;
diff --git a/packages/astro/src/content/server-listeners.ts b/packages/astro/src/content/server-listeners.ts
index 28f5b16a83e0..33ab2c8959ee 100644
--- a/packages/astro/src/content/server-listeners.ts
+++ b/packages/astro/src/content/server-listeners.ts
@@ -24,8 +24,16 @@ export async function attachContentServerListeners({
 	settings,
 }: ContentServerListenerParams) {
 	const contentPaths = getContentPaths(settings.config, fs);
-
-	if (fs.existsSync(contentPaths.contentDir)) {
+	if (!settings.config.legacy?.collections) {
+		const contentGenerator = await createContentTypesGenerator({
+			fs,
+			settings,
+			logger,
+			viteServer,
+			contentConfigObserver: globalContentConfigObserver,
+		});
+		await contentGenerator.init();
+	} else if (fs.existsSync(contentPaths.contentDir)) {
 		logger.debug(
 			'content',
 			`Watching ${cyan(
diff --git a/packages/astro/src/content/types-generator.ts b/packages/astro/src/content/types-generator.ts
index c5123acef22d..fe76400bba30 100644
--- a/packages/astro/src/content/types-generator.ts
+++ b/packages/astro/src/content/types-generator.ts
@@ -86,13 +86,12 @@ export async function createContentTypesGenerator({
 	async function init(): Promise<
 		{ typesGenerated: true } | { typesGenerated: false; reason: 'no-content-dir' }
 	> {
-		if (!fs.existsSync(contentPaths.contentDir)) {
-			return { typesGenerated: false, reason: 'no-content-dir' };
-		}
-
 		events.push({ name: 'add', entry: contentPaths.config.url });
 
 		if (settings.config.legacy.collections) {
+			if (!fs.existsSync(contentPaths.contentDir)) {
+				return { typesGenerated: false, reason: 'no-content-dir' };
+			}
 			const globResult = await glob('**', {
 				cwd: fileURLToPath(contentPaths.contentDir),
 				fs: {
diff --git a/packages/astro/src/content/utils.ts b/packages/astro/src/content/utils.ts
index f67a7ad63d5b..1cdafa190741 100644
--- a/packages/astro/src/content/utils.ts
+++ b/packages/astro/src/content/utils.ts
@@ -597,7 +597,7 @@ export async function autogenerateCollections({
 			}) as any,
 		};
 	}
-	if (!usesContentLayer) {
+	if (!usesContentLayer && fs.existsSync(contentDir)) {
 		// If the user hasn't defined any collections using the content layer, we'll try and help out by checking for
 		// any orphaned folders in the content directory and creating collections for them.
 		const orphanedCollections = [];
@@ -623,7 +623,7 @@ export async function autogenerateCollections({
 			console.warn(
 				`
 Auto-generating collections for folders in "src/content/" that are not defined as collections.
-This is deprecated, so you should define these collections yourself in "src/content/config.ts".
+This is deprecated, so you should define these collections yourself in "src/content.config.ts".
 The following collections have been auto-generated: ${orphanedCollections
 					.map((name) => green(name))
 					.join(', ')}\n`,
@@ -715,10 +715,10 @@ export type ContentPaths = {
 };
 
 export function getContentPaths(
-	{ srcDir }: Pick<AstroConfig, 'root' | 'srcDir'>,
+	{ srcDir, legacy }: Pick<AstroConfig, 'root' | 'srcDir' | 'legacy'>,
 	fs: typeof fsMod = fsMod,
 ): ContentPaths {
-	const configStats = search(fs, srcDir);
+	const configStats = search(fs, srcDir, legacy?.collections);
 	const pkgBase = new URL('../../', import.meta.url);
 	return {
 		contentDir: new URL('./content/', srcDir),
@@ -728,10 +728,16 @@ export function getContentPaths(
 		config: configStats,
 	};
 }
-function search(fs: typeof fsMod, srcDir: URL) {
-	const paths = ['config.mjs', 'config.js', 'config.mts', 'config.ts'].map(
-		(p) => new URL(`./content/${p}`, srcDir),
-	);
+function search(fs: typeof fsMod, srcDir: URL, legacy?: boolean) {
+	const paths = [
+		...(legacy
+			? []
+			: ['content.config.mjs', 'content.config.js', 'content.config.mts', 'content.config.ts']),
+		'content/config.mjs',
+		'content/config.js',
+		'content/config.mts',
+		'content/config.ts',
+	].map((p) => new URL(`./${p}`, srcDir));
 	for (const file of paths) {
 		if (fs.existsSync(file)) {
 			return { exists: true, url: file };
diff --git a/packages/astro/src/core/errors/errors-data.ts b/packages/astro/src/core/errors/errors-data.ts
index 7c5479a665f5..1b19b6064a1f 100644
--- a/packages/astro/src/core/errors/errors-data.ts
+++ b/packages/astro/src/core/errors/errors-data.ts
@@ -1446,7 +1446,8 @@ export const GenerateContentTypesError = {
 	title: 'Failed to generate content types.',
 	message: (errorMessage: string) =>
 		`\`astro sync\` command failed to generate content collection types: ${errorMessage}`,
-	hint: 'This error is often caused by a syntax error inside your content, or your content configuration file. Check your `src/content/config.*` file for typos.',
+	hint: (fileName?: string) =>
+		`This error is often caused by a syntax error inside your content, or your content configuration file. Check your ${fileName ?? 'content config'} file for typos.`,
 } satisfies ErrorData;
 /**
  * @docs
@@ -1458,7 +1459,7 @@ export const GenerateContentTypesError = {
  * @docs
  * @description
  * Astro encountered an unknown error loading your content collections.
- * This can be caused by certain errors inside your `src/content/config.ts` file or some internal errors.
+ * This can be caused by certain errors inside your `src/content.config.ts` file or some internal errors.
  *
  * If you can reliably cause this error to happen, we'd appreciate if you could [open an issue](https://astro.build/issues/)
  */
@@ -1501,7 +1502,7 @@ export const GetEntryDeprecationError = {
  * @description
  * A Markdown or MDX entry does not match its collection schema.
  * Make sure that all required fields are present, and that all fields are of the correct type.
- * You can check against the collection schema in your `src/content/config.*` file.
+ * You can check against the collection schema in your `src/content.config.*` file.
  * See the [Content collections documentation](https://docs.astro.build/en/guides/content-collections/) for more information.
  */
 export const InvalidContentEntryFrontmatterError = {
@@ -1528,7 +1529,7 @@ export const InvalidContentEntryFrontmatterError = {
  * @description
  * A content entry does not match its collection schema.
  * Make sure that all required fields are present, and that all fields are of the correct type.
- * You can check against the collection schema in your `src/content/config.*` file.
+ * You can check against the collection schema in your `src/content.config.*` file.
  * See the [Content collections documentation](https://docs.astro.build/en/guides/content-collections/) for more information.
  */
 export const InvalidContentEntryDataError = {
@@ -1553,7 +1554,7 @@ export const InvalidContentEntryDataError = {
  * @description
  * A content entry does not match its collection schema.
  * Make sure that all required fields are present, and that all fields are of the correct type.
- * You can check against the collection schema in your `src/content/config.*` file.
+ * You can check against the collection schema in your `src/content.config.*` file.
  * See the [Content collections documentation](https://docs.astro.build/en/guides/content-collections/) for more information.
  */
 export const ContentEntryDataError = {
diff --git a/packages/astro/src/core/sync/index.ts b/packages/astro/src/core/sync/index.ts
index 5fba6196a819..832a55b2827f 100644
--- a/packages/astro/src/core/sync/index.ts
+++ b/packages/astro/src/core/sync/index.ts
@@ -21,7 +21,6 @@ import { resolveConfig } from '../config/config.js';
 import { createNodeLogger } from '../config/logging.js';
 import { createSettings } from '../config/settings.js';
 import { createVite } from '../create-vite.js';
-import { collectErrorMetadata } from '../errors/dev/utils.js';
 import {
 	AstroError,
 	AstroErrorData,
@@ -31,7 +30,6 @@ import {
 	isAstroError,
 } from '../errors/index.js';
 import type { Logger } from '../logger/core.js';
-import { formatErrorMessage } from '../messages.js';
 import { createRouteManifest } from '../routing/index.js';
 import { ensureProcessNodeEnv } from '../util.js';
 
@@ -255,7 +253,20 @@ async function syncContentCollections(
 		if (isAstroError(e)) {
 			throw e;
 		}
-		const hint = AstroUserError.is(e) ? e.hint : AstroErrorData.GenerateContentTypesError.hint;
+		let configFile
+		try {
+			const contentPaths = getContentPaths(settings.config, fs);
+			if(contentPaths.config.exists) {
+				const matches = /\/(src\/.+)/.exec(contentPaths.config.url.href);
+				if (matches) {
+					configFile = matches[1]
+				}
+			}
+		} catch {
+			// ignore
+		}
+
+		const hint = AstroUserError.is(e) ? e.hint : AstroErrorData.GenerateContentTypesError.hint(configFile);
 		throw new AstroError(
 			{
 				...AstroErrorData.GenerateContentTypesError,
diff --git a/packages/astro/src/types/public/config.ts b/packages/astro/src/types/public/config.ts
index 354d3049375e..a01dc42cd7bb 100644
--- a/packages/astro/src/types/public/config.ts
+++ b/packages/astro/src/types/public/config.ts
@@ -1707,7 +1707,7 @@ export interface ViteUserConfig extends OriginalViteUserConfig {
 		 * When you are ready to remove this flag and migrate to the new Content Layer API for your legacy collections, you must define a collection for any directories in `src/content/` that you want to continue to use as a collection. It is sufficient to declare an empty collection, and Astro will implicitly generate an appropriate definition for your legacy collections:
 		 *  
 		 * ```js
-		 * // src/content/config.ts
+		 * // src/content.config.ts
 		 * import { defineCollection, z } from 'astro:content';
 		 * 
 		 * const blog = defineCollection({ })
diff --git a/packages/astro/test/astro-sync.test.js b/packages/astro/test/astro-sync.test.js
index 94e6b326b731..e9ab90cf7569 100644
--- a/packages/astro/test/astro-sync.test.js
+++ b/packages/astro/test/astro-sync.test.js
@@ -209,7 +209,7 @@ describe('astro sync', () => {
 				assert.fail();
 			}
 		});
-		it('Does not throw if a virtual module is imported in content/config.ts', async () => {
+		it('Does not throw if a virtual module is imported in content.config.ts', async () => {
 			try {
 				await fixture.load('./fixtures/astro-env-content-collections/');
 				fixture.clean();
diff --git a/packages/astro/test/content-layer.test.js b/packages/astro/test/content-layer.test.js
index 7353c2384306..e2d92df3d989 100644
--- a/packages/astro/test/content-layer.test.js
+++ b/packages/astro/test/content-layer.test.js
@@ -285,7 +285,7 @@ describe('Content Layer', () => {
 		it('clears the store on new build if the config has changed', async () => {
 			let newJson = devalue.parse(await fixture.readFile('/collections.json'));
 			assert.equal(newJson.increment.data.lastValue, 1);
-			await fixture.editFile('src/content/config.ts', (prev) => {
+			await fixture.editFile('src/content.config.ts', (prev) => {
 				return `${prev}\nexport const foo = 'bar';`;
 			});
 			await fixture.build();
diff --git a/packages/astro/test/fixtures/astro-assets-dir/src/content/config.ts b/packages/astro/test/fixtures/astro-assets-dir/src/content.config.ts
similarity index 100%
rename from packages/astro/test/fixtures/astro-assets-dir/src/content/config.ts
rename to packages/astro/test/fixtures/astro-assets-dir/src/content.config.ts
diff --git a/packages/astro/test/fixtures/astro-assets-prefix/src/content/config.ts b/packages/astro/test/fixtures/astro-assets-prefix/src/content.config.ts
similarity index 100%
rename from packages/astro/test/fixtures/astro-assets-prefix/src/content/config.ts
rename to packages/astro/test/fixtures/astro-assets-prefix/src/content.config.ts
diff --git a/packages/astro/test/fixtures/astro-env-content-collections/src/content/config.ts b/packages/astro/test/fixtures/astro-env-content-collections/src/content.config.ts
similarity index 100%
rename from packages/astro/test/fixtures/astro-env-content-collections/src/content/config.ts
rename to packages/astro/test/fixtures/astro-env-content-collections/src/content.config.ts
diff --git a/packages/astro/test/fixtures/content with spaces in folder name/src/content/config.ts b/packages/astro/test/fixtures/content with spaces in folder name/src/content.config.ts
similarity index 100%
rename from packages/astro/test/fixtures/content with spaces in folder name/src/content/config.ts
rename to packages/astro/test/fixtures/content with spaces in folder name/src/content.config.ts
diff --git a/packages/astro/test/fixtures/content-collection-references/src/content/config.ts b/packages/astro/test/fixtures/content-collection-references/src/content.config.ts
similarity index 100%
rename from packages/astro/test/fixtures/content-collection-references/src/content/config.ts
rename to packages/astro/test/fixtures/content-collection-references/src/content.config.ts
diff --git a/packages/astro/test/fixtures/content-collections-base/src/content/config.ts b/packages/astro/test/fixtures/content-collections-base/src/content.config.ts
similarity index 100%
rename from packages/astro/test/fixtures/content-collections-base/src/content/config.ts
rename to packages/astro/test/fixtures/content-collections-base/src/content.config.ts
diff --git a/packages/astro/test/fixtures/content-collections-cache-invalidation/src/content/config.ts b/packages/astro/test/fixtures/content-collections-cache-invalidation/src/content.config.ts
similarity index 100%
rename from packages/astro/test/fixtures/content-collections-cache-invalidation/src/content/config.ts
rename to packages/astro/test/fixtures/content-collections-cache-invalidation/src/content.config.ts
diff --git a/packages/astro/test/fixtures/content-collections-empty-dir/src/content/config.ts b/packages/astro/test/fixtures/content-collections-empty-dir/src/content.config.ts
similarity index 100%
rename from packages/astro/test/fixtures/content-collections-empty-dir/src/content/config.ts
rename to packages/astro/test/fixtures/content-collections-empty-dir/src/content.config.ts
diff --git a/packages/astro/test/fixtures/content-collections-mutation/src/content/config.ts b/packages/astro/test/fixtures/content-collections-mutation/src/content.config.ts
similarity index 100%
rename from packages/astro/test/fixtures/content-collections-mutation/src/content/config.ts
rename to packages/astro/test/fixtures/content-collections-mutation/src/content.config.ts
diff --git a/packages/astro/test/fixtures/content-collections-same-contents/src/content/config.ts b/packages/astro/test/fixtures/content-collections-same-contents/src/content.config.ts
similarity index 100%
rename from packages/astro/test/fixtures/content-collections-same-contents/src/content/config.ts
rename to packages/astro/test/fixtures/content-collections-same-contents/src/content.config.ts
diff --git a/packages/astro/test/fixtures/content-collections/src/content/config.ts b/packages/astro/test/fixtures/content-collections/src/content.config.ts
similarity index 100%
rename from packages/astro/test/fixtures/content-collections/src/content/config.ts
rename to packages/astro/test/fixtures/content-collections/src/content.config.ts
diff --git a/packages/astro/test/fixtures/content-intellisense/src/content/config.ts b/packages/astro/test/fixtures/content-intellisense/src/content.config.ts
similarity index 100%
rename from packages/astro/test/fixtures/content-intellisense/src/content/config.ts
rename to packages/astro/test/fixtures/content-intellisense/src/content.config.ts
diff --git a/packages/astro/test/fixtures/content-layer-markdoc/src/content/config.ts b/packages/astro/test/fixtures/content-layer-markdoc/src/content.config.ts
similarity index 100%
rename from packages/astro/test/fixtures/content-layer-markdoc/src/content/config.ts
rename to packages/astro/test/fixtures/content-layer-markdoc/src/content.config.ts
diff --git a/packages/astro/test/fixtures/content-layer-rendering/src/content/config.ts b/packages/astro/test/fixtures/content-layer-rendering/src/content.config.ts
similarity index 84%
rename from packages/astro/test/fixtures/content-layer-rendering/src/content/config.ts
rename to packages/astro/test/fixtures/content-layer-rendering/src/content.config.ts
index eb175fa99bd8..2a8c005d5f21 100644
--- a/packages/astro/test/fixtures/content-layer-rendering/src/content/config.ts
+++ b/packages/astro/test/fixtures/content-layer-rendering/src/content.config.ts
@@ -4,7 +4,7 @@ import { glob } from 'astro/loaders';
 const reptiles = defineCollection({
 	loader: glob({
 		pattern: '*.mdx',
-		base: new URL('../../content-outside-src-mdx', import.meta.url),
+		base: new URL('../content-outside-src-mdx', import.meta.url),
 	}),
 	schema: () =>
 		z.object({
diff --git a/packages/astro/test/fixtures/content-layer/src/content/config.ts b/packages/astro/test/fixtures/content-layer/src/content.config.ts
similarity index 98%
rename from packages/astro/test/fixtures/content-layer/src/content/config.ts
rename to packages/astro/test/fixtures/content-layer/src/content.config.ts
index 65c0c5df05f4..82228f61daa0 100644
--- a/packages/astro/test/fixtures/content-layer/src/content/config.ts
+++ b/packages/astro/test/fixtures/content-layer/src/content.config.ts
@@ -1,6 +1,6 @@
 import { defineCollection, z, reference } from 'astro:content';
 import { file, glob } from 'astro/loaders';
-import { loader } from '../loaders/post-loader.js';
+import { loader } from './loaders/post-loader.js';
 import { parse as parseToml } from 'toml';
 
 const blog = defineCollection({
@@ -141,7 +141,7 @@ const birds = defineCollection({
 });
 
 // Absolute paths should also work
-const absoluteRoot = new URL('space', import.meta.url);
+const absoluteRoot = new URL('content/space', import.meta.url);
 
 const spacecraft = defineCollection({
 	loader: glob({ pattern: '*.md', base: absoluteRoot }),
diff --git a/packages/astro/test/fixtures/content-static-paths-integration/src/content/config.ts b/packages/astro/test/fixtures/content-static-paths-integration/src/content.config.ts
similarity index 100%
rename from packages/astro/test/fixtures/content-static-paths-integration/src/content/config.ts
rename to packages/astro/test/fixtures/content-static-paths-integration/src/content.config.ts
diff --git a/packages/astro/test/fixtures/core-image-base/src/content/config.ts b/packages/astro/test/fixtures/core-image-base/src/content.config.ts
similarity index 100%
rename from packages/astro/test/fixtures/core-image-base/src/content/config.ts
rename to packages/astro/test/fixtures/core-image-base/src/content.config.ts
diff --git a/packages/astro/test/fixtures/core-image-deletion/src/content/config.ts b/packages/astro/test/fixtures/core-image-deletion/src/content.config.ts
similarity index 100%
rename from packages/astro/test/fixtures/core-image-deletion/src/content/config.ts
rename to packages/astro/test/fixtures/core-image-deletion/src/content.config.ts
diff --git a/packages/astro/test/fixtures/core-image-ssg/src/content/config.ts b/packages/astro/test/fixtures/core-image-ssg/src/content.config.ts
similarity index 100%
rename from packages/astro/test/fixtures/core-image-ssg/src/content/config.ts
rename to packages/astro/test/fixtures/core-image-ssg/src/content.config.ts
diff --git a/packages/astro/test/fixtures/data-collections-schema/src/content/config.ts b/packages/astro/test/fixtures/data-collections-schema/src/content.config.ts
similarity index 100%
rename from packages/astro/test/fixtures/data-collections-schema/src/content/config.ts
rename to packages/astro/test/fixtures/data-collections-schema/src/content.config.ts
diff --git a/packages/astro/test/fixtures/data-collections/src/content/config.ts b/packages/astro/test/fixtures/data-collections/src/content.config.ts
similarity index 100%
rename from packages/astro/test/fixtures/data-collections/src/content/config.ts
rename to packages/astro/test/fixtures/data-collections/src/content.config.ts
diff --git a/packages/astro/test/units/content-collections/frontmatter.test.js b/packages/astro/test/units/content-collections/frontmatter.test.js
index 4f587a90fe59..feffabf9567a 100644
--- a/packages/astro/test/units/content-collections/frontmatter.test.js
+++ b/packages/astro/test/units/content-collections/frontmatter.test.js
@@ -10,7 +10,7 @@ describe('frontmatter', () => {
 					title: One
 					---
 				`,
-			'/src/content/config.ts': `\
+			'/src/content.config.ts': `\
 					import { defineCollection, z } from 'astro:content';
 
 					const posts = defineCollection({
diff --git a/packages/astro/test/units/content-collections/get-entry-type.test.js b/packages/astro/test/units/content-collections/get-entry-type.test.js
index 2876e5bfc31b..9d60c4c5c03d 100644
--- a/packages/astro/test/units/content-collections/get-entry-type.test.js
+++ b/packages/astro/test/units/content-collections/get-entry-type.test.js
@@ -8,7 +8,7 @@ const fixtures = [
 		title: 'Without any underscore above the content directory tree',
 		contentPaths: {
 			config: {
-				url: new URL('src/content/config.ts', import.meta.url),
+				url: new URL('src/content.config.ts', import.meta.url),
 				exists: true,
 			},
 			contentDir: new URL('src/content/', import.meta.url),
diff --git a/packages/astro/test/units/dev/collections-mixed-content-errors.test.js b/packages/astro/test/units/dev/collections-mixed-content-errors.test.js
index da72f4a7681d..295662c93110 100644
--- a/packages/astro/test/units/dev/collections-mixed-content-errors.test.js
+++ b/packages/astro/test/units/dev/collections-mixed-content-errors.test.js
@@ -57,7 +57,7 @@ name: Ben
 # Ben
 `,
 			'/src/content/authors/tony.json': `{ "name": "Tony" }`,
-			'/src/content/config.ts': `\
+			'/src/content.config.ts': `\
 import { z, defineCollection } from 'astro:content';
 
 const authors = defineCollection({
@@ -85,7 +85,7 @@ title: Post
 # Post
 `,
 			'/src/content/blog/post.yaml': `title: YAML Post`,
-			'/src/content/config.ts': `\
+			'/src/content.config.ts': `\
 import { z, defineCollection } from 'astro:content';
 
 const blog = defineCollection({
@@ -128,7 +128,7 @@ export const collections = { banners };
 			...baseFileTree,
 			// Add placeholder to ensure directory exists
 			'/src/content/i18n/_placeholder.txt': 'Need content here',
-			'/src/content/config.ts': `\
+			'/src/content.config.ts': `\
 import { z, defineCollection } from 'astro:content';
 
 const i18n = defineCollection({
diff --git a/packages/astro/test/units/dev/collections-renderentry.test.js b/packages/astro/test/units/dev/collections-renderentry.test.js
index 42e11c2a224f..c30c471b880b 100644
--- a/packages/astro/test/units/dev/collections-renderentry.test.js
+++ b/packages/astro/test/units/dev/collections-renderentry.test.js
@@ -166,7 +166,7 @@ describe('Content Collections - render()', () => {
 	it('can be used in a slot', async () => {
 		const fixture = await createFixture({
 			...baseFileTree,
-			'/src/content/config.ts': `
+			'/src/content.config.ts': `
 					import { z, defineCollection } from 'astro:content';
 
 					const blog = defineCollection({
@@ -233,7 +233,7 @@ describe('Content Collections - render()', () => {
 	it('can be called from any js/ts file', async () => {
 		const fixture = await createFixture({
 			...baseFileTree,
-			'/src/content/config.ts': `
+			'/src/content.config.ts': `
 					import { z, defineCollection } from 'astro:content';
 
 					const blog = defineCollection({