diff --git a/.changeset/soft-bags-flash.md b/.changeset/soft-bags-flash.md new file mode 100644 index 0000000000..8fa979cc0e --- /dev/null +++ b/.changeset/soft-bags-flash.md @@ -0,0 +1,5 @@ +--- +"astro": patch +--- + +Fixes HMR for MDX dependencies in Content Collections diff --git a/packages/astro/e2e/content-collections.test.js b/packages/astro/e2e/content-collections.test.js new file mode 100644 index 0000000000..344a27e997 --- /dev/null +++ b/packages/astro/e2e/content-collections.test.js @@ -0,0 +1,32 @@ +import { expect } from '@playwright/test'; +import { testFactory } from './test-utils.js'; + +const test = testFactory({ root: './fixtures/content-collections/' }); + +let devServer; + +test.beforeAll(async ({ astro }) => { + devServer = await astro.startDevServer(); +}); + +test.afterAll(async ({ astro }) => { + await devServer.stop(); + astro.resetAllFiles(); +}); + +test.describe('Content Collections', () => { + test('HMR', async ({ page, astro }) => { + await page.goto(astro.resolveUrl('/')); + + await astro.editFile('./src/components/MyComponent.astro', (original) => + original.replace('red', 'green') + ); + + const h1 = page.locator('#my-heading'); + + await expect(h1, 'should have green color').toHaveCSS( + 'color', + 'rgb(0, 128, 0)' + ); + }); +}); diff --git a/packages/astro/e2e/fixtures/content-collections/astro.config.mjs b/packages/astro/e2e/fixtures/content-collections/astro.config.mjs new file mode 100644 index 0000000000..c14d5d0128 --- /dev/null +++ b/packages/astro/e2e/fixtures/content-collections/astro.config.mjs @@ -0,0 +1,9 @@ +import { defineConfig } from 'astro/config'; +import mdx from '@astrojs/mdx'; + +// https://astro.build/config +export default defineConfig({ + integrations: [ + mdx(), + ], +}); diff --git a/packages/astro/e2e/fixtures/content-collections/package.json b/packages/astro/e2e/fixtures/content-collections/package.json new file mode 100644 index 0000000000..169f7590b3 --- /dev/null +++ b/packages/astro/e2e/fixtures/content-collections/package.json @@ -0,0 +1,9 @@ +{ + "name": "@e2e/content-collections", + "version": "0.0.0", + "private": true, + "dependencies": { + "@astrojs/mdx": "workspace:*", + "astro": "workspace:*" + } +} diff --git a/packages/astro/e2e/fixtures/content-collections/src/components/MyComponent.astro b/packages/astro/e2e/fixtures/content-collections/src/components/MyComponent.astro new file mode 100644 index 0000000000..f9f831bb2e --- /dev/null +++ b/packages/astro/e2e/fixtures/content-collections/src/components/MyComponent.astro @@ -0,0 +1,10 @@ +

+ Some text here +

