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

add support for styles rendering and streaming #394

Open
wants to merge 14 commits into
base: master
Choose a base branch
from

Conversation

AyoubElk
Copy link

This closes styletron/styletron/#334

Implementation details:

  • Exposes a new method getStylesheetsStream that takes as an input a readable html stream and adds the newly rendered styles to it
  • Clears older styles after each render
  • Generates script tags that inject style tags in the document head
  • New rendered styles are injected inside the appropriate media scoped style tag
  • Style tags are ordered inside the head tag to support a mobile first approach

Looking forward to your feedback before adding tests/docs!
Thanks

Checklist

  • run tests
  • tests updated
  • documentation is changed or added

const transformer = new Transform({
transform: function appendStyleChunks(chunk, /* encoding */ _, callback) {
// Get the chunk and retrieve the sheet's CSS as an HTML chunk,
// then reset its rules so we get only new ones for the next chunk

Choose a reason for hiding this comment

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

Which piece actually does the reset for the next chunk?

Copy link
Author

Choose a reason for hiding this comment

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

@airhorns
Copy link

@rtsao would love to know what you think of this approach!

@airhorns
Copy link

or maybe @tajo might be able to say if this is a satisfactory direction? I think Ayoub is waiting on confirmation before writing tests and docs so it'd be awesome to get a "looks about right" or a "don't think we should do it this way" from you folks before investing more in this approach!

@airhorns
Copy link

Also worth mentioning is that React is building out more and more support for streaming server side rendering, especially to support Suspense and the other stuff that Server Components might bring. See https://twitter.com/dan_abramov/status/1375275660994883588?s=20 for a reference to all the work they are putting in. I feel like this feature for Styletron would be great to land to have a strategy for working in the streaming future and for working better with those of us rendering very big pages that still desire a good TTFB!

Copy link
Member

@rtsao rtsao left a comment

Choose a reason for hiding this comment

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

This is pretty interesting, thanks for putting this together! A couple thoughts:

It's definitely a bit unfortunate JS is required for this. CSP is another factor that could make this approach tricky. There probably needs to be a way to control the attributes of the injected script tags for this reason (e.g. supply a nonce).

I'd definitely be interested in seeing how this mechanism of style injection would affect performance. I'm still wondering if it'd be better to simply disallow or issue warnings for broken edge cases (sibling selectors, etc.) with some kind of progressive rendering "strict mode". But it's certainly cool that this works for all styles.

packages/styletron-engine-atomic/src/server/server.js Outdated Show resolved Hide resolved
packages/styletron-engine-atomic/src/server/server.js Outdated Show resolved Hide resolved
this.styleRules[media] = "";
(media, _cache, insertBeforeMedia) => {
this.styleRules[media] = {
...(insertBeforeMedia && {insertBeforeMedia}),
Copy link
Member

Choose a reason for hiding this comment

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

I'm thinking it should be OK if the value of this property is undefined

Suggested change
...(insertBeforeMedia && {insertBeforeMedia}),
insertBeforeMedia,

Copy link
Author

Choose a reason for hiding this comment

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

The reason for this addition was to make some of the current tests work since they use deepEqual comparison and do not expect the existence of the insertBeforeMedia property.

I will update the current tests as well when adding new ones, and then commit this change

packages/styletron-engine-atomic/src/server/server.js Outdated Show resolved Hide resolved
AyoubElk and others added 3 commits March 30, 2021 21:48
Co-authored-by: Ryan Tsao <ryan.j.tsao@gmail.com>
Co-authored-by: Ryan Tsao <ryan.j.tsao@gmail.com>
Co-authored-by: Ryan Tsao <ryan.j.tsao@gmail.com>
@AyoubElk
Copy link
Author

This is pretty interesting, thanks for putting this together!

Thanks!

It's definitely a bit unfortunate JS is required for this. CSP is another factor that could make this approach tricky. There probably needs to be a way to control the attributes of the injected script tags for this reason (e.g. supply a nonce).

Understood, I'm thinking we could allow the user to provide a callback function that would be called for each generated script tag and used to do all sorts of things (including calculating and supplying a nonce).

I feel this would be generic enough to cover the CSP use case, and potentially other not so straight-forward use cases users might have.

What do you think?

I'd definitely be interested in seeing how this mechanism of style injection would affect performance.

How do you propose we test performance? I haven't seen any performance-related tests/benchmarks anywhere.

I'm still wondering if it'd be better to simply disallow or issue warnings for broken edge cases (sibling selectors, etc.) with some kind of progressive rendering "strict mode". But it's certainly cool that this works for all styles.

I think issuing warnings or disallowing edge cases would be tricky to get right. I will think about that some more and see if I come up with a clean way to achieve it.

@airhorns
Copy link

I feel this would be generic enough to cover the CSP use case, and potentially other not so straight-forward use cases users might have.

I agree, I think if you made the function actually responsible for generating the script tag and then had a default that made a simple one that would give a lot of control.

I'd definitely be interested in seeing how this mechanism of style injection would affect performance.

Do you mean page load performance for the user experience or server side rendering performance for the backends doing SSR?

I'm still wondering if it'd be better to simply disallow or issue warnings for broken edge cases (sibling selectors, etc.) with some kind of progressive rendering "strict mode". But it's certainly cool that this works for all styles.

To do this we'd have to parse and understand CSS selectors that styletron can find right, and then it'd still be hard to warn for CSS that is coming from some other spot on the page like a plain old link tag, right? That seems like a fair amount of complexity but I guess it'd only have to run in development.

@AyoubElk
Copy link
Author

AyoubElk commented Apr 29, 2021

I added tests and support for providing a custom script generation function.

For client-side hydration, any thoughts on the best way to test it?
The hydration can only be done after the script tags code is executed and style tags have been generated

@AyoubElk
Copy link
Author

@rtsao It would be great to know what you think about this.

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.

Streaming rendering support
3 participants