diff --git a/.github/workflows/build_test_deploy.yml b/.github/workflows/build_test_deploy.yml index af7ec8598aa5b6..917d65de05b192 100644 --- a/.github/workflows/build_test_deploy.yml +++ b/.github/workflows/build_test_deploy.yml @@ -923,13 +923,6 @@ jobs: node-version: 16 check-latest: true - - name: Install - uses: actions-rs/toolchain@v1 - if: ${{ steps.docs-change.outputs.DOCS_CHANGE == 'nope' }} - with: - profile: minimal - toolchain: ${{ env.RUST_TOOLCHAIN }} - - name: Cache cargo registry uses: actions/cache@v3 if: ${{ steps.docs-change.outputs.DOCS_CHANGE == 'nope' }} @@ -976,14 +969,20 @@ jobs: restore-keys: | next-swc-cargo-cache-dev-ubuntu-latest - # since the repo's dependencies aren't installed we need - # to install napi globally - - run: npm i -g @napi-rs/cli@${{ env.NAPI_CLI_VERSION }} turbo@${{ env.TURBO_VERSION }} pnpm@${PNPM_VERSION} - - name: Build - if: ${{ steps.docs-change.outputs.DOCS_CHANGE == 'nope' }} - run: turbo run build-native --cache-dir=".turbo" -- --release - env: - MACOSX_DEPLOYMENT_TARGET: '10.13' + - name: Build in docker + uses: addnab/docker-run-action@v3 + with: + image: ghcr.io/napi-rs/napi-rs/nodejs-rust:stable-2022-10-24-x64 + options: -e RUST_TOOLCHAIN=${{ env.RUST_TOOLCHAIN }} -e NAPI_CLI_VERSION=${{ env.NAPI_CLI_VERSION }} -e TURBO_VERSION=${{ env.TURBO_VERSION }} -e TURBO_TEAM=vercel -e TURBO_TOKEN=${{ secrets.TURBO_TOKEN }} -e TURBO_REMOTE_ONLY=true -v ${{ env.HOME }}/.cargo/git:/root/.cargo/git -v ${{ env.HOME }}/.cargo/registry:/root/.cargo/registry -v ${{ github.workspace }}:/build -w /build + run: | + set -e && + rustup toolchain install "${RUST_TOOLCHAIN}" && + rustup default "${RUST_TOOLCHAIN}" && + rustup target add x86_64-unknown-linux-gnu && + npm i -g "@napi-rs/cli@${NAPI_CLI_VERSION}" "turbo@${TURBO_VERSION}" && if [ ! -f $(dirname $(which yarn))/pnpm ]; then ln -s $(which yarn) $(dirname $(which yarn))/pnpm;fi && + unset CC_x86_64_unknown_linux_gnu && unset CC && + turbo run build-native --cache-dir=".turbo" -- --release --target x86_64-unknown-linux-gnu && + strip packages/next-swc/native/next-swc.*.node - name: Upload artifact uses: actions/upload-artifact@v3 diff --git a/packages/next/client/components/layout-router.tsx b/packages/next/client/components/layout-router.tsx index 6f852803831858..aa3be0ae8441e9 100644 --- a/packages/next/client/components/layout-router.tsx +++ b/packages/next/client/components/layout-router.tsx @@ -1,10 +1,4 @@ 'use client' - -import React, { useContext, useEffect, useRef, use } from 'react' -import type { - ChildProp, - //Segment -} from '../../server/app-render' import type { AppRouterInstance, ChildSegmentMap, @@ -12,9 +6,13 @@ import type { import type { FlightRouterState, FlightSegmentPath, - // FlightDataPath, } from '../../server/app-render' import type { ErrorComponent } from './error-boundary' +import type { FocusAndScrollRef } from './reducer' + +import React, { useContext, useEffect, use } from 'react' +import { findDOMNode as ReactDOMfindDOMNode } from 'react-dom' +import type { ChildProp } from '../../server/app-render' import { CacheStates, LayoutRouterContext, @@ -77,6 +75,31 @@ function walkAddRefetch( return treeToRecreate } +// TODO-APP: Replace with new React API for finding dom nodes without a `ref` when available +/** + * Wraps ReactDOM.findDOMNode with additional logic to hide React Strict Mode warning + */ +function findDOMNode( + instance: Parameters[0] +): ReturnType { + // Only apply strict mode warning when not in production + if (process.env.NODE_ENV !== 'production') { + const originalConsoleError = console.error + try { + console.error = (...messages) => { + // Ignore strict mode warning for the findDomNode call below + if (!messages[0].includes('Warning: %s is deprecated in StrictMode.')) { + originalConsoleError(...messages) + } + } + return ReactDOMfindDOMNode(instance) + } finally { + console.error = originalConsoleError! + } + } + return ReactDOMfindDOMNode(instance) +} + /** * Check if the top of the HTMLElement is in the viewport. */ @@ -85,6 +108,36 @@ function topOfElementInViewport(element: HTMLElement) { return rect.top >= 0 } +class ScrollAndFocusHandler extends React.Component<{ + focusAndScrollRef: FocusAndScrollRef + children: React.ReactNode +}> { + componentDidMount() { + // Handle scroll and focus, it's only applied once in the first useEffect that triggers that changed. + const { focusAndScrollRef } = this.props + const domNode = findDOMNode(this) + + if (focusAndScrollRef.apply && domNode instanceof HTMLElement) { + // State is mutated to ensure that the focus and scroll is applied only once. + focusAndScrollRef.apply = false + // Set focus on the element + domNode.focus() + // Only scroll into viewport when the layout is not visible currently. + if (!topOfElementInViewport(domNode)) { + const htmlElement = document.documentElement + const existing = htmlElement.style.scrollBehavior + htmlElement.style.scrollBehavior = 'auto' + domNode.scrollIntoView() + htmlElement.style.scrollBehavior = existing + } + } + } + + render() { + return this.props.children + } +} + /** * InnerLayoutRouter handles rendering the provided segment based on the cache. */ @@ -117,26 +170,6 @@ export function InnerLayoutRouter({ const { changeByServerResponse, tree: fullTree, focusAndScrollRef } = context - const focusAndScrollElementRef = useRef(null) - - useEffect(() => { - // Handle scroll and focus, it's only applied once in the first useEffect that triggers that changed. - if (focusAndScrollRef.apply && focusAndScrollElementRef.current) { - // State is mutated to ensure that the focus and scroll is applied only once. - focusAndScrollRef.apply = false - // Set focus on the element - focusAndScrollElementRef.current.focus() - // Only scroll into viewport when the layout is not visible currently. - if (!topOfElementInViewport(focusAndScrollElementRef.current)) { - const htmlElement = document.documentElement - const existing = htmlElement.style.scrollBehavior - htmlElement.style.scrollBehavior = 'auto' - focusAndScrollElementRef.current.scrollIntoView() - htmlElement.style.scrollBehavior = existing - } - } - }, [focusAndScrollRef]) - // Read segment path from the parallel router cache node. let childNode = childNodes.get(path) @@ -257,9 +290,9 @@ export function InnerLayoutRouter({ // Ensure root layout is not wrapped in a div as the root layout renders `` return rootLayoutIncluded ? ( -
+ {subtree} -
+ ) : ( subtree )