Skip to content
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

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open

RFC: Microsite v2.0.0 #163

wants to merge 4 commits into from

Conversation

natemoo-re
Copy link
Owner

@natemoo-re natemoo-re commented Aug 29, 2021

Collecting some thoughts about Microsite v2.0.0.

Read the full RFC

@vercel
Copy link

vercel bot commented Aug 29, 2021

This pull request is being automatically deployed with Vercel (learn more).
To see the status of your deployments, click below or on the icon next to each commit.

microsite – ./site

🔍 Inspect: https://vercel.com/nmoo/microsite/8ZkCEcuAWGKZoapm4f1yAPLUdJQX
✅ Preview: Canceled

[Deployment for abbcf5d canceled]

microsite-examples – ./

🔍 Inspect: https://vercel.com/nmoo/microsite-examples/2pBhag436EH9KFg9MCLCQ4tZkvXH
✅ Preview: https://microsite-examples-git-rfc-v2-nmoo.vercel.app

@changeset-bot
Copy link

changeset-bot bot commented Aug 29, 2021

⚠️ No Changeset found

Latest commit: abbcf5d

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

Copy link
Collaborator

@eyelidlessness eyelidlessness left a 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`
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. 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 a component?

  2. I think component used this way may be challenging when supporting Solid, where its equivalent is createComponent(() => 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.
Copy link
Collaborator

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
Copy link
Collaborator

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.

Copy link
Owner Author

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`.
Copy link
Collaborator

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?
Copy link
Collaborator

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.
Copy link
Collaborator

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!

@eyelidlessness
Copy link
Collaborator

Some more thoughts, on hydration, compiler, project scope and structure…

Bikeshedding

Is the @ prefix intended to mirror the future @key in React JSX? FWIW I find Solid's use of namespaces (eg attr:foo, prop:quux) nice, and I like the idea of namespaces for special prop functionality generally. It's a little more explicit, and has a lot more functionality.

Hydration method

I'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/children

It’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 DocumentPersistentFragment and it’s polyfill, which seemed promising but my prototype is incomplete. But I think there may be a simpler solution loosely based on an earlier approach I took.

Before I looked at DPF, I explored using Suspense to noop existing DOM nodes. This works very well if the full subtree is static, but halts rendering of children (at least in current implementations). But for portals this is great! If you have a structure like:

<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 shouldComponentUpdate() { return false } or equivalent.

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:

  • Identifying subroots
  • Populating a sparse tree with no-op nodes
  • Hydrating live nodes with serialized props

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 scope

I 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 lazy. All of those calls can be compiled to static constants at build time.

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants