Skip to content

Commit

Permalink
Use flushed effects to generate styled-jsx styles insted of gIP by de…
Browse files Browse the repository at this point in the history
…fault
  • Loading branch information
huozhi committed May 1, 2022
1 parent 7a09f88 commit 0483d06
Show file tree
Hide file tree
Showing 7 changed files with 51 additions and 27 deletions.
9 changes: 7 additions & 2 deletions packages/next/pages/_document.tsx
@@ -1,9 +1,13 @@
import React, { Component, ReactElement, ReactNode, useContext } from 'react'
import { OPTIMIZED_FONT_PROVIDERS } from '../shared/lib/constants'
import {
OPTIMIZED_FONT_PROVIDERS,
NEXT_BUILTIN_DOCUMENT,
} from '../shared/lib/constants'
import type {
DocumentContext,
DocumentInitialProps,
DocumentProps,
DocumentType,
} from '../shared/lib/utils'
import { BuildManifest, getPageFiles } from '../server/get-page-files'
import { cleanAmpPath } from '../server/utils'
Expand Down Expand Up @@ -309,7 +313,7 @@ export default class Document<P = {}> extends Component<DocumentProps & P> {

// Add a special property to the built-in `Document` component so later we can
// identify if a user customized `Document` is used or not.
;(Document as any).__next_internal_document =
const InternalFunctionDocument: DocumentType =
function InternalFunctionDocument() {
return (
<Html>
Expand All @@ -321,6 +325,7 @@ export default class Document<P = {}> extends Component<DocumentProps & P> {
</Html>
)
}
;(Document as any)[NEXT_BUILTIN_DOCUMENT] = InternalFunctionDocument

export function Html(
props: React.DetailedHTMLProps<
Expand Down
3 changes: 2 additions & 1 deletion packages/next/server/base-server.ts
Expand Up @@ -22,6 +22,7 @@ import { parse as parseQs } from 'querystring'
import { format as formatUrl, parse as parseUrl } from 'url'
import { getRedirectStatus } from '../lib/load-custom-routes'
import {
NEXT_BUILTIN_DOCUMENT,
SERVERLESS_DIRECTORY,
SERVER_DIRECTORY,
STATIC_STATUS_PAGES,
Expand Down Expand Up @@ -1216,7 +1217,7 @@ export default abstract class Server<ServerOptions extends Options = Options> {
// When concurrent features is enabled, the built-in `Document`
// component also supports dynamic HTML.
(this.renderOpts.reactRoot &&
!!(components.Document as any)?.__next_internal_document)
NEXT_BUILTIN_DOCUMENT in components.Document)

// Disable dynamic HTML in cases that we know it won't be generated,
// so that we can continue generating a cache key when possible.
Expand Down
29 changes: 18 additions & 11 deletions packages/next/server/render.tsx
Expand Up @@ -6,6 +6,7 @@ import type { DomainLocale } from './config'
import type {
AppType,
DocumentInitialProps,
DocumentType,
DocumentProps,
DocumentContext,
NextComponentType,
Expand Down Expand Up @@ -35,6 +36,7 @@ import {
UNSTABLE_REVALIDATE_RENAME_ERROR,
} from '../lib/constants'
import {
NEXT_BUILTIN_DOCUMENT,
SERVER_PROPS_ID,
STATIC_PROPS_ID,
STATIC_STATUS_PAGES,
Expand Down Expand Up @@ -88,6 +90,7 @@ let warn: typeof import('../build/output/log').warn
let postProcess: typeof import('../shared/lib/post-process').default

const DOCTYPE = '<!DOCTYPE html>'
const GIP_GEN_STYLES = '__GIP_GEN_STYLES__'
const ReactDOMServer = process.env.__NEXT_REACT_ROOT
? require('react-dom/server.browser')
: require('react-dom/server')
Expand Down Expand Up @@ -769,6 +772,7 @@ export async function renderToHTML(

const { html, head } = await docCtx.renderPage({ enhanceApp })
const styles = jsxStyleRegistry.styles({ nonce: options.nonce })
;(styles as any)[GIP_GEN_STYLES] = true
return { html, head, styles }
},
}
Expand Down Expand Up @@ -1311,14 +1315,14 @@ export async function renderToHTML(
// 1. Using `Document.getInitialProps` in the Edge runtime.
// 2. Using the class component `Document` with concurrent features.

const builtinDocument = (Document as any).__next_internal_document as
| typeof Document
| undefined
const BuiltinFunctionalDocument: DocumentType | undefined = (
Document as any
)[NEXT_BUILTIN_DOCUMENT]

if (process.env.NEXT_RUNTIME === 'edge' && Document.getInitialProps) {
// In the Edge runtime, `Document.getInitialProps` isn't supported.
// We throw an error here if it's customized.
if (!builtinDocument) {
if (!BuiltinFunctionalDocument) {
throw new Error(
'`getInitialProps` in Document component is not supported with the Edge Runtime.'
)
Expand All @@ -1329,8 +1333,8 @@ export async function renderToHTML(
(isServerComponent || process.env.NEXT_RUNTIME === 'edge') &&
Document.getInitialProps
) {
if (builtinDocument) {
Document = builtinDocument
if (BuiltinFunctionalDocument) {
Document = BuiltinFunctionalDocument
} else {
throw new Error(
'`getInitialProps` in Document component is not supported with React Server Components.'
Expand Down Expand Up @@ -1433,6 +1437,7 @@ export async function renderToHTML(
}

if (!process.env.__NEXT_REACT_ROOT) {
// Enabling react legacy rendering mode: __NEXT_REACT_ROOT = false
if (Document.getInitialProps) {
const documentInitialPropsRes = await documentInitialProps()
if (documentInitialPropsRes === null) return null
Expand Down Expand Up @@ -1468,6 +1473,7 @@ export async function renderToHTML(
}
}
} else {
// Enabling react concurrent rendering mode: __NEXT_REACT_ROOT = true
let renderStream: ReadableStream<Uint8Array> & {
allReady?: Promise<void> | undefined
}
Expand Down Expand Up @@ -1534,13 +1540,11 @@ export async function renderToHTML(
// be streamed.
// Do not use `await` here.
generateStaticFlightDataIfNeeded()

return await continueFromInitialStream({
renderStream,
suffix,
dataStream: serverComponentsInlinedTransformStream?.readable,
generateStaticHTML:
generateStaticHTML || !process.env.__NEXT_REACT_ROOT,
generateStaticHTML,
flushEffectHandler,
})
}
Expand Down Expand Up @@ -1572,10 +1576,13 @@ export async function renderToHTML(

return <Document {...htmlProps} {...docProps} />
}
let styles

let styles: any[]
if (hasDocumentGetInitialProps) {
styles = docProps.styles
// If styles are generated from default Document.getInitialProps in concurrent mode,
// discard it and use generated styles from flush-effects instead.
// To guarantee it's concurrent safe and no duplicates in both head and body.
styles = docProps.styles[GIP_GEN_STYLES] ? [] : docProps.styles
} else {
styles = jsxStyleRegistry.styles()
jsxStyleRegistry.flush()
Expand Down
1 change: 1 addition & 0 deletions packages/next/shared/lib/constants.ts
Expand Up @@ -25,6 +25,7 @@ export const CLIENT_PUBLIC_FILES_PATH = 'public'
export const CLIENT_STATIC_FILES_PATH = 'static'
export const CLIENT_STATIC_FILES_RUNTIME = 'runtime'
export const STRING_LITERAL_DROP_BUNDLE = '__NEXT_DROP_CLIENT_FILE__'
export const NEXT_BUILTIN_DOCUMENT = '__NEXT_BUILTIN_DOCUMENT__'

// server/middleware-flight-manifest.js
export const MIDDLEWARE_FLIGHT_MANIFEST = 'middleware-flight-manifest'
Expand Down
8 changes: 8 additions & 0 deletions test/e2e/styled-jsx/app/pages/index.js
@@ -0,0 +1,8 @@
export default function Page() {
return (
<div>
<style jsx>{'p { color: red; }'}</style>
<p>hello world</p>
</div>
)
}
19 changes: 7 additions & 12 deletions test/e2e/styled-jsx/index.test.ts
@@ -1,25 +1,19 @@
import { createNext } from 'e2e-utils'
import path from 'path'
import { createNext, FileRef } from 'e2e-utils'
import { NextInstance } from 'test/lib/next-modes/base'
import { renderViaHTTP } from 'next-test-utils'
import cheerio from 'cheerio'

const appDir = path.join(__dirname, 'app')

function runTest(packageManager?: string) {
describe(`styled-jsx${packageManager ? ' ' + packageManager : ''}`, () => {
let next: NextInstance

beforeAll(async () => {
next = await createNext({
files: {
'pages/index.js': `
export default function Page() {
return (
<div>
<style jsx>{'p { color: red; }'}</style>
<p>hello world</p>
</div>
)
}
`,
pages: new FileRef(path.join(appDir, 'pages')),
},
...(packageManager
? {
Expand All @@ -33,7 +27,8 @@ function runTest(packageManager?: string) {
it('should contain styled-jsx styles in html', async () => {
const html = await renderViaHTTP(next.url, '/')
const $ = cheerio.load(html)
expect($('head').text()).toContain('color:red')
const isReact17 = process.env.NEXT_TEST_REACT_VERSION === '^17'
expect($(isReact17 ? 'head' : 'body').text()).toMatch(/color:(\s)*red/)
})
})
}
Expand Down
9 changes: 8 additions & 1 deletion test/integration/react-18/test/concurrent.js
Expand Up @@ -32,7 +32,14 @@ export default (context, _render) => {
})
})

it('flushes styles as the page renders', async () => {
it('flushes styled-jsx styles as the page renders', async () => {
const html = await renderViaHTTP(
context.appPort,
'/use-flush-effect/styled-jsx'
)
const stylesOccurrence = html.match(/color:(\s)*blue/g) || []
expect(stylesOccurrence.length).toBe(1)

await withBrowser('/use-flush-effect/styled-jsx', async (browser) => {
await check(
() => browser.waitForElementByCss('#__jsx-900f996af369fc74').text(),
Expand Down

0 comments on commit 0483d06

Please sign in to comment.