|
1 | 1 | import React from 'react'; |
2 | 2 | import { ServerStyleSheets } from '@material-ui/styles'; |
| 3 | +import { ServerStyleSheet } from 'styled-components'; |
| 4 | +import { extractCritical } from 'emotion-server'; |
3 | 5 | import Document, { Html, Head, Main, NextScript } from 'next/document'; |
4 | 6 | import { LANGUAGES_SSR } from 'docs/src/modules/constants'; |
5 | 7 | import { pathnameToLanguage } from 'docs/src/modules/utils/helpers'; |
@@ -92,55 +94,78 @@ export default class MyDocument extends Document { |
92 | 94 | } |
93 | 95 | } |
94 | 96 |
|
| 97 | +// `getInitialProps` belongs to `_document` (instead of `_app`), |
| 98 | +// it's compatible with static-site generation (SSG). |
95 | 99 | MyDocument.getInitialProps = async (ctx) => { |
96 | 100 | // Resolution order |
97 | 101 | // |
98 | 102 | // On the server: |
99 | | - // 1. page.getInitialProps |
100 | | - // 2. document.getInitialProps |
101 | | - // 3. page.render |
102 | | - // 4. document.render |
| 103 | + // 1. app.getInitialProps |
| 104 | + // 2. page.getInitialProps |
| 105 | + // 3. document.getInitialProps |
| 106 | + // 4. app.render |
| 107 | + // 5. page.render |
| 108 | + // 6. document.render |
103 | 109 | // |
104 | 110 | // On the server with error: |
105 | | - // 2. document.getInitialProps |
| 111 | + // 1. document.getInitialProps |
| 112 | + // 2. app.render |
106 | 113 | // 3. page.render |
107 | 114 | // 4. document.render |
108 | 115 | // |
109 | 116 | // On the client |
110 | | - // 1. page.getInitialProps |
111 | | - // 3. page.render |
| 117 | + // 1. app.getInitialProps |
| 118 | + // 2. page.getInitialProps |
| 119 | + // 3. app.render |
| 120 | + // 4. page.render |
112 | 121 |
|
113 | 122 | // Render app and page and get the context of the page with collected side effects. |
114 | | - const sheets = new ServerStyleSheets(); |
| 123 | + const materialSheets = new ServerStyleSheets(); |
| 124 | + const styledComponentsSheet = new ServerStyleSheet(); |
115 | 125 | const originalRenderPage = ctx.renderPage; |
116 | 126 |
|
117 | | - ctx.renderPage = () => |
118 | | - originalRenderPage({ |
119 | | - enhanceApp: (App) => (props) => sheets.collect(<App {...props} />), |
120 | | - }); |
| 127 | + try { |
| 128 | + ctx.renderPage = () => |
| 129 | + originalRenderPage({ |
| 130 | + enhanceApp: (App) => (props) => |
| 131 | + styledComponentsSheet.collectStyles(materialSheets.collect(<App {...props} />)), |
| 132 | + }); |
121 | 133 |
|
122 | | - const initialProps = await Document.getInitialProps(ctx); |
| 134 | + const initialProps = await Document.getInitialProps(ctx); |
| 135 | + const emotionStyles = extractCritical(initialProps.html); |
123 | 136 |
|
124 | | - let css = sheets.toString(); |
125 | | - // It might be undefined, e.g. after an error. |
126 | | - if (css && process.env.NODE_ENV === 'production') { |
127 | | - const result1 = await prefixer.process(css, { from: undefined }); |
128 | | - css = result1.css; |
129 | | - css = cleanCSS.minify(css).styles; |
130 | | - } |
| 137 | + let css = materialSheets.toString(); |
| 138 | + // It might be undefined, e.g. after an error. |
| 139 | + if (css && process.env.NODE_ENV === 'production') { |
| 140 | + const result1 = await prefixer.process(css, { from: undefined }); |
| 141 | + css = result1.css; |
| 142 | + css = cleanCSS.minify(css).styles; |
| 143 | + } |
131 | 144 |
|
132 | | - return { |
133 | | - ...initialProps, |
134 | | - canonical: pathnameToLanguage(ctx.req.url).canonical, |
135 | | - userLanguage: ctx.query.userLanguage || 'en', |
136 | | - styles: [ |
137 | | - ...React.Children.toArray(initialProps.styles), |
138 | | - <style |
139 | | - id="jss-server-side" |
140 | | - key="jss-server-side" |
141 | | - // eslint-disable-next-line react/no-danger |
142 | | - dangerouslySetInnerHTML={{ __html: css }} |
143 | | - />, |
144 | | - ], |
145 | | - }; |
| 145 | + return { |
| 146 | + ...initialProps, |
| 147 | + canonical: pathnameToLanguage(ctx.req.url).canonical, |
| 148 | + userLanguage: ctx.query.userLanguage || 'en', |
| 149 | + // Styles fragment is rendered after the app and page rendering finish. |
| 150 | + styles: [ |
| 151 | + ...React.Children.toArray(initialProps.styles), |
| 152 | + <style |
| 153 | + id="jss-server-side" |
| 154 | + key="jss-server-side" |
| 155 | + // eslint-disable-next-line react/no-danger |
| 156 | + dangerouslySetInnerHTML={{ __html: css }} |
| 157 | + />, |
| 158 | + <style |
| 159 | + id="emotion-server-side" |
| 160 | + key="emotion-server-side" |
| 161 | + data-emotion-css={emotionStyles.ids.join(' ')} |
| 162 | + // eslint-disable-next-line react/no-danger |
| 163 | + dangerouslySetInnerHTML={{ __html: emotionStyles.css }} |
| 164 | + />, |
| 165 | + styledComponentsSheet.getStyleElement(), |
| 166 | + ], |
| 167 | + }; |
| 168 | + } finally { |
| 169 | + styledComponentsSheet.seal(); |
| 170 | + } |
146 | 171 | }; |
0 commit comments