diff --git a/data.sqlite b/data.sqlite new file mode 100644 index 000000000000000..e69de29bb2d1d64 diff --git a/docs/api-reference/create-next-app.md b/docs/api-reference/create-next-app.md index 1b96f94179d2016..24f4255df576c35 100644 --- a/docs/api-reference/create-next-app.md +++ b/docs/api-reference/create-next-app.md @@ -18,7 +18,7 @@ yarn create next-app - **-e, --example [name]|[github-url]** - An example to bootstrap the app with. You can use an example name from the [Next.js repo](https://github.com/vercel/next.js/tree/master/examples) or a GitHub URL. The URL can use any branch and/or subdirectory. - **--example-path [path-to-example]** - In a rare case, your GitHub URL might contain a branch name with a slash (e.g. bug/fix-1) and the path to the example (e.g. foo/bar). In this case, you must specify the path to the example separately: `--example-path foo/bar` -- **--use-npm** - Explicitly tell the CLI to bootstrap the app using npm. Yarn will be used by default if it's installed +- **--use-npm** - Explicitly tell the CLI to bootstrap the app using npm. To bootstrap using yarn we recommend to run `yarn create next-app` ### Why use Create Next App? diff --git a/docs/api-reference/next.config.js/rewrites.md b/docs/api-reference/next.config.js/rewrites.md index 1d5e0c5592fb599..927fc23778c2763 100644 --- a/docs/api-reference/next.config.js/rewrites.md +++ b/docs/api-reference/next.config.js/rewrites.md @@ -25,8 +25,6 @@ Rewrites allow you to map an incoming request path to a different destination pa Rewrites act as a URL proxy and mask the destination path, making it appear the user hasn't changed their location on the site. In contrast, [redirects](/docs/api-reference/next.config.js/redirects.md) will reroute to a new page a show the URL changes. -Rewrites are only available on the Node.js environment and do not affect client-side routing. - To use rewrites you can use the `rewrites` key in `next.config.js`: ```js @@ -42,6 +40,8 @@ module.exports = { } ``` +Rewrites are applied to client-side routing, a `` will have the rewrite applied in the above example. + `rewrites` is an async function that expects an array to be returned holding objects with `source` and `destination` properties: - `source`: `String` - is the incoming request path pattern. @@ -58,8 +58,8 @@ module.exports = { return { beforeFiles: [ // These rewrites are checked after headers/redirects - // and before pages/public files which allows overriding - // page files + // and before all files including _next/public files which + // allows overriding page files { source: '/some-page', destination: '/somewhere-else', diff --git a/docs/basic-features/data-fetching.md b/docs/basic-features/data-fetching.md index cf0dbe880712e5c..ef4c057382cdf5a 100644 --- a/docs/basic-features/data-fetching.md +++ b/docs/basic-features/data-fetching.md @@ -72,7 +72,7 @@ The `context` parameter is an object containing the following keys: `getStaticProps` should return an object with: -- `props` - A **required** object with the props that will be received by the page component. It should be a [serializable object](https://en.wikipedia.org/wiki/Serialization) +- `props` - An **optional** object with the props that will be received by the page component. It should be a [serializable object](https://en.wikipedia.org/wiki/Serialization) - `revalidate` - An **optional** amount in seconds after which a page re-generation can occur. More on [Incremental Static Regeneration](#incremental-static-regeneration) - `notFound` - An **optional** boolean value to allow the page to return a 404 status and page. Below is an example of how it works: @@ -672,7 +672,7 @@ The `context` parameter is an object containing the following keys: `getServerSideProps` should return an object with: -- `props` - A **required** object with the props that will be received by the page component. It should be a [serializable object](https://en.wikipedia.org/wiki/Serialization) +- `props` - An **optional** object with the props that will be received by the page component. It should be a [serializable object](https://en.wikipedia.org/wiki/Serialization) - `notFound` - An **optional** boolean value to allow the page to return a 404 status and page. Below is an example of how it works: ```js diff --git a/errors/no-sync-scripts.md b/errors/no-sync-scripts.md index 4af7ef58b521138..1788236d85df141 100644 --- a/errors/no-sync-scripts.md +++ b/errors/no-sync-scripts.md @@ -16,7 +16,7 @@ import Script from 'next/experimental-script' const Home = () => { return (
- +
Home Page
) diff --git a/examples/with-supertokens/package.json b/examples/with-supertokens/package.json index ac40d1262d1a431..b98f6ca22612f95 100644 --- a/examples/with-supertokens/package.json +++ b/examples/with-supertokens/package.json @@ -11,7 +11,7 @@ "next": "latest", "react": "17.0.1", "react-dom": "17.0.1", - "supertokens-auth-react": "^0.12.0", + "supertokens-auth-react": "^0.13.0", "supertokens-node": "^5.0.0" }, "license": "MIT" diff --git a/examples/with-supertokens/pages/index.js b/examples/with-supertokens/pages/index.js index 00284df70e94cff..befe0d5d5cb9429 100644 --- a/examples/with-supertokens/pages/index.js +++ b/examples/with-supertokens/pages/index.js @@ -42,13 +42,17 @@ export default function Home(props) { function ProtectedPage({ userId }) { async function logoutClicked() { await ThirdPartyEmailPassword.signOut() - window.location.href = '/auth' + ThirdPartyEmailPassword.redirectToAuth() } async function fetchUserData() { const res = await fetch('/api/user') - const json = await res.json() - alert(JSON.stringify(json)) + if (res.status === 401) { + ThirdPartyEmailPassword.redirectToAuth() + } else { + const json = await res.json() + alert(JSON.stringify(json)) + } } return ( diff --git a/lerna.json b/lerna.json index 86d8e732e8b56c6..a3862230b7a83c8 100644 --- a/lerna.json +++ b/lerna.json @@ -17,5 +17,5 @@ "registry": "https://registry.npmjs.org/" } }, - "version": "10.2.1-canary.5" + "version": "10.2.1-canary.7" } diff --git a/package.json b/package.json index 02d0e7044949964..30f33b5a997bb13 100644 --- a/package.json +++ b/package.json @@ -87,6 +87,7 @@ "image-size": "0.9.3", "is-animated": "2.0.0", "isomorphic-unfetch": "3.0.0", + "jest": "27.0.0-next.8", "ky": "0.19.1", "ky-universal": "0.6.0", "lerna": "4.0.0", @@ -119,6 +120,8 @@ "selenium-standalone": "6.18.0", "selenium-webdriver": "4.0.0-alpha.7", "shell-quote": "1.7.2", + "sqlite": "4.0.22", + "sqlite3": "5.0.2", "styled-components": "5.1.0", "styled-jsx-plugin-postcss": "3.0.2", "tailwindcss": "1.1.3", @@ -128,8 +131,7 @@ "wait-port": "0.2.2", "web-streams-polyfill": "2.1.1", "webpack-bundle-analyzer": "4.3.0", - "worker-loader": "3.0.7", - "jest": "27.0.0-next.8" + "worker-loader": "3.0.7" }, "resolutions": { "browserslist": "4.16.1", diff --git a/packages/create-next-app/README.md b/packages/create-next-app/README.md index deeb80d65567128..8e629058467a3bb 100644 --- a/packages/create-next-app/README.md +++ b/packages/create-next-app/README.md @@ -19,7 +19,7 @@ npx create-next-app blog-app - **--ts, --typescript** - Initialize as a TypeScript project. - **-e, --example [name]|[github-url]** - An example to bootstrap the app with. You can use an example name from the [Next.js repo](https://github.com/vercel/next.js/tree/master/examples) or a GitHub URL. The URL can use any branch and/or subdirectory. - **--example-path <path-to-example>** - In a rare case, your GitHub URL might contain a branch name with a slash (e.g. bug/fix-1) and the path to the example (e.g. foo/bar). In this case, you must specify the path to the example separately: `--example-path foo/bar` -- **--use-npm** - Explicitly tell the CLI to bootstrap the app using npm. Yarn will be used by default if it's installed +- **--use-npm** - Explicitly tell the CLI to bootstrap the app using npm. To bootstrap using yarn we recommend to run `yarn create next-app` ## Why use Create Next App? diff --git a/packages/create-next-app/package.json b/packages/create-next-app/package.json index 34925c4bb810d66..d8af0393e17bd99 100644 --- a/packages/create-next-app/package.json +++ b/packages/create-next-app/package.json @@ -1,6 +1,6 @@ { "name": "create-next-app", - "version": "10.2.1-canary.5", + "version": "10.2.1-canary.7", "keywords": [ "react", "next", diff --git a/packages/eslint-config-next/package.json b/packages/eslint-config-next/package.json index a4791577e5b037d..0244f2361c3f0a6 100644 --- a/packages/eslint-config-next/package.json +++ b/packages/eslint-config-next/package.json @@ -1,6 +1,6 @@ { "name": "eslint-config-next", - "version": "10.2.1-canary.5", + "version": "10.2.1-canary.7", "description": "ESLint configuration used by NextJS.", "main": "index.js", "license": "MIT", diff --git a/packages/eslint-plugin-next/package.json b/packages/eslint-plugin-next/package.json index 3020c3171692cdd..0a0597a86b5927d 100644 --- a/packages/eslint-plugin-next/package.json +++ b/packages/eslint-plugin-next/package.json @@ -1,6 +1,6 @@ { "name": "@next/eslint-plugin-next", - "version": "10.2.1-canary.5", + "version": "10.2.1-canary.7", "description": "ESLint plugin for NextJS.", "main": "lib/index.js", "license": "MIT", diff --git a/packages/next-bundle-analyzer/package.json b/packages/next-bundle-analyzer/package.json index 532a95566e26c86..6ee3401c6530ebb 100644 --- a/packages/next-bundle-analyzer/package.json +++ b/packages/next-bundle-analyzer/package.json @@ -1,6 +1,6 @@ { "name": "@next/bundle-analyzer", - "version": "10.2.1-canary.5", + "version": "10.2.1-canary.7", "main": "index.js", "license": "MIT", "repository": { diff --git a/packages/next-codemod/package.json b/packages/next-codemod/package.json index 40902ae77fdabc3..8329b1b49be7a33 100644 --- a/packages/next-codemod/package.json +++ b/packages/next-codemod/package.json @@ -1,6 +1,6 @@ { "name": "@next/codemod", - "version": "10.2.1-canary.5", + "version": "10.2.1-canary.7", "license": "MIT", "dependencies": { "chalk": "4.1.0", diff --git a/packages/next-env/package.json b/packages/next-env/package.json index 45b077bf8b5eb33..2b2712ce964cc25 100644 --- a/packages/next-env/package.json +++ b/packages/next-env/package.json @@ -1,6 +1,6 @@ { "name": "@next/env", - "version": "10.2.1-canary.5", + "version": "10.2.1-canary.7", "keywords": [ "react", "next", diff --git a/packages/next-mdx/package.json b/packages/next-mdx/package.json index d64a047d925cb54..5b7cbf0571dc163 100644 --- a/packages/next-mdx/package.json +++ b/packages/next-mdx/package.json @@ -1,6 +1,6 @@ { "name": "@next/mdx", - "version": "10.2.1-canary.5", + "version": "10.2.1-canary.7", "main": "index.js", "license": "MIT", "repository": { diff --git a/packages/next-plugin-storybook/package.json b/packages/next-plugin-storybook/package.json index e649aac8e562f65..3254f35c4738792 100644 --- a/packages/next-plugin-storybook/package.json +++ b/packages/next-plugin-storybook/package.json @@ -1,6 +1,6 @@ { "name": "@next/plugin-storybook", - "version": "10.2.1-canary.5", + "version": "10.2.1-canary.7", "repository": { "url": "vercel/next.js", "directory": "packages/next-plugin-storybook" diff --git a/packages/next-polyfill-module/package.json b/packages/next-polyfill-module/package.json index a77c8bfb9fc104b..a648471ea0ff759 100644 --- a/packages/next-polyfill-module/package.json +++ b/packages/next-polyfill-module/package.json @@ -1,6 +1,6 @@ { "name": "@next/polyfill-module", - "version": "10.2.1-canary.5", + "version": "10.2.1-canary.7", "description": "A standard library polyfill for ES Modules supporting browsers (Edge 16+, Firefox 60+, Chrome 61+, Safari 10.1+)", "main": "dist/polyfill-module.js", "license": "MIT", diff --git a/packages/next-polyfill-nomodule/package.json b/packages/next-polyfill-nomodule/package.json index 618df2f0c0be2c6..9b629ebb490806b 100644 --- a/packages/next-polyfill-nomodule/package.json +++ b/packages/next-polyfill-nomodule/package.json @@ -1,6 +1,6 @@ { "name": "@next/polyfill-nomodule", - "version": "10.2.1-canary.5", + "version": "10.2.1-canary.7", "description": "A polyfill for non-dead, nomodule browsers.", "main": "dist/polyfill-nomodule.js", "license": "MIT", diff --git a/packages/next/build/babel/preset.ts b/packages/next/build/babel/preset.ts index 04a9aee246dd9a9..32657be2e384d50 100644 --- a/packages/next/build/babel/preset.ts +++ b/packages/next/build/babel/preset.ts @@ -41,7 +41,6 @@ type NextBabelPresetOptions = { 'preset-react'?: any 'class-properties'?: any 'transform-runtime'?: any - 'experimental-modern-preset'?: PluginItem 'styled-jsx'?: StyledJsxBabelOptions 'preset-typescript'?: any } @@ -89,10 +88,6 @@ export default ( (Boolean(api.caller((caller: any) => !!caller && caller.hasJsxRuntime)) && options['preset-react']?.runtime !== 'classic') - const isLaxModern = - options['preset-env']?.targets && - options['preset-env'].targets.esmodules === true - const presetEnvConfig = { // In the test environment `modules` is often needed to be set to true, babel figures that out by itself using the `'auto'` option // In production/development this option is set to `false` so that webpack can handle import/export with tree-shaking @@ -122,17 +117,10 @@ export default ( } } - // specify a preset to use instead of @babel/preset-env - const customModernPreset = - isLaxModern && options['experimental-modern-preset'] - return { sourceType: 'unambiguous', presets: [ - customModernPreset || [ - require('next/dist/compiled/babel/preset-env'), - presetEnvConfig, - ], + [require('next/dist/compiled/babel/preset-env'), presetEnvConfig], [ require('next/dist/compiled/babel/preset-react'), { diff --git a/packages/next/build/index.ts b/packages/next/build/index.ts index 89617e2e609e934..182680febcfc09f 100644 --- a/packages/next/build/index.ts +++ b/packages/next/build/index.ts @@ -79,6 +79,7 @@ import { trace, setGlobal } from '../telemetry/trace' import { collectPages, detectConflictingPaths, + computeFromManifest, getJsPageSizeInKb, getNamedExports, hasCustomGetInitialProps, @@ -724,6 +725,11 @@ export default async function build( ) } + const computedManifestData = await computeFromManifest( + buildManifest, + distDir, + config.experimental.gzipSize + ) await Promise.all( pageKeys.map(async (page) => { const checkPageSpan = staticCheckSpan.traceChild('check-page', { @@ -734,7 +740,9 @@ export default async function build( const [selfSize, allSize] = await getJsPageSizeInKb( actualPage, distDir, - buildManifest + buildManifest, + config.experimental.gzipSize, + computedManifestData ) let isSsg = false @@ -1525,6 +1533,7 @@ export default async function build( useStatic404, pageExtensions: config.pageExtensions, buildManifest, + gzipSize: config.experimental.gzipSize, }) ) diff --git a/packages/next/build/utils.ts b/packages/next/build/utils.ts index c29735acdfb0bb2..14a00b71cdaa0b7 100644 --- a/packages/next/build/utils.ts +++ b/packages/next/build/utils.ts @@ -1,8 +1,9 @@ import '../next-server/server/node-polyfill-fetch' import chalk from 'chalk' -import gzipSize from 'next/dist/compiled/gzip-size' +import getGzipSize from 'next/dist/compiled/gzip-size' import textTable from 'next/dist/compiled/text-table' import path from 'path' +import { promises as fs } from 'fs' import { isValidElementType } from 'react-is' import stripAnsi from 'next/dist/compiled/strip-ansi' import { @@ -32,11 +33,20 @@ import * as Log from './output/log' import { loadComponents } from '../next-server/server/load-components' import { trace } from '../telemetry/trace' -const fileGzipStats: { [k: string]: Promise } = {} +const fileGzipStats: { [k: string]: Promise | undefined } = {} const fsStatGzip = (file: string) => { - if (fileGzipStats[file]) return fileGzipStats[file] - fileGzipStats[file] = gzipSize.file(file) - return fileGzipStats[file] + const cached = fileGzipStats[file] + if (cached) return cached + return (fileGzipStats[file] = getGzipSize.file(file)) +} + +const fileSize = async (file: string) => (await fs.stat(file)).size + +const fileStats: { [k: string]: Promise | undefined } = {} +const fsStat = (file: string) => { + const cached = fileStats[file] + if (cached) return cached + return (fileStats[file] = fileSize(file)) } export function collectPages( @@ -70,6 +80,7 @@ export async function printTreeView( pageExtensions, buildManifest, useStatic404, + gzipSize = true, }: { distPath: string buildId: string @@ -77,6 +88,7 @@ export async function printTreeView( pageExtensions: string[] buildManifest: BuildManifest useStatic404: boolean + gzipSize?: boolean } ) { const getPrettySize = (_size: number): string => { @@ -96,7 +108,7 @@ export async function printTreeView( // Re-add `static/` for root files .replace(/^/, 'static') // Remove file hash - .replace(/[.-]([0-9a-z]{6})[0-9a-z]{14}(?=\.)/, '.$1') + .replace(/(?:^|[.-])([0-9a-z]{6})[0-9a-z]{14}(?=\.)/, '.$1') const messages: [string, string, string][] = [ ['Page', 'Size', 'First Load JS'].map((entry) => @@ -115,7 +127,12 @@ export async function printTreeView( list = [...list, '/404'] } - const sizeData = await computeFromManifest(buildManifest, distPath, pageInfos) + const sizeData = await computeFromManifest( + buildManifest, + distPath, + gzipSize, + pageInfos + ) const pageList = list .slice() @@ -378,9 +395,10 @@ let cachedBuildManifest: BuildManifest | undefined let lastCompute: ComputeManifestShape | undefined let lastComputePageInfo: boolean | undefined -async function computeFromManifest( +export async function computeFromManifest( manifest: BuildManifest, distPath: string, + gzipSize: boolean = true, pageInfos?: Map ): Promise { if ( @@ -414,6 +432,8 @@ async function computeFromManifest( }) }) + const getSize = gzipSize ? fsStatGzip : fsStat + const commonFiles = [...files.entries()] .filter(([, len]) => len === expected || len === Infinity) .map(([f]) => f) @@ -426,7 +446,7 @@ async function computeFromManifest( stats = await Promise.all( commonFiles.map( async (f) => - [f, await fsStatGzip(path.join(distPath, f))] as [string, number] + [f, await getSize(path.join(distPath, f))] as [string, number] ) ) } catch (_) { @@ -438,7 +458,7 @@ async function computeFromManifest( uniqueStats = await Promise.all( uniqueFiles.map( async (f) => - [f, await fsStatGzip(path.join(distPath, f))] as [string, number] + [f, await getSize(path.join(distPath, f))] as [string, number] ) ) } catch (_) { @@ -486,9 +506,13 @@ function sum(a: number[]): number { export async function getJsPageSizeInKb( page: string, distPath: string, - buildManifest: BuildManifest + buildManifest: BuildManifest, + gzipSize: boolean = true, + computedManifestData?: ComputeManifestShape ): Promise<[number, number]> { - const data = await computeFromManifest(buildManifest, distPath) + const data = + computedManifestData || + (await computeFromManifest(buildManifest, distPath, gzipSize)) const fnFilterJs = (entry: string) => entry.endsWith('.js') @@ -507,11 +531,13 @@ export async function getJsPageSizeInKb( data.commonFiles ).map(fnMapRealPath) + const getSize = gzipSize ? fsStatGzip : fsStat + try { // Doesn't use `Promise.all`, as we'd double compute duplicate files. This // function is memoized, so the second one will instantly resolve. - const allFilesSize = sum(await Promise.all(allFilesReal.map(fsStatGzip))) - const selfFilesSize = sum(await Promise.all(selfFilesReal.map(fsStatGzip))) + const allFilesSize = sum(await Promise.all(allFilesReal.map(getSize))) + const selfFilesSize = sum(await Promise.all(selfFilesReal.map(getSize))) return [selfFilesSize, allFilesSize] } catch (_) {} diff --git a/packages/next/build/webpack-config.ts b/packages/next/build/webpack-config.ts index 9f3c72a177d24fd..4050a470e4b88d5 100644 --- a/packages/next/build/webpack-config.ts +++ b/packages/next/build/webpack-config.ts @@ -287,7 +287,6 @@ export default async function getBaseWebpackConfig( /next[\\/]dist[\\/]next-server[\\/]lib/, /next[\\/]dist[\\/]client/, /next[\\/]dist[\\/]pages/, - /[\\/](strip-ansi|ansi-regex)[\\/]/, ] // Support for NODE_PATH @@ -1260,6 +1259,7 @@ export default async function getBaseWebpackConfig( pageEnv: config.experimental.pageEnv, excludeDefaultMomentLocales: config.future.excludeDefaultMomentLocales, assetPrefix: config.assetPrefix, + disableOptimizedLoading: config.experimental.disableOptimizedLoading, target, reactProductionProfiling, webpack: !!config.webpack, diff --git a/packages/next/build/webpack/loaders/next-serverless-loader/utils.ts b/packages/next/build/webpack/loaders/next-serverless-loader/utils.ts index de8e14e420d84c6..2619b5a4f114254 100644 --- a/packages/next/build/webpack/loaders/next-serverless-loader/utils.ts +++ b/packages/next/build/webpack/loaders/next-serverless-loader/utils.ts @@ -289,7 +289,13 @@ export function getUtils({ // on the parsed params, this is used to signal if we need // to parse x-now-route-matches or not const isDefaultValue = Array.isArray(value) - ? value.every((val, idx) => val === defaultRouteMatches![key][idx]) + ? value.some((val) => { + const defaultValue = defaultRouteMatches![key] + + return Array.isArray(defaultValue) + ? defaultValue.includes(val) + : defaultValue === val + }) : value === defaultRouteMatches![key] if (isDefaultValue || typeof value === 'undefined') { diff --git a/packages/next/data.sqlite b/packages/next/data.sqlite new file mode 100644 index 000000000000000..e69de29bb2d1d64 diff --git a/packages/next/export/index.ts b/packages/next/export/index.ts index 7d8cc3fca103494..54a8bf54b38da3a 100644 --- a/packages/next/export/index.ts +++ b/packages/next/export/index.ts @@ -369,6 +369,7 @@ export default async function exportApp( defaultLocale: i18n?.defaultLocale, domainLocales: i18n?.domains, trailingSlash: nextConfig.trailingSlash, + disableOptimizedLoading: nextConfig.experimental.disableOptimizedLoading, } const { serverRuntimeConfig, publicRuntimeConfig } = nextConfig @@ -511,7 +512,7 @@ export default async function exportApp( const worker = new Worker(require.resolve('./worker'), { maxRetries: 0, numWorkers: threads, - enableWorkerThreads: true, + enableWorkerThreads: nextConfig.experimental.workerThreads, exposedMethods: ['default'], }) as Worker & { default: typeof exportPage } @@ -541,6 +542,8 @@ export default async function exportApp( optimizeFonts: nextConfig.optimizeFonts, optimizeImages: nextConfig.experimental.optimizeImages, optimizeCss: nextConfig.experimental.optimizeCss, + disableOptimizedLoading: + nextConfig.experimental.disableOptimizedLoading, parentSpanId: pageExportSpan.id, }) diff --git a/packages/next/export/worker.ts b/packages/next/export/worker.ts index 68f03b80f4ba674..057c4c5f1ebd546 100644 --- a/packages/next/export/worker.ts +++ b/packages/next/export/worker.ts @@ -52,6 +52,7 @@ interface ExportPageInput { optimizeFonts: boolean optimizeImages?: boolean optimizeCss: any + disableOptimizedLoading: any parentSpanId: any } @@ -70,6 +71,7 @@ interface RenderOpts { ampSkipValidation?: boolean optimizeFonts?: boolean optimizeImages?: boolean + disableOptimizedLoading?: boolean optimizeCss?: any fontManifest?: FontManifest locales?: string[] @@ -98,6 +100,7 @@ export default async function exportPage({ optimizeFonts, optimizeImages, optimizeCss, + disableOptimizedLoading, }: ExportPageInput): Promise { const exportPageSpan = trace('export-page-worker', parentSpanId) @@ -284,6 +287,7 @@ export default async function exportPage({ optimizeImages, /// @ts-ignore optimizeCss, + disableOptimizedLoading, distDir, fontManifest: optimizeFonts ? requireFontManifest(distDir, serverless) @@ -357,6 +361,7 @@ export default async function exportPage({ optimizeFonts, optimizeImages, optimizeCss, + disableOptimizedLoading, fontManifest: optimizeFonts ? requireFontManifest(distDir, serverless) : null, diff --git a/packages/next/lib/typescript/writeConfigurationDefaults.ts b/packages/next/lib/typescript/writeConfigurationDefaults.ts index 0f8ee3ae4f0495d..04333a8b33eae12 100644 --- a/packages/next/lib/typescript/writeConfigurationDefaults.ts +++ b/packages/next/lib/typescript/writeConfigurationDefaults.ts @@ -1,7 +1,6 @@ import { promises as fs } from 'fs' import chalk from 'chalk' import * as CommentJson from 'next/dist/compiled/comment-json' -import semver from 'next/dist/compiled/semver' import os from 'os' import { getTypeScriptConfiguration } from './getTypeScriptConfiguration' @@ -29,9 +28,6 @@ function getDesiredCompilerOptions( strict: { suggested: false }, forceConsistentCasingInFileNames: { suggested: true }, noEmit: { suggested: true }, - ...(semver.gte(ts.version, '4.3.0-beta') - ? { incremental: { suggested: true } } - : undefined), // These values are required and cannot be changed by the user // Keep this in sync with the webpack config diff --git a/packages/next/lib/verifyTypeScriptSetup.ts b/packages/next/lib/verifyTypeScriptSetup.ts index b37b26e0508809e..c2c36b2d0934e90 100644 --- a/packages/next/lib/verifyTypeScriptSetup.ts +++ b/packages/next/lib/verifyTypeScriptSetup.ts @@ -8,7 +8,7 @@ import { CompileError } from './compile-error' import { FatalError } from './fatal-error' import { getTypeScriptIntent } from './typescript/getTypeScriptIntent' -import type { TypeCheckResult } from './typescript/runTypeCheck' +import { TypeCheckResult } from './typescript/runTypeCheck' import { writeAppTypeDeclarations } from './typescript/writeAppTypeDeclarations' import { writeConfigurationDefaults } from './typescript/writeConfigurationDefaults' diff --git a/packages/next/next-server/lib/post-process.ts b/packages/next/next-server/lib/post-process.ts index fc446f581189a91..2a448f64bab560a 100644 --- a/packages/next/next-server/lib/post-process.ts +++ b/packages/next/next-server/lib/post-process.ts @@ -1,3 +1,4 @@ +import escapeRegexp from 'next/dist/compiled/escape-string-regexp' import { parse, HTMLElement } from 'node-html-parser' import { OPTIMIZED_FONT_PROVIDERS } from './constants' @@ -188,7 +189,8 @@ function isImgEligible(imgElement: HTMLElement): boolean { } function preloadTagAlreadyExists(html: string, href: string) { - const regex = new RegExp(`]*href[^>]*${href}`) + const escapedHref = escapeRegexp(href) + const regex = new RegExp(`]*href[^>]*${escapedHref}`) return html.match(regex) } diff --git a/packages/next/next-server/lib/router/router.ts b/packages/next/next-server/lib/router/router.ts index de1a91b942c7211..9e495493ebf8cd2 100644 --- a/packages/next/next-server/lib/router/router.ts +++ b/packages/next/next-server/lib/router/router.ts @@ -800,6 +800,7 @@ export default class Router implements BaseRouter { window.location.href = url return false } + const shouldResolveHref = url === as || (options as any)._h // for static pages with query params in the URL we delay // marking the router ready until after the query is updated @@ -976,7 +977,7 @@ export default class Router implements BaseRouter { ? removePathTrailingSlash(delBasePath(pathname)) : pathname - if (pathname !== '/_error') { + if (shouldResolveHref && pathname !== '/_error') { if (process.env.__NEXT_HAS_REWRITES && as.startsWith('/')) { const rewritesResult = resolveRewrites( addBasePath(addLocale(cleanedAs, this.locale)), diff --git a/packages/next/next-server/lib/utils.ts b/packages/next/next-server/lib/utils.ts index eaaa30b2c806ce3..6af8e33dbdb7f67 100644 --- a/packages/next/next-server/lib/utils.ts +++ b/packages/next/next-server/lib/utils.ts @@ -193,6 +193,7 @@ export type DocumentProps = DocumentInitialProps & { devOnlyCacheBusterQueryString: string scriptLoader: { afterInteractive?: string[]; beforeInteractive?: any[] } locale?: string + disableOptimizedLoading?: boolean } /** diff --git a/packages/next/next-server/server/config-shared.ts b/packages/next/next-server/server/config-shared.ts index fb45863f3269e17..42976289ee886c9 100644 --- a/packages/next/next-server/server/config-shared.ts +++ b/packages/next/next-server/server/config-shared.ts @@ -61,6 +61,8 @@ export type NextConfig = { [key: string]: any } & { eslint?: boolean reactRoot: boolean enableBlurryPlaceholder: boolean + disableOptimizedLoading: boolean + gzipSize: boolean } } @@ -118,6 +120,8 @@ export const defaultConfig: NextConfig = { eslint: false, reactRoot: Number(process.env.NEXT_PRIVATE_REACT_ROOT) > 0, enableBlurryPlaceholder: false, + disableOptimizedLoading: true, + gzipSize: true, }, future: { strictPostcssConfiguration: false, diff --git a/packages/next/next-server/server/config-utils.ts b/packages/next/next-server/server/config-utils.ts index 742b30bca485f19..77f34ff05fb5751 100644 --- a/packages/next/next-server/server/config-utils.ts +++ b/packages/next/next-server/server/config-utils.ts @@ -1,7 +1,7 @@ import path from 'path' import { Worker } from 'jest-worker' import * as Log from '../../build/output/log' -import type { CheckReasons, CheckResult } from './config-utils-worker' +import { CheckReasons, CheckResult } from './config-utils-worker' import { install, shouldLoadWithWebpack5 } from './config-utils-worker' export { install, shouldLoadWithWebpack5 } diff --git a/packages/next/next-server/server/config.ts b/packages/next/next-server/server/config.ts index 04b5691d5b0ab6d..beeb39de5a9fc9e 100644 --- a/packages/next/next-server/server/config.ts +++ b/packages/next/next-server/server/config.ts @@ -56,7 +56,11 @@ function assignDefaults(userConfig: { [key: string]: any }) { return currentConfig } - if (key === 'experimental' && value && value !== defaultConfig[key]) { + if ( + key === 'experimental' && + value !== undefined && + value !== defaultConfig[key] + ) { experimentalWarning() } diff --git a/packages/next/next-server/server/next-server.ts b/packages/next/next-server/server/next-server.ts index 0b367f43a1a4e18..edb480402608a18 100644 --- a/packages/next/next-server/server/next-server.ts +++ b/packages/next/next-server/server/next-server.ts @@ -157,6 +157,7 @@ export default class Server { images: string fontManifest: FontManifest optimizeImages: boolean + disableOptimizedLoading: boolean optimizeCss: any locale?: string locales?: string[] @@ -217,6 +218,8 @@ export default class Server { : null, optimizeImages: !!this.nextConfig.experimental.optimizeImages, optimizeCss: this.nextConfig.experimental.optimizeCss, + disableOptimizedLoading: this.nextConfig.experimental + .disableOptimizedLoading, domainLocales: this.nextConfig.i18n?.domains, distDir: this.distDir, } @@ -815,28 +818,30 @@ export default class Server { } // Headers come very first - const headers = this.customRoutes.headers.map((r) => { - const headerRoute = getCustomRoute(r, 'header') - return { - match: headerRoute.match, - has: headerRoute.has, - type: headerRoute.type, - name: `${headerRoute.type} ${headerRoute.source} header route`, - fn: async (_req, res, params, _parsedUrl) => { - const hasParams = Object.keys(params).length > 0 - - for (const header of (headerRoute as Header).headers) { - let { key, value } = header - if (hasParams) { - key = compileNonPath(key, params) - value = compileNonPath(value, params) - } - res.setHeader(key, value) - } - return { finished: false } - }, - } as Route - }) + const headers = this.minimalMode + ? [] + : this.customRoutes.headers.map((r) => { + const headerRoute = getCustomRoute(r, 'header') + return { + match: headerRoute.match, + has: headerRoute.has, + type: headerRoute.type, + name: `${headerRoute.type} ${headerRoute.source} header route`, + fn: async (_req, res, params, _parsedUrl) => { + const hasParams = Object.keys(params).length > 0 + + for (const header of (headerRoute as Header).headers) { + let { key, value } = header + if (hasParams) { + key = compileNonPath(key, params) + value = compileNonPath(value, params) + } + res.setHeader(key, value) + } + return { finished: false } + }, + } as Route + }) // since initial query values are decoded by querystring.parse // we need to re-encode them here but still allow passing through @@ -968,12 +973,14 @@ export default class Server { let afterFiles: Route[] = [] let fallback: Route[] = [] - if (Array.isArray(this.customRoutes.rewrites)) { - afterFiles = this.customRoutes.rewrites.map(buildRewrite) - } else { - beforeFiles = this.customRoutes.rewrites.beforeFiles.map(buildRewrite) - afterFiles = this.customRoutes.rewrites.afterFiles.map(buildRewrite) - fallback = this.customRoutes.rewrites.fallback.map(buildRewrite) + if (!this.minimalMode) { + if (Array.isArray(this.customRoutes.rewrites)) { + afterFiles = this.customRoutes.rewrites.map(buildRewrite) + } else { + beforeFiles = this.customRoutes.rewrites.beforeFiles.map(buildRewrite) + afterFiles = this.customRoutes.rewrites.afterFiles.map(buildRewrite) + fallback = this.customRoutes.rewrites.fallback.map(buildRewrite) + } } const catchAllRoute: Route = { diff --git a/packages/next/next-server/server/render.tsx b/packages/next/next-server/server/render.tsx index b00e19fb162d4dd..6508c887415a10a 100644 --- a/packages/next/next-server/server/render.tsx +++ b/packages/next/next-server/server/render.tsx @@ -190,6 +190,7 @@ export type RenderOptsPartial = { locales?: string[] defaultLocale?: string domainLocales?: DomainLocales + disableOptimizedLoading?: boolean } export type RenderOpts = LoadComponentsReturnType & RenderOptsPartial @@ -234,6 +235,7 @@ function renderDocument( defaultLocale, domainLocales, isPreview, + disableOptimizedLoading, }: RenderOpts & { props: any docComponentsRendered: DocumentProps['docComponentsRendered'] @@ -305,6 +307,7 @@ function renderDocument( devOnlyCacheBusterQueryString, scriptLoader, locale, + disableOptimizedLoading, ...docProps, })} diff --git a/packages/next/package.json b/packages/next/package.json index 5057f90d64a5d91..37cbeffc3058dfb 100644 --- a/packages/next/package.json +++ b/packages/next/package.json @@ -1,6 +1,6 @@ { "name": "next", - "version": "10.2.1-canary.5", + "version": "10.2.1-canary.7", "description": "The React Framework", "main": "./dist/server/next.js", "license": "MIT", @@ -64,10 +64,10 @@ "dependencies": { "@babel/runtime": "7.12.5", "@hapi/accept": "5.0.1", - "@next/env": "10.2.1-canary.5", - "@next/polyfill-module": "10.2.1-canary.5", - "@next/react-dev-overlay": "10.2.1-canary.5", - "@next/react-refresh-utils": "10.2.1-canary.5", + "@next/env": "10.2.1-canary.7", + "@next/polyfill-module": "10.2.1-canary.7", + "@next/react-dev-overlay": "10.2.1-canary.7", + "@next/react-refresh-utils": "10.2.1-canary.7", "@opentelemetry/api": "0.14.0", "assert": "2.0.0", "ast-types": "0.13.2", @@ -151,7 +151,7 @@ "@babel/preset-typescript": "7.12.7", "@babel/traverse": "^7.12.10", "@babel/types": "7.12.12", - "@next/polyfill-nomodule": "10.2.1-canary.5", + "@next/polyfill-nomodule": "10.2.1-canary.7", "@taskr/clear": "1.1.0", "@taskr/esnext": "1.1.0", "@taskr/watch": "1.1.0", diff --git a/packages/next/pages/_document.tsx b/packages/next/pages/_document.tsx index 98d1d7e1907c06a..d3a486a0e4de99c 100644 --- a/packages/next/pages/_document.tsx +++ b/packages/next/pages/_document.tsx @@ -51,6 +51,115 @@ function getDocumentFiles( } } +function getPolyfillScripts(context: DocumentProps, props: OriginProps) { + // polyfills.js has to be rendered as nomodule without async + // It also has to be the first script to load + const { + assetPrefix, + buildManifest, + devOnlyCacheBusterQueryString, + disableOptimizedLoading, + } = context + + return buildManifest.polyfillFiles + .filter( + (polyfill) => polyfill.endsWith('.js') && !polyfill.endsWith('.module.js') + ) + .map((polyfill) => ( +