Present and run client-side TypeScript in Astro/MDX
- There is some typescript that should be presented as a code block in mdx markdown.
- Same code should also be run in the browser on the page.
Solution
-
Add
_app
folder next to the.mdx
file. (_
makes astro ignore for html generation) -
Add
_app/Source.astro
component which reads and presents raw source code:
---
import { Code } from "astro:components";
export interface Props {
path: string;
lang: string;
}
const sources = await import.meta.glob(
[
// all the files we want to present on this page, relative to `./_app/`
"./*.{ts,tsx}",
"../../../../../maybe-a-rust-project-too/src/*.rs",
"../*.mdx", // this page itself even!
"./*.astro", // but doesn't include the own module 🤷😢
],
{
as: "raw",
eager: true,
}
);
const { path, lang } = Astro.props;
---
<>
<Code code={sources[path]} lang={lang as any} />
</>
- Write some cool code, e.g.
_app/app.ts
:
import { isAppEnabled } from "./config.ts";
const app = () => {
setInterval(() => {
document.getElementById("epoch-seconds")!.textContent = `${Math.floor(
Date.now() / 1000
)}`;
}, 1000);
};
if (isAppEnabled) {
app();
}
- Add an astro component which includes the script,
_app/app.astro
:
<script src="./app.ts"></script>
- Import and use
<Source>
component in the.mdx
file (I just did this in steps 3 and 4 🤩):
import Source from "./_app/Source.astro";
...
3. Write some cool code, e.g. `_app/app.ts`:
<Source path="./app.ts" lang="ts" />
4. Add an astro component which includes the script, `_app/app.astro`:
<Source path="./app.astro" lang="astro" />
...
- Import and add the
<app>
component to.mdx
file:
import app from "./_app/app.astro";
...
<app />
...
TypeScript gets compiled, imports resolved, and the script is run in the browser.
- Add element the script can interact with:
epoch seconds: <span id="epoch-seconds"></span>
➡️ epoch seconds:
- 🥳
Caveats?
Probably.