diff --git a/.changeset/tiny-geckos-reply.md b/.changeset/tiny-geckos-reply.md new file mode 100644 index 000000000000..c9b7917d0c47 --- /dev/null +++ b/.changeset/tiny-geckos-reply.md @@ -0,0 +1,5 @@ +--- +'astro': patch +--- + +Ensure prefixed underscore ignores only child paths of the content directory. diff --git a/packages/astro/src/content/utils.ts b/packages/astro/src/content/utils.ts index 70771f2dcd72..afe15eeb0077 100644 --- a/packages/astro/src/content/utils.ts +++ b/packages/astro/src/content/utils.ts @@ -160,12 +160,12 @@ export function getEntryInfo({ export function getEntryType( entryPath: string, - paths: Pick + paths: Pick ): 'content' | 'config' | 'ignored' | 'unsupported' { const { ext, base } = path.parse(entryPath); const fileUrl = pathToFileURL(entryPath); - if (hasUnderscoreInPath(fileUrl) || isOnIgnoreList(base)) { + if (hasUnderscoreBelowContentDirectoryPath(fileUrl, paths.contentDir) || isOnIgnoreList(base)) { return 'ignored'; } else if ((contentFileExts as readonly string[]).includes(ext)) { return 'content'; @@ -180,8 +180,11 @@ function isOnIgnoreList(fileName: string) { return ['.DS_Store'].includes(fileName); } -function hasUnderscoreInPath(fileUrl: URL): boolean { - const parts = fileUrl.pathname.split('/'); +function hasUnderscoreBelowContentDirectoryPath( + fileUrl: URL, + contentDir: ContentPaths['contentDir'] +): boolean { + const parts = fileUrl.pathname.replace(contentDir.pathname, '').split('/'); for (const part of parts) { if (part.startsWith('_')) return true; } 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 b9293d22d5e2..3e549c2a2ff3 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 @@ -2,64 +2,83 @@ import { getEntryType } from '../../../dist/content/utils.js'; import { expect } from 'chai'; import { fileURLToPath } from 'node:url'; -describe('Content Collections - getEntryType', () => { - const contentDir = new URL('src/content/', import.meta.url); - const contentPaths = { - config: { - url: new URL('src/content/config.ts', import.meta.url), - exists: true, +const fixtures = [ + { + title: 'Without any underscore above the content directory tree', + contentPaths: { + config: { + url: new URL('src/content/config.ts', import.meta.url), + exists: true, + }, + contentDir: new URL('src/content/', import.meta.url), + }, + }, + { + title: 'With underscore levels above the content directory tree', + contentPaths: { + config: { + url: new URL('_src/content/config.ts', import.meta.url), + exists: true, + }, + contentDir: new URL('_src/content/', import.meta.url), }, - }; + }, +]; - it('Returns "content" for Markdown files', () => { - for (const entryPath of ['blog/first-post.md', 'blog/first-post.mdx']) { - const entry = fileURLToPath(new URL(entryPath, contentDir)); - const type = getEntryType(entry, contentPaths); - expect(type).to.equal('content'); - } - }); +describe('Content Collections - getEntryType', () => { + fixtures.forEach(({ title, contentPaths }) => { + describe(title, () => { + it('Returns "content" for Markdown files', () => { + for (const entryPath of ['blog/first-post.md', 'blog/first-post.mdx']) { + const entry = fileURLToPath(new URL(entryPath, contentPaths.contentDir)); + const type = getEntryType(entry, contentPaths); + expect(type).to.equal('content'); + } + }); - it('Returns "content" for Markdown files in nested directories', () => { - for (const entryPath of ['blog/2021/01/01/index.md', 'blog/2021/01/01/index.mdx']) { - const entry = fileURLToPath(new URL(entryPath, contentDir)); - const type = getEntryType(entry, contentPaths); - expect(type).to.equal('content'); - } - }); + it('Returns "content" for Markdown files in nested directories', () => { + for (const entryPath of ['blog/2021/01/01/index.md', 'blog/2021/01/01/index.mdx']) { + const entry = fileURLToPath(new URL(entryPath, contentPaths.contentDir)); + const type = getEntryType(entry, contentPaths); + expect(type).to.equal('content'); + } + }); - it('Returns "config" for config files', () => { - const entry = fileURLToPath(contentPaths.config.url); - const type = getEntryType(entry, contentPaths); - expect(type).to.equal('config'); - }); + it('Returns "config" for config files', () => { + const entry = fileURLToPath(contentPaths.config.url); + const type = getEntryType(entry, contentPaths); + expect(type).to.equal('config'); + }); - it('Returns "unsupported" for non-Markdown files', () => { - const entry = fileURLToPath(new URL('blog/robots.txt', contentDir)); - const type = getEntryType(entry, contentPaths); - expect(type).to.equal('unsupported'); - }); + it('Returns "unsupported" for non-Markdown files', () => { + const entry = fileURLToPath(new URL('blog/robots.txt', contentPaths.contentDir)); + const type = getEntryType(entry, contentPaths); + expect(type).to.equal('unsupported'); + }); - it('Returns "ignored" for .DS_Store', () => { - const entry = fileURLToPath(new URL('blog/.DS_Store', contentDir)); - const type = getEntryType(entry, contentPaths); - expect(type).to.equal('ignored'); - }); + it('Returns "ignored" for .DS_Store', () => { + const entry = fileURLToPath(new URL('blog/.DS_Store', contentPaths.contentDir)); + const type = getEntryType(entry, contentPaths); + expect(type).to.equal('ignored'); + }); - it('Returns "ignored" for unsupported files using an underscore', () => { - const entry = fileURLToPath(new URL('blog/_draft-robots.txt', contentDir)); - const type = getEntryType(entry, contentPaths); - expect(type).to.equal('ignored'); - }); + it('Returns "ignored" for unsupported files using an underscore', () => { + const entry = fileURLToPath(new URL('blog/_draft-robots.txt', contentPaths.contentDir)); + const type = getEntryType(entry, contentPaths); + expect(type).to.equal('ignored'); + }); - it('Returns "ignored" when using underscore on file name', () => { - const entry = fileURLToPath(new URL('blog/_first-post.md', contentDir)); - const type = getEntryType(entry, contentPaths); - expect(type).to.equal('ignored'); - }); + it('Returns "ignored" when using underscore on file name', () => { + const entry = fileURLToPath(new URL('blog/_first-post.md', contentPaths.contentDir)); + const type = getEntryType(entry, contentPaths); + expect(type).to.equal('ignored'); + }); - it('Returns "ignored" when using underscore on directory name', () => { - const entry = fileURLToPath(new URL('blog/_draft/first-post.md', contentDir)); - const type = getEntryType(entry, contentPaths); - expect(type).to.equal('ignored'); + it('Returns "ignored" when using underscore on directory name', () => { + const entry = fileURLToPath(new URL('blog/_draft/first-post.md', contentPaths.contentDir)); + const type = getEntryType(entry, contentPaths); + expect(type).to.equal('ignored'); + }); + }); }); });