Skip to content

Commit 37b19b4

Browse files
authored
Merge branch 'canary' into add/manifest-check
2 parents 5ae5a33 + 51a2a02 commit 37b19b4

File tree

11 files changed

+206
-30
lines changed

11 files changed

+206
-30
lines changed

packages/next/build/index.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -930,6 +930,22 @@ export default async function build(
930930
return returnValue
931931
})
932932

933+
if (isNextImageImported) {
934+
try {
935+
if (process.env.NEXT_SHARP_PATH) {
936+
require(process.env.NEXT_SHARP_PATH)
937+
} else {
938+
require.resolve('sharp', {
939+
paths: [path.join(dir, 'node_modules')],
940+
})
941+
}
942+
} catch (e) {
943+
Log.warn(
944+
'Detected `next/image` usage without `sharp`. https://nextjs.org/docs/messages/sharp-missing-in-production'
945+
)
946+
}
947+
}
948+
933949
if (customAppGetInitialProps) {
934950
console.warn(
935951
chalk.bold.yellow(`Warning: `) +

packages/next/client/image.tsx

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -57,13 +57,6 @@ type OnLoadingComplete = (result: {
5757

5858
type ImgElementStyle = NonNullable<JSX.IntrinsicElements['img']['style']>
5959

60-
interface StaticImageData {
61-
src: string
62-
height: number
63-
width: number
64-
blurDataURL?: string
65-
}
66-
6760
interface StaticRequire {
6861
default: StaticImageData
6962
}
@@ -607,6 +600,7 @@ export default function Image({
607600
loader,
608601
})}
609602
decoding="async"
603+
data-nimg
610604
style={imgStyle}
611605
className={className}
612606
/>
@@ -616,6 +610,7 @@ export default function Image({
616610
{...rest}
617611
{...imgAttributes}
618612
decoding="async"
613+
data-nimg
619614
className={className}
620615
ref={(img) => {
621616
setRef(img)
@@ -640,9 +635,9 @@ export default function Image({
640635
rel="preload"
641636
as="image"
642637
href={imgAttributes.srcSet ? undefined : imgAttributes.src}
643-
// @ts-ignore: imagesrcset is not yet in the link element type
638+
// @ts-ignore: imagesrcset is not yet in the link element type.
644639
imagesrcset={imgAttributes.srcSet}
645-
// @ts-ignore: imagesizes is not yet in the link element type
640+
// @ts-ignore: imagesizes is not yet in the link element type.
646641
imagesizes={imgAttributes.sizes}
647642
></link>
648643
</Head>

packages/next/server/config-shared.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,7 @@ export type NextConfig = { [key: string]: any } & {
113113
staticPageGenerationTimeout?: number
114114
pageDataCollectionTimeout?: number
115115
isrMemoryCacheSize?: number
116+
concurrentFeatures?: boolean
116117
}
117118
}
118119

@@ -185,6 +186,7 @@ export const defaultConfig: NextConfig = {
185186
pageDataCollectionTimeout: 60,
186187
// default to 50MB limit
187188
isrMemoryCacheSize: 50 * 1024 * 1024,
189+
concurrentFeatures: false,
188190
},
189191
future: {
190192
strictPostcssConfiguration: false,

packages/next/server/next-server.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,7 @@ export default class Server {
181181
defaultLocale?: string
182182
domainLocales?: DomainLocale[]
183183
distDir: string
184+
concurrentFeatures?: boolean
184185
}
185186
private compression?: Middleware
186187
private incrementalCache: IncrementalCache
@@ -241,6 +242,7 @@ export default class Server {
241242
.disableOptimizedLoading,
242243
domainLocales: this.nextConfig.i18n?.domains,
243244
distDir: this.distDir,
245+
concurrentFeatures: this.nextConfig.experimental.concurrentFeatures,
244246
}
245247

246248
// Only the `publicRuntimeConfig` key is exposed to the client side

packages/next/server/render.tsx

Lines changed: 51 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
import { IncomingMessage, ServerResponse } from 'http'
22
import { ParsedUrlQuery } from 'querystring'
3+
import { PassThrough } from 'stream'
34
import React from 'react'
4-
import { renderToStaticMarkup, renderToString } from 'react-dom/server'
5+
import * as ReactDOMServer from 'react-dom/server'
56
import { warn } from '../build/output/log'
67
import { UnwrapPromise } from '../lib/coalesced-function'
78
import {
@@ -43,6 +44,7 @@ import {
4344
loadGetInitialProps,
4445
NextComponentType,
4546
RenderPage,
47+
RenderPageResult,
4648
} from '../shared/lib/utils'
4749
import {
4850
tryGetPreviewData,
@@ -190,6 +192,7 @@ export type RenderOptsPartial = {
190192
domainLocales?: DomainLocale[]
191193
disableOptimizedLoading?: boolean
192194
requireStaticHTML?: boolean
195+
concurrentFeatures?: boolean
193196
}
194197

195198
export type RenderOpts = LoadComponentsReturnType & RenderOptsPartial
@@ -263,7 +266,7 @@ function renderDocument(
263266
): string {
264267
return (
265268
'<!DOCTYPE html>' +
266-
renderToStaticMarkup(
269+
ReactDOMServer.renderToStaticMarkup(
267270
<AmpStateContext.Provider value={ampState}>
268271
{Document.renderDocument(Document, {
269272
__NEXT_DATA__: {
@@ -408,6 +411,7 @@ export async function renderToHTML(
408411
previewProps,
409412
basePath,
410413
devOnlyCacheBusterQueryString,
414+
concurrentFeatures,
411415
} = renderOpts
412416

413417
const getFontDefinition = (url: string): string => {
@@ -626,6 +630,8 @@ export async function renderToHTML(
626630
let head: JSX.Element[] = defaultHead(inAmpMode)
627631

628632
let scriptLoader: any = {}
633+
const nextExport =
634+
!isSSG && (renderOpts.nextExport || (dev && (isAutoExport || isFallback)))
629635

630636
const AppContainer = ({ children }: any) => (
631637
<RouterContext.Provider value={router}>
@@ -991,11 +997,45 @@ export async function renderToHTML(
991997
}
992998
}
993999

1000+
// TODO: Support SSR streaming of Suspense.
1001+
const renderToString = concurrentFeatures
1002+
? (element: React.ReactElement) =>
1003+
new Promise<string>((resolve, reject) => {
1004+
const stream = new PassThrough()
1005+
const buffers: Buffer[] = []
1006+
stream.on('data', (chunk) => {
1007+
buffers.push(chunk)
1008+
})
1009+
stream.once('end', () => {
1010+
resolve(Buffer.concat(buffers).toString('utf-8'))
1011+
})
1012+
1013+
const {
1014+
abort,
1015+
startWriting,
1016+
} = (ReactDOMServer as any).pipeToNodeWritable(element, stream, {
1017+
onError(error: Error) {
1018+
abort()
1019+
reject(error)
1020+
},
1021+
onCompleteAll() {
1022+
startWriting()
1023+
},
1024+
})
1025+
})
1026+
: ReactDOMServer.renderToString
1027+
9941028
const renderPage: RenderPage = (
9951029
options: ComponentsEnhancer = {}
996-
): { html: string; head: any } => {
1030+
): RenderPageResult | Promise<RenderPageResult> => {
9971031
if (ctx.err && ErrorDebug) {
998-
return { html: renderToString(<ErrorDebug error={ctx.err} />), head }
1032+
const htmlOrPromise = renderToString(<ErrorDebug error={ctx.err} />)
1033+
return typeof htmlOrPromise === 'string'
1034+
? { html: htmlOrPromise, head }
1035+
: htmlOrPromise.then((html) => ({
1036+
html,
1037+
head,
1038+
}))
9991039
}
10001040

10011041
if (dev && (props.router || props.Component)) {
@@ -1009,13 +1049,17 @@ export async function renderToHTML(
10091049
Component: EnhancedComponent,
10101050
} = enhanceComponents(options, App, Component)
10111051

1012-
const html = renderToString(
1052+
const htmlOrPromise = renderToString(
10131053
<AppContainer>
10141054
<EnhancedApp Component={EnhancedComponent} router={router} {...props} />
10151055
</AppContainer>
10161056
)
1017-
1018-
return { html, head }
1057+
return typeof htmlOrPromise === 'string'
1058+
? { html: htmlOrPromise, head }
1059+
: htmlOrPromise.then((html) => ({
1060+
html,
1061+
head,
1062+
}))
10191063
}
10201064
const documentCtx = { ...ctx, renderPage }
10211065
const docProps: DocumentInitialProps = await loadGetInitialProps(
@@ -1049,8 +1093,6 @@ export async function renderToHTML(
10491093
const hybridAmp = ampState.hybrid
10501094

10511095
const docComponentsRendered: DocumentProps['docComponentsRendered'] = {}
1052-
const nextExport =
1053-
!isSSG && (renderOpts.nextExport || (dev && (isAutoExport || isFallback)))
10541096

10551097
let html = renderDocument(Document, {
10561098
...renderOpts,
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
#!/usr/env node
2+
3+
module.exports = 789
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
#!/usr/env node
2+
3+
module.exports = 123
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
#!/usr/env node
2+
3+
export default 456
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
/**
2+
* Import hashbang modules.
3+
*/
4+
import js from '../cases/js.js'
5+
import cjs from '../cases/cjs.cjs'
6+
import mjs from '../cases/mjs.mjs'
7+
8+
const jsMsg = `JS: ${js}`
9+
const mjsMsg = `MJS: ${mjs}`
10+
const cjsMsg = `CJS: ${cjs}`
11+
12+
const Page = () => (
13+
<div>
14+
<h3>{jsMsg}</h3>
15+
<h3>{mjsMsg}</h3>
16+
<h3>{cjsMsg}</h3>
17+
</div>
18+
)
19+
20+
export default Page
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
/* eslint-env jest */
2+
3+
import { join } from 'path'
4+
import fs from 'fs-extra'
5+
import {
6+
renderViaHTTP,
7+
findPort,
8+
launchApp,
9+
killApp,
10+
nextBuild,
11+
nextStart,
12+
} from 'next-test-utils'
13+
14+
jest.setTimeout(1000 * 60 * 2)
15+
16+
let app
17+
let appPort
18+
const appDir = join(__dirname, '../')
19+
20+
function runTests() {
21+
describe('first-line hashbang (#!) parse', () => {
22+
it('should work for .js files', async () => {
23+
const html = await renderViaHTTP(appPort, '/')
24+
expect(html).toMatch('JS: 123')
25+
})
26+
27+
it('should work for .mjs files', async () => {
28+
const html = await renderViaHTTP(appPort, '/')
29+
expect(html).toMatch('MJS: 456')
30+
})
31+
32+
it('should work for .cjs files', async () => {
33+
const html = await renderViaHTTP(appPort, '/')
34+
expect(html).toMatch('CJS: 789')
35+
})
36+
})
37+
}
38+
39+
const nextConfig = join(appDir, 'next.config.js')
40+
41+
describe('Hashbang', () => {
42+
describe('dev mode', () => {
43+
beforeAll(async () => {
44+
appPort = await findPort()
45+
app = await launchApp(appDir, appPort)
46+
})
47+
afterAll(() => killApp(app))
48+
49+
runTests(true)
50+
})
51+
52+
describe('production mode', () => {
53+
beforeAll(async () => {
54+
await fs.remove(nextConfig)
55+
await nextBuild(appDir)
56+
appPort = await findPort()
57+
app = await nextStart(appDir, appPort)
58+
})
59+
afterAll(() => killApp(app))
60+
61+
runTests()
62+
})
63+
})

0 commit comments

Comments
 (0)