mirror of
https://github.com/withastro/astro.git
synced 2025-01-06 22:10:10 -05:00
Fix HMR in MDX deps in Content Collections (#9956)
Co-authored-by: Florian Lefebvre <contact@florian-lefebvre.dev> Co-authored-by: bluwy <bjornlu.dev@gmail.com>
This commit is contained in:
parent
6e30bef652
commit
81acac24a3
13 changed files with 124 additions and 10 deletions
5
.changeset/soft-bags-flash.md
Normal file
5
.changeset/soft-bags-flash.md
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
"astro": patch
|
||||||
|
---
|
||||||
|
|
||||||
|
Fixes HMR for MDX dependencies in Content Collections
|
32
packages/astro/e2e/content-collections.test.js
Normal file
32
packages/astro/e2e/content-collections.test.js
Normal file
|
@ -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)'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,9 @@
|
||||||
|
import { defineConfig } from 'astro/config';
|
||||||
|
import mdx from '@astrojs/mdx';
|
||||||
|
|
||||||
|
// https://astro.build/config
|
||||||
|
export default defineConfig({
|
||||||
|
integrations: [
|
||||||
|
mdx(),
|
||||||
|
],
|
||||||
|
});
|
|
@ -0,0 +1,9 @@
|
||||||
|
{
|
||||||
|
"name": "@e2e/content-collections",
|
||||||
|
"version": "0.0.0",
|
||||||
|
"private": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@astrojs/mdx": "workspace:*",
|
||||||
|
"astro": "workspace:*"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,10 @@
|
||||||
|
<h1 id="my-heading">
|
||||||
|
Some text here
|
||||||
|
</h1>
|
||||||
|
|
||||||
|
|
||||||
|
<style>
|
||||||
|
h1 {
|
||||||
|
color: red;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,6 @@
|
||||||
|
---
|
||||||
|
---
|
||||||
|
|
||||||
|
import MyComponent from '../../components/MyComponent.astro';
|
||||||
|
|
||||||
|
<MyComponent />
|
|
@ -0,0 +1,8 @@
|
||||||
|
---
|
||||||
|
import { getEntryBySlug } from 'astro:content'
|
||||||
|
|
||||||
|
const post = await getEntryBySlug('posts', 'post-1')
|
||||||
|
const { Content } = await post.render();
|
||||||
|
---
|
||||||
|
|
||||||
|
<Content/>
|
|
@ -64,13 +64,29 @@ export function astroContentAssetPropagationPlugin({
|
||||||
if (!devModuleLoader.getModuleById(basePath)?.ssrModule) {
|
if (!devModuleLoader.getModuleById(basePath)?.ssrModule) {
|
||||||
await devModuleLoader.import(basePath);
|
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(
|
const { scripts: hoistedScripts, crawledFiles: scriptCrawledFiles } =
|
||||||
pathToFileURL(basePath),
|
await getScriptsForURL(pathToFileURL(basePath), settings.config.root, devModuleLoader);
|
||||||
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]);
|
stringifiedLinks = JSON.stringify([...urls]);
|
||||||
stringifiedStyles = JSON.stringify(styles.map((s) => s.content));
|
stringifiedStyles = JSON.stringify(styles.map((s) => s.content));
|
||||||
|
|
|
@ -41,6 +41,7 @@ export interface ModuleLoader {
|
||||||
export interface ModuleNode {
|
export interface ModuleNode {
|
||||||
id: string | null;
|
id: string | null;
|
||||||
url: string;
|
url: string;
|
||||||
|
file: string | null;
|
||||||
ssrModule: Record<string, any> | null;
|
ssrModule: Record<string, any> | null;
|
||||||
ssrTransformResult: {
|
ssrTransformResult: {
|
||||||
deps?: string[];
|
deps?: string[];
|
||||||
|
|
|
@ -13,12 +13,16 @@ interface ImportedStyle {
|
||||||
export async function getStylesForURL(
|
export async function getStylesForURL(
|
||||||
filePath: URL,
|
filePath: URL,
|
||||||
loader: ModuleLoader
|
loader: ModuleLoader
|
||||||
): Promise<{ urls: Set<string>; styles: ImportedStyle[] }> {
|
): Promise<{ urls: Set<string>; styles: ImportedStyle[]; crawledFiles: Set<string> }> {
|
||||||
const importedCssUrls = new Set<string>();
|
const importedCssUrls = new Set<string>();
|
||||||
// Map of url to injected style object. Use a `url` key to deduplicate styles
|
// Map of url to injected style object. Use a `url` key to deduplicate styles
|
||||||
const importedStylesMap = new Map<string, ImportedStyle>();
|
const importedStylesMap = new Map<string, ImportedStyle>();
|
||||||
|
const crawledFiles = new Set<string>();
|
||||||
|
|
||||||
for await (const importedModule of crawlGraph(loader, viteID(filePath), true)) {
|
for await (const importedModule of crawlGraph(loader, viteID(filePath), true)) {
|
||||||
|
if (importedModule.file) {
|
||||||
|
crawledFiles.add(importedModule.file);
|
||||||
|
}
|
||||||
if (isBuildableCSSRequest(importedModule.url)) {
|
if (isBuildableCSSRequest(importedModule.url)) {
|
||||||
// In dev, we inline all styles if possible
|
// In dev, we inline all styles if possible
|
||||||
let css = '';
|
let css = '';
|
||||||
|
@ -60,5 +64,6 @@ export async function getStylesForURL(
|
||||||
return {
|
return {
|
||||||
urls: importedCssUrls,
|
urls: importedCssUrls,
|
||||||
styles: [...importedStylesMap.values()],
|
styles: [...importedStylesMap.values()],
|
||||||
|
crawledFiles,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -393,7 +393,7 @@ async function getScriptsAndStyles({ pipeline, filePath }: GetScriptsAndStylesPa
|
||||||
const settings = pipeline.getSettings();
|
const settings = pipeline.getSettings();
|
||||||
const mode = pipeline.getEnvironment().mode;
|
const mode = pipeline.getEnvironment().mode;
|
||||||
// Add hoisted script tags
|
// 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
|
// Inject HMR scripts
|
||||||
if (isPage(filePath, settings) && mode === 'development') {
|
if (isPage(filePath, settings) && mode === 'development') {
|
||||||
|
|
|
@ -9,12 +9,16 @@ export async function getScriptsForURL(
|
||||||
filePath: URL,
|
filePath: URL,
|
||||||
root: URL,
|
root: URL,
|
||||||
loader: ModuleLoader
|
loader: ModuleLoader
|
||||||
): Promise<Set<SSRElement>> {
|
): Promise<{ scripts: Set<SSRElement>; crawledFiles: Set<string> }> {
|
||||||
const elements = new Set<SSRElement>();
|
const elements = new Set<SSRElement>();
|
||||||
|
const crawledFiles = new Set<string>();
|
||||||
const rootID = viteID(filePath);
|
const rootID = viteID(filePath);
|
||||||
const modInfo = loader.getModuleInfo(rootID);
|
const modInfo = loader.getModuleInfo(rootID);
|
||||||
addHoistedScripts(elements, modInfo, root);
|
addHoistedScripts(elements, modInfo, root);
|
||||||
for await (const moduleNode of crawlGraph(loader, rootID, true)) {
|
for await (const moduleNode of crawlGraph(loader, rootID, true)) {
|
||||||
|
if (moduleNode.file) {
|
||||||
|
crawledFiles.add(moduleNode.file);
|
||||||
|
}
|
||||||
const id = moduleNode.id;
|
const id = moduleNode.id;
|
||||||
if (id) {
|
if (id) {
|
||||||
const info = loader.getModuleInfo(id);
|
const info = loader.getModuleInfo(id);
|
||||||
|
@ -22,7 +26,7 @@ export async function getScriptsForURL(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return elements;
|
return { scripts: elements, crawledFiles };
|
||||||
}
|
}
|
||||||
|
|
||||||
function addHoistedScripts(set: Set<SSRElement>, info: ModuleInfo | null, root: URL) {
|
function addHoistedScripts(set: Set<SSRElement>, info: ModuleInfo | null, root: URL) {
|
||||||
|
|
|
@ -935,6 +935,15 @@ importers:
|
||||||
specifier: workspace:*
|
specifier: workspace:*
|
||||||
version: link:../../..
|
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:
|
packages/astro/e2e/fixtures/css:
|
||||||
dependencies:
|
dependencies:
|
||||||
astro:
|
astro:
|
||||||
|
|
Loading…
Reference in a new issue