Skip to content

Commit

Permalink
Merge branch 'canary' into fix/revalidate-stuff
Browse files Browse the repository at this point in the history
  • Loading branch information
ijjk committed Oct 25, 2022
2 parents 03b8db5 + 67c802a commit c298638
Show file tree
Hide file tree
Showing 28 changed files with 297 additions and 20 deletions.
2 changes: 1 addition & 1 deletion packages/next/build/entries.ts
Expand Up @@ -521,7 +521,7 @@ export function finalizeEntrypoint({
name !== CLIENT_STATIC_FILES_RUNTIME_REACT_REFRESH
) {
// TODO-APP: this is a temporary fix. @shuding is going to change the handling of server components
if (hasAppDir && entry.import.includes('flight')) {
if (hasAppDir && entry.import.includes('next-flight-client-entry-loader')) {
return {
dependOn: CLIENT_STATIC_FILES_RUNTIME_MAIN_APP,
...entry,
Expand Down
4 changes: 2 additions & 2 deletions packages/next/build/webpack-config.ts
Expand Up @@ -1618,7 +1618,7 @@ export default async function getBaseWebpackConfig(
},
module: {
rules: [
...(hasAppDir && !isClient && !isEdgeServer
...(hasAppDir && !isClient
? [
{
issuerLayer: WEBPACK_LAYERS.server,
Expand All @@ -1643,7 +1643,7 @@ export default async function getBaseWebpackConfig(
// If missing the alias override here, the default alias will be used which aliases
// react to the direct file path, not the package name. In that case the condition
// will be ignored completely.
react: 'next/dist/compiled/react',
react: 'next/dist/compiled/react/react.shared-subset',
'react-dom$':
'next/dist/compiled/react-dom/server-rendering-stub',
},
Expand Down
1 change: 1 addition & 0 deletions packages/next/build/webpack/loaders/next-app-loader.ts
Expand Up @@ -13,6 +13,7 @@ export const FILE_TYPES = {
template: 'template',
error: 'error',
loading: 'loading',
head: 'head',
'not-found': 'not-found',
} as const

Expand Down
Expand Up @@ -231,7 +231,7 @@ export class FlightClientEntryPlugin {

function collectModule(entryName: string, mod: any) {
const resource = mod.resource
const modId = resource // compilation.chunkGraph.getModuleId(mod) + ''
const modId = resource
if (modId) {
if (regexCSS.test(modId)) {
cssImportsForChunk[entryName].push(modId)
Expand Down Expand Up @@ -361,9 +361,8 @@ export class FlightClientEntryPlugin {
!rawRequest.startsWith(APP_DIR_ALIAS)

const modRequest: string | undefined = isLocal
? rawRequest // compilation.chunkGraph.getModuleId(mod) + ''
? rawRequest
: mod.resourceResolveData?.path + mod.resourceResolveData?.query
// console.log('modId:after', modRequest)

// Ensure module is not walked again if it's already been visited
if (!visitedBySegment[layoutOrPageRequest]) {
Expand Down
Expand Up @@ -186,10 +186,8 @@ export class FlightManifestPlugin {
context,
mod.resourceResolveData?.path || resource
)
// if (resource.includes('script'))
// console.log('ssrNamedModuleId', ssrNamedModuleId, modId)

if (!ssrNamedModuleId.startsWith('.'))
// TODO use getModuleId instead
ssrNamedModuleId = `./${ssrNamedModuleId.replace(/\\/g, '/')}`

if (isCSSModule) {
Expand Down
4 changes: 3 additions & 1 deletion packages/next/client/app-index.tsx
Expand Up @@ -7,7 +7,6 @@ import { createFromReadableStream } from 'next/dist/compiled/react-server-dom-we

import measureWebVitals from './performance-relayer'
import { HeadManagerContext } from '../shared/lib/head-manager-context'
import HotReload from './components/react-dev-overlay/hot-reloader-client'
import { GlobalLayoutRouterContext } from '../shared/lib/app-router-context'

/// <reference types="react-dom/experimental" />
Expand Down Expand Up @@ -178,6 +177,9 @@ export function hydrate() {
if (process.env.NODE_ENV !== 'production') {
const rootLayoutMissingTagsError = (self as any)
.__next_root_layout_missing_tags_error
const HotReload: typeof import('./components/react-dev-overlay/hot-reloader-client').default =
require('./components/react-dev-overlay/hot-reloader-client')
.default as typeof import('./components/react-dev-overlay/hot-reloader-client').default

// Don't try to hydrate if root layout is missing required tags, render error instead
if (rootLayoutMissingTagsError) {
Expand Down
28 changes: 28 additions & 0 deletions packages/next/server/app-render.tsx
Expand Up @@ -914,19 +914,27 @@ export async function renderToHTMLOrFlight(
template,
error,
loading,
head,
page,
'not-found': notFound,
},
],
parentParams,
firstItem,
rootLayoutIncluded,
collectedHeads = [],
}: {
createSegmentPath: CreateSegmentPath
loaderTree: LoaderTree
parentParams: { [key: string]: any }
rootLayoutIncluded?: boolean
firstItem?: boolean
collectedHeads?: Array<
(ctx: {
params?: Record<string, string | string[]>
searchParams?: Record<string, string | string[]>
}) => Promise<React.ElementType>
>
}): Promise<{ Component: React.ComponentType }> => {
// TODO-APP: enable stylesheet per layout/page
const stylesheets: string[] = layoutOrPagePath
Expand All @@ -950,6 +958,7 @@ export async function renderToHTMLOrFlight(
: React.Fragment
const ErrorComponent = error ? await interopDefault(error()) : undefined
const Loading = loading ? await interopDefault(loading()) : undefined
const Head = head ? await interopDefault(head()) : undefined
const isLayout = typeof layout !== 'undefined'
const isPage = typeof page !== 'undefined'
const layoutOrPageMod = isLayout
Expand Down Expand Up @@ -1055,6 +1064,17 @@ export async function renderToHTMLOrFlight(
// Resolve the segment param
const actualSegment = segmentParam ? segmentParam.treeSegment : segment

// collect head pieces
if (typeof Head === 'function') {
collectedHeads.push(() =>
Head({
params: currentParams,
// TODO-APP: allow searchParams?
// ...(isPage ? { searchParams: query } : {}),
})
)
}

// This happens outside of rendering in order to eagerly kick off data fetching for layouts / the page further down
const parallelRouteMap = await Promise.all(
Object.keys(parallelRoutes).map(
Expand Down Expand Up @@ -1104,6 +1124,7 @@ export async function renderToHTMLOrFlight(
loaderTree: parallelRoutes[parallelRouteKey],
parentParams: currentParams,
rootLayoutIncluded: rootLayoutIncludedAtThisLevelOrAbove,
collectedHeads,
})

const childProp: ChildProp = {
Expand Down Expand Up @@ -1162,6 +1183,12 @@ export async function renderToHTMLOrFlight(
// Add extra cache busting (DEV only) for https://github.com/vercel/next.js/issues/5860
// See also https://bugs.webkit.org/show_bug.cgi?id=187726
const cacheBustingUrlSuffix = dev ? `?ts=${Date.now()}` : ''
let HeadTags
if (rootLayoutAtThisLevel) {
// TODO: iterate HeadTag children and add a data-path attribute
// so that we can remove elements on client-transition
HeadTags = collectedHeads[collectedHeads.length - 1] as any
}

return (
<>
Expand Down Expand Up @@ -1202,6 +1229,7 @@ export async function renderToHTMLOrFlight(
// Query is only provided to page
{...(isPage ? { searchParams: query } : {})}
/>
{HeadTags ? <HeadTags /> : null}
</>
)
},
Expand Down
4 changes: 1 addition & 3 deletions test/.stats-app/app/app-edge-ssr/page.js
Expand Up @@ -2,6 +2,4 @@ export default function page() {
return 'edge-ssr'
}

export const config = {
runtime: 'experimental-edge',
}
export const runtime = 'experimental-edge'
8 changes: 8 additions & 0 deletions test/.stats-app/next.config.js
Expand Up @@ -3,3 +3,11 @@ module.exports = {
appDir: true,
},
}

// For development: analyze the bundled chunks for stats app
if (process.env.ANALYZE) {
const withBundleAnalyzer = require('@next/bundle-analyzer')({
enabled: true,
})
module.exports = withBundleAnalyzer(module.exports)
}
3 changes: 2 additions & 1 deletion test/e2e/app-dir/back-button-download-bug.test.ts
Expand Up @@ -3,7 +3,8 @@ import { NextInstance } from 'test/lib/next-modes/base'
import path from 'path'
import webdriver from 'next-webdriver'

describe('app-dir back button download bug', () => {
// TODO-APP: fix test as it's failing randomly
describe.skip('app-dir back button download bug', () => {
if ((global as any).isNextDeploy) {
it('should skip next deploy for now', () => {})
return
Expand Down
4 changes: 2 additions & 2 deletions test/e2e/app-dir/back-button-download-bug/app/page.js
Expand Up @@ -4,8 +4,8 @@ export default function Home() {
return (
<>
<h1 id="home-page">Home!</h1>
<Link href="/post/1">
<a id="to-post-1">To post 1</a>
<Link href="/post/1" id="to-post-1">
To post 1
</Link>
<Link href="/">To /</Link>
</>
Expand Down
97 changes: 97 additions & 0 deletions test/e2e/app-dir/head.test.ts
@@ -0,0 +1,97 @@
import path from 'path'
import cheerio from 'cheerio'
import { createNext, FileRef } from 'e2e-utils'
import { NextInstance } from 'test/lib/next-modes/base'
import { renderViaHTTP } from 'next-test-utils'

describe('app dir head', () => {
if ((global as any).isNextDeploy) {
it('should skip next deploy for now', () => {})
return
}

if (process.env.NEXT_TEST_REACT_VERSION === '^17') {
it('should skip for react v17', () => {})
return
}
let next: NextInstance

function runTests() {
beforeAll(async () => {
next = await createNext({
files: new FileRef(path.join(__dirname, 'head')),
dependencies: {
react: 'experimental',
'react-dom': 'experimental',
},
skipStart: true,
})

await next.start()
})
afterAll(() => next.destroy())

it('should use head from index page', async () => {
const html = await renderViaHTTP(next.url, '/')
const $ = cheerio.load(html)
const headTags = $('head').children().toArray()

expect(headTags.find((el) => el.attribs.src === '/hello.js')).toBeTruthy()
expect(
headTags.find((el) => el.attribs.src === '/another.js')
).toBeTruthy()
})

it('should use correct head for /blog', async () => {
const html = await renderViaHTTP(next.url, '/blog')
const $ = cheerio.load(html)
const headTags = $('head').children().toArray()

expect(headTags.find((el) => el.attribs.src === '/hello3.js')).toBeFalsy()
expect(
headTags.find((el) => el.attribs.src === '/hello1.js')
).toBeTruthy()
expect(
headTags.find((el) => el.attribs.src === '/hello2.js')
).toBeTruthy()
expect(
headTags.find((el) => el.attribs.src === '/another.js')
).toBeTruthy()
})

it('should use head from layout when not on page', async () => {
const html = await renderViaHTTP(next.url, '/blog/about')
const $ = cheerio.load(html)
const headTags = $('head').children().toArray()

expect(
headTags.find((el) => el.attribs.src === '/hello1.js')
).toBeTruthy()
expect(
headTags.find((el) => el.attribs.src === '/hello2.js')
).toBeTruthy()
expect(
headTags.find((el) => el.attribs.src === '/another.js')
).toBeTruthy()
})

it('should pass params to head for dynamic path', async () => {
const html = await renderViaHTTP(next.url, '/blog/post-1')
const $ = cheerio.load(html)
const headTags = $('head').children().toArray()

expect(
headTags.find(
(el) =>
el.attribs.src === '/hello3.js' &&
el.attribs['data-slug'] === 'post-1'
)
).toBeTruthy()
expect(
headTags.find((el) => el.attribs.src === '/another.js')
).toBeTruthy()
})
}

runTests()
})
8 changes: 8 additions & 0 deletions test/e2e/app-dir/head/app/blog/[slug]/head.js
@@ -0,0 +1,8 @@
export default async function Head({ params }) {
return (
<>
<script async src="/hello3.js" data-slug={params.slug} />
<title>{`hello from dynamic blog page ${params.slug}`}</title>
</>
)
}
13 changes: 13 additions & 0 deletions test/e2e/app-dir/head/app/blog/[slug]/page.js
@@ -0,0 +1,13 @@
import Link from 'next/link'

export default function Page() {
return (
<>
<p id="page">dynamic blog page</p>
<Link href="/" id="to-index">
to /
</Link>
<br />
</>
)
}
13 changes: 13 additions & 0 deletions test/e2e/app-dir/head/app/blog/about/page.js
@@ -0,0 +1,13 @@
import Link from 'next/link'

export default function Page() {
return (
<>
<p id="page">blog about page</p>
<Link href="/" id="to-index">
to /
</Link>
<br />
</>
)
}
10 changes: 10 additions & 0 deletions test/e2e/app-dir/head/app/blog/head.js
@@ -0,0 +1,10 @@
export default async function Head() {
return (
<>
<script async src="/hello1.js" />
<script async src="/hello2.js" />
<title>hello from blog layout</title>
<meta name="description" content="a neat blog" />
</>
)
}
8 changes: 8 additions & 0 deletions test/e2e/app-dir/head/app/blog/layout.js
@@ -0,0 +1,8 @@
export default function Layout({ children }) {
return (
<>
<p id="layout">blog layout</p>
{children}
</>
)
}
13 changes: 13 additions & 0 deletions test/e2e/app-dir/head/app/blog/page.js
@@ -0,0 +1,13 @@
import Link from 'next/link'

export default function Page() {
return (
<>
<p id="page">blog page</p>
<Link href="/" id="to-index">
to /
</Link>
<br />
</>
)
}
9 changes: 9 additions & 0 deletions test/e2e/app-dir/head/app/head.js
@@ -0,0 +1,9 @@
export default async function Head() {
return (
<>
<script async src="/hello.js" />
<title>hello from index</title>
<meta name="description" content="an index page" />
</>
)
}
10 changes: 10 additions & 0 deletions test/e2e/app-dir/head/app/layout.js
@@ -0,0 +1,10 @@
export default function Root({ children }) {
return (
<html lang="en">
<head>
<script async src="/another.js" />
</head>
<body>{children}</body>
</html>
)
}

0 comments on commit c298638

Please sign in to comment.