diff --git a/.changeset/sweet-cows-eat.md b/.changeset/sweet-cows-eat.md new file mode 100644 index 000000000..c71c91835 --- /dev/null +++ b/.changeset/sweet-cows-eat.md @@ -0,0 +1,5 @@ +--- +'astro': patch +--- + +Fix unnecessary warning when using images inside the `src/content` folder with `experimental.assets` diff --git a/packages/astro/src/assets/consts.ts b/packages/astro/src/assets/consts.ts index 30e5ef34c..9fd1bc7f0 100644 --- a/packages/astro/src/assets/consts.ts +++ b/packages/astro/src/assets/consts.ts @@ -1,9 +1,5 @@ export const VIRTUAL_MODULE_ID = 'astro:assets'; export const VIRTUAL_SERVICE_ID = 'virtual:image-service'; -/** - * Valid formats for optimizations in our base services. - * Certain formats can be imported (namely SVGs) but not optimized, so they are excluded from this list. - */ export const VALID_INPUT_FORMATS = [ // TODO: `image-size` does not support the following formats, so users can't import them. // However, it would be immensely useful to add, for three reasons: @@ -19,5 +15,11 @@ export const VALID_INPUT_FORMATS = [ 'tiff', 'webp', 'gif', + 'svg', ] as const; +/** + * Valid formats for optimizations in our base services. + * Certain formats can be imported (namely SVGs) but not processed, so they are excluded from this list. + */ +export const VALID_OPTIMIZABLE_FORMATS = ['jpeg', 'jpg', 'png', 'tiff', 'webp', 'gif'] as const; export const VALID_OUTPUT_FORMATS = ['avif', 'png', 'webp', 'jpeg', 'jpg'] as const; diff --git a/packages/astro/src/assets/services/service.ts b/packages/astro/src/assets/services/service.ts index 24284d158..aa33d8901 100644 --- a/packages/astro/src/assets/services/service.ts +++ b/packages/astro/src/assets/services/service.ts @@ -1,5 +1,5 @@ import { AstroError, AstroErrorData } from '../../core/errors/index.js'; -import { VALID_INPUT_FORMATS } from '../consts.js'; +import { VALID_OPTIMIZABLE_FORMATS } from '../consts.js'; import { isESMImportedImage } from '../internal.js'; import type { ImageOutputFormat, ImageTransform } from '../types.js'; @@ -129,13 +129,13 @@ export const baseService: Omit = { }); } } else { - if (!VALID_INPUT_FORMATS.includes(options.src.format as any)) { + if (!VALID_OPTIMIZABLE_FORMATS.includes(options.src.format as any)) { throw new AstroError({ ...AstroErrorData.UnsupportedImageFormat, message: AstroErrorData.UnsupportedImageFormat.message( options.src.format, options.src.src, - VALID_INPUT_FORMATS + VALID_OPTIMIZABLE_FORMATS ), }); } diff --git a/packages/astro/src/assets/types.ts b/packages/astro/src/assets/types.ts index 91bf97732..31a7106ec 100644 --- a/packages/astro/src/assets/types.ts +++ b/packages/astro/src/assets/types.ts @@ -4,7 +4,7 @@ import type { ImageService } from './services/service.js'; export type ImageQualityPreset = 'low' | 'mid' | 'high' | 'max' | (string & {}); export type ImageQuality = ImageQualityPreset | number; -export type ImageInputFormat = (typeof VALID_INPUT_FORMATS)[number] | 'svg'; +export type ImageInputFormat = (typeof VALID_INPUT_FORMATS)[number]; export type ImageOutputFormat = (typeof VALID_OUTPUT_FORMATS)[number] | (string & {}); declare global { diff --git a/packages/astro/src/content/types-generator.ts b/packages/astro/src/content/types-generator.ts index 5f9563a12..b6185c26a 100644 --- a/packages/astro/src/content/types-generator.ts +++ b/packages/astro/src/content/types-generator.ts @@ -10,13 +10,13 @@ import { info, warn, type LogOptions } from '../core/logger/core.js'; import { isRelativePath } from '../core/path.js'; import { CONTENT_TYPES_FILE } from './consts.js'; import { + NoCollectionError, getContentEntryExts, getContentPaths, getEntryInfo, getEntrySlug, getEntryType, loadContentConfig, - NoCollectionError, parseFrontmatter, type ContentConfig, type ContentObservable, @@ -123,7 +123,12 @@ export async function createContentTypesGenerator({ } return { shouldGenerateTypes: true }; } - const fileType = getEntryType(fileURLToPath(event.entry), contentPaths, contentEntryExts); + const fileType = getEntryType( + fileURLToPath(event.entry), + contentPaths, + contentEntryExts, + settings.config.experimental.assets + ); if (fileType === 'ignored') { return { shouldGenerateTypes: false }; } diff --git a/packages/astro/src/content/utils.ts b/packages/astro/src/content/utils.ts index 5d8a38908..97e96dc98 100644 --- a/packages/astro/src/content/utils.ts +++ b/packages/astro/src/content/utils.ts @@ -4,9 +4,10 @@ import fsMod from 'node:fs'; import path from 'node:path'; import { fileURLToPath, pathToFileURL } from 'node:url'; import type { PluginContext } from 'rollup'; -import { normalizePath, type ErrorPayload as ViteErrorPayload, type ViteDevServer } from 'vite'; +import { normalizePath, type ViteDevServer, type ErrorPayload as ViteErrorPayload } from 'vite'; import { z } from 'zod'; -import type { AstroConfig, AstroSettings } from '../@types/astro.js'; +import type { AstroConfig, AstroSettings, ImageInputFormat } from '../@types/astro.js'; +import { VALID_INPUT_FORMATS } from '../assets/consts.js'; import { AstroError, AstroErrorData } from '../core/errors/index.js'; import { CONTENT_TYPES_FILE } from './consts.js'; import { errorMap } from './error-map.js'; @@ -166,12 +167,18 @@ export function getEntryInfo({ export function getEntryType( entryPath: string, paths: Pick, - contentFileExts: string[] + contentFileExts: string[], + // TODO: Unflag this when we're ready to release assets - erika, 2023-04-12 + experimentalAssets: boolean ): 'content' | 'config' | 'ignored' | 'unsupported' { const { ext, base } = path.parse(entryPath); const fileUrl = pathToFileURL(entryPath); - if (hasUnderscoreBelowContentDirectoryPath(fileUrl, paths.contentDir) || isOnIgnoreList(base)) { + if ( + hasUnderscoreBelowContentDirectoryPath(fileUrl, paths.contentDir) || + isOnIgnoreList(base) || + (experimentalAssets && isImageAsset(ext)) + ) { return 'ignored'; } else if (contentFileExts.includes(ext)) { return 'content'; @@ -186,6 +193,13 @@ function isOnIgnoreList(fileName: string) { return ['.DS_Store'].includes(fileName); } +/** + * Return if a file extension is a valid image asset, so we can avoid outputting a warning for them. + */ +function isImageAsset(fileExt: string) { + return VALID_INPUT_FORMATS.includes(fileExt.slice(1) as ImageInputFormat); +} + function hasUnderscoreBelowContentDirectoryPath( fileUrl: URL, contentDir: ContentPaths['contentDir'] diff --git a/packages/astro/src/content/vite-plugin-content-imports.ts b/packages/astro/src/content/vite-plugin-content-imports.ts index 5b0116cdf..992986573 100644 --- a/packages/astro/src/content/vite-plugin-content-imports.ts +++ b/packages/astro/src/content/vite-plugin-content-imports.ts @@ -10,6 +10,7 @@ import { AstroError } from '../core/errors/errors.js'; import { escapeViteEnvReferences, getFileInfo } from '../vite-plugin-utils/index.js'; import { CONTENT_FLAG } from './consts.js'; import { + NoCollectionError, getContentEntryExts, getContentPaths, getEntryData, @@ -17,7 +18,6 @@ import { getEntrySlug, getEntryType, globalContentConfigObserver, - NoCollectionError, type ContentConfig, } from './utils.js'; @@ -91,7 +91,12 @@ export function astroContentImportPlugin({ viteServer.watcher.on('all', async (event, entry) => { if ( CHOKIDAR_MODIFIED_EVENTS.includes(event) && - getEntryType(entry, contentPaths, contentEntryExts) === 'config' + getEntryType( + entry, + contentPaths, + contentEntryExts, + settings.config.experimental.assets + ) === 'config' ) { // Content modules depend on config, so we need to invalidate them. for (const modUrl of viteServer.moduleGraph.urlToModuleMap.keys()) { 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 e6ab3141d..2aa9580e8 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 @@ -1,6 +1,6 @@ -import { getEntryType } from '../../../dist/content/utils.js'; import { expect } from 'chai'; import { fileURLToPath } from 'node:url'; +import { getEntryType } from '../../../dist/content/utils.js'; const fixtures = [ { @@ -27,13 +27,14 @@ const fixtures = [ const contentFileExts = ['.md', '.mdx']; +// TODO: Remove `getEntryType` last parameter once `experimental.assets` is no longer experimental 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, contentFileExts); + const type = getEntryType(entry, contentPaths, contentFileExts, false); expect(type).to.equal('content'); } }); @@ -41,44 +42,50 @@ describe('Content Collections - getEntryType', () => { 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, contentFileExts); + const type = getEntryType(entry, contentPaths, contentFileExts, false); expect(type).to.equal('content'); } }); it('Returns "config" for config files', () => { const entry = fileURLToPath(contentPaths.config.url); - const type = getEntryType(entry, contentPaths, contentFileExts); + const type = getEntryType(entry, contentPaths, contentFileExts, false); expect(type).to.equal('config'); }); it('Returns "unsupported" for non-Markdown files', () => { const entry = fileURLToPath(new URL('blog/robots.txt', contentPaths.contentDir)); - const type = getEntryType(entry, contentPaths, contentFileExts); + const type = getEntryType(entry, contentPaths, contentFileExts, false); expect(type).to.equal('unsupported'); }); it('Returns "ignored" for .DS_Store', () => { const entry = fileURLToPath(new URL('blog/.DS_Store', contentPaths.contentDir)); - const type = getEntryType(entry, contentPaths, contentFileExts); + const type = getEntryType(entry, contentPaths, contentFileExts, false); 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, contentFileExts); + const type = getEntryType(entry, contentPaths, contentFileExts, false); 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, contentFileExts); + const type = getEntryType(entry, contentPaths, contentFileExts, false); 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, contentFileExts); + const type = getEntryType(entry, contentPaths, contentFileExts, false); + expect(type).to.equal('ignored'); + }); + + it('Returns "ignored" for images', () => { + const entry = fileURLToPath(new URL('blog/first-post.png', contentPaths.contentDir)); + const type = getEntryType(entry, contentPaths, contentFileExts, true); expect(type).to.equal('ignored'); }); });