Skip to content

Commit

Permalink
[Content collections] Fix "underscore to ignore" warnings (#6122)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
bholmesdev authored Feb 3, 2023
1 parent f7f4721 commit 9f22ac3
Show file tree
Hide file tree
Showing 5 changed files with 117 additions and 34 deletions.
5 changes: 5 additions & 0 deletions .changeset/grumpy-bees-worry.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'astro': patch
---

Content collections: Fix accidental "use underscore to ignore" logs for `.DS_Store` files and underscored directory names.
45 changes: 14 additions & 31 deletions packages/astro/src/content/types-generator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -16,6 +16,7 @@ import {
getContentPaths,
getEntryInfo,
getEntrySlug,
getEntryType,
loadContentConfig,
NoCollectionError,
parseFrontmatter,
Expand Down Expand Up @@ -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') {
Expand All @@ -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,
Expand Down Expand Up @@ -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,
Expand Down
37 changes: 35 additions & 2 deletions packages/astro/src/content/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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(),
Expand Down Expand Up @@ -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) => {
Expand Down
2 changes: 1 addition & 1 deletion packages/astro/src/content/vite-plugin-content-imports.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
Original file line number Diff line number Diff line change
@@ -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');
});
});

0 comments on commit 9f22ac3

Please sign in to comment.