Remove experiment (#9945)

* Remove experiment

* Add back ssr-prepass for serverless test

* Update size as they both decreased

Co-authored-by: JJ Kasper <jj@jjsweb.site>
This commit is contained in:
Tim Neutkens 2020-01-04 17:40:18 +01:00 committed by GitHub
parent 8898b853e6
commit 1fd57d22d5
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
23 changed files with 38 additions and 408 deletions

View file

@ -88,7 +88,7 @@
"prettier": "1.19.1",
"react": "16.10.2",
"react-dom": "16.10.2",
"react-ssr-prepass": "1.0.7",
"react-ssr-prepass": "1.0.8",
"release": "6.0.1",
"request-promise-core": "1.1.2",
"rimraf": "2.6.3",

View file

@ -75,7 +75,6 @@ export function createEntrypoints(
buildId,
assetPrefix: config.assetPrefix,
generateEtags: config.generateEtags,
ampBindInitData: config.experimental.ampBindInitData,
canonicalBase: config.canonicalBase,
basePath: config.experimental.basePath,
}

View file

@ -445,21 +445,6 @@ export default async function getBaseWebpackConfig(
try {
res = resolveRequest(request, `${context}/`)
} catch (err) {
// This is a special case for the Next.js data experiment. This
// will be removed in the future.
// We're telling webpack to externalize a package that doesn't
// exist because we know it won't ever be used at runtime.
if (
request === 'react-ssr-prepass' &&
!config.experimental.ampBindInitData
) {
if (
context.replace(/\\/g, '/').includes('next-server/server')
) {
return callback(undefined, `commonjs ${request}`)
}
}
// If the request cannot be resolved, we need to tell webpack to
// "bundle" it so that webpack shows an error (that it cannot be
// resolved).
@ -520,19 +505,6 @@ export default async function getBaseWebpackConfig(
// When the 'serverless' target is used all node_modules will be compiled into the output bundles
// So that the 'serverless' bundles have 0 runtime dependencies
'@ampproject/toolbox-optimizer', // except this one
(context, request, callback) => {
if (
request === 'react-ssr-prepass' &&
!config.experimental.ampBindInitData
) {
// if it's the Next.js' require mark it as external
// since it's not used
if (context.replace(/\\/g, '/').includes('next-server/server')) {
return callback(undefined, `commonjs ${request}`)
}
}
return callback()
},
],
optimization: {
checkWasmTypes: false,
@ -868,7 +840,6 @@ export default async function getBaseWebpackConfig(
isDevelopment: dev,
isServer,
hasSupportCss: !!config.experimental.css,
hasExperimentalData: !!config.experimental.ampBindInitData,
assetPrefix: config.assetPrefix || '',
})

View file

@ -1,28 +0,0 @@
import curry from 'lodash.curry'
import path from 'path'
import { Configuration } from 'webpack'
import { unshiftLoader } from '../helpers'
import { ConfigurationContext, pipe } from '../utils'
export const experimentData = curry(function experimentData(
enabled: boolean,
ctx: ConfigurationContext,
config: Configuration
) {
if (!enabled) {
return config
}
if (ctx.isServer) {
return config
}
const fn = pipe(
unshiftLoader({
test: /\.(tsx|ts|js|mjs|jsx)$/,
include: [path.join(ctx.rootDirectory, 'data')],
use: 'next-data-loader',
})
)
return fn(config)
})

View file

@ -2,7 +2,6 @@ import webpack from 'webpack'
import { base } from './blocks/base'
import { css } from './blocks/css'
import { ConfigurationContext, pipe } from './utils'
import { experimentData } from './blocks/experiment-data'
export async function build(
config: webpack.Configuration,
@ -12,7 +11,6 @@ export async function build(
isDevelopment,
isServer,
hasSupportCss,
hasExperimentalData,
assetPrefix,
}: {
rootDirectory: string
@ -20,7 +18,6 @@ export async function build(
isDevelopment: boolean
isServer: boolean
hasSupportCss: boolean
hasExperimentalData: boolean
assetPrefix: string
}
): Promise<webpack.Configuration> {
@ -38,10 +35,6 @@ export async function build(
: '',
}
const fn = pipe(
base(ctx),
experimentData(hasExperimentalData, ctx),
css(hasSupportCss, ctx)
)
const fn = pipe(base(ctx), css(hasSupportCss, ctx))
return fn(config)
}

View file

@ -18,7 +18,6 @@ export type ServerlessLoaderQuery = {
absoluteErrorPath: string
buildId: string
assetPrefix: string
ampBindInitData: boolean | string
generateEtags: string
canonicalBase: string
basePath: string
@ -32,7 +31,6 @@ const nextServerlessLoader: loader.Loader = function() {
buildId,
canonicalBase,
assetPrefix,
ampBindInitData,
absoluteAppPath,
absoluteDocumentPath,
absoluteErrorPath,
@ -213,8 +211,6 @@ const nextServerlessLoader: loader.Loader = function() {
canonicalBase: "${canonicalBase}",
buildId: "${buildId}",
assetPrefix: "${assetPrefix}",
ampBindInitData: ${ampBindInitData === true ||
ampBindInitData === 'true'},
..._renderOpts
}
let sprData = false
@ -231,7 +227,6 @@ const nextServerlessLoader: loader.Loader = function() {
{
Component,
pageConfig: config,
dataOnly: req.headers && (req.headers.accept || '').indexOf('application/amp.bind+json') !== -1,
nextExport: fromExport
},
options,

View file

@ -12,9 +12,7 @@ import {
import PageLoader from './page-loader'
import * as envConfig from '../next-server/lib/runtime-config'
import { HeadManagerContext } from '../next-server/lib/head-manager-context'
import { DataManagerContext } from '../next-server/lib/data-manager-context'
import { RouterContext } from '../next-server/lib/router-context'
import { DataManager } from '../next-server/lib/data-manager'
import { parse as parseQs, stringify as stringifyQs } from 'querystring'
import { isDynamicRoute } from '../next-server/lib/router/utils/is-dynamic'
@ -45,9 +43,6 @@ const {
dynamicIds,
} = data
const d = JSON.parse(window.__NEXT_DATA__.dataManager)
export const dataManager = new DataManager(d)
const prefix = assetPrefix || ''
// With dynamic assetPrefix it's no longer possible to set assetPrefix at the build time
@ -384,11 +379,9 @@ function AppContainer({ children }) {
}
>
<RouterContext.Provider value={makePublicRouterInstance(router)}>
<DataManagerContext.Provider value={dataManager}>
<HeadManagerContext.Provider value={headManager.updateHead}>
{children}
</HeadManagerContext.Provider>
</DataManagerContext.Provider>
<HeadManagerContext.Provider value={headManager.updateHead}>
{children}
</HeadManagerContext.Provider>
</RouterContext.Provider>
</Container>
)

View file

@ -1,75 +0,0 @@
import { useContext } from 'react'
import { DataManagerContext } from '../next-server/lib/data-manager-context'
import { RouterContext } from '../next-server/lib/router-context'
import fetch from 'unfetch'
import { stringify } from 'querystring'
type Args = string | number | Array<string | number>
function generateArgsKey(args: Args[]) {
return args.reduce((a: string, b: Args): string => {
if (Array.isArray(b)) {
return a + generateArgsKey(b)
}
if (typeof b !== 'string' && typeof b !== 'number') {
throw new Error('arguments can only be string or number')
}
return a + b.toString()
}, '')
}
export function createHook(
fetcher: (...args: Args[]) => Promise<any>,
options: { key: string }
) {
if (!options.key) {
throw new Error('key not provided to createHook options.')
}
return function useData(...args: Array<string | number>) {
const router: import('../next-server/lib/router/router').NextRouter = useContext(
RouterContext
)
const dataManager: import('../next-server/lib/data-manager').DataManager = useContext(
DataManagerContext
)
const key = `${options.key}${generateArgsKey(args)}`
const existing = dataManager.get(key)
if (existing) {
if (existing.status === 'resolved') {
return existing.result
}
if (existing === 'mismatched-key') {
throw new Error(
'matching key was missing from returned data. make sure arguments match between the client and server'
)
}
}
// @ts-ignore webpack optimization
if (typeof window !== 'undefined') {
const res = fetch(router.route + '?' + stringify(router.query), {
headers: {
accept: 'application/amp.bind+json',
},
})
.then((res: any) => res.json())
.then((result: any) => {
const hasKey = result.some((pair: [string, any]) => pair[0] === key)
if (!hasKey) {
result = [[key, 'mismatched-key']]
}
dataManager.overwrite(result)
})
throw res
} else {
const res = fetcher(...args).then(result => {
dataManager.set(key, {
status: 'resolved',
result,
})
})
throw res
}
}
}

View file

@ -1,3 +0,0 @@
import * as React from 'react'
export const DataManagerContext: React.Context<any> = React.createContext(null)

View file

@ -1,21 +0,0 @@
export class DataManager {
data: Map<string, any>
constructor(data?: any) {
this.data = new Map(data)
}
getData() {
return this.data
}
get(key: string) {
return this.data.get(key)
}
set(key: string, value: any) {
this.data.set(key, value)
}
overwrite(data: any) {
this.data = new Map(data)
}
}

View file

@ -56,7 +56,6 @@ export type ComponentsEnhancer =
export type RenderPageResult = {
html: string
head?: Array<JSX.Element | null>
dataOnly?: true
}
export type RenderPage = (
@ -69,7 +68,6 @@ export type BaseContext = {
}
export type NEXT_DATA = {
dataManager: string
props: any
page: string
query: ParsedUrlQuery

View file

@ -36,7 +36,6 @@ const defaultConfig: { [key: string]: any } = {
},
exportTrailingSlash: false,
experimental: {
ampBindInitData: false,
cpus: Math.max(
1,
(Number(process.env.CIRCLE_NODE_TOTAL) ||

View file

@ -92,7 +92,6 @@ export default class Server {
buildId: string
renderOpts: {
poweredByHeader: boolean
ampBindInitData: boolean
staticMarkup: boolean
buildId: string
generateEtags: boolean
@ -141,7 +140,6 @@ export default class Server {
this.buildId = this.readBuildId()
this.renderOpts = {
ampBindInitData: this.nextConfig.experimental.ampBindInitData,
poweredByHeader: this.nextConfig.poweredByHeader,
canonicalBase: this.nextConfig.amp.canonicalBase,
documentMiddlewareEnabled: this.nextConfig.experimental
@ -743,13 +741,7 @@ export default class Server {
return this.render404(req, res, parsedUrl)
}
const html = await this.renderToHTML(req, res, pathname, query, {
dataOnly:
(this.renderOpts.ampBindInitData && Boolean(query.dataOnly)) ||
(req.headers &&
(req.headers.accept || '').indexOf('application/amp.bind+json') !==
-1),
})
const html = await this.renderToHTML(req, res, pathname, query, {})
// Request was ended by the user
if (html === null) {
return
@ -952,12 +944,10 @@ export default class Server {
query: ParsedUrlQuery = {},
{
amphtml,
dataOnly,
hasAmp,
}: {
amphtml?: boolean
hasAmp?: boolean
dataOnly?: boolean
} = {}
): Promise<string | null> {
return this.findPageComponents(pathname, query)
@ -971,7 +961,7 @@ export default class Server {
? { _nextSprData: query._nextSprData }
: query,
result,
{ ...this.renderOpts, amphtml, hasAmp, dataOnly }
{ ...this.renderOpts, amphtml, hasAmp }
)
},
err => {
@ -1003,7 +993,6 @@ export default class Server {
...this.renderOpts,
amphtml,
hasAmp,
dataOnly,
}
)
}

View file

@ -19,10 +19,8 @@ import {
import Head, { defaultHead } from '../lib/head'
// @ts-ignore types will be added later as it's an internal module
import Loadable from '../lib/loadable'
import { DataManagerContext } from '../lib/data-manager-context'
import { LoadableContext } from '../lib/loadable-context'
import { RouterContext } from '../lib/router-context'
import { DataManager } from '../lib/data-manager'
import { getPageFiles, BuildManifest } from './get-page-files'
import { AmpStateContext } from '../lib/amp-context'
import optimizeAmp from './optimize-amp'
@ -126,7 +124,6 @@ function render(
type RenderOpts = {
documentMiddlewareEnabled: boolean
ampBindInitData: boolean
staticMarkup: boolean
buildId: string
canonicalBase: string
@ -140,7 +137,6 @@ type RenderOpts = {
dev?: boolean
ampMode?: any
ampPath?: string
dataOnly?: boolean
inAmpMode?: boolean
hybridAmp?: boolean
buildManifest: BuildManifest
@ -164,7 +160,6 @@ type RenderOpts = {
function renderDocument(
Document: DocumentType,
{
dataManagerData,
props,
docProps,
pathname,
@ -193,7 +188,6 @@ function renderDocument(
bodyTags,
headTags,
}: RenderOpts & {
dataManagerData: string
props: any
docProps: DocumentInitialProps
pathname: string
@ -219,7 +213,6 @@ function renderDocument(
<AmpStateContext.Provider value={ampState}>
{Document.renderDocument(Document, {
__NEXT_DATA__: {
dataManager: dataManagerData,
props, // The result of getInitialProps
page: pathname, // The rendered page
query, // querystring parsed / passed by the user
@ -267,7 +260,6 @@ export async function renderToHTML(
err,
dev = false,
documentMiddlewareEnabled = false,
ampBindInitData = false,
staticMarkup = false,
ampPath = '',
App,
@ -399,11 +391,6 @@ export async function renderToHTML(
await DocumentMiddleware(ctx)
}
let dataManager: DataManager | undefined
if (ampBindInitData) {
dataManager = new DataManager()
}
const ampState = {
ampFirst: pageConfig.amp === true,
hasQuery: Boolean(query.amp),
@ -414,15 +401,13 @@ export async function renderToHTML(
const AppContainer = ({ children }: any) => (
<RouterContext.Provider value={router}>
<DataManagerContext.Provider value={dataManager}>
<AmpStateContext.Provider value={ampState}>
<LoadableContext.Provider
value={moduleName => reactLoadableModules.push(moduleName)}
>
{children}
</LoadableContext.Provider>
</AmpStateContext.Provider>
</DataManagerContext.Provider>
<AmpStateContext.Provider value={ampState}>
<LoadableContext.Provider
value={moduleName => reactLoadableModules.push(moduleName)}
>
{children}
</LoadableContext.Provider>
</AmpStateContext.Provider>
</RouterContext.Provider>
)
@ -525,87 +510,30 @@ export async function renderToHTML(
}
}
let renderPage: RenderPage
let renderPage: RenderPage = (
options: ComponentsEnhancer = {}
): { html: string; head: any } => {
const renderError = renderPageError()
if (renderError) return renderError
if (ampBindInitData) {
const ssrPrepass = require('react-ssr-prepass')
const {
App: EnhancedApp,
Component: EnhancedComponent,
} = enhanceComponents(options, App, Component)
renderPage = async (
options: ComponentsEnhancer = {}
): Promise<{ html: string; head: any; dataOnly?: true }> => {
const renderError = renderPageError()
if (renderError) return renderError
const {
App: EnhancedApp,
Component: EnhancedComponent,
} = enhanceComponents(options, App, Component)
const Application = () => (
<AppContainer>
<EnhancedApp
Component={EnhancedComponent}
router={router}
{...props}
/>
</AppContainer>
)
const element = <Application />
try {
return render(renderElementToString, element, ampState)
} catch (err) {
if (err && typeof err === 'object' && typeof err.then === 'function') {
await ssrPrepass(element)
if (renderOpts.dataOnly) {
return {
html: '',
head: [],
dataOnly: true,
}
} else {
return render(renderElementToString, element, ampState)
}
}
throw err
}
}
} else {
renderPage = (
options: ComponentsEnhancer = {}
): { html: string; head: any } => {
const renderError = renderPageError()
if (renderError) return renderError
const {
App: EnhancedApp,
Component: EnhancedComponent,
} = enhanceComponents(options, App, Component)
return render(
renderElementToString,
<AppContainer>
<EnhancedApp
Component={EnhancedComponent}
router={router}
{...props}
/>
</AppContainer>,
ampState
)
}
return render(
renderElementToString,
<AppContainer>
<EnhancedApp Component={EnhancedComponent} router={router} {...props} />
</AppContainer>,
ampState
)
}
const documentCtx = { ...ctx, renderPage }
const docProps = await loadGetInitialProps(Document, documentCtx)
// the response might be finished on the getInitialProps call
if (isResSent(res) && !isSpr) return null
let dataManagerData = '[]'
if (dataManager) {
dataManagerData = JSON.stringify([...dataManager.getData()])
}
if (!docProps || typeof docProps.html !== 'string') {
const message = `"${getDisplayName(
Document
@ -613,10 +541,6 @@ export async function renderToHTML(
throw new Error(message)
}
if (docProps.dataOnly) {
return dataManagerData
}
const dynamicImportIdsSet = new Set<string>()
const dynamicImports: ManifestItem[] = []
@ -642,7 +566,6 @@ export async function renderToHTML(
let html = renderDocument(Document, {
...renderOpts,
dangerousAsPath: router.asPath,
dataManagerData,
ampState,
props,
headTags: await headTags(documentCtx),

View file

@ -93,7 +93,7 @@ export default class Document<P = {}> extends Component<DocumentProps & P> {
return (props: any) => <App {...props} />
}
const { html, head, dataOnly } = await ctx.renderPage({ enhanceApp })
const { html, head } = await ctx.renderPage({ enhanceApp })
const styles = [
...flush(),
...(process.env.__NEXT_PLUGINS
@ -103,7 +103,7 @@ export default class Document<P = {}> extends Component<DocumentProps & P> {
).then(mod => mod.default(ctx))
: []),
]
return { html, head, styles, dataOnly }
return { html, head, styles }
}
static renderDocument<P>(

View file

@ -1,6 +0,0 @@
import { createHook } from 'next/data'
import os from 'os'
export default createHook(async () => {
return os.uptime()
})

View file

@ -1,5 +0,0 @@
module.exports = {
experimental: {
ampBindInitData: true,
},
}

View file

@ -1,19 +0,0 @@
import React, { Suspense } from 'react'
import App from 'next/app'
class MyApp extends App {
render() {
const { Component, pageProps } = this.props
if (typeof window === 'undefined') {
return <Component {...pageProps} />
}
return (
<Suspense fallback={<div>Loading...</div>}>
<Component {...pageProps} />
</Suspense>
)
}
}
export default MyApp

View file

@ -1,9 +0,0 @@
import Link from 'next/link'
export default () => {
return (
<Link href="/">
<a>home</a>
</Link>
)
}

View file

@ -1,6 +0,0 @@
import getUptime from '../data/get-uptime'
export default function Index() {
const uptime = getUptime()
return <h1>The uptime of the server is {uptime}</h1>
}

View file

@ -1,58 +0,0 @@
/* eslint-env jest */
/* global jasmine */
import { join } from 'path'
import webdriver from 'next-webdriver'
import {
killApp,
findPort,
launchApp,
fetchViaHTTP,
renderViaHTTP,
} from 'next-test-utils'
const appDir = join(__dirname, '../')
let appPort
let server
jasmine.DEFAULT_TIMEOUT_INTERVAL = 1000 * 60 * 2
describe.skip('AMP Bind Initial Data', () => {
beforeAll(async () => {
appPort = await findPort()
server = await launchApp(appDir, appPort)
})
afterAll(() => killApp(server))
it('responds with json with accept header on page', async () => {
const data = await fetchViaHTTP(appPort, '/', null, {
headers: {
accept: 'application/amp.bind+json',
},
}).then(res => res.ok && res.text())
let isJSON = false
try {
JSON.parse(data)
isJSON = true
} catch (_) {}
expect(isJSON).toBe(true)
})
it('renders the data during SSR', async () => {
const html = await renderViaHTTP(appPort, '/')
expect(html).toMatch(/The uptime of the server is.*?\d.*?\d/)
})
it('renders a page without data', async () => {
const html = await renderViaHTTP(appPort, '/about')
expect(html).toMatch(/<a.*?home/)
})
it('navigates to a page with data correctly', async () => {
const browser = await webdriver(appPort, '/about')
await browser.elementByCss('a').click()
await browser.waitForElementByCss('h1')
const h1Text = await browser.elementByCss('h1').text()
expect(h1Text).toMatch(/The uptime of the server is.*?\d.*?\d/)
await browser.close()
})
})

View file

@ -81,7 +81,7 @@ describe('Production response size', () => {
)
// These numbers are without gzip compression!
const delta = responseSizeKilobytes - 234
const delta = responseSizeKilobytes - 230
expect(delta).toBeLessThanOrEqual(0) // don't increase size
expect(delta).toBeGreaterThanOrEqual(-1) // don't decrease size without updating target
})
@ -101,7 +101,7 @@ describe('Production response size', () => {
)
// These numbers are without gzip compression!
const delta = responseSizeKilobytes - 202
const delta = responseSizeKilobytes - 199
expect(delta).toBeLessThanOrEqual(0) // don't increase size
expect(delta).toBeGreaterThanOrEqual(-1) // don't decrease size without updating target
})

View file

@ -12815,10 +12815,10 @@ react-is@16.8.6, react-is@^16.8.1, react-is@^16.8.4:
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.8.6.tgz#5bbc1e2d29141c9fbdfed456343fe2bc430a6a16"
integrity sha512-aUk3bHfZ2bRSVFFbbeVS4i+lNPZr3/WM5jT2J5omUVV1zzcs1nAaf3l51ctA5FFvCRbhrH0bdAsRRQddFJZPtA==
react-ssr-prepass@1.0.7:
version "1.0.7"
resolved "https://registry.yarnpkg.com/react-ssr-prepass/-/react-ssr-prepass-1.0.7.tgz#6b6fac5b4d3a8f10e6a45f33798c7cdf7a164eeb"
integrity sha512-DnXVwgytL+9UvAMnotUKW3GlksFgqOegsGOQjUI2v31pkbJBUunBvnOoNu2tNr4KRdxL5X8l66sAW5/1wSRiQw==
react-ssr-prepass@1.0.8:
version "1.0.8"
resolved "https://registry.yarnpkg.com/react-ssr-prepass/-/react-ssr-prepass-1.0.8.tgz#036abffe541975b20213cf7b261c05ac2843480d"
integrity sha512-O0gfRA1SaK+9ITKxqfnXsej2jF+OHGP/+GxD4unROQaM/0/UczGF9fuF+wTboxaQoKdIf4FvS3h/OigWh704VA==
dependencies:
object-is "^1.0.1"