From eddabd98f8e4e5a82f4341bc1d8926959327919a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bal=C3=A1zs=20Orb=C3=A1n?= Date: Thu, 17 Feb 2022 19:21:40 +0100 Subject: [PATCH 01/54] refactor: move `HtmlContext` (#34482) The shared utils file included an import from `react` (because it was using `createContext`) which seems to be unnecessary in the Middleware bundle. With this PR and steps #34425 laid out, the bundle size did decrease without breaking functionality. ![image](https://user-images.githubusercontent.com/18369201/154508389-0a813e3e-1e07-4c45-8b71-444cc54a7f9e.png) Fixes #34425 ## Bug - [ ] Related issues linked using `fixes #number` - [ ] Integration tests added - [ ] Errors have helpful link attached, see `contributing.md` ## Feature - [ ] Implements an existing feature request or RFC. Make sure the feature request has been accepted for implementation before opening a PR. - [ ] Related issues linked using `fixes #number` - [ ] Integration tests added - [ ] Documentation added - [ ] Telemetry added. In case of a feature if it's used or not. - [ ] Errors have helpful link attached, see `contributing.md` ## Documentation / Examples - [ ] Make sure the linting passes by running `yarn lint` --- packages/next/pages/_document.tsx | 7 +- packages/next/server/render.tsx | 6 +- packages/next/shared/lib/html-context.ts | 42 +++++++++++ packages/next/shared/lib/router/router.ts | 2 +- .../shared/lib/router/utils/format-url.ts | 35 ++++++++- packages/next/shared/lib/utils.ts | 74 +------------------ 6 files changed, 85 insertions(+), 81 deletions(-) create mode 100644 packages/next/shared/lib/html-context.ts diff --git a/packages/next/pages/_document.tsx b/packages/next/pages/_document.tsx index 91b0b526fea9..6af1f81fc86a 100644 --- a/packages/next/pages/_document.tsx +++ b/packages/next/pages/_document.tsx @@ -1,11 +1,9 @@ import React, { Component, ReactElement, ReactNode, useContext } from 'react' import { OPTIMIZED_FONT_PROVIDERS } from '../shared/lib/constants' -import { +import type { DocumentContext, DocumentInitialProps, DocumentProps, - HtmlContext, - HtmlProps, } from '../shared/lib/utils' import { BuildManifest, getPageFiles } from '../server/get-page-files' import { cleanAmpPath } from '../server/utils' @@ -13,6 +11,9 @@ import { htmlEscapeJsonString } from '../server/htmlescape' import Script, { ScriptProps } from '../client/script' import isError from '../lib/is-error' +import { HtmlContext } from '../shared/lib/html-context' +import type { HtmlProps } from '../shared/lib/html-context' + export { DocumentContext, DocumentInitialProps, DocumentProps } export type OriginProps = { diff --git a/packages/next/server/render.tsx b/packages/next/server/render.tsx index 4c00eb0b92fc..573fdf5d268f 100644 --- a/packages/next/server/render.tsx +++ b/packages/next/server/render.tsx @@ -37,8 +37,6 @@ import { DocumentInitialProps, DocumentProps, DocumentContext, - HtmlContext, - HtmlProps, getDisplayName, isResSent, loadGetInitialProps, @@ -46,6 +44,10 @@ import { RenderPage, RenderPageResult, } from '../shared/lib/utils' + +import { HtmlContext } from '../shared/lib/html-context' +import type { HtmlProps } from '../shared/lib/html-context' + import type { NextApiRequestCookies, __ApiPreviewProps } from './api-utils' import { denormalizePagePath } from './denormalize-page-path' import type { FontManifest } from './font-utils' diff --git a/packages/next/shared/lib/html-context.ts b/packages/next/shared/lib/html-context.ts new file mode 100644 index 000000000000..25baecbd7bd0 --- /dev/null +++ b/packages/next/shared/lib/html-context.ts @@ -0,0 +1,42 @@ +import type { BuildManifest } from '../../server/get-page-files' +import type { NEXT_DATA, MaybeDeferContentHook } from './utils' + +import { createContext } from 'react' + +export type HtmlProps = { + __NEXT_DATA__: NEXT_DATA + dangerousAsPath: string + docComponentsRendered: { + Html?: boolean + Main?: boolean + Head?: boolean + NextScript?: boolean + } + buildManifest: BuildManifest + ampPath: string + inAmpMode: boolean + hybridAmp: boolean + isDevelopment: boolean + dynamicImports: string[] + assetPrefix?: string + canonicalBase: string + headTags: any[] + unstable_runtimeJS?: false + unstable_JsPreload?: false + devOnlyCacheBusterQueryString: string + scriptLoader: { afterInteractive?: string[]; beforeInteractive?: any[] } + locale?: string + disableOptimizedLoading?: boolean + styles?: React.ReactElement[] | React.ReactFragment + head?: Array + useMaybeDeferContent: MaybeDeferContentHook + crossOrigin?: string + optimizeCss?: boolean + optimizeFonts?: boolean + runtime?: 'edge' | 'nodejs' +} + +export const HtmlContext = createContext(null as any) +if (process.env.NODE_ENV !== 'production') { + HtmlContext.displayName = 'HtmlContext' +} diff --git a/packages/next/shared/lib/router/router.ts b/packages/next/shared/lib/router/router.ts index d6ca2f98fba8..d3e172431880 100644 --- a/packages/next/shared/lib/router/router.ts +++ b/packages/next/shared/lib/router/router.ts @@ -22,7 +22,6 @@ import { normalizeLocalePath } from '../i18n/normalize-locale-path' import mitt from '../mitt' import { AppContextType, - formatWithValidation, getLocationOrigin, getURL, loadGetInitialProps, @@ -38,6 +37,7 @@ import resolveRewrites from './utils/resolve-rewrites' import { getRouteMatcher } from './utils/route-matcher' import { getRouteRegex } from './utils/route-regex' import { getMiddlewareRegex } from './utils/get-middleware-regex' +import { formatWithValidation } from './utils/format-url' declare global { interface Window { diff --git a/packages/next/shared/lib/router/utils/format-url.ts b/packages/next/shared/lib/router/utils/format-url.ts index bef24b4e3e7d..205ab30a7142 100644 --- a/packages/next/shared/lib/router/utils/format-url.ts +++ b/packages/next/shared/lib/router/utils/format-url.ts @@ -20,8 +20,8 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. -import { UrlObject } from 'url' -import { ParsedUrlQuery } from 'querystring' +import type { UrlObject } from 'url' +import type { ParsedUrlQuery } from 'querystring' import * as querystring from './querystring' const slashedProtocols = /https?|ftp|gopher|file/ @@ -71,3 +71,34 @@ export function formatUrl(urlObj: UrlObject) { return `${protocol}${host}${pathname}${search}${hash}` } + +export const urlObjectKeys = [ + 'auth', + 'hash', + 'host', + 'hostname', + 'href', + 'path', + 'pathname', + 'port', + 'protocol', + 'query', + 'search', + 'slashes', +] + +export function formatWithValidation(url: UrlObject): string { + if (process.env.NODE_ENV === 'development') { + if (url !== null && typeof url === 'object') { + Object.keys(url).forEach((key) => { + if (urlObjectKeys.indexOf(key) === -1) { + console.warn( + `Unknown key passed via urlObject into url.format: ${key}` + ) + } + }) + } + } + + return formatUrl(url) +} diff --git a/packages/next/shared/lib/utils.ts b/packages/next/shared/lib/utils.ts index 94196af4f597..f18e19f6cf45 100644 --- a/packages/next/shared/lib/utils.ts +++ b/packages/next/shared/lib/utils.ts @@ -1,4 +1,4 @@ -import type { BuildManifest } from '../../server/get-page-files' +import type { HtmlProps } from './html-context' import type { ComponentType } from 'react' import type { DomainLocale } from '../../server/config' import type { Env } from '@next/env' @@ -6,9 +6,6 @@ import type { IncomingMessage, ServerResponse } from 'http' import type { NextRouter } from './router/router' import type { ParsedUrlQuery } from 'querystring' import type { PreviewData } from 'next/types' -import type { UrlObject } from 'url' -import { createContext } from 'react' -import { formatUrl } from './router/utils/format-url' export type NextComponentType< C extends BaseContext = NextPageContext, @@ -195,39 +192,6 @@ export type MaybeDeferContentHook = ( contentFn: () => JSX.Element ) => [boolean, JSX.Element] -export type HtmlProps = { - __NEXT_DATA__: NEXT_DATA - dangerousAsPath: string - docComponentsRendered: { - Html?: boolean - Main?: boolean - Head?: boolean - NextScript?: boolean - } - buildManifest: BuildManifest - ampPath: string - inAmpMode: boolean - hybridAmp: boolean - isDevelopment: boolean - dynamicImports: string[] - assetPrefix?: string - canonicalBase: string - headTags: any[] - unstable_runtimeJS?: false - unstable_JsPreload?: false - devOnlyCacheBusterQueryString: string - scriptLoader: { afterInteractive?: string[]; beforeInteractive?: any[] } - locale?: string - disableOptimizedLoading?: boolean - styles?: React.ReactElement[] | React.ReactFragment - head?: Array - useMaybeDeferContent: MaybeDeferContentHook - crossOrigin?: string - optimizeCss?: boolean - optimizeFonts?: boolean - runtime?: 'edge' | 'nodejs' -} - /** * Next `API` route request */ @@ -410,37 +374,6 @@ export async function loadGetInitialProps< return props } -export const urlObjectKeys = [ - 'auth', - 'hash', - 'host', - 'hostname', - 'href', - 'path', - 'pathname', - 'port', - 'protocol', - 'query', - 'search', - 'slashes', -] - -export function formatWithValidation(url: UrlObject): string { - if (process.env.NODE_ENV === 'development') { - if (url !== null && typeof url === 'object') { - Object.keys(url).forEach((key) => { - if (urlObjectKeys.indexOf(key) === -1) { - console.warn( - `Unknown key passed via urlObject into url.format: ${key}` - ) - } - }) - } - } - - return formatUrl(url) -} - export const SP = typeof performance !== 'undefined' export const ST = SP && @@ -449,11 +382,6 @@ export const ST = export class DecodeError extends Error {} -export const HtmlContext = createContext(null as any) -if (process.env.NODE_ENV !== 'production') { - HtmlContext.displayName = 'HtmlContext' -} - export interface CacheFs { readFile(f: string): Promise readFileSync(f: string): string From 1c167afa589264df6d1a58632a5e5a171de25dce Mon Sep 17 00:00:00 2001 From: Jiachi Liu Date: Thu, 17 Feb 2022 19:57:53 +0100 Subject: [PATCH 02/54] rsc: clean client buffer cache after flushed (#34475) ## Bug Fixes #34464 - [x] Related issues linked using `fixes #number` - [x] Integration tests added - [x] Errors have helpful link attached, see `contributing.md` --- packages/next/client/index.tsx | 1 + .../app/pages/index.server.js | 4 +++ .../test/rsc.js | 36 +++++++++++++++++-- 3 files changed, 38 insertions(+), 3 deletions(-) diff --git a/packages/next/client/index.tsx b/packages/next/client/index.tsx index 3e408f1cefe6..08fdab14c86a 100644 --- a/packages/next/client/index.tsx +++ b/packages/next/client/index.tsx @@ -702,6 +702,7 @@ if (process.env.__NEXT_RSC) { writer.write(encoder.encode(val)) }) buffer.length = 0 + serverDataBuffer.delete(key) } serverDataWriter.set(key, writer) } diff --git a/test/integration/react-streaming-and-server-components/app/pages/index.server.js b/test/integration/react-streaming-and-server-components/app/pages/index.server.js index 4874b99a3422..cec07820c64b 100644 --- a/test/integration/react-streaming-and-server-components/app/pages/index.server.js +++ b/test/integration/react-streaming-and-server-components/app/pages/index.server.js @@ -1,4 +1,5 @@ import Foo from '../components/foo.client' +import Link from 'next/link' const envVar = process.env.ENV_VAR_TEST const headerKey = 'x-next-test-client' @@ -13,6 +14,9 @@ export default function Index({ header, router }) {
+ + refresh + ) } diff --git a/test/integration/react-streaming-and-server-components/test/rsc.js b/test/integration/react-streaming-and-server-components/test/rsc.js index 2cccad780122..604073c3930f 100644 --- a/test/integration/react-streaming-and-server-components/test/rsc.js +++ b/test/integration/react-streaming-and-server-components/test/rsc.js @@ -38,7 +38,7 @@ export default function (context, { runtime, env }) { page.on('request', (request) => { requestsCount++ const url = request.url() - if (/__flight__=1/.test(url)) { + if (/\?__flight__=1/.test(url)) { hasFlightRequest = true } }) @@ -95,9 +95,39 @@ export default function (context, { runtime, env }) { }) } - // For prod build, the directory contains the build ID so it's not deterministic. - // Only enable it for dev for now. + it('should refresh correctly with next/link', async () => { + // Select the button which is not hidden but rendered + const selector = '#__next #refresh' + let hasFlightRequest = false + const browser = await webdriver(context.appPort, '/', { + beforePageLoad(page) { + page.on('request', (request) => { + const url = request.url() + if (/\?__flight__=1/.test(url)) { + hasFlightRequest = true + } + }) + }, + }) + + // wait for hydration + await new Promise((res) => setTimeout(res, 1000)) + if (env === 'dev') { + expect(hasFlightRequest).toBe(false) + } + await browser.elementByCss(selector).click() + // wait for re-hydration + await new Promise((res) => setTimeout(res, 1000)) + if (env === 'dev') { + expect(hasFlightRequest).toBe(true) + } + const refreshText = await browser.elementByCss(selector).text() + expect(refreshText).toBe('refresh') + }) + if (env === 'dev') { + // For prod build, the directory contains the build ID so it's not deterministic. + // Only enable it for dev for now. it('should not bundle external imports into client builds for RSC', async () => { const html = await renderViaHTTP(context.appPort, '/external-imports') expect(html).toContain('date:') From 787186a85a054ea870fc964583fe65e9f2286354 Mon Sep 17 00:00:00 2001 From: Aman Mittal Date: Fri, 18 Feb 2022 00:34:08 +0530 Subject: [PATCH 03/54] Add info on rendering an error page when using getServerSideProps (#34488) This PR adds information on rendering an error page (500 page specifically) when using `getServerSideProps`. ## Bug - [x] Related issues linked using https://github.com/vercel/documentation/issues/106 - [ ] Integration tests added - [ ] Errors have helpful link attached, see `contributing.md` ## Feature - [ ] Implements an existing feature request or RFC. Make sure the feature request has been accepted for implementation before opening a PR. - [ ] Related issues linked using `fixes #number` - [ ] Integration tests added - [ ] Documentation added - [ ] Telemetry added. In case of a feature if it's used or not. - [ ] Errors have helpful link attached, see `contributing.md` ## Documentation / Examples - [ ] Make sure the linting passes by running `yarn lint` Co-authored-by: JJ Kasper <22380829+ijjk@users.noreply.github.com> --- .../basic-features/data-fetching/get-server-side-props.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/docs/basic-features/data-fetching/get-server-side-props.md b/docs/basic-features/data-fetching/get-server-side-props.md index 377a775aa5a2..da0463f1fecd 100644 --- a/docs/basic-features/data-fetching/get-server-side-props.md +++ b/docs/basic-features/data-fetching/get-server-side-props.md @@ -47,8 +47,8 @@ Take the following example. An API route is used to fetch some data from a CMS. If your page contains frequently updating data, and you don’t need to pre-render the data, you can fetch the data on the [client side](/docs/basic-features/data-fetching/client-side.md). An example of this is user-specific data: -- First, immediately show the page without data. Parts of the page can be pre-rendered using Static Generation. You can show loading states for missing data. -- Then, fetch the data on the client side and display it when ready. +- First, immediately show the page without data. Parts of the page can be pre-rendered using Static Generation. You can show loading states for missing data +- Then, fetch the data on the client side and display it when ready This approach works well for user dashboard pages, for example. Because a dashboard is a private, user-specific page, SEO is not relevant and the page doesn’t need to be pre-rendered. The data is frequently updated, which requires request-time data fetching. @@ -74,6 +74,10 @@ export async function getServerSideProps() { export default Page ``` +## Does getServerSideProps render an error page + +If an error is thrown inside `getServerSideProps`, it will show the `pages/500.js` file. Check out the documentation for [500 page](/docs/advanced-features/custom-error-page#500-page) to learn more on how to create it. During development this file will not be used and the dev overlay will be shown instead. + ## Related For more information on what to do next, we recommend the following sections: From 0b95da5358a58dee48a6910885a167575534a745 Mon Sep 17 00:00:00 2001 From: Bennett Dams Date: Thu, 17 Feb 2022 22:48:27 +0100 Subject: [PATCH 04/54] Ensure config's `experimental` field exists (#34500) Fixes #34499 Starting with `v12.1.0`, you can't use React 18 when you don't use the `experimental` field in the `next.config.js` ![image](https://user-images.githubusercontent.com/29319414/154569017-38f72690-6879-47d1-a0cd-09072af2967c.png) That's because [this recent change](https://github.com/vercel/next.js/commit/1aee93581f7154ca4c191388904c33980cd74653) sets `reactRoot` on the user's config without checking if the key already exists: https://github.com/vercel/next.js/blob/787186a85a054ea870fc964583fe65e9f2286354/packages/next/server/config.ts#L679-L682 This change initializes `experimental` on the `userConfig` if necessary. ## Bug - [x] Related issues linked using `fixes #number` - [ ] Integration tests added - [ ] Errors have helpful link attached, see `contributing.md` --- packages/next/server/config.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/next/server/config.ts b/packages/next/server/config.ts index f51deaa8254a..1250ea0dfe38 100644 --- a/packages/next/server/config.ts +++ b/packages/next/server/config.ts @@ -678,6 +678,8 @@ export default async function loadConfig( const hasReactRoot = shouldUseReactRoot() if (hasReactRoot) { + // users might not have the `experimental` key in their config + userConfig.experimental = userConfig.experimental || {} userConfig.experimental.reactRoot = true } From d4bef88b2b8132b5b892ddf31b5944871dd5f31e Mon Sep 17 00:00:00 2001 From: Sophia Willows <20146550+sophiabits@users.noreply.github.com> Date: Fri, 18 Feb 2022 11:22:52 +1300 Subject: [PATCH 05/54] Only log experiments warning if user actually opted in to an experiment(s) (#34413) Currently if you have a Next config like the following: ```js module.exports = { experimental: {}, }, ``` You are presented with the warning for experimental features, even though you haven't actually enabled any experiments. This PR checks there's at least one key in the `experimental` object before logging the warning. --- packages/next/server/config.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/next/server/config.ts b/packages/next/server/config.ts index 1250ea0dfe38..2e334084a915 100644 --- a/packages/next/server/config.ts +++ b/packages/next/server/config.ts @@ -67,8 +67,9 @@ function assignDefaults(userConfig: { [key: string]: any }) { if ( key === 'experimental' && - value !== undefined && - value !== defaultConfig[key] + value !== defaultConfig[key] && + typeof value === 'object' && + Object.keys(value).length > 0 ) { experimentalWarning() } From a52bd712fe797b59cfd05ceaa4c33096a0c346ff Mon Sep 17 00:00:00 2001 From: Maedah Batool Date: Thu, 17 Feb 2022 15:17:26 -0800 Subject: [PATCH 06/54] Update details about Cache Control Headers (#34416) We need to update details about adding Serverless Functions' `Cache-Control` headers to `next.config.js` files. And inform developers that these headers cannot be set in `next.config.js` files. ## Bug - [x] [Slack Thread](https://vercel.slack.com/archives/C02F56A54LU/p1644993522741169) - [ ] Integration tests added - [ ] Errors have helpful link attached, see `contributing.md` ## Feature - [ ] Implements an existing feature request or RFC. Make sure the feature request has been accepted for implementation before opening a PR. - [ ] Related issues linked using `fixes #number` - [ ] Integration tests added - [x] Documentation added - [ ] Telemetry added. In case of a feature if it's used or not. - [ ] Errors have helpful link attached, see `contributing.md` ## Documentation / Examples - [x] Make sure the linting passes by running `yarn lint` Co-authored-by: Steven <229881+styfle@users.noreply.github.com> --- docs/api-reference/next.config.js/headers.md | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/docs/api-reference/next.config.js/headers.md b/docs/api-reference/next.config.js/headers.md index 5905a0860dd9..3fb5be5578a3 100644 --- a/docs/api-reference/next.config.js/headers.md +++ b/docs/api-reference/next.config.js/headers.md @@ -376,7 +376,20 @@ module.exports = { ### Cache-Control -Cache-Control headers set in next.config.js will be overwritten in production to ensure that static assets can be cached effectively. If you need to revalidate the cache of a page that has been [statically generated](https://nextjs.org/docs/basic-features/pages#static-generation-recommended), you can do so by setting `revalidate` in the page's [`getStaticProps`](/docs/basic-features/data-fetching/get-static-props.md) function. +You can set the `Cache-Control` header in your [Next.js API Routes](/docs/api-routes/introduction.md) by using the `res.setHeader` method: + +```js +// pages/api/user.js + +export default function handler(req, res) { + res.setHeader('Cache-Control', 's-maxage=86400') + res.status(200).json({ name: 'John Doe' }) +} +``` + +You cannot set `Cache-Control` headers in `next.config.js` file as these will be overwritten in production to ensure that API Routes and static assets are cached effectively. + +If you need to revalidate the cache of a page that has been [statically generated](/docs/basic-features/pages.md#static-generation-recommended), you can do so by setting the `revalidate` prop in the page's [`getStaticProps`](/docs/basic-features/data-fetching/get-static-props.md) function. ## Related From 944c734d04e1d0a949fb19639bf5ca4847b154f7 Mon Sep 17 00:00:00 2001 From: Gerald Monaco Date: Thu, 17 Feb 2022 16:18:28 -0800 Subject: [PATCH 07/54] Add `unstable_useFlushEffects` hook (#34117) Implements https://github.com/vercel/next.js/issues/30997 with some minor tweaks to the design: * The hook is moved to Client Components (e.g. `pages/_app` instead of `pages/_document`). This was a silly oversight in the original design: the hook needs to be called during server prerendering. * `useFlushEffects` instead of `useFlushEffect` as there isn't a particularly safe way to implement the singular semantics as a Client Component hook given the current implementation of server rendering. --- Fixes #30997 --- errors/client-flush-effects.md | 24 + errors/manifest.json | 8 + errors/multiple-flush-effects.md | 9 + packages/next/client/streaming/index.ts | 1 + packages/next/server/render-result.ts | 4 +- packages/next/server/render.tsx | 411 +++++++++++------- packages/next/shared/lib/flush-effects.ts | 21 + .../react-18/app/components/red.js | 21 + test/integration/react-18/app/package.json | 1 + .../app/pages/use-flush-effect/client.js | 36 ++ .../app/pages/use-flush-effect/custom.js | 12 + .../pages/use-flush-effect/multiple-calls.js | 23 + .../app/pages/use-flush-effect/styled-jsx.js | 30 ++ test/integration/react-18/test/concurrent.js | 44 +- test/integration/react-18/test/index.test.js | 4 +- 15 files changed, 492 insertions(+), 157 deletions(-) create mode 100644 errors/client-flush-effects.md create mode 100644 errors/multiple-flush-effects.md create mode 100644 packages/next/shared/lib/flush-effects.ts create mode 100644 test/integration/react-18/app/components/red.js create mode 100644 test/integration/react-18/app/pages/use-flush-effect/client.js create mode 100644 test/integration/react-18/app/pages/use-flush-effect/custom.js create mode 100644 test/integration/react-18/app/pages/use-flush-effect/multiple-calls.js create mode 100644 test/integration/react-18/app/pages/use-flush-effect/styled-jsx.js diff --git a/errors/client-flush-effects.md b/errors/client-flush-effects.md new file mode 100644 index 000000000000..64f107b49990 --- /dev/null +++ b/errors/client-flush-effects.md @@ -0,0 +1,24 @@ +# `useFlushEffects` can not be called on the client + +#### Why This Error Occurred + +The `useFlushEffects` hook was executed while rendering a component on the client, or in another unsupported environment. + +#### Possible Ways to Fix It + +The `useFlushEffects` hook can only be called while _server rendering a client component_. As a best practice, we recommend creating a wrapper hook: + +```jsx +// lib/use-style-libraries.js +import { useFlushEffects } from 'next/streaming' + +export default function useStyleLibraries() { + if (typeof window === 'undefined') { + // eslint-disable-next-line react-hooks/rules-of-hooks + useFlushEffects([ + /* ... */ + ]) + } + /* ... */ +} +``` diff --git a/errors/manifest.json b/errors/manifest.json index b14e95bfdd42..d8afb228b260 100644 --- a/errors/manifest.json +++ b/errors/manifest.json @@ -623,6 +623,14 @@ { "title": "opening-an-issue", "path": "/errors/opening-an-issue.md" + }, + { + "title": "multiple-flush-effects", + "path": "/errors/multiple-flush-effects.md" + }, + { + "title": "client-flush-effects", + "path": "/errors/client-flush-effects.md" } ] } diff --git a/errors/multiple-flush-effects.md b/errors/multiple-flush-effects.md new file mode 100644 index 000000000000..51a9d7a58a0d --- /dev/null +++ b/errors/multiple-flush-effects.md @@ -0,0 +1,9 @@ +# The `useFlushEffects` hook cannot be used more than once. + +#### Why This Error Occurred + +The `useFlushEffects` hook is being used more than once while a page is being rendered. + +#### Possible Ways to Fix It + +The `useFlushEffects` hook should only be called inside the body of the `pages/_app` component, before returning any `` boundaries. Restructure your application to prevent extraneous calls. diff --git a/packages/next/client/streaming/index.ts b/packages/next/client/streaming/index.ts index f6fc5aaf42e4..d072e85795ae 100644 --- a/packages/next/client/streaming/index.ts +++ b/packages/next/client/streaming/index.ts @@ -1,2 +1,3 @@ export { useRefreshRoot as unstable_useRefreshRoot } from './refresh' export { useWebVitalsReport as unstable_useWebVitalsReport } from './vitals' +export { useFlushEffects as unstable_useFlushEffects } from '../../shared/lib/flush-effects' diff --git a/packages/next/server/render-result.ts b/packages/next/server/render-result.ts index 052fbc85fa0d..e7c2cd51b4b6 100644 --- a/packages/next/server/render-result.ts +++ b/packages/next/server/render-result.ts @@ -1,9 +1,9 @@ import type { ServerResponse } from 'http' export default class RenderResult { - _result: string | ReadableStream + _result: string | ReadableStream - constructor(response: string | ReadableStream) { + constructor(response: string | ReadableStream) { this._result = response } diff --git a/packages/next/server/render.tsx b/packages/next/server/render.tsx index 573fdf5d268f..d18e1f336b00 100644 --- a/packages/next/server/render.tsx +++ b/packages/next/server/render.tsx @@ -65,6 +65,7 @@ import isError from '../lib/is-error' import { readableStreamTee } from './web/utils' import { ImageConfigContext } from '../shared/lib/image-config-context' import { ImageConfigComplete } from './image-config' +import { FlushEffectsContext } from '../shared/lib/flush-effects' let optimizeAmp: typeof import('./optimize-amp').default let getFontDefinitionFromManifest: typeof import('./font-utils').getFontDefinitionFromManifest @@ -301,19 +302,18 @@ function checkRedirectValues( const rscCache = new Map() function createRSCHook() { - const decoder = new TextDecoder() - const encoder = new TextEncoder() - return ( - writable: WritableStream, + writable: WritableStream, id: string, - req: ReadableStream, + req: ReadableStream, bootstrap: boolean ) => { let entry = rscCache.get(id) if (!entry) { const [renderStream, forwardStream] = readableStreamTee(req) - entry = createFromReadableStream(renderStream) + entry = createFromReadableStream( + pipeThrough(renderStream, createTextEncoderStream()) + ) rscCache.set(id, entry) let bootstrapped = false @@ -324,11 +324,10 @@ function createRSCHook() { if (bootstrap && !bootstrapped) { bootstrapped = true writer.write( - encoder.encode( - `` - ) + `` ) } if (done) { @@ -336,11 +335,11 @@ function createRSCHook() { writer.close() } else { writer.write( - encoder.encode( - `` - ) + `` ) process() } @@ -365,7 +364,7 @@ function createServerComponentRenderer( runtime, }: { cachePrefix: string - transformStream: TransformStream + transformStream: TransformStream serverComponentManifest: NonNullable runtime: 'nodejs' | 'edge' } @@ -381,9 +380,12 @@ function createServerComponentRenderer( const writable = transformStream.writable const ServerComponentWrapper = (props: any) => { const id = (React as any).useId() - const reqStream = renderToReadableStream( - renderFlight(App, OriginalComponent, props), - serverComponentManifest + const reqStream: ReadableStream = pipeThrough( + renderToReadableStream( + renderFlight(App, OriginalComponent, props), + serverComponentManifest + ), + createTextDecoderStream() ) const response = useRSCResponse( @@ -478,8 +480,10 @@ export async function renderToHTML( let Component: React.ComponentType<{}> | ((props: any) => JSX.Element) = renderOpts.Component - let serverComponentsInlinedTransformStream: TransformStream | null = - null + let serverComponentsInlinedTransformStream: TransformStream< + string, + string + > | null = null if (isServerComponent) { serverComponentsInlinedTransformStream = new TransformStream() @@ -734,33 +738,68 @@ export async function renderToHTML( const nextExport = !isSSG && (renderOpts.nextExport || (dev && (isAutoExport || isFallback))) + const styledJsxFlushEffect = () => { + const styles = jsxStyleRegistry.styles() + jsxStyleRegistry.flush() + return <>{styles} + } + + let flushEffects: Array<() => React.ReactNode> | null = null + function FlushEffectContainer({ children }: { children: JSX.Element }) { + // If the client tree suspends, this component will be rendered multiple + // times before we flush. To ensure we don't call old callbacks corresponding + // to a previous render, we clear any registered callbacks whenever we render. + flushEffects = null + + const flushEffectsImpl = React.useCallback( + (callbacks: Array<() => React.ReactNode>) => { + if (flushEffects) { + throw new Error( + 'The `useFlushEffects` hook cannot be used more than once.' + + '\nRead more: https://nextjs.org/docs/messages/multiple-flush-effects' + ) + } + flushEffects = callbacks + }, + [] + ) + + return ( + + {children} + + ) + } + const AppContainer = ({ children }: { children: JSX.Element }) => ( - - - { - head = state - }, - updateScripts: (scripts) => { - scriptLoader = scripts - }, - scripts: {}, - mountedInstances: new Set(), - }} - > - reactLoadableModules.push(moduleName)} + + + + { + head = state + }, + updateScripts: (scripts) => { + scriptLoader = scripts + }, + scripts: {}, + mountedInstances: new Set(), + }} > - - - {children} - - - - - - + reactLoadableModules.push(moduleName)} + > + + + {children} + + + + + + + ) // The `useId` API uses the path indexes to generate an ID for each node. @@ -1141,15 +1180,21 @@ export async function renderToHTML( if (isResSent(res) && !isSSG) return null if (renderServerComponentData) { - const stream: ReadableStream = renderToReadableStream( - renderFlight(App, OriginalComponent, { - ...props.pageProps, - ...serverComponentProps, - }), - serverComponentManifest + const stream: ReadableStream = pipeThrough( + renderToReadableStream( + renderFlight(App, OriginalComponent, { + ...props.pageProps, + ...serverComponentProps, + }), + serverComponentManifest + ), + createTextDecoderStream() ) return new RenderResult( - pipeThrough(stream, createBufferedTransformStream()) + pipeThrough( + pipeThrough(stream, createBufferedTransformStream()), + createTextEncoderStream() + ) ) } @@ -1290,14 +1335,34 @@ export async function renderToHTML( // up to date when getWrappedApp is called const content = renderContent() - return await renderToStream( + const flushEffectHandler = async () => { + const allFlushEffects = [ + styledJsxFlushEffect, + ...(flushEffects || []), + ] + const flushEffectStream = await renderToStream({ + ReactDOMServer, + element: ( + <> + {allFlushEffects.map((flushEffect, i) => ( + {flushEffect()} + ))} + + ), + generateStaticHTML: true, + }) + + return await streamToString(flushEffectStream) + } + + return await renderToStream({ ReactDOMServer, - content, + element: content, suffix, - serverComponentsInlinedTransformStream?.readable ?? - streamFromArray([]), - generateStaticHTML || !hasConcurrentFeatures - ) + dataStream: serverComponentsInlinedTransformStream?.readable, + generateStaticHTML: generateStaticHTML || !hasConcurrentFeatures, + flushEffectHandler, + }) } } else { const content = renderContent() @@ -1308,12 +1373,15 @@ export async function renderToHTML( bodyResult = (suffix: string) => streamFromArray([result, suffix]) } + const styles = jsxStyleRegistry.styles() + jsxStyleRegistry.flush() + return { bodyResult, documentElement: () => (Document as any)(), head, headTags: [], - styles: jsxStyleRegistry.styles(), + styles, } } } @@ -1423,13 +1491,11 @@ export async function renderToHTML( let documentHTML: string if (hasConcurrentFeatures) { - const documentStream = await renderToStream( + const documentStream = await renderToStream({ ReactDOMServer, - document, - null, - streamFromArray([]), - true - ) + element: document, + generateStaticHTML: true, + }) documentHTML = await streamToString(documentStream) } else { documentHTML = ReactDOMServer.renderToStaticMarkup(document) @@ -1471,7 +1537,7 @@ export async function renderToHTML( prefix.push('') } - let streams: Array = [ + let streams = [ streamFromArray(prefix), await documentResult.bodyResult(renderTargetSuffix), ] @@ -1534,7 +1600,9 @@ export async function renderToHTML( return new RenderResult(html) } - return new RenderResult(chainStreams(streams)) + return new RenderResult( + pipeThrough(chainStreams(streams), createTextEncoderStream()) + ) } function errorToJSON(err: Error) { @@ -1561,23 +1629,25 @@ function serializeError( } } -function createTransformStream({ +function createTransformStream({ flush, transform, }: { - flush?: (controller: TransformStreamDefaultController) => Promise | void + flush?: ( + controller: TransformStreamDefaultController + ) => Promise | void transform?: ( - chunk: Uint8Array, - controller: TransformStreamDefaultController - ) => void -}): TransformStream { + chunk: Input, + controller: TransformStreamDefaultController + ) => Promise | void +}): TransformStream { const source = new TransformStream() const sink = new TransformStream() const reader = source.readable.getReader() const writer = sink.writable.getWriter() const controller = { - enqueue(chunk: Uint8Array) { + enqueue(chunk: Output) { writer.write(chunk) }, @@ -1611,7 +1681,10 @@ function createTransformStream({ } if (transform) { - transform(value, controller) + const maybePromise = transform(value, controller) + if (maybePromise) { + await maybePromise + } } else { controller.enqueue(value) } @@ -1627,10 +1700,27 @@ function createTransformStream({ } } -function createBufferedTransformStream(): TransformStream { +function createTextDecoderStream(): TransformStream { const decoder = new TextDecoder() + return createTransformStream({ + transform(chunk, controller) { + controller.enqueue( + typeof chunk === 'string' ? chunk : decoder.decode(chunk) + ) + }, + }) +} + +function createTextEncoderStream(): TransformStream { const encoder = new TextEncoder() + return createTransformStream({ + transform(chunk, controller) { + controller.enqueue(encoder.encode(chunk)) + }, + }) +} +function createBufferedTransformStream(): TransformStream { let bufferedString = '' let pendingFlush: Promise | null = null @@ -1638,7 +1728,7 @@ function createBufferedTransformStream(): TransformStream { if (!pendingFlush) { pendingFlush = new Promise((resolve) => { setTimeout(() => { - controller.enqueue(encoder.encode(bufferedString)) + controller.enqueue(bufferedString) bufferedString = '' pendingFlush = null resolve() @@ -1650,7 +1740,7 @@ function createBufferedTransformStream(): TransformStream { return createTransformStream({ transform(chunk, controller) { - bufferedString += decoder.decode(chunk) + bufferedString += chunk flushBuffer(controller) }, @@ -1662,24 +1752,37 @@ function createBufferedTransformStream(): TransformStream { }) } -function renderToStream( - ReactDOMServer: typeof import('react-dom/server'), - element: React.ReactElement, - suffix: string | null, - dataStream: ReadableStream, +function createFlushEffectStream( + handleFlushEffect: () => Promise +): TransformStream { + return createTransformStream({ + async transform(chunk, controller) { + const extraChunk = await handleFlushEffect() + controller.enqueue(extraChunk + chunk) + }, + }) +} + +function renderToStream({ + ReactDOMServer, + element, + suffix, + dataStream, + generateStaticHTML, + flushEffectHandler, +}: { + ReactDOMServer: typeof import('react-dom/server') + element: React.ReactElement + suffix?: string + dataStream?: ReadableStream generateStaticHTML: boolean -): Promise { + flushEffectHandler?: () => Promise +}): Promise> { return new Promise((resolve, reject) => { let resolved = false - const closeTagString = '' - const encoder = new TextEncoder() - const suffixState = suffix - ? { - closeTag: encoder.encode(closeTagString), - suffixUnclosed: encoder.encode(suffix.split(closeTagString)[0]), - } - : null + const closeTag = '' + const suffixUnclosed = suffix ? suffix.split(closeTag)[0] : null const doResolve = () => { if (!resolved) { @@ -1687,47 +1790,52 @@ function renderToStream( // React will call our callbacks synchronously, so we need to // defer to a microtask to ensure `stream` is set. - Promise.resolve().then(() => - resolve( - pipeThrough( - pipeThrough( - pipeThrough(stream, createBufferedTransformStream()), - createInlineDataStream( - pipeThrough( - dataStream, - createPrefixStream(suffixState?.suffixUnclosed ?? null) - ) - ) - ), - createSuffixStream(suffixState?.closeTag ?? null) + resolve( + Promise.resolve().then(() => { + const transforms: Array> = [ + createBufferedTransformStream(), + flushEffectHandler + ? createFlushEffectStream(flushEffectHandler) + : null, + suffixUnclosed != null + ? createPrefixStream(suffixUnclosed) + : null, + dataStream ? createInlineDataStream(dataStream) : null, + suffixUnclosed != null ? createSuffixStream(closeTag) : null, + ].filter(Boolean) as any + + return transforms.reduce( + (readable, transform) => pipeThrough(readable, transform), + renderStream ) - ) + }) ) } } - const stream: ReadableStream = ( - ReactDOMServer as any - ).renderToReadableStream(element, { - onError(err: Error) { - if (!resolved) { - resolved = true - reject(err) - } - }, - onCompleteShell() { - if (!generateStaticHTML) { + const renderStream = pipeThrough( + (ReactDOMServer as any).renderToReadableStream(element, { + onError(err: Error) { + if (!resolved) { + resolved = true + reject(err) + } + }, + onCompleteShell() { + if (!generateStaticHTML) { + doResolve() + } + }, + onCompleteAll() { doResolve() - } - }, - onCompleteAll() { - doResolve() - }, - }) + }, + }), + createTextDecoderStream() + ) }) } -function createSuffixStream(suffix: Uint8Array | null) { +function createSuffixStream(suffix: string): TransformStream { return createTransformStream({ flush(controller) { if (suffix) { @@ -1737,15 +1845,16 @@ function createSuffixStream(suffix: Uint8Array | null) { }) } -function createPrefixStream(prefix: Uint8Array | null) { +function createPrefixStream(prefix: string): TransformStream { let prefixFlushed = false return createTransformStream({ transform(chunk, controller) { if (!prefixFlushed && prefix) { prefixFlushed = true - controller.enqueue(prefix) + controller.enqueue(chunk + prefix) + } else { + controller.enqueue(chunk) } - controller.enqueue(chunk) }, flush(controller) { if (!prefixFlushed && prefix) { @@ -1757,14 +1866,14 @@ function createPrefixStream(prefix: Uint8Array | null) { } function createInlineDataStream( - dataStream: ReadableStream | null -): TransformStream { + dataStream: ReadableStream +): TransformStream { let dataStreamFinished: Promise | null = null return createTransformStream({ transform(chunk, controller) { controller.enqueue(chunk) - if (!dataStreamFinished && dataStream) { + if (!dataStreamFinished) { const dataStreamReader = dataStream.getReader() // We are buffering here for the inlined data stream because the @@ -1798,9 +1907,9 @@ function createInlineDataStream( }) } -function pipeTo( - readable: ReadableStream, - writable: WritableStream, +function pipeTo( + readable: ReadableStream, + writable: WritableStream, options?: { preventClose: boolean } ) { let resolver: () => void @@ -1827,15 +1936,15 @@ function pipeTo( return promise } -function pipeThrough( - readable: ReadableStream, - transformStream: TransformStream +function pipeThrough( + readable: ReadableStream, + transformStream: TransformStream ) { pipeTo(readable, transformStream.writable) return transformStream.readable } -function chainStreams(streams: ReadableStream[]): ReadableStream { +function chainStreams(streams: ReadableStream[]): ReadableStream { const { readable, writable } = new TransformStream() let promise = Promise.resolve() @@ -1850,22 +1959,20 @@ function chainStreams(streams: ReadableStream[]): ReadableStream { return readable } -function streamFromArray(strings: string[]): ReadableStream { - const encoder = new TextEncoder() - const chunks = Array.from(strings.map((str) => encoder.encode(str))) +function streamFromArray(strings: string[]): ReadableStream { + // Note: we use a TransformStream here instead of instantiating a ReadableStream + // because the built-in ReadableStream polyfill runs strings through TextEncoder. + const { readable, writable } = new TransformStream() - return new ReadableStream({ - start(controller) { - chunks.forEach((chunk) => controller.enqueue(chunk)) - controller.close() - }, - }) + const writer = writable.getWriter() + strings.forEach((str) => writer.write(str)) + writer.close() + + return readable } -async function streamToString(stream: ReadableStream): Promise { +async function streamToString(stream: ReadableStream): Promise { const reader = stream.getReader() - const decoder = new TextDecoder() - let bufferedString = '' while (true) { @@ -1875,7 +1982,7 @@ async function streamToString(stream: ReadableStream): Promise { return bufferedString } - bufferedString += decoder.decode(value) + bufferedString += value } } diff --git a/packages/next/shared/lib/flush-effects.ts b/packages/next/shared/lib/flush-effects.ts new file mode 100644 index 000000000000..341c126ed9e6 --- /dev/null +++ b/packages/next/shared/lib/flush-effects.ts @@ -0,0 +1,21 @@ +import { createContext, useContext } from 'react' + +export type FlushEffectsHook = (callbacks: Array<() => React.ReactNode>) => void + +export const FlushEffectsContext: React.Context = + createContext(null as any) + +export function useFlushEffects(callbacks: Array<() => React.ReactNode>): void { + const flushEffectsImpl = useContext(FlushEffectsContext) + if (!flushEffectsImpl) { + throw new Error( + `useFlushEffects can not be called on the client.` + + `\nRead more: https://nextjs.org/docs/messages/client-flush-effects` + ) + } + return flushEffectsImpl(callbacks) +} + +if (process.env.NODE_ENV !== 'production') { + FlushEffectsContext.displayName = 'FlushEffectsContext' +} diff --git a/test/integration/react-18/app/components/red.js b/test/integration/react-18/app/components/red.js new file mode 100644 index 000000000000..8f92c5ccc52d --- /dev/null +++ b/test/integration/react-18/app/components/red.js @@ -0,0 +1,21 @@ +import React from 'react' +import { useCachedPromise } from './promise-cache' + +export default function Styled({ name }) { + useCachedPromise( + name, + () => new Promise((resolve) => setTimeout(resolve, 1000)), + true + ) + + return ( +
+

