diff --git a/packages/astro/components/Picture.astro b/packages/astro/components/Picture.astro index b0791f1635..f42846f184 100644 --- a/packages/astro/components/Picture.astro +++ b/packages/astro/components/Picture.astro @@ -1,7 +1,7 @@ --- import { getImage, type LocalImageProps, type RemoteImageProps } from 'astro:assets'; import type { GetImageResult, ImageOutputFormat } from '../dist/@types/astro'; -import { isESMImportedImage } from '../dist/assets/internal'; +import { isESMImportedImage } from '../dist/assets/utils/imageKind'; import { AstroError, AstroErrorData } from '../dist/core/errors/index.js'; import type { HTMLAttributes } from '../types'; diff --git a/packages/astro/src/assets/build/generate.ts b/packages/astro/src/assets/build/generate.ts index 1c55a93b98..0776dc2d69 100644 --- a/packages/astro/src/assets/build/generate.ts +++ b/packages/astro/src/assets/build/generate.ts @@ -10,9 +10,10 @@ import type { Logger } from '../../core/logger/core.js'; import { isRemotePath, prependForwardSlash } from '../../core/path.js'; import { isServerLikeOutput } from '../../prerender/utils.js'; import type { MapValue } from '../../type-utils.js'; -import { getConfiguredImageService, isESMImportedImage } from '../internal.js'; +import { getConfiguredImageService } from '../internal.js'; import type { LocalImageService } from '../services/service.js'; import type { AssetsGlobalStaticImagesList, ImageMetadata, ImageTransform } from '../types.js'; +import { isESMImportedImage } from '../utils/imageKind.js'; import { loadRemoteImage, type RemoteCacheEntry } from './remote.js'; interface GenerationDataUncached { diff --git a/packages/astro/src/assets/endpoint/config.ts b/packages/astro/src/assets/endpoint/config.ts new file mode 100644 index 0000000000..07cfe8faec --- /dev/null +++ b/packages/astro/src/assets/endpoint/config.ts @@ -0,0 +1,15 @@ +import type { AstroSettings } from '../../@types/astro.js'; + +export function injectImageEndpoint(settings: AstroSettings, mode: 'dev' | 'build') { + const endpointEntrypoint = + settings.config.image.endpoint ?? + (mode === 'dev' ? 'astro/assets/endpoint/node' : 'astro/assets/endpoint/generic'); + + settings.injectedRoutes.push({ + pattern: '/_image', + entrypoint: endpointEntrypoint, + prerender: false, + }); + + return settings; +} diff --git a/packages/astro/src/assets/endpoint/generic.ts b/packages/astro/src/assets/endpoint/generic.ts index a158448edb..5243a7e7c2 100644 --- a/packages/astro/src/assets/endpoint/generic.ts +++ b/packages/astro/src/assets/endpoint/generic.ts @@ -1,10 +1,11 @@ import { isRemotePath } from '@astrojs/internal-helpers/path'; import mime from 'mime/lite.js'; import type { APIRoute } from '../../@types/astro.js'; -import { getConfiguredImageService, isRemoteAllowed } from '../internal.js'; +import { getConfiguredImageService } from '../internal.js'; import { etag } from '../utils/etag.js'; // @ts-expect-error import { imageConfig } from 'astro:assets'; +import { isRemoteAllowed } from '../utils/remotePattern.js'; async function loadRemoteImage(src: URL) { try { diff --git a/packages/astro/src/assets/endpoint/node.ts b/packages/astro/src/assets/endpoint/node.ts index 7542b5eafd..604d75d255 100644 --- a/packages/astro/src/assets/endpoint/node.ts +++ b/packages/astro/src/assets/endpoint/node.ts @@ -3,10 +3,11 @@ import { readFile } from 'fs/promises'; import mime from 'mime/lite.js'; import os from 'os'; import type { APIRoute } from '../../@types/astro.js'; -import { getConfiguredImageService, isRemoteAllowed } from '../internal.js'; +import { getConfiguredImageService } from '../internal.js'; import { etag } from '../utils/etag.js'; // @ts-expect-error import { assetsDir, imageConfig } from 'astro:assets'; +import { isRemoteAllowed } from '../utils/remotePattern.js'; function replaceFileSystemReferences(src: string) { return os.platform().includes('win32') ? src.replace(/^\/@fs\//, '') : src.replace(/^\/@fs/, ''); diff --git a/packages/astro/src/assets/internal.ts b/packages/astro/src/assets/internal.ts index 06dad68524..2098e0d754 100644 --- a/packages/astro/src/assets/internal.ts +++ b/packages/astro/src/assets/internal.ts @@ -1,54 +1,14 @@ -import { isRemotePath } from '@astrojs/internal-helpers/path'; -import type { AstroConfig, AstroSettings } from '../@types/astro.js'; +import type { AstroConfig } from '../@types/astro.js'; import { AstroError, AstroErrorData } from '../core/errors/index.js'; import { DEFAULT_HASH_PROPS } from './consts.js'; import { isLocalService, type ImageService } from './services/service.js'; import type { GetImageResult, - ImageMetadata, ImageTransform, SrcSetValue, UnresolvedImageTransform, } from './types.js'; -import { matchHostname, matchPattern } from './utils/remotePattern.js'; - -export function injectImageEndpoint(settings: AstroSettings, mode: 'dev' | 'build') { - const endpointEntrypoint = - settings.config.image.endpoint ?? - (mode === 'dev' ? 'astro/assets/endpoint/node' : 'astro/assets/endpoint/generic'); - - settings.injectedRoutes.push({ - pattern: '/_image', - entrypoint: endpointEntrypoint, - prerender: false, - }); - - return settings; -} - -export function isESMImportedImage(src: ImageMetadata | string): src is ImageMetadata { - return typeof src === 'object'; -} - -export function isRemoteImage(src: ImageMetadata | string): src is string { - return typeof src === 'string'; -} - -export function isRemoteAllowed( - src: string, - { - domains = [], - remotePatterns = [], - }: Partial> -): boolean { - if (!isRemotePath(src)) return false; - - const url = new URL(src); - return ( - domains.some((domain) => matchHostname(url, domain)) || - remotePatterns.some((remotePattern) => matchPattern(url, remotePattern)) - ); -} +import { isESMImportedImage, isRemoteImage } from './utils/imageKind.js'; export async function getConfiguredImageService(): Promise { if (!globalThis?.astroAsset?.imageService) { diff --git a/packages/astro/src/assets/services/service.ts b/packages/astro/src/assets/services/service.ts index ab647b7107..0a4ff4064e 100644 --- a/packages/astro/src/assets/services/service.ts +++ b/packages/astro/src/assets/services/service.ts @@ -2,8 +2,9 @@ import type { AstroConfig } from '../../@types/astro.js'; import { AstroError, AstroErrorData } from '../../core/errors/index.js'; import { isRemotePath, joinPaths } from '../../core/path.js'; import { DEFAULT_HASH_PROPS, DEFAULT_OUTPUT_FORMAT, VALID_SUPPORTED_FORMATS } from '../consts.js'; -import { isESMImportedImage, isRemoteAllowed } from '../internal.js'; import type { ImageOutputFormat, ImageTransform, UnresolvedSrcSetValue } from '../types.js'; +import { isESMImportedImage } from '../utils/imageKind.js'; +import { isRemoteAllowed } from '../utils/remotePattern.js'; export type ImageService = LocalImageService | ExternalImageService; diff --git a/packages/astro/src/assets/utils/imageKind.ts b/packages/astro/src/assets/utils/imageKind.ts new file mode 100644 index 0000000000..866d9ed876 --- /dev/null +++ b/packages/astro/src/assets/utils/imageKind.ts @@ -0,0 +1,9 @@ +import type { ImageMetadata } from '../types.js'; + +export function isESMImportedImage(src: ImageMetadata | string): src is ImageMetadata { + return typeof src === 'object'; +} + +export function isRemoteImage(src: ImageMetadata | string): src is string { + return typeof src === 'string'; +} diff --git a/packages/astro/src/assets/utils/index.ts b/packages/astro/src/assets/utils/index.ts index 3e62465195..4fb4e42db3 100644 --- a/packages/astro/src/assets/utils/index.ts +++ b/packages/astro/src/assets/utils/index.ts @@ -1,4 +1,14 @@ export { emitESMImage } from './emitAsset.js'; +export { isESMImportedImage, isRemoteImage } from './imageKind.js'; export { imageMetadata } from './metadata.js'; export { getOrigQueryParams } from './queryParams.js'; +export { + isRemoteAllowed, + matchHostname, + matchPathname, + matchPattern, + matchPort, + matchProtocol, + type RemotePattern, +} from './remotePattern.js'; export { hashTransform, propsToFilename } from './transformToPath.js'; diff --git a/packages/astro/src/assets/utils/remotePattern.ts b/packages/astro/src/assets/utils/remotePattern.ts index 7708b42e73..3384e313e1 100644 --- a/packages/astro/src/assets/utils/remotePattern.ts +++ b/packages/astro/src/assets/utils/remotePattern.ts @@ -1,3 +1,6 @@ +import { isRemotePath } from '@astrojs/internal-helpers/path'; +import type { AstroConfig } from '../../@types/astro.js'; + export type RemotePattern = { hostname?: string; pathname?: string; @@ -61,3 +64,19 @@ export function matchPathname(url: URL, pathname?: string, allowWildcard?: boole return false; } + +export function isRemoteAllowed( + src: string, + { + domains = [], + remotePatterns = [], + }: Partial> +): boolean { + if (!isRemotePath(src)) return false; + + const url = new URL(src); + return ( + domains.some((domain) => matchHostname(url, domain)) || + remotePatterns.some((remotePattern) => matchPattern(url, remotePattern)) + ); +} diff --git a/packages/astro/src/assets/utils/transformToPath.ts b/packages/astro/src/assets/utils/transformToPath.ts index 4738ef2a1d..affeea8eb4 100644 --- a/packages/astro/src/assets/utils/transformToPath.ts +++ b/packages/astro/src/assets/utils/transformToPath.ts @@ -2,8 +2,8 @@ import { deterministicString } from 'deterministic-object-hash'; import { basename, extname } from 'node:path'; import { removeQueryString } from '../../core/path.js'; import { shorthash } from '../../runtime/server/shorthash.js'; -import { isESMImportedImage } from '../internal.js'; import type { ImageTransform } from '../types.js'; +import { isESMImportedImage } from './imageKind.js'; export function propsToFilename(transform: ImageTransform, hash: string) { let filename = removeQueryString( diff --git a/packages/astro/src/assets/vite-plugin-assets.ts b/packages/astro/src/assets/vite-plugin-assets.ts index ff6d0034e5..8dfd86d662 100644 --- a/packages/astro/src/assets/vite-plugin-assets.ts +++ b/packages/astro/src/assets/vite-plugin-assets.ts @@ -12,8 +12,8 @@ import { } from '../core/path.js'; import { isServerLikeOutput } from '../prerender/utils.js'; import { VALID_INPUT_FORMATS, VIRTUAL_MODULE_ID, VIRTUAL_SERVICE_ID } from './consts.js'; -import { isESMImportedImage } from './internal.js'; import { emitESMImage } from './utils/emitAsset.js'; +import { isESMImportedImage } from './utils/imageKind.js'; import { getProxyCode } from './utils/proxy.js'; import { hashTransform, propsToFilename } from './utils/transformToPath.js'; diff --git a/packages/astro/src/core/build/index.ts b/packages/astro/src/core/build/index.ts index 551e686dc4..8cb53f3c7c 100644 --- a/packages/astro/src/core/build/index.ts +++ b/packages/astro/src/core/build/index.ts @@ -10,7 +10,7 @@ import type { ManifestData, RuntimeMode, } from '../../@types/astro.js'; -import { injectImageEndpoint } from '../../assets/internal.js'; +import { injectImageEndpoint } from '../../assets/endpoint/config.js'; import { telemetry } from '../../events/index.js'; import { eventCliSession } from '../../events/session.js'; import { diff --git a/packages/astro/src/core/dev/container.ts b/packages/astro/src/core/dev/container.ts index 6cc5713f2b..4f2b5d4f2f 100644 --- a/packages/astro/src/core/dev/container.ts +++ b/packages/astro/src/core/dev/container.ts @@ -4,7 +4,7 @@ import type { AstroInlineConfig, AstroSettings } from '../../@types/astro.js'; import nodeFs from 'node:fs'; import * as vite from 'vite'; -import { injectImageEndpoint } from '../../assets/internal.js'; +import { injectImageEndpoint } from '../../assets/endpoint/config.js'; import { runHookConfigDone, runHookConfigSetup,