Skip to content

Commit

Permalink
MDX support (withastro#3706)
Browse files Browse the repository at this point in the history
* feat: first pass at MDX support

* fix: move built-in JSX renderer to come first

* chore: remove jsx example

* chore: update lockfile

* chore: cleanup example

* fix: missing deps

* refactor: move component render logic to `renderPage`

* chore: update HMR script

* chore: update MDX example

* refactor: prefer unshit

* refactor: remove TODO comment

* fix: remove duplicate identifier

* refactor: cleanup mdx entrypoint

* fix: better html handling

* fix: add tsconfig to mdx package

* chore: update lockfile

* fix: do not sort plugins unless mdx is enabled

* chore: update compiler

* fix(hmr): maybe render head for non-Astro pages

* fix: set initial pageExtensions

* refactor: cleanup addPageExtension

* refactor: remove addPageExtensions from types

* refactor: expose HookParameters type

* fix: only default to astro for MDX

* test: pick up jsx support in test fixtures

* refactor: simplify mdx entrypoint

* test: add basic MDX tests

* test(e2e): add mdx + framework tests

* chore: update lockfile

* test(e2e): fix preact mdx e2e test

* fix(mdx): disable .md support

* test(e2e): fix vue-component test missing mdx

* test(e2e): fix solid component needing import

* fix: allow `client:only="solid"` as an alias to `solid-js`

* chore: move to with-mdx example

* chore: update MDX readme

* chore: update example readme

* chore: bump astro version

* chore: update lockfile

* Update mod.d.ts

* feat: support `export const components` in MDX pages

* chore: update mdx example

* fix: update jsx-runtime with better slot support

* refactor: remove object style support

* chore: cleanup package exports

* chore: add todo comment

* refactor: improve isPage function, move to utils

* refactor: dry up manual HMR updates

* chore: add dev tests for MDX

* chore: prefer set to array

* chore: add changesets

* fix(hmr): flip public/private route

Co-authored-by: Nate Moore <nate@astro.build>
  • Loading branch information
natemoo-re and natemoo-re committed Jun 30, 2022
1 parent cf68c9c commit 3066713
Show file tree
Hide file tree
Showing 35 changed files with 358 additions and 50 deletions.
3 changes: 2 additions & 1 deletion e2e/fixtures/preact-component/astro.config.mjs
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { defineConfig } from 'astro/config';
import preact from '@astrojs/preact';
import mdx from '@astrojs/mdx';

// https://astro.build/config
export default defineConfig({
integrations: [preact()],
integrations: [preact(), mdx()],
});
1 change: 1 addition & 0 deletions e2e/fixtures/preact-component/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
"private": true,
"dependencies": {
"@astrojs/preact": "workspace:*",
"@astrojs/mdx": "workspace:*",
"astro": "workspace:*",
"preact": "^10.7.3"
}
Expand Down
29 changes: 29 additions & 0 deletions e2e/fixtures/preact-component/src/pages/mdx.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@

import Counter from '../components/Counter.jsx';
import PreactComponent from '../components/JSXComponent.jsx';

export const someProps = {
count: 0,
};

<Counter id="server-only" {...someProps}>
# Hello, server!
</Counter>

<Counter id="client-idle" {...someProps} client:idle>
# Hello, client:idle!
</Counter>

<Counter id="client-load" {...someProps} client:load>
# Hello, client:load!
</Counter>

<Counter id="client-visible" {...someProps} client:visible>
# Hello, client:visible!
</Counter>

<Counter id="client-media" {...someProps} client:media="(max-width: 50em)">
# Hello, client:media!
</Counter>

<PreactComponent id="client-only" client:only="preact" />
3 changes: 2 additions & 1 deletion e2e/fixtures/react-component/astro.config.mjs
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { defineConfig } from 'astro/config';
import react from '@astrojs/react';
import mdx from '@astrojs/mdx';

// https://astro.build/config
export default defineConfig({
integrations: [react()],
integrations: [react(), mdx()],
});
1 change: 1 addition & 0 deletions e2e/fixtures/react-component/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
"dependencies": {
"@astrojs/react": "workspace:*",
"astro": "workspace:*",
"@astrojs/mdx": "workspace:*",
"react": "^18.1.0",
"react-dom": "^18.1.0"
}
Expand Down
28 changes: 28 additions & 0 deletions e2e/fixtures/react-component/src/pages/mdx.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import Counter from '../components/Counter.jsx';
import ReactComponent from '../components/JSXComponent.jsx';

export const someProps = {
count: 0,
};

<Counter id="server-only" {...someProps}>
# Hello, server!
</Counter>

<Counter id="client-idle" {...someProps} client:idle>
# Hello, client:idle!
</Counter>

<Counter id="client-load" {...someProps} client:load>
# Hello, client:load!
</Counter>

<Counter id="client-visible" {...someProps} client:visible>
# Hello, client:visible!
</Counter>

<Counter id="client-media" {...someProps} client:media="(max-width: 50em)">
# Hello, client:media!
</Counter>

<ReactComponent id="client-only" client:only="react" />
3 changes: 2 additions & 1 deletion e2e/fixtures/solid-component/astro.config.mjs
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { defineConfig } from 'astro/config';
import mdx from '@astrojs/mdx';
import solid from '@astrojs/solid-js';

// https://astro.build/config
export default defineConfig({
integrations: [solid()],
integrations: [solid(), mdx()],
});
3 changes: 2 additions & 1 deletion e2e/fixtures/solid-component/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@
"private": true,
"dependencies": {
"@astrojs/solid-js": "workspace:*",
"astro": "workspace:*"
"astro": "workspace:*",
"@astrojs/mdx": "workspace:*"
},
"devDependencies": {
"solid-js": "^1.4.3"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import 'solid-js';

export default function SolidComponent({ id }) {
return (
<div id={id}>Framework client:only component</div>
Expand Down
28 changes: 28 additions & 0 deletions e2e/fixtures/solid-component/src/pages/mdx.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import Counter from '../components/Counter.jsx';
import SolidComponent from '../components/SolidComponent.jsx';

export const someProps = {
count: 0,
};

<Counter id="server-only" {...someProps}>
# Hello, server!
</Counter>

<Counter id="client-idle" {...someProps} client:idle>
# Hello, client:idle!
</Counter>

<Counter id="client-load" {...someProps} client:load>
# Hello, client:load!
</Counter>

<Counter id="client-visible" {...someProps} client:visible>
# Hello, client:visible!
</Counter>

<Counter id="client-media" {...someProps} client:media="(max-width: 50em)">
# Hello, client:media!
</Counter>

<SolidComponent id="client-only" client:only="solid" />
3 changes: 2 additions & 1 deletion e2e/fixtures/svelte-component/astro.config.mjs
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { defineConfig } from 'astro/config';
import svelte from '@astrojs/svelte';
import mdx from '@astrojs/mdx';

// https://astro.build/config
export default defineConfig({
integrations: [svelte()],
integrations: [svelte(), mdx()],
});
1 change: 1 addition & 0 deletions e2e/fixtures/svelte-component/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
"dependencies": {
"@astrojs/svelte": "workspace:*",
"astro": "workspace:*",
"@astrojs/mdx": "workspace:*",
"svelte": "^3.48.0"
}
}
28 changes: 28 additions & 0 deletions e2e/fixtures/svelte-component/src/pages/mdx.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import Counter from '../components/Counter.svelte';
import SvelteComponent from '../components/SvelteComponent.svelte';

export const someProps = {
count: 0,
};

<Counter id="server-only" {...someProps}>
# Hello, server!
</Counter>

<Counter id="client-idle" {...someProps} client:idle>
# Hello, client:idle!
</Counter>

<Counter id="client-load" {...someProps} client:load>
# Hello, client:load!
</Counter>

<Counter id="client-visible" {...someProps} client:visible>
# Hello, client:visible!
</Counter>

<Counter id="client-media" {...someProps} client:media="(max-width: 50em)">
# Hello, client:media!
</Counter>

<SvelteComponent id="client-only" client:only="svelte" />
14 changes: 9 additions & 5 deletions e2e/fixtures/vue-component/astro.config.mjs
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
import { defineConfig } from 'astro/config';
import vue from '@astrojs/vue';
import mdx from '@astrojs/mdx';

// https://astro.build/config
export default defineConfig({
integrations: [vue({
template: {
compilerOptions: {
isCustomElement: tag => tag.includes('my-button')
integrations: [
mdx(),
vue({
template: {
compilerOptions: {
isCustomElement: tag => tag.includes('my-button')
}
}
}
})],
)],
});
3 changes: 2 additions & 1 deletion e2e/fixtures/vue-component/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
"private": true,
"dependencies": {
"@astrojs/vue": "workspace:*",
"astro": "workspace:*"
"astro": "workspace:*",
"@astrojs/mdx": "workspace:*"
}
}
28 changes: 28 additions & 0 deletions e2e/fixtures/vue-component/src/pages/mdx.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import Counter from '../components/Counter.vue';
import VueComponent from '../components/VueComponent.vue';

export const someProps = {
count: 0,
};

<Counter id="server-only" {...someProps}>
# Hello, server!
</Counter>

<Counter id="client-idle" {...someProps} client:idle>
# Hello, client:idle!
</Counter>

<Counter id="client-load" {...someProps} client:load>
# Hello, client:load!
</Counter>

<Counter id="client-visible" {...someProps} client:visible>
# Hello, client:visible!
</Counter>

<Counter id="client-media" {...someProps} client:media="(max-width: 50em)">
# Hello, client:media!
</Counter>

<VueComponent id="client-only" client:only="vue" />
8 changes: 8 additions & 0 deletions e2e/preact-component.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,11 @@ test.describe('Preact components in Markdown files', () => {
componentFilePath: './src/components/JSXComponent.jsx',
});
});

test.describe('Preact components in MDX files', () => {
createTests({
pageUrl: '/mdx/',
pageSourceFilePath: './src/pages/mdx.mdx',
componentFilePath: './src/components/JSXComponent.jsx',
});
});
8 changes: 8 additions & 0 deletions e2e/react-component.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,11 @@ test.describe('React components in Markdown files', () => {
componentFilePath: './src/components/JSXComponent.jsx',
});
});

