summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Ben Holmes <hey@bholmes.dev> 2022-05-20 15:03:21 -0400
committerGravatar GitHub <noreply@github.com> 2022-05-20 15:03:21 -0400
commitfb5572bebd211786bc8a08eb073bc08af97287bc (patch)
treeec53b4f2b3a6df1a43dcb22b3d65800207144134
parent195f3f7eb72033fe827f82a2d579d1c9ca7b3d9d (diff)
downloadastro-fb5572bebd211786bc8a08eb073bc08af97287bc.tar.gz
astro-fb5572bebd211786bc8a08eb073bc08af97287bc.tar.zst
astro-fb5572bebd211786bc8a08eb073bc08af97287bc.zip
Fix: components in imported markdown (#3398)
* test: add with-components to astro-markdown fixture * fix: markdown pathname with /@fs prefix * feat: add loadMetadata helper for md * feat: fix components in Astro.glob results! * fix: md import path * Revert "feat: add loadMetadata helper for md" This reverts commit 76cf96f4be3d0e19589f84025c0131352d0b6cc8. * fix: add back $$loadMetadata helper * feat: add second comp framework to md test * chore: core/render/dev lint * chore: changeset * fix: short circuit if mod doesn't have metadata * fix: skip mod graph preloading in dev * refactor: make md metadata check recursive * refactor: extract metadata helper to util * fix: remove unecessary mod graph query * fix: move md import flag to util for deno bundling issue * fix: remove 'dev' mode from test utils build * feat: add global hashset for seen metadata * refactor: flip Promise.all to for await for perf! * Revert bc I was wrong! "refactor: flip Promise.all to for await for perf!" This reverts commit da8a6873f5cd57f6bb78302c20e4a2b4a578067b.
-rw-r--r--.changeset/dull-monkeys-grab.md5
-rw-r--r--packages/astro/src/core/render/dev/index.ts21
-rw-r--r--packages/astro/src/core/render/util.ts52
-rw-r--r--packages/astro/src/vite-plugin-markdown/index.ts10
-rw-r--r--packages/astro/test/fixtures/astro-markdown/astro.config.mjs3
-rw-r--r--packages/astro/test/fixtures/astro-markdown/package.json1
-rw-r--r--packages/astro/test/fixtures/astro-markdown/src/components/SvelteButton.svelte11
-rw-r--r--packages/astro/test/fixtures/astro-markdown/src/imported-md/plain.md6
-rw-r--r--packages/astro/test/fixtures/astro-markdown/src/imported-md/with-components.md17
-rw-r--r--packages/astro/test/fixtures/astro-markdown/src/pages/imported-md/with-components.astro9
-rw-r--r--packages/astro/test/test-utils.js2
-rw-r--r--pnpm-lock.yaml2
12 files changed, 128 insertions, 11 deletions
diff --git a/.changeset/dull-monkeys-grab.md b/.changeset/dull-monkeys-grab.md
new file mode 100644
index 000000000..c7ba4dcdb
--- /dev/null
+++ b/.changeset/dull-monkeys-grab.md
@@ -0,0 +1,5 @@
+---
+'astro': patch
+---
+
+Fix component usage in imported markdown files
diff --git a/packages/astro/src/core/render/dev/index.ts b/packages/astro/src/core/render/dev/index.ts
index e71284085..1522086f2 100644
--- a/packages/astro/src/core/render/dev/index.ts
+++ b/packages/astro/src/core/render/dev/index.ts
@@ -1,5 +1,5 @@
import { fileURLToPath } from 'url';
-import type * as vite from 'vite';
+import type { HtmlTagDescriptor, ViteDevServer } from 'vite';
import type {
AstroConfig,
AstroRenderer,
@@ -15,9 +15,9 @@ import { prependForwardSlash } from '../../../core/path.js';
import { RouteCache } from '../route-cache.js';
import { createModuleScriptElementWithSrcSet } from '../ssr-element.js';
import { getStylesForURL } from './css.js';
-import { getHmrScript } from './hmr.js';
import { injectTags } from './html.js';
import { isBuildingToSSR } from '../../util.js';
+import { collectMdMetadata } from '../util.js';
export interface SSROptions {
/** an instance of the AstroConfig */
@@ -37,7 +37,7 @@ export interface SSROptions {
/** pass in route cache because SSR can’t manage cache-busting */
routeCache: RouteCache;
/** Vite instance */
- viteServer: vite.ViteDevServer;
+ viteServer: ViteDevServer;
/** Request */
request: Request;
}
@@ -51,7 +51,7 @@ export type RenderResponse =
const svelteStylesRE = /svelte\?svelte&type=style/;
async function loadRenderer(
- viteServer: vite.ViteDevServer,
+ viteServer: ViteDevServer,
renderer: AstroRenderer
): Promise<SSRLoadedRenderer> {
// Vite modules can be out-of-date when using an un-resolved url
@@ -65,7 +65,7 @@ async function loadRenderer(
}
export async function loadRenderers(
- viteServer: vite.ViteDevServer,
+ viteServer: ViteDevServer,
astroConfig: AstroConfig
): Promise<SSRLoadedRenderer[]> {
return Promise.all(astroConfig._ctx.renderers.map((r) => loadRenderer(viteServer, r)));
@@ -80,6 +80,15 @@ export async function preload({
const renderers = await loadRenderers(viteServer, astroConfig);
// Load the module from the Vite SSR Runtime.
const mod = (await viteServer.ssrLoadModule(fileURLToPath(filePath))) as ComponentInstance;
+ if (viteServer.config.mode === 'development' || !mod?.$$metadata) {
+ return [renderers, mod];
+ }
+
+ // append all nested markdown metadata to mod.$$metadata
+ const modGraph = await viteServer.moduleGraph.getModuleByUrl(fileURLToPath(filePath));
+ if (modGraph) {
+ await collectMdMetadata(mod.$$metadata, modGraph, viteServer);
+ }
return [renderers, mod];
}
@@ -179,7 +188,7 @@ export async function render(
}
// inject tags
- const tags: vite.HtmlTagDescriptor[] = [];
+ const tags: HtmlTagDescriptor[] = [];
// add injected tags
let html = injectTags(content.html, tags);
diff --git a/packages/astro/src/core/render/util.ts b/packages/astro/src/core/render/util.ts
index 6ef412a9c..da34dfd3c 100644
--- a/packages/astro/src/core/render/util.ts
+++ b/packages/astro/src/core/render/util.ts
@@ -1,4 +1,6 @@
import npath from 'path-browserify';
+import type { ModuleNode, ViteDevServer } from 'vite';
+import type { Metadata } from '../../runtime/server/metadata.js';
/** Normalize URL to its canonical form */
export function createCanonicalURL(url: string, base?: string): URL {
@@ -30,9 +32,59 @@ export const STYLE_EXTENSIONS = new Set([
'.less',
]);
+// duplicate const from vite-plugin-markdown
+// can't import directly due to Deno bundling issue
+// (node fs import failing during prod builds)
+const MARKDOWN_IMPORT_FLAG = '?mdImport';
+
const cssRe = new RegExp(
`\\.(${Array.from(STYLE_EXTENSIONS)
.map((s) => s.slice(1))
.join('|')})($|\\?)`
);
export const isCSSRequest = (request: string): boolean => cssRe.test(request);
+
+// During prod builds, some modules have dependencies we should preload by hand
+// Ex. markdown files imported asynchronously or via Astro.glob(...)
+// This calls each md file's $$loadMetadata to discover those dependencies
+// and writes all results to the input `metadata` object
+const seenMdMetadata = new Set<string>();
+export async function collectMdMetadata(
+ metadata: Metadata,
+ modGraph: ModuleNode,
+ viteServer: ViteDevServer,
+) {
+ const importedModules = [...(modGraph?.importedModules ?? [])];
+ await Promise.all(
+ importedModules.map(async (importedModule) => {
+ // recursively check for importedModules
+ if (!importedModule.id || seenMdMetadata.has(importedModule.id)) return;
+
+ seenMdMetadata.add(importedModule.id);
+ await collectMdMetadata(metadata, importedModule, viteServer);
+
+ if (!importedModule?.id?.endsWith(MARKDOWN_IMPORT_FLAG)) return;
+
+ const mdSSRMod = await viteServer.ssrLoadModule(importedModule.id);
+ const mdMetadata = (await mdSSRMod.$$loadMetadata?.()) as Metadata;
+ if (!mdMetadata) return;
+
+ for (let mdMod of mdMetadata.modules) {
+ mdMod.specifier = mdMetadata.resolvePath(mdMod.specifier);
+ metadata.modules.push(mdMod);
+ }
+ for (let mdHoisted of mdMetadata.hoisted) {
+ metadata.hoisted.push(mdHoisted);
+ }
+ for (let mdHydrated of mdMetadata.hydratedComponents) {
+ metadata.hydratedComponents.push(mdHydrated);
+ }
+ for (let mdClientOnly of mdMetadata.clientOnlyComponents) {
+ metadata.clientOnlyComponents.push(mdClientOnly);
+ }
+ for (let mdHydrationDirective of mdMetadata.hydrationDirectives) {
+ metadata.hydrationDirectives.add(mdHydrationDirective);
+ }
+ })
+ );
+}
diff --git a/packages/astro/src/vite-plugin-markdown/index.ts b/packages/astro/src/vite-plugin-markdown/index.ts
index 9c7577fc3..d75351ca3 100644
--- a/packages/astro/src/vite-plugin-markdown/index.ts
+++ b/packages/astro/src/vite-plugin-markdown/index.ts
@@ -89,6 +89,10 @@ export default function markdown({ config }: AstroPluginOptions): Plugin {
export const frontmatter = ${JSON.stringify(frontmatter)};
export const file = ${JSON.stringify(fileId)};
export const url = ${JSON.stringify(fileUrl)};
+
+ export function $$loadMetadata() {
+ return load().then((m) => m.$$metadata)
+ }
// Deferred
export default async function load() {
@@ -109,10 +113,10 @@ export default function markdown({ config }: AstroPluginOptions): Plugin {
// directly as a page in Vite, or it was a deferred render from a JS module.
// This returns the compiled markdown -> astro component that renders to HTML.
if (id.endsWith('.md')) {
- const source = await fs.promises.readFile(id, 'utf8');
+ const filename = normalizeFilename(id);
+ const source = await fs.promises.readFile(filename, 'utf8');
const renderOpts = config.markdown;
- const filename = normalizeFilename(id);
const fileUrl = new URL(`file://${filename}`);
const isPage = fileUrl.pathname.startsWith(resolvePages(config).pathname);
const hasInjectedScript = isPage && config._ctx.scripts.some((s) => s.stage === 'page-ssr');
@@ -142,7 +146,7 @@ ${setup}`.trim();
// Transform from `.astro` to valid `.ts`
let { code: tsResult } = await transform(astroResult, {
- pathname: fileUrl.pathname.slice(config.root.pathname.length - 1),
+ pathname: '/@fs' + prependForwardSlash(fileUrl.pathname),
projectRoot: config.root.toString(),
site: config.site ? new URL(config.base, config.site).toString() : undefined,
sourcefile: id,
diff --git a/packages/astro/test/fixtures/astro-markdown/astro.config.mjs b/packages/astro/test/fixtures/astro-markdown/astro.config.mjs
index 08916b1fe..e1986197c 100644
--- a/packages/astro/test/fixtures/astro-markdown/astro.config.mjs
+++ b/packages/astro/test/fixtures/astro-markdown/astro.config.mjs
@@ -1,7 +1,8 @@
import { defineConfig } from 'astro/config';
import preact from '@astrojs/preact';
+import svelte from "@astrojs/svelte";
// https://astro.build/config
export default defineConfig({
- integrations: [preact()],
+ integrations: [preact(), svelte()]
});
diff --git a/packages/astro/test/fixtures/astro-markdown/package.json b/packages/astro/test/fixtures/astro-markdown/package.json
index bf24faecd..e5a54cf7c 100644
--- a/packages/astro/test/fixtures/astro-markdown/package.json
+++ b/packages/astro/test/fixtures/astro-markdown/package.json
@@ -4,6 +4,7 @@
"private": true,
"dependencies": {
"@astrojs/preact": "workspace:*",
+ "@astrojs/svelte": "workspace:*",
"astro": "workspace:*"
}
}
diff --git a/packages/astro/test/fixtures/astro-markdown/src/components/SvelteButton.svelte b/packages/astro/test/fixtures/astro-markdown/src/components/SvelteButton.svelte
new file mode 100644
index 000000000..74f3ff6a9
--- /dev/null
+++ b/packages/astro/test/fixtures/astro-markdown/src/components/SvelteButton.svelte
@@ -0,0 +1,11 @@
+<script>
+ let cool = false
+</script>
+
+<button on:click={() => cool = true}>This is cool right? {cool}</button>
+
+<style>
+ button {
+ background: green;
+ }
+</style>
diff --git a/packages/astro/test/fixtures/astro-markdown/src/imported-md/plain.md b/packages/astro/test/fixtures/astro-markdown/src/imported-md/plain.md
new file mode 100644
index 000000000..d548b3356
--- /dev/null
+++ b/packages/astro/test/fixtures/astro-markdown/src/imported-md/plain.md
@@ -0,0 +1,6 @@
+---
+---
+
+## Plain jane
+
+I am plain markdown!
diff --git a/packages/astro/test/fixtures/astro-markdown/src/imported-md/with-components.md b/packages/astro/test/fixtures/astro-markdown/src/imported-md/with-components.md
new file mode 100644
index 000000000..dc6c23bf9
--- /dev/null
+++ b/packages/astro/test/fixtures/astro-markdown/src/imported-md/with-components.md
@@ -0,0 +1,17 @@
+---
+setup: |
+ import Counter from '../components/Counter.jsx'
+ import Hello from '../components/Hello.jsx'
+ import SvelteButton from '../components/SvelteButton.svelte'
+---
+
+## With components
+
+### Non-hydrated
+
+<Hello name="Astro Naut" />
+
+### Hydrated
+
+<Counter client:load />
+<SvelteButton client:load />
diff --git a/packages/astro/test/fixtures/astro-markdown/src/pages/imported-md/with-components.astro b/packages/astro/test/fixtures/astro-markdown/src/pages/imported-md/with-components.astro
new file mode 100644
index 000000000..97cd8f211
--- /dev/null
+++ b/packages/astro/test/fixtures/astro-markdown/src/pages/imported-md/with-components.astro
@@ -0,0 +1,9 @@
+---
+import Layout from '../../layouts/content.astro'
+
+const posts = await Astro.glob('../../imported-md/*.md')
+---
+
+<Layout>
+ {posts.map(({ Content }) => <Content />)}
+</Layout>
diff --git a/packages/astro/test/test-utils.js b/packages/astro/test/test-utils.js
index 8b4e9d310..bbdd86b57 100644
--- a/packages/astro/test/test-utils.js
+++ b/packages/astro/test/test-utils.js
@@ -118,7 +118,7 @@ export async function loadFixture(inlineConfig) {
let devServer;
return {
- build: (opts = {}) => build(config, { mode: 'development', logging, telemetry, ...opts }),
+ build: (opts = {}) => build(config, { logging, telemetry, ...opts }),
startDevServer: async (opts = {}) => {
devServer = await dev(config, { logging, telemetry, ...opts });
config.server.port = devServer.address.port; // update port
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index ea9a4cc41..df5a78140 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -854,9 +854,11 @@ importers:
packages/astro/test/fixtures/astro-markdown:
specifiers:
'@astrojs/preact': workspace:*
+ '@astrojs/svelte': workspace:*
astro: workspace:*
dependencies:
'@astrojs/preact': link:../../../../integrations/preact
+ '@astrojs/svelte': link:../../../../integrations/svelte
astro: link:../../..
packages/astro/test/fixtures/astro-markdown-css: