-
-
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?
Conversation
This pull request is being automatically deployed with Vercel (learn more). microsite – ./site🔍 Inspect: https://vercel.com/nmoo/microsite/8ZkCEcuAWGKZoapm4f1yAPLUdJQX [Deployment for abbcf5d canceled] microsite-examples – ./🔍 Inspect: https://vercel.com/nmoo/microsite-examples/2pBhag436EH9KFg9MCLCQ4tZkvXH |
|
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.
Besides the inline comments, a few general thoughts. Some of this is probably stretch goals or out of scope for 2.0.0, but wanna add stuff that we’ve already discussed in other places.
Static data/rendering
One current pain point with v1 is that data collection for static props evaluates the entire dependency tree for each page. Rendering does as well. This can be quite expensive, and can cause additional pain when combined with tools designed to be built with other tooling.
I’d like to consider an additional compiler goal to isolate those code paths that must be run at build time and treeshake as much else out as possible before evaluating anything.
Virtual modules
We’ve discussed this on Discord, but I think there’s an opportunity to provide a generalized solution to dynamic virtual modules and user-customizable code splitting points. This is essentially the inverse of the previous section. What this would probably look like is an interface which, if evaluated at build time and returns something like importChunk(\
data:…`)`, it can be compiled to a dynamic import with a static chunk reference so esbuild/plugins can split it as they see fit.
I’m sure I’ll have more, but wanted to get these up while this is fresh!
|
||
const Output = () => { | ||
return ssr` | ||
${component(Head, null, ssr` |
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.
|
||
## 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 comment
The 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 comment
The 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 comment
The reason will be displayed to describe this comment to others. Learn more.
That is a very good point and simplifies things a lot!
|
||
### 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 comment
The 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 component
in (potentially nested) props positions. I think it’s doable but I definitely want to make note of it early on.
|
||
## `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 comment
The 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.
|
||
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 comment
The reason will be displayed to describe this comment to others. Learn more.
👏👏👏 no further comment on this!
Some more thoughts, on hydration, compiler, project scope and structure… BikesheddingIs the Hydration methodI'd like to support custom hydration functions from the start. I would have found it super useful building out some components in v1. Hydrating props/childrenIt’s probably taken as implicit, but I want to make explicit that the compiler should be able to hydrate arbitrary props/JS structures as written in source. This is currently solved by limited serialization, but I think at a compiler level it should be able to just compile relevant code back to plain JS. Partial-Full Hydration™We should rev and explore the possibility of full tree partial hydration to support context etc, and hopefully eliminate the need for global state hooks. To clarify for other readers/posterity: v1 currently implements hydration by mounting each hydrated component as a new root (Astro does this too). Other partial hydration techniques I’ve encountered (notably used by The Guardian if anyone wants to see their implementation) use portals from a single off-screen root, projected onto their respective mount nodes. This allows components to share context as if they’re rendered in a normal full page “app”. This is simple works great so long as the component returns a single non-fragment element. It gets more complicated for conditional returns or fragments. Microsite is already smart enough to support mounting fragments without introducing a container, which I think we should preserve. So the challenge is how to support those more complex scenarios. I’ve already shared some early exploration of this with @natemoo-re. I’ve looked at several techniques, including a proposed Before I looked at DPF, I explored using <section>
<h1>whatever</h1>
<!-- Foo -->
<FooChild1 />
<FooChild2 />
<!-- /Foo -->
<p>more static</p>
</section> That can easily be represented as: <section>
<NoOp />
<!-- Foo -->
<FooChild1 />
<FooChild2 />
<!-- /Foo -->
<NoOp />
</section> Doesn’t even need suspense, just It’s early to settle on the optimal solution to this, but just noting it’s something I really want for v2 and have been actively exploring. I’ll also note that The Guardian’s solution was an inspiration for this, but it’s exceedingly manual and specific to their document structure. I already have a working (if naive) solution for generalizing:
All of this has a little bit of runtime weight, but it’s probably worth the tradeoff, and may perform better by rendering first pass off screen. And probably none of this is necessary to support Solid 🙃 Project and package scopeI think we should consider splitting off functionality related to compilation and hydration from everything else (library code) into a separate package. While I find the Next.js-like APIs appealing, this proposal very likely lets Microsite’s other goals work without such opinionated data-fetching APIs. @natemoo-re will again not be surprised to hear me say, while I think Astro is super cool, its scope is way too huge. So long as Microsite is coupled to JSX, so too I hope it’ll embrace the unopinionated spirit of JSX as syntax extension. Data fetching in build could be as simple as “call whatever you like, in a callback to a clearly statically analyzable call to a build-time function”. Not unlike I don’t think we should deprecate existing data fetching APIs, just treat them as optional convenience methods that help make Microsite more palatable where fallback to Next.js is a risk mitigation. |
Collecting some thoughts about Microsite v2.0.0.
Read the full RFC