test.describe('React components in MDX files', () => {
createTests({
pageUrl: '/mdx/',
pageSourceFilePath: './src/pages/mdx.mdx',
componentFilePath: './src/components/JSXComponent.jsx',
});
});
8 changes: 8 additions & 0 deletions e2e/solid-component.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,11 @@ test.describe('Solid components in Markdown files', () => {
componentFilePath: './src/components/SolidComponent.jsx',
});
});

test.describe('Solid components in MDX files', () => {
createTests({
pageUrl: '/mdx/',
pageSourceFilePath: './src/pages/mdx.mdx',
componentFilePath: './src/components/SolidComponent.jsx',
});
});
9 changes: 9 additions & 0 deletions e2e/svelte-component.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,12 @@ test.describe('Svelte components in Markdown files', () => {
counterCssFilePath: './src/components/Counter.svelte',
});
});

test.describe('Svelte components in MDX files', () => {
createTests({
pageUrl: '/mdx/',
pageSourceFilePath: './src/pages/mdx.mdx',
componentFilePath: './src/components/SvelteComponent.svelte',
counterCssFilePath: './src/components/Counter.svelte',
});
});
9 changes: 9 additions & 0 deletions e2e/vue-component.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,12 @@ test.describe('Vue components in Markdown files', () => {
counterCssFilePath: './src/components/Counter.vue',
});
});

