Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(remix-dev): add support for handle, links & loader to mdx #3763

Merged
merged 3 commits into from
Jul 16, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .changeset/tender-impalas-invite.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"remix": patch
"@remix-run/dev": patch
---

Adds support for handle, links, and loader to mdx routes
1 change: 1 addition & 0 deletions contributors.yml
Original file line number Diff line number Diff line change
Expand Up @@ -394,3 +394,4 @@
- zhe
- garand
- kiancross
- tagraves
7 changes: 6 additions & 1 deletion docs/guides/mdx.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ Remix supports using MDX in two ways:

The simplest way to get started with MDX in Remix is to create a route module. Just like `.js` and `.ts` files in your `app/routes` directory, `.mdx` (and `.md`) files will participate in automatic file system based routing.

MDX routes allow you to define both meta and headers as if they were a code based route:
MDX routes allow you to define `handle`, `headers`, `links`, and `meta` as if they were a code based route:

```md
---
Expand All @@ -25,6 +25,9 @@ meta:
description: Isn't this awesome?
headers:
Cache-Control: no-cache
links: [{ rel: "stylesheet", href: "app.css" }]
handle:
someData: "abc"
---

# Hello Content!
Expand All @@ -47,6 +50,8 @@ import SomeComponent from "~/components/some-component";
<SomeComponent {...attributes.componentData} />
```

You can also access your frontmatter fields from a parent component via the `data` field in `useMatches`.

### Example

By creating a `app/routes/posts/first-post.mdx` we can start writing a blog post:
Expand Down
104 changes: 104 additions & 0 deletions integration/mdx-test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
import { test, expect } from "@playwright/test";

import {
createAppFixture,
createFixture,
js,
mdx,
} from "./helpers/create-fixture";
import type { Fixture, AppFixture } from "./helpers/create-fixture";
import { PlaywrightFixture } from "./helpers/playwright-fixture";

test.describe("mdx", () => {
let fixture: Fixture;
let appFixture: AppFixture;

test.beforeAll(async () => {
fixture = await createFixture({
files: {
"app/root.jsx": js`
import { json } from "@remix-run/node";
import { Links, Meta, Outlet, Scripts } from "@remix-run/react";
export default function Root() {
return (
<html lang="en">
<head>
<Meta />
<Links />
</head>
<body>
<Outlet />
<Scripts />
</body>
</html>
);
}
`,

"app/routes/blog.jsx": js`
import { json } from "@remix-run/node";
import { useMatches, Outlet } from "@remix-run/react";
export default function Index() {
const matches = useMatches();
const mdxMatch = matches[matches.length - 1];
return (
<div>
<p id="additionalData">{mdxMatch.data.additionalData === 10 && 'Additional Data: 10'}</p>
<p id="handle">{mdxMatch.handle.someData}</p>
<Outlet />
</div>
);
}
`,

"app/routes/blog/post.mdx": mdx`---
meta:
title: My First Post
description: Isn't this awesome?
headers:
Cache-Control: no-cache
links: [
{ rel: "stylesheet", href: "app.css" }
]
handle:
someData: "abc"
additionalData: 10
---
# This is some markdown!
`,

"app/routes/basic.mdx": mdx`
# This is some basic markdown!
`,
},
});
appFixture = await createAppFixture(fixture);
});

test.afterAll(async () => {
await appFixture.close();
});

test("can render basic markdown", async ({ page }) => {
let app = new PlaywrightFixture(appFixture, page);
await app.goto("/basic");

expect(await app.getHtml()).toMatch("This is some basic markdown!");
});

test("converts the frontmatter to meta, headers, links, handle, and loader", async ({
page,
}) => {
let app = new PlaywrightFixture(appFixture, page);
await app.goto("/blog/post");
expect(await app.getHtml('meta[name="description"]')).toMatch(
"Isn't this awesome?"
);
expect(await app.getHtml("title")).toMatch("My First Post");
expect(await app.getHtml("#additionalData")).toMatch("Additional Data: 10");
expect(await app.getHtml("#handle")).toMatch("abc");
expect(await app.getHtml('link[rel="stylesheet"]')).toMatch("app.css");
});
});
4 changes: 3 additions & 1 deletion packages/remix-dev/compiler/plugins/mdx.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,9 @@ export function mdxPlugin(config: RemixConfig): esbuild.Plugin {
export const filename = ${JSON.stringify(path.basename(args.path))};
export const headers = typeof attributes !== "undefined" && attributes.headers;
export const meta = typeof attributes !== "undefined" && attributes.meta;
export const links = undefined;
export const handle = typeof attributes !== "undefined" && attributes.handle;
export const links = typeof attributes === "undefined" ? undefined : () => attributes.links;
export const loader = typeof attributes === "undefined" ? undefined : () => attributes;
`;

let compiled = await xdm.compile(fileContents, {
Expand Down