diff --git a/.changeset/early-times-drop.md b/.changeset/early-times-drop.md
new file mode 100644
index 000000000000..59fdc8cb8bd9
--- /dev/null
+++ b/.changeset/early-times-drop.md
@@ -0,0 +1,5 @@
+---
+'@astrojs/cloudflare': patch
+---
+
+Fixes duplicate logging showing up in some cases when prerendering pages
diff --git a/.changeset/long-bushes-rest.md b/.changeset/long-bushes-rest.md
new file mode 100644
index 000000000000..9107682aeb6b
--- /dev/null
+++ b/.changeset/long-bushes-rest.md
@@ -0,0 +1,5 @@
+---
+'astro': patch
+---
+
+Fixes images not working in MDX when using the Cloudflare adapter in certain cases
diff --git a/packages/astro/src/assets/utils/proxy.ts b/packages/astro/src/assets/utils/proxy.ts
index 975e8e0f36c6..05325b5d4986 100644
--- a/packages/astro/src/assets/utils/proxy.ts
+++ b/packages/astro/src/assets/utils/proxy.ts
@@ -13,7 +13,7 @@ export function getProxyCode(options: ImageMetadata, isSSR: boolean): string {
}
${
!isSSR
- ? `if (target[name] !== undefined && globalThis.astroAsset) globalThis.astroAsset?.referencedImages.add(${stringifiedFSPath});`
+ ? `if (target[name] !== undefined && globalThis.astroAsset) globalThis.astroAsset?.referencedImages?.add(${stringifiedFSPath});`
: ''
}
return target[name];
diff --git a/packages/astro/src/content/vite-plugin-content-assets.ts b/packages/astro/src/content/vite-plugin-content-assets.ts
index b6c1689043d7..71bbe7a7f25d 100644
--- a/packages/astro/src/content/vite-plugin-content-assets.ts
+++ b/packages/astro/src/content/vite-plugin-content-assets.ts
@@ -78,10 +78,19 @@ export function astroContentAssetPropagationPlugin({
},
},
configureServer(server) {
- if (!isRunnableDevEnvironment(server.environments[ASTRO_VITE_ENVIRONMENT_NAMES.ssr])) {
- return;
+ const ssrEnv = server.environments[ASTRO_VITE_ENVIRONMENT_NAMES.ssr];
+ if (isRunnableDevEnvironment(ssrEnv)) {
+ environment = ssrEnv;
+ } else if (
+ isRunnableDevEnvironment(server.environments[ASTRO_VITE_ENVIRONMENT_NAMES.astro])
+ ) {
+ // When the ssr environment is not a RunnableDevEnvironment (e.g. when using the
+ // Cloudflare adapter which runs ssr in workerd), fall back to the 'astro' environment
+ // which is always a RunnableDevEnvironment available in dev.
+ environment = server.environments[
+ ASTRO_VITE_ENVIRONMENT_NAMES.astro
+ ] as RunnableDevEnvironment;
}
- environment = server.environments[ASTRO_VITE_ENVIRONMENT_NAMES.ssr] as RunnableDevEnvironment;
},
transform: {
filter: {
@@ -96,7 +105,11 @@ export function astroContentAssetPropagationPlugin({
// so resolve collected styles and scripts here.
if (isAstroServerEnvironment(this.environment) && environment) {
if (!environment.moduleGraph.getModuleById(basePath)?.ssrModule) {
- await environment.runner.import(basePath);
+ // Ignore errors here — when using a fallback environment (e.g. the 'astro'
+ // env when Cloudflare's ssr env is non-runnable), the module may already be
+ // loaded in the fallback env's graph even if this import throws due to
+ // concurrent bundle editing.
+ await environment.runner.import(basePath).catch(() => {});
}
const {
styles,
diff --git a/packages/integrations/cloudflare/src/prerenderer.ts b/packages/integrations/cloudflare/src/prerenderer.ts
index 7bac45c085c7..8a3554ea695e 100644
--- a/packages/integrations/cloudflare/src/prerenderer.ts
+++ b/packages/integrations/cloudflare/src/prerenderer.ts
@@ -61,6 +61,7 @@ export function createCloudflarePrerenderer({
outDir: fileURLToPath(serverDir),
},
root: fileURLToPath(root),
+ logLevel: 'error',
preview: {
host: 'localhost',
port: 0, // Let the OS pick a free port
diff --git a/packages/integrations/cloudflare/test/fixtures/prerender-styles/astro.config.mjs b/packages/integrations/cloudflare/test/fixtures/prerender-styles/astro.config.mjs
index 2b45c7068f52..a5924b350fd7 100644
--- a/packages/integrations/cloudflare/test/fixtures/prerender-styles/astro.config.mjs
+++ b/packages/integrations/cloudflare/test/fixtures/prerender-styles/astro.config.mjs
@@ -1,7 +1,9 @@
+import mdx from '@astrojs/mdx';
import { defineConfig } from 'astro/config';
import tailwindcss from '@tailwindcss/vite';
export default defineConfig({
+ integrations: [mdx()],
vite: {
plugins: [tailwindcss()],
},
diff --git a/packages/integrations/cloudflare/test/fixtures/prerender-styles/package.json b/packages/integrations/cloudflare/test/fixtures/prerender-styles/package.json
index f2fa6954030c..a2a6ea674f7b 100644
--- a/packages/integrations/cloudflare/test/fixtures/prerender-styles/package.json
+++ b/packages/integrations/cloudflare/test/fixtures/prerender-styles/package.json
@@ -4,6 +4,7 @@
"private": true,
"dependencies": {
"@astrojs/cloudflare": "workspace:*",
+ "@astrojs/mdx": "workspace:*",
"@tailwindcss/vite": "^4.2.0",
"astro": "workspace:*",
"tailwindcss": "^4.2.0"
diff --git a/packages/integrations/cloudflare/test/fixtures/prerender-styles/src/components/StyledCard.astro b/packages/integrations/cloudflare/test/fixtures/prerender-styles/src/components/StyledCard.astro
new file mode 100644
index 000000000000..f97e50b6608f
--- /dev/null
+++ b/packages/integrations/cloudflare/test/fixtures/prerender-styles/src/components/StyledCard.astro
@@ -0,0 +1,13 @@
+---
+---
+
+
+
+
+
+
diff --git a/packages/integrations/cloudflare/test/fixtures/prerender-styles/src/content.config.ts b/packages/integrations/cloudflare/test/fixtures/prerender-styles/src/content.config.ts
new file mode 100644
index 000000000000..ba89389c8032
--- /dev/null
+++ b/packages/integrations/cloudflare/test/fixtures/prerender-styles/src/content.config.ts
@@ -0,0 +1,11 @@
+import { defineCollection, z } from 'astro:content';
+import { glob } from 'astro/loaders';
+
+const posts = defineCollection({
+ loader: glob({ pattern: '**/*.mdx', base: './src/content/posts' }),
+ schema: z.object({
+ title: z.string(),
+ }),
+});
+
+export const collections = { posts };
diff --git a/packages/integrations/cloudflare/test/fixtures/prerender-styles/src/content/posts/styled.mdx b/packages/integrations/cloudflare/test/fixtures/prerender-styles/src/content/posts/styled.mdx
new file mode 100644
index 000000000000..473eedc04d40
--- /dev/null
+++ b/packages/integrations/cloudflare/test/fixtures/prerender-styles/src/content/posts/styled.mdx
@@ -0,0 +1,7 @@
+---
+title: Styled Post
+---
+
+import StyledCard from '../../components/StyledCard.astro';
+
+Hello from MDX
diff --git a/packages/integrations/cloudflare/test/fixtures/prerender-styles/src/pages/posts/[slug].astro b/packages/integrations/cloudflare/test/fixtures/prerender-styles/src/pages/posts/[slug].astro
new file mode 100644
index 000000000000..8fe535e1928c
--- /dev/null
+++ b/packages/integrations/cloudflare/test/fixtures/prerender-styles/src/pages/posts/[slug].astro
@@ -0,0 +1,27 @@
+---
+import { getEntry, render } from 'astro:content';
+
+export const prerender = true;
+
+export async function getStaticPaths() {
+ const { getCollection } = await import('astro:content');
+ const posts = await getCollection('posts');
+ return posts.map((post) => ({
+ params: { slug: post.id },
+ props: { post },
+ }));
+}
+
+const { post } = Astro.props;
+const { Content } = await render(post);
+---
+
+
+
+
+ {post.data.title}
+
+
+
+
+
diff --git a/packages/integrations/cloudflare/test/prerender-styles.test.js b/packages/integrations/cloudflare/test/prerender-styles.test.js
index 73892a62f282..77df3fb8b270 100644
--- a/packages/integrations/cloudflare/test/prerender-styles.test.js
+++ b/packages/integrations/cloudflare/test/prerender-styles.test.js
@@ -43,8 +43,69 @@ describe('Prerendered page styles', () => {
it('includes Tailwind styles in prerendered page', async () => {
// With cloudflare adapter, prerendered pages are in dist/client/
const html = await fixture.readFile('/client/index.html');
- // Check that the bg-amber-500 class has its styles included
- assert.ok(html.includes('.bg-amber-500'), 'Expected .bg-amber-500 class to be in the HTML');
+ // Tailwind CSS is emitted as an external stylesheet linked from the HTML.
+ // Verify the HTML references a stylesheet and that the stylesheet contains the expected class.
+ assert.ok(html.includes('rel="stylesheet"'), 'Expected the HTML to reference a stylesheet');
+ const cssFiles = await fixture.glob('client/_astro/*.css');
+ assert.ok(cssFiles.length > 0, 'Expected at least one CSS file in _astro/');
+ let foundClass = false;
+ for (const cssFile of cssFiles) {
+ const css = await fixture.readFile('/' + cssFile);
+ if (css.includes('.bg-amber-500')) {
+ foundClass = true;
+ break;
+ }
+ }
+ assert.ok(foundClass, 'Expected .bg-amber-500 class to be in a generated CSS file');
+ });
+ });
+});
+
+describe('Styles from Astro components imported in MDX content collections', () => {
+ /** @type {import('../../../astro/test/test-utils').Fixture} */
+ let fixture;
+ let devServer;
+
+ before(async () => {
+ fixture = await loadFixture({
+ root: new URL('./fixtures/prerender-styles/', import.meta.url).toString(),
+ adapter: cloudflare(),
+ });
+ });
+
+ after(async () => {
+ await devServer?.stop();
+ await fixture.clean();
+ });
+
+ describe('dev', () => {
+ before(async () => {
+ devServer = await fixture.startDevServer();
+ });
+
+ it('includes styles from an Astro component imported in an MDX content collection entry', async () => {
+ const res = await fixture.fetch('/posts/styled');
+ const html = await res.text();
+ assert.ok(
+ html.includes('.mdx-styled-card'),
+ 'Expected .mdx-styled-card styles from StyledCard.astro to be injected in the MDX page',
+ );
+ });
+ });
+
+ describe('build', () => {
+ before(async () => {
+ await devServer?.stop();
+ devServer = undefined;
+ await fixture.build();
+ });
+
+ it('includes styles from an Astro component imported in an MDX content collection entry', async () => {
+ const html = await fixture.readFile('/client/posts/styled/index.html');
+ assert.ok(
+ html.includes('.mdx-styled-card'),
+ 'Expected .mdx-styled-card styles from StyledCard.astro to be in the built MDX page',
+ );
});
});
});
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index d2d2ca0808e5..db432a3a971d 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -4988,6 +4988,9 @@ importers:
'@astrojs/cloudflare':
specifier: workspace:*
version: link:../../..
+ '@astrojs/mdx':
+ specifier: workspace:*
+ version: link:../../../../mdx
'@tailwindcss/vite':
specifier: ^4.2.0
version: 4.2.0(vite@7.3.1(@types/node@25.2.3)(jiti@2.6.1)(lightningcss@1.31.1)(sass@1.97.3)(tsx@4.21.0)(yaml@2.8.2))