test.describe('Vue components in MDX files', () => {
createTests({
pageUrl: '/mdx/',
pageSourceFilePath: './src/pages/mdx.mdx',
componentFilePath: './src/components/VueComponent.vue',
counterCssFilePath: './src/components/Counter.vue',
});
});
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@
"test:e2e:match": "playwright test -g"
},
"dependencies": {
"@astrojs/compiler": "^0.17.0",
"@astrojs/compiler": "^0.17.1",
"@astrojs/language-server": "^0.13.4",
"@astrojs/markdown-remark": "^0.11.3",
"@astrojs/prism": "0.4.1",
Expand Down
6 changes: 5 additions & 1 deletion src/@types/astro.ts
Original file line number Diff line number Diff line change
Expand Up @@ -939,6 +939,10 @@ export interface SSRLoadedRenderer extends AstroRenderer {
};
}

export type HookParameters<Hook extends keyof AstroIntegration['hooks'], Fn = AstroIntegration['hooks'][Hook]> = Fn extends (...args: any) => any
? Parameters<Fn>[0]
: never;

export interface AstroIntegration {
/** The name of the integration. */
name: string;
Expand All @@ -955,7 +959,7 @@ export interface AstroIntegration {
// This may require some refactoring of `scripts`, `styles`, and `links` into something
// more generalized. Consider the SSR use-case as well.
// injectElement: (stage: vite.HtmlTagDescriptor, element: string) => void;
}) => void;
}) => void | Promise<void>;
'astro:config:done'?: (options: {
config: AstroConfig;
setAdapter: (adapter: AstroAdapter) => void;
Expand Down
7 changes: 3 additions & 4 deletions src/core/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -339,20 +339,19 @@ export async function validateConfig(
const result = {
...(await AstroConfigRelativeSchema.parseAsync(userConfig)),
_ctx: {
pageExtensions: [],
pageExtensions: ['.astro', '.md'],
scripts: [],
renderers: [],
injectedRoutes: [],
adapter: undefined,
},
};
if (
// TODO: expose @astrojs/mdx package
result.integrations.find((integration) => integration.name === '@astrojs/mdx')
) {
// Enable default JSX integration
// Enable default JSX integration. It needs to come first, so unshift rather than push!
const { default: jsxRenderer } = await import('../jsx/renderer.js');
(result._ctx.renderers as any[]).push(jsxRenderer);
(result._ctx.renderers as any[]).unshift(jsxRenderer);
}

// Final-Pass Validation (perform checks that require the full config object)
Expand Down
17 changes: 17 additions & 0 deletions src/core/create-vite.ts
Original file line number Diff line number Diff line change
Expand Up @@ -128,9 +128,26 @@ export async function createVite(
let result = commonConfig;
result = vite.mergeConfig(result, astroConfig.vite || {});
result = vite.mergeConfig(result, commandConfig);
sortPlugins(result);
return result;
}

function getPluginName(plugin: vite.PluginOption) {
if (plugin && typeof plugin === 'object' && !Array.isArray(plugin)) {
return plugin.name;
}
}

function sortPlugins(result: ViteConfigWithSSR) {
// HACK: move mdxPlugin to top because it needs to run before internal JSX plugin
const mdxPluginIndex = result.plugins?.findIndex(plugin => getPluginName(plugin) === '@mdx-js/rollup') ?? -1;
if (mdxPluginIndex === -1) return;
const jsxPluginIndex = result.plugins?.findIndex(plugin => getPluginName(plugin) === 'astro:jsx') ?? -1;
const mdxPlugin = result.plugins?.[mdxPluginIndex];
result.plugins?.splice(mdxPluginIndex, 1);
result.plugins?.splice(jsxPluginIndex, 0, mdxPlugin);
}

// Scans `projectRoot` for third-party Astro packages that could export an `.astro` file
// `.astro` files need to be built by Vite, so these should use `noExternal`
async function getAstroPackages({ root }: AstroConfig): Promise<string[]> {
Expand Down
Loading

0 comments on commit 3066713

Please sign in to comment.