diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md
index dd585c5365423..83e70b093047b 100644
--- a/.github/pull_request_template.md
+++ b/.github/pull_request_template.md
@@ -22,3 +22,4 @@ Choose the right checklist for the change that you're making:
## Documentation / Examples
- [ ] Make sure the linting passes by running `yarn lint`
+- [ ] The examples guidelines are followed from [our contributing doc](https://github.com/vercel/next.js/blob/canary/contributing.md#adding-examples)
diff --git a/contributing.md b/contributing.md
index 33251e0633e45..4ac04d261749f 100644
--- a/contributing.md
+++ b/contributing.md
@@ -265,7 +265,17 @@ Below are the steps to add a new link:
## Adding examples
-When you add an example to the [examples](examples) directory, don’t forget to add a `README.md` file with the following format:
+When you add an example to the [examples](examples) directory, please follow these guidelines to ensure high quality examples:
+
+- TypeScript should be leveraged for new examples (no need for separate JavaScript and TypeScript examples)
+- Examples should not add custom ESLint configuration (we have specific templates for ESLint)
+- If API routes aren't used in an example, they should be omitted
+- If an example exists for a certain library and you would like to showcase a specific feature of that library, the existing example should be updated (instead of adding a new example)
+- Package manager specific config should not be added (e.g. `resolutions` in `package.json`)
+- In `package.json` the version of `next` (and `eslint-config-next`) should be `latest`
+- In `package.json` the dependency versions should be up-to-date
+
+Also don’t forget to add a `README.md` file with the following format:
- Replace `DIRECTORY_NAME` with the directory name you’re adding.
- Fill in `Example Name` and `Description`.
diff --git a/examples/cms-kontent/components/image.js b/examples/cms-kontent/components/image.js
index 60c4b11de7558..7b47e9176ad06 100644
--- a/examples/cms-kontent/components/image.js
+++ b/examples/cms-kontent/components/image.js
@@ -1,8 +1,6 @@
import NextImage from 'next/image'
import { transformImageUrl } from '@kentico/kontent-delivery'
-const KONTENT_ASSET_HOSTNAME_REGEX = /.kc-usercontent.com$/
-
const getLoader = (src) => {
return srcIsKontentAsset(src) ? kontentImageLoader : undefined
}
@@ -10,13 +8,13 @@ const getLoader = (src) => {
const srcIsKontentAsset = (src) => {
try {
const { hostname } = new URL(src)
- return KONTENT_ASSET_HOSTNAME_REGEX.test(hostname)
+ return hostname.endsWith('.kc-usercontent.com')
} catch {
return false
}
}
-const kontentImageLoader = ({ src, width, quality = 100 }) => {
+const kontentImageLoader = ({ src, width, quality = 75 }) => {
return new transformImageUrl(src)
.withWidth(width)
.withQuality(quality)
diff --git a/examples/cms-kontent/package.json b/examples/cms-kontent/package.json
index 81ccbfbfb88d3..79665f418ac6a 100644
--- a/examples/cms-kontent/package.json
+++ b/examples/cms-kontent/package.json
@@ -19,6 +19,7 @@
"devDependencies": {
"autoprefixer": "10.4.7",
"postcss": "8.4.14",
- "tailwindcss": "^3.0.15"
+ "tailwindcss": "^3.0.15",
+ "tslib": "2.4.0"
}
}
diff --git a/packages/next/client/app-index.tsx b/packages/next/client/app-index.tsx
index c16884831644c..c0b958017bc3b 100644
--- a/packages/next/client/app-index.tsx
+++ b/packages/next/client/app-index.tsx
@@ -3,9 +3,11 @@ import '../build/polyfills/polyfill-module'
// @ts-ignore react-dom/client exists when using React 18
import ReactDOMClient from 'react-dom/client'
// @ts-ignore startTransition exists when using React 18
-import React, { useState, startTransition } from 'react'
-import { RefreshContext } from './streaming/refresh'
-import { createFromFetch } from 'next/dist/compiled/react-server-dom-webpack'
+import React from 'react'
+import {
+ createFromFetch,
+ createFromReadableStream,
+} from 'next/dist/compiled/react-server-dom-webpack'
///
@@ -36,7 +38,8 @@ const getCacheKey = () => {
const encoder = new TextEncoder()
let initialServerDataBuffer: string[] | undefined = undefined
-let initialServerDataWriter: WritableStreamDefaultWriter | undefined = undefined
+let initialServerDataWriter: ReadableStreamDefaultController | undefined =
+ undefined
let initialServerDataLoaded = false
let initialServerDataFlushed = false
@@ -48,7 +51,7 @@ function nextServerDataCallback(seg: [number, string, string]) {
throw new Error('Unexpected server data: missing bootstrap script.')
if (initialServerDataWriter) {
- initialServerDataWriter.write(encoder.encode(seg[2]))
+ initialServerDataWriter.enqueue(encoder.encode(seg[2]))
} else {
initialServerDataBuffer.push(seg[2])
}
@@ -63,19 +66,19 @@ function nextServerDataCallback(seg: [number, string, string]) {
// Hence, we use two variables `initialServerDataLoaded` and
// `initialServerDataFlushed` to make sure the writer will be closed and
// `initialServerDataBuffer` will be cleared in the right time.
-function nextServerDataRegisterWriter(writer: WritableStreamDefaultWriter) {
+function nextServerDataRegisterWriter(ctr: ReadableStreamDefaultController) {
if (initialServerDataBuffer) {
initialServerDataBuffer.forEach((val) => {
- writer.write(encoder.encode(val))
+ ctr.enqueue(encoder.encode(val))
})
if (initialServerDataLoaded && !initialServerDataFlushed) {
- writer.close()
+ ctr.close()
initialServerDataFlushed = true
initialServerDataBuffer = undefined
}
}
- initialServerDataWriter = writer
+ initialServerDataWriter = ctr
}
// When `DOMContentLoaded`, we can close all pending writers to finish hydration.
@@ -104,54 +107,26 @@ function createResponseCache() {
}
const rscCache = createResponseCache()
-function fetchFlight(href: string, props?: any) {
- const url = new URL(href, location.origin)
- const searchParams = url.searchParams
- searchParams.append('__flight__', '1')
- if (props) {
- searchParams.append('__props__', JSON.stringify(props))
- }
- return fetch(url.toString())
-}
-
-function useServerResponse(cacheKey: string, serialized?: string) {
- let response = rscCache.get(cacheKey)
+function useInitialServerResponse(cacheKey: string) {
+ const response = rscCache.get(cacheKey)
if (response) return response
- if (initialServerDataBuffer) {
- const t = new TransformStream()
- const writer = t.writable.getWriter()
- response = createFromFetch(Promise.resolve({ body: t.readable }))
- nextServerDataRegisterWriter(writer)
- } else {
- const fetchPromise = serialized
- ? (() => {
- const t = new TransformStream()
- const writer = t.writable.getWriter()
- writer.ready.then(() => {
- writer.write(new TextEncoder().encode(serialized))
- })
- return Promise.resolve({ body: t.readable })
- })()
- : fetchFlight(getCacheKey())
- response = createFromFetch(fetchPromise)
- }
+ const readable = new ReadableStream({
+ start(controller) {
+ nextServerDataRegisterWriter(controller)
+ },
+ })
+ const newResponse = createFromReadableStream(readable)
- rscCache.set(cacheKey, response)
- return response
+ rscCache.set(cacheKey, newResponse)
+ return newResponse
}
-const ServerRoot = ({
- cacheKey,
- serialized,
-}: {
- cacheKey: string
- serialized?: string
-}) => {
+const ServerRoot = ({ cacheKey }: { cacheKey: string }) => {
React.useEffect(() => {
rscCache.delete(cacheKey)
})
- const response = useServerResponse(cacheKey, serialized)
+ const response = useInitialServerResponse(cacheKey)
const root = response.readRoot()
return root
}
@@ -171,27 +146,27 @@ function Root({ children }: React.PropsWithChildren<{}>): React.ReactElement {
return children as React.ReactElement
}
-const RSCComponent = (props: any) => {
+const RSCComponent = () => {
const cacheKey = getCacheKey()
- const { __flight_serialized__ } = props
- const [, dispatch] = useState({})
- const rerender = () => dispatch({})
- // If there is no cache, or there is serialized data already
- function refreshCache(nextProps: any) {
- startTransition(() => {
- const currentCacheKey = getCacheKey()
- const response = createFromFetch(fetchFlight(currentCacheKey, nextProps))
-
- rscCache.set(currentCacheKey, response)
- rerender()
- })
- }
+ return
+}
- return (
-
-
-
- )
+function fetchFlight(href: string) {
+ const url = new URL(href, location.origin)
+ const searchParams = url.searchParams
+ searchParams.append('__flight__', '1')
+
+ return fetch(url.toString())
+}
+
+function useServerResponse(cacheKey: string) {
+ let response = rscCache.get(cacheKey)
+ if (response) return response
+
+ response = createFromFetch(fetchFlight(getCacheKey()))
+
+ rscCache.set(cacheKey, response)
+ return response
}
const AppRouterContext = React.createContext({})
diff --git a/packages/next/server/app-render.tsx b/packages/next/server/app-render.tsx
index 04e01c4d8d1c9..7dcbd2046a4aa 100644
--- a/packages/next/server/app-render.tsx
+++ b/packages/next/server/app-render.tsx
@@ -372,10 +372,6 @@ export async function renderToHTML(
}
)
- // const serverComponentProps = query.__props__
- // ? JSON.parse(query.__props__ as string)
- // : undefined
-
const jsxStyleRegistry = createStyleRegistry()
const styledJsxFlushEffect = () => {