-
-
Notifications
You must be signed in to change notification settings - Fork 16
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
RFC: Microsite v2.0.0 #163
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,93 @@ | ||
# RFC: Microsite v2.0.0 | ||
|
||
Microsite v1.0.0 was released in December 2020. Conceptually, it was my first attempt to unify several strands of work on "Partial Hydration" into a single, well-optimized tool. | ||
|
||
In March 2021, I was hired to work on [Astro](https://github.com/snowpackjs/astro), a framework-agnostic tool that built on many of the ideas I was exploring with Microsite. | ||
|
||
I've learned so much while working on Astro. Now seems like as good a time as any to apply those lessons to Microsite. | ||
|
||
## JSX Compiler | ||
|
||
The core of Microsite v2.0.0 would be built around a custom JSX compiler. Conceptually, this would be similar to [Solid](https://www.solidjs.com/)'s approach with [`dom-expressions`](http://npm.im/dom-expressions)—leverage JSX/TSX as the defacto standard for authoring Markup in JavaScript, but compile JSX to a non-standard, highly optimized output. | ||
|
||
Instead of the `withHydrate` HOC, this compiler would be able to leverage a hydration directive directly inside of JSX. The compiler output would be highly optimized for SSR speed, relying on strings rather than a server-side VDOM. | ||
|
||
It's possible that hydration directives wouldn't even be necessary if we can detect whether a component uses `on*` event listeners or `use*` hooks. In that case, I'd default to `visible` hydration. | ||
|
||
```tsx | ||
// Input | ||
/* imports */ | ||
|
||
const Input = () => { | ||
return ( | ||
<> | ||
<Head> | ||
<seo.title>Microsite v2.0.0</seo.title> | ||
|
||
<link rel="icon" type="image/svg+xml" href="/favicon.svg" /> | ||
<link rel="alternate icon" href="/favicon.ico" /> | ||
</Head> | ||
|
||
<main> | ||
<div class="stack"> | ||
<h1>Hello world!</h1> | ||
</div> | ||
|
||
<Counter @hydrate={{ method: 'visible' }} /> | ||
</main> | ||
</> | ||
) | ||
} | ||
|
||
// Output | ||
import { ssr, component, escape } from 'microsite/internal'; | ||
/* imports */ | ||
|
||
const Output = () => { | ||
return ssr` | ||
${component(Head, null, ssr` | ||
${component(seo.title, null, ssr`Microsite v2.0.0`)} | ||
<link rel="icon" type="image/svg+xml" href="/favicon.svg" /> | ||
<link rel="alternate icon" href="/favicon.ico" /> | ||
`)} | ||
<main> | ||
<div class="stack"> | ||
<h1>Hello world!</h1> | ||
</div> | ||
|
||
${component(Counter, { '@hydrate': { method: 'visible' } })} | ||
</main> | ||
` | ||
} | ||
``` | ||
|
||
## Framework Agnostic | ||
|
||
Switching the majority of JSX compilation to a custom compiler would also open the door for other JSX-based frameworks. Preact would be the primary one, but Solid would be supported as well. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I love that this is an explicit goal! Because they’re both quite different in practice, I’d like to consider them both co-equal “primary” targets. This would help to ensure design and APIs are flexible enough to achieve this goal without major discrepancies. |
||
|
||
> I'm not interested in supporting React unless it is aliased to `preact/compat`. | ||
|
||
### Open Questions | ||
- Framework detection | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In my opinion, Microsite may want to consider sidestepping this issue entirely, by limiting projects to one framework per package. Multi-framework support in Astro is neat (in the “wow it’s wild you can do that” sense), but I suspect it’s less of a must-have than generalized JSX partial hydration. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That is a very good point and simplifies things a lot! |
||
- Framework JSX compilation | ||
|
||
## Custom Rendering | ||
|
||
Microsite would have its own internal `renderToString` that resolves everything to a string of HTML. | ||
`component` would be able to render the component instance to a string of HTML and generate the final hydration script. | ||
|
||
### Streaming Rendering? | ||
|
||
We could potentially return a generator function to enable streaming responses by exposing something like `renderToStream`. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is another case where we’re probably going to want to anticipate |
||
|
||
## `esbuild` | ||
|
||
`esbuild` is pretty amazing, and there's also a Deno module. Assuming we could build an HMR engine for `esbuild`, do we really need all the extra weight that Snowpack/Vite bring along? | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 👏👏👏 And while we’re on that topic, I think it might be good to be very explicit from the start about how v2 lives alongside the rest of the esbuild ecosystem. Which is to say, I think Microsite v2 should be designed to be a pipeable esbuild plugin, rather than a build step outside of or adjacent to esbuild. Longer term, I’d like to also consider providing compiler APIs for additional AST transforms. This is currently a huge deoptimization/pain point with esbuild’s design. If you agree with this goal, I think it would be best to be ahead of the curve, by designing AST and transform as separate plugins even if they’re exposed as a single plugin by default. |
||
|
||
## Deno | ||
|
||
I really like [Deno](https://deno.land/) and Microsite should run on Deno. We probably _shouldn't_ drop Node support, but... | ||
|
||
If the compiler was written in Rust, we could ship `microsite` as a stand-alone executable that uses [`deno_core`](https://docs.rs/deno_core/0.98.0/deno_core/) under the hood. The idea is extremely appealing! | ||
|
||
Using `deno deploy` with no build step sounds amazing. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 👏👏👏 no further comment on this! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not to overly focus on implementation details right away, but I like the use of
ssr
as a tagged literal. It suggests, among other things, a potential solution to one of Astro’s rough edges: rendered HTML and raw strings being the same type at build-render time.That said, I’m wondering about potential issues supporting that in a children position. Does
ssr
also return acomponent
?I think
component
used this way may be challenging when supporting Solid, where its equivalent iscreateComponent(() => Component, props)
. If Microsite is going to have something similar, I think following Solid’s lead here is probably the most flexible option.Alternatively, this might be something better left to per-JSX library configuration, not dissimilar to various pragmas.