Skip to content

Commit

Permalink
Merge branch 'canary' into update/use-pnpm
Browse files Browse the repository at this point in the history
  • Loading branch information
ijjk committed May 29, 2022
2 parents d2555f4 + 3930214 commit 6f0710a
Show file tree
Hide file tree
Showing 15 changed files with 231 additions and 22 deletions.
1 change: 1 addition & 0 deletions packages/next/export/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -385,6 +385,7 @@ export default async function exportApp(
nextScriptWorkers: nextConfig.experimental.nextScriptWorkers,
optimizeFonts: nextConfig.optimizeFonts,
reactRoot: nextConfig.experimental.reactRoot || false,
largePageDataBytes: nextConfig.experimental.largePageDataBytes,
}

const { serverRuntimeConfig, publicRuntimeConfig } = nextConfig
Expand Down
22 changes: 11 additions & 11 deletions packages/next/pages/_document.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -946,20 +946,20 @@ export class NextScript extends Component<OriginProps> {
}

static getInlineScriptSource(context: Readonly<HtmlProps>): string {
const { __NEXT_DATA__ } = context
const { __NEXT_DATA__, largePageDataBytes } = context
try {
const data = JSON.stringify(__NEXT_DATA__)
const bytes = Buffer.from(data).byteLength
const prettyBytes = require('../lib/pretty-bytes').default

if (process.env.NODE_ENV === 'development') {
const bytes = Buffer.from(data).byteLength
const prettyBytes = require('../lib/pretty-bytes').default
if (bytes > 128 * 1000) {
console.warn(
`Warning: data for page "${__NEXT_DATA__.page}" is ${prettyBytes(
bytes
)}, this amount of data can reduce performance.\nSee more info here: https://nextjs.org/docs/messages/large-page-data`
)
}
if (largePageDataBytes && bytes > largePageDataBytes) {
console.warn(
`Warning: data for page "${__NEXT_DATA__.page}" is ${prettyBytes(
bytes
)} which exceeds the threshold of ${prettyBytes(
largePageDataBytes
)}, this amount of data can reduce performance.\nSee more info here: https://nextjs.org/docs/messages/large-page-data`
)
}

return htmlEscapeJsonString(data)
Expand Down
2 changes: 2 additions & 0 deletions packages/next/server/base-server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,7 @@ export default abstract class Server<ServerOptions extends Options = Options> {
renderServerComponentData?: boolean
serverComponentProps?: any
reactRoot: boolean
largePageDataBytes?: number
}
protected serverOptions: ServerOptions
private incrementalCache: IncrementalCache
Expand Down Expand Up @@ -327,6 +328,7 @@ export default abstract class Server<ServerOptions extends Options = Options> {
? this.nextConfig.crossOrigin
: undefined,
reactRoot: this.nextConfig.experimental.reactRoot === true,
largePageDataBytes: this.nextConfig.experimental.largePageDataBytes,
}

// Only the `publicRuntimeConfig` key is exposed to the client side
Expand Down
2 changes: 2 additions & 0 deletions packages/next/server/config-shared.ts
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,7 @@ export interface ExperimentalConfig {
swcTraceProfiling?: boolean
forceSwcTransforms?: boolean
swcPlugins?: Array<[string, Record<string, unknown>]>
largePageDataBytes?: number
}

/**
Expand Down Expand Up @@ -516,6 +517,7 @@ export const defaultConfig: NextConfig = {
remotePatterns: [],
},
forceSwcTransforms: false,
largePageDataBytes: 128 * 1000, // 128KB by default
},
}

Expand Down
2 changes: 2 additions & 0 deletions packages/next/server/render.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,7 @@ export type RenderOptsPartial = {
crossOrigin?: string
images: ImageConfigComplete
reactRoot: boolean
largePageDataBytes?: number
}

export type RenderOpts = LoadComponentsReturnType & RenderOptsPartial
Expand Down Expand Up @@ -1562,6 +1563,7 @@ export async function renderToHTML(
optimizeFonts: renderOpts.optimizeFonts,
nextScriptWorkers: renderOpts.nextScriptWorkers,
runtime: globalRuntime,
largePageDataBytes: renderOpts.largePageDataBytes,
}

const document = (
Expand Down
1 change: 1 addition & 0 deletions packages/next/shared/lib/html-context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ export type HtmlProps = {
nextScriptWorkers?: boolean
runtime?: 'edge' | 'nodejs'
hasConcurrentFeatures?: boolean
largePageDataBytes?: number
}

export const HtmlContext = createContext<HtmlProps>(null as any)
Expand Down
15 changes: 14 additions & 1 deletion packages/next/shared/lib/router/router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ type PreflightEffect =
| { type: 'refresh' }
| { type: 'next' }

type HistoryState =
export type HistoryState =
| null
| { __N: false }
| ({ __N: true; key: string } & NextHistoryState)
Expand Down Expand Up @@ -642,6 +642,7 @@ export default class Router implements BaseRouter {
domainLocales?: DomainLocale[] | undefined
isReady: boolean
isLocaleDomain: boolean
isFirstPopStateEvent = true

private state: Readonly<{
route: string
Expand Down Expand Up @@ -797,6 +798,9 @@ export default class Router implements BaseRouter {
}

onPopState = (e: PopStateEvent): void => {
const { isFirstPopStateEvent } = this
this.isFirstPopStateEvent = false

const state = e.state as HistoryState

if (!state) {
Expand All @@ -822,6 +826,15 @@ export default class Router implements BaseRouter {
return
}

// Safari fires popstateevent when reopening the browser.
if (
isFirstPopStateEvent &&
this.locale === state.options.locale &&
state.as === this.asPath
) {
return
}

let forcedScroll: { x: number; y: number } | undefined
const { url, as, options, key } = state
if (process.env.__NEXT_SCROLL_RESTORATION) {
Expand Down
6 changes: 6 additions & 0 deletions test/e2e/ignore-invalid-popstateevent/app/next.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
module.exports = {
i18n: {
locales: ['en', 'sv'],
defaultLocale: 'en',
},
}
3 changes: 3 additions & 0 deletions test/e2e/ignore-invalid-popstateevent/app/pages/[dynamic].js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export default function DynamicPage() {
return <p id="page-type">dynamic</p>
}
3 changes: 3 additions & 0 deletions test/e2e/ignore-invalid-popstateevent/app/pages/static.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export default function StaticPage() {
return <p id="page-type">static</p>
}
94 changes: 94 additions & 0 deletions test/e2e/ignore-invalid-popstateevent/with-i18n.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
import { join } from 'path'
import { createNext, FileRef } from 'e2e-utils'
import { NextInstance } from 'test/lib/next-modes/base'
import { check, waitFor } from 'next-test-utils'
import webdriver from 'next-webdriver'

import type { HistoryState } from '../../../packages/next/shared/lib/router/router'
import { BrowserInterface } from 'test/lib/browsers/base'

const emitPopsStateEvent = (browser: BrowserInterface, state: HistoryState) =>
browser.eval(
`window.dispatchEvent(new PopStateEvent("popstate", { state: ${JSON.stringify(
state
)} }))`
)

describe('i18n: Event with stale state - static route previously was dynamic', () => {
let next: NextInstance

beforeAll(async () => {
next = await createNext({
files: {
pages: new FileRef(join(__dirname, 'app/pages')),
'next.config.js': new FileRef(join(__dirname, 'app/next.config.js')),
},
dependencies: {},
})
})
afterAll(() => next.destroy())

test('Ignore event without query param', async () => {
const browser = await webdriver(next.url, '/sv/static')
browser.close()

const state: HistoryState = {
url: '/[dynamic]?',
as: '/static',
options: { locale: 'sv' },
__N: true,
key: '',
}

expect(await browser.elementByCss('#page-type').text()).toBe('static')

// 1st event is ignored
await emitPopsStateEvent(browser, state)
await waitFor(1000)
expect(await browser.elementByCss('#page-type').text()).toBe('static')

// 2nd event isn't ignored
await emitPopsStateEvent(browser, state)
await check(() => browser.elementByCss('#page-type').text(), 'dynamic')
})

test('Ignore event with query param', async () => {
const browser = await webdriver(next.url, '/sv/static?param=1')

const state: HistoryState = {
url: '/[dynamic]?param=1',
as: '/static?param=1',
options: { locale: 'sv' },
__N: true,
key: '',
}

expect(await browser.elementByCss('#page-type').text()).toBe('static')

// 1st event is ignored
await emitPopsStateEvent(browser, state)
await waitFor(1000)
expect(await browser.elementByCss('#page-type').text()).toBe('static')

// 2nd event isn't ignored
await emitPopsStateEvent(browser, state)
await check(() => browser.elementByCss('#page-type').text(), 'dynamic')
})

test("Don't ignore event with different locale", async () => {
const browser = await webdriver(next.url, '/sv/static?param=1')

const state: HistoryState = {
url: '/[dynamic]?param=1',
as: '/static?param=1',
options: { locale: 'en' },
__N: true,
key: '',
}

expect(await browser.elementByCss('#page-type').text()).toBe('static')

await emitPopsStateEvent(browser, state)
await check(() => browser.elementByCss('#page-type').text(), 'dynamic')
})
})
77 changes: 77 additions & 0 deletions test/e2e/ignore-invalid-popstateevent/without-i18n.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import { join } from 'path'
import { createNext, FileRef } from 'e2e-utils'
import { NextInstance } from 'test/lib/next-modes/base'
import { check, waitFor } from 'next-test-utils'
import webdriver from 'next-webdriver'

import type { HistoryState } from '../../../packages/next/shared/lib/router/router'
import { BrowserInterface } from 'test/lib/browsers/base'

const emitPopsStateEvent = (browser: BrowserInterface, state: HistoryState) =>
browser.eval(
`window.dispatchEvent(new PopStateEvent("popstate", { state: ${JSON.stringify(
state
)} }))`
)

describe('Event with stale state - static route previously was dynamic', () => {
let next: NextInstance

beforeAll(async () => {
next = await createNext({
files: {
pages: new FileRef(join(__dirname, 'app/pages')),
// Don't use next.config.js to avoid getting i18n
},
dependencies: {},
})
})
afterAll(() => next.destroy())

test('Ignore event without query param', async () => {
const browser = await webdriver(next.url, '/static')
browser.close()

const state: HistoryState = {
url: '/[dynamic]?',
as: '/static',
options: {},
__N: true,
key: '',
}

expect(await browser.elementByCss('#page-type').text()).toBe('static')

// 1st event is ignored
await emitPopsStateEvent(browser, state)
await waitFor(1000)
expect(await browser.elementByCss('#page-type').text()).toBe('static')

// 2nd event isn't ignored
await emitPopsStateEvent(browser, state)
await check(() => browser.elementByCss('#page-type').text(), 'dynamic')
})

test('Ignore event with query param', async () => {
const browser = await webdriver(next.url, '/static?param=1')

const state: HistoryState = {
url: '/[dynamic]?param=1',
as: '/static?param=1',
options: {},
__N: true,
key: '',
}

expect(await browser.elementByCss('#page-type').text()).toBe('static')

// 1st event is ignored
await emitPopsStateEvent(browser, state)
await waitFor(1000)
expect(await browser.elementByCss('#page-type').text()).toBe('static')

// 2nd event isn't ignored
await emitPopsStateEvent(browser, state)
await check(() => browser.elementByCss('#page-type').text(), 'dynamic')
})
})
16 changes: 8 additions & 8 deletions test/e2e/prerender.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -942,15 +942,15 @@ describe('Prerender', () => {
})
})

if ((global as any).isNextDev) {
it('should show warning when large amount of page data is returned', async () => {
await renderViaHTTP(next.url, '/large-page-data')
await check(
() => next.cliOutput,
/Warning: data for page "\/large-page-data" is 128 kB, this amount of data can reduce performance/
)
})
it('should show warning when large amount of page data is returned', async () => {
await renderViaHTTP(next.url, '/large-page-data')
await check(
() => next.cliOutput,
/Warning: data for page "\/large-page-data" is 256 kB which exceeds the threshold of 128 kB, this amount of data can reduce performance/
)
})

if ((global as any).isNextDev) {
it('should not show warning from url prop being returned', async () => {
const urlPropPage = 'pages/url-prop.js'
await next.patchFile(
Expand Down
2 changes: 1 addition & 1 deletion test/e2e/prerender/pages/large-page-data.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import Link from 'next/link'
export async function getStaticProps({ params }) {
return {
props: {
lotsOfData: new Array(128 * 1000).fill('a').join(''),
lotsOfData: new Array(256 * 1000).fill('a').join(''),
},
revalidate: false,
}
Expand Down
7 changes: 6 additions & 1 deletion tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,10 @@
"e2e-utils": ["test/lib/e2e-utils"]
}
},
"include": ["test/**/*.test.ts", "test/**/*.test.tsx", "test/**/test/*"]
"include": [
"test/**/*.test.ts",
"test/**/*.test.tsx",
"test/**/test/*",
"packages/next/types/webpack.d.ts"
]
}

0 comments on commit 6f0710a

Please sign in to comment.