Skip to content

Commit

Permalink
Merge branch 'canary' into build/external-store-bundle
Browse files Browse the repository at this point in the history
  • Loading branch information
kodiakhq[bot] committed May 26, 2022
2 parents 994506c + 6a5bdb5 commit fe6229e
Show file tree
Hide file tree
Showing 6 changed files with 59 additions and 78 deletions.
1 change: 1 addition & 0 deletions .github/pull_request_template.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
12 changes: 11 additions & 1 deletion contributing.md
Original file line number Diff line number Diff line change
Expand Up @@ -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`.
Expand Down
6 changes: 2 additions & 4 deletions examples/cms-kontent/components/image.js
Original file line number Diff line number Diff line change
@@ -1,22 +1,20 @@
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
}

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)
Expand Down
3 changes: 2 additions & 1 deletion examples/cms-kontent/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"
}
}
111 changes: 43 additions & 68 deletions packages/next/client/app-index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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'

/// <reference types="react-dom/experimental" />

Expand Down Expand Up @@ -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

Expand All @@ -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])
}
Expand All @@ -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.
Expand Down Expand Up @@ -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
}
Expand All @@ -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 <ServerRoot cacheKey={cacheKey} />
}

return (
<RefreshContext.Provider value={refreshCache}>
<ServerRoot cacheKey={cacheKey} serialized={__flight_serialized__} />
</RefreshContext.Provider>
)
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({})
Expand Down
4 changes: 0 additions & 4 deletions packages/next/server/app-render.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -372,10 +372,6 @@ export async function renderToHTML(
}
)

// const serverComponentProps = query.__props__
// ? JSON.parse(query.__props__ as string)
// : undefined

const jsxStyleRegistry = createStyleRegistry()

const styledJsxFlushEffect = () => {
Expand Down

0 comments on commit fe6229e

Please sign in to comment.