[Content collections] Fix "underscore to ignore" warnings (#6122)
* refactor: fix underscore check * fix: add "ignore list" to always silence log * fix: hide log on file deleted * refactor: move getEntryType to util * test: getEntryType unit * fix: handle all unsupported cases * chore: changeset
This commit is contained in:
parent
f7f4721231
commit
9f22ac3d09
5 changed files with 117 additions and 34 deletions
5
.changeset/grumpy-bees-worry.md
Normal file
5
.changeset/grumpy-bees-worry.md
Normal file
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
'astro': patch
|
||||
---
|
||||
|
||||
Content collections: Fix accidental "use underscore to ignore" logs for `.DS_Store` files and underscored directory names.
|
|
@ -6,8 +6,8 @@ import { fileURLToPath, pathToFileURL } from 'node:url';
|
|||
import { normalizePath, ViteDevServer } from 'vite';
|
||||
import type { AstroSettings } from '../@types/astro.js';
|
||||
import { info, LogOptions, warn } from '../core/logger/core.js';
|
||||
import { appendForwardSlash, isRelativePath } from '../core/path.js';
|
||||
import { contentFileExts, CONTENT_TYPES_FILE } from './consts.js';
|
||||
import { isRelativePath } from '../core/path.js';
|
||||
import { CONTENT_TYPES_FILE } from './consts.js';
|
||||
import {
|
||||
ContentConfig,
|
||||
ContentObservable,
|
||||
|
@ -16,6 +16,7 @@ import {
|
|||
getContentPaths,
|
||||
getEntryInfo,
|
||||
getEntrySlug,
|
||||
getEntryType,
|
||||
loadContentConfig,
|
||||
NoCollectionError,
|
||||
parseFrontmatter,
|
||||
|
@ -111,7 +112,7 @@ export async function createContentTypesGenerator({
|
|||
return { shouldGenerateTypes: true };
|
||||
}
|
||||
const fileType = getEntryType(fileURLToPath(event.entry), contentPaths);
|
||||
if (fileType === 'generated-types') {
|
||||
if (fileType === 'ignored') {
|
||||
return { shouldGenerateTypes: false };
|
||||
}
|
||||
if (fileType === 'config') {
|
||||
|
@ -125,22 +126,21 @@ export async function createContentTypesGenerator({
|
|||
|
||||
return { shouldGenerateTypes: true };
|
||||
}
|
||||
if (fileType === 'unknown') {
|
||||
if (fileType === 'unsupported') {
|
||||
// Avoid warning if file was deleted.
|
||||
if (event.name === 'unlink') {
|
||||
return { shouldGenerateTypes: false };
|
||||
}
|
||||
const entryInfo = getEntryInfo({
|
||||
entry: event.entry,
|
||||
contentDir: contentPaths.contentDir,
|
||||
// Allow underscore `_` files outside collection directories
|
||||
// Skip invalid file check. We already know it’s invalid.
|
||||
allowFilesOutsideCollection: true,
|
||||
});
|
||||
if (entryInfo.id.startsWith('_') && (event.name === 'add' || event.name === 'change')) {
|
||||
// Silently ignore `_` files.
|
||||
return { shouldGenerateTypes: false };
|
||||
} else {
|
||||
return {
|
||||
shouldGenerateTypes: false,
|
||||
error: new UnsupportedFileTypeError(entryInfo.id),
|
||||
};
|
||||
}
|
||||
return {
|
||||
shouldGenerateTypes: false,
|
||||
error: new UnsupportedFileTypeError(entryInfo.id),
|
||||
};
|
||||
}
|
||||
const entryInfo = getEntryInfo({
|
||||
entry: event.entry,
|
||||
|
@ -289,23 +289,6 @@ function removeEntry(contentTypes: ContentTypes, collectionKey: string, entryKey
|
|||
delete contentTypes[collectionKey][entryKey];
|
||||
}
|
||||
|
||||
export function getEntryType(
|
||||
entryPath: string,
|
||||
paths: ContentPaths
|
||||
): 'content' | 'config' | 'unknown' | 'generated-types' {
|
||||
const { dir: rawDir, ext, base } = path.parse(entryPath);
|
||||
const dir = appendForwardSlash(pathToFileURL(rawDir).href);
|
||||
if ((contentFileExts as readonly string[]).includes(ext)) {
|
||||
return 'content';
|
||||
} else if (new URL(base, dir).href === paths.config.href) {
|
||||
return 'config';
|
||||
} else if (new URL(base, dir).href === new URL(CONTENT_TYPES_FILE, paths.cacheDir).href) {
|
||||
return 'generated-types';
|
||||
} else {
|
||||
return 'unknown';
|
||||
}
|
||||
}
|
||||
|
||||
async function writeContentFiles({
|
||||
fs,
|
||||
contentPaths,
|
||||
|
|
|
@ -2,12 +2,13 @@ import { slug as githubSlug } from 'github-slugger';
|
|||
import matter from 'gray-matter';
|
||||
import type fsMod from 'node:fs';
|
||||
import path from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import { fileURLToPath, pathToFileURL } from 'node:url';
|
||||
import { ErrorPayload as ViteErrorPayload, normalizePath, ViteDevServer } from 'vite';
|
||||
import { z } from 'zod';
|
||||
import { AstroConfig, AstroSettings } from '../@types/astro.js';
|
||||
import { AstroError, AstroErrorData } from '../core/errors/index.js';
|
||||
import { CONTENT_TYPES_FILE } from './consts.js';
|
||||
import { appendForwardSlash } from '../core/path.js';
|
||||
import { contentFileExts, CONTENT_TYPES_FILE } from './consts.js';
|
||||
|
||||
export const collectionConfigParser = z.object({
|
||||
schema: z.any().optional(),
|
||||
|
@ -158,6 +159,38 @@ export function getEntryInfo({
|
|||
return res;
|
||||
}
|
||||
|
||||
export function getEntryType(
|
||||
entryPath: string,
|
||||
paths: Pick<ContentPaths, 'config'>
|
||||
): 'content' | 'config' | 'ignored' | 'unsupported' {
|
||||
const { dir: rawDir, ext, base } = path.parse(entryPath);
|
||||
const dir = appendForwardSlash(pathToFileURL(rawDir).href);
|
||||
const fileUrl = new URL(base, dir);
|
||||
|
||||
if (hasUnderscoreInPath(fileUrl) || isOnIgnoreList(fileUrl)) {
|
||||
return 'ignored';
|
||||
} else if ((contentFileExts as readonly string[]).includes(ext)) {
|
||||
return 'content';
|
||||
} else if (fileUrl.href === paths.config.href) {
|
||||
return 'config';
|
||||
} else {
|
||||
return 'unsupported';
|
||||
}
|
||||
}
|
||||
|
||||
function isOnIgnoreList(fileUrl: URL) {
|
||||
const { base } = path.parse(fileURLToPath(fileUrl));
|
||||
return ['.DS_Store'].includes(base);
|
||||
}
|
||||
|
||||
function hasUnderscoreInPath(fileUrl: URL): boolean {
|
||||
const parts = fileUrl.pathname.split('/');
|
||||
for (const part of parts) {
|
||||
if (part.startsWith('_')) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
const flattenErrorPath = (errorPath: (string | number)[]) => errorPath.join('.');
|
||||
|
||||
const errorMap: z.ZodErrorMap = (error, ctx) => {
|
||||
|
|
|
@ -7,7 +7,7 @@ import { AstroErrorData } from '../core/errors/errors-data.js';
|
|||
import { AstroError } from '../core/errors/errors.js';
|
||||
import { escapeViteEnvReferences, getFileInfo } from '../vite-plugin-utils/index.js';
|
||||
import { contentFileExts, CONTENT_FLAG } from './consts.js';
|
||||
import { getEntryType } from './types-generator.js';
|
||||
import { getEntryType } from './utils.js';
|
||||
import {
|
||||
ContentConfig,
|
||||
getContentPaths,
|
||||
|
|
|
@ -0,0 +1,62 @@
|
|||
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: new URL('src/content/config.ts', 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');
|
||||
}
|
||||
});
|
||||
|
||||
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 "config" for config files', () => {
|
||||
const entry = fileURLToPath(contentPaths.config);
|
||||
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 "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 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" 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 directory name', () => {
|
||||
const entry = fileURLToPath(new URL('blog/_draft/first-post.md', contentDir));
|
||||
const type = getEntryType(entry, contentPaths);
|
||||
expect(type).to.equal('ignored');
|
||||
});
|
||||
});
|
Loading…
Reference in a new issue