Present and run client-side TypeScript in Astro/MDX

Solution

  1. Add _app folder next to the .mdx file. (_ makes astro ignore for html generation)

  2. 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} />
</>
  1. 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();
}
  1. Add an astro component which includes the script, _app/app.astro:
<script src="./app.ts"></script>
  1. 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" />

...
  1. 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.

  1. Add element the script can interact with:
epoch seconds: <span id="epoch-seconds"></span>

➡️ epoch seconds:

  1. 🥳

Caveats?

Probably.

Open on GitHub