Skip to content

Commit

Permalink
Merge branch 'add/remove-react-17' of github.com:vercel/next.js into …
Browse files Browse the repository at this point in the history
…add/remove-react-17
  • Loading branch information
timneutkens committed Oct 21, 2022
2 parents 63b01dc + 2391e7f commit 6415534
Show file tree
Hide file tree
Showing 6 changed files with 137 additions and 16 deletions.
Expand Up @@ -14,7 +14,7 @@ export type RootLayoutErrorProps = { missingTags: string[] }
export const RootLayoutError: React.FC<RootLayoutErrorProps> =
function BuildError({ missingTags }) {
const message =
'Please make sure to include the following tags in your root layout: <html>, <head>, <body>.\n\n' +
'Please make sure to include the following tags in your root layout: <html>, <body>.\n\n' +
`Missing required root layout tag${
missingTags.length === 1 ? '' : 's'
}: ` +
Expand Down
7 changes: 1 addition & 6 deletions packages/next/server/node-web-streams-helper.ts
Expand Up @@ -263,19 +263,15 @@ export function createRootLayoutValidatorStream(
getTree: () => FlightRouterState
): TransformStream<Uint8Array, Uint8Array> {
let foundHtml = false
let foundHead = false
let foundBody = false

return new TransformStream({
async transform(chunk, controller) {
if (!foundHtml || !foundHead || !foundBody) {
if (!foundHtml || !foundBody) {
const content = decodeText(chunk)
if (!foundHtml && content.includes('<html')) {
foundHtml = true
}
if (!foundHead && content.includes('<head')) {
foundHead = true
}
if (!foundBody && content.includes('<body')) {
foundBody = true
}
Expand All @@ -285,7 +281,6 @@ export function createRootLayoutValidatorStream(
flush(controller) {
const missingTags = [
foundHtml ? null : 'html',
foundHead ? null : 'head',
foundBody ? null : 'body',
].filter(nonNullable)

Expand Down
3 changes: 3 additions & 0 deletions packages/next/server/web/sandbox/context.ts
@@ -1,3 +1,4 @@
import { AsyncLocalStorage } from 'async_hooks'
import type { AssetBinding } from '../../../build/webpack/loaders/get-module-build-info'
import {
decorateServerError,
Expand Down Expand Up @@ -286,6 +287,8 @@ Learn More: https://nextjs.org/docs/messages/edge-dynamic-code-evaluation`),

Object.assign(context, wasm)

context.AsyncLocalStorage = AsyncLocalStorage

return context
},
})
Expand Down
12 changes: 6 additions & 6 deletions test/e2e/app-dir/root-layout.test.ts
Expand Up @@ -39,9 +39,9 @@ describe('app-dir root layout', () => {

expect(await hasRedbox(browser, true)).toBe(true)
expect(await getRedboxSource(browser)).toMatchInlineSnapshot(`
"Please make sure to include the following tags in your root layout: <html>, <head>, <body>.
"Please make sure to include the following tags in your root layout: <html>, <body>.
Missing required root layout tags: html, head, body"
Missing required root layout tags: html, body"
`)
})

Expand All @@ -53,9 +53,9 @@ describe('app-dir root layout', () => {

expect(await hasRedbox(browser, true)).toBe(true)
expect(await getRedboxSource(browser)).toMatchInlineSnapshot(`
"Please make sure to include the following tags in your root layout: <html>, <head>, <body>.
"Please make sure to include the following tags in your root layout: <html>, <body>.
Missing required root layout tags: html, head, body"
Missing required root layout tags: html, body"
`)
})

Expand All @@ -66,9 +66,9 @@ describe('app-dir root layout', () => {

expect(await hasRedbox(browser, true)).toBe(true)
expect(await getRedboxSource(browser)).toMatchInlineSnapshot(`
"Please make sure to include the following tags in your root layout: <html>, <head>, <body>.
"Please make sure to include the following tags in your root layout: <html>, <body>.
Missing required root layout tags: html, head, body"
Missing required root layout tags: html, body"
`)
})
})
Expand Down
Expand Up @@ -4,9 +4,6 @@ export const revalidate = 0
export default function Root({ children }) {
return (
<html>
<head>
<title>Hello World</title>
</head>
<body>{children}</body>
</html>
)
Expand Down
126 changes: 126 additions & 0 deletions test/e2e/edge-async-local-storage/index.test.ts
@@ -0,0 +1,126 @@
/* eslint-disable jest/valid-expect-in-promise */
import { createNext } from 'e2e-utils'
import { NextInstance } from 'test/lib/next-modes/base'
import { fetchViaHTTP } from 'next-test-utils'

describe('edge api can use async local storage', () => {
let next: NextInstance

const cases = [
{
title: 'a single instance',
code: `
export const config = { runtime: 'experimental-edge' }
const storage = new AsyncLocalStorage()
export default async function handler(request) {
const id = request.headers.get('req-id')
return storage.run({ id }, async () => {
await getSomeData()
return Response.json(storage.getStore())
})
}
async function getSomeData() {
try {
const response = await fetch('https://example.vercel.sh')
await response.text()
} finally {
return true
}
}
`,
expectResponse: (response, id) =>
expect(response).toMatchObject({ status: 200, json: { id } }),
},
{
title: 'multiple instances',
code: `
export const config = { runtime: 'experimental-edge' }
const topStorage = new AsyncLocalStorage()
export default async function handler(request) {
const id = request.headers.get('req-id')
return topStorage.run({ id }, async () => {
const nested = await getSomeData(id)
return Response.json({ ...nested, ...topStorage.getStore() })
})
}
async function getSomeData(id) {
const nestedStorage = new AsyncLocalStorage()
return nestedStorage.run('nested-' + id, async () => {
try {
const response = await fetch('https://example.vercel.sh')
await response.text()
} finally {
return { nestedId: nestedStorage.getStore() }
}
})
}
`,
expectResponse: (response, id) =>
expect(response).toMatchObject({
status: 200,
json: { id: id, nestedId: `nested-${id}` },
}),
},
]

afterEach(() => next.destroy())

it.each(cases)(
'cans use $title per request',
async ({ code, expectResponse }) => {
next = await createNext({
files: {
'pages/index.js': `
export default function () { return <div>Hello, world!</div> }
`,
'pages/api/async.js': code,
},
})
const ids = Array.from({ length: 100 }, (_, i) => `req-${i}`)

const responses = await Promise.all(
ids.map((id) =>
fetchViaHTTP(
next.url,
'/api/async',
{},
{ headers: { 'req-id': id } }
).then((response) =>
response.headers.get('content-type')?.startsWith('application/json')
? response.json().then((json) => ({
status: response.status,
json,
text: null,
}))
: response.text().then((text) => ({
status: response.status,
json: null,
text,
}))
)
)
)
const rankById = new Map(ids.map((id, rank) => [id, rank]))

const errors: Error[] = []
for (const [rank, response] of responses.entries()) {
try {
expectResponse(response, ids[rank])
} catch (error) {
const received = response.json?.id
console.log(
`response #${rank} has id from request #${rankById.get(received)}`
)
errors.push(error as Error)
}
}
if (errors.length) {
throw errors[0]
}
}
)
})

0 comments on commit 6415534

Please sign in to comment.