bluwy / whyframe

Develop components in isolation with just an iframe

Home Page:https://whyframe.dev

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Using whyframe in Astro with Markdown & MDX files

paularmstrong opened this issue · comments

Use case: I don't want to use Storybook, but instead Astro, like storybook. I have a bunch of MDX "stories" that have components in them. However, I'm going to need them in iframes, so Whyframe seems like a great choice.

Problem:

Nothing gets written to the iframe

Is this just not possible at all? Am I doing something horribly wrong?

Hey! Yeah this is not supported currently. The Astro integration only works in .astro files. For .md and .mdx, they need a remark and rehype plugin which I've not written one yet. (And I haven't look into whether it's possible)

But you can workaround this by writing the examples in components instead, and use them in MDX. Here's how I do it for the docs currently:

# @whyframe/jsx
<SolidDemo />
<PreactDemo />
<ReactDemo />
`@whyframe/jsx` exports a [Vite](https://vitejs.dev) plugin, while `@whyframe/jsx/loader` exports a [Webpack](https://webpack.js.org) loader. They share the same options below, except `include` and `exclude` being Vite specific:

Depending on the framework you use for the components, you can install the respective whyframe plugin to work too. Here's the doc's vite config:

vite: {
plugins: [
cloudflareRedirect(),
inspect(),
whyframe({
defaultSrc: '/frames/default'
}),
whyframeAstro({
defaultFramework: 'svelte'
}),
whyframeSvelte(),
whyframeVue(),
whyframeJsx()
],

It's a bit cumbersome, but I believe this should be supported one day so I'll leave this open.

What about being able to have a JSX component that then has the iframe in it? This would be a nice workaround:

import { Example } from '@/components/Example';

# Button

<Example>
  <Button />
</Example>
import { useId } from 'react';

type Props = {
  children: React.ReactNode;
};

export function Example(props: Props) {
  return (
    <iframe title="Example" data-why>
      <div>{props.children}</div>
    </iframe>
  );
}

The resulting built component seems to have args/props stripped:

import __vite__cjsImport0_react from "/node_modules/.vite/deps/react.js?v=8dac57f3"; const useId = __vite__cjsImport0_react["useId"];
import __vite__cjsImport1_react_jsxRuntime from "/node_modules/.vite/deps/react_jsx-runtime.js?v=8dac57f3"; const _jsx = __vite__cjsImport1_react_jsxRuntime["jsx"];
import __vite__cjsImport2_react_jsxRuntime from "/node_modules/.vite/deps/react_jsx-runtime.js?v=8dac57f3"; const _Fragment = __vite__cjsImport2_react_jsxRuntime["Fragment"];
function Example() {
}
export function WhyframeApp() {
  const uid = useId();
  return /* @__PURE__ */ _jsx(_Fragment, {
    children: /* @__PURE__ */ _jsx("div", {
      children: props.children
    })
  });
}

That would be similar to the abstracting components concept, but unfortunately it needs a bit more work too. Since we need to extract the <Button /> inside the <Example> as a separate virtual module, which the iframe would load that module.

Imperatively passing it to the component can't guarantee the isolation that we want, since it's created within the context of the parent in the first place, so it needs a fresh virtual module. (If that makes sense 😅)

I actually find this quite interesting to tackle, maybe I'll take a look at it this weekend. Having preliminary support for MDX would be great.

Really cool, let me know if I can help out in some way. Might take a bit to get spun up on the code, but I'm happy to assist if you've got ideas to get started.

I made a prototype at https://github.com/bluwy/whyframe/tree/astro-mdx, which is a separate Vite plugin at @astrojs/astro-mdx that does very similar things to @astrojs/astro. Unfortunately, it looks like Astro's MDX completely bypasses the Vite plugin's code transformation here, so it's not working now.

I might look into fixing the core issue soon if that's possible, but another idea is to introduce a remark/rehype plugin through an Astro integration, that internally creates a Vite plugin to intercept the Vite pipeline to make transformation work. But I think this could be the last resort as it's a bit clunky to have some APIs be a Vite plugin, and an Astro integration. Plus a remark/rehype plugin that can access the Vite pipeline is also fragile.

So there's not anything you can use for now, until I find some time to continue on it 😬 If you're interested in continuing the effort, I'm happy to discuss it on Discord too if it's more convenient.