This is Red.

+ +
+ ) +} diff --git a/test/integration/react-18/app/package.json b/test/integration/react-18/app/package.json index f9dafc993a79..478f826a5969 100644 --- a/test/integration/react-18/app/package.json +++ b/test/integration/react-18/app/package.json @@ -1,6 +1,7 @@ { "scripts": { "next": "node -r ../test/require-hook.js ../../../../packages/next/dist/bin/next", + "debug": "NODE_OPTIONS='--inspect' node -r ../../react-18/test/require-hook.js ../../../../packages/next/dist/bin/next", "dev": "yarn next dev", "build": "yarn next build", "start": "yarn next start" diff --git a/test/integration/react-18/app/pages/use-flush-effect/client.js b/test/integration/react-18/app/pages/use-flush-effect/client.js new file mode 100644 index 000000000000..884d7f9266ec --- /dev/null +++ b/test/integration/react-18/app/pages/use-flush-effect/client.js @@ -0,0 +1,36 @@ +import { unstable_useFlushEffects } from 'next/streaming' +import React from 'react' + +class ErrorBoundary extends React.Component { + state = {} + + static getDerivedStateFromError(error) { + return { error } + } + + render() { + return this.state.error ? ( + {this.state.error.message} + ) : ( + this.props.children + ) + } +} + +function Component() { + unstable_useFlushEffects([]) + return null +} + +export default function Client() { + return ( + + + + ) +} + +export async function getServerSideProps() { + // disable exporting this page + return { props: {} } +} diff --git a/test/integration/react-18/app/pages/use-flush-effect/custom.js b/test/integration/react-18/app/pages/use-flush-effect/custom.js new file mode 100644 index 000000000000..f8d09da3462d --- /dev/null +++ b/test/integration/react-18/app/pages/use-flush-effect/custom.js @@ -0,0 +1,12 @@ +import { unstable_useFlushEffects } from 'next/streaming' + +export default function Custom() { + if (typeof window === 'undefined') { + // eslint-disable-next-line react-hooks/rules-of-hooks + unstable_useFlushEffects([ + () => foo, + () => bar, + ]) + } + return null +} diff --git a/test/integration/react-18/app/pages/use-flush-effect/multiple-calls.js b/test/integration/react-18/app/pages/use-flush-effect/multiple-calls.js new file mode 100644 index 000000000000..5417eac3457a --- /dev/null +++ b/test/integration/react-18/app/pages/use-flush-effect/multiple-calls.js @@ -0,0 +1,23 @@ +import { unstable_useFlushEffects } from 'next/streaming' + +function Component() { + if (typeof window === 'undefined') { + // eslint-disable-next-line react-hooks/rules-of-hooks + unstable_useFlushEffects([]) + } + return null +} + +export default function MultipleCalls() { + return ( + <> + + + + ) +} + +export async function getServerSideProps() { + // disable exporting this page + return { props: {} } +} diff --git a/test/integration/react-18/app/pages/use-flush-effect/styled-jsx.js b/test/integration/react-18/app/pages/use-flush-effect/styled-jsx.js new file mode 100644 index 000000000000..27105d3f9c3a --- /dev/null +++ b/test/integration/react-18/app/pages/use-flush-effect/styled-jsx.js @@ -0,0 +1,30 @@ +import React from 'react' +import dynamic from 'next/dynamic' + +const Red = dynamic(() => import('../../components/red'), { + suspense: true, +}) + +function Blue() { + return ( +
+

This is Blue.

+ +
+ ) +} + +export default function StyledJsx() { + return ( + <> + + + + + + ) +} diff --git a/test/integration/react-18/test/concurrent.js b/test/integration/react-18/test/concurrent.js index 2f11225ba782..15eb424b16be 100644 --- a/test/integration/react-18/test/concurrent.js +++ b/test/integration/react-18/test/concurrent.js @@ -1,7 +1,7 @@ /* eslint-env jest */ import webdriver from 'next-webdriver' -import { check } from 'next-test-utils' +import { check, renderViaHTTP } from 'next-test-utils' export default (context, _render) => { async function withBrowser(path, cb) { @@ -70,4 +70,46 @@ export default (context, _render) => { ) }) }) + + it('throws if useFlushEffects is used more than once', async () => { + await renderViaHTTP(context.appPort, '/use-flush-effect/multiple-calls') + expect(context.stderr).toContain( + 'Error: The `useFlushEffects` hook cannot be used more than once.' + ) + }) + + it('throws if useFlushEffects is called on the client', async () => { + await withBrowser('/use-flush-effect/client', async (browser) => { + await check( + () => browser.waitForElementByCss('#error').text(), + /useFlushEffects can not be called on the client/ + ) + }) + }) + + it('flushes styles as the page renders', async () => { + await withBrowser('/use-flush-effect/styled-jsx', async (browser) => { + await check( + () => browser.waitForElementByCss('#__jsx-900f996af369fc74').text(), + /blue/ + ) + await check( + () => browser.waitForElementByCss('#__jsx-c74678abd3b78a').text(), + /red/ + ) + }) + }) + + it('flushes custom effects', async () => { + await withBrowser('/use-flush-effect/custom', async (browser) => { + await check( + () => browser.waitForElementByCss('#custom-flush-effect-1').text(), + /foo/ + ) + await check( + () => browser.waitForElementByCss('#custom-flush-effect-2').text(), + /bar/ + ) + }) + }) } diff --git a/test/integration/react-18/test/index.test.js b/test/integration/react-18/test/index.test.js index f0e634e92477..8d68baaef191 100644 --- a/test/integration/react-18/test/index.test.js +++ b/test/integration/react-18/test/index.test.js @@ -135,9 +135,9 @@ describe('Blocking mode', () => { dynamicHello.restore() }) - runTests('`runtime` is disabled', (context) => + runTests('`runtime` is disabled', (context) => { blocking(context, (p, q) => renderViaHTTP(context.appPort, p, q)) - ) + }) }) function runTestsAgainstRuntime(runtime) { From a74af1f31d6b6be84350679166eb4a236f1186a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bal=C3=A1zs=20Orb=C3=A1n?= Date: Fri, 18 Feb 2022 05:15:57 +0100 Subject: [PATCH 08/54] fix: mock image path as `next/image` expects it (#34350) The default mock value caused `next/image` to throw an error. Fixes #33976 ## Bug - [ ] Related issues linked using `fixes #number` - [ ] Integration tests added - [ ] Errors have helpful link attached, see `contributing.md` ## Feature - [ ] Implements an existing feature request or RFC. Make sure the feature request has been accepted for implementation before opening a PR. - [ ] Related issues linked using `fixes #number` - [ ] Integration tests added - [ ] Documentation added - [ ] Telemetry added. In case of a feature if it's used or not. - [ ] Errors have helpful link attached, see `contributing.md` ## Documentation / Examples - [ ] Make sure the linting passes by running `yarn lint` --- docs/testing.md | 16 +++++++--------- examples/with-jest-babel/__mocks__/fileMock.js | 7 ++++++- examples/with-jest-babel/jest.config.js | 2 +- packages/next/build/jest/__mocks__/fileMock.js | 7 ++++++- packages/next/build/jest/jest.ts | 2 +- test/production/next/jest/index.test.ts | 6 ++++++ 6 files changed, 27 insertions(+), 13 deletions(-) diff --git a/docs/testing.md b/docs/testing.md index 6b0bc97e2699..6b4bdfaa17ce 100644 --- a/docs/testing.md +++ b/docs/testing.md @@ -325,7 +325,7 @@ module.exports = { // Handle image imports // https://jestjs.io/docs/webpack#handling-static-assets - '^.+\\.(jpg|jpeg|png|gif|webp|avif|svg)$': `/__mocks__/fileMock.js`, + '^.+\\.(png|jpg|jpeg|gif|webp|avif|ico|bmp|svg)$/i': `/__mocks__/fileMock.js`, // Handle module aliases '^@/components/(.*)$': '/components/$1', @@ -354,7 +354,12 @@ Stylesheets and images aren't used in the tests but importing them may cause err ```js // __mocks__/fileMock.js -module.exports = 'test-file-stub' +module.exports = { + src: '/img.jpg', + height: 24, + width: 24, + blurDataURL: 'data:image/png;base64,imagedata', +} ``` ```js @@ -362,13 +367,6 @@ module.exports = 'test-file-stub' module.exports = {} ``` -If you're running into the issue `"Failed to parse src "test-file-stub" on 'next/image'"`, add a '/' to your fileMock. - -```js -// __mocks__/fileMock.js -module.exports = '/test-file-stub' -``` - For more information on handling static assets, please refer to the [Jest Docs](https://jestjs.io/docs/webpack#handling-static-assets). **Optional: Extend Jest with custom matchers** diff --git a/examples/with-jest-babel/__mocks__/fileMock.js b/examples/with-jest-babel/__mocks__/fileMock.js index af027da05073..4a271a81f5da 100644 --- a/examples/with-jest-babel/__mocks__/fileMock.js +++ b/examples/with-jest-babel/__mocks__/fileMock.js @@ -1,3 +1,8 @@ // Read more at "Handling stylesheets and image imports" on https://nextjs.org/docs/testing -module.exports = 'test-file-stub' +module.exports = { + src: '/img.jpg', + height: 24, + width: 24, + blurDataURL: 'data:image/png;base64,imagedata', +} diff --git a/examples/with-jest-babel/jest.config.js b/examples/with-jest-babel/jest.config.js index 5a7adbd4d5f9..6351201bf42a 100644 --- a/examples/with-jest-babel/jest.config.js +++ b/examples/with-jest-babel/jest.config.js @@ -16,7 +16,7 @@ module.exports = { // Handle image imports // https://jestjs.io/docs/webpack#handling-static-assets - '^.+\\.(jpg|jpeg|png|gif|webp|avif|svg)$': `/__mocks__/fileMock.js`, + '^.+\\.(png|jpg|jpeg|gif|webp|avif|ico|bmp|svg)$': `/__mocks__/fileMock.js`, // Handle module aliases '^@/components/(.*)$': '/components/$1', diff --git a/packages/next/build/jest/__mocks__/fileMock.js b/packages/next/build/jest/__mocks__/fileMock.js index 0e56c5b5f765..8761cdaa4652 100644 --- a/packages/next/build/jest/__mocks__/fileMock.js +++ b/packages/next/build/jest/__mocks__/fileMock.js @@ -1 +1,6 @@ -module.exports = 'test-file-stub' +module.exports = { + src: '/img.jpg', + height: 24, + width: 24, + blurDataURL: 'data:image/png;base64,imagedata', +} diff --git a/packages/next/build/jest/jest.ts b/packages/next/build/jest/jest.ts index 88fd60b4854b..45733ea25322 100644 --- a/packages/next/build/jest/jest.ts +++ b/packages/next/build/jest/jest.ts @@ -81,7 +81,7 @@ export default function nextJest(options: { dir?: string } = {}) { '^.+\\.(css|sass|scss)$': require.resolve('./__mocks__/styleMock.js'), // Handle image imports - '^.+\\.(jpg|jpeg|png|gif|webp|avif|svg)$': require.resolve( + '^.+\\.(png|jpg|jpeg|gif|webp|avif|ico|bmp|svg)$': require.resolve( `./__mocks__/fileMock.js` ), diff --git a/test/production/next/jest/index.test.ts b/test/production/next/jest/index.test.ts index 6d110b910697..73265c52fcdb 100644 --- a/test/production/next/jest/index.test.ts +++ b/test/production/next/jest/index.test.ts @@ -8,6 +8,8 @@ describe('next/jest', () => { beforeAll(async () => { next = await createNext({ files: { + 'public/vercel.svg': + '', 'components/comp.js': ` export default function Comp() { return

Hello Dynamic

; @@ -15,6 +17,8 @@ describe('next/jest', () => { `, 'pages/index.js': ` import dynamic from "next/dynamic"; + import Image from "next/image"; + import img from "../public/vercel.svg"; const Comp = dynamic(() => import("../components/comp"), { loading: () =>

Loading...

, @@ -23,6 +27,8 @@ describe('next/jest', () => { export default function Page() { return <> + logo + logo 2

hello world

} From 7c103fac7d96f7ebadf03620044f3b2c02c4df48 Mon Sep 17 00:00:00 2001 From: Naoyuki Kanezawa Date: Fri, 18 Feb 2022 15:39:30 +0700 Subject: [PATCH 09/54] fix process polyfill on middleware (#34426) Fixes the problem that global `process` variable has only the `env` field. Also fixed the issue that the `env` field is empty when the `process` module is used as the value of the variable (which happens when the module is contained in a dependency of application). ## Bug - [ ] Related issues linked using `fixes #number` - [x] Integration tests added - [ ] Errors have helpful link attached, see `contributing.md` --- .../webpack/loaders/next-middleware-loader.ts | 7 ++++++ packages/next/server/web/sandbox/context.ts | 1 + packages/next/server/web/sandbox/polyfills.ts | 3 ++- packages/next/types/misc.d.ts | 5 ++++ .../core/pages/global/_middleware.js | 12 ++++++++++ .../middleware/core/test/index.test.js | 24 +++++++++++++++++++ 6 files changed, 51 insertions(+), 1 deletion(-) create mode 100644 test/integration/middleware/core/pages/global/_middleware.js diff --git a/packages/next/build/webpack/loaders/next-middleware-loader.ts b/packages/next/build/webpack/loaders/next-middleware-loader.ts index 3ccdc57c01f1..3beab89b7f4a 100644 --- a/packages/next/build/webpack/loaders/next-middleware-loader.ts +++ b/packages/next/build/webpack/loaders/next-middleware-loader.ts @@ -12,6 +12,13 @@ export default function middlewareLoader(this: any) { return ` import { adapter } from 'next/dist/server/web/adapter' + // The condition is true when the "process" module is provided + if (process !== global.process) { + // prefer local process but global.process has correct "env" + process.env = global.process.env; + global.process = process; + } + var mod = require(${stringifiedPagePath}) var handler = mod.middleware || mod.default; diff --git a/packages/next/server/web/sandbox/context.ts b/packages/next/server/web/sandbox/context.ts index de3876ffae94..42f480bb6a31 100644 --- a/packages/next/server/web/sandbox/context.ts +++ b/packages/next/server/web/sandbox/context.ts @@ -202,6 +202,7 @@ function createContext(options: { File, FormData, process: { + ...polyfills.process, env: buildEnvironmentVariablesFrom(options.env), }, ReadableStream: polyfills.ReadableStream, diff --git a/packages/next/server/web/sandbox/polyfills.ts b/packages/next/server/web/sandbox/polyfills.ts index 3b47e18af6cf..ce0834745265 100644 --- a/packages/next/server/web/sandbox/polyfills.ts +++ b/packages/next/server/web/sandbox/polyfills.ts @@ -1,6 +1,7 @@ import { Crypto as WebCrypto } from 'next/dist/compiled/@peculiar/webcrypto' import { CryptoKey } from 'next/dist/compiled/@peculiar/webcrypto' import { v4 as uuid } from 'next/dist/compiled/uuid' +import processPolyfill from 'next/dist/compiled/process' import { ReadableStream } from './readable-stream' import crypto from 'crypto' @@ -13,7 +14,7 @@ export function btoa(str: string) { return Buffer.from(str, 'binary').toString('base64') } -export { CryptoKey, ReadableStream } +export { CryptoKey, ReadableStream, processPolyfill as process } export class Crypto extends WebCrypto { // @ts-ignore Remove once types are updated and we deprecate node 12 diff --git a/packages/next/types/misc.d.ts b/packages/next/types/misc.d.ts index 555d3ad83d6e..cbbc83ee852e 100644 --- a/packages/next/types/misc.d.ts +++ b/packages/next/types/misc.d.ts @@ -331,6 +331,11 @@ declare module 'next/dist/compiled/comment-json' { export = m } +declare module 'next/dist/compiled/process' { + import m from 'process' + export = m +} + declare module 'pnp-webpack-plugin' { import webpack from 'webpack4' diff --git a/test/integration/middleware/core/pages/global/_middleware.js b/test/integration/middleware/core/pages/global/_middleware.js new file mode 100644 index 000000000000..7e3020009875 --- /dev/null +++ b/test/integration/middleware/core/pages/global/_middleware.js @@ -0,0 +1,12 @@ +import { NextResponse } from 'next/server' + +export async function middleware(request, ev) { + console.log(process.env.MIDDLEWARE_TEST) + + return NextResponse.json({ + process: { + env: process.env, + nextTick: typeof process.nextTick, + }, + }) +} diff --git a/test/integration/middleware/core/test/index.test.js b/test/integration/middleware/core/test/index.test.js index 805cf48fd395..6cc789bc76d9 100644 --- a/test/integration/middleware/core/test/index.test.js +++ b/test/integration/middleware/core/test/index.test.js @@ -108,6 +108,30 @@ describe('Middleware base tests', () => { } }) }) + + describe('global', () => { + beforeAll(async () => { + context.appPort = await findPort() + context.app = await launchApp(context.appDir, context.appPort, { + env: { + MIDDLEWARE_TEST: 'asdf', + }, + }) + }) + + it('should contains process polyfill', async () => { + const res = await fetchViaHTTP(context.appPort, `/global`) + const json = await res.json() + expect(json).toEqual({ + process: { + env: { + MIDDLEWARE_TEST: 'asdf', + }, + nextTick: 'function', + }, + }) + }) + }) }) function urlTests(_log, locale = '') { From ab40370ea5b69aa4dd601907eb85d25da1140b6b Mon Sep 17 00:00:00 2001 From: Andrew Gerard Date: Fri, 18 Feb 2022 02:12:56 -0700 Subject: [PATCH 10/54] Ensure workers are not left open (#34503) This fixes #33615. If an App uses `getInitialProps`, the build function never enters [this conditional block](https://github.com/vercel/next.js/blob/a52bd712fe797b59cfd05ceaa4c33096a0c346ff/packages/next/build/index.ts#L1481-L1484) and the static worker is left open. --- packages/next/build/index.ts | 3 +++ packages/next/lib/worker.ts | 9 +++++++++ 2 files changed, 12 insertions(+) diff --git a/packages/next/build/index.ts b/packages/next/build/index.ts index 1e6eaff73433..27ed5cbe45e0 100644 --- a/packages/next/build/index.ts +++ b/packages/next/build/index.ts @@ -1910,6 +1910,9 @@ export default async function build( }) } + // ensure the worker is not left hanging + staticWorkers.close() + const analysisEnd = process.hrtime(analysisBegin) telemetry.record( eventBuildOptimize(pagePaths, { diff --git a/packages/next/lib/worker.ts b/packages/next/lib/worker.ts index 8182f2b6b0ea..023d9166e53f 100644 --- a/packages/next/lib/worker.ts +++ b/packages/next/lib/worker.ts @@ -85,4 +85,13 @@ export class Worker { this._worker = undefined return worker.end() } + + /** + * Quietly end the worker if it exists + */ + close(): void { + if (this._worker) { + this._worker.end() + } + } } From 4ad1c5a2bc3384d0eeaef2d05452748a3a9ae467 Mon Sep 17 00:00:00 2001 From: Lionel Date: Fri, 18 Feb 2022 11:03:27 +0100 Subject: [PATCH 11/54] Update wrong code snippet (#34520) Following the [example](https://nextjs.org/docs/advanced-features/custom-document), avoiding the following error: > Identifier 'Document' has already been declared ## Bug - [ ] Related issues linked using `fixes #number` - [ ] Integration tests added - [ ] Errors have helpful link attached, see `contributing.md` ## Feature - [ ] Implements an existing feature request or RFC. Make sure the feature request has been accepted for implementation before opening a PR. - [ ] Related issues linked using `fixes #number` - [ ] Integration tests added - [ ] Documentation added - [ ] Telemetry added. In case of a feature if it's used or not. - [ ] Errors have helpful link attached, see `contributing.md` ## Documentation / Examples - [ ] Make sure the linting passes by running `yarn lint` --- errors/no-stylesheets-in-head-component.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/errors/no-stylesheets-in-head-component.md b/errors/no-stylesheets-in-head-component.md index 3e3901a682dd..319cae672bfe 100644 --- a/errors/no-stylesheets-in-head-component.md +++ b/errors/no-stylesheets-in-head-component.md @@ -18,7 +18,7 @@ Add the stylesheet in a custom `Document` component. ```jsx // pages/_document.js -import Document, { Html, Head, Main, NextScript } from 'next/document' +import { Html, Head, Main, NextScript } from 'next/document' export default function Document() { return ( From ce76d1712e98d7310385c89ec1917242f7c5111c Mon Sep 17 00:00:00 2001 From: Jiachi Liu Date: Fri, 18 Feb 2022 16:25:10 +0100 Subject: [PATCH 12/54] Leverage existing component checking warning for streaming (#34526) ## Bug Fixes: #31993 * Remove the simple component checking in middleware ssr * Leverage existing components checking for Component / App / Document, if any of these component is not valid react type or is undefined nextjs will error in dev mode with redbox. Like above. image - [x] Related issues linked using `fixes #number` - [x] Integration tests added - [x] Errors have helpful link attached, see `contributing.md` --- .../next-middleware-ssr-loader/index.ts | 4 -- packages/next/server/base-server.ts | 2 +- packages/next/server/load-components.ts | 8 ++- packages/next/server/render.tsx | 4 +- test/integration/react-18/test/index.test.js | 69 +++++++++++++------ 5 files changed, 56 insertions(+), 31 deletions(-) diff --git a/packages/next/build/webpack/loaders/next-middleware-ssr-loader/index.ts b/packages/next/build/webpack/loaders/next-middleware-ssr-loader/index.ts index d457fcaa72d3..870a6b901693 100644 --- a/packages/next/build/webpack/loaders/next-middleware-ssr-loader/index.ts +++ b/packages/next/build/webpack/loaders/next-middleware-ssr-loader/index.ts @@ -39,10 +39,6 @@ export default async function middlewareSSRLoader(this: any) { const reactLoadableManifest = self.__REACT_LOADABLE_MANIFEST const rscManifest = self.__RSC_MANIFEST - if (typeof pageMod.default !== 'function') { - throw new Error('Your page must export a \`default\` component') - } - // Set server context self.__server_context = { page: ${JSON.stringify(page)}, diff --git a/packages/next/server/base-server.ts b/packages/next/server/base-server.ts index 77160619770f..19e62dcc11f2 100644 --- a/packages/next/server/base-server.ts +++ b/packages/next/server/base-server.ts @@ -1122,7 +1122,7 @@ export default abstract class Server { const isSSG = !!components.getStaticProps const hasServerProps = !!components.getServerSideProps const hasStaticPaths = !!components.getStaticPaths - const hasGetInitialProps = !!(components.Component as any).getInitialProps + const hasGetInitialProps = !!components.Component?.getInitialProps // Toggle whether or not this is a Data request const isDataReq = !!query._nextDataReq && (isSSG || hasServerProps) diff --git a/packages/next/server/load-components.ts b/packages/next/server/load-components.ts index e4c86fee8bcb..b4d4b02a0ac0 100644 --- a/packages/next/server/load-components.ts +++ b/packages/next/server/load-components.ts @@ -1,3 +1,8 @@ +import type { + AppType, + DocumentType, + NextComponentType, +} from '../shared/lib/utils' import { BUILD_MANIFEST, REACT_LOADABLE_MANIFEST, @@ -5,7 +10,6 @@ import { import { join } from 'path' import { requirePage } from './require' import { BuildManifest } from './get-page-files' -import { AppType, DocumentType } from '../shared/lib/utils' import { interopDefault } from '../lib/interop-default' import { PageConfig, @@ -22,7 +26,7 @@ export type ManifestItem = { export type ReactLoadableManifest = { [moduleId: string]: ManifestItem } export type LoadComponentsReturnType = { - Component: React.ComponentType + Component: NextComponentType pageConfig: PageConfig buildManifest: BuildManifest reactLoadableManifest: ReactLoadableManifest diff --git a/packages/next/server/render.tsx b/packages/next/server/render.tsx index d18e1f336b00..ce5b2a5d9a42 100644 --- a/packages/next/server/render.tsx +++ b/packages/next/server/render.tsx @@ -545,7 +545,7 @@ export async function renderToHTML( const defaultAppGetInitialProps = App.getInitialProps === (App as any).origGetInitialProps - const hasPageGetInitialProps = !!(Component as any).getInitialProps + const hasPageGetInitialProps = !!(Component as any)?.getInitialProps const pageIsDynamic = isDynamicRoute(pathname) @@ -561,7 +561,7 @@ export async function renderToHTML( 'getServerSideProps', 'getStaticPaths', ]) { - if ((Component as any)[methodName]) { + if ((Component as any)?.[methodName]) { throw new Error( `page ${pathname} ${methodName} ${GSSP_COMPONENT_MEMBER_ERROR}` ) diff --git a/test/integration/react-18/test/index.test.js b/test/integration/react-18/test/index.test.js index 8d68baaef191..2660a9fd20b1 100644 --- a/test/integration/react-18/test/index.test.js +++ b/test/integration/react-18/test/index.test.js @@ -11,11 +11,14 @@ import { nextStart, renderViaHTTP, fetchViaHTTP, + hasRedbox, + getRedboxHeader, } from 'next-test-utils' import blocking from './blocking' import concurrent from './concurrent' import basics from './basics' import strictMode from './strict-mode' +import webdriver from 'next-webdriver' // overrides react and react-dom to v18 const nodeArgs = ['-r', join(__dirname, 'require-hook.js')] @@ -23,6 +26,7 @@ const appDir = join(__dirname, '../app') const nextConfig = new File(join(appDir, 'next.config.js')) const dynamicHello = new File(join(appDir, 'components/dynamic-hello.js')) const unwrappedPage = new File(join(appDir, 'pages/suspense/unwrapped.js')) +const invalidPage = new File(join(appDir, 'pages/invalid.js')) const USING_CREATE_ROOT = 'Using the createRoot API for React' @@ -141,21 +145,22 @@ describe('Blocking mode', () => { }) function runTestsAgainstRuntime(runtime) { - describe(`Concurrent mode in the ${runtime} runtime`, () => { - beforeAll(async () => { - nextConfig.replace("// runtime: 'edge'", `runtime: '${runtime}'`) - dynamicHello.replace('suspense = false', `suspense = true`) - // `noSSR` mode will be ignored by suspense - dynamicHello.replace('let ssr', `let ssr = false`) - }) - afterAll(async () => { - nextConfig.restore() - dynamicHello.restore() - }) - - runTests(`runtime is set to '${runtime}'`, (context) => { + runTests( + `Concurrent mode in the ${runtime} runtime`, + (context, env) => { concurrent(context, (p, q) => renderViaHTTP(context.appPort, p, q)) + if (env === 'dev') { + it('should recover after undefined exported as default', async () => { + const browser = await webdriver(context.appPort, '/invalid') + + expect(await hasRedbox(browser)).toBe(true) + expect(await getRedboxHeader(browser)).toMatch( + `Error: The default export is not a React Component in page: "/invalid"` + ) + }) + } + it('should stream to users', async () => { const res = await fetchViaHTTP(context.appPort, '/ssr') expect(res.headers.get('etag')).toBeNull() @@ -189,17 +194,36 @@ function runTestsAgainstRuntime(runtime) { ) expect(res.headers.get('etag')).toBeDefined() }) - }) - }) + }, + { + beforeAll: (env) => { + if (env === 'dev') { + invalidPage.write(`export const value = 1`) + } + nextConfig.replace("// runtime: 'edge'", `runtime: '${runtime}'`) + dynamicHello.replace('suspense = false', `suspense = true`) + // `noSSR` mode will be ignored by suspense + dynamicHello.replace('let ssr', `let ssr = false`) + }, + afterAll: (env) => { + if (env === 'dev') { + invalidPage.delete() + } + nextConfig.restore() + dynamicHello.restore() + }, + } + ) } -function runTest(mode, name, fn) { +function runTest(env, name, fn, options) { const context = { appDir } - describe(`${name} (${mode})`, () => { + describe(`${name} (${env})`, () => { beforeAll(async () => { context.appPort = await findPort() context.stderr = '' - if (mode === 'dev') { + options?.beforeAll(env) + if (env === 'dev') { context.server = await launchApp(context.appDir, context.appPort, { nodeArgs, onStderr(msg) { @@ -217,16 +241,17 @@ function runTest(mode, name, fn) { } }) afterAll(async () => { + options?.afterAll(env) await killApp(context.server) }) - fn(context) + fn(context, env) }) } runTestsAgainstRuntime('edge') runTestsAgainstRuntime('nodejs') -function runTests(name, fn) { - runTest('dev', name, fn) - runTest('prod', name, fn) +function runTests(name, fn, options) { + runTest('dev', name, fn, options) + runTest('prod', name, fn, options) } From 924b71ccc16c434005c0f4111e86d658734c0133 Mon Sep 17 00:00:00 2001 From: LongYinan Date: Sat, 19 Feb 2022 00:04:43 +0800 Subject: [PATCH 13/54] build(next-swc): linux glibc compatible issue (#34481) Fix `GLIBC` compatible issues: - Fixes https://github.com/vercel/next.js/issues/33854 - All `CentOS 7` related issues in https://github.com/vercel/next.js/discussions/30468 Should also fix: - https://github.com/vercel/next.js/discussions/33530 Refactored jobs tested in https://github.com/Brooooooklyn/next.js/runs/5233199833?check_suite_focus=true ### `objdump` in `next@12.0.10`
Symbols node_modules/@next/swc-linux-x64-gnu/next-swc.linux-x64-gnu.node: file format elf64-x86-64 DYNAMIC SYMBOL TABLE: 0000000000000000 D *UND* 0000000000000000 napi_resolve_deferred 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.3.4 __xpg_strerror_r 0000000000000000 D *UND* 0000000000000000 napi_typeof 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 pthread_setspecific 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.3.2 pthread_cond_destroy 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.4 __stack_chk_fail 0000000000000000 DF *UND* 0000000000000000 GCC_3.0 _Unwind_GetRegionStart 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 memset 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 fputs 0000000000000000 DF *UND* 0000000000000000 GCC_3.3 _Unwind_FindEnclosingFunction 0000000000000000 DF *UND* 0000000000000000 GCC_3.0 _Unwind_GetTextRelBase 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 setgroups 0000000000000000 DO *UND* 0000000000000000 GLIBC_2.2.5 stdout 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 madvise 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.3 realpath 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 posix_spawn_file_actions_adddup2 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 pow 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 sigaltstack 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 pthread_self 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 trunc 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.3.4 __strncat_chk 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 pthread_attr_setstacksize 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 sigemptyset 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 pthread_detach 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 __fxstat64 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.3.2 pthread_cond_broadcast 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 strtol 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 __cxa_atexit 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 round 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 sigaddset 0000000000000000 DF *UND* 0000000000000000 GCC_3.0 _Unwind_RaiseException 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.15 posix_spawnp 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 memcmp 0000000000000000 D *UND* 0000000000000000 napi_queue_async_work 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 pthread_rwlock_wrlock 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 floor 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.3.4 __vsnprintf_chk 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 pthread_key_create 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 close 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 pthread_mutexattr_destroy 0000000000000000 D *UND* 0000000000000000 napi_throw_error 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.3 __ctype_toupper_loc 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 poll 0000000000000000 D *UND* 0000000000000000 napi_reject_deferred 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 signal 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 getcwd 0000000000000000 w D *UND* 0000000000000000 __gmon_start__ 0000000000000000 D *UND* 0000000000000000 napi_get_value_string_utf8 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 posix_spawnattr_setsigmask 0000000000000000 D *UND* 0000000000000000 napi_create_promise 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 dup2 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 pthread_attr_getguardsize 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 __xstat64 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 pthread_attr_getstack 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 strerror 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 getenv 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 read 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 memrchr 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 pthread_rwlock_rdlock 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 fmod 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 fork 0000000000000000 DF *UND* 0000000000000000 GCC_3.3 _Unwind_Backtrace 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 posix_spawn_file_actions_destroy 0000000000000000 D *UND* 0000000000000000 napi_get_value_bool 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 setuid 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.9 pipe2 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.3.4 __snprintf_chk 0000000000000000 DF *UND* 0000000000000000 GCC_3.0 _Unwind_SetIP 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 dlsym 0000000000000000 D *UND* 0000000000000000 napi_create_error 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 pthread_mutex_destroy 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 access 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 ioctl 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 strncpy 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 localtime_r 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 memmove 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 syscall 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 readlink 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 fma 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 getuid 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 sin 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 pthread_mutex_init 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 strlen 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 posix_spawnattr_setflags 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 posix_spawn_file_actions_init 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 strstr 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 pthread_key_delete 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 pthread_getattr_np 0000000000000000 D *UND* 0000000000000000 napi_get_buffer_info 0000000000000000 DO *UND* 0000000000000000 GLIBC_2.2.5 stderr 0000000000000000 DF *UND* 0000000000000000 GCC_3.0 _Unwind_Resume 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 posix_spawnattr_destroy 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.3.2 pthread_cond_signal 0000000000000000 w DF *UND* 0000000000000000 GLIBC_2.18 __cxa_thread_atexit_impl 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 pthread_mutex_trylock 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 writev 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 clock_gettime 0000000000000000 D *UND* 0000000000000000 napi_delete_async_work 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 pthread_getspecific 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 pthread_condattr_destroy 0000000000000000 DF *UND* 0000000000000000 GCC_3.0 _Unwind_DeleteException 0000000000000000 D *UND* 0000000000000000 napi_create_function 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 prctl 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 waitpid 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 lseek64 0000000000000000 D *UND* 0000000000000000 napi_set_named_property 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 cos 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 posix_spawnattr_init 0000000000000000 DF *UND* 0000000000000000 GCC_3.0 _Unwind_GetIP 0000000000000000 w D *UND* 0000000000000000 _ITM_registerTMCloneTable 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 execvp 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 ceil 0000000000000000 DO *UND* 0000000000000000 GLIBC_2.2.5 environ 0000000000000000 D *UND* 0000000000000000 napi_get_cb_info 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 pthread_mutexattr_init 0000000000000000 D *UND* 0000000000000000 napi_coerce_to_object 0000000000000000 D *UND* 0000000000000000 napi_throw 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 mprotect 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.14 memcpy 0000000000000000 w D *UND* 0000000000000000 _ITM_deregisterTMCloneTable 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 pthread_attr_init 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 fcntl 0000000000000000 DF *UND* 0000000000000000 GCC_4.2.0 _Unwind_GetIPInfo 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 free 0000000000000000 w DF *UND* 0000000000000000 GLIBC_2.2.5 __cxa_finalize 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 pthread_mutex_unlock 0000000000000000 DF *UND* 0000000000000000 GCC_3.0 _Unwind_GetDataRelBase 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 pthread_mutex_lock 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 dl_iterate_phdr 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.3.4 sched_getaffinity 0000000000000000 DF *UND* 0000000000000000 GCC_3.0 _Unwind_SetGR 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.3.2 pthread_cond_init 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 memchr 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 open 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.3 __tls_get_addr 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 log10 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.3.2 pthread_cond_wait 0000000000000000 D *UND* 0000000000000000 napi_create_async_work 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 sysconf 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 munmap 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 log2 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.3.3 pthread_condattr_setclock 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 pthread_attr_destroy 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 bcmp 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 pthread_create 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 posix_spawnattr_setsigdefault 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 abort 0000000000000000 D *UND* 0000000000000000 napi_create_string_utf8 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 pthread_condattr_init 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 pthread_sigmask 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 pthread_mutexattr_settype 0000000000000000 DO *UND* 0000000000000000 GLIBC_2.2.5 __environ 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 mmap 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 __errno_location 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 _exit 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 write 0000000000000000 DF *UND* 0000000000000000 GCC_3.3 _Unwind_GetCFA 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 getrusage 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 pthread_rwlock_unlock 0000000000000000 DF *UND* 0000000000000000 GCC_3.0 _Unwind_GetLanguageSpecificData 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 sched_yield 0000000000000000 D *UND* 0000000000000000 napi_create_object 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 setgid 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 chdir 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 open64 000000000039bbd0 g DF .text 000000000000032a Base napi_register_module_v1 0000000001ddd750 g DF .text 00000000000002e5 Base rust_eh_personality
There is `0000000000000000 w DF *UND* 0000000000000000 GLIBC_2.18 __cxa_thread_atexit_impl` introduced by https://github.com/rust-lang/rust/issues/36826 ### `objdump` in current branch
Symbols next-swc.linux-x64-gnu.node: file format elf64-x86-64 DYNAMIC SYMBOL TABLE: 0000000000000000 w D *UND* 0000000000000000 Base __gmon_start__ 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 memcpy 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 trunc 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.3 __tls_get_addr 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 bcmp 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 memmove 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 memset 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 pthread_mutex_lock 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 pthread_mutex_trylock 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 memcmp 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 pthread_mutex_unlock 0000000000000000 D *UND* 0000000000000000 Base napi_create_function 0000000000000000 D *UND* 0000000000000000 Base napi_set_named_property 0000000000000000 D *UND* 0000000000000000 Base napi_create_string_utf8 0000000000000000 D *UND* 0000000000000000 Base napi_coerce_to_object 0000000000000000 D *UND* 0000000000000000 Base napi_get_cb_info 0000000000000000 D *UND* 0000000000000000 Base napi_create_error 0000000000000000 D *UND* 0000000000000000 Base napi_throw 0000000000000000 D *UND* 0000000000000000 Base napi_throw_error 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 fma 0000000000000000 D *UND* 0000000000000000 Base napi_create_object 0000000000000000 D *UND* 0000000000000000 Base napi_create_promise 0000000000000000 D *UND* 0000000000000000 Base napi_create_async_work 0000000000000000 D *UND* 0000000000000000 Base napi_queue_async_work 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 strlen 0000000000000000 D *UND* 0000000000000000 Base napi_get_value_string_utf8 0000000000000000 D *UND* 0000000000000000 Base napi_get_value_bool 0000000000000000 D *UND* 0000000000000000 Base napi_typeof 0000000000000000 D *UND* 0000000000000000 Base napi_get_buffer_info 0000000000000000 D *UND* 0000000000000000 Base napi_reject_deferred 0000000000000000 D *UND* 0000000000000000 Base napi_resolve_deferred 0000000000000000 D *UND* 0000000000000000 Base napi_delete_async_work 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 close 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 pow 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 round 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 localtime_r 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 log10 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 cos 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 sin 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 fmod 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 ceil 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.3.2 pthread_cond_wait 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.3.4 sched_getaffinity 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 sysconf 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.3.4 __xpg_strerror_r 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 syscall 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 __errno_location 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 read 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 open64 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 poll 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 log2 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 munmap 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 mmap 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 dl_iterate_phdr 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 clock_gettime 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 getrusage 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 snprintf 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 access 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 vsnprintf 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 madvise 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 strerror 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 mprotect 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 pthread_setspecific 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 memchr 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.3 realpath 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 getenv 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 pthread_key_create 0000000000000000 DO *UND* 0000000000000000 GLIBC_2.2.5 stderr 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 fputs 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 strncat 0000000000000000 DO *UND* 0000000000000000 GLIBC_2.2.5 environ 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.3 __ctype_toupper_loc 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 strncpy 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 strstr 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 strtol 0000000000000000 DO *UND* 0000000000000000 GLIBC_2.2.5 stdout 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 posix_spawnattr_destroy 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 posix_spawn_file_actions_destroy 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 pthread_mutexattr_destroy 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 pthread_rwlock_unlock 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 pthread_mutex_destroy 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 sched_yield 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 getcwd 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 pthread_rwlock_rdlock 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 pthread_rwlock_wrlock 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 getuid 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 write 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 fcntl 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 lseek64 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 writev 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 memrchr 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 ioctl 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 waitpid 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.3.2 pthread_cond_broadcast 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 pthread_condattr_init 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.3.3 pthread_condattr_setclock 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.3.2 pthread_cond_init 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 pthread_condattr_destroy 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.3.2 pthread_cond_signal 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 sigaltstack 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 abort 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 signal 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 pthread_self 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 pthread_getattr_np 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 pthread_attr_getstack 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 pthread_attr_destroy 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 malloc 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 free 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.3.2 pthread_cond_destroy 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 pthread_mutexattr_init 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 pthread_mutexattr_settype 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 pthread_mutex_init 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 pthread_getspecific 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 pthread_key_delete 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 dlsym 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 readlink 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 chdir 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.9 pipe2 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 pthread_attr_init 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 pthread_attr_setstacksize 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 pthread_create 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 prctl 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 pthread_detach 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 pthread_attr_getguardsize 0000000000000000 w D *UND* 0000000000000000 Base __cxa_thread_atexit_impl 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 posix_spawnattr_init 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 posix_spawn_file_actions_init 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 posix_spawn_file_actions_adddup2 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 sigemptyset 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 posix_spawnattr_setsigmask 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 sigaddset 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 posix_spawnattr_setsigdefault 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 posix_spawnattr_setflags 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 fork 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 posix_spawnp 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 _exit 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 dup2 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 setgroups 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 setgid 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 setuid 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 pthread_sigmask 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 execvp 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 fprintf 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 fflush 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 dladdr 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 fwrite 0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 __cxa_atexit 0000000002830dc0 g DF .text 00000000000002e5 Base rust_eh_personality 0000000000e10380 g DF .text 000000000000032a Base napi_register_module_v1
No more `GLIBC_2.18` symbols. Confirm it works on `centos:7` docker image. --- .github/workflows/build_test_deploy.yml | 616 +++++------------- packages/next-swc/.cargo/config.toml | 2 +- .../napi/npm/android-arm-eabi/README.md | 3 + .../napi/npm/android-arm-eabi/package.json | 18 + packages/next-swc/package.json | 1 + packages/next/package.json | 2 +- scripts/install-native.mjs | 1 + yarn.lock | 8 +- 8 files changed, 180 insertions(+), 471 deletions(-) create mode 100644 packages/next-swc/crates/napi/npm/android-arm-eabi/README.md create mode 100644 packages/next-swc/crates/napi/npm/android-arm-eabi/package.json diff --git a/.github/workflows/build_test_deploy.yml b/.github/workflows/build_test_deploy.yml index 7906496c0d9a..e2b02075f956 100644 --- a/.github/workflows/build_test_deploy.yml +++ b/.github/workflows/build_test_deploy.yml @@ -518,13 +518,6 @@ jobs: - build - build-wasm - build-native - - build-windows-i686 - - build-windows-aarch64 - - build-linux-musl - - build-linux-arm7 - - build-linux-aarch64-gnu - - build-android-aarch64 - - build-linux-aarch64-musl env: NPM_TOKEN: ${{ secrets.NPM_TOKEN_ELEVATED }} steps: @@ -606,7 +599,7 @@ jobs: uses: actions/setup-node@v2 if: ${{ steps.docs-change.outputs.DOCS_CHANGE != 'docs only change' }} with: - node-version: 14 + node-version: 16 check-latest: true - name: Install @@ -621,14 +614,14 @@ jobs: if: ${{ steps.docs-change.outputs.DOCS_CHANGE != 'docs only change' }} with: path: ~/.cargo/registry - key: stable-ubuntu-18.04-node@14-cargo-registry-trimmed-${{ hashFiles('**/Cargo.lock') }} + key: stable-ubuntu-18.04-cargo-registry - name: Cache cargo index uses: actions/cache@v1 if: ${{ steps.docs-change.outputs.DOCS_CHANGE != 'docs only change' }} with: path: ~/.cargo/git - key: stable-ubuntu-18.04-node@14-cargo-index-trimmed-${{ hashFiles('**/Cargo.lock') }} + key: stable-ubuntu-18.04-cargo-index # We use week in the turbo cache key to keep the cache from infinitely growing - id: get-week @@ -660,9 +653,7 @@ jobs: # since the repo's dependencies aren't installed we need # to install napi globally - - run: npm i -g @napi-rs/cli@1.2.1 - - run: npm i -g turbo@1.0.28 - + - run: npm i -g @napi-rs/cli@2.4.4 turbo@1.0.28 - name: Build if: ${{ steps.docs-change.outputs.DOCS_CHANGE != 'docs only change' }} run: turbo run build-native --cache-dir=".turbo" @@ -747,95 +738,137 @@ jobs: # Build binaries for publishing build-native: - needs: build - if: ${{ needs.build.outputs.isRelease == 'true' }} strategy: + fail-fast: false matrix: - os: [ubuntu-18.04, macos-latest, windows-latest] - description: [default] - include: - - os: ubuntu-18.04 - target: x86_64-unknown-linux-gnu - name: linux-x64-gnu - - os: windows-latest - target: x86_64-pc-windows-msvc - name: win32-x64-msvc - - os: macos-latest - target: x86_64-apple-darwin - name: darwin-x64 - - os: macos-latest - target: aarch64-apple-darwin - name: darwin-arm64 - description: m1 - - name: next-swc - ${{ matrix.os }} - ${{ matrix.target }} - node@14 - runs-on: ${{ matrix.os }} - + settings: + - host: macos-latest + target: 'x86_64-apple-darwin' + build: | + npm i -g @napi-rs/cli@2.4.4 turbo@1.0.28 + turbo run build-native --cache-dir=".turbo" -- --release + strip -x packages/next-swc/native/next-swc.*.node + - host: windows-latest + build: | + npm i -g @napi-rs/cli@2.4.4 turbo@1.0.28 + turbo run build-native --cache-dir=".turbo" -- --release + target: 'x86_64-pc-windows-msvc' + - host: windows-latest + build: | + npm i -g @napi-rs/cli@2.4.4 turbo@1.0.28 + turbo run build-native --cache-dir=".turbo" -- --release --target i686-pc-windows-msvc + target: 'i686-pc-windows-msvc' + - host: ubuntu-latest + target: 'x86_64-unknown-linux-gnu' + docker: ghcr.io/napi-rs/napi-rs/nodejs-rust:lts-alpine-zig + # Node.js in Baidu need to compatible with `GLIBC_2.12` + build: >- + set -e && + rustup toolchain install nightly-2021-11-15 && + rustup default nightly-2021-11-15 && + rustup target add x86_64-unknown-linux-gnu && + npm i -g @napi-rs/cli@2.4.4 turbo@1.0.28 && + turbo run build-native --cache-dir=".turbo" -- --release --target x86_64-unknown-linux-gnu --zig --zig-abi-suffix 2.12 && + llvm-strip -x packages/next-swc/native/next-swc.*.node + - host: ubuntu-latest + target: 'x86_64-unknown-linux-musl' + docker: ghcr.io/napi-rs/napi-rs/nodejs-rust:lts-alpine + build: >- + set -e && + rustup toolchain install nightly-2021-11-15 && + rustup default nightly-2021-11-15 && + rustup target add x86_64-unknown-linux-musl && + npm i -g @napi-rs/cli@2.4.4 turbo@1.0.28 && + turbo run build-native --cache-dir=".turbo" -- --release --target x86_64-unknown-linux-musl && + strip packages/next-swc/native/next-swc.*.node + - host: macos-latest + target: 'aarch64-apple-darwin' + build: | + sudo rm -Rf /Library/Developer/CommandLineTools/SDKs/*; + export CC=$(xcrun -f clang); + export CXX=$(xcrun -f clang++); + SYSROOT=$(xcrun --sdk macosx --show-sdk-path); + export CFLAGS="-isysroot $SYSROOT -isystem $SYSROOT"; + npm i -g @napi-rs/cli@2.4.4 turbo@1.0.28 + turbo run build-native --cache-dir=".turbo" -- --release --target aarch64-apple-darwin + strip -x packages/next-swc/native/next-swc.*.node + - host: ubuntu-latest + target: 'aarch64-unknown-linux-gnu' + docker: ghcr.io/napi-rs/napi-rs/nodejs-rust:lts-alpine-zig + build: >- + set -e && + rustup toolchain install nightly-2021-11-15 && + rustup default nightly-2021-11-15 && + rustup target add aarch64-unknown-linux-gnu && + npm i -g @napi-rs/cli@2.4.4 turbo@1.0.28 && + turbo run build-native --cache-dir=".turbo" -- --release --target aarch64-unknown-linux-gnu --zig --zig-abi-suffix 2.12 && + llvm-strip -x packages/next-swc/native/next-swc.*.node + - host: ubuntu-18.04 + target: 'armv7-unknown-linux-gnueabihf' + setup: | + sudo apt-get update + sudo apt-get install gcc-arm-linux-gnueabihf g++-arm-linux-gnueabihf -y + build: | + npm i -g @napi-rs/cli@2.4.4 turbo@1.0.28 + turbo run build-native --cache-dir=".turbo" -- --release --target armv7-unknown-linux-gnueabihf + arm-linux-gnueabihf-strip packages/next-swc/native/next-swc.*.node + - host: ubuntu-latest + target: aarch64-linux-android + build: | + export CARGO_TARGET_AARCH64_LINUX_ANDROID_LINKER="${ANDROID_NDK_HOME}/toolchains/llvm/prebuilt/linux-x86_64/bin/aarch64-linux-android24-clang" + export CC="${ANDROID_NDK_HOME}/toolchains/llvm/prebuilt/linux-x86_64/bin/aarch64-linux-android24-clang" + export CXX="${ANDROID_NDK_HOME}/toolchains/llvm/prebuilt/linux-x86_64/bin/aarch64-linux-android24-clang++" + export PATH="${ANDROID_NDK_HOME}/toolchains/llvm/prebuilt/linux-x86_64/bin:${PATH}" + npm i -g @napi-rs/cli@2.4.4 turbo@1.0.28 + turbo run build-native --cache-dir=".turbo" -- --release --target aarch64-linux-android + ${ANDROID_NDK_HOME}/toolchains/llvm/prebuilt/linux-x86_64/bin/aarch64-linux-android-strip packages/next-swc/native/next-swc.*.node + - host: ubuntu-latest + target: armv7-linux-androideabi + build: | + export CARGO_TARGET_ARMV7_LINUX_ANDROIDEABI_LINKER="${ANDROID_NDK_HOME}/toolchains/llvm/prebuilt/linux-x86_64/bin/armv7a-linux-androideabi24-clang" + export CC="${ANDROID_NDK_HOME}/toolchains/llvm/prebuilt/linux-x86_64/bin/armv7a-linux-androideabi24-clang" + export CXX="${ANDROID_NDK_HOME}/toolchains/llvm/prebuilt/linux-x86_64/bin/armv7a-linux-androideabi24-clang++" + export PATH="${ANDROID_NDK_HOME}/toolchains/llvm/prebuilt/linux-x86_64/bin:${PATH}" + npm i -g @napi-rs/cli@2.4.4 turbo@1.0.28 + turbo run build-native --cache-dir=".turbo" -- --release --target armv7-linux-androideabi + ${ANDROID_NDK_HOME}/toolchains/llvm/prebuilt/linux-x86_64/bin/arm-linux-androideabi-strip packages/next-swc/native/next-swc.*.node + - host: ubuntu-latest + target: 'aarch64-unknown-linux-musl' + docker: ghcr.io/napi-rs/napi-rs/nodejs-rust:lts-alpine + build: >- + set -e && + npm i -g @napi-rs/cli@2.4.4 turbo@1.0.28 && + rustup toolchain install nightly-2021-11-15 && + rustup default nightly-2021-11-15 && + rustup target add aarch64-unknown-linux-musl && + turbo run build-native --cache-dir=".turbo" -- --release --target aarch64-unknown-linux-musl && + llvm-strip -x packages/next-swc/native/next-swc.*.node + - host: windows-latest + target: 'aarch64-pc-windows-msvc' + build: | + npm i -g @napi-rs/cli@2.4.4 turbo@1.0.28 + turbo run build-native --cache-dir=".turbo" -- --release --target aarch64-pc-windows-msvc + if: ${{ needs.build.outputs.isRelease == 'true' }} + needs: build + name: stable - ${{ matrix.settings.target }} - node@16 + runs-on: ${{ matrix.settings.host }} steps: # https://github.com/actions/virtual-environments/issues/1187 - name: tune linux network run: sudo ethtool -K eth0 tx off rx off - if: ${{ matrix.os == 'ubuntu-18.04' }} + if: ${{ matrix.settings.host == 'ubuntu-18.04' }} + - name: tune linux network + run: sudo ethtool -K eth0 tx off rx off + if: ${{ matrix.settings.host == 'ubuntu-latest' }} - name: tune windows network run: Disable-NetAdapterChecksumOffload -Name * -TcpIPv4 -UdpIPv4 -TcpIPv6 -UdpIPv6 - if: ${{ matrix.os == 'windows-latest' }} + if: ${{ matrix.settings.host == 'windows-latest' }} - name: tune mac network run: sudo sysctl -w net.link.generic.system.hwcksum_tx=0 && sudo sysctl -w net.link.generic.system.hwcksum_rx=0 - if: ${{ matrix.os == 'macos-latest' }} - - - name: Setup node - uses: actions/setup-node@v2 - with: - node-version: 14 - check-latest: true - + if: ${{ matrix.settings.host == 'macos-latest' }} # we use checkout here instead of the build cache since # it can fail to restore in different OS' - uses: actions/checkout@v2 - - # since the repo's dependencies aren't installed we need - # to install napi globally - - run: npm i -g @napi-rs/cli@1.2.1 - - run: npm i -g turbo@1.0.28 - - - name: Install - uses: actions-rs/toolchain@v1 - with: - profile: minimal - toolchain: nightly-2021-11-15 - target: ${{ matrix.target }} - - - name: Cache cargo registry - uses: actions/cache@v1 - with: - path: ~/.cargo/registry - key: stable-${{ matrix.os }}-node@14-cargo-registry-trimmed-${{ hashFiles('**/Cargo.lock') }} - - - name: Cache cargo index - uses: actions/cache@v1 - with: - path: ~/.cargo/git - key: stable-${{ matrix.os }}-node@14-cargo-index-trimmed-${{ hashFiles('**/Cargo.lock') }} - - - name: Turbo cache - id: turbo-cache - uses: actions/cache@v2 - with: - path: .turbo - key: turbo-${{ github.job }}-${{ matrix.name }}-${{ github.ref_name }}-${{ needs.build.outputs.weekNum }}-${{ github.sha }} - restore-keys: | - turbo-${{ github.job }}-${{ matrix.name }}- - turbo-${{ github.job }}-${{ matrix.name }}-${{ github.ref_name }}-${{ needs.build.outputs.weekNum }}- - turbo-${{ github.job }}-${{ matrix.name }}-canary-${{ needs.build.outputs.weekNum }}- - - - name: Cross build aarch64 setup - if: ${{ matrix.target == 'aarch64-apple-darwin' }} - run: | - sudo rm -Rf /Library/Developer/CommandLineTools/SDKs/*; - export CC=$(xcrun -f clang); - export CXX=$(xcrun -f clang++); - SYSROOT=$(xcrun --sdk macosx --show-sdk-path); - export CFLAGS="-isysroot $SYSROOT -isystem $SYSROOT"; # We use restore-key to pick latest cache. # We will not get exact match, but doc says # "If there are multiple partial matches for a restore key, the action returns the most recently created cache." @@ -844,65 +877,9 @@ jobs: uses: actions/cache@v2 with: path: ./packages/next-swc/target - key: next-swc-cargo-cache-${{ matrix.os }}--${{ hashFiles('**/Cargo.lock') }} + key: next-swc-cargo-cache-${{ matrix.settings.target }}--${{ hashFiles('**/Cargo.lock') }} restore-keys: | - next-swc-cargo-cache-${{ matrix.os }} - - - name: 'Build' - shell: bash - run: turbo run build-native --cache-dir=".turbo" -- --release --target ${{ matrix.target }} - env: - MACOSX_DEPLOYMENT_TARGET: '10.13' - - - name: Upload artifact - uses: actions/upload-artifact@v2.2.4 - with: - name: next-swc-binaries - path: packages/next-swc/native/next-swc.${{ matrix.name }}.node - - - name: Clear the cargo caches - run: | - cargo install cargo-cache --no-default-features --features ci-autoclean - cargo-cache - - build-windows-i686: - needs: build - if: ${{ needs.build.outputs.isRelease == 'true' }} - name: next-swc - windows-i686 - node@14 - runs-on: windows-latest - env: - CARGO_PROFILE_RELEASE_CODEGEN_UNITS: 32 - CARGO_PROFILE_RELEASE_LTO: 'false' - steps: - - name: Install node x86 - run: | - choco install nodejs-lts --x86 -y --force - refreshenv - - - name: Set 32bit Node.js path - run: | - echo "C:\\Program Files (x86)\\nodejs" >> $GITHUB_PATH - shell: bash - - - name: Node.js arch - run: node -e "console.log(process.arch)" - - # we use checkout here instead of the build cache since - # it can fail to restore in different OS' - - uses: actions/checkout@v2 - - # since the repo's dependencies aren't installed we need - # to install napi globally - - run: npm i -g @napi-rs/cli@1.2.1 - - run: npm i -g turbo@1.0.28 - - name: Install Rust - uses: actions-rs/toolchain@v1 - with: - profile: minimal - toolchain: nightly-2021-11-15 - override: true - target: i686-pc-windows-msvc - + next-swc-cargo-cache-${{ matrix.settings.target }} - name: Turbo Cache id: turbo-cache uses: actions/cache@v2 @@ -914,349 +891,58 @@ jobs: turbo-${{ github.job }}-${{ github.ref_name }}-${{ needs.build.outputs.weekNum }}- turbo-${{ github.job }}-canary-${{ needs.build.outputs.weekNum }}- - - name: Build - shell: bash - run: turbo run build-native --cache-dir=".turbo" -- --release --target i686-pc-windows-msvc - - - name: Upload artifact - uses: actions/upload-artifact@v2 - with: - name: next-swc-binaries - path: packages/next-swc/native/next-swc.win32-ia32-msvc.node - - build-windows-aarch64: - needs: build - if: ${{ needs.build.outputs.isRelease == 'true' }} - name: next-swc - windows-aarch64 - node@14 - runs-on: windows-latest - steps: - name: Setup node uses: actions/setup-node@v2 + if: ${{ !matrix.settings.docker }} with: - node-version: 14 - - # we use checkout here instead of the build cache since - # it can fail to restore in different OS' - - uses: actions/checkout@v2 - - # since the repo's dependencies aren't installed we need - # to install napi globally - - run: npm i -g @napi-rs/cli@1.2.1 - - run: npm i -g turbo@1.0.28 - - name: Install Rust - uses: actions-rs/toolchain@v1 - with: - profile: minimal - toolchain: nightly-2021-11-15 - override: true - target: aarch64-pc-windows-msvc - - - name: Turbo Cache - id: turbo-cache - uses: actions/cache@v2 - with: - path: .turbo - key: turbo-${{ github.job }}-${{ github.ref_name }}-${{ needs.build.outputs.weekNum }}-${{ github.sha }} - restore-keys: | - turbo-${{ github.job }}- - turbo-${{ github.job }}-${{ github.ref_name }}-${{ needs.build.outputs.weekNum }}- - turbo-${{ github.job }}-canary-${{ needs.build.outputs.weekNum }}- - - - name: Build - shell: bash - run: turbo run build-native --cache-dir=".turbo" -- --release --target aarch64-pc-windows-msvc - - - name: Upload artifact - uses: actions/upload-artifact@v2 - with: - name: next-swc-binaries - path: packages/next-swc/native/next-swc.win32-arm64-msvc.node - - build-linux-musl: - needs: build - if: ${{ needs.build.outputs.isRelease == 'true' }} - name: next-swc - linux-musl - node@lts - runs-on: ubuntu-latest - steps: - # we use checkout here instead of the build cache since - # it can fail to restore in different OS' - - uses: actions/checkout@v2 - - - name: Login to registry - run: | - docker login -u $DOCKER_USERNAME -p $DOCKER_PASSWORD $DOCKER_REGISTRY_URL - env: - DOCKER_REGISTRY_URL: ghcr.io - DOCKER_USERNAME: ${{ github.actor }} - DOCKER_PASSWORD: ${{ secrets.GITHUB_TOKEN }} - - - name: Cache - uses: actions/cache@v2 - with: - path: | - target/ - key: linux-musl-publish-integration - - - name: Pull docker image - run: | - docker pull ghcr.io/napi-rs/napi-rs/nodejs-rust:lts-alpine - docker tag ghcr.io/napi-rs/napi-rs/nodejs-rust:lts-alpine builder - - - name: Turbo Cache - id: turbo-cache - uses: actions/cache@v2 - with: - path: .turbo - key: turbo-${{ github.job }}-${{ github.ref_name }}-${{ needs.build.outputs.weekNum }}-${{ github.sha }} - restore-keys: | - turbo-${{ github.job }}- - turbo-${{ github.job }}-${{ github.ref_name }}-${{ needs.build.outputs.weekNum }}- - turbo-${{ github.job }}-canary-${{ needs.build.outputs.weekNum }}- - - - name: 'Build' - run: | - docker run --rm -v ~/.cargo/git:/root/.cargo/git -v ~/.cargo/registry:/root/.cargo/registry -v $(pwd):/build -w /build builder sh -c "npm i -g @napi-rs/cli@1.2.1 && npm i -g turbo@1.0.28 && turbo run build-native --cache-dir=".turbo" -- --release --target x86_64-unknown-linux-musl" - - - name: Upload artifact - uses: actions/upload-artifact@v2 - with: - name: next-swc-binaries - path: packages/next-swc/native/next-swc.linux-x64-musl.node - - build-linux-aarch64-gnu: - needs: build - if: ${{ needs.build.outputs.isRelease == 'true' }} - name: next-swc - aarch64-unknown-linux-gnu - node@14 - runs-on: ubuntu-18.04 - steps: - - run: docker run --rm --privileged multiarch/qemu-user-static:register --reset - - - name: Setup node - uses: actions/setup-node@v2 - with: - node-version: 14 - - # we use checkout here instead of the build cache since - # it can fail to restore in different OS' - - uses: actions/checkout@v2 - - # since the repo's dependencies aren't installed we need - # to install napi globally - - run: npm i -g @napi-rs/cli@1.2.1 - - run: npm i -g turbo@1.0.28 - - - name: Install Rust - uses: actions-rs/toolchain@v1 - with: - profile: minimal - toolchain: nightly-2021-11-15 - override: true - target: aarch64-unknown-linux-gnu - - - name: Cache - uses: actions/cache@v2 - with: - path: | - target/ - key: aarch64-linux-gnu-publish-integration - - - name: Install cross compile toolchain - run: | - sudo apt-get update - sudo apt-get install gcc-aarch64-linux-gnu g++-aarch64-linux-gnu -y - - - name: Turbo Cache - id: turbo-cache - uses: actions/cache@v2 - with: - path: .turbo - key: turbo-${{ github.job }}-${{ github.ref_name }}-${{ needs.build.outputs.weekNum }}-${{ github.sha }} - restore-keys: | - turbo-${{ github.job }}- - turbo-${{ github.job }}-${{ github.ref_name }}-${{ needs.build.outputs.weekNum }}- - turbo-${{ github.job }}-canary-${{ needs.build.outputs.weekNum }}- - - - name: Cross build aarch64 - if: ${{ steps.binary-cache.outputs.cache-hit != 'true' }} - run: turbo run build-native --cache-dir=".turbo" -- --release --target aarch64-unknown-linux-gnu - - - name: Upload artifact - uses: actions/upload-artifact@v2 - with: - name: next-swc-binaries - path: packages/next-swc/native/next-swc.linux-arm64-gnu.node - - build-linux-aarch64-musl: - needs: build - if: ${{ needs.build.outputs.isRelease == 'true' }} - name: next-swc - aarch64-unknown-linux-musl - node@14 - runs-on: ubuntu-18.04 - steps: - - name: Setup node - uses: actions/setup-node@v2 - with: - node-version: 14 - - # we use checkout here instead of the build cache since - # it can fail to restore in different OS' - - uses: actions/checkout@v2 + node-version: 16 + check-latest: true + cache: yarn - # since the repo's dependencies aren't installed we need - # to install napi globally - - run: npm i -g @napi-rs/cli@1.2.1 - - run: npm i -g turbo@1.0.28 - - name: Install Rust + - name: Install uses: actions-rs/toolchain@v1 + if: ${{ !matrix.settings.docker }} with: profile: minimal - toolchain: nightly-2021-11-15 override: true - target: aarch64-unknown-linux-musl - - - name: Cache - uses: actions/cache@v2 - with: - path: | - target/ - key: aarch64-linux-musl-publish-integration - - - name: Install cross compile toolchain - run: | - sudo apt-get update - sudo apt-get install gcc-aarch64-linux-gnu -y - - - name: Turbo Cache - id: turbo-cache - uses: actions/cache@v2 - with: - path: .turbo - key: turbo-${{ github.job }}-${{ github.ref_name }}-${{ needs.build.outputs.weekNum }}-${{ github.sha }} - restore-keys: | - turbo-${{ github.job }}- - turbo-${{ github.job }}-${{ github.ref_name }}-${{ needs.build.outputs.weekNum }}- - turbo-${{ github.job }}-canary-${{ needs.build.outputs.weekNum }}- - - - name: Cross build aarch64 - run: turbo run build-native --cache-dir=".turbo" -- --release --target aarch64-unknown-linux-musl - - - name: Upload artifact - uses: actions/upload-artifact@v2 - with: - name: next-swc-binaries - path: packages/next-swc/native/next-swc.linux-arm64-musl.node - - build-linux-arm7: - needs: build - if: ${{ needs.build.outputs.isRelease == 'true' }} - name: next-swc - arm7-unknown-linux-gnu - node@14 - runs-on: ubuntu-18.04 - steps: - - run: docker run --rm --privileged multiarch/qemu-user-static:register --reset - - - name: Setup node - uses: actions/setup-node@v2 - with: - node-version: 14 - - # we use checkout here instead of the build cache since - # it can fail to restore in different OS' - - uses: actions/checkout@v2 - - # since the repo's dependencies aren't installed we need - # to install napi globally - - run: npm i -g @napi-rs/cli@1.2.1 - - run: npm i -g turbo@1.0.28 - - - name: Install Rust - uses: actions-rs/toolchain@v1 - with: - profile: minimal toolchain: nightly-2021-11-15 - override: true - target: armv7-unknown-linux-gnueabihf + target: ${{ matrix.settings.target }} - - name: Cache + - name: Cache cargo registry uses: actions/cache@v2 with: - path: | - target/ - key: arm7-linux-gnu-publish-integration - - - name: Install cross compile toolchain - run: | - sudo apt-get update - sudo apt-get install gcc-arm-linux-gnueabihf -y + path: ~/.cargo/registry + key: ${{ matrix.settings.target }}-cargo-registry - - name: Turbo Cache - id: turbo-cache + - name: Cache cargo index uses: actions/cache@v2 with: - path: .turbo - key: turbo-${{ github.job }}-${{ github.ref_name }}-${{ needs.build.outputs.weekNum }}-${{ github.sha }} - restore-keys: | - turbo-${{ github.job }}- - turbo-${{ github.job }}-${{ github.ref_name }}-${{ needs.build.outputs.weekNum }}- - turbo-${{ github.job }}-canary-${{ needs.build.outputs.weekNum }}- - - - name: Cross build aarch64 - run: turbo run build-native --cache-dir=".turbo" -- --release --target armv7-unknown-linux-gnueabihf - - - name: Upload artifact - uses: actions/upload-artifact@v2 - with: - name: next-swc-binaries - path: packages/next-swc/native/next-swc.linux-arm-gnueabihf.node - - build-android-aarch64: - needs: build - if: ${{ needs.build.outputs.isRelease == 'true' }} - name: next-swc - Android - aarch64 - runs-on: macos-latest - steps: - - name: Setup node - uses: actions/setup-node@v2 - with: - node-version: 14 - - # we use checkout here instead of the build cache since - # it can fail to restore in different OS' - - uses: actions/checkout@v2 - - # since the repo's dependencies aren't installed we need - # to install napi globally - - run: npm i -g @napi-rs/cli@1.2.1 - - run: npm i -g turbo@1.0.28 + path: ~/.cargo/git + key: ${{ matrix.settings.target }}-cargo-index - - name: Install Rust - uses: actions-rs/toolchain@v1 - with: - profile: minimal - toolchain: nightly-2021-11-15 - override: true - target: aarch64-linux-android + - name: Setup toolchain + run: ${{ matrix.settings.setup }} + if: ${{ matrix.settings.setup }} + shell: bash - - name: Turbo Cache - id: turbo-cache - uses: actions/cache@v2 + - name: Build in docker + uses: addnab/docker-run-action@v3 + if: ${{ matrix.settings.docker }} with: - path: .turbo - key: turbo-${{ github.job }}-${{ github.ref_name }}-${{ needs.build.outputs.weekNum }}-${{ github.sha }} - restore-keys: | - turbo-${{ github.job }}- - turbo-${{ github.job }}-${{ github.ref_name }}-${{ needs.build.outputs.weekNum }}- - turbo-${{ github.job }}-canary-${{ needs.build.outputs.weekNum }}- + image: ${{ matrix.settings.docker }} + options: -v ${{ env.HOME }}/.cargo/git:/root/.cargo/git -v ${{ env.HOME }}/.cargo/registry:/root/.cargo/registry -v ${{ github.workspace }}:/build -w /build + run: ${{ matrix.settings.build }} - - name: Build + - name: 'Build' + run: ${{ matrix.settings.build }} + if: ${{ !matrix.settings.docker }} shell: bash - run: | - export CARGO_TARGET_AARCH64_LINUX_ANDROID_LINKER="${ANDROID_NDK_HOME}/toolchains/llvm/prebuilt/darwin-x86_64/bin/aarch64-linux-android24-clang" - turbo run build-native --cache-dir=".turbo" -- --release --target aarch64-linux-android - name: Upload artifact uses: actions/upload-artifact@v2 with: name: next-swc-binaries - path: packages/next-swc/native/next-swc.android-arm64.node + path: packages/next-swc/native/next-swc.*.node build-wasm: needs: build diff --git a/packages/next-swc/.cargo/config.toml b/packages/next-swc/.cargo/config.toml index 1fb099927fed..1d7e537b13c3 100644 --- a/packages/next-swc/.cargo/config.toml +++ b/packages/next-swc/.cargo/config.toml @@ -21,7 +21,7 @@ rustflags = [ ] [target.aarch64-unknown-linux-musl] -linker = "aarch64-linux-gnu-gcc" +linker = "aarch64-linux-musl-gcc" rustflags = [ "-C", "target-feature=-crt-static", diff --git a/packages/next-swc/crates/napi/npm/android-arm-eabi/README.md b/packages/next-swc/crates/napi/npm/android-arm-eabi/README.md new file mode 100644 index 000000000000..7044554db68c --- /dev/null +++ b/packages/next-swc/crates/napi/npm/android-arm-eabi/README.md @@ -0,0 +1,3 @@ +# `@next/swc-android-arm-eabi` + +This is the **android-arm-eabi** binary for `@next/swc` diff --git a/packages/next-swc/crates/napi/npm/android-arm-eabi/package.json b/packages/next-swc/crates/napi/npm/android-arm-eabi/package.json new file mode 100644 index 000000000000..69ec8a4b23a9 --- /dev/null +++ b/packages/next-swc/crates/napi/npm/android-arm-eabi/package.json @@ -0,0 +1,18 @@ +{ + "name": "@next/swc-android-arm-eabi", + "version": "0.0.0", + "os": [ + "android" + ], + "cpu": [ + "arm" + ], + "main": "next-swc.android-arm-eabi.node", + "files": [ + "next-swc.android-arm-eabi.node" + ], + "license": "MIT", + "engines": { + "node": ">= 10" + } +} diff --git a/packages/next-swc/package.json b/packages/next-swc/package.json index 9f16469b8681..f71acc377734 100644 --- a/packages/next-swc/package.json +++ b/packages/next-swc/package.json @@ -16,6 +16,7 @@ "armv7-unknown-linux-gnueabihf", "aarch64-apple-darwin", "aarch64-linux-android", + "arm-linux-androideabi", "x86_64-unknown-freebsd", "x86_64-unknown-linux-musl", "aarch64-unknown-linux-musl", diff --git a/packages/next/package.json b/packages/next/package.json index 316aee06f97a..d681c2e8b8a1 100644 --- a/packages/next/package.json +++ b/packages/next/package.json @@ -115,7 +115,7 @@ "@babel/traverse": "7.15.0", "@babel/types": "7.15.0", "@hapi/accept": "5.0.2", - "@napi-rs/cli": "1.2.1", + "@napi-rs/cli": "2.4.4", "@napi-rs/triples": "1.0.3", "@next/polyfill-module": "12.1.0", "@next/polyfill-nomodule": "12.1.0", diff --git a/scripts/install-native.mjs b/scripts/install-native.mjs index a52305df8881..198b6385e13b 100644 --- a/scripts/install-native.mjs +++ b/scripts/install-native.mjs @@ -19,6 +19,7 @@ import fs from 'fs-extra' version: '1.0.0', optionalDependencies: { '@next/swc-android-arm64': 'canary', + '@next/swc-android-arm-eabi': 'canary', '@next/swc-darwin-arm64': 'canary', '@next/swc-darwin-x64': 'canary', '@next/swc-linux-arm-gnueabihf': 'canary', diff --git a/yarn.lock b/yarn.lock index 1ee4e9b20c7e..08be08516ea1 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3706,10 +3706,10 @@ call-me-maybe "^1.0.1" glob-to-regexp "^0.3.0" -"@napi-rs/cli@1.2.1": - version "1.2.1" - resolved "https://registry.yarnpkg.com/@napi-rs/cli/-/cli-1.2.1.tgz#eccdf9e0835aec3adcef30074bf69c110ea65960" - integrity sha512-7FoYn1JSK5rTIG9KcKfYnZL/O0UjUMMuzZCXd//bJdkLw0Xx9EqQJs1X/Mg4KqywJYb79LDfxRMiJRSukPGDNw== +"@napi-rs/cli@2.4.4": + version "2.4.4" + resolved "https://registry.yarnpkg.com/@napi-rs/cli/-/cli-2.4.4.tgz#878a38f0fba1709d89d66eba706745ce728a61a5" + integrity sha512-f+tvwCv1ka24dBqI2DgBhR7Oxl3DKHOp4onxLXwyBFt6iCADnr3YZIr1/2Iq5r3uqxFgaf01bfPsRQZPkEp0kQ== "@napi-rs/triples@1.0.3", "@napi-rs/triples@^1.0.3": version "1.0.3" From aa35aa13f11ff9188c09a916bd8eaee0dfb62003 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bal=C3=A1zs=20Orb=C3=A1n?= Date: Fri, 18 Feb 2022 17:42:05 +0100 Subject: [PATCH 14/54] chore: clarify GitHub action (bot) comments (#34546) For anyone landing on this PR, if you are new to contributing to open source, here is a helpful video with some good tips: [How to Contribute to Open Source (Next.js) ](https://www.youtube.com/watch?v=cuoNzXFLitc) We use two GitHub Actions (here bots) to help us maintain the project better: 1. Stalebot When an issue is triaged, we look for a reproduction and clear steps to reproduce the reported issue. This helps us identify bugs easier, which will result in a quicker resolution of the bug. Skipping these steps could mean that we will not be able to investigate. In these cases, the issue will receive a `please add a complete reproduction` label, that indicates to the original poster (or those following along in the issue) that we would like them to post the missed steps. Unless that happens within 30 days, we are marking the issue as stale. A day after, the issue is automatically closed. If someone finds this now-closed issue later, through the Stalebot we encourage that a new issue will be opened and reference the old one if it holds important/relevant contextual information. The reason is that newer releases might have already fixed the issue that was reported in the first place. This is indicated in the bug report issue template forms. 2. Lockbot This bot is similar to Stalebot, but it strictly operates on **already closed/resolved** issues, it **never** closes ongoing/open issue discussions. An issue can be closed for multiple reasons. It either has been closed by the original poster because they found the solution, or a PR merge included a fix in a recent Next.js release. It is important to look for the reason before commenting. If the issue was seemingly locked by accident, through the Lockbot comment we encourage that a new issue will be opened and reference the old one if it holds important/relevant contextual information. The reason is that newer releases might have already fixed the issue that was reported in the first place. This is indicated in the bug report issue template forms. Important: Issues can be closed/locked by accident, and we encourage everyone to feel welcome opening a new case and reference back to the old issue. Our team values everyone's opinion and we would like to make sure that we resolve any confusion in each individual case. --- .github/workflows/lock.yml | 4 ++-- .github/workflows/stale.yml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/lock.yml b/.github/workflows/lock.yml index bf90a0aaf429..41b7e8c78beb 100644 --- a/.github/workflows/lock.yml +++ b/.github/workflows/lock.yml @@ -20,8 +20,8 @@ jobs: steps: - uses: dessant/lock-threads@v3 with: - github-token: ${{ secrets.LOCK_TOKEN }} + github-token: ${{ secrets.GITHUB_TOKEN }} issue-inactive-days: 30 - issue-comment: 'This issue has been automatically locked due to no recent activity. If you are running into a similar issue, please create a new issue with the steps to reproduce. Thank you.' + issue-comment: 'This closed issue has been automatically locked because it had no new activity for a month. If you are running into a similar issue, please create a new issue with the steps to reproduce. Thank you.' pr-inactive-days: 30 log-output: true diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml index 9c858d67608b..cb6abe0bc488 100644 --- a/.github/workflows/stale.yml +++ b/.github/workflows/stale.yml @@ -16,7 +16,7 @@ jobs: with: repo-token: ${{ secrets.STALE_TOKEN }} only-labels: 'please add a complete reproduction' - close-issue-message: 'This issue has been automatically closed after 30 days of inactivity with no reproduction. If you are running into a similar issue, please open a new issue with a reproduction. Thank you.' + close-issue-message: 'This issue has been automatically closed because it received no activity for a month and had no reproduction to investigate. If you think this was closed by accident, please leave a comment. If you are running into a similar issue, please open a new issue with a reproduction. Thank you.' days-before-issue-close: 1 days-before-issue-stale: 30 days-before-pr-close: -1 From 44bc4d92f5e61da28fd658494e80eea3f59d3b34 Mon Sep 17 00:00:00 2001 From: JJ Kasper Date: Fri, 18 Feb 2022 12:02:13 -0600 Subject: [PATCH 15/54] v12.1.1-canary.0 --- lerna.json | 2 +- packages/create-next-app/package.json | 2 +- packages/eslint-config-next/package.json | 4 ++-- packages/eslint-plugin-next/package.json | 2 +- packages/next-bundle-analyzer/package.json | 2 +- packages/next-codemod/package.json | 2 +- packages/next-env/package.json | 2 +- packages/next-mdx/package.json | 2 +- packages/next-plugin-storybook/package.json | 2 +- packages/next-polyfill-module/package.json | 2 +- packages/next-polyfill-nomodule/package.json | 2 +- packages/next-swc/package.json | 2 +- packages/next/package.json | 14 +++++++------- packages/react-dev-overlay/package.json | 2 +- packages/react-refresh-utils/package.json | 2 +- 15 files changed, 22 insertions(+), 22 deletions(-) diff --git a/lerna.json b/lerna.json index 3aa8531b8bb4..6f5ea94c351e 100644 --- a/lerna.json +++ b/lerna.json @@ -16,5 +16,5 @@ "registry": "https://registry.npmjs.org/" } }, - "version": "12.1.0" + "version": "12.1.1-canary.0" } diff --git a/packages/create-next-app/package.json b/packages/create-next-app/package.json index 4e57156340a9..80080f1cca89 100644 --- a/packages/create-next-app/package.json +++ b/packages/create-next-app/package.json @@ -1,6 +1,6 @@ { "name": "create-next-app", - "version": "12.1.0", + "version": "12.1.1-canary.0", "keywords": [ "react", "next", diff --git a/packages/eslint-config-next/package.json b/packages/eslint-config-next/package.json index c141e9f35c9d..0809099cd2f7 100644 --- a/packages/eslint-config-next/package.json +++ b/packages/eslint-config-next/package.json @@ -1,6 +1,6 @@ { "name": "eslint-config-next", - "version": "12.1.0", + "version": "12.1.1-canary.0", "description": "ESLint configuration used by NextJS.", "main": "index.js", "license": "MIT", @@ -9,7 +9,7 @@ "directory": "packages/eslint-config-next" }, "dependencies": { - "@next/eslint-plugin-next": "12.1.0", + "@next/eslint-plugin-next": "12.1.1-canary.0", "@rushstack/eslint-patch": "^1.0.8", "@typescript-eslint/parser": "^5.0.0", "eslint-import-resolver-node": "^0.3.4", diff --git a/packages/eslint-plugin-next/package.json b/packages/eslint-plugin-next/package.json index 16f5abeb55cb..1c49757b9d03 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": "12.1.0", + "version": "12.1.1-canary.0", "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 90bd9a3bccf2..a69a2c0bf255 100644 --- a/packages/next-bundle-analyzer/package.json +++ b/packages/next-bundle-analyzer/package.json @@ -1,6 +1,6 @@ { "name": "@next/bundle-analyzer", - "version": "12.1.0", + "version": "12.1.1-canary.0", "main": "index.js", "license": "MIT", "repository": { diff --git a/packages/next-codemod/package.json b/packages/next-codemod/package.json index 25b85d8f7df4..c89409fea431 100644 --- a/packages/next-codemod/package.json +++ b/packages/next-codemod/package.json @@ -1,6 +1,6 @@ { "name": "@next/codemod", - "version": "12.1.0", + "version": "12.1.1-canary.0", "license": "MIT", "dependencies": { "chalk": "4.1.0", diff --git a/packages/next-env/package.json b/packages/next-env/package.json index 42a1b5c61c38..fa4c647088a0 100644 --- a/packages/next-env/package.json +++ b/packages/next-env/package.json @@ -1,6 +1,6 @@ { "name": "@next/env", - "version": "12.1.0", + "version": "12.1.1-canary.0", "keywords": [ "react", "next", diff --git a/packages/next-mdx/package.json b/packages/next-mdx/package.json index 06c4c6b95eb7..98e7de195679 100644 --- a/packages/next-mdx/package.json +++ b/packages/next-mdx/package.json @@ -1,6 +1,6 @@ { "name": "@next/mdx", - "version": "12.1.0", + "version": "12.1.1-canary.0", "main": "index.js", "license": "MIT", "repository": { diff --git a/packages/next-plugin-storybook/package.json b/packages/next-plugin-storybook/package.json index 2fedae0f986f..14d507724998 100644 --- a/packages/next-plugin-storybook/package.json +++ b/packages/next-plugin-storybook/package.json @@ -1,6 +1,6 @@ { "name": "@next/plugin-storybook", - "version": "12.1.0", + "version": "12.1.1-canary.0", "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 9f6788d25e1b..b854ebc7b32d 100644 --- a/packages/next-polyfill-module/package.json +++ b/packages/next-polyfill-module/package.json @@ -1,6 +1,6 @@ { "name": "@next/polyfill-module", - "version": "12.1.0", + "version": "12.1.1-canary.0", "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 3882f7dc5d81..41e661030c92 100644 --- a/packages/next-polyfill-nomodule/package.json +++ b/packages/next-polyfill-nomodule/package.json @@ -1,6 +1,6 @@ { "name": "@next/polyfill-nomodule", - "version": "12.1.0", + "version": "12.1.1-canary.0", "description": "A polyfill for non-dead, nomodule browsers.", "main": "dist/polyfill-nomodule.js", "license": "MIT", diff --git a/packages/next-swc/package.json b/packages/next-swc/package.json index f71acc377734..21019962a9a5 100644 --- a/packages/next-swc/package.json +++ b/packages/next-swc/package.json @@ -1,6 +1,6 @@ { "name": "@next/swc", - "version": "12.1.0", + "version": "12.1.1-canary.0", "private": true, "scripts": { "build-native": "napi build --platform --cargo-name next_swc_napi native", diff --git a/packages/next/package.json b/packages/next/package.json index d681c2e8b8a1..a506ebba60e7 100644 --- a/packages/next/package.json +++ b/packages/next/package.json @@ -1,6 +1,6 @@ { "name": "next", - "version": "12.1.0", + "version": "12.1.1-canary.0", "description": "The React Framework", "main": "./dist/server/next.js", "license": "MIT", @@ -69,7 +69,7 @@ ] }, "dependencies": { - "@next/env": "12.1.0", + "@next/env": "12.1.1-canary.0", "caniuse-lite": "^1.0.30001283", "postcss": "8.4.5", "styled-jsx": "5.0.0", @@ -117,11 +117,11 @@ "@hapi/accept": "5.0.2", "@napi-rs/cli": "2.4.4", "@napi-rs/triples": "1.0.3", - "@next/polyfill-module": "12.1.0", - "@next/polyfill-nomodule": "12.1.0", - "@next/react-dev-overlay": "12.1.0", - "@next/react-refresh-utils": "12.1.0", - "@next/swc": "12.1.0", + "@next/polyfill-module": "12.1.1-canary.0", + "@next/polyfill-nomodule": "12.1.1-canary.0", + "@next/react-dev-overlay": "12.1.1-canary.0", + "@next/react-refresh-utils": "12.1.1-canary.0", + "@next/swc": "12.1.1-canary.0", "@peculiar/webcrypto": "1.1.7", "@taskr/clear": "1.1.0", "@taskr/esnext": "1.1.0", diff --git a/packages/react-dev-overlay/package.json b/packages/react-dev-overlay/package.json index a1d512c8a6f7..9ec2fdd3d028 100644 --- a/packages/react-dev-overlay/package.json +++ b/packages/react-dev-overlay/package.json @@ -1,6 +1,6 @@ { "name": "@next/react-dev-overlay", - "version": "12.1.0", + "version": "12.1.1-canary.0", "description": "A development-only overlay for developing React applications.", "repository": { "url": "vercel/next.js", diff --git a/packages/react-refresh-utils/package.json b/packages/react-refresh-utils/package.json index f797514e1ac9..0389263d1fe1 100644 --- a/packages/react-refresh-utils/package.json +++ b/packages/react-refresh-utils/package.json @@ -1,6 +1,6 @@ { "name": "@next/react-refresh-utils", - "version": "12.1.0", + "version": "12.1.1-canary.0", "description": "An experimental package providing utilities for React Refresh.", "repository": { "url": "vercel/next.js", From 9e77ef4ec6d9faaeb0e01469337382b04ba34c42 Mon Sep 17 00:00:00 2001 From: Steven Date: Fri, 18 Feb 2022 13:44:32 -0500 Subject: [PATCH 16/54] Fix `next/image` incorrectly warning for `position: absolute` parent (#34551) - Fixes #33007 - Fixes #31340 --- packages/next/client/image.tsx | 3 ++- .../default/pages/layout-fill-inside-nonrelative.js | 4 ++++ test/integration/image-component/default/test/index.test.js | 3 +++ 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/packages/next/client/image.tsx b/packages/next/client/image.tsx index 2bc5f9ed8cb1..c16b26a05678 100644 --- a/packages/next/client/image.tsx +++ b/packages/next/client/image.tsx @@ -280,7 +280,8 @@ function handleLoading( } else if ( layout === 'fill' && parent.position !== 'relative' && - parent.position !== 'fixed' + parent.position !== 'fixed' && + parent.position !== 'absolute' ) { console.warn( `Image with src "${src}" may not render properly with a parent using position:"${parent.position}". Consider changing the parent style to position:"relative" with a width and height.` diff --git a/test/integration/image-component/default/pages/layout-fill-inside-nonrelative.js b/test/integration/image-component/default/pages/layout-fill-inside-nonrelative.js index 855c370058fe..2c831a789e89 100644 --- a/test/integration/image-component/default/pages/layout-fill-inside-nonrelative.js +++ b/test/integration/image-component/default/pages/layout-fill-inside-nonrelative.js @@ -2,6 +2,7 @@ import React from 'react' import Image from 'next/image' import jpg from '../public/test.jpg' import png from '../public/test.png' +import avif from '../public/test.avif' import webp from '../public/test.webp' const Page = () => { @@ -14,6 +15,9 @@ const Page = () => {
+
+ +
diff --git a/test/integration/image-component/default/test/index.test.js b/test/integration/image-component/default/test/index.test.js index 7d406b9e6335..2821fe553d6c 100644 --- a/test/integration/image-component/default/test/index.test.js +++ b/test/integration/image-component/default/test/index.test.js @@ -669,6 +669,9 @@ function runTests(mode) { expect(warnings).not.toMatch( /Image with src (.*)png(.*) may not render properly/gm ) + expect(warnings).not.toMatch( + /Image with src (.*)avif(.*) may not render properly/gm + ) expect(warnings).not.toMatch( /Image with src (.*)webp(.*) may not render properly/gm ) From 670eb03841d242cadfd6040c323f256ffde718fa Mon Sep 17 00:00:00 2001 From: Mitchell Abbott Date: Fri, 18 Feb 2022 12:51:02 -0600 Subject: [PATCH 17/54] Convert custom-server-hapi Example to Typescript (#34507) In the contributing doc, it mentions "Examples should be TypeScript first, if possible" so I thought about converting some examples to be typescript along with updating a couple in my free time. Let me know if that would be an issue. ## Bug - [ ] Related issues linked using `fixes #number` - [ ] Integration tests added - [ ] Errors have helpful link attached, see `contributing.md` ## Feature - [ ] Implements an existing feature request or RFC. Make sure the feature request has been accepted for implementation before opening a PR. - [ ] Related issues linked using `fixes #number` - [ ] Integration tests added - [ ] Documentation added - [ ] Telemetry added. In case of a feature if it's used or not. - [ ] Errors have helpful link attached, see `contributing.md` ## Documentation / Examples - [X] Make sure the linting passes by running `yarn lint` --- examples/custom-server-hapi/next-env.d.ts | 5 ++++ examples/custom-server-hapi/next-wrapper.js | 26 ------------------- examples/custom-server-hapi/nodemon.json | 5 ++++ examples/custom-server-hapi/package.json | 19 ++++++++++---- .../custom-server-hapi/pages/{a.js => a.tsx} | 0 .../custom-server-hapi/pages/{b.js => b.tsx} | 0 .../pages/{index.js => index.tsx} | 0 .../custom-server-hapi/server/next-wrapper.ts | 16 ++++++++++++ .../{server.js => server/server.ts} | 6 ++--- examples/custom-server-hapi/tsconfig.json | 21 +++++++++++++++ .../custom-server-hapi/tsconfig.server.json | 12 +++++++++ 11 files changed, 76 insertions(+), 34 deletions(-) create mode 100644 examples/custom-server-hapi/next-env.d.ts delete mode 100644 examples/custom-server-hapi/next-wrapper.js create mode 100644 examples/custom-server-hapi/nodemon.json rename examples/custom-server-hapi/pages/{a.js => a.tsx} (100%) rename examples/custom-server-hapi/pages/{b.js => b.tsx} (100%) rename examples/custom-server-hapi/pages/{index.js => index.tsx} (100%) create mode 100644 examples/custom-server-hapi/server/next-wrapper.ts rename examples/custom-server-hapi/{server.js => server/server.ts} (81%) create mode 100644 examples/custom-server-hapi/tsconfig.json create mode 100644 examples/custom-server-hapi/tsconfig.server.json diff --git a/examples/custom-server-hapi/next-env.d.ts b/examples/custom-server-hapi/next-env.d.ts new file mode 100644 index 000000000000..4f11a03dc6cc --- /dev/null +++ b/examples/custom-server-hapi/next-env.d.ts @@ -0,0 +1,5 @@ +/// +/// + +// NOTE: This file should not be edited +// see https://nextjs.org/docs/basic-features/typescript for more information. diff --git a/examples/custom-server-hapi/next-wrapper.js b/examples/custom-server-hapi/next-wrapper.js deleted file mode 100644 index 431191084fa8..000000000000 --- a/examples/custom-server-hapi/next-wrapper.js +++ /dev/null @@ -1,26 +0,0 @@ -const nextHandlerWrapper = (app) => { - const handler = app.getRequestHandler() - return async ({ raw, url, query }, h) => { - url.query = query - await handler(raw.req, raw.res, url) - return h.close - } -} - -const pathWrapper = - (app, pathName, opts) => - async ({ raw, query, params }, h) => { - const html = await app.render( - raw.req, - raw.res, - pathName, - { ...query, ...params }, - opts - ) - return h.response(html).code(raw.res.statusCode) - } - -module.exports = { - pathWrapper, - nextHandlerWrapper, -} diff --git a/examples/custom-server-hapi/nodemon.json b/examples/custom-server-hapi/nodemon.json new file mode 100644 index 000000000000..c9fa4132a155 --- /dev/null +++ b/examples/custom-server-hapi/nodemon.json @@ -0,0 +1,5 @@ +{ + "watch": ["server"], + "exec": "ts-node --project tsconfig.server.json server/server.ts", + "ext": "js ts" +} diff --git a/examples/custom-server-hapi/package.json b/examples/custom-server-hapi/package.json index 6de2563a24e1..60b0362bd7c6 100644 --- a/examples/custom-server-hapi/package.json +++ b/examples/custom-server-hapi/package.json @@ -1,15 +1,24 @@ { "private": true, "scripts": { - "dev": "node server.js", - "build": "next build", - "start": "cross-env NODE_ENV=production node server.js" + "dev": "nodemon", + "build": "next build && tsc --project tsconfig.server.json", + "start": "cross-env NODE_ENV=production node dist/server.js" }, "dependencies": { - "@hapi/hapi": "^18.3.1", - "cross-env": "^5.2.0", + "@hapi/hapi": "^20.2.1", + "cross-env": "^7.0.3", "next": "latest", "react": "^17.0.2", "react-dom": "^17.0.2" + }, + "devDependencies": { + "@types/hapi__hapi": "^20.0.10", + "@types/node": "^16.11.25", + "@types/react": "^17.0.39", + "@types/react-dom": "^17.0.11", + "nodemon": "^2.0.15", + "ts-node": "^10.5.0", + "typescript": "^4.5.5" } } diff --git a/examples/custom-server-hapi/pages/a.js b/examples/custom-server-hapi/pages/a.tsx similarity index 100% rename from examples/custom-server-hapi/pages/a.js rename to examples/custom-server-hapi/pages/a.tsx diff --git a/examples/custom-server-hapi/pages/b.js b/examples/custom-server-hapi/pages/b.tsx similarity index 100% rename from examples/custom-server-hapi/pages/b.js rename to examples/custom-server-hapi/pages/b.tsx diff --git a/examples/custom-server-hapi/pages/index.js b/examples/custom-server-hapi/pages/index.tsx similarity index 100% rename from examples/custom-server-hapi/pages/index.js rename to examples/custom-server-hapi/pages/index.tsx diff --git a/examples/custom-server-hapi/server/next-wrapper.ts b/examples/custom-server-hapi/server/next-wrapper.ts new file mode 100644 index 000000000000..596aac18c613 --- /dev/null +++ b/examples/custom-server-hapi/server/next-wrapper.ts @@ -0,0 +1,16 @@ +import type { NextServer } from 'next/dist/server/next' +import type { Lifecycle } from '@hapi/hapi' +import type { NextUrlWithParsedQuery } from 'next/dist/server/request-meta' + +const nextHandlerWrapper = (app: NextServer): Lifecycle.Method => { + const handler = app.getRequestHandler() + + return async ({ raw, url, query }, h) => { + const nextUrl = url as unknown as NextUrlWithParsedQuery + nextUrl.query = query + await handler(raw.req, raw.res, nextUrl) + return h.close + } +} + +export { nextHandlerWrapper } diff --git a/examples/custom-server-hapi/server.js b/examples/custom-server-hapi/server/server.ts similarity index 81% rename from examples/custom-server-hapi/server.js rename to examples/custom-server-hapi/server/server.ts index 90101efe9d8e..ea7dc52ef78e 100644 --- a/examples/custom-server-hapi/server.js +++ b/examples/custom-server-hapi/server/server.ts @@ -1,6 +1,6 @@ -const next = require('next') -const Hapi = require('@hapi/hapi') -const { /* pathWrapper, */ nextHandlerWrapper } = require('./next-wrapper') +import next from 'next' +import Hapi from '@hapi/hapi' +import { nextHandlerWrapper } from './next-wrapper' const port = parseInt(process.env.PORT, 10) || 3000 const dev = process.env.NODE_ENV !== 'production' diff --git a/examples/custom-server-hapi/tsconfig.json b/examples/custom-server-hapi/tsconfig.json new file mode 100644 index 000000000000..fe57f7dbf638 --- /dev/null +++ b/examples/custom-server-hapi/tsconfig.json @@ -0,0 +1,21 @@ +{ + "compilerOptions": { + "target": "es5", + "lib": ["dom", "dom.iterable", "esnext"], + "baseUrl": ".", + "allowJs": true, + "skipLibCheck": true, + "strict": false, + "forceConsistentCasingInFileNames": true, + "noEmit": true, + "incremental": true, + "esModuleInterop": true, + "module": "esnext", + "moduleResolution": "node", + "resolveJsonModule": true, + "isolatedModules": true, + "jsx": "preserve" + }, + "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"], + "exclude": ["node_modules"] +} diff --git a/examples/custom-server-hapi/tsconfig.server.json b/examples/custom-server-hapi/tsconfig.server.json new file mode 100644 index 000000000000..0cf1e05d407a --- /dev/null +++ b/examples/custom-server-hapi/tsconfig.server.json @@ -0,0 +1,12 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "module": "commonjs", + "outDir": "dist", + "lib": ["es2019"], + "target": "es2019", + "isolatedModules": false, + "noEmit": false + }, + "include": ["server/**/*.ts"] +} From 204a95586dccefb24bc0790f2a39f267310dc402 Mon Sep 17 00:00:00 2001 From: Gerald Monaco Date: Fri, 18 Feb 2022 11:07:19 -0800 Subject: [PATCH 18/54] Remove useMaybeDeferContent hook (#34506) --- packages/next/pages/_document.tsx | 391 ++++++++++------------ packages/next/server/render.tsx | 8 - packages/next/shared/lib/html-context.ts | 3 +- packages/next/shared/lib/utils.ts | 5 - test/unit/use-maybe-defer-content.test.ts | 12 - 5 files changed, 171 insertions(+), 248 deletions(-) delete mode 100644 test/unit/use-maybe-defer-content.test.ts diff --git a/packages/next/pages/_document.tsx b/packages/next/pages/_document.tsx index 6af1f81fc86a..309ae6321dcb 100644 --- a/packages/next/pages/_document.tsx +++ b/packages/next/pages/_document.tsx @@ -492,7 +492,6 @@ export class Head extends Component< unstable_runtimeJS, unstable_JsPreload, disableOptimizedLoading, - useMaybeDeferContent, optimizeCss, optimizeFonts, runtime, @@ -610,36 +609,96 @@ export class Head extends Component< inAmpMode ) - // Must use nested component to allow use of a custom hook - const DeferrableHead = () => { - const getDynamicHeadContent = () => { - return ( + return ( + + {!hasConcurrentFeatures && this.context.isDevelopment && ( <> - {head} - +