+ + + diff --git a/packages/astro/e2e/fixtures/content-collections/src/content/posts/post-1.mdx b/packages/astro/e2e/fixtures/content-collections/src/content/posts/post-1.mdx new file mode 100644 index 0000000000..d03b9b996c --- /dev/null +++ b/packages/astro/e2e/fixtures/content-collections/src/content/posts/post-1.mdx @@ -0,0 +1,6 @@ +--- +--- + +import MyComponent from '../../components/MyComponent.astro'; + + diff --git a/packages/astro/e2e/fixtures/content-collections/src/pages/index.astro b/packages/astro/e2e/fixtures/content-collections/src/pages/index.astro new file mode 100644 index 0000000000..4d6f175019 --- /dev/null +++ b/packages/astro/e2e/fixtures/content-collections/src/pages/index.astro @@ -0,0 +1,8 @@ +--- +import { getEntryBySlug } from 'astro:content' + +const post = await getEntryBySlug('posts', 'post-1') +const { Content } = await post.render(); +--- + + diff --git a/packages/astro/src/content/vite-plugin-content-assets.ts b/packages/astro/src/content/vite-plugin-content-assets.ts index 6eeef1c406..b985eb9a73 100644 --- a/packages/astro/src/content/vite-plugin-content-assets.ts +++ b/packages/astro/src/content/vite-plugin-content-assets.ts @@ -64,13 +64,29 @@ export function astroContentAssetPropagationPlugin({ if (!devModuleLoader.getModuleById(basePath)?.ssrModule) { await devModuleLoader.import(basePath); } - const { styles, urls } = await getStylesForURL(pathToFileURL(basePath), devModuleLoader); + const { + styles, + urls, + crawledFiles: styleCrawledFiles, + } = await getStylesForURL(pathToFileURL(basePath), devModuleLoader); - const hoistedScripts = await getScriptsForURL( - pathToFileURL(basePath), - settings.config.root, - devModuleLoader - ); + const { scripts: hoistedScripts, crawledFiles: scriptCrawledFiles } = + await getScriptsForURL(pathToFileURL(basePath), settings.config.root, devModuleLoader); + + // Register files we crawled to be able to retrieve the rendered styles and scripts, + // as when they get updated, we need to re-transform ourselves. + // We also only watch files within the user source code, as changes in node_modules + // are usually also ignored by Vite. + for (const file of styleCrawledFiles) { + if (!file.includes('node_modules')) { + this.addWatchFile(file); + } + } + for (const file of scriptCrawledFiles) { + if (!file.includes('node_modules')) { + this.addWatchFile(file); + } + } stringifiedLinks = JSON.stringify([...urls]); stringifiedStyles = JSON.stringify(styles.map((s) => s.content)); diff --git a/packages/astro/src/core/module-loader/loader.ts b/packages/astro/src/core/module-loader/loader.ts index 4d98727172..976354448a 100644 --- a/packages/astro/src/core/module-loader/loader.ts +++ b/packages/astro/src/core/module-loader/loader.ts @@ -41,6 +41,7 @@ export interface ModuleLoader { export interface ModuleNode { id: string | null; url: string; + file: string | null; ssrModule: Record | null; ssrTransformResult: { deps?: string[]; diff --git a/packages/astro/src/vite-plugin-astro-server/css.ts b/packages/astro/src/vite-plugin-astro-server/css.ts index 0f0002907a..cda2ef002b 100644 --- a/packages/astro/src/vite-plugin-astro-server/css.ts +++ b/packages/astro/src/vite-plugin-astro-server/css.ts @@ -13,12 +13,16 @@ interface ImportedStyle { export async function getStylesForURL( filePath: URL, loader: ModuleLoader -): Promise<{ urls: Set; styles: ImportedStyle[] }> { +): Promise<{ urls: Set; styles: ImportedStyle[]; crawledFiles: Set }> { const importedCssUrls = new Set(); // Map of url to injected style object. Use a `url` key to deduplicate styles const importedStylesMap = new Map(); + const crawledFiles = new Set(); for await (const importedModule of crawlGraph(loader, viteID(filePath), true)) { + if (importedModule.file) { + crawledFiles.add(importedModule.file); + } if (isBuildableCSSRequest(importedModule.url)) { // In dev, we inline all styles if possible let css = ''; @@ -60,5 +64,6 @@ export async function getStylesForURL( return { urls: importedCssUrls, styles: [...importedStylesMap.values()], + crawledFiles, }; } diff --git a/packages/astro/src/vite-plugin-astro-server/route.ts b/packages/astro/src/vite-plugin-astro-server/route.ts index 9f9951b7e3..f04e236418 100644 --- a/packages/astro/src/vite-plugin-astro-server/route.ts +++ b/packages/astro/src/vite-plugin-astro-server/route.ts @@ -393,7 +393,7 @@ async function getScriptsAndStyles({ pipeline, filePath }: GetScriptsAndStylesPa const settings = pipeline.getSettings(); const mode = pipeline.getEnvironment().mode; // Add hoisted script tags - const scripts = await getScriptsForURL(filePath, settings.config.root, moduleLoader); + const { scripts } = await getScriptsForURL(filePath, settings.config.root, moduleLoader); // Inject HMR scripts if (isPage(filePath, settings) && mode === 'development') { diff --git a/packages/astro/src/vite-plugin-astro-server/scripts.ts b/packages/astro/src/vite-plugin-astro-server/scripts.ts index 00bc4054b0..9029097532 100644 --- a/packages/astro/src/vite-plugin-astro-server/scripts.ts +++ b/packages/astro/src/vite-plugin-astro-server/scripts.ts @@ -9,12 +9,16 @@ export async function getScriptsForURL( filePath: URL, root: URL, loader: ModuleLoader -): Promise> { +): Promise<{ scripts: Set; crawledFiles: Set }> { const elements = new Set(); + const crawledFiles = new Set(); const rootID = viteID(filePath); const modInfo = loader.getModuleInfo(rootID); addHoistedScripts(elements, modInfo, root); for await (const moduleNode of crawlGraph(loader, rootID, true)) { + if (moduleNode.file) { + crawledFiles.add(moduleNode.file); + } const id = moduleNode.id; if (id) { const info = loader.getModuleInfo(id); @@ -22,7 +26,7 @@ export async function getScriptsForURL( } } - return elements; + return { scripts: elements, crawledFiles }; } function addHoistedScripts(set: Set, info: ModuleInfo | null, root: URL) { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 193bb1ae73..d0d0cb211e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -935,6 +935,15 @@ importers: specifier: workspace:* version: link:../../.. + packages/astro/e2e/fixtures/content-collections: + dependencies: + '@astrojs/mdx': + specifier: workspace:* + version: link:../../../../integrations/mdx + astro: + specifier: workspace:* + version: link:../../.. + packages/astro/e2e/fixtures/css: dependencies: astro: