-
Notifications
You must be signed in to change notification settings - Fork 30
Description
Happo has a stylesheets config option to ensure styles are applied at render time. We'd like these styles to also be injected at prerender time. This allows components such as react-autosize-textarea, which call window.getComputedStyle while rendering, to behave more realistically.
Furthermore, we've run into some execution ordering bugs in JSDOM which result in the <script> tags after a stylesheet <link> tag executing before the stylesheet is loaded. This should not happen in a real browser, since styles ought to be parser-blocking. Our solution was to inline the stylesheet contents in a <style> tag.
We were able to work around all of these problems by using the plugins API, which allows providing a custom DomProvider. We extended the JSDOMDomProvider and modified this.dom.
Implementation ideas
Prerender currently operates on a static JSDOM template:
happo.io/src/JSDOMDomProvider.js
Lines 42 to 52 in 2f2bf5a
| this.dom = new JSDOM( | |
| ` | |
| <!DOCTYPE html> | |
| <html> | |
| <head> | |
| <script src='file://${webpackBundle}'></script> | |
| </head> | |
| <body> | |
| </body> | |
| </html> | |
| `.trim(), |
- A very basic solution would be to just inject the
stylesheetsparam in here as a tag, the same way that the render environment works. However, this won't support the JSDOM workaround. - Another option would be to automatically inline the styles. However, users might want to link to an external stylesheet that isn't on the filesystem, which would require some work to fetch the stylesheet.
- A third option would be to allow customizing the JSDOM template in addition to the existing jsdomOptions.
- Lastly, we could just document the fact that DomProviders exist and can be overridden by a plugin. We could use our custom DomProvider as an example:
module.exports = function happoPrerenderWithStylesheetsPlugin(
stylesheetAbsolutePaths,
// the regular jsdomOptions from the config aren't passed to a plugin DOMProvider, maybe we can fix this
jsdomOptions,
) {
class JSDOMProviderWithStylesheets extends HappoJSDOMProvider {
constructor(jsdomOpts, providerOptions) {
super(jsdomOpts, providerOptions);
// this.dom was created in the superclass
const {
window: { document },
} = this.dom;
stylesheetAbsolutePaths.forEach((href) => {
const cssSource = fs.readFileSync(href, 'utf-8');
const style = document.createElement('style');
style.textContent = cssSource;
document.head.prepend(style);
});
}
}
return {
DomProvider: JSDOMProviderWithStylesheets.bind(JSDOMProviderWithStylesheets, jsdomOptions),
};
};