diff --git a/.changeset/neat-balloons-doubt.md b/.changeset/neat-balloons-doubt.md new file mode 100644 index 000000000..d0a004dec --- /dev/null +++ b/.changeset/neat-balloons-doubt.md @@ -0,0 +1,5 @@ +--- +'astro': patch +--- + +Fix `astro:assets` endpoint not working in dev and SSR if `experimental.assets` was enabled by an integration (such as Starlight) diff --git a/packages/astro/src/assets/internal.ts b/packages/astro/src/assets/internal.ts index 635d0a5e7..c56b5369c 100644 --- a/packages/astro/src/assets/internal.ts +++ b/packages/astro/src/assets/internal.ts @@ -1,7 +1,18 @@ +import type { AstroSettings } from '../@types/astro.js'; import { AstroError, AstroErrorData } from '../core/errors/index.js'; import { isLocalService, type ImageService } from './services/service.js'; import type { GetImageResult, ImageMetadata, ImageTransform } from './types.js'; +export function injectImageEndpoint(settings: AstroSettings) { + settings.injectedRoutes.push({ + pattern: '/_image', + entryPoint: 'astro/assets/image-endpoint', + prerender: false, + }); + + return settings; +} + export function isESMImportedImage(src: ImageMetadata | string): src is ImageMetadata { return typeof src === 'object'; } diff --git a/packages/astro/src/cli/load-settings.ts b/packages/astro/src/cli/load-settings.ts index ce9094c65..9377825c4 100644 --- a/packages/astro/src/cli/load-settings.ts +++ b/packages/astro/src/cli/load-settings.ts @@ -27,10 +27,9 @@ export async function loadSettings({ cmd, flags, logging }: LoadSettingsOptions) return {} as any; }); - const mode = cmd === 'build' ? 'build' : 'dev'; if (!initialAstroConfig) return; telemetry.record(event.eventCliSession(cmd, initialUserConfig, flags)); - return createSettings(initialAstroConfig, mode, root); + return createSettings(initialAstroConfig, root); } export async function handleConfigError( diff --git a/packages/astro/src/config/index.ts b/packages/astro/src/config/index.ts index 2536df8e7..a9e32186d 100644 --- a/packages/astro/src/config/index.ts +++ b/packages/astro/src/config/index.ts @@ -35,7 +35,7 @@ export function getViteConfig(inlineConfig: UserConfig) { level: 'info', }; const { astroConfig: config } = await openConfig({ cmd }); - const settings = createSettings(config, cmd, inlineConfig.root); + const settings = createSettings(config, inlineConfig.root); await runHookConfigSetup({ settings, command: cmd, logging }); const viteConfig = await createVite( { diff --git a/packages/astro/src/core/build/index.ts b/packages/astro/src/core/build/index.ts index 8935c956e..5ac5d2b0f 100644 --- a/packages/astro/src/core/build/index.ts +++ b/packages/astro/src/core/build/index.ts @@ -4,12 +4,14 @@ import { performance } from 'node:perf_hooks'; import type * as vite from 'vite'; import type yargs from 'yargs-parser'; import type { AstroConfig, AstroSettings, ManifestData, RuntimeMode } from '../../@types/astro'; +import { injectImageEndpoint } from '../../assets/internal.js'; import { runHookBuildDone, runHookBuildStart, runHookConfigDone, runHookConfigSetup, } from '../../integrations/index.js'; +import { isServerLikeOutput } from '../../prerender/utils.js'; import { createVite } from '../create-vite.js'; import { debug, info, levels, timerMessage, warn, type LogOptions } from '../logger/core.js'; import { printHelp } from '../messages.js'; @@ -89,6 +91,13 @@ class AstroBuilder { command: 'build', logging, }); + + // HACK: Since we only inject the endpoint if `experimental.assets` is on and it's possible for an integration to + // add that flag, we need to only check and inject the endpoint after running the config setup hook. + if (this.settings.config.experimental.assets && isServerLikeOutput(this.settings.config)) { + this.settings = injectImageEndpoint(this.settings); + } + this.manifest = createRouteManifest({ settings: this.settings }, this.logging); const viteConfig = await createVite( diff --git a/packages/astro/src/core/config/settings.ts b/packages/astro/src/core/config/settings.ts index 222e6461b..35d7d252b 100644 --- a/packages/astro/src/core/config/settings.ts +++ b/packages/astro/src/core/config/settings.ts @@ -4,7 +4,6 @@ import { fileURLToPath, pathToFileURL } from 'node:url'; import type { AstroConfig, AstroSettings, AstroUserConfig } from '../../@types/astro'; import { getContentPaths } from '../../content/index.js'; import jsxRenderer from '../../jsx/renderer.js'; -import { isServerLikeOutput } from '../../prerender/utils.js'; import { markdownContentEntryType } from '../../vite-plugin-markdown/content-entry-type.js'; import { getDefaultClientDirectives } from '../client-directive/index.js'; import { AstroError, AstroErrorData } from '../errors/index.js'; @@ -14,7 +13,7 @@ import { createDefaultDevConfig } from './config.js'; import { AstroTimer } from './timer.js'; import { loadTSConfig } from './tsconfig.js'; -export function createBaseSettings(config: AstroConfig, mode: 'build' | 'dev'): AstroSettings { +export function createBaseSettings(config: AstroConfig): AstroSettings { const { contentDir } = getContentPaths(config); return { config, @@ -22,10 +21,7 @@ export function createBaseSettings(config: AstroConfig, mode: 'build' | 'dev'): tsConfigPath: undefined, adapter: undefined, - injectedRoutes: - config.experimental.assets && (isServerLikeOutput(config) || mode === 'dev') - ? [{ pattern: '/_image', entryPoint: 'astro/assets/image-endpoint', prerender: false }] - : [], + injectedRoutes: [], pageExtensions: ['.astro', '.html', ...SUPPORTED_MARKDOWN_FILE_EXTENSIONS], contentEntryTypes: [markdownContentEntryType], dataEntryTypes: [ @@ -108,13 +104,9 @@ export function createBaseSettings(config: AstroConfig, mode: 'build' | 'dev'): }; } -export function createSettings( - config: AstroConfig, - mode: 'build' | 'dev', - cwd?: string -): AstroSettings { +export function createSettings(config: AstroConfig, cwd?: string): AstroSettings { const tsconfig = loadTSConfig(cwd); - const settings = createBaseSettings(config, mode); + const settings = createBaseSettings(config); const watchFiles = tsconfig?.exists ? [tsconfig.path, ...tsconfig.extendedPaths] : []; @@ -136,5 +128,5 @@ export async function createDefaultDevSettings( root = fileURLToPath(root); } const config = await createDefaultDevConfig(userConfig, root); - return createBaseSettings(config, 'dev'); + return createBaseSettings(config); } diff --git a/packages/astro/src/core/dev/container.ts b/packages/astro/src/core/dev/container.ts index 8f922c6dd..d7ab1ae9f 100644 --- a/packages/astro/src/core/dev/container.ts +++ b/packages/astro/src/core/dev/container.ts @@ -4,6 +4,7 @@ import type { AstroSettings, AstroUserConfig } from '../../@types/astro'; import nodeFs from 'node:fs'; import * as vite from 'vite'; +import { injectImageEndpoint } from '../../assets/internal.js'; import { runHookConfigDone, runHookConfigSetup, @@ -64,6 +65,13 @@ export async function createContainer(params: CreateContainerParams = {}): Promi logging, isRestart, }); + + // HACK: Since we only inject the endpoint if `experimental.assets` is on and it's possible for an integration to + // add that flag, we need to only check and inject the endpoint after running the config setup hook. + if (settings.config.experimental.assets) { + settings = injectImageEndpoint(settings); + } + const { host, headers, open } = settings.config.server; // The client entrypoint for renderers. Since these are imported dynamically diff --git a/packages/astro/src/core/dev/restart.ts b/packages/astro/src/core/dev/restart.ts index 887470fb8..d96cc0b50 100644 --- a/packages/astro/src/core/dev/restart.ts +++ b/packages/astro/src/core/dev/restart.ts @@ -92,7 +92,7 @@ export async function restartContainer({ }); info(logging, 'astro', logMsg + '\n'); let astroConfig = newConfig.astroConfig; - const settings = createSettings(astroConfig, 'dev', resolvedRoot); + const settings = createSettings(astroConfig, resolvedRoot); await close(); return { container: await createRestartedContainer(container, settings, needsStart), diff --git a/packages/astro/test/test-utils.js b/packages/astro/test/test-utils.js index 7301040c5..94912687f 100644 --- a/packages/astro/test/test-utils.js +++ b/packages/astro/test/test-utils.js @@ -138,8 +138,8 @@ export async function loadFixture(inlineConfig) { * the `AstroSettings`. This function helps to create a fresh settings object that is used by the * command functions below to prevent tests from polluting each other. */ - const getSettings = async (mode) => { - let settings = createSettings(config, mode, fileURLToPath(cwd)); + const getSettings = async () => { + let settings = createSettings(config, fileURLToPath(cwd)); if (config.integrations.find((integration) => integration.name === '@astrojs/mdx')) { // Enable default JSX integration. It needs to come first, so unshift rather than push! const { default: jsxRenderer } = await import('astro/jsx/renderer.js'); diff --git a/packages/astro/test/units/config/format.test.js b/packages/astro/test/units/config/format.test.js index cfa614bfb..b230ea6b4 100644 --- a/packages/astro/test/units/config/format.test.js +++ b/packages/astro/test/units/config/format.test.js @@ -27,7 +27,7 @@ describe('Astro config formats', () => { logging: defaultLogging, fsMod: fs, }); - const settings = createSettings(astroConfig, 'dev'); + const settings = createSettings(astroConfig); await runInContainer({ fs, root, settings }, () => { expect(true).to.equal( diff --git a/packages/astro/test/units/dev/collections-mixed-content-errors.test.js b/packages/astro/test/units/dev/collections-mixed-content-errors.test.js index b7f571019..4ebf2b510 100644 --- a/packages/astro/test/units/dev/collections-mixed-content-errors.test.js +++ b/packages/astro/test/units/dev/collections-mixed-content-errors.test.js @@ -10,7 +10,7 @@ const logging = defaultLogging; async function sync({ fs, config = {} }) { const astroConfig = await validateConfig(config, fileURLToPath(root), 'prod'); - const settings = createSettings(astroConfig, 'build', fileURLToPath(root)); + const settings = createSettings(astroConfig, fileURLToPath(root)); return _sync(settings, { logging, fs }); } diff --git a/packages/astro/test/units/dev/restart.test.js b/packages/astro/test/units/dev/restart.test.js index f5fa679bf..5dd03fe46 100644 --- a/packages/astro/test/units/dev/restart.test.js +++ b/packages/astro/test/units/dev/restart.test.js @@ -133,7 +133,7 @@ describe('dev container restarts', () => { cmd: 'dev', logging: defaultLogging, }); - const settings = createSettings(astroConfig, 'dev'); + const settings = createSettings(astroConfig); let restart = await createContainerWithAutomaticRestart({ params: { fs, root, settings }, @@ -167,7 +167,7 @@ describe('dev container restarts', () => { cmd: 'dev', logging: defaultLogging, }); - const settings = createSettings(astroConfig, 'dev', fileURLToPath(root)); + const settings = createSettings(astroConfig, fileURLToPath(root)); let restart = await createContainerWithAutomaticRestart({ params: { fs, root, settings }, @@ -199,7 +199,7 @@ describe('dev container restarts', () => { cmd: 'dev', logging: defaultLogging, }); - const settings = createSettings(astroConfig, 'dev', fileURLToPath(root)); + const settings = createSettings(astroConfig, fileURLToPath(root)); let restart = await createContainerWithAutomaticRestart({ params: { fs, root, settings },