fix(content): Remove unsupported file warning for images (#6825)
This commit is contained in:
parent
44d935d5d6
commit
948a6d7be0
8 changed files with 63 additions and 25 deletions
5
.changeset/sweet-cows-eat.md
Normal file
5
.changeset/sweet-cows-eat.md
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
'astro': patch
|
||||||
|
---
|
||||||
|
|
||||||
|
Fix unnecessary warning when using images inside the `src/content` folder with `experimental.assets`
|
|
@ -1,9 +1,5 @@
|
||||||
export const VIRTUAL_MODULE_ID = 'astro:assets';
|
export const VIRTUAL_MODULE_ID = 'astro:assets';
|
||||||
export const VIRTUAL_SERVICE_ID = 'virtual:image-service';
|
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 = [
|
export const VALID_INPUT_FORMATS = [
|
||||||
// TODO: `image-size` does not support the following formats, so users can't import them.
|
// 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:
|
// However, it would be immensely useful to add, for three reasons:
|
||||||
|
@ -19,5 +15,11 @@ export const VALID_INPUT_FORMATS = [
|
||||||
'tiff',
|
'tiff',
|
||||||
'webp',
|
'webp',
|
||||||
'gif',
|
'gif',
|
||||||
|
'svg',
|
||||||
] as const;
|
] 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;
|
export const VALID_OUTPUT_FORMATS = ['avif', 'png', 'webp', 'jpeg', 'jpg'] as const;
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { AstroError, AstroErrorData } from '../../core/errors/index.js';
|
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 { isESMImportedImage } from '../internal.js';
|
||||||
import type { ImageOutputFormat, ImageTransform } from '../types.js';
|
import type { ImageOutputFormat, ImageTransform } from '../types.js';
|
||||||
|
|
||||||
|
@ -129,13 +129,13 @@ export const baseService: Omit<LocalImageService, 'transform'> = {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (!VALID_INPUT_FORMATS.includes(options.src.format as any)) {
|
if (!VALID_OPTIMIZABLE_FORMATS.includes(options.src.format as any)) {
|
||||||
throw new AstroError({
|
throw new AstroError({
|
||||||
...AstroErrorData.UnsupportedImageFormat,
|
...AstroErrorData.UnsupportedImageFormat,
|
||||||
message: AstroErrorData.UnsupportedImageFormat.message(
|
message: AstroErrorData.UnsupportedImageFormat.message(
|
||||||
options.src.format,
|
options.src.format,
|
||||||
options.src.src,
|
options.src.src,
|
||||||
VALID_INPUT_FORMATS
|
VALID_OPTIMIZABLE_FORMATS
|
||||||
),
|
),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,7 @@ import type { ImageService } from './services/service.js';
|
||||||
|
|
||||||
export type ImageQualityPreset = 'low' | 'mid' | 'high' | 'max' | (string & {});
|
export type ImageQualityPreset = 'low' | 'mid' | 'high' | 'max' | (string & {});
|
||||||
export type ImageQuality = ImageQualityPreset | number;
|
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 & {});
|
export type ImageOutputFormat = (typeof VALID_OUTPUT_FORMATS)[number] | (string & {});
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
|
|
|
@ -10,13 +10,13 @@ import { info, warn, type LogOptions } from '../core/logger/core.js';
|
||||||
import { isRelativePath } from '../core/path.js';
|
import { isRelativePath } from '../core/path.js';
|
||||||
import { CONTENT_TYPES_FILE } from './consts.js';
|
import { CONTENT_TYPES_FILE } from './consts.js';
|
||||||
import {
|
import {
|
||||||
|
NoCollectionError,
|
||||||
getContentEntryExts,
|
getContentEntryExts,
|
||||||
getContentPaths,
|
getContentPaths,
|
||||||
getEntryInfo,
|
getEntryInfo,
|
||||||
getEntrySlug,
|
getEntrySlug,
|
||||||
getEntryType,
|
getEntryType,
|
||||||
loadContentConfig,
|
loadContentConfig,
|
||||||
NoCollectionError,
|
|
||||||
parseFrontmatter,
|
parseFrontmatter,
|
||||||
type ContentConfig,
|
type ContentConfig,
|
||||||
type ContentObservable,
|
type ContentObservable,
|
||||||
|
@ -123,7 +123,12 @@ export async function createContentTypesGenerator({
|
||||||
}
|
}
|
||||||
return { shouldGenerateTypes: true };
|
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') {
|
if (fileType === 'ignored') {
|
||||||
return { shouldGenerateTypes: false };
|
return { shouldGenerateTypes: false };
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,9 +4,10 @@ import fsMod from 'node:fs';
|
||||||
import path from 'node:path';
|
import path from 'node:path';
|
||||||
import { fileURLToPath, pathToFileURL } from 'node:url';
|
import { fileURLToPath, pathToFileURL } from 'node:url';
|
||||||
import type { PluginContext } from 'rollup';
|
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 { 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 { AstroError, AstroErrorData } from '../core/errors/index.js';
|
||||||
import { CONTENT_TYPES_FILE } from './consts.js';
|
import { CONTENT_TYPES_FILE } from './consts.js';
|
||||||
import { errorMap } from './error-map.js';
|
import { errorMap } from './error-map.js';
|
||||||
|
@ -166,12 +167,18 @@ export function getEntryInfo({
|
||||||
export function getEntryType(
|
export function getEntryType(
|
||||||
entryPath: string,
|
entryPath: string,
|
||||||
paths: Pick<ContentPaths, 'config' | 'contentDir'>,
|
paths: Pick<ContentPaths, 'config' | 'contentDir'>,
|
||||||
contentFileExts: string[]
|
contentFileExts: string[],
|
||||||
|
// TODO: Unflag this when we're ready to release assets - erika, 2023-04-12
|
||||||
|
experimentalAssets: boolean
|
||||||
): 'content' | 'config' | 'ignored' | 'unsupported' {
|
): 'content' | 'config' | 'ignored' | 'unsupported' {
|
||||||
const { ext, base } = path.parse(entryPath);
|
const { ext, base } = path.parse(entryPath);
|
||||||
const fileUrl = pathToFileURL(entryPath);
|
const fileUrl = pathToFileURL(entryPath);
|
||||||
|
|
||||||
if (hasUnderscoreBelowContentDirectoryPath(fileUrl, paths.contentDir) || isOnIgnoreList(base)) {
|
if (
|
||||||
|
hasUnderscoreBelowContentDirectoryPath(fileUrl, paths.contentDir) ||
|
||||||
|
isOnIgnoreList(base) ||
|
||||||
|
(experimentalAssets && isImageAsset(ext))
|
||||||
|
) {
|
||||||
return 'ignored';
|
return 'ignored';
|
||||||
} else if (contentFileExts.includes(ext)) {
|
} else if (contentFileExts.includes(ext)) {
|
||||||
return 'content';
|
return 'content';
|
||||||
|
@ -186,6 +193,13 @@ function isOnIgnoreList(fileName: string) {
|
||||||
return ['.DS_Store'].includes(fileName);
|
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(
|
function hasUnderscoreBelowContentDirectoryPath(
|
||||||
fileUrl: URL,
|
fileUrl: URL,
|
||||||
contentDir: ContentPaths['contentDir']
|
contentDir: ContentPaths['contentDir']
|
||||||
|
|
|
@ -10,6 +10,7 @@ import { AstroError } from '../core/errors/errors.js';
|
||||||
import { escapeViteEnvReferences, getFileInfo } from '../vite-plugin-utils/index.js';
|
import { escapeViteEnvReferences, getFileInfo } from '../vite-plugin-utils/index.js';
|
||||||
import { CONTENT_FLAG } from './consts.js';
|
import { CONTENT_FLAG } from './consts.js';
|
||||||
import {
|
import {
|
||||||
|
NoCollectionError,
|
||||||
getContentEntryExts,
|
getContentEntryExts,
|
||||||
getContentPaths,
|
getContentPaths,
|
||||||
getEntryData,
|
getEntryData,
|
||||||
|
@ -17,7 +18,6 @@ import {
|
||||||
getEntrySlug,
|
getEntrySlug,
|
||||||
getEntryType,
|
getEntryType,
|
||||||
globalContentConfigObserver,
|
globalContentConfigObserver,
|
||||||
NoCollectionError,
|
|
||||||
type ContentConfig,
|
type ContentConfig,
|
||||||
} from './utils.js';
|
} from './utils.js';
|
||||||
|
|
||||||
|
@ -91,7 +91,12 @@ export function astroContentImportPlugin({
|
||||||
viteServer.watcher.on('all', async (event, entry) => {
|
viteServer.watcher.on('all', async (event, entry) => {
|
||||||
if (
|
if (
|
||||||
CHOKIDAR_MODIFIED_EVENTS.includes(event) &&
|
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.
|
// Content modules depend on config, so we need to invalidate them.
|
||||||
for (const modUrl of viteServer.moduleGraph.urlToModuleMap.keys()) {
|
for (const modUrl of viteServer.moduleGraph.urlToModuleMap.keys()) {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { getEntryType } from '../../../dist/content/utils.js';
|
|
||||||
import { expect } from 'chai';
|
import { expect } from 'chai';
|
||||||
import { fileURLToPath } from 'node:url';
|
import { fileURLToPath } from 'node:url';
|
||||||
|
import { getEntryType } from '../../../dist/content/utils.js';
|
||||||
|
|
||||||
const fixtures = [
|
const fixtures = [
|
||||||
{
|
{
|
||||||
|
@ -27,13 +27,14 @@ const fixtures = [
|
||||||
|
|
||||||
const contentFileExts = ['.md', '.mdx'];
|
const contentFileExts = ['.md', '.mdx'];
|
||||||
|
|
||||||
|
// TODO: Remove `getEntryType` last parameter once `experimental.assets` is no longer experimental
|
||||||
describe('Content Collections - getEntryType', () => {
|
describe('Content Collections - getEntryType', () => {
|
||||||
fixtures.forEach(({ title, contentPaths }) => {
|
fixtures.forEach(({ title, contentPaths }) => {
|
||||||
describe(title, () => {
|
describe(title, () => {
|
||||||
it('Returns "content" for Markdown files', () => {
|
it('Returns "content" for Markdown files', () => {
|
||||||
for (const entryPath of ['blog/first-post.md', 'blog/first-post.mdx']) {
|
for (const entryPath of ['blog/first-post.md', 'blog/first-post.mdx']) {
|
||||||
const entry = fileURLToPath(new URL(entryPath, contentPaths.contentDir));
|
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');
|
expect(type).to.equal('content');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -41,44 +42,50 @@ describe('Content Collections - getEntryType', () => {
|
||||||
it('Returns "content" for Markdown files in nested directories', () => {
|
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']) {
|
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 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');
|
expect(type).to.equal('content');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Returns "config" for config files', () => {
|
it('Returns "config" for config files', () => {
|
||||||
const entry = fileURLToPath(contentPaths.config.url);
|
const entry = fileURLToPath(contentPaths.config.url);
|
||||||
const type = getEntryType(entry, contentPaths, contentFileExts);
|
const type = getEntryType(entry, contentPaths, contentFileExts, false);
|
||||||
expect(type).to.equal('config');
|
expect(type).to.equal('config');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Returns "unsupported" for non-Markdown files', () => {
|
it('Returns "unsupported" for non-Markdown files', () => {
|
||||||
const entry = fileURLToPath(new URL('blog/robots.txt', contentPaths.contentDir));
|
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');
|
expect(type).to.equal('unsupported');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Returns "ignored" for .DS_Store', () => {
|
it('Returns "ignored" for .DS_Store', () => {
|
||||||
const entry = fileURLToPath(new URL('blog/.DS_Store', contentPaths.contentDir));
|
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');
|
expect(type).to.equal('ignored');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Returns "ignored" for unsupported files using an underscore', () => {
|
it('Returns "ignored" for unsupported files using an underscore', () => {
|
||||||
const entry = fileURLToPath(new URL('blog/_draft-robots.txt', contentPaths.contentDir));
|
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');
|
expect(type).to.equal('ignored');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Returns "ignored" when using underscore on file name', () => {
|
it('Returns "ignored" when using underscore on file name', () => {
|
||||||
const entry = fileURLToPath(new URL('blog/_first-post.md', contentPaths.contentDir));
|
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');
|
expect(type).to.equal('ignored');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Returns "ignored" when using underscore on directory name', () => {
|
it('Returns "ignored" when using underscore on directory name', () => {
|
||||||
const entry = fileURLToPath(new URL('blog/_draft/first-post.md', contentPaths.contentDir));
|
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');
|
expect(type).to.equal('ignored');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in a new issue