diff --git a/.alexrc b/.alexrc index 1f1de4b4382a9..d50d2433b6386 100644 --- a/.alexrc +++ b/.alexrc @@ -18,6 +18,7 @@ "host-hostess", "invalid", "remains", + "special", "white" ] } diff --git a/packages/next-swc/.cargo/config.toml b/.cargo/config.toml similarity index 92% rename from packages/next-swc/.cargo/config.toml rename to .cargo/config.toml index 859c53d0aa414..4b7106839cd3f 100644 --- a/packages/next-swc/.cargo/config.toml +++ b/.cargo/config.toml @@ -16,6 +16,7 @@ linker = "aarch64-linux-musl-gcc" rustflags = [ "--cfg", "tokio_unstable", + "-Zshare-generics=y", "-Csymbol-mangling-version=v0", "-Ctarget-feature=-crt-static", "-Clink-arg=-lgcc", @@ -28,6 +29,7 @@ linker = "arm-linux-gnueabihf-gcc" rustflags = [ "--cfg", "tokio_unstable", + "-Zshare-generics=y", "-Csymbol-mangling-version=v0", "-Aclippy::too_many_arguments", ] diff --git a/packages/next-swc/.config/nextest.toml b/.config/nextest.toml similarity index 100% rename from packages/next-swc/.config/nextest.toml rename to .config/nextest.toml diff --git a/.eslintignore b/.eslintignore index 2ccbb94acecb3..1909ecb580b1a 100644 --- a/.eslintignore +++ b/.eslintignore @@ -1,6 +1,7 @@ node_modules **/.next/** **/_next/** +**/.vscode/** **/dist/** e2e-tests/** examples/with-eslint/** diff --git a/.github/.kodiak.toml b/.github/.kodiak.toml index ee535afe225c9..c993778527988 100644 --- a/.github/.kodiak.toml +++ b/.github/.kodiak.toml @@ -6,7 +6,7 @@ automerge_label = "ready to land" require_automerge_label = false method = "squash" delete_branch_on_merge = true -optimistic_updates = true +optimistic_updates = false prioritize_ready_to_merge = true notify_on_conflict = false @@ -16,4 +16,4 @@ body = "pull_request_body" include_coauthors= true include_pr_number = true body_type = "markdown" -strip_html_comments = true +strip_html_comments = true \ No newline at end of file diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 556cc321fc9ea..f56babacbfa40 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1,29 +1,19 @@ # Learn how to add code owners here: # https://help.github.com/en/articles/about-code-owners -* @timneutkens @ijjk @shuding @huozhi @feedthejim +# Part of code owners now use Vercel Spaces +# Search .vercel.approvers for all files + /.git* @vercel/next-js -/docs/ @vercel/next-js @leerob -/errors/ @vercel/next-js @leerob -/examples/ @vercel/next-js @leerob @steven-tey -/scripts/ @vercel/next-js /.alex* @vercel/next-js @leerob /.eslint* @vercel/next-js @leerob /.prettier* @vercel/next-js @leerob -/*.md @vercel/next-js @leerob -/packages/create-next-app/ @vercel/next-js # Image Component (@styfle) -/**/*image* @timneutkens @ijjk @shuding @styfle -/**/*image*/** @timneutkens @ijjk @shuding @styfle +/**/*image* @timneutkens @ijjk @shuding @styfle @huozhi +/**/*image*/** @timneutkens @ijjk @shuding @styfle @huozhi /packages/next/client/use-intersection.tsx @timneutkens @ijjk @shuding @styfle /packages/next/server/lib/squoosh/ @timneutkens @ijjk @shuding @styfle -/packages/next/server/serve-static.ts @timneutkens @ijjk @shuding @styfle -/packages/next/server/config.ts @timneutkens @ijjk @shuding @styfle - -# Tooling & Telemetry - -/packages/next/src/build/ @timneutkens @ijjk @shuding @vercel/web-tooling @huozhi -/packages/next/src/telemetry/ @timneutkens @ijjk @shuding @padmaia -/packages/next-swc/ @timneutkens @ijjk @shuding @vercel/web-tooling +/packages/next/server/serve-static.ts @timneutkens @ijjk @shuding @styfle @huozhi +/packages/next/server/config.ts @timneutkens @ijjk @shuding @styfle @huozhi diff --git a/.github/ISSUE_TEMPLATE/1.bug_report.yml b/.github/ISSUE_TEMPLATE/1.bug_report.yml index 5c89c839b26bd..272eb0f8b3e84 100644 --- a/.github/ISSUE_TEMPLATE/1.bug_report.yml +++ b/.github/ISSUE_TEMPLATE/1.bug_report.yml @@ -51,10 +51,11 @@ body: - 'TypeScript (plugin, built-in types)' - type: input attributes: - label: Link to the code that reproduces this issue + label: Link to the code that reproduces this issue or a replay of the bug description: | - A link to a GitHub repository, a [CodeSandbox](https://codesandbox.io/p/sandbox/github/vercel/next.js/tree/canary/examples/reproduction-template) or a [StackBlitz](https://stackblitz.com/fork/github/vercel/next.js/tree/canary/examples/reproduction-template) minimal reproduction. Minimal reproductions should be created from our [bug report template with `npx create-next-app -e reproduction-template`](https://github.com/vercel/next.js/tree/canary/examples/reproduction-template) and should include only changes that contribute to the issue. - To report an App Router related issue, you can use these templates: [CodeSandbox](https://codesandbox.io/p/sandbox/github/vercel/next.js/tree/canary/examples/reproduction-template-app-dir), [StackBlitz](https://stackblitz.com/fork/github/vercel/next.js/tree/canary/examples/reproduction-template-app-dir) or [`npx create-next-app -e reproduction-template-app-dir`](https://github.com/vercel/next.js/tree/canary/examples/reproduction-template-app-dir) + A link to a [GitHub repository](https://github.com/vercel/next.js/tree/canary/examples/reproduction-template) or a [CodeSandbox](https://codesandbox.io/p/sandbox/github/vercel/next.js/tree/canary/examples/reproduction-template) minimal reproduction. Minimal reproductions should be created from our [bug report template with `npx create-next-app -e reproduction-template`](https://github.com/vercel/next.js/tree/canary/examples/reproduction-template) and should include only changes that contribute to the issue. + If a minimal reproduction can't be created please share a [replay](https://www.replay.io/) of the bug which doesn't require sharing a private repo. + To report an App Router related issue, you can use these templates: [CodeSandbox](https://codesandbox.io/p/sandbox/github/vercel/next.js/tree/canary/examples/reproduction-template-app-dir) or [`npx create-next-app -e reproduction-template-app-dir`](https://github.com/vercel/next.js/tree/canary/examples/reproduction-template-app-dir) validations: required: true - type: textarea diff --git a/.github/ISSUE_TEMPLATE/4.docs_request.yml b/.github/ISSUE_TEMPLATE/4.docs_request.yml index a2fe06929844e..67111ee7e776d 100644 --- a/.github/ISSUE_TEMPLATE/4.docs_request.yml +++ b/.github/ISSUE_TEMPLATE/4.docs_request.yml @@ -4,6 +4,12 @@ title: 'Docs: ' labels: - 'template: documentation' body: + - type: markdown + attributes: + value: Before opening this issue to request a docs improvement, is this something you can help us with? Your contributions are welcomed and appreciated. See our [Docs Contribution Guide](https://nextjs.org/docs/community/contribution-guide) to learn how to edit the Next.js docs. + - type: markdown + attributes: + value: Thank you for helping us improve the docs! - type: textarea attributes: label: What is the improvement or update you wish to see? diff --git a/.github/actions/issue-validator/canary.md b/.github/actions/issue-validator/canary.md index 9f27fff657478..1f8d0fd59c7fa 100644 --- a/.github/actions/issue-validator/canary.md +++ b/.github/actions/issue-validator/canary.md @@ -10,7 +10,7 @@ If the issue does not reproduce with the `canary` version, then it has already b ### **How can I quickly verify if my issue has been fixed in `canary`?** -The safest way is to install `next@canary` in your project and test it, but you can also search through [closed Next.js issues](https://github.com/vercel/next.js/issues?q=is%3Aissue+is%3Aclosed) for duplicates or check the [Next.js releases](https://github.com/vercel/next.js/releases). You can also use the GitHub [template](https://github.com/vercel/next.js/tree/canary/examples/reproduction-template) (preferred), or the [CodeSandbox](https://codesandbox.io/s/github/vercel/next.js/tree/canary/examples/reproduction-template) or [StackBlitz](https://stackblitz.com/fork/github/vercel/next.js/tree/canary/examples/reproduction-template) templates to create a reproduction with `canary` from scratch. +The safest way is to install `next@canary` in your project and test it, but you can also search through [closed Next.js issues](https://github.com/vercel/next.js/issues?q=is%3Aissue+is%3Aclosed) for duplicates or check the [Next.js releases](https://github.com/vercel/next.js/releases). You can also use the GitHub templates (preferred) for [pages](https://github.com/vercel/next.js/tree/canary/examples/reproduction-template) and [App Router](https://github.com/vercel/next.js/tree/canary/examples/reproduction-template-app-dir), or the [CodeSandbox: `pages`](https://codesandbox.io/s/github/vercel/next.js/tree/canary/examples/reproduction-template) or [CodeSandbox: App Router](https://github.com/vercel/next.js/tree/canary/examples/reproduction-template-app-dir) templates to create a reproduction with `canary` from scratch. ### **My issue has been open for a long time, why do I need to verify `canary` now?** diff --git a/.github/actions/issue-validator/repro.md b/.github/actions/issue-validator/repro.md index 0dbfa09a35ad9..484297011d8ee 100644 --- a/.github/actions/issue-validator/repro.md +++ b/.github/actions/issue-validator/repro.md @@ -2,7 +2,7 @@ We cannot recreate the issue with the provided information. **Please add a repro ### **Why was this issue marked with the `please add a complete reproduction` label?** -To be able to investigate, we need access to a reproduction to identify what triggered the issue. We prefer a link to a public GitHub repository ([template](https://github.com/vercel/next.js/tree/canary/examples/reproduction-template)), but you can also use a tool like [CodeSandbox](https://codesandbox.io/s/github/vercel/next.js/tree/canary/examples/reproduction-template) or [StackBlitz](https://stackblitz.com/fork/github/vercel/next.js/tree/canary/examples/reproduction-template). +To be able to investigate, we need access to a reproduction to identify what triggered the issue. We prefer a link to a public GitHub repository ([template for `pages`](https://github.com/vercel/next.js/tree/canary/examples/reproduction-template), [template for App Router](https://github.com/vercel/next.js/tree/canary/examples/reproduction-template-app-dir)), but you can also use these templates: [CodeSandbox: `pages`](https://codesandbox.io/s/github/vercel/next.js/tree/canary/examples/reproduction-template) or [CodeSandbox: App Router](https://github.com/vercel/next.js/tree/canary/examples/reproduction-template-app-dir). To make sure the issue is resolved as quickly as possible, please make sure that the reproduction is as **minimal** as possible. This means that you should **remove unnecessary code, files, and dependencies** that do not contribute to the issue. diff --git a/.github/actions/next-stats-action/Dockerfile b/.github/actions/next-stats-action/Dockerfile index 61492813942db..d61c0eedb1091 100644 --- a/.github/actions/next-stats-action/Dockerfile +++ b/.github/actions/next-stats-action/Dockerfile @@ -1,4 +1,4 @@ -FROM node:16-bullseye +FROM ubuntu:22.04 LABEL com.github.actions.name="Next.js PR Stats" LABEL com.github.actions.description="Compares stats of a PR with the main branch" @@ -6,8 +6,15 @@ LABEL repository="https://github.com/vercel/next-stats-action" COPY . /next-stats +RUN apt update && apt upgrade -y +RUN apt install unzip wget curl nano htop screen build-essential pkg-config libssl-dev git build-essential zlib1g-dev libncurses5-dev libgdbm-dev libnss3-dev libreadline-dev libffi-dev python3 moreutils jq iproute2 openssh-server sudo whois dnsutils -y + +RUN ln $(which python3) /usr/bin/python + +RUN curl -sfLS https://install-node.vercel.app/v18 | bash -s -- -f + # Install node_modules -RUN npm i -g pnpm@7.24.3 +RUN npm i -g pnpm@7.24.3 yarn@1.22.19 RUN cd /next-stats && pnpm install --production RUN git config --global user.email 'stats@localhost' diff --git a/.github/actions/next-stats-action/src/constants.js b/.github/actions/next-stats-action/src/constants.js index 7d199d9dd1083..66eff2c821155 100644 --- a/.github/actions/next-stats-action/src/constants.js +++ b/.github/actions/next-stats-action/src/constants.js @@ -8,9 +8,6 @@ const mainRepoDir = path.join(workDir, 'main-repo') const diffRepoDir = path.join(workDir, 'diff-repo') const statsAppDir = path.join(workDir, 'stats-app') const diffingDir = path.join(workDir, 'diff') -const yarnEnvValues = { - YARN_CACHE_FOLDER: path.join(workDir, 'yarn-cache'), -} const allowedConfigLocations = [ './', '.stats-app', @@ -25,6 +22,5 @@ module.exports = { mainRepoDir, diffRepoDir, statsAppDir, - yarnEnvValues, allowedConfigLocations, } diff --git a/.github/actions/next-stats-action/src/index.js b/.github/actions/next-stats-action/src/index.js index 23ea3882c5a46..56a80d5b40b56 100644 --- a/.github/actions/next-stats-action/src/index.js +++ b/.github/actions/next-stats-action/src/index.js @@ -41,8 +41,7 @@ if (!allowedActions.has(actionInfo.actionName) && !actionInfo.isRelease) { // clone PR/newer repository/ref first to get settings if (!actionInfo.skipClone) { - await cloneRepo(actionInfo.prRepo, diffRepoDir) - await checkoutRef(actionInfo.prRef, diffRepoDir) + await cloneRepo(actionInfo.prRepo, diffRepoDir, actionInfo.prRef) } if (actionInfo.isRelease) { @@ -67,8 +66,7 @@ if (!allowedActions.has(actionInfo.actionName) && !actionInfo.isRelease) { // clone main repository/ref if (!actionInfo.skipClone) { - await cloneRepo(statsConfig.mainRepo, mainRepoDir) - await checkoutRef(statsConfig.mainBranch, mainRepoDir) + await cloneRepo(statsConfig.mainRepo, mainRepoDir, statsConfig.mainBranch) } /* eslint-disable-next-line */ actionInfo.commitId = await getCommitId(diffRepoDir) @@ -134,6 +132,9 @@ if (!allowedActions.has(actionInfo.actionName) && !actionInfo.isRelease) { ) .catch(console.error) + console.log(await exec(`ls ${path.join(__dirname, '../native')}`)) + console.log(await exec(`cd ${dir} && ls ${dir}/packages/next-swc/native`)) + logger(`Linking packages in ${dir}`) const isMainRepo = dir === mainRepoDir const pkgPaths = await linkPackages({ diff --git a/.github/actions/next-stats-action/src/prepare/repo-setup.js b/.github/actions/next-stats-action/src/prepare/repo-setup.js index 21ad117508bdc..1da820c0c8275 100644 --- a/.github/actions/next-stats-action/src/prepare/repo-setup.js +++ b/.github/actions/next-stats-action/src/prepare/repo-setup.js @@ -8,12 +8,11 @@ const execa = require('execa') module.exports = (actionInfo) => { return { - async cloneRepo(repoPath = '', dest = '') { + async cloneRepo(repoPath = '', dest = '', branch = '', depth = '20') { await remove(dest) - await exec(`git clone ${actionInfo.gitRoot}${repoPath} ${dest}`) - }, - async checkoutRef(ref = '', repoDir = '') { - await exec(`cd ${repoDir} && git fetch && git checkout ${ref}`) + await exec( + `git clone ${actionInfo.gitRoot}${repoPath} --single-branch --branch ${branch} --depth=${depth} ${dest}` + ) }, async getLastStable(repoDir = '', ref) { const { stdout } = await exec(`cd ${repoDir} && git tag -l`) @@ -55,146 +54,105 @@ module.exports = (actionInfo) => { } }, async linkPackages({ repoDir, nextSwcVersion }) { - let useTestPack = process.env.NEXT_TEST_PACK - - if (useTestPack) { - execa.sync('pnpm', ['turbo', 'run', 'test-pack'], { - cwd: repoDir, - env: { NEXT_SWC_VERSION: nextSwcVersion }, - }) - - const pkgPaths = new Map() - const pkgs = (await fs.readdir(path.join(repoDir, 'packages'))).filter( - (item) => !item.startsWith('.') - ) + const pkgPaths = new Map() + const pkgDatas = new Map() + let pkgs - pkgs.forEach((pkgDirname) => { - const { name } = require(path.join( - repoDir, - 'packages', - pkgDirname, - 'package.json' - )) - pkgPaths.set( - name, - path.join( - repoDir, - 'packages', - pkgDirname, - `packed-${pkgDirname}.tgz` - ) - ) - }) - return pkgPaths - } else { - // TODO: remove after next stable release (current v13.1.2) - const pkgPaths = new Map() - const pkgDatas = new Map() - let pkgs - - try { - pkgs = await fs.readdir(path.join(repoDir, 'packages')) - } catch (err) { - if (err.code === 'ENOENT') { - require('console').log('no packages to link') - return pkgPaths - } - throw err + try { + pkgs = await fs.readdir(path.join(repoDir, 'packages')) + } catch (err) { + if (err.code === 'ENOENT') { + require('console').log('no packages to link') + return pkgPaths } + throw err + } - for (const pkg of pkgs) { - const pkgPath = path.join(repoDir, 'packages', pkg) - const packedPkgPath = path.join(pkgPath, `${pkg}-packed.tgz`) + for (const pkg of pkgs) { + const pkgPath = path.join(repoDir, 'packages', pkg) + const packedPkgPath = path.join(pkgPath, `${pkg}-packed.tgz`) - const pkgDataPath = path.join(pkgPath, 'package.json') - if (!fs.existsSync(pkgDataPath)) { - require('console').log(`Skipping ${pkgDataPath}`) - continue - } - const pkgData = require(pkgDataPath) - const { name } = pkgData - pkgDatas.set(name, { - pkgDataPath, - pkg, - pkgPath, - pkgData, - packedPkgPath, - }) - pkgPaths.set(name, packedPkgPath) + const pkgDataPath = path.join(pkgPath, 'package.json') + if (!fs.existsSync(pkgDataPath)) { + require('console').log(`Skipping ${pkgDataPath}`) + continue } + const pkgData = require(pkgDataPath) + const { name } = pkgData + + pkgDatas.set(name, { + pkgDataPath, + pkg, + pkgPath, + pkgData, + packedPkgPath, + }) + pkgPaths.set(name, packedPkgPath) + } - for (const pkg of pkgDatas.keys()) { - const { pkgDataPath, pkgData } = pkgDatas.get(pkg) + for (const pkg of pkgDatas.keys()) { + const { pkgDataPath, pkgData } = pkgDatas.get(pkg) - for (const pkg of pkgDatas.keys()) { - const { packedPkgPath } = pkgDatas.get(pkg) - if (!pkgData.dependencies || !pkgData.dependencies[pkg]) continue - pkgData.dependencies[pkg] = packedPkgPath - } + for (const pkg of pkgDatas.keys()) { + const { packedPkgPath } = pkgDatas.get(pkg) + if (!pkgData.dependencies || !pkgData.dependencies[pkg]) continue + pkgData.dependencies[pkg] = packedPkgPath + } - // make sure native binaries are included in local linking - if (pkg === '@next/swc') { - if (!pkgData.files) { - pkgData.files = [] - } - pkgData.files.push('native/*') - require('console').log( - 'using swc binaries: ', - await exec(`ls ${path.join(path.dirname(pkgDataPath), 'native')}`) - ) + // make sure native binaries are included in local linking + if (pkg === '@next/swc') { + if (!pkgData.files) { + pkgData.files = [] } + pkgData.files.push('native') + require('console').log( + 'using swc binaries: ', + await exec(`ls ${path.join(path.dirname(pkgDataPath), 'native')}`) + ) + } - if (pkg === 'next') { - if (nextSwcVersion) { - Object.assign(pkgData.dependencies, { - '@next/swc-linux-x64-gnu': nextSwcVersion, - }) + if (pkg === 'next') { + console.log('using swc dep', { + nextSwcVersion, + nextSwcPkg: pkgDatas.get('@next/swc'), + }) + if (nextSwcVersion) { + Object.assign(pkgData.dependencies, { + '@next/swc-linux-x64-gnu': nextSwcVersion, + }) + } else { + if (pkgDatas.get('@next/swc')) { + pkgData.dependencies['@next/swc'] = + pkgDatas.get('@next/swc').packedPkgPath } else { - if (pkgDatas.get('@next/swc')) { - pkgData.dependencies['@next/swc'] = - pkgDatas.get('@next/swc').packedPkgPath - } else { - pkgData.files.push('native/*') - } + pkgData.files.push('native') } } - - if (pkgData?.scripts?.prepublishOnly) { - // There's a bug in `pnpm pack` where it will run - // the prepublishOnly script and that will fail. - // See https://github.com/pnpm/pnpm/issues/2941 - delete pkgData.scripts.prepublishOnly - } - - await fs.writeFile( - pkgDataPath, - JSON.stringify(pkgData, null, 2), - 'utf8' - ) } - // wait to pack packages until after dependency paths have been updated - // to the correct versions - await Promise.all( - Array.from(pkgDatas.keys()).map(async (pkgName) => { - const { pkg, pkgPath, pkgData, packedPkgPath } = - pkgDatas.get(pkgName) - // Copied from pnpm source: https://github.com/pnpm/pnpm/blob/5a5512f14c47f4778b8d2b6d957fb12c7ef40127/releasing/plugin-commands-publishing/src/pack.ts#L96 - const tmpTarball = path.join( - pkgPath, - `${pkgData.name.replace('@', '').replace('/', '-')}-${ - pkgData.version - }.tgz` - ) - await execa('pnpm', ['pack'], { - cwd: pkgPath, - }) - await fs.copyFile(tmpTarball, packedPkgPath) - }) + await fs.writeFile( + pkgDataPath, + JSON.stringify(pkgData, null, 2), + 'utf8' ) - - return pkgPaths } + + // wait to pack packages until after dependency paths have been updated + // to the correct versions + await Promise.all( + Array.from(pkgDatas.keys()).map(async (pkgName) => { + const { pkgPath, packedPkgPath } = pkgDatas.get(pkgName) + + await execa('yarn', ['pack', '-f', packedPkgPath], { + cwd: pkgPath, + env: { + ...process.env, + COREPACK_ENABLE_STRICT: '0', + }, + }) + }) + ) + return pkgPaths }, } } diff --git a/.github/actions/next-stats-action/src/run/index.js b/.github/actions/next-stats-action/src/run/index.js index 2f0e053c35c78..afc647d399be5 100644 --- a/.github/actions/next-stats-action/src/run/index.js +++ b/.github/actions/next-stats-action/src/run/index.js @@ -1,13 +1,12 @@ const path = require('path') const fs = require('fs-extra') -const getPort = require('get-port') const glob = require('../util/glob') const exec = require('../util/exec') const logger = require('../util/logger') const getDirSize = require('./get-dir-size') const collectStats = require('./collect-stats') const collectDiffs = require('./collect-diffs') -const { statsAppDir, diffRepoDir, yarnEnvValues } = require('../constants') +const { statsAppDir, diffRepoDir } = require('../constants') async function runConfigs( configs = [], @@ -59,13 +58,7 @@ async function runConfigs( const buildStart = Date.now() console.log( - await exec( - `cd ${statsAppDir} && ${statsConfig.appBuildCommand}`, - false, - { - env: yarnEnvValues, - } - ) + await exec(`cd ${statsAppDir} && ${statsConfig.appBuildCommand}`, false) ) curStats.General.buildDuration = Date.now() - buildStart @@ -160,13 +153,7 @@ async function runConfigs( const secondBuildStart = Date.now() console.log( - await exec( - `cd ${statsAppDir} && ${statsConfig.appBuildCommand}`, - false, - { - env: yarnEnvValues, - } - ) + await exec(`cd ${statsAppDir} && ${statsConfig.appBuildCommand}`, false) ) curStats.General.buildDurationCached = Date.now() - secondBuildStart } @@ -203,13 +190,9 @@ async function linkPkgs(pkgDir = '', pkgPaths) { } await fs.writeFile(pkgJsonPath, JSON.stringify(pkgData, null, 2), 'utf8') - await fs.remove(yarnEnvValues.YARN_CACHE_FOLDER) await exec( `cd ${pkgDir} && pnpm install --strict-peer-dependencies=false`, - false, - { - env: yarnEnvValues, - } + false ) } diff --git a/.github/actions/setup-rust/action.yml b/.github/actions/setup-rust/action.yml new file mode 100644 index 0000000000000..bb6044cbf08f1 --- /dev/null +++ b/.github/actions/setup-rust/action.yml @@ -0,0 +1,65 @@ +name: 'Rust Setup' +description: 'Sets up the Rust toolchain for CI' +inputs: + targets: + description: 'Comma-separated list of target triples to install for this toolchain' + required: false + components: + description: 'Comma-separated list of components to be additionally installed' + required: false + skip-install: + description: 'Sets environment variables without installing the rust toolchain' + required: false + +runs: + using: 'composite' + steps: + - name: 'Get toolchain version from file' + id: file + shell: bash + run: echo "toolchain=$(cat ./rust-toolchain)" >> $GITHUB_OUTPUT + + - shell: bash + run: | + : force toolchain version + echo "RUST_TOOLCHAIN=${{ steps.file.outputs.toolchain }}" >> $GITHUB_ENV + + - shell: bash + run: | + : disable incremental compilation + if [ -z "${CARGO_INCREMENTAL+set}" ]; then + echo CARGO_INCREMENTAL=0 >> $GITHUB_ENV + fi + + - shell: bash + run: | + : enable colors in Cargo output + if [ -z "${CARGO_TERM_COLOR+set}" ]; then + echo CARGO_TERM_COLOR=always >> $GITHUB_ENV + fi + + - shell: bash + run: | + : enable rust backtrace + if [ -z "${RUST_BACKTRACE+set}" ]; then + echo RUST_BACKTRACE=short >> $GITHUB_ENV + fi + + - shell: bash + run: | + : enable faster cargo sparse registry + if [ -z "${CARGO_REGISTRIES_CRATES_IO_PROTOCOL+set}" ]; then + echo CARGO_REGISTRIES_CRATES_IO_PROTOCOL=sparse >> $GITHUB_ENV + fi + + - name: 'Setup Rust toolchain' + uses: dtolnay/rust-toolchain@master + if: ${{ !inputs.skip-install }} + with: + toolchain: ${{ steps.file.outputs.toolchain }} + targets: ${{ inputs.targets }} + components: ${{ inputs.components }} + + - name: 'Add cargo problem matchers' + shell: bash + run: echo "::add-matcher::${{ github.action_path }}/matchers.json" diff --git a/.github/actions/setup-rust/matchers.json b/.github/actions/setup-rust/matchers.json new file mode 100644 index 0000000000000..aff246556a3db --- /dev/null +++ b/.github/actions/setup-rust/matchers.json @@ -0,0 +1,44 @@ +{ + "problemMatcher": [ + { + "owner": "cargo-common", + "pattern": [ + { + "regexp": "^(warning|warn|error)(?:\\[(\\S*)\\])?: (.*)$", + "severity": 1, + "code": 2, + "message": 3 + }, + { + "regexp": "^(?:[\\s->=]*(.*):(\\d*):(\\d*)|.*)$", + "file": 1, + "line": 2, + "column": 3 + } + ] + }, + { + "owner": "cargo-test", + "pattern": [ + { + "regexp": "^.*panicked\\s+at\\s+'(.*)',\\s+(.*):(\\d+):(\\d+)$", + "message": 1, + "file": 2, + "line": 3, + "column": 4 + } + ] + }, + { + "owner": "rustfmt", + "pattern": [ + { + "regexp": "^(Diff in (\\S+)) at line (\\d+):", + "message": 1, + "file": 2, + "line": 3 + } + ] + } + ] +} diff --git a/.github/labeler.json b/.github/labeler.json index 4696c7aa8f41c..4ce7736dd6498 100644 --- a/.github/labeler.json +++ b/.github/labeler.json @@ -24,17 +24,27 @@ { "type": "user", "pattern": "huozhi" }, { "type": "user", "pattern": "ijjk" }, { "type": "user", "pattern": "JanKaifer" }, - { "type": "user", "pattern": "leerob" }, + { "type": "user", "pattern": "javivelasco" }, + { "type": "user", "pattern": "kikobeats" }, + { "type": "user", "pattern": "schniz" }, { "type": "user", "pattern": "sebmarkbage" }, { "type": "user", "pattern": "shuding" }, { "type": "user", "pattern": "styfle" }, { "type": "user", "pattern": "timneutkens" }, { "type": "user", "pattern": "wyattjoh" } ], - "created-by: Next.js docs team": [ + "created-by: Next.js DevEx team": [ + { "type": "user", "pattern": "leerob" }, { "type": "user", "pattern": "ismaelrumzan" }, - { "type": "user", "pattern": "MaedahBatool" }, - { "type": "user", "pattern": "molebox" } + { "type": "user", "pattern": "molebox" }, + { "type": "user", "pattern": "delbaoliveira" }, + { "type": "user", "pattern": "lydiahallie" }, + { "type": "user", "pattern": "steven-tey" }, + { "type": "user", "pattern": "nutlope" }, + { "type": "user", "pattern": "stephdietz" }, + { "type": "user", "pattern": "timeyoutakeit" }, + { "type": "user", "pattern": "s3prototype" }, + { "type": "user", "pattern": "manovotny" } ], "created-by: web-tooling team": [ { "type": "user", "pattern": "alexkirsz" }, diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index b898a902797e1..495728c63b988 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -4,7 +4,11 @@ Choose the right checklist for the change(s) that you're making: ## For Contributors -### Improving Documentation or adding/fixing Examples +### Improving Documentation + +- Read the Docs Contribution Guide to ensure your contribution follows the docs guidelines: https://nextjs.org/docs/community/contribution-guide + +### Adding or Updating Examples - The "examples guidelines" are followed from our contributing doc https://github.com/vercel/next.js/blob/canary/contributing/examples/adding-examples.md - Make sure the linting passes by running `pnpm build && pnpm lint`. See https://github.com/vercel/next.js/blob/canary/contributing/repository/linting.md diff --git a/.github/workflows/build_and_deploy.yml b/.github/workflows/build_and_deploy.yml new file mode 100644 index 0000000000000..acbede9271c01 --- /dev/null +++ b/.github/workflows/build_and_deploy.yml @@ -0,0 +1,471 @@ +name: build-and-deploy + +on: + push: + branches: ['canary'] + workflow_dispatch: + +env: + NAPI_CLI_VERSION: 2.14.7 + TURBO_VERSION: 1.9.6 + PNPM_VERSION: 7.24.3 + NODE_MAINTENANCE_VERSION: 16 + NODE_LTS_VERSION: 18 + +jobs: + build: + runs-on: ubuntu-latest + env: + NEXT_TELEMETRY_DISABLED: 1 + # we build a dev binary for use in CI so skip downloading + # canary next-swc binaries in the monorepo + NEXT_SKIP_NATIVE_POSTINSTALL: 1 + TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }} + outputs: + isRelease: ${{ github.event_name != 'workflow_dispatch' && steps.check-release.outputs.IS_RELEASE }} + steps: + - name: Setup node + uses: actions/setup-node@v3 + with: + node-version: ${{ env.NODE_LTS_VERSION }} + check-latest: true + + - uses: actions/checkout@v3 + with: + fetch-depth: 25 + + - run: npm i -g pnpm@${PNPM_VERSION} + + - id: get-store-path + run: echo STORE_PATH=$(pnpm store path) >> $GITHUB_OUTPUT + + - uses: actions/cache@v3 + timeout-minutes: 5 + id: cache-pnpm-store + with: + path: ${{ steps.get-store-path.outputs.STORE_PATH }} + key: pnpm-store-${{ hashFiles('pnpm-lock.yaml') }} + restore-keys: | + pnpm-store- + pnpm-store-${{ hashFiles('pnpm-lock.yaml') }} + + - run: pnpm install + + - run: pnpm run build + + - id: check-release + run: | + if [[ $(node ./scripts/check-is-release.js 2> /dev/null || :) = v* ]]; + then + echo "IS_RELEASE=true" >> $GITHUB_OUTPUT + else + echo "IS_RELEASE=false" >> $GITHUB_OUTPUT + fi + + - uses: actions/cache@v3 + timeout-minutes: 5 + id: cache-build + with: + path: ./* + key: ${{ github.sha }}-${{ github.run_number }} + + # Build binaries for publishing + build-native: + strategy: + fail-fast: false + matrix: + settings: + # pnpm is aliased here temporarily until the build docker + # image is updated past Node.js v14.19 (current 14.18.1) + - host: macos-latest + target: 'x86_64-apple-darwin' + build: | + 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 + turbo run build-native-release --summarize -- --target x86_64-apple-darwin --release + strip -x packages/next-swc/native/next-swc.*.node + - host: windows-latest + build: | + npm i -g "@napi-rs/cli@${NAPI_CLI_VERSION}" "turbo@${TURBO_VERSION}" "pnpm@${PNPM_VERSION}" + turbo run build-native-release --summarize -- --target x86_64-pc-windows-msvc + target: 'x86_64-pc-windows-msvc' + - host: windows-latest + build: | + npm i -g "@napi-rs/cli@${NAPI_CLI_VERSION}" "turbo@${TURBO_VERSION}" "pnpm@${PNPM_VERSION}" + turbo run build-native-no-plugin -- --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:stable-2022-10-24-x64 + build: >- + 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-release --summarize -- --target x86_64-unknown-linux-gnu && + strip 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:stable-2022-10-24-alpine + build: >- + set -e && + apk add --no-cache libc6-compat && + rustup toolchain install "${RUST_TOOLCHAIN}" && + rustup default "${RUST_TOOLCHAIN}" && + rustup target add x86_64-unknown-linux-musl && + 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 && + turbo run build-native-release --summarize -- --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@${NAPI_CLI_VERSION}" "turbo@${TURBO_VERSION}" && if [ ! -f $(dirname $(which yarn))/pnpm ]; then ln -s $(which yarn) $(dirname $(which yarn))/pnpm;fi + turbo run build-native-release --summarize -- --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:stable-2022-10-24-aarch64 + build: >- + set -e && + export JEMALLOC_SYS_WITH_LG_PAGE=16 && + rustup toolchain install "${RUST_TOOLCHAIN}" && + rustup default "${RUST_TOOLCHAIN}" && + rustup target add aarch64-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 && + export CC_aarch64_unknown_linux_gnu=/usr/aarch64-unknown-linux-gnu/bin/aarch64-unknown-linux-gnu-gcc && + turbo run build-native-release --summarize -- --target aarch64-unknown-linux-gnu && + llvm-strip -x packages/next-swc/native/next-swc.*.node + - host: ubuntu-latest + target: 'aarch64-unknown-linux-musl' + docker: ghcr.io/napi-rs/napi-rs/nodejs-rust:stable-2022-10-24-alpine + build: >- + set -e && + apk add --no-cache libc6-compat && + export JEMALLOC_SYS_WITH_LG_PAGE=16 && + 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 && + rustup toolchain install "${RUST_TOOLCHAIN}" && + rustup default "${RUST_TOOLCHAIN}" && + rustup target add aarch64-unknown-linux-musl && + turbo run build-native-release --summarize -- --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@${NAPI_CLI_VERSION}" "turbo@${TURBO_VERSION}" "pnpm@${PNPM_VERSION}" + turbo run build-native-no-plugin-woa-release -- --target aarch64-pc-windows-msvc + needs: build + name: stable - ${{ matrix.settings.target }} - node@16 + runs-on: ${{ matrix.settings.host }} + env: + TURBO_TEAM: 'vercel' + TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }} + TURBO_REMOTE_ONLY: 'true' + DATADOG_API_KEY: ${{ secrets.DATA_DOG_API_KEY }} + steps: + # https://github.com/actions/virtual-environments/issues/1187 + - name: tune linux network + run: sudo ethtool -K eth0 tx off rx off + if: ${{ matrix.settings.host == 'ubuntu-latest' }} + - 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.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.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@v3 + + - name: Setup node + uses: actions/setup-node@v3 + if: ${{ !matrix.settings.docker }} + with: + node-version: ${{ env.NODE_LTS_VERSION }} + check-latest: true + + - name: Install Rust + uses: ./.github/actions/setup-rust + with: + targets: ${{ matrix.settings.target }} + skip-install: ${{ matrix.settings.docker }} + + - name: Cache cargo registry + uses: actions/cache@v3 + timeout-minutes: 5 + with: + path: ~/.cargo/registry + key: ${{ matrix.settings.target }}-cargo-registry + + - name: Cache cargo index + uses: actions/cache@v3 + timeout-minutes: 5 + with: + path: ~/.cargo/git + key: ${{ matrix.settings.target }}-cargo-index + + - name: normalize versions + run: node scripts/normalize-version-bump.js + + - name: Setup toolchain + run: ${{ matrix.settings.setup }} + if: ${{ matrix.settings.setup }} + shell: bash + + - name: Build in docker + uses: addnab/docker-run-action@v3 + if: ${{ matrix.settings.docker }} + with: + image: ${{ matrix.settings.docker }} + options: >- + -e RUST_TOOLCHAIN=${{ env.RUST_TOOLCHAIN }} + -e CARGO_INCREMENTAL=${{ env.CARGO_INCREMENTAL }} + -e CARGO_TERM_COLOR=${{ env.CARGO_TERM_COLOR }} + -e RUST_BACKTRACE=${{ env.RUST_BACKTRACE }} + -e CARGO_REGISTRIES_CRATES_IO_PROTOCOL=${{ env.CARGO_REGISTRIES_CRATES_IO_PROTOCOL }} + -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: ${{ matrix.settings.build }} + + - name: 'Build' + run: ${{ matrix.settings.build }} + if: ${{ !matrix.settings.docker }} + shell: bash + + - name: 'check build cache status' + id: check-did-build + shell: bash + run: if [[ ! -z $(ls .turbo/runs) ]]; then echo "DID_BUILD=yup" >> $GITHUB_OUTPUT; fi + + # Trying to upload metrics for the Turbopack to datadog's CI pipeline execution + - name: 'Upload turbopack build metrics' + shell: bash + if: ${{ steps.check-did-build.outputs.DID_BUILD == 'yup' }} + continue-on-error: true + run: | + npm install -g @datadog/datadog-ci + shopt -s nullglob + for filename in packages/next-swc/native/next-swc.*.node; do + # Strip out filename to extract target triple + export FILENAME=$(basename ${filename}) + export FILENAME=${FILENAME#*.} + export FILENAME=${FILENAME%.node} + export BYTESIZE=$(wc -c < $filename | xargs) + echo "Reporting $FILENAME:$BYTESIZE for Turbopack bytesize" + datadog-ci metric --no-fail --level pipeline --metrics "turbopack.bytesize.$FILENAME:$BYTESIZE" + done + + - name: Upload swc artifact + if: ${{ needs.build.outputs.isRelease == 'true' }} + uses: actions/upload-artifact@v3 + with: + name: next-swc-binaries + path: packages/next-swc/native/next-swc.*.node + + - name: Upload turbo summary artifact + uses: actions/upload-artifact@v3 + with: + name: turbo run summary + path: .turbo/runs + + build-wasm: + needs: build + strategy: + matrix: + target: [web, nodejs] + runs-on: macos-latest + env: + TURBO_TEAM: 'vercel' + TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }} + TURBO_REMOTE_ONLY: 'true' + steps: + - uses: actions/checkout@v3 + + - name: Setup node + uses: actions/setup-node@v3 + with: + node-version: ${{ env.NODE_LTS_VERSION }} + check-latest: true + + - name: Install Rust + uses: ./.github/actions/setup-rust + with: + targets: wasm32-unknown-unknown + + - run: npm i -g turbo@${{ env.TURBO_VERSION }} pnpm@${PNPM_VERSION} + + - name: Install wasm-pack + run: curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh + + - name: normalize versions + run: node scripts/normalize-version-bump.js + + - name: Build + run: turbo run build-wasm --summarize -- --target ${{ matrix.target }} --features tracing/release_max_level_info + + - name: Add target to folder name + run: '[[ -d "packages/next-swc/crates/wasm/pkg" ]] && mv packages/next-swc/crates/wasm/pkg packages/next-swc/crates/wasm/pkg-${{ matrix.target }} || ls packages/next-swc/crates/wasm' + + - name: Upload turbo summary artifact + uses: actions/upload-artifact@v3 + with: + name: turbo run summary + path: .turbo/runs + + - name: Upload swc artifact + uses: actions/upload-artifact@v3 + with: + name: wasm-binaries + path: packages/next-swc/crates/wasm/pkg-* + + publishRelease: + if: ${{ needs.build.outputs.isRelease == 'true' }} + name: Potentially publish release + runs-on: ubuntu-latest + needs: + - build + - build-wasm + - build-native + permissions: + contents: write + id-token: write + env: + NPM_TOKEN: ${{ secrets.NPM_TOKEN_ELEVATED }} + steps: + - name: Setup node + uses: actions/setup-node@v3 + with: + node-version: ${{ env.NODE_LTS_VERSION }} + check-latest: true + + # https://github.com/actions/virtual-environments/issues/1187 + - name: tune linux network + run: sudo ethtool -K eth0 tx off rx off + + - uses: actions/cache@v3 + timeout-minutes: 5 + id: restore-build + with: + path: ./* + key: ${{ github.sha }}-${{ github.run_number }} + + - uses: actions/download-artifact@v3 + with: + name: next-swc-binaries + path: packages/next-swc/native + + - uses: actions/download-artifact@v3 + with: + name: wasm-binaries + path: packages/next-swc/crates/wasm + + - run: npm i -g npm@9 # need latest version for provenance + - run: npm i -g pnpm@${PNPM_VERSION} + - run: echo "//registry.npmjs.org/:_authToken=$NPM_TOKEN" >> ~/.npmrc + - run: ./scripts/publish-native.js + - run: ./scripts/publish-release.js + + deployExamples: + name: Deploy examples + runs-on: ubuntu-latest + needs: [build] + steps: + - uses: actions/checkout@v3 + with: + fetch-depth: 25 + - name: Install Vercel CLI + run: npm i -g vercel@28.16.15 + - name: Deploy preview examples + if: ${{ needs.build.outputs.isRelease != 'true' }} + run: ./scripts/deploy-examples.sh + env: + VERCEL_API_TOKEN: ${{ secrets.VERCEL_API_TOKEN }} + DEPLOY_ENVIRONMENT: preview + - name: Deploy production examples + if: ${{ needs.build.outputs.isRelease == 'true' }} + run: ./scripts/deploy-examples.sh + env: + VERCEL_API_TOKEN: ${{ secrets.VERCEL_API_TOKEN }} + DEPLOY_ENVIRONMENT: production + + testDeployE2E: + name: E2E (deploy) + runs-on: ubuntu-latest + needs: [publishRelease] + env: + NEXT_TELEMETRY_DISABLED: 1 + VERCEL_TEST_TOKEN: ${{ secrets.VERCEL_TEST_TOKEN }} + VERCEL_TEST_TEAM: vtest314-next-e2e-tests + steps: + - uses: actions/cache@v3 + timeout-minutes: 5 + id: restore-build + with: + path: ./* + key: ${{ github.sha }}-${{ github.run_number }} + + - run: npm i -g vercel@latest + + - uses: actions/download-artifact@v3 + with: + name: next-swc-binaries + path: packages/next-swc/native + + - run: RESET_VC_PROJECT=true node scripts/reset-vercel-project.mjs + name: Reset test project + + - run: docker run --rm -v $(pwd):/work mcr.microsoft.com/playwright:v1.28.1-jammy /bin/bash -c "cd /work && NODE_VERSION=${{ env.NODE_LTS_VERSION }} ./scripts/setup-node.sh && npm i -g pnpm@${PNPM_VERSION} > /dev/null && VERCEL_TEST_TOKEN=${{ secrets.VERCEL_TEST_TOKEN }} VERCEL_TEST_TEAM=vtest314-next-e2e-tests NEXT_TEST_JOB=1 NEXT_TEST_MODE=deploy TEST_TIMINGS_TOKEN=${{ secrets.TEST_TIMINGS_TOKEN }} xvfb-run node run-tests.js --type e2e >> /proc/1/fd/1" + name: Run test/e2e (deploy) + + - name: Upload test trace + if: always() + uses: actions/upload-artifact@v3 + with: + name: test-trace + if-no-files-found: ignore + retention-days: 2 + path: | + test/traces + + releaseStats: + name: Release Stats + runs-on: ubuntu-latest + needs: [publishRelease] + steps: + - name: Setup node + uses: actions/setup-node@v3 + with: + node-version: ${{ env.NODE_LTS_VERSION }} + check-latest: true + + - uses: actions/cache@v3 + timeout-minutes: 5 + id: restore-build + with: + path: ./* + key: ${{ github.sha }}-${{ github.run_number }} + + - uses: actions/download-artifact@v3 + with: + name: next-swc-binaries + path: packages/next-swc/native + + - run: ./scripts/release-stats.sh + - uses: ./.github/actions/next-stats-action + env: + PR_STATS_COMMENT_TOKEN: ${{ secrets.PR_STATS_COMMENT_TOKEN }} diff --git a/.github/workflows/build_and_test.yml b/.github/workflows/build_and_test.yml new file mode 100644 index 0000000000000..f223777170a16 --- /dev/null +++ b/.github/workflows/build_and_test.yml @@ -0,0 +1,169 @@ +name: build-and-test + +on: + push: + branches: ['canary'] + pull_request: + types: [opened, synchronize] + +env: + NAPI_CLI_VERSION: 2.14.7 + TURBO_VERSION: 1.9.6 + PNPM_VERSION: 7.24.3 + NODE_MAINTENANCE_VERSION: 16 + NODE_LTS_VERSION: 18 + TEST_CONCURRENCY: 6 + # disable backtrace for test snapshots + RUST_BACKTRACE: 0 + + TURBO_TEAM: 'vercel' + TURBO_REMOTE_ONLY: 'true' + NEXT_TELEMETRY_DISABLED: 1 + # we build a dev binary for use in CI so skip downloading + # canary next-swc binaries in the monorepo + NEXT_SKIP_NATIVE_POSTINSTALL: 1 + DATADOG_API_KEY: ${{ secrets.DATA_DOG_API_KEY }} + DD_ENV: 'ci' + TEST_TIMINGS_TOKEN: ${{ secrets.TEST_TIMINGS_TOKEN }} + NEXT_TEST_JOB: 1 + +jobs: + build: + name: build + uses: ./.github/workflows/build_reusable.yml + secrets: inherit + + lint: + name: lint + needs: ['build'] + + uses: ./.github/workflows/build_reusable.yml + with: + afterBuild: pnpm lint-no-typescript && pnpm check-examples + secrets: inherit + + check-types-precompiled: + name: types and precompiled + needs: ['build'] + + uses: ./.github/workflows/build_reusable.yml + with: + afterBuild: pnpm types-and-precompiled + skipForDocsOnly: 'yes' + secrets: inherit + + test-cargo-unit: + name: test cargo unit + needs: ['build'] + + uses: ./.github/workflows/build_reusable.yml + with: + skipForDocsOnly: 'yes' + skipInstallBuild: 'yes' + afterBuild: turbo run test-cargo-unit + secrets: inherit + + rust-check: + name: rust check + needs: ['build'] + + uses: ./.github/workflows/build_reusable.yml + with: + skipForDocsOnly: 'yes' + skipInstallBuild: 'yes' + afterBuild: turbo run rust-check + secrets: inherit + + test-turbopack-dev: + name: test turbopack dev + needs: ['build'] + uses: ./.github/workflows/build_reusable.yml + with: + skipForDocsOnly: 'yes' + afterBuild: RUST_BACKTRACE=0 NEXT_EXTERNAL_TESTS_FILTERS="$(pwd)/packages/next-swc/crates/next-dev-tests/tests-manifest.js" __INTERNAL_NEXT_DEV_TEST_TURBO_DEV=TRUE __INTERNAL_CUSTOM_TURBOPACK_BINDINGS="$(pwd)/packages/next-swc/native/next-swc.linux-x64-gnu.node" __INTERNAL_NEXT_DEV_TEST_TURBO_GLOB_MATCH="*" NEXT_E2E_TEST_TIMEOUT=240000 NEXT_TEST_MODE=dev node run-tests.js --type development --timings -c ${TEST_CONCURRENCY} + secrets: inherit + + test-next-swc-wasm: + name: test next-swc wasm + needs: ['build'] + + uses: ./.github/workflows/build_reusable.yml + with: + skipForDocsOnly: 'yes' + afterBuild: rustup target add wasm32-unknown-unknown && curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh && node ./scripts/normalize-version-bump.js && turbo run build-wasm -- --target nodejs --features tracing/release_max_level_info && git checkout . && mv packages/next-swc/crates/wasm/pkg packages/next-swc/crates/wasm/pkg-nodejs && node ./scripts/setup-wasm.mjs && NEXT_TEST_MODE=start TEST_WASM=true node run-tests.js test/integration/production/test/index.test.js test/e2e/streaming-ssr/index.test.ts + secrets: inherit + + test-dev: + name: test dev + needs: ['build'] + strategy: + fail-fast: false + matrix: + group: [1, 2, 3] + + uses: ./.github/workflows/build_reusable.yml + with: + skipForDocsOnly: 'yes' + afterBuild: NEXT_TEST_MODE=dev node run-tests.js --timings -g ${{ matrix.group }}/3 -c ${TEST_CONCURRENCY} --test-pattern '^(development|e2e|unit)/.*\.test\.(js|jsx|ts|tsx)$' + secrets: inherit + + test-prod: + name: test prod + needs: ['build'] + strategy: + fail-fast: false + matrix: + group: [1, 2, 3] + + uses: ./.github/workflows/build_reusable.yml + with: + skipForDocsOnly: 'yes' + afterBuild: NEXT_TEST_MODE=start node run-tests.js --timings -g ${{ matrix.group }}/3 -c ${TEST_CONCURRENCY} --test-pattern '^(production|e2e)/.*\.test\.(js|jsx|ts|tsx)$' + secrets: inherit + + test-integration: + name: test integration + needs: ['build'] + strategy: + fail-fast: false + matrix: + group: [1, 2, 3, 4, 5, 6] + + uses: ./.github/workflows/build_reusable.yml + with: + nodeVersion: 16 + skipForDocsOnly: 'yes' + afterBuild: node run-tests.js --timings -g ${{ matrix.group }}/6 -c ${TEST_CONCURRENCY} --test-pattern '^(integration)/.*\.test\.(js|jsx|ts|tsx)$' + secrets: inherit + + test-firefox-safari: + name: test firefox and safari + needs: ['build'] + + uses: ./.github/workflows/build_reusable.yml + with: + skipForDocsOnly: 'yes' + afterBuild: pnpm playwright install && BROWSER_NAME=firefox node run-tests.js test/integration/production/test/index.test.js && BROWSER_NAME=safari NEXT_TEST_MODE=start node run-tests.js -c 1 test/integration/production/test/index.test.js test/e2e/basepath.test.ts && BROWSER_NAME=safari DEVICE_NAME='iPhone XR' node run-tests.js -c 1 test/production/prerender-prefetch/index.test.ts + secrets: inherit + + tests-pass: + needs: + [ + 'build', + 'lint', + 'check-types-precompiled', + 'test-dev', + 'test-prod', + 'test-integration', + 'test-cargo-unit', + 'rust-check', + 'test-next-swc-wasm', + 'test-turbopack-dev', + ] + + if: always() + runs-on: [self-hosted, linux, x64] + name: thank you, next + steps: + - run: exit 1 + if: ${{ always() && contains(needs.*.result, 'failure') }} diff --git a/.github/workflows/build_reusable.yml b/.github/workflows/build_reusable.yml new file mode 100644 index 0000000000000..0742b98469b7e --- /dev/null +++ b/.github/workflows/build_reusable.yml @@ -0,0 +1,128 @@ +name: Build Reusable + +on: + workflow_call: + inputs: + afterBuild: + required: false + description: 'additional steps to run' + type: string + skipInstallBuild: + required: false + description: 'whether to skip pnpm install && pnpm build' + type: string + skipForDocsOnly: + required: false + description: 'skip for docs only changes' + type: string + nodeVersion: + required: false + description: 'version of Node.js to use' + type: string + needsNextest: + required: false + description: 'if nextest rust dep is needed' + type: string + uploadSwcArtifact: + required: false + description: 'if swc artifact needs uploading' + type: string + +env: + NAPI_CLI_VERSION: 2.14.7 + TURBO_VERSION: 1.9.6 + PNPM_VERSION: 7.24.3 + NODE_MAINTENANCE_VERSION: 16 + NODE_LTS_VERSION: 18 + TEST_CONCURRENCY: 6 + # disable backtrace for test snapshots + RUST_BACKTRACE: 0 + + TURBO_TEAM: 'vercel' + TURBO_REMOTE_ONLY: 'true' + NEXT_TELEMETRY_DISABLED: 1 + # we build a dev binary for use in CI so skip downloading + # canary next-swc binaries in the monorepo + NEXT_SKIP_NATIVE_POSTINSTALL: 1 + DATADOG_API_KEY: ${{ secrets.DATA_DOG_API_KEY }} + DD_ENV: 'ci' + TEST_TIMINGS_TOKEN: ${{ secrets.TEST_TIMINGS_TOKEN }} + NEXT_TEST_JOB: 1 + +jobs: + build: + timeout-minutes: 25 + runs-on: [self-hosted, linux, x64] + + steps: + - run: fnm install ${{ inputs.nodeVersion || env.NODE_LTS_VERSION }} + - run: fnm use ${{ inputs.nodeVersion || env.NODE_LTS_VERSION }} + - run: node -v + - run: pwd + + - uses: actions/checkout@v3 + with: + fetch-depth: 25 + + # local action -> needs to run after checkout + - name: Install Rust + uses: ./.github/actions/setup-rust + with: + components: rustfmt, clippy + + - name: Install nextest + if: ${{ inputs.needsNextest == 'yes' }} + uses: taiki-e/install-action@nextest + + - run: rustc --version + + - run: npm i -g yarn "pnpm@${PNPM_VERSION}" "turbo@${TURBO_VERSION}" "@napi-rs/cli@${NAPI_CLI_VERSION}" + + # clean up any previous artifacts to avoid hitting disk space limits + - run: git clean -xdf && rm -rf /tmp/next-repo-*; rm -rf /tmp/next-install-* /tmp/yarn-* /tmp/ncc-cache target && cargo clean + + - run: echo "DOCS_CHANGE<> $GITHUB_OUTPUT; echo "$(node scripts/run-for-change.js --not --type docs --exec echo 'nope')" >> $GITHUB_OUTPUT; echo 'EOF' >> $GITHUB_OUTPUT + name: check docs only change + id: docs-change + + # normalize versions before build-native for better cache hits + - run: node scripts/normalize-version-bump.js + name: normalize versions + + - run: turbo run build-native-release --summarize -- --target x86_64-unknown-linux-gnu + + - name: Upload next-swc artifact + if: ${{ inputs.uploadSwcArtifact == 'yes' }} + uses: actions/upload-artifact@v3 + with: + name: next-swc-binary + path: packages/next-swc/native/next-swc.linux-x64-gnu.node + + # undo normalize version changes for install/build + - run: git checkout . + if: ${{ inputs.skipInstallBuild != 'yes' }} + + - run: pnpm store path + + - run: pnpm install + if: ${{ inputs.skipInstallBuild != 'yes' }} + + - run: pnpm build + if: ${{ inputs.skipInstallBuild != 'yes' }} + + - run: pnpm playwright install-deps + if: ${{ inputs.skipInstallBuild != 'yes' }} + + - run: pnpm playwright install chromium + if: ${{ inputs.skipInstallBuild != 'yes' }} + + - run: turbo run get-test-timings -- --build ${{ github.sha }} + + - run: /bin/bash -c "${{ inputs.afterBuild }}" + if: ${{inputs.skipForDocsOnly != 'yes' || steps.docs-change.outputs.DOCS_CHANGE == 'nope'}} + + - name: Upload artifact + uses: actions/upload-artifact@v3 + with: + name: turbo run summary + path: .turbo/runs diff --git a/.github/workflows/build_test_deploy.yml b/.github/workflows/build_test_deploy.yml deleted file mode 100644 index 2d2ff00c7bdc1..0000000000000 --- a/.github/workflows/build_test_deploy.yml +++ /dev/null @@ -1,1635 +0,0 @@ -on: - push: - branches: ['canary', 'trunk-merge/*'] - pull_request: - types: [opened, synchronize] - -name: Build, test, and deploy - -env: - NAPI_CLI_VERSION: 2.14.7 - TURBO_VERSION: 1.6.3 - RUST_TOOLCHAIN: nightly-2023-03-09 - PNPM_VERSION: 7.24.3 - NODE_MAINTENANCE_VERSION: 16 - NODE_LTS_VERSION: 18 - -jobs: - check-examples: - name: Check examples - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - name: Install moreutils - run: sudo apt install moreutils - - name: Check examples - run: ./scripts/check-examples.sh - - build: - runs-on: ubuntu-latest - env: - NEXT_TELEMETRY_DISABLED: 1 - # we build a dev binary for use in CI so skip downloading - # canary next-swc binaries in the monorepo - NEXT_SKIP_NATIVE_POSTINSTALL: 1 - TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }} - outputs: - docsChange: ${{ steps.docs-change.outputs.DOCS_CHANGE }} - codemodChange: ${{ steps.codemod-change.outputs.CODEMOD_CHANGE }} - isRelease: ${{ steps.check-release.outputs.IS_RELEASE }} - swcChange: ${{ steps.swc-change.outputs.SWC_CHANGE }} - turboToken: ${{ steps.turbo-token.outputs.TURBO_TOKEN }} - weekNum: ${{ steps.get-week.outputs.WEEK }} - steps: - - name: Setup node - uses: actions/setup-node@v3 - if: ${{ steps.docs-change.outputs.docsChange == 'nope' }} - with: - node-version: ${{ env.NODE_LTS_VERSION }} - check-latest: true - - - uses: actions/checkout@v3 - with: - fetch-depth: 25 - - # https://github.com/actions/virtual-environments/issues/1187 - - name: tune linux network - run: sudo ethtool -K eth0 tx off rx off - - - name: Check non-docs only change - run: echo "DOCS_CHANGE<> $GITHUB_OUTPUT; echo "$(node scripts/run-for-change.js --not --type docs --exec echo 'nope')" >> $GITHUB_OUTPUT; echo 'EOF' >> $GITHUB_OUTPUT - id: docs-change - - - run: echo "${{steps.docs-change.outputs.DOCS_CHANGE}}" - - - name: Check codemod change - run: echo "CODEMOD_CHANGE<> $GITHUB_OUTPUT; echo "$(node scripts/run-for-change.js --type next-codemod --exec echo 'yup')" >> $GITHUB_OUTPUT; echo 'EOF' >> $GITHUB_OUTPUT - id: codemod-change - - - run: echo "${{steps.codemod-change.outputs.CODEMOD_CHANGE}}" - - - run: echo "SWC_CHANGE<> $GITHUB_OUTPUT; echo "$(node scripts/run-for-change.js --type next-swc --exec echo 'yup')" >> $GITHUB_OUTPUT; echo 'EOF' >> $GITHUB_OUTPUT - id: swc-change - - - run: echo "TURBO_TOKEN=$(echo ${TURBO_TOKEN:-empty})" >> $GITHUB_OUTPUT - id: turbo-token - - - run: echo "${{steps.swc-change.outputs.SWC_CHANGE}}" - - - run: npm i -g pnpm@${PNPM_VERSION} - - - id: get-store-path - run: echo STORE_PATH=$(pnpm store path) >> $GITHUB_OUTPUT - - - uses: actions/cache@v3 - timeout-minutes: 5 - id: cache-pnpm-store - with: - path: ${{ steps.get-store-path.outputs.STORE_PATH }} - key: pnpm-store-${{ hashFiles('pnpm-lock.yaml') }} - restore-keys: | - pnpm-store- - pnpm-store-${{ hashFiles('pnpm-lock.yaml') }} - - - run: pnpm install - - - run: TEST_TIMINGS_TOKEN=${{ secrets.TEST_TIMINGS_TOKEN }} node run-tests.js --timings --write-timings -g 1/1 - if: ${{ steps.docs-change.outputs.DOCS_CHANGE == 'nope' }} - - - run: pnpm run build - - - id: check-release - run: | - if [[ $(node ./scripts/check-is-release.js 2> /dev/null || :) = v* ]]; - then - echo "IS_RELEASE=true" >> $GITHUB_OUTPUT - else - echo "IS_RELEASE=false" >> $GITHUB_OUTPUT - fi - # We use week in the turbo cache key to keep the cache from infinitely growing - - id: get-week - run: echo "WEEK=$(date +%U)" >> $GITHUB_OUTPUT - - - uses: actions/cache@v3 - timeout-minutes: 5 - id: cache-build - with: - path: ./* - key: ${{ github.sha }}-${{ github.run_number }} - - lint: - runs-on: ubuntu-latest - needs: build - steps: - - name: Setup node - uses: actions/setup-node@v3 - with: - node-version: ${{ env.NODE_LTS_VERSION }} - check-latest: true - - - run: npm i -g pnpm@${PNPM_VERSION} - - - uses: actions/cache@v3 - timeout-minutes: 5 - id: restore-build - with: - path: ./* - key: ${{ github.sha }}-${{ github.run_number }} - - run: ./scripts/check-manifests.js - - - run: pnpm lint - if: ${{needs.build.outputs.docsChange == 'nope'}} - - - run: pnpm lint-no-typescript - if: ${{needs.build.outputs.docsChange != 'nope'}} - - rust-check: - runs-on: ubuntu-latest - needs: build - steps: - - uses: actions/cache@v3 - timeout-minutes: 5 - id: restore-build - if: ${{needs.build.outputs.docsChange == 'nope'}} - with: - path: ./* - key: ${{ github.sha }}-${{ github.run_number }} - - - run: echo "SWC_CHANGE<> $GITHUB_OUTPUT; echo "$(node scripts/run-for-change.js --type next-swc --exec echo 'yup')" >> $GITHUB_OUTPUT; echo 'EOF' >> $GITHUB_OUTPUT - id: swc-change - - - run: echo "${{ steps.swc-change.outputs.SWC_CHANGE }}" - - - name: Install - uses: actions-rs/toolchain@v1 - if: ${{ steps.swc-change.outputs.SWC_CHANGE == 'yup' }} - with: - profile: minimal - toolchain: ${{ env.RUST_TOOLCHAIN }} - components: rustfmt, clippy - - - name: Cache cargo registry - uses: actions/cache@v3 - timeout-minutes: 5 - if: ${{ steps.swc-change.outputs.SWC_CHANGE == 'yup' }} - with: - path: ~/.cargo/registry - key: stable-ubuntu-clippy-cargo-registry - - - name: Cache cargo index - uses: actions/cache@v3 - timeout-minutes: 5 - if: ${{ steps.swc-change.outputs.SWC_CHANGE == 'yup' }} - with: - path: ~/.cargo/git - key: stable-ubuntu-clippy-cargo-index - - - name: Check - if: ${{ steps.swc-change.outputs.SWC_CHANGE == 'yup' }} - run: | - cargo fmt -- --check - cargo clippy --all -- -D warnings -A deprecated - cargo check -p next-dev --no-default-features --features cli,custom_allocator,rustls-tls,__internal_nextjs_integration_test - working-directory: packages/next-swc - - checkPrecompiled: - name: Check Pre-compiled - runs-on: ubuntu-latest - needs: build - env: - NEXT_TELEMETRY_DISABLED: 1 - steps: - - name: Setup node - uses: actions/setup-node@v3 - if: ${{needs.build.outputs.docsChange == 'nope'}} - with: - node-version: ${{ env.NODE_LTS_VERSION }} - check-latest: true - - # https://github.com/actions/virtual-environments/issues/1187 - - name: tune linux network - if: ${{needs.build.outputs.docsChange == 'nope'}} - run: sudo ethtool -K eth0 tx off rx off - - - uses: actions/checkout@v3 - if: ${{needs.build.outputs.docsChange == 'nope'}} - - - run: mv .git .git-bak - if: ${{needs.build.outputs.docsChange == 'nope'}} - - - uses: actions/cache@v3 - timeout-minutes: 5 - if: ${{needs.build.outputs.docsChange == 'nope'}} - id: restore-build - with: - path: ./* - key: ${{ github.sha }}-${{ github.run_number }} - - - run: npm i -g pnpm@${PNPM_VERSION} - - - run: rm -rf .git && mv .git-bak .git - if: ${{needs.build.outputs.docsChange == 'nope'}} - - - run: ./scripts/check-pre-compiled.sh - if: ${{needs.build.outputs.docsChange == 'nope'}} - - - uses: EndBug/add-and-commit@v7 - if: ${{ failure() }} - with: - add: 'packages/next/compiled packages/next/bundles --force' - message: '⚙ Update compiled files' - - testUnit: - name: Test Unit - runs-on: ubuntu-latest - needs: [build, build-native-test] - timeout-minutes: 10 - env: - NEXT_TELEMETRY_DISABLED: 1 - steps: - - name: Setup node - uses: actions/setup-node@v3 - if: ${{needs.build.outputs.docsChange == 'nope'}} - with: - node-version: ${{ env.NODE_LTS_VERSION }} - check-latest: true - - - uses: actions/cache@v3 - timeout-minutes: 5 - if: ${{needs.build.outputs.docsChange == 'nope'}} - id: restore-build - with: - path: ./* - key: ${{ github.sha }}-${{ github.run_number }} - - - uses: actions/download-artifact@v3 - if: ${{needs.build.outputs.docsChange == 'nope'}} - with: - name: next-swc-test-binary - path: packages/next-swc/native - - - run: npm i -g pnpm@${PNPM_VERSION} - if: ${{needs.build.outputs.docsChange == 'nope'}} - - - run: node run-tests.js --type unit - if: ${{needs.build.outputs.docsChange == 'nope'}} - - # run LTS matrix always and maintenance tests during a release - testDevLTS: - name: Test Development Node.js LTS - runs-on: ubuntu-latest - needs: [build, build-native-test] - timeout-minutes: 35 - env: - NEXT_TELEMETRY_DISABLED: 1 - TEST_TIMINGS_TOKEN: ${{ secrets.TEST_TIMINGS_TOKEN }} - strategy: - fail-fast: false - matrix: - node: [18] - group: [1, 2, 3, 4, 5] - steps: - - run: echo "${{needs.build.outputs.docsChange}}" - - # https://github.com/actions/virtual-environments/issues/1187 - - name: tune linux network - run: sudo ethtool -K eth0 tx off rx off - - - uses: actions/cache@v3 - timeout-minutes: 5 - if: ${{needs.build.outputs.docsChange == 'nope'}} - id: restore-build - with: - path: ./* - key: ${{ github.sha }}-${{ github.run_number }} - - - uses: actions/download-artifact@v3 - if: ${{needs.build.outputs.docsChange == 'nope'}} - with: - name: next-swc-test-binary - path: packages/next-swc/native - - - run: docker run --rm -v $(pwd):/work mcr.microsoft.com/playwright:v1.28.1-jammy /bin/bash -c "cd /work && curl -s https://install-node.vercel.app/v${{ matrix.node }} | FORCE=1 bash && node -v && npm i -g pnpm@${PNPM_VERSION} > /dev/null && NEXT_TEST_JOB=1 NEXT_TEST_MODE=dev TEST_TIMINGS_TOKEN=${{ secrets.TEST_TIMINGS_TOKEN }} xvfb-run node run-tests.js --type development --timings -g ${{ matrix.group }}/5 >> /proc/1/fd/1" - name: Run test/development - if: ${{needs.build.outputs.docsChange == 'nope'}} - - - name: Upload test trace - if: always() - uses: actions/upload-artifact@v3 - with: - name: test-trace - if-no-files-found: ignore - retention-days: 2 - path: | - test/traces - - testDevMaintenance: - name: Test Development Node.js Maintenance - runs-on: ubuntu-latest - needs: [build, build-native-test] - timeout-minutes: 35 - env: - NEXT_TELEMETRY_DISABLED: 1 - TEST_TIMINGS_TOKEN: ${{ secrets.TEST_TIMINGS_TOKEN }} - strategy: - fail-fast: false - matrix: - node: [16] - group: [1, 2, 3, 4, 5] - # we can't use "matrix" context in job level "if" so we have - # to create a separate job which can skip - if: ${{ needs.build.outputs.isRelease == 'true' }} - steps: - - run: echo "${{needs.build.outputs.docsChange}}" - - # https://github.com/actions/virtual-environments/issues/1187 - - name: tune linux network - run: sudo ethtool -K eth0 tx off rx off - - - uses: actions/cache@v3 - timeout-minutes: 5 - if: ${{needs.build.outputs.docsChange == 'nope'}} - id: restore-build - with: - path: ./* - key: ${{ github.sha }}-${{ github.run_number }} - - - uses: actions/download-artifact@v3 - if: ${{needs.build.outputs.docsChange == 'nope'}} - with: - name: next-swc-test-binary - path: packages/next-swc/native - - - run: docker run --rm -v $(pwd):/work mcr.microsoft.com/playwright:v1.28.1-jammy /bin/bash -c "cd /work && curl -s https://install-node.vercel.app/v${{ matrix.node }} | FORCE=1 bash && node -v && npm i -g pnpm@${PNPM_VERSION} > /dev/null && NEXT_TEST_JOB=1 NEXT_TEST_MODE=dev TEST_TIMINGS_TOKEN=${{ secrets.TEST_TIMINGS_TOKEN }} xvfb-run node run-tests.js --type development --timings -g ${{ matrix.group }}/5 >> /proc/1/fd/1" - name: Run test/development - if: ${{needs.build.outputs.docsChange == 'nope'}} - - - name: Upload test trace - if: always() - uses: actions/upload-artifact@v3 - with: - name: test-trace - if-no-files-found: ignore - retention-days: 2 - path: | - test/traces - - testDevE2ELTS: - name: Test Development (E2E) Node.js LTS - runs-on: ubuntu-latest - needs: [build, build-native-test] - timeout-minutes: 35 - env: - NEXT_TELEMETRY_DISABLED: 1 - TEST_TIMINGS_TOKEN: ${{ secrets.TEST_TIMINGS_TOKEN }} - strategy: - fail-fast: false - matrix: - node: [18] - group: [1, 2, 3, 4, 5, 6, 7, 8] - steps: - - run: echo "${{needs.build.outputs.docsChange}}" - - # https://github.com/actions/virtual-environments/issues/1187 - - name: tune linux network - run: sudo ethtool -K eth0 tx off rx off - - - uses: actions/cache@v3 - timeout-minutes: 5 - if: ${{needs.build.outputs.docsChange == 'nope'}} - id: restore-build - with: - path: ./* - key: ${{ github.sha }}-${{ github.run_number }} - - - uses: actions/download-artifact@v3 - if: ${{needs.build.outputs.docsChange == 'nope'}} - with: - name: next-swc-test-binary - path: packages/next-swc/native - - - run: docker run --rm -v $(pwd):/work mcr.microsoft.com/playwright:v1.28.1-jammy /bin/bash -c "cd /work && curl -s https://install-node.vercel.app/v${{ matrix.node }} | FORCE=1 bash && node -v && npm i -g pnpm@${PNPM_VERSION} > /dev/null && NEXT_TEST_JOB=1 NEXT_TEST_MODE=dev TEST_TIMINGS_TOKEN=${{ secrets.TEST_TIMINGS_TOKEN }} xvfb-run node run-tests.js --type e2e --timings -g ${{ matrix.group }}/8 >> /proc/1/fd/1" - name: Run test/e2e (dev) - if: ${{needs.build.outputs.docsChange == 'nope'}} - - - name: Upload test trace - if: always() - uses: actions/upload-artifact@v3 - with: - name: test-trace - if-no-files-found: ignore - retention-days: 2 - path: | - test/traces - - testDevE2EMaintenance: - name: Test Development (E2E) Node.js Maintenance - runs-on: ubuntu-latest - needs: [build, build-native-test] - timeout-minutes: 35 - env: - NEXT_TELEMETRY_DISABLED: 1 - TEST_TIMINGS_TOKEN: ${{ secrets.TEST_TIMINGS_TOKEN }} - strategy: - fail-fast: false - matrix: - node: [16] - group: [1, 2, 3, 4, 5, 6, 7, 8] - if: ${{ needs.build.outputs.isRelease == 'true' }} - steps: - - run: echo "${{needs.build.outputs.docsChange}}" - - # https://github.com/actions/virtual-environments/issues/1187 - - name: tune linux network - run: sudo ethtool -K eth0 tx off rx off - - - uses: actions/cache@v3 - timeout-minutes: 5 - if: ${{needs.build.outputs.docsChange == 'nope'}} - id: restore-build - with: - path: ./* - key: ${{ github.sha }}-${{ github.run_number }} - - - uses: actions/download-artifact@v3 - if: ${{needs.build.outputs.docsChange == 'nope'}} - with: - name: next-swc-test-binary - path: packages/next-swc/native - - - run: docker run --rm -v $(pwd):/work mcr.microsoft.com/playwright:v1.28.1-jammy /bin/bash -c "cd /work && curl -s https://install-node.vercel.app/v${{ matrix.node }} | FORCE=1 bash && node -v && npm i -g pnpm@${PNPM_VERSION} > /dev/null && NEXT_TEST_JOB=1 NEXT_TEST_MODE=dev TEST_TIMINGS_TOKEN=${{ secrets.TEST_TIMINGS_TOKEN }} xvfb-run node run-tests.js --type e2e --timings -g ${{ matrix.group }}/8 >> /proc/1/fd/1" - name: Run test/e2e (dev) - if: ${{needs.build.outputs.docsChange == 'nope'}} - - - name: Upload test trace - if: always() - uses: actions/upload-artifact@v3 - with: - name: test-trace - if-no-files-found: ignore - retention-days: 2 - path: | - test/traces - - testProdLTS: - name: Test Production Node.js LTS - runs-on: ubuntu-latest - needs: [build, build-native-test] - timeout-minutes: 15 - env: - NEXT_TELEMETRY_DISABLED: 1 - TEST_TIMINGS_TOKEN: ${{ secrets.TEST_TIMINGS_TOKEN }} - strategy: - fail-fast: false - matrix: - node: [18] - group: [1, 2, 3, 4, 5] - steps: - - run: echo "${{needs.build.outputs.docsChange}}" - - # https://github.com/actions/virtual-environments/issues/1187 - - name: tune linux network - run: sudo ethtool -K eth0 tx off rx off - - - uses: actions/cache@v3 - timeout-minutes: 5 - if: ${{needs.build.outputs.docsChange == 'nope'}} - id: restore-build - with: - path: ./* - key: ${{ github.sha }}-${{ github.run_number }} - - - uses: actions/download-artifact@v3 - if: ${{needs.build.outputs.docsChange == 'nope'}} - with: - name: next-swc-test-binary - path: packages/next-swc/native - - - run: docker run --rm -v $(pwd):/work mcr.microsoft.com/playwright:v1.28.1-jammy /bin/bash -c "cd /work && curl -s https://install-node.vercel.app/v${{ matrix.node }} | FORCE=1 bash && node -v && npm i -g pnpm@${PNPM_VERSION} > /dev/null && NEXT_TEST_JOB=1 NEXT_TEST_MODE=start TEST_TIMINGS_TOKEN=${{ secrets.TEST_TIMINGS_TOKEN }} xvfb-run node run-tests.js --type production --timings -g ${{ matrix.group }}/5 >> /proc/1/fd/1" - name: Run test/production - if: ${{needs.build.outputs.docsChange == 'nope'}} - - testProdMaintenance: - name: Test Production Node.js Maintenance - runs-on: ubuntu-latest - needs: [build, build-native-test] - timeout-minutes: 15 - env: - NEXT_TELEMETRY_DISABLED: 1 - TEST_TIMINGS_TOKEN: ${{ secrets.TEST_TIMINGS_TOKEN }} - strategy: - fail-fast: false - matrix: - node: [16] - group: [1, 2, 3, 4, 5] - if: ${{ needs.build.outputs.isRelease == 'true' }} - steps: - - run: echo "${{needs.build.outputs.docsChange}}" - - # https://github.com/actions/virtual-environments/issues/1187 - - name: tune linux network - run: sudo ethtool -K eth0 tx off rx off - - - uses: actions/cache@v3 - timeout-minutes: 5 - if: ${{needs.build.outputs.docsChange == 'nope'}} - id: restore-build - with: - path: ./* - key: ${{ github.sha }}-${{ github.run_number }} - - - uses: actions/download-artifact@v3 - if: ${{needs.build.outputs.docsChange == 'nope'}} - with: - name: next-swc-test-binary - path: packages/next-swc/native - - - run: docker run --rm -v $(pwd):/work mcr.microsoft.com/playwright:v1.28.1-jammy /bin/bash -c "cd /work && curl -s https://install-node.vercel.app/v${{ matrix.node }} | FORCE=1 bash && node -v && npm i -g pnpm@${PNPM_VERSION} > /dev/null && NEXT_TEST_JOB=1 NEXT_TEST_MODE=start TEST_TIMINGS_TOKEN=${{ secrets.TEST_TIMINGS_TOKEN }} xvfb-run node run-tests.js --type production --timings -g ${{ matrix.group }}/5 >> /proc/1/fd/1" - name: Run test/production - if: ${{needs.build.outputs.docsChange == 'nope'}} - - testProdE2ELTS: - name: Test Production (E2E) Node.js LTS - runs-on: ubuntu-latest - needs: [build, build-native-test] - timeout-minutes: 35 - env: - NEXT_TELEMETRY_DISABLED: 1 - TEST_TIMINGS_TOKEN: ${{ secrets.TEST_TIMINGS_TOKEN }} - strategy: - fail-fast: false - matrix: - node: [18] - group: [1, 2, 3, 4, 5, 6, 7, 8] - steps: - - run: echo "${{needs.build.outputs.docsChange}}" - - # https://github.com/actions/virtual-environments/issues/1187 - - name: tune linux network - run: sudo ethtool -K eth0 tx off rx off - - - uses: actions/cache@v3 - timeout-minutes: 5 - if: ${{needs.build.outputs.docsChange == 'nope'}} - id: restore-build - with: - path: ./* - key: ${{ github.sha }}-${{ github.run_number }} - - - uses: actions/download-artifact@v3 - if: ${{needs.build.outputs.docsChange == 'nope'}} - with: - name: next-swc-test-binary - path: packages/next-swc/native - - - run: docker run --rm -v $(pwd):/work mcr.microsoft.com/playwright:v1.28.1-jammy /bin/bash -c "cd /work && curl -s https://install-node.vercel.app/v${{ matrix.node }} | FORCE=1 bash && node -v && npm i -g pnpm@${PNPM_VERSION} > /dev/null && NEXT_TEST_JOB=1 NEXT_TEST_MODE=start TEST_TIMINGS_TOKEN=${{ secrets.TEST_TIMINGS_TOKEN }} xvfb-run node run-tests.js --type e2e --timings -g ${{ matrix.group }}/8 >> /proc/1/fd/1" - name: Run test/e2e (production) - if: ${{needs.build.outputs.docsChange == 'nope'}} - - testProdE2EMaintenance: - name: Test Production (E2E) Node.js Maintenance - runs-on: ubuntu-latest - needs: [build, build-native-test] - timeout-minutes: 35 - env: - NEXT_TELEMETRY_DISABLED: 1 - TEST_TIMINGS_TOKEN: ${{ secrets.TEST_TIMINGS_TOKEN }} - strategy: - fail-fast: false - matrix: - node: [16] - group: [1, 2, 3, 4, 5, 6, 7, 8] - # run LTS matrix always and maintenance tests during a release - if: ${{ needs.build.outputs.isRelease == 'true' }} - steps: - - run: echo "${{needs.build.outputs.docsChange}}" - - # https://github.com/actions/virtual-environments/issues/1187 - - name: tune linux network - run: sudo ethtool -K eth0 tx off rx off - - - uses: actions/cache@v3 - timeout-minutes: 5 - if: ${{needs.build.outputs.docsChange == 'nope'}} - id: restore-build - with: - path: ./* - key: ${{ github.sha }}-${{ github.run_number }} - - - uses: actions/download-artifact@v3 - if: ${{needs.build.outputs.docsChange == 'nope'}} - with: - name: next-swc-test-binary - path: packages/next-swc/native - - - run: docker run --rm -v $(pwd):/work mcr.microsoft.com/playwright:v1.28.1-jammy /bin/bash -c "cd /work && curl -s https://install-node.vercel.app/v${{ matrix.node }} | FORCE=1 bash && node -v && npm i -g pnpm@${PNPM_VERSION} > /dev/null && NEXT_TEST_JOB=1 NEXT_TEST_MODE=start TEST_TIMINGS_TOKEN=${{ secrets.TEST_TIMINGS_TOKEN }} xvfb-run node run-tests.js --type e2e --timings -g ${{ matrix.group }}/8 >> /proc/1/fd/1" - name: Run test/e2e (production) - if: ${{needs.build.outputs.docsChange == 'nope'}} - - testCNA: - name: Test Create Next App - runs-on: ubuntu-latest - needs: [build, build-native-test] - timeout-minutes: 35 - env: - NEXT_TELEMETRY_DISABLED: 1 - TEST_TIMINGS_TOKEN: ${{ secrets.TEST_TIMINGS_TOKEN }} - steps: - - run: echo "${{needs.build.outputs.docsChange}}" - - # https://github.com/actions/virtual-environments/issues/1187 - - name: tune linux network - run: sudo ethtool -K eth0 tx off rx off - - - uses: actions/cache@v3 - timeout-minutes: 5 - if: ${{ needs.build.outputs.docsChange == 'nope' }} - id: restore-build - with: - path: ./* - key: ${{ github.sha }}-${{ github.run_number }} - - - uses: actions/download-artifact@v3 - if: ${{ needs.build.outputs.docsChange == 'nope' }} - with: - name: next-swc-test-binary - path: packages/next-swc/native - - - run: docker run --rm -v $(pwd):/work mcr.microsoft.com/playwright:v1.28.1-jammy /bin/bash -c "cd /work && NODE_VERSION=${{ env.NODE_LTS_VERSION }} ./scripts/setup-node.sh && npm i -g pnpm@${PNPM_VERSION} > /dev/null && NEXT_TEST_JOB=1 NEXT_TEST_CNA=1 xvfb-run node run-tests.js test/integration/create-next-app/index.test.ts test/integration/create-next-app/templates.test.ts >> /proc/1/fd/1" - if: ${{ needs.build.outputs.docsChange == 'nope' }} - - - name: Upload test trace - if: always() - uses: actions/upload-artifact@v3 - with: - name: test-trace - if-no-files-found: ignore - retention-days: 2 - path: | - test/traces - - testCodemods: - name: Test Codemods - runs-on: ubuntu-latest - needs: [build] - if: ${{ needs.build.outputs.codemodChange == 'yup' }} - timeout-minutes: 5 - env: - NEXT_TELEMETRY_DISABLED: 1 - TEST_TIMINGS_TOKEN: ${{ secrets.TEST_TIMINGS_TOKEN }} - - steps: - - run: echo "${{needs.build.outputs.codemodChange}}" - - # https://github.com/actions/virtual-environments/issues/1187 - - name: tune linux network - run: sudo ethtool -K eth0 tx off rx off - - - uses: actions/cache@v3 - timeout-minutes: 5 - id: restore-build - with: - path: ./* - key: ${{ github.sha }}-${{ github.run_number }} - - - name: Run tests - run: docker run --rm -v $(pwd):/work mcr.microsoft.com/playwright:v1.28.1-jammy /bin/bash -c "cd /work && NODE_VERSION=${{ env.NODE_LTS_VERSION }} ./scripts/setup-node.sh && npm i -g pnpm@${PNPM_VERSION} > /dev/null && cd ./packages/next-codemod && pnpm build && pnpm test >> /proc/1/fd/1" - - testIntegration: - name: Test Integration - runs-on: ubuntu-latest - needs: [build, build-native-test] - timeout-minutes: 35 - env: - NEXT_TELEMETRY_DISABLED: 1 - TEST_TIMINGS_TOKEN: ${{ secrets.TEST_TIMINGS_TOKEN }} - strategy: - fail-fast: false - matrix: - group: - [ - 1, - 2, - 3, - 4, - 5, - 6, - 7, - 8, - 9, - 10, - 11, - 12, - 13, - 14, - 15, - 16, - 17, - 18, - 19, - 20, - 21, - 22, - 23, - 24, - 25, - 26, - 27, - 28, - ] - steps: - - run: echo "${{needs.build.outputs.docsChange}}" - - # https://github.com/actions/virtual-environments/issues/1187 - - name: tune linux network - run: sudo ethtool -K eth0 tx off rx off - - - uses: actions/cache@v3 - timeout-minutes: 5 - if: ${{needs.build.outputs.docsChange == 'nope'}} - id: restore-build - with: - path: ./* - key: ${{ github.sha }}-${{ github.run_number }} - - - uses: actions/download-artifact@v3 - if: ${{needs.build.outputs.docsChange == 'nope'}} - with: - name: next-swc-test-binary - path: packages/next-swc/native - - - run: docker run --rm -v $(pwd):/work mcr.microsoft.com/playwright:v1.28.1-jammy /bin/bash -c "cd /work && NODE_VERSION=${{ env.NODE_LTS_VERSION }} ./scripts/setup-node.sh && npm i -g pnpm@${PNPM_VERSION} > /dev/null && NEXT_TEST_JOB=1 TEST_TIMINGS_TOKEN=${{ secrets.TEST_TIMINGS_TOKEN }} xvfb-run node run-tests.js --timings -g ${{ matrix.group }}/28 >> /proc/1/fd/1" - if: ${{needs.build.outputs.docsChange == 'nope'}} - - - name: Upload test trace - if: always() - uses: actions/upload-artifact@v3 - with: - name: test-trace - if-no-files-found: ignore - retention-days: 2 - path: | - test/traces - - testElectron: - name: Test Electron - runs-on: ubuntu-latest - needs: [build, build-native-test] - timeout-minutes: 10 - env: - NEXT_TELEMETRY_DISABLED: 1 - TEST_ELECTRON: 1 - steps: - - name: Setup node - uses: actions/setup-node@v3 - if: ${{needs.build.outputs.docsChange == 'nope'}} - with: - node-version: ${{ env.NODE_MAINTENANCE_VERSION }} - check-latest: true - - - uses: actions/cache@v3 - timeout-minutes: 5 - if: ${{needs.build.outputs.docsChange == 'nope'}} - id: restore-build - with: - path: ./* - key: ${{ github.sha }}-${{ github.run_number }} - - - uses: actions/download-artifact@v3 - if: ${{needs.build.outputs.docsChange == 'nope'}} - with: - name: next-swc-test-binary - path: packages/next-swc/native - - - run: npm i -g pnpm@${PNPM_VERSION} - if: ${{needs.build.outputs.docsChange == 'nope'}} - - - run: cd test/integration/with-electron/app && yarn install - if: ${{needs.build.outputs.docsChange == 'nope'}} - - - run: xvfb-run node run-tests.js test/integration/with-electron/test/index.test.js - if: ${{needs.build.outputs.docsChange == 'nope'}} - - # A job to run sets of tests with turbopack enabled. These tests are considered as `stable`, - # that running with turbopack should always pass. - testTurbopack: - name: Test Development (Turbopack) - runs-on: ubuntu-latest - needs: [build, build-native-test] - timeout-minutes: 35 - env: - NEXT_TELEMETRY_DISABLED: 1 - TEST_TIMINGS_TOKEN: ${{ secrets.TEST_TIMINGS_TOKEN }} - # Enabling backtrace will makes snapshot tests fail - RUST_BACKTRACE: 0 - # Path to the custom next-swc bindings located in **docker container** image. - NEXT_BINDINGS_BIN: /work/packages/next-swc/native/next-swc.linux-x64-gnu.node - # Glob pattern to run specific tests with --turbo. - NEXT_DEV_TEST_GLOB: '*' - # List of test files to run with turbopack as blocking CI check. - # [TODO]: as list grows we should consider different way to manage this. - TEST_FILES_LIST: | - test/development/acceptance-app/dynamic-error.test.ts \ - test/development/acceptance-app/unsupported-app-features.test.ts \ - test/development/acceptance-app/ReactRefresh.test.ts - strategy: - fail-fast: false - steps: - - run: echo "${{needs.build.outputs.docsChange}}" - - # https://github.com/actions/virtual-environments/issues/1187 - - name: tune linux network - run: sudo ethtool -K eth0 tx off rx off - - - uses: actions/cache@v3 - timeout-minutes: 5 - if: ${{needs.build.outputs.docsChange == 'nope'}} - id: restore-build - with: - path: ./* - key: ${{ github.sha }}-${{ github.run_number }} - - - uses: actions/download-artifact@v3 - if: ${{needs.build.outputs.docsChange == 'nope'}} - with: - name: next-swc-test-binary - path: packages/next-swc/native - - - run: docker run --rm -v $(pwd):/work mcr.microsoft.com/playwright:v1.28.1-jammy /bin/bash -c "cd /work && NODE_VERSION=${{ env.NODE_LTS_VERSION }} ./scripts/setup-node.sh && node -v && npm i -g pnpm@${PNPM_VERSION} > /dev/null && __INTERNAL_NEXT_DEV_TEST_TURBO_DEV=TRUE __INTERNAL_CUSTOM_TURBOPACK_BINDINGS=${NEXT_BINDINGS_BIN} __INTERNAL_NEXT_DEV_TEST_TURBO_GLOB_MATCH=${NEXT_DEV_TEST_GLOB} NEXT_E2E_TEST_TIMEOUT=240000 NEXT_TEST_JOB=1 NEXT_TEST_MODE=dev TEST_TIMINGS_TOKEN=${{ secrets.TEST_TIMINGS_TOKEN }} xvfb-run node run-tests.js --type development --timings -c 1 $TEST_FILES_LIST >> /proc/1/fd/1" - name: Run test/development - if: ${{needs.build.outputs.docsChange == 'nope'}} - - testsPass: - name: thank you, next - runs-on: ubuntu-latest - needs: - [ - lint, - check-examples, - test-native, - test-native-integration, - checkPrecompiled, - testIntegration, - testUnit, - testDevLTS, - testProdLTS, - testDevE2ELTS, - testProdE2ELTS, - ] - steps: - - run: exit 0 - - testFirefox: - name: Test Firefox (production) - runs-on: ubuntu-latest - needs: [build, build-native-test] - timeout-minutes: 10 - env: - NEXT_TELEMETRY_DISABLED: 1 - steps: - - uses: actions/cache@v3 - timeout-minutes: 5 - if: ${{needs.build.outputs.docsChange == 'nope'}} - id: restore-build - with: - path: ./* - key: ${{ github.sha }}-${{ github.run_number }} - - - uses: actions/download-artifact@v3 - if: ${{needs.build.outputs.docsChange == 'nope'}} - with: - name: next-swc-test-binary - path: packages/next-swc/native - - - run: docker run --rm -v $(pwd):/work mcr.microsoft.com/playwright:v1.28.1-jammy /bin/bash -c "cd /work && NODE_VERSION=${{ env.NODE_MAINTENANCE_VERSION }} ./scripts/setup-node.sh && npm i -g pnpm@${PNPM_VERSION} > /dev/null && BROWSERNAME=firefox NEXT_TEST_JOB=1 TEST_TIMINGS_TOKEN=${{ secrets.TEST_TIMINGS_TOKEN }} xvfb-run node run-tests.js test/integration/production/test/index.test.js >> /proc/1/fd/1" - if: ${{needs.build.outputs.docsChange == 'nope'}} - - testSafari: - name: Test Safari (production) - runs-on: ubuntu-latest - needs: [build, build-native-test] - timeout-minutes: 15 - env: - NEXT_TELEMETRY_DISABLED: 1 - steps: - # https://github.com/actions/virtual-environments/issues/1187 - - name: tune linux network - run: sudo ethtool -K eth0 tx off rx off - - - uses: actions/cache@v3 - timeout-minutes: 5 - if: ${{needs.build.outputs.docsChange == 'nope'}} - id: restore-build - with: - path: ./* - key: ${{ github.sha }}-${{ github.run_number }} - - - uses: actions/download-artifact@v3 - if: ${{needs.build.outputs.docsChange == 'nope'}} - with: - name: next-swc-test-binary - path: packages/next-swc/native - - - run: docker run --rm -v $(pwd):/work mcr.microsoft.com/playwright:v1.28.1-jammy /bin/bash -c "cd /work && curl -s https://install-node.vercel.app/v{{ env.NODE_LTS_VERSION }} | FORCE=1 bash && npm i -g pnpm@${PNPM_VERSION} > /dev/null && NEXT_TEST_JOB=1 NEXT_TEST_MODE=start BROWSER_NAME=safari node run-tests.js -c 1 test/integration/production/test/index.test.js test/e2e/basepath.test.ts && DEVICE_NAME='iPhone XR' node run-tests.js -c 1 test/production/prerender-prefetch/index.test.ts >> /proc/1/fd/1" - if: ${{needs.build.outputs.docsChange == 'nope'}} - - testFirefoxNodeLTS: - name: Test Firefox Node.js LTS - runs-on: ubuntu-latest - needs: [build, testFirefox, build-native-test] - timeout-minutes: 10 - env: - NEXT_TELEMETRY_DISABLED: 1 - steps: - - uses: actions/cache@v3 - timeout-minutes: 5 - if: ${{needs.build.outputs.docsChange == 'nope'}} - id: restore-build - with: - path: ./* - key: ${{ github.sha }}-${{ github.run_number }} - - - uses: actions/download-artifact@v3 - if: ${{needs.build.outputs.docsChange == 'nope'}} - with: - name: next-swc-test-binary - path: packages/next-swc/native - - - run: docker run --rm -v $(pwd):/work mcr.microsoft.com/playwright:v1.28.1-jammy /bin/bash -c "cd /work && NODE_VERSION=${{ env.NODE_LTS_VERSION }} ./scripts/setup-node.sh && node -v && npm i -g pnpm@${PNPM_VERSION} > /dev/null && BROWSER_NAME=firefox NEXT_TEST_JOB=1 TEST_TIMINGS_TOKEN=${{ secrets.TEST_TIMINGS_TOKEN }} xvfb-run node run-tests.js test/integration/production/test/index.test.js >> /proc/1/fd/1" - if: ${{needs.build.outputs.docsChange == 'nope'}} - - publishRelease: - if: ${{ needs.build.outputs.isRelease == 'true' }} - name: Potentially publish release - runs-on: ubuntu-latest - needs: - - build - - build-wasm - - build-native - env: - NPM_TOKEN: ${{ secrets.NPM_TOKEN_ELEVATED }} - steps: - - name: Setup node - uses: actions/setup-node@v3 - if: ${{needs.build.outputs.docsChange == 'nope'}} - with: - node-version: ${{ env.NODE_LTS_VERSION }} - check-latest: true - - # https://github.com/actions/virtual-environments/issues/1187 - - name: tune linux network - run: sudo ethtool -K eth0 tx off rx off - - - uses: actions/cache@v3 - timeout-minutes: 5 - id: restore-build - with: - path: ./* - key: ${{ github.sha }}-${{ github.run_number }} - - - uses: actions/download-artifact@v3 - with: - name: next-swc-binaries - path: packages/next-swc/native - - - uses: actions/download-artifact@v3 - with: - name: wasm-binaries - path: packages/next-swc/crates/wasm - - - run: npm i -g pnpm@${PNPM_VERSION} - - run: echo "//registry.npmjs.org/:_authToken=$NPM_TOKEN" >> ~/.npmrc - - run: ./scripts/publish-native.js - - run: ./scripts/publish-release.js - - deployExamples: - name: Deploy examples - runs-on: ubuntu-latest - needs: [build] - steps: - - uses: actions/checkout@v3 - with: - fetch-depth: 25 - - name: Install Vercel CLI - run: npm i -g vercel@28.16.15 - - name: Deploy preview examples - if: ${{ needs.build.outputs.isRelease != 'true' }} - run: ./scripts/deploy-examples.sh - env: - VERCEL_API_TOKEN: ${{ secrets.VERCEL_API_TOKEN }} - DEPLOY_ENVIRONMENT: preview - - name: Deploy production examples - if: ${{ needs.build.outputs.isRelease == 'true' }} - run: ./scripts/deploy-examples.sh - env: - VERCEL_API_TOKEN: ${{ secrets.VERCEL_API_TOKEN }} - DEPLOY_ENVIRONMENT: production - - testDeployE2E: - name: E2E (deploy) - runs-on: ubuntu-latest - needs: [publishRelease, build, build-native-test] - env: - NEXT_TELEMETRY_DISABLED: 1 - VERCEL_TEST_TOKEN: ${{ secrets.VERCEL_TEST_TOKEN }} - VERCEL_TEST_TEAM: vtest314-next-e2e-tests - steps: - - uses: actions/cache@v3 - timeout-minutes: 5 - id: restore-build - with: - path: ./* - key: ${{ github.sha }}-${{ github.run_number }} - - - uses: actions/download-artifact@v3 - if: ${{needs.build.outputs.docsChange == 'nope'}} - with: - name: next-swc-test-binary - path: packages/next-swc/native - - - run: npm i -g vercel@latest - - - run: RESET_VC_PROJECT=true node scripts/reset-vercel-project.mjs - name: Reset test project - - - run: docker run --rm -v $(pwd):/work mcr.microsoft.com/playwright:v1.28.1-jammy /bin/bash -c "cd /work && NODE_VERSION=${{ env.NODE_LTS_VERSION }} ./scripts/setup-node.sh && npm i -g pnpm@${PNPM_VERSION} > /dev/null && VERCEL_TEST_TOKEN=${{ secrets.VERCEL_TEST_TOKEN }} VERCEL_TEST_TEAM=vtest314-next-e2e-tests NEXT_TEST_JOB=1 NEXT_TEST_MODE=deploy TEST_TIMINGS_TOKEN=${{ secrets.TEST_TIMINGS_TOKEN }} xvfb-run node run-tests.js --type e2e >> /proc/1/fd/1" - name: Run test/e2e (deploy) - - - name: Upload test trace - if: always() - uses: actions/upload-artifact@v3 - with: - name: test-trace - if-no-files-found: ignore - retention-days: 2 - path: | - test/traces - - releaseStats: - name: Release Stats - runs-on: ubuntu-latest - needs: [publishRelease, build-native-test] - steps: - - name: Setup node - uses: actions/setup-node@v3 - with: - node-version: ${{ env.NODE_LTS_VERSION }} - check-latest: true - - - uses: actions/cache@v3 - timeout-minutes: 5 - id: restore-build - with: - path: ./* - key: ${{ github.sha }}-${{ github.run_number }} - - - uses: actions/download-artifact@v3 - with: - name: next-swc-test-binary - path: packages/next-swc/native - - - run: cp -r packages/next-swc/native .github/actions/next-stats-action/native - - - run: ./scripts/release-stats.sh - - uses: ./.github/actions/next-stats-action - env: - PR_STATS_COMMENT_TOKEN: ${{ secrets.PR_STATS_COMMENT_TOKEN }} - - build-native-test: - name: Build native binary for tests and metrics - runs-on: ubuntu-latest - env: - TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }} - TURBO_TEAM: 'vercel' - - steps: - # https://github.com/actions/virtual-environments/issues/1187 - - name: tune linux network - run: sudo ethtool -K eth0 tx off rx off - - - uses: actions/checkout@v3 - with: - fetch-depth: 25 - - - run: echo "DOCS_CHANGE<> $GITHUB_OUTPUT; echo "$(node scripts/run-for-change.js --not --type docs --exec echo 'nope')" >> $GITHUB_OUTPUT; echo 'EOF' >> $GITHUB_OUTPUT - id: docs-change - - - name: Cache cargo registry - uses: actions/cache@v3 - timeout-minutes: 5 - if: ${{ steps.docs-change.outputs.DOCS_CHANGE == 'nope' }} - with: - path: ~/.cargo/registry - key: stable-ubuntu-latest-cargo-registry - - - name: Cache cargo index - uses: actions/cache@v3 - timeout-minutes: 5 - if: ${{ steps.docs-change.outputs.DOCS_CHANGE == 'nope' }} - with: - path: ~/.cargo/git - key: stable-ubuntu-latest-cargo-index - - # We use week in the turbo cache key to keep the cache from infinitely growing - - id: get-week - run: echo "WEEK=$(date +%U)" >> $GITHUB_OUTPUT - - - name: Turbo Cache - id: turbo-cache - uses: actions/cache@v3 - timeout-minutes: 5 - if: ${{ steps.docs-change.outputs.DOCS_CHANGE == 'nope' }} - with: - path: .turbo - key: turbo-${{ github.job }}-${{ github.ref_name }}-${{ steps.get-week.outputs.WEEK }}-${{ github.sha }} - restore-keys: | - turbo-${{ github.job }}- - turbo-${{ github.job }}-${{ github.ref_name }}-${{ steps.get-week.outputs.WEEK }}- - turbo-${{ github.job }}-canary-${{ steps.get-week.outputs.WEEK }}- - - - name: normalize versions - if: ${{ steps.docs-change.outputs.DOCS_CHANGE == 'nope' }} - run: node scripts/normalize-version-bump.js - - - name: Build in docker - uses: addnab/docker-run-action@v3 - if: ${{ steps.docs-change.outputs.DOCS_CHANGE == 'nope' }} - with: - image: ghcr.io/napi-rs/napi-rs/nodejs-rust:stable-2022-10-24-x64 - options: -e TURBO_TOKEN=${{ env.TURBO_TOKEN }} -e TURBO_TEAM=${{ env.TURBO_TEAM }} -e RUST_TOOLCHAIN=${{ env.RUST_TOOLCHAIN }} -e NAPI_CLI_VERSION=${{ env.NAPI_CLI_VERSION }} -e TURBO_VERSION=${{ env.TURBO_VERSION }} -v ${{ env.HOME }}/.cargo/git:/root/.cargo/git -v ${{ env.HOME }}/.cargo/registry:/root/.cargo/registry -v ${{ github.workspace }}:/build -w /build - # turn on some optimization while building Rust codes to prevent tests timeout - run: | - set -e && - export CARGO_PROFILE_DEV_OPT_LEVEL=1 && - 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" -- --target x86_64-unknown-linux-gnu && - strip packages/next-swc/native/next-swc.*.node - - - name: Upload artifact - uses: actions/upload-artifact@v3 - if: ${{ steps.docs-change.outputs.DOCS_CHANGE == 'nope' }} - with: - name: next-swc-test-binary - path: packages/next-swc/native/next-swc.linux-x64-gnu.node - - - name: Clear the cargo caches - if: ${{ steps.docs-change.outputs.DOCS_CHANGE == 'nope' }} - run: | - cargo install cargo-cache --no-default-features --features ci-autoclean - cargo-cache - - test-native: - name: Unit Test Native Code - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v3 - with: - fetch-depth: 25 - - - run: echo "SWC_CHANGE<> $GITHUB_OUTPUT; echo "$(node scripts/run-for-change.js --type next-swc --exec echo 'yup')" >> $GITHUB_OUTPUT; echo 'EOF' >> $GITHUB_OUTPUT - id: swc-change - - - run: echo "${{ steps.swc-change.outputs.SWC_CHANGE }}" - - - name: Install - if: ${{ steps.swc-change.outputs.SWC_CHANGE == 'yup' }} - uses: actions-rs/toolchain@v1 - with: - toolchain: ${{ env.RUST_TOOLCHAIN }} - profile: minimal - - - run: cd packages/next-swc && cargo test --workspace --exclude next-dev-tests - if: ${{ steps.swc-change.outputs.SWC_CHANGE == 'yup' }} - - test-native-integration: - name: Integration Test Native Code - runs-on: ubuntu-latest-16-core-oss - needs: build - - env: - CARGO_PROFILE_RELEASE_LTO: false - - steps: - - uses: actions/cache@v3 - timeout-minutes: 5 - id: restore-build - with: - path: ./* - key: ${{ github.sha }}-${{ github.run_number }} - - - run: echo "${{needs.build.outputs.docsChange == 'nope'}}" - - - name: Install - if: ${{needs.build.outputs.docsChange == 'nope'}} - uses: actions-rs/toolchain@v1 - with: - toolchain: ${{ env.RUST_TOOLCHAIN }} - profile: minimal - - - name: Install nextest - uses: taiki-e/install-action@nextest - - - name: Build tests - timeout-minutes: 60 - run: cd packages/next-swc && cargo nextest run -p next-dev-tests --release --no-run - if: ${{needs.build.outputs.docsChange == 'nope'}} - - - name: Run tests - timeout-minutes: 20 - run: cd packages/next-swc && cargo nextest run -p next-dev-tests --release --no-fail-fast - if: ${{needs.build.outputs.docsChange == 'nope'}} - - test-wasm: - name: Test the wasm build - runs-on: ubuntu-latest - timeout-minutes: 15 - needs: [build, build-native-test, build-wasm-dev] - - steps: - - uses: actions/cache@v3 - timeout-minutes: 5 - if: ${{needs.build.outputs.docsChange == 'nope'}} - id: restore-build - with: - path: ./* - key: ${{ github.sha }} - - - uses: actions/download-artifact@v3 - if: ${{needs.build.outputs.docsChange == 'nope'}} - with: - name: wasm-dev-binary - path: packages/next-swc/crates/wasm/pkg-nodejs - - - run: ls packages/next-swc/crates/wasm - if: ${{needs.build.outputs.docsChange == 'nope'}} - - - uses: actions/download-artifact@v3 - if: ${{needs.build.outputs.docsChange == 'nope'}} - with: - name: next-swc-test-binary - path: packages/next-swc/native - - - run: docker run --rm -v $(pwd):/work mcr.microsoft.com/playwright:v1.28.1-jammy /bin/bash -c "cd /work && NODE_VERSION=${{ env.NODE_LTS_VERSION }} ./scripts/setup-node.sh && node -v && node ./scripts/setup-wasm.mjs && npm i -g pnpm@${PNPM_VERSION} > /dev/null && TEST_WASM=true xvfb-run node run-tests.js test/integration/production/test/index.test.js test/e2e/streaming-ssr/index.test.ts >> /proc/1/fd/1" - if: ${{needs.build.outputs.docsChange == 'nope'}} - - # Build binaries for publishing - build-native: - strategy: - fail-fast: false - matrix: - settings: - # pnpm is aliased here temporarily until the build docker - # image is updated past Node.js v14.19 (current 14.18.1) - - host: macos-latest - target: 'x86_64-apple-darwin' - build: | - 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 - turbo run build-native -- --release --target x86_64-apple-darwin - strip -x packages/next-swc/native/next-swc.*.node - - host: windows-latest - build: | - npm i -g "@napi-rs/cli@${NAPI_CLI_VERSION}" "turbo@${TURBO_VERSION}" "pnpm@${PNPM_VERSION}" - turbo run build-native -- --release --target x86_64-pc-windows-msvc - target: 'x86_64-pc-windows-msvc' - - host: windows-latest - build: | - npm i -g "@napi-rs/cli@${NAPI_CLI_VERSION}" "turbo@${TURBO_VERSION}" "pnpm@${PNPM_VERSION}" - turbo run build-native-no-plugin -- --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:stable-2022-10-24-x64 - build: >- - 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 -- --release --target x86_64-unknown-linux-gnu && - strip 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:stable-2022-10-24-alpine - build: >- - set -e && - apk add --no-cache libc6-compat && - rustup toolchain install "${RUST_TOOLCHAIN}" && - rustup default "${RUST_TOOLCHAIN}" && - rustup target add x86_64-unknown-linux-musl && - 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 && - turbo run build-native -- --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@${NAPI_CLI_VERSION}" "turbo@${TURBO_VERSION}" && if [ ! -f $(dirname $(which yarn))/pnpm ]; then ln -s $(which yarn) $(dirname $(which yarn))/pnpm;fi - turbo run build-native -- --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:stable-2022-10-24-aarch64 - build: >- - set -e && - export JEMALLOC_SYS_WITH_LG_PAGE=16 && - rustup toolchain install "${RUST_TOOLCHAIN}" && - rustup default "${RUST_TOOLCHAIN}" && - rustup target add aarch64-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 && - export CC_aarch64_unknown_linux_gnu=/usr/aarch64-unknown-linux-gnu/bin/aarch64-unknown-linux-gnu-gcc && - turbo run build-native -- --release --target aarch64-unknown-linux-gnu && - llvm-strip -x packages/next-swc/native/next-swc.*.node - - host: ubuntu-latest - target: 'aarch64-unknown-linux-musl' - docker: ghcr.io/napi-rs/napi-rs/nodejs-rust:stable-2022-10-24-alpine - build: >- - set -e && - apk add --no-cache libc6-compat && - export JEMALLOC_SYS_WITH_LG_PAGE=16 && - 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 && - rustup toolchain install "${RUST_TOOLCHAIN}" && - rustup default "${RUST_TOOLCHAIN}" && - rustup target add aarch64-unknown-linux-musl && - turbo run build-native -- --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@${NAPI_CLI_VERSION}" "turbo@${TURBO_VERSION}" "pnpm@${PNPM_VERSION}" - turbo run build-native-no-plugin-woa -- --release --target aarch64-pc-windows-msvc --cargo-flags=--no-default-features - if: ${{ needs.build.outputs.isRelease == 'true' || (needs.build.outputs.turboToken != 'empty') }} - needs: build - name: stable - ${{ matrix.settings.target }} - node@16 - runs-on: ${{ matrix.settings.host }} - env: - TURBO_TEAM: 'vercel' - TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }} - TURBO_REMOTE_ONLY: 'true' - steps: - # https://github.com/actions/virtual-environments/issues/1187 - - name: tune linux network - run: sudo ethtool -K eth0 tx off rx off - if: ${{ matrix.settings.host == 'ubuntu-latest' }} - - 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.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.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@v3 - - - name: Setup node - uses: actions/setup-node@v3 - if: ${{ !matrix.settings.docker }} - with: - node-version: ${{ env.NODE_LTS_VERSION }} - check-latest: true - - - name: Install - uses: actions-rs/toolchain@v1 - if: ${{ !matrix.settings.docker }} - with: - profile: minimal - override: true - toolchain: ${{ env.RUST_TOOLCHAIN }} - target: ${{ matrix.settings.target }} - - - name: Cache cargo registry - uses: actions/cache@v3 - timeout-minutes: 5 - with: - path: ~/.cargo/registry - key: ${{ matrix.settings.target }}-cargo-registry - - - name: Cache cargo index - uses: actions/cache@v3 - timeout-minutes: 5 - with: - path: ~/.cargo/git - key: ${{ matrix.settings.target }}-cargo-index - - - name: normalize versions - run: node scripts/normalize-version-bump.js - - - name: Setup toolchain - run: ${{ matrix.settings.setup }} - if: ${{ matrix.settings.setup }} - shell: bash - - - name: Build in docker - uses: addnab/docker-run-action@v3 - if: ${{ matrix.settings.docker }} - with: - image: ${{ matrix.settings.docker }} - 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: ${{ matrix.settings.build }} - - - name: 'Build' - run: ${{ matrix.settings.build }} - if: ${{ !matrix.settings.docker }} - shell: bash - - - name: Upload artifact - if: ${{ needs.build.outputs.isRelease == 'true' }} - uses: actions/upload-artifact@v3 - with: - name: next-swc-binaries - path: packages/next-swc/native/next-swc.*.node - - build-wasm: - needs: build - if: ${{ needs.build.outputs.isRelease == 'true' || (needs.build.outputs.turboToken != 'empty') }} - strategy: - matrix: - target: [web, nodejs] - runs-on: macos-latest - env: - TURBO_TEAM: 'vercel' - TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }} - TURBO_REMOTE_ONLY: 'true' - steps: - - uses: actions/checkout@v3 - - - name: Setup node - uses: actions/setup-node@v3 - with: - node-version: ${{ env.NODE_LTS_VERSION }} - check-latest: true - - - name: Install Rust - uses: actions-rs/toolchain@v1 - with: - profile: minimal - toolchain: ${{ env.RUST_TOOLCHAIN }} - override: true - target: wasm32-unknown-unknown - - - run: npm i -g turbo@${{ env.TURBO_VERSION }} pnpm@${PNPM_VERSION} - - - name: Install wasm-pack - run: curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh - - - name: normalize versions - run: node scripts/normalize-version-bump.js - - - name: Build - run: turbo run build-wasm -- --target ${{ matrix.target }} - - - name: Add target to folder name - run: '[[ -d "packages/next-swc/crates/wasm/pkg" ]] && mv packages/next-swc/crates/wasm/pkg packages/next-swc/crates/wasm/pkg-${{ matrix.target }} || ls packages/next-swc/crates/wasm' - - - name: Upload artifact - if: ${{ needs.build.outputs.isRelease == 'true' }} - uses: actions/upload-artifact@v3 - with: - name: wasm-binaries - path: packages/next-swc/crates/wasm/pkg-* - - build-wasm-dev: - needs: build - runs-on: ubuntu-latest - steps: - - uses: actions/cache@v3 - timeout-minutes: 5 - if: ${{needs.build.outputs.docsChange == 'nope'}} - id: restore-build - with: - path: ./* - key: ${{ github.sha }}-${{ github.run_number }} - - - name: Setup node - if: ${{needs.build.outputs.docsChange == 'nope'}} - uses: actions/setup-node@v3 - with: - node-version: ${{ env.NODE_LTS_VERSION }} - check-latest: true - - - run: npm i -g turbo@${{ env.TURBO_VERSION }} pnpm@${PNPM_VERSION} - - - name: Install Rust - if: ${{needs.build.outputs.docsChange == 'nope'}} - uses: actions-rs/toolchain@v1 - with: - profile: minimal - toolchain: ${{ env.RUST_TOOLCHAIN }} - override: true - target: wasm32-unknown-unknown - - - name: Turbo Cache - id: turbo-cache - uses: actions/cache@v3 - timeout-minutes: 5 - if: ${{needs.build.outputs.docsChange == 'nope'}} - with: - path: .turbo - key: turbo-${{ github.job }}-${{ github.ref_name }}-${{ steps.get-week.outputs.WEEK }}-${{ github.sha }} - restore-keys: | - turbo-${{ github.job }}- - turbo-${{ github.job }}-${{ github.ref_name }}-${{ steps.get-week.outputs.WEEK }}- - turbo-${{ github.job }}-canary-${{ steps.get-week.outputs.WEEK }}- - - - name: Install wasm-pack - if: ${{needs.build.outputs.docsChange == 'nope'}} - run: curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh - - - name: normalize versions - if: ${{needs.build.outputs.docsChange == 'nope'}} - run: node scripts/normalize-version-bump.js - - - name: Build - if: ${{needs.build.outputs.docsChange == 'nope'}} - run: turbo run build-wasm --cache-dir=".turbo" -- --target nodejs --dev - - - name: Add target to folder name - if: ${{needs.build.outputs.docsChange == 'nope'}} - run: '[[ -d "packages/next-swc/crates/wasm/pkg" ]] && mv packages/next-swc/crates/wasm/pkg packages/next-swc/crates/wasm/pkg-nodejs || ls packages/next-swc/crates/wasm' - - - name: Upload artifact - if: ${{needs.build.outputs.docsChange == 'nope'}} - uses: actions/upload-artifact@v3 - with: - name: wasm-dev-binary - path: packages/next-swc/crates/wasm/pkg-nodejs - - check-trace-secrests: - runs-on: ubuntu-latest - outputs: - trace-api-key: ${{ steps.trace-api-key.outputs.defined }} - steps: - - id: trace-api-key - env: - TRACE_API_KEY: ${{ secrets.DATA_DOG_API_KEY }} - if: "${{ env.TRACE_API_KEY != '' }}" - run: echo "defined=true" >> $GITHUB_OUTPUT - - build-performance-metrics: - name: Performance Metrics for Release Build - runs-on: ubuntu-latest - needs: [build, build-native-test, check-trace-secrests] - if: needs.check-trace-secrests.outputs.trace-api-key == 'true' - env: - NEXT_TELEMETRY_DISABLED: 1 - steps: - - name: Setup node - uses: actions/setup-node@v3 - if: ${{needs.build.outputs.docsChange == 'nope'}} - with: - node-version: ${{ env.NODE_LTS_VERSION }} - check-latest: true - - - uses: actions/cache@v3 - timeout-minutes: 5 - id: restore-build - if: ${{needs.build.outputs.docsChange == 'nope'}} - with: - path: ./* - key: ${{ github.sha }}-${{ github.run_number }} - - - name: Set Git Short sha Env - if: ${{needs.build.outputs.docsChange == 'nope'}} - run: echo "GIT_SHORT_SHA=`echo ${GITHUB_SHA} | cut -c1-8`" >> $GITHUB_ENV - - - name: Check Git Short sha Env - if: ${{needs.build.outputs.docsChange == 'nope'}} - run: echo ${GIT_SHORT_SHA} - - - uses: actions/download-artifact@v3 - if: ${{needs.build.outputs.docsChange == 'nope'}} - with: - name: next-swc-test-binary - path: packages/next-swc/native - - - name: Generate metrics data - if: ${{needs.build.outputs.docsChange == 'nope'}} - run: | - yarn --cwd bench/nested-deps install - node bench/nested-deps/bench.mjs build - - uses: datadog/agent-github-action@v1 - if: ${{needs.build.outputs.docsChange == 'nope'}} - with: - api_key: ${{ secrets.DATA_DOG_API_KEY }} - - name: Sending metrics data to Datadog - if: ${{needs.build.outputs.docsChange == 'nope'}} - run: | - node scripts/trace-dd.mjs bench/nested-deps/.next/trace build ${GIT_SHORT_SHA} ./bench/nested-deps/next.config.js - env: - DATA_DOG_API_KEY: ${{ secrets.DATA_DOG_API_KEY }} - DD_TRACE_PARTIAL_FLUSH_MIN_SPANS: 10 - DD_ENV: canary - DD_SERVICE: nextjs-dev-build - DD_TRACE_DEBUG: true diff --git a/.github/workflows/cancel.yml b/.github/workflows/cancel.yml index 093016a7beaf7..8149dfac820e4 100644 --- a/.github/workflows/cancel.yml +++ b/.github/workflows/cancel.yml @@ -13,5 +13,5 @@ jobs: steps: - uses: styfle/cancel-workflow-action@0.9.1 with: - workflow_id: 444921, 444987 + workflow_id: 444921, 444987, 57419851 access_token: ${{ github.token }} diff --git a/.github/workflows/pull_request_stats.yml b/.github/workflows/pull_request_stats.yml index e31cb5fd4b73e..0b061b8291029 100644 --- a/.github/workflows/pull_request_stats.yml +++ b/.github/workflows/pull_request_stats.yml @@ -6,103 +6,32 @@ name: Generate Pull Request Stats env: NAPI_CLI_VERSION: 2.14.7 - TURBO_VERSION: 1.6.3 - RUST_TOOLCHAIN: nightly-2023-03-09 + TURBO_VERSION: 1.9.6 PNPM_VERSION: 7.24.3 + NODE_MAINTENANCE_VERSION: 16 + NODE_LTS_VERSION: 18 + TEST_CONCURRENCY: 6 + + TURBO_TEAM: 'vercel' + TURBO_REMOTE_ONLY: 'true' + NEXT_TELEMETRY_DISABLED: 1 + # we build a dev binary for use in CI so skip downloading + # canary next-swc binaries in the monorepo + NEXT_SKIP_NATIVE_POSTINSTALL: 1 + TEST_TIMINGS_TOKEN: ${{ secrets.TEST_TIMINGS_TOKEN }} + NEXT_TEST_JOB: 1 + NEXT_DISABLE_SWC_WASM: 1 jobs: - build-native-dev: - name: Build dev binary for tests - runs-on: ubuntu-latest - steps: - # https://github.com/actions/virtual-environments/issues/1187 - - name: tune linux network - run: sudo ethtool -K eth0 tx off rx off - - - uses: actions/checkout@v3 - with: - fetch-depth: 25 - - - name: Check non-docs only change - run: echo "DOCS_CHANGE<> $GITHUB_OUTPUT; echo "$(node scripts/run-for-change.js --not --type docs --exec echo 'nope')" >> $GITHUB_OUTPUT; echo 'EOF' >> $GITHUB_OUTPUT - id: docs-change - - - name: Cache cargo registry - uses: actions/cache@v3 - timeout-minutes: 5 - if: ${{ steps.docs-change.outputs.DOCS_CHANGE == 'nope' }} - with: - path: ~/.cargo/registry - key: stable-ubuntu-latest-node@14-cargo-registry-trimmed-${{ hashFiles('**/Cargo.lock') }} - - - name: Cache cargo index - uses: actions/cache@v3 - timeout-minutes: 5 - if: ${{ steps.docs-change.outputs.DOCS_CHANGE == 'nope' }} - with: - path: ~/.cargo/git - key: stable-ubuntu-latest-node@14-cargo-index-trimmed-${{ hashFiles('**/Cargo.lock') }} - - # We use week in the turbo cache key to keep the cache from infinitely growing - - id: get-week - run: echo "WEEK=$(date +%U)" >> $GITHUB_OUTPUT - - - name: Turbo Cache - id: turbo-cache - uses: actions/cache@v3 - timeout-minutes: 5 - if: ${{ steps.docs-change.outputs.DOCS_CHANGE == 'nope' }} - with: - path: .turbo - key: turbo-${{ github.job }}-${{ github.ref_name }}-${{ steps.get-week.outputs.WEEK }}-${{ github.sha }} - restore-keys: | - turbo-${{ github.job }}-${{ github.ref_name }}-${{ steps.get-week.outputs.WEEK }}- - turbo-${{ github.job }}-canary-${{ steps.get-week.outputs.WEEK }}- - - # 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." - # So we get latest cache - # - name: Cache built files - # uses: actions/cache@v3 - # timeout-minutes: 5 - # with: - # path: ./packages/next-target - # key: next-swc-cargo-cache-ubuntu-latest--${{ hashFiles('**/Cargo.lock') }} - # restore-keys: | - # next-swc-cargo-cache-ubuntu-latest - - - name: Build in docker - uses: addnab/docker-run-action@v3 - if: ${{ steps.docs-change.outputs.DOCS_CHANGE == 'nope' }} - 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 }} -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" -- --target x86_64-unknown-linux-gnu && - strip packages/next-swc/native/next-swc.*.node - - - name: Upload artifact - uses: actions/upload-artifact@v3 - with: - name: next-swc-dev-binary - path: packages/next-swc/native/next-swc.linux-x64-gnu.node - - - name: Clear the cargo caches - if: ${{ steps.docs-change.outputs.DOCS_CHANGE == 'nope' }} - run: | - cargo install cargo-cache --no-default-features --features ci-autoclean - cargo-cache + build: + uses: ./.github/workflows/build_reusable.yml + secrets: inherit + with: + uploadSwcArtifact: 'yes' stats: name: PR Stats - needs: build-native-dev + needs: build runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 @@ -116,7 +45,7 @@ jobs: - uses: actions/download-artifact@v3 if: ${{ steps.docs-change.outputs.DOCS_CHANGE == 'nope' }} with: - name: next-swc-dev-binary + name: next-swc-binary path: packages/next-swc/native - run: cp -r packages/next-swc/native .github/actions/next-stats-action/native diff --git a/.github/workflows/trigger_release.yml b/.github/workflows/trigger_release.yml index 06fe22d6ddd0f..1c36715db4ccd 100644 --- a/.github/workflows/trigger_release.yml +++ b/.github/workflows/trigger_release.yml @@ -2,24 +2,30 @@ on: workflow_dispatch: inputs: releaseType: - description: stable or canary (case sensitive)? + description: stable or canary? required: true - type: string + type: choice + options: + - canary + - stable semverType: - description: patch, minor, or major (case sensitive)? - type: string + description: semver type? + type: choice + options: + - patch + - minor + - major secrets: - START_RELEASE_TOKEN: + RELEASE_BOT_GITHUB_TOKEN: required: true name: Trigger Release env: NAPI_CLI_VERSION: 2.14.7 - TURBO_VERSION: 1.6.3 - RUST_TOOLCHAIN: nightly-2023-03-09 + TURBO_VERSION: 1.9.6 PNPM_VERSION: 7.24.3 NODE_MAINTENANCE_VERSION: 16 NODE_LTS_VERSION: 18 @@ -42,7 +48,9 @@ jobs: node-version: 18 check-latest: true - - run: git clone https://ijjk:${{ secrets.START_RELEASE_TOKEN }}@github.com/vercel/next.js.git --depth=25 . + - run: git clone https://github.com/vercel/next.js.git --depth=25 . + + - run: git describe || 'echo failed to get tag' # https://github.com/actions/virtual-environments/issues/1187 - name: tune linux network @@ -69,4 +77,4 @@ jobs: - run: node ./scripts/start-release.js --release-type ${{ github.event.inputs.releaseType }} --semver-type ${{ github.event.inputs.semverType }} env: - START_RELEASE_TOKEN: ${{ secrets.START_RELEASE_TOKEN }} + RELEASE_BOT_GITHUB_TOKEN: ${{ secrets.RELEASE_BOT_GITHUB_TOKEN }} diff --git a/.gitignore b/.gitignore index 68e8a13768228..59c5d02415266 100644 --- a/.gitignore +++ b/.gitignore @@ -3,7 +3,6 @@ dist .next target packages/next/wasm/@next -packages/*/packed-*.tgz # dependencies node_modules diff --git a/.npmrc b/.npmrc index 7e6ce68381fb2..5626f81fde083 100644 --- a/.npmrc +++ b/.npmrc @@ -1,3 +1,4 @@ +provenance = true save-exact = true tag-version-prefix="" strict-peer-dependencies = false diff --git a/.prettierignore b/.prettierignore index 0c00bf252d776..baf4892dfbfbb 100644 --- a/.prettierignore +++ b/.prettierignore @@ -1,30 +1,42 @@ -node_modules -**/.next/** -**/_next/** -**/dist/** +# Build artifacts +.next/ +.turbo/ +_next/ +__tmp__/ +dist/ +node_modules/ +target/ +compiled/ + +lerna.json +test-timings.json +pnpm-lock.yaml + packages/next/src/bundles/webpack/packages/*.runtime.js packages/next/src/bundles/webpack/packages/lazy-compilation-*.js -packages/next/src/compiled/** -packages/react-refresh-utils/**/*.js -packages/react-refresh-utils/**/*.d.ts -packages/react-dev-overlay/lib/** -**/__tmp__/** -lerna.json + .github/actions/next-stats-action/.work .github/actions/issue-validator/index.mjs .github/actions/issue-labeler/lib/index.js -packages/next-swc/crates/**/* -packages/next-swc/target/**/* + +packages/next-swc/crates/**/tests/**/output* +packages/next-swc/crates/core/tests/loader/issue-32553/input.js +packages/next-swc/crates/next-dev-tests/tests/integration/turbopack/basic/error/input/broken.js +packages/next-swc/crates/next-dev-tests/tests/integration/next/webpack-loaders/no-options/input/pages/hello.raw.js +packages/next-swc/crates/next-dev-tests/tests/integration/next/webpack-loaders/no-options/input/raw/hello.js packages/next-swc/native/**/* -packages/next-codemod/transforms/__testfixtures__/**/* -packages/next-codemod/transforms/__tests__/**/* + +packages/next-codemod/transforms/__testfixtures__/** +packages/next-codemod/transforms/__tests__/** packages/next-codemod/**/*.js packages/next-codemod/**/*.d.ts + packages/next-env/**/*.d.ts -test-timings.json + test/**/out/** test/development/basic/hmr/components/parse-error.js + bench/nested-deps/pages/**/* bench/nested-deps/components/**/* -pnpm-lock.yaml + **/convex/_generated/** diff --git a/packages/next-swc/.rustfmt.toml b/.rustfmt.toml similarity index 100% rename from packages/next-swc/.rustfmt.toml rename to .rustfmt.toml diff --git a/.vercel.approvers b/.vercel.approvers new file mode 100644 index 0000000000000..44ad372730eb9 --- /dev/null +++ b/.vercel.approvers @@ -0,0 +1,17 @@ +# Global Owners +@timneutkens +@ijjk +@shuding +@huozhi +@feedthejim + +# Tooling & Telemetry +pnpm-lock.yaml @vercel/web-tooling +Cargo.toml @vercel/web-tooling +Cargo.lock @vercel/web-tooling +/.cargo/config.toml @vercel/web-tooling +/.config/nextest.toml @vercel/web-tooling + +# Catch any markdown files outside docs (e.g. examples) +**/*.md @vercel/devex +**/*.mdx @vercel/devex \ No newline at end of file diff --git a/.vscode/extensions.json b/.vscode/extensions.json index 75f549ad6d9a4..b37ae9cdb17c8 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -19,6 +19,9 @@ "ms-vsliveshare.vsliveshare", // Debugging - "ms-vscode.vscode-js-profile-flame" + "ms-vscode.vscode-js-profile-flame", + + // MDX Authoring + "unifiedjs.vscode-mdx" ] } diff --git a/.vscode/launch.json b/.vscode/launch.json index 112f8d16542a9..8a3d1eab5bf1f 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -5,7 +5,7 @@ "version": "0.2.0", "configurations": [ { - "name": "Launch app-dir development", + "name": "Launch test/e2e/app-dir/app development", "type": "node", "request": "launch", "cwd": "${workspaceFolder}", @@ -17,7 +17,7 @@ } }, { - "name": "Launch app-dir build", + "name": "Launch test/e2e/app-dir/app build", "type": "node", "request": "launch", "cwd": "${workspaceFolder}", @@ -29,7 +29,7 @@ } }, { - "name": "Launch app development", + "name": "Launch examples/hello-world development", "type": "node", "request": "launch", "cwd": "${workspaceFolder}", @@ -41,7 +41,7 @@ } }, { - "name": "Launch app build", + "name": "Launch examples/hello-world build", "type": "node", "request": "launch", "cwd": "${workspaceFolder}", @@ -53,7 +53,7 @@ } }, { - "name": "Launch app production", + "name": "Launch examples/hello-world production", "type": "node", "request": "launch", "cwd": "${workspaceFolder}", diff --git a/.vscode/settings.json b/.vscode/settings.json index 9ad4110abc2b6..2de2ef61939c7 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -58,8 +58,6 @@ "*.ts": "$(capture).test.ts, $(capture).test.tsx", "*.tsx": "$(capture).test.ts, $(capture).test.tsx" }, - // Allow to find the cargo project for rust-analyzer. - "rust-analyzer.linkedProjects": ["packages/next-swc/Cargo.toml"], // Compile rust-analyzer in a separate directory to avoid conflicts with the main project. "rust-analyzer.checkOnSave.extraEnv": { "CARGO_TARGET_DIR": "target/rust-analyzer" @@ -68,11 +66,21 @@ "CARGO_TARGET_DIR": "target/rust-analyzer", "RUST_BACKTRACE": "0" }, + "rust-analyzer.cargo.extraEnv": { + "CARGO_TARGET_DIR": "target/rust_analyzer" + }, "cSpell.words": [ "Entrypoints", "napi", + "nextjs", "opentelemetry", "Threadsafe", "zipkin" + ], + "grammarly.selectors": [ + { + "language": "markdown", + "scheme": "file" + } ] } diff --git a/packages/next-swc/Cargo.lock b/Cargo.lock similarity index 82% rename from packages/next-swc/Cargo.lock rename to Cargo.lock index 592782849a8b2..ca5a601473a25 100644 --- a/packages/next-swc/Cargo.lock +++ b/Cargo.lock @@ -41,9 +41,9 @@ dependencies = [ [[package]] name = "aho-corasick" -version = "0.7.20" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc936419f96fa211c1b9166887b38e5e40b19958e5b895be7c1f93adec7071ac" +checksum = "67fc08ce920c31afb70f013dcce1bfc3a3195de6a228474e45e1f145b36f8d04" dependencies = [ "memchr", ] @@ -121,11 +121,17 @@ dependencies = [ "winapi 0.3.9", ] +[[package]] +name = "any_ascii" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70033777eb8b5124a81a1889416543dddef2de240019b674c81285a2635a7e1e" + [[package]] name = "anyhow" -version = "1.0.70" +version = "1.0.71" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7de8ce5e0f9f8d88245311066a578d72b7af3e7088f32783804676302df237e4" +checksum = "9c7d0618f0e0b7e8ff11427422b64564d5fb0be1940354bfe2e0529b18a9d9b8" dependencies = [ "backtrace", ] @@ -154,11 +160,10 @@ dependencies = [ [[package]] name = "ast_node" -version = "0.8.6" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf94863c5fdfee166d0907c44e5fee970123b2b7307046d35d1e671aa93afbba" +checksum = "c704e2f6ee1a98223f5a7629a6ef0f3decb3b552ed282889dc957edff98ce1e6" dependencies = [ - "darling 0.13.4", "pmutil", "proc-macro2", "quote", @@ -233,7 +238,7 @@ dependencies = [ "log", "parking", "polling", - "rustix 0.37.3", + "rustix 0.37.19", "slab", "socket2", "waker-fn", @@ -283,7 +288,7 @@ checksum = "0e97ce7de6cf12de5d7226c73f5ba9811622f4db3a5b91b55c53e987e5f91cba" dependencies = [ "proc-macro2", "quote", - "syn 2.0.8", + "syn 2.0.18", ] [[package]] @@ -343,13 +348,13 @@ checksum = "7a40729d2133846d9ed0ea60a8b9541bccddab49cd30f0715a1da672fe9a2524" [[package]] name = "async-trait" -version = "0.1.67" +version = "0.1.68" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86ea188f25f0255d8f92797797c97ebf5631fa88178beb1a46fdf5622c9a00e4" +checksum = "b9ccdd8f2a161be9bd5c023df56f1b2a0bd1d83872ae53b71a84a12c9bf6e842" dependencies = [ "proc-macro2", "quote", - "syn 2.0.8", + "syn 2.0.18", ] [[package]] @@ -366,6 +371,15 @@ dependencies = [ "tungstenite 0.17.3", ] +[[package]] +name = "atomic-polyfill" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3ff7eb3f316534d83a8a2c3d1674ace8a5a71198eba31e2e2b597833f699b28" +dependencies = [ + "critical-section", +] + [[package]] name = "atomic-waker" version = "1.1.0" @@ -386,7 +400,7 @@ dependencies = [ [[package]] name = "auto-hash-map" version = "0.1.0" -source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230331.3#1414a695ace2fb52a204fdee374be4dd0a98c8f5" +source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230608.1#a00ba87b486db48038e1d6e74775a29b920ef03b" dependencies = [ "serde", ] @@ -464,8 +478,8 @@ dependencies = [ "cc", "cfg-if 1.0.0", "libc", - "miniz_oxide", - "object 0.30.3", + "miniz_oxide 0.6.2", + "object", "rustc-demangle", ] @@ -502,11 +516,20 @@ dependencies = [ "scoped-tls", ] +[[package]] +name = "bincode" +version = "1.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" +dependencies = [ + "serde", +] + [[package]] name = "binding_macros" -version = "0.44.23" +version = "0.50.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df018ae4a80a06cef289384bf89614451c4f30c6238976ce5c00c1f4379b8eca" +checksum = "a31f397ada59fbcd6cb7f0a9d5bab4a53bb7cfc3ee2277b9f93d0ea2c88704d6" dependencies = [ "anyhow", "console_error_panic_hook", @@ -531,9 +554,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.0.2" +version = "2.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "487f1e0fcbe47deb8b0574e646def1c903389d95241dd1bbcc6ce4a715dfc0c1" +checksum = "24a6904aef64d73cf10ab17ebace7befb918b82164785cb89907993be7f83813" [[package]] name = "bitreader" @@ -544,6 +567,18 @@ dependencies = [ "cfg-if 1.0.0", ] +[[package]] +name = "bitvec" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" +dependencies = [ + "funty", + "radium", + "tap", + "wyz", +] + [[package]] name = "blake3" version = "1.3.3" @@ -623,9 +658,9 @@ checksum = "0d261e256854913907f67ed06efbc3338dfe6179796deefc1ff763fc1aee5535" [[package]] name = "bytecheck" -version = "0.6.10" +version = "0.6.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13fe11640a23eb24562225322cd3e452b93a3d4091d62fab69c70542fcd17d1f" +checksum = "8b6372023ac861f6e6dc89c8344a8f398fb42aaba2b5dbc649ca0c0e9dbcb627" dependencies = [ "bytecheck_derive", "ptr_meta", @@ -634,15 +669,21 @@ dependencies = [ [[package]] name = "bytecheck_derive" -version = "0.6.10" +version = "0.6.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e31225543cb46f81a7e224762764f4a6a0f097b1db0b175f69e8065efaa42de5" +checksum = "a7ec4c6f261935ad534c0c22dbef2201b45918860eb1c574b972bd213a76af61" dependencies = [ "proc-macro2", "quote", "syn 1.0.109", ] +[[package]] +name = "bytemuck" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17febce684fd15d89027105661fec94afb475cb995fbc59d2865198446ba2eea" + [[package]] name = "byteorder" version = "1.4.3" @@ -654,6 +695,9 @@ name = "bytes" version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" +dependencies = [ + "serde", +] [[package]] name = "cargo-lock" @@ -748,7 +792,7 @@ checksum = "4cdf6513e24d260548345a5ef13a04110f5915b7764c274933e10f9363a43e3b" dependencies = [ "chromiumoxide_types", "either", - "heck", + "heck 0.4.1", "once_cell", "proc-macro2", "quote", @@ -777,6 +821,7 @@ dependencies = [ "js-sys", "num-integer", "num-traits", + "serde", "time 0.1.45", "wasm-bindgen", "winapi 0.3.9", @@ -827,7 +872,7 @@ version = "4.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42dfd32784433290c51d92c438bb72ea5063797fc3cc9a21a8c4346bebbb2098" dependencies = [ - "bitflags 2.0.2", + "bitflags 2.2.1", "clap_derive", "clap_lex 0.3.3", "is-terminal", @@ -842,7 +887,7 @@ version = "4.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fddf67631444a3a3e3e5ac51c36a5e01335302de677bd78759eaa90ab1f46644" dependencies = [ - "heck", + "heck 0.4.1", "proc-macro-error", "proc-macro2", "quote", @@ -876,6 +921,12 @@ dependencies = [ "bitflags 1.3.2", ] +[[package]] +name = "cobs" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67ba02a97a2bd10f4b59b25c7973101c79642302776489e030cd13cdab09ed15" + [[package]] name = "codespan-reporting" version = "0.11.1" @@ -886,6 +937,12 @@ dependencies = [ "unicode-width", ] +[[package]] +name = "color_quant" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" + [[package]] name = "combine" version = "4.6.6" @@ -998,6 +1055,12 @@ dependencies = [ "unicode-segmentation", ] +[[package]] +name = "cooked-waker" +version = "5.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147be55d677052dabc6b22252d5dd0fd4c29c8c27aa4f2fbef0f94aa003b406f" + [[package]] name = "core-foundation" version = "0.9.3" @@ -1010,9 +1073,9 @@ dependencies = [ [[package]] name = "core-foundation-sys" -version = "0.8.3" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" +checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" [[package]] name = "corosensei" @@ -1029,65 +1092,83 @@ dependencies = [ [[package]] name = "cpufeatures" -version = "0.2.5" +version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28d997bd5e24a5928dd43e46dc529867e207907fe0b239c3477d924f7f2ca320" +checksum = "3e4c1eaa2012c47becbbad2ab175484c2a84d1185b566fb2cc5b8707343dfe58" dependencies = [ "libc", ] [[package]] name = "cranelift-bforest" -version = "0.82.3" +version = "0.91.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38faa2a16616c8e78a18d37b4726b98bfd2de192f2fdc8a39ddf568a408a0f75" +checksum = "2a2ab4512dfd3a6f4be184403a195f76e81a8a9f9e6c898e19d2dc3ce20e0115" dependencies = [ "cranelift-entity", ] [[package]] name = "cranelift-codegen" -version = "0.82.3" +version = "0.91.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26f192472a3ba23860afd07d2b0217dc628f21fcc72617aa1336d98e1671f33b" +checksum = "98b022ed2a5913a38839dfbafe6cf135342661293b08049843362df4301261dc" dependencies = [ + "arrayvec", + "bumpalo", "cranelift-bforest", "cranelift-codegen-meta", "cranelift-codegen-shared", + "cranelift-egraph", "cranelift-entity", + "cranelift-isle", "gimli 0.26.2", "log", - "regalloc", + "regalloc2", "smallvec", "target-lexicon", ] [[package]] name = "cranelift-codegen-meta" -version = "0.82.3" +version = "0.91.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f32ddb89e9b89d3d9b36a5b7d7ea3261c98235a76ac95ba46826b8ec40b1a24" +checksum = "639307b45434ad112a98f8300c0f0ab085cbefcd767efcdef9ef19d4c0756e74" dependencies = [ "cranelift-codegen-shared", ] [[package]] name = "cranelift-codegen-shared" -version = "0.82.3" +version = "0.91.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "278e52e29c53fcf32431ef08406c295699a70306d05a0715c5b1bf50e33a9ab7" + +[[package]] +name = "cranelift-egraph" +version = "0.91.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01fd0d9f288cc1b42d9333b7a776b17e278fc888c28e6a0f09b5573d45a150bc" +checksum = "624b54323b06e675293939311943ba82d323bb340468ce1889be5da7932c8d73" +dependencies = [ + "cranelift-entity", + "fxhash", + "hashbrown", + "indexmap", + "log", + "smallvec", +] [[package]] name = "cranelift-entity" -version = "0.82.3" +version = "0.91.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e3bfe172b83167604601faf9dc60453e0d0a93415b57a9c4d1a7ae6849185cf" +checksum = "9a59bcbca89c3f1b70b93ab3cbba5e5e0cbf3e63dadb23c7525cb142e21a9d4c" [[package]] name = "cranelift-frontend" -version = "0.82.3" +version = "0.91.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a006e3e32d80ce0e4ba7f1f9ddf66066d052a8c884a110b91d05404d6ce26dce" +checksum = "0d70abacb8cfef3dc8ff7e8836e9c1d70f7967dfdac824a4cd5e30223415aca6" dependencies = [ "cranelift-codegen", "log", @@ -1095,6 +1176,12 @@ dependencies = [ "target-lexicon", ] +[[package]] +name = "cranelift-isle" +version = "0.91.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "393bc73c451830ff8dbb3a07f61843d6cb41a084f9996319917c0b291ed785bb" + [[package]] name = "crc" version = "2.1.0" @@ -1157,11 +1244,17 @@ dependencies = [ "itertools", ] +[[package]] +name = "critical-section" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6548a0ad5d2549e111e1f6a11a6c2e2d00ce6a3dafe22948d67c2b443f775e52" + [[package]] name = "crossbeam-channel" -version = "0.5.7" +version = "0.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf2b3e8478797446514c91ef04bafcb59faba183e621ad488df88983cc14128c" +checksum = "a33c2bf77f2df06183c3aa30d1e96c0695a313d4f9c453cc3762a6db39f99200" dependencies = [ "cfg-if 1.0.0", "crossbeam-utils", @@ -1245,6 +1338,26 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "ctor" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd4056f63fce3b82d852c3da92b08ea59959890813a7f4ce9c0ff85b10cf301b" +dependencies = [ + "quote", + "syn 2.0.18", +] + +[[package]] +name = "ctrlc" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04d778600249295e82b6ab12e291ed9029407efee0cfb7baf67157edc65964df" +dependencies = [ + "nix", + "windows-sys 0.48.0", +] + [[package]] name = "curl" version = "0.4.44" @@ -1278,9 +1391,9 @@ dependencies = [ [[package]] name = "cxx" -version = "1.0.93" +version = "1.0.94" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9c00419335c41018365ddf7e4d5f1c12ee3659ddcf3e01974650ba1de73d038" +checksum = "f61f1b6389c3fe1c316bf8a4dccc90a38208354b330925bce1f74a6c4756eb93" dependencies = [ "cc", "cxxbridge-flags", @@ -1290,9 +1403,9 @@ dependencies = [ [[package]] name = "cxx-build" -version = "1.0.93" +version = "1.0.94" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb8307ad413a98fff033c8545ecf133e3257747b3bae935e7602aab8aa92d4ca" +checksum = "12cee708e8962df2aeb38f594aae5d827c022b6460ac71a7a3e2c3c2aae5a07b" dependencies = [ "cc", "codespan-reporting", @@ -1300,34 +1413,24 @@ dependencies = [ "proc-macro2", "quote", "scratch", - "syn 2.0.8", + "syn 2.0.18", ] [[package]] name = "cxxbridge-flags" -version = "1.0.93" +version = "1.0.94" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edc52e2eb08915cb12596d29d55f0b5384f00d697a646dbd269b6ecb0fbd9d31" +checksum = "7944172ae7e4068c533afbb984114a56c46e9ccddda550499caa222902c7f7bb" [[package]] name = "cxxbridge-macro" -version = "1.0.93" +version = "1.0.94" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "631569015d0d8d54e6c241733f944042623ab6df7bc3be7466874b05fcdb1c5f" +checksum = "2345488264226bf682893e25de0769f3360aac9957980ec49361b083ddaa5bc5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.8", -] - -[[package]] -name = "darling" -version = "0.13.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a01d95850c592940db9b8194bc39f4bc0e89dee5c4265e4b1807c34a9aba453c" -dependencies = [ - "darling_core 0.13.4", - "darling_macro 0.13.4", + "syn 2.0.18", ] [[package]] @@ -1336,22 +1439,8 @@ version = "0.14.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7b750cb3417fd1b327431a470f388520309479ab0bf5e323505daf0290cd3850" dependencies = [ - "darling_core 0.14.4", - "darling_macro 0.14.4", -] - -[[package]] -name = "darling_core" -version = "0.13.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "859d65a907b6852c9361e3185c862aae7fafd2887876799fa55f5f99dc40d610" -dependencies = [ - "fnv", - "ident_case", - "proc-macro2", - "quote", - "strsim", - "syn 1.0.109", + "darling_core", + "darling_macro", ] [[package]] @@ -1364,17 +1453,7 @@ dependencies = [ "ident_case", "proc-macro2", "quote", - "syn 1.0.109", -] - -[[package]] -name = "darling_macro" -version = "0.13.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c972679f83bdf9c42bd905396b6c3588a843a17f0f16dfcfa3e2c5d57441835" -dependencies = [ - "darling_core 0.13.4", - "quote", + "strsim", "syn 1.0.109", ] @@ -1384,7 +1463,7 @@ version = "0.14.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a4aab4dbc9f7611d8b55048a3a16d2d010c2c8334e46304b40ac1cc14bf3b48e" dependencies = [ - "darling_core 0.14.4", + "darling_core", "quote", "syn 1.0.109", ] @@ -1396,7 +1475,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "907076dfda823b0b36d2a1bb5f90c96660a5bbcd7729e10727f07858f22c4edc" dependencies = [ "cfg-if 1.0.0", - "hashbrown 0.12.3", + "hashbrown", "lock_api", "once_cell", "parking_lot_core 0.9.7", @@ -1404,9 +1483,9 @@ dependencies = [ [[package]] name = "data-encoding" -version = "2.3.3" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23d8666cb01533c39dde32bcbab8e227b4ed6679b2c925eba05feabea39508fb" +checksum = "c2e66c9d817f1720209181c316d28635c050fa304f9c79e47a520882661b7308" [[package]] name = "debugid" @@ -1418,6 +1497,17 @@ dependencies = [ "uuid", ] +[[package]] +name = "derivative" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "dhat" version = "0.3.2" @@ -1448,9 +1538,9 @@ checksum = "524cbf6897b527295dff137cec09ecf3a05f4fddffd7dfcd1585403449e74198" [[package]] name = "digest" -version = "0.10.6" +version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ "block-buffer", "crypto-common", @@ -1556,18 +1646,6 @@ dependencies = [ "syn 1.0.109", ] -[[package]] -name = "enum_kind" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78b940da354ae81ef0926c5eaa428207b8f4f091d3956c891dfbd124162bed99" -dependencies = [ - "pmutil", - "proc-macro2", - "swc_macros_common", - "syn 1.0.109", -] - [[package]] name = "enumset" version = "1.0.12" @@ -1583,7 +1661,7 @@ version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "03e7b551eba279bf0fa88b83a46330168c1560a52a94f5126f892f0b364ab3e0" dependencies = [ - "darling 0.14.4", + "darling", "proc-macro2", "quote", "syn 1.0.109", @@ -1624,13 +1702,13 @@ dependencies = [ [[package]] name = "errno" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50d6a0976c999d473fe89ad888d5a284e55366d9dc9038b1ba2aa15128c4afa0" +checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a" dependencies = [ "errno-dragonfly", "libc", - "windows-sys 0.45.0", + "windows-sys 0.48.0", ] [[package]] @@ -1664,16 +1742,25 @@ dependencies = [ "instant", ] +[[package]] +name = "fdeflate" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d329bdeac514ee06249dabc27877490f17f5d371ec693360768b838e19f3ae10" +dependencies = [ + "simd-adler32", +] + [[package]] name = "filetime" -version = "0.2.20" +version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a3de6e8d11b22ff9edc6d916f890800597d60f8b2da1caf2955c274638d6412" +checksum = "5cbc844cecaee9d4443931972e1289c8ff485cb4cc2767cb03ca139ed6885153" dependencies = [ "cfg-if 1.0.0", "libc", "redox_syscall 0.2.16", - "windows-sys 0.45.0", + "windows-sys 0.48.0", ] [[package]] @@ -1689,7 +1776,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8a2db397cb1c8772f31494cb8917e48cd1e64f0fa7efac59fbd741a0a8ce841" dependencies = [ "crc32fast", - "miniz_oxide", + "miniz_oxide 0.6.2", ] [[package]] @@ -1724,9 +1811,9 @@ dependencies = [ [[package]] name = "from_variant" -version = "0.1.4" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0981e470d2ab9f643df3921d54f1952ea100c39fdb6a3fdc820e20d2291df6c" +checksum = "1d449976075322384507443937df2f1d5577afbf4282f12a5a66ef29fa3e6307" dependencies = [ "pmutil", "proc-macro2", @@ -1734,6 +1821,12 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "fs_extra" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c" + [[package]] name = "fsevent" version = "0.4.0" @@ -1769,11 +1862,17 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" +[[package]] +name = "funty" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" + [[package]] name = "futures" -version = "0.3.27" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "531ac96c6ff5fd7c62263c5e3c67a603af4fcaee2e1a0ae5565ba3a11e69e549" +checksum = "23342abe12aba583913b2e62f22225ff9c950774065e4bfb61a19cd9770fec40" dependencies = [ "futures-channel", "futures-core", @@ -1786,9 +1885,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.27" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "164713a5a0dcc3e7b4b1ed7d3b433cabc18025386f9339346e8daf15963cf7ac" +checksum = "955518d47e09b25bbebc7a18df10b81f0c766eaf4c4f1cccef2fca5f2a4fb5f2" dependencies = [ "futures-core", "futures-sink", @@ -1796,15 +1895,15 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.27" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86d7a0c1aa76363dac491de0ee99faf6941128376f1cf96f07db7603b7de69dd" +checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c" [[package]] name = "futures-executor" -version = "0.3.27" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1997dd9df74cdac935c76252744c1ed5794fac083242ea4fe77ef3ed60ba0f83" +checksum = "ccecee823288125bd88b4d7f565c9e58e41858e47ab72e8ea2d64e93624386e0" dependencies = [ "futures-core", "futures-task", @@ -1813,9 +1912,9 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.27" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89d422fa3cbe3b40dca574ab087abb5bc98258ea57eea3fd6f1fa7162c778b91" +checksum = "4fff74096e71ed47f8e023204cfd0aa1289cd54ae5430a9523be060cdb849964" [[package]] name = "futures-lite" @@ -1834,13 +1933,13 @@ dependencies = [ [[package]] name = "futures-macro" -version = "0.3.27" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3eb14ed937631bd8b8b8977f2c198443447a8355b6e3ca599f38c975e5a963b6" +checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.18", ] [[package]] @@ -1856,15 +1955,15 @@ dependencies = [ [[package]] name = "futures-sink" -version = "0.3.27" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec93083a4aecafb2a80a885c9de1f0ccae9dbd32c2bb54b0c3a65690e0b8d2f2" +checksum = "f43be4fe21a13b9781a69afa4985b0f6ee0e1afab2c6f454a8cf30e2b2237b6e" [[package]] name = "futures-task" -version = "0.3.27" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd65540d33b37b16542a0438c12e6aeead10d4ac5d05bd3f805b8f35ab592879" +checksum = "76d3d132be6c0e6aa1534069c705a74a5997a356c0dc2f86a47765e5617c5b65" [[package]] name = "futures-timer" @@ -1874,9 +1973,9 @@ checksum = "e64b03909df88034c26dc1547e8970b91f98bdb65165d6a4e9110d94263dbb2c" [[package]] name = "futures-util" -version = "0.3.27" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ef6b17e481503ec85211fed8f39d1970f128935ca1f814cd32ac4a6842e84ab" +checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533" dependencies = [ "futures-channel", "futures-core", @@ -1899,20 +1998,11 @@ dependencies = [ "byteorder", ] -[[package]] -name = "generational-arena" -version = "0.2.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e1d3b771574f62d0548cee0ad9057857e9fc25d7a3335f140c84f6acd0bf601" -dependencies = [ - "cfg-if 0.1.10", -] - [[package]] name = "generic-array" -version = "0.14.6" +version = "0.14.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bff49e947297f3312447abdca79f45f4738097cc82b06e72054d2223f601f1b9" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" dependencies = [ "typenum", "version_check", @@ -1920,9 +2010,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.8" +version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31" +checksum = "c85e1d9ab2eadba7e5040d4e09cbd6d072b76a557ad64e797c2cb9d4da21d7e4" dependencies = [ "cfg-if 1.0.0", "js-sys", @@ -1943,6 +2033,16 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "gif" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80792593675e051cf94a4b111980da2ba60d4a83e43e0048c5693baab3977045" +dependencies = [ + "color_quant", + "weezl", +] + [[package]] name = "gimli" version = "0.26.2" @@ -2024,12 +2124,12 @@ dependencies = [ ] [[package]] -name = "hashbrown" -version = "0.11.2" +name = "hash32" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" +checksum = "b0c35f58762feb77d74ebe43bdbc3210f09be9fe6742234d573bacc26ed92b67" dependencies = [ - "ahash", + "byteorder", ] [[package]] @@ -2054,6 +2154,29 @@ dependencies = [ "num-traits", ] +[[package]] +name = "heapless" +version = "0.7.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db04bc24a18b9ea980628ecf00e6c0264f3c1426dac36c00cb49b6fbad8b0743" +dependencies = [ + "atomic-polyfill", + "hash32", + "rustc_version 0.4.0", + "serde", + "spin 0.9.8", + "stable_deref_trait", +] + +[[package]] +name = "heck" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c" +dependencies = [ + "unicode-segmentation", +] + [[package]] name = "heck" version = "0.4.1" @@ -2159,7 +2282,7 @@ dependencies = [ "serde", "serde_json", "serde_regex", - "serde_yaml", + "serde_yaml 0.9.19", "similar", "tokio", "url", @@ -2248,9 +2371,9 @@ dependencies = [ [[package]] name = "iana-time-zone" -version = "0.1.54" +version = "0.1.56" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c17cc76786e99f8d2f055c11159e7f0091c42474dcc3189fbab96072e873e6d" +checksum = "0722cd7114b7de04316e7ea5456a0bbb20e4adb46fd27a3697adb812cff0f37c" dependencies = [ "android_system_properties", "core-foundation-sys", @@ -2270,6 +2393,12 @@ dependencies = [ "cxx-build", ] +[[package]] +name = "id-arena" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25a2bc672d1148e28034f176e01fffebb08b35768468cc954630da77a1449005" + [[package]] name = "ident_case" version = "1.0.1" @@ -2292,6 +2421,22 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cb56e1aa765b4b4f3aadfab769793b7087bb03a4ea4920644a6d238e2df5b9ed" +[[package]] +name = "image" +version = "0.24.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "527909aa81e20ac3a44803521443a765550f09b5130c2c2fa1ea59c2f8f50a3a" +dependencies = [ + "bytemuck", + "byteorder", + "color_quant", + "gif", + "jpeg-decoder", + "num-rational", + "num-traits", + "png", +] + [[package]] name = "include_dir" version = "0.7.3" @@ -2313,12 +2458,12 @@ dependencies = [ [[package]] name = "indexmap" -version = "1.9.2" +version = "1.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1885e79c1fc4b10f0e172c475f458b7f7b93061064d98c3293e98c5ba0c8b399" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" dependencies = [ "autocfg", - "hashbrown 0.12.3", + "hashbrown", "rayon", "serde", ] @@ -2360,13 +2505,13 @@ dependencies = [ [[package]] name = "io-lifetimes" -version = "1.0.9" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09270fd4fa1111bc614ed2246c7ef56239a3063d5be0d1ec3b589c505d400aeb" +checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2" dependencies = [ "hermit-abi 0.3.1", "libc", - "windows-sys 0.45.0", + "windows-sys 0.48.0", ] [[package]] @@ -2479,6 +2624,12 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" +[[package]] +name = "jpeg-decoder" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc0000e42512c92e31c2252315bda326620a4e034105e900c98ec492fa077b3e" + [[package]] name = "js-sys" version = "0.3.61" @@ -2589,6 +2740,15 @@ dependencies = [ "static_assertions", ] +[[package]] +name = "lexical-sort" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c09e4591611e231daf4d4c685a66cb0410cc1e502027a20ae55f2bb9e997207a" +dependencies = [ + "any_ascii", +] + [[package]] name = "lexical-util" version = "0.8.5" @@ -2621,9 +2781,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.140" +version = "0.2.144" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99227334921fae1a979cf0bfdfcc6b3e5ce376ef57e16fb6fb3ea2ed6095f80c" +checksum = "2b00cc1c228a6782d0f076e7b232802e0c5689d41bb5df366f2a6b6621cfdfe1" [[package]] name = "libloading" @@ -2676,6 +2836,21 @@ dependencies = [ "cc", ] +[[package]] +name = "linked-hash-map" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" + +[[package]] +name = "linked_hash_set" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47186c6da4d81ca383c7c47c1bfc80f4b95f4720514d860a5407aaf4233f9588" +dependencies = [ + "linked-hash-map", +] + [[package]] name = "linux-raw-sys" version = "0.1.4" @@ -2684,9 +2859,9 @@ checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4" [[package]] name = "linux-raw-sys" -version = "0.3.0" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd550e73688e6d578f0ac2119e32b797a327631a42f9433e59d02e139c8df60d" +checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519" [[package]] name = "lock_api" @@ -2700,42 +2875,20 @@ dependencies = [ [[package]] name = "log" -version = "0.4.17" +version = "0.4.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" +checksum = "518ef76f2f87365916b142844c16d8fefd85039bc5699050210a7778ee1cd1de" dependencies = [ - "cfg-if 1.0.0", "value-bag", ] -[[package]] -name = "loupe" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b6a72dfa44fe15b5e76b94307eeb2ff995a8c5b283b55008940c02e0c5b634d" -dependencies = [ - "indexmap", - "loupe-derive", - "rustversion", -] - -[[package]] -name = "loupe-derive" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0fbfc88337168279f2e9ae06e157cfed4efd3316e14dc96ed074d4f2e6c5952" -dependencies = [ - "quote", - "syn 1.0.109", -] - [[package]] name = "lru" version = "0.7.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e999beba7b6e8345721bd280141ed958096a2e4abdf74f67ff4ce49b4b54e47a" dependencies = [ - "hashbrown 0.12.3", + "hashbrown", ] [[package]] @@ -2797,9 +2950,9 @@ dependencies = [ [[package]] name = "mdxjs" -version = "0.1.8" +version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe25a3b6ba9aad427fa5ef59c99506bb6954748dd82211223dfd8a40dd75ae80" +checksum = "c88a71be094e8cf4f13b62e6ba304472332f8890e7cdf15098dc6512b812fdab" dependencies = [ "markdown", "serde", @@ -2821,15 +2974,6 @@ dependencies = [ "libc", ] -[[package]] -name = "memoffset" -version = "0.6.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" -dependencies = [ - "autocfg", -] - [[package]] name = "memoffset" version = "0.7.1" @@ -2920,12 +3064,22 @@ dependencies = [ ] [[package]] -name = "mintex" -version = "0.1.2" +name = "miniz_oxide" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd7c5ba1c3b5a23418d7bbf98c71c3d4946a0125002129231da8d6b723d559cb" +checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" dependencies = [ - "once_cell", + "adler", + "simd-adler32", +] + +[[package]] +name = "mintex" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd7c5ba1c3b5a23418d7bbf98c71c3d4946a0125002129231da8d6b723d559cb" +dependencies = [ + "once_cell", "sys-info", ] @@ -2986,9 +3140,9 @@ dependencies = [ [[package]] name = "modularize_imports" -version = "0.26.11" +version = "0.30.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a0afe8dd51536ab2038f14324245d53effcc3934ba646479f28834744209a7c" +checksum = "13e83b2f2095735e151ab41afe33b0ede0d8173c5ccd753e2d87a144dd97e6af" dependencies = [ "convert_case 0.5.0", "handlebars", @@ -3012,13 +3166,13 @@ checksum = "7843ec2de400bcbc6a6328c958dc38e5359da6e93e72e37bc5246bf1ae776389" [[package]] name = "napi" -version = "2.12.1" +version = "2.12.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8de689526aff547ad70ad7feef42f1a5ccaa6f768910fd93984dae25a3fc9699" +checksum = "69b29acdc6cc5c918c3eabd51d241b1c6dfa8914f3552fcfd76e1d7536934581" dependencies = [ "anyhow", - "bitflags 2.0.2", - "ctor", + "bitflags 2.2.1", + "ctor 0.2.0", "napi-derive", "napi-sys", "once_cell", @@ -3035,9 +3189,9 @@ checksum = "882a73d9ef23e8dc2ebbffb6a6ae2ef467c0f18ac10711e4cc59c5485d41df0e" [[package]] name = "napi-derive" -version = "2.12.2" +version = "2.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6bd0beb0ac7e8576bc92d293361a461b42eaf41740bbdec7e0cbf64d8dc89f7" +checksum = "af2ac63101a19228b0881694cac07468d642fd10e4f943a9c9feebeebf1a4787" dependencies = [ "convert_case 0.6.0", "napi-derive-backend", @@ -3048,9 +3202,9 @@ dependencies = [ [[package]] name = "napi-derive-backend" -version = "1.0.48" +version = "1.0.49" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c713ff9ff5baa6d6ad9aedc46fad73c91e2f16ebe5ece0f41983d4e70e020c7c" +checksum = "0e32b5bc4d803e40b783b0aa3fe488eac8711cfaa4c5c9915293dfd3d0b99925" dependencies = [ "convert_case 0.6.0", "once_cell", @@ -3117,7 +3271,7 @@ version = "0.1.0" dependencies = [ "anyhow", "next-core", - "turbo-binding", + "turbopack-binding", "vergen", ] @@ -3128,9 +3282,11 @@ dependencies = [ "allsorts", "anyhow", "async-recursion", + "async-trait", "futures", "indexmap", "indoc", + "lazy_static", "mime", "next-transform-dynamic", "next-transform-font", @@ -3141,9 +3297,10 @@ dependencies = [ "serde", "serde_json", "swc_core", - "turbo-binding", + "thiserror", "turbo-tasks", "turbo-tasks-fs", + "turbopack-binding", ] [[package]] @@ -3157,6 +3314,7 @@ dependencies = [ "criterion", "dunce", "futures", + "indexmap", "mime", "next-core", "nix", @@ -3170,9 +3328,11 @@ dependencies = [ "serde_json", "tempfile", "tokio", + "tracing", + "tracing-subscriber", "tungstenite 0.17.3", - "turbo-binding", "turbo-tasks", + "turbopack-binding", "url", "vergen", "webbrowser", @@ -3201,8 +3361,8 @@ dependencies = [ "testing", "tokio", "tungstenite 0.17.3", - "turbo-binding", "turbo-tasks", + "turbopack-binding", "url", "webbrowser", ] @@ -3212,6 +3372,7 @@ name = "next-swc" version = "0.0.0" dependencies = [ "chrono", + "convert_case 0.5.0", "easy-error", "either", "fxhash", @@ -3220,12 +3381,12 @@ dependencies = [ "once_cell", "pathdiff", "regex", + "rustc-hash", "serde", "serde_json", "sha1 0.10.5", - "swc_relay", "tracing", - "turbo-binding", + "turbopack-binding", "walkdir", ] @@ -3252,8 +3413,8 @@ dependencies = [ "tracing-chrome", "tracing-futures", "tracing-subscriber", - "turbo-binding", "turbo-tasks", + "turbopack-binding", ] [[package]] @@ -3302,7 +3463,7 @@ dependencies = [ [[package]] name = "node-file-trace" version = "0.1.0" -source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230331.3#1414a695ace2fb52a204fdee374be4dd0a98c8f5" +source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230608.1#a00ba87b486db48038e1d6e74775a29b920ef03b" dependencies = [ "anyhow", "serde", @@ -3392,6 +3553,17 @@ dependencies = [ "num-traits", ] +[[package]] +name = "num-rational" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + [[package]] name = "num-traits" version = "0.2.15" @@ -3412,24 +3584,33 @@ dependencies = [ ] [[package]] -name = "objc" -version = "0.2.7" +name = "num_enum" +version = "0.5.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "915b1b472bc21c53464d6c8461c9d3af805ba1ef837e1cac254428f4a77177b1" +checksum = "1f646caf906c20226733ed5b1374287eb97e3c2a5c227ce668c1f2ce20ae57c9" dependencies = [ - "malloc_buf", + "num_enum_derive", ] [[package]] -name = "object" -version = "0.28.4" +name = "num_enum_derive" +version = "0.5.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e42c982f2d955fac81dd7e1d0e1426a7d702acd9c98d19ab01083a6a0328c424" +checksum = "dcbff9bc912032c62bf65ef1d5aea88983b420f4f839db1e9b0c281a25c9c799" dependencies = [ - "crc32fast", - "hashbrown 0.11.2", - "indexmap", - "memchr", + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "objc" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "915b1b472bc21c53464d6c8461c9d3af805ba1ef837e1cac254428f4a77177b1" +dependencies = [ + "malloc_buf", ] [[package]] @@ -3738,22 +3919,22 @@ dependencies = [ [[package]] name = "pin-project" -version = "1.0.12" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad29a609b6bcd67fee905812e544992d216af9d755757c05ed2d0e15a74c6ecc" +checksum = "c95a7476719eab1e366eaf73d0260af3021184f18177925b07f54b30089ceead" dependencies = [ "pin-project-internal", ] [[package]] name = "pin-project-internal" -version = "1.0.12" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "069bdb1e05adc7a8990dce9cc75370895fbe4e3d58b9b73bf1aee56359344a55" +checksum = "39407670928234ebc5e6e580247dd567ad73a3578460c5990f9503df207e8f07" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.18", ] [[package]] @@ -3813,6 +3994,19 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "png" +version = "0.17.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aaeebc51f9e7d2c150d3f3bfeb667f2aa985db5ef1e3d212847bdedb488beeaa" +dependencies = [ + "bitflags 1.3.2", + "crc32fast", + "fdeflate", + "flate2", + "miniz_oxide 0.7.1", +] + [[package]] name = "polling" version = "2.6.0" @@ -3838,6 +4032,17 @@ dependencies = [ "rand", ] +[[package]] +name = "postcard" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfa512cd0d087cc9f99ad30a1bf64795b67871edbead083ffc3a4dfafa59aa00" +dependencies = [ + "cobs", + "heapless", + "serde", +] + [[package]] name = "ppv-lite86" version = "0.2.17" @@ -3852,9 +4057,9 @@ checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" [[package]] name = "preset_env_base" -version = "0.4.0" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db4a43af74678e784b17db952b7fd726937b0a058c7c972624ddfd366e7603e4" +checksum = "b09a48d8ea8b031bde7755cdf6f87f7123a0cbefc36b0cd09cbb2de726594393" dependencies = [ "ahash", "anyhow", @@ -3874,7 +4079,7 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a25e9bcb20aa780fd0bb16b72403a9064d6b3f22f026946029acb941a50af755" dependencies = [ - "ctor", + "ctor 0.1.26", "diff", "output_vt100", "yansi", @@ -3890,6 +4095,16 @@ dependencies = [ "indexmap", ] +[[package]] +name = "proc-macro-crate" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" +dependencies = [ + "once_cell", + "toml_edit", +] + [[package]] name = "proc-macro-error" version = "1.0.4" @@ -3922,9 +4137,9 @@ checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068" [[package]] name = "proc-macro2" -version = "1.0.53" +version = "1.0.59" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba466839c78239c09faf015484e5cc04860f88242cff4d03eb038f04b4699b73" +checksum = "6aeca18b86b413c660b781aa319e4e2648a3e6f9eadc9b47e9038e6fe9f3451b" dependencies = [ "unicode-ident", ] @@ -3990,6 +4205,17 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "pulldown-cmark" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffade02495f22453cd593159ea2f59827aae7f53fa8323f756799b670881dcf8" +dependencies = [ + "bitflags 1.3.2", + "memchr", + "unicase", +] + [[package]] name = "qstring" version = "0.7.2" @@ -4001,13 +4227,19 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.26" +version = "1.0.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc" +checksum = "1b9ab9c7eadfd8df19006f1cf1a4aed13540ed5cbc047010ece5826e10825488" dependencies = [ "proc-macro2", ] +[[package]] +name = "radium" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" + [[package]] name = "radix_fmt" version = "1.0.0" @@ -4087,6 +4319,15 @@ dependencies = [ "bitflags 1.3.2", ] +[[package]] +name = "redox_syscall" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" +dependencies = [ + "bitflags 1.3.2", +] + [[package]] name = "redox_users" version = "0.4.3" @@ -4099,25 +4340,26 @@ dependencies = [ ] [[package]] -name = "regalloc" -version = "0.0.34" +name = "regalloc2" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62446b1d3ebf980bdc68837700af1d77b37bc430e524bf95319c6eada2a4cc02" +checksum = "300d4fbfb40c1c66a78ba3ddd41c1110247cf52f97b87d0f2fc9209bd49b030c" dependencies = [ + "fxhash", "log", - "rustc-hash", + "slice-group-by", "smallvec", ] [[package]] name = "regex" -version = "1.7.2" +version = "1.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cce168fea28d3e05f158bda4576cf0c844d5045bc2cc3620fa0292ed5bb5814c" +checksum = "81ca098a9821bd52d6b24fd8b10bd081f47d39c22778cafaa75a2857a62c6390" dependencies = [ "aho-corasick", "memchr", - "regex-syntax", + "regex-syntax 0.7.2", ] [[package]] @@ -4126,7 +4368,7 @@ version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" dependencies = [ - "regex-syntax", + "regex-syntax 0.6.29", ] [[package]] @@ -4135,6 +4377,12 @@ version = "0.6.29" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" +[[package]] +name = "regex-syntax" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "436b050e76ed2903236f032a59761c1eb99e1b0aead2c257922771dab1fc8c78" + [[package]] name = "region" version = "3.0.0" @@ -4155,9 +4403,9 @@ checksum = "4bf2521270932c3c7bed1a59151222bd7643c79310f2916f01925e1e16255698" [[package]] name = "rend" -version = "0.3.6" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79af64b4b6362ffba04eef3a4e10829718a4896dac19daa741851c86781edf95" +checksum = "581008d2099240d37fb08d77ad713bcaec2c4d89d50b5b21a8bb1996bbab68ab" dependencies = [ "bytecheck", ] @@ -4213,7 +4461,7 @@ dependencies = [ "cc", "libc", "once_cell", - "spin", + "spin 0.5.2", "untrusted", "web-sys", "winapi 0.3.9", @@ -4221,23 +4469,27 @@ dependencies = [ [[package]] name = "rkyv" -version = "0.7.37" +version = "0.7.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f08c8062c1fe1253064043b8fc07bfea1b9702b71b4a86c11ea3588183b12e1" +checksum = "0200c8230b013893c0b2d6213d6ec64ed2b9be2e0e016682b7224ff82cff5c58" dependencies = [ + "bitvec", "bytecheck", - "hashbrown 0.12.3", + "hashbrown", + "indexmap", "ptr_meta", "rend", "rkyv_derive", "seahash", + "tinyvec", + "uuid", ] [[package]] name = "rkyv_derive" -version = "0.7.37" +version = "0.7.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e289706df51226e84814bf6ba1a9e1013112ae29bc7a9878f73fce360520c403" +checksum = "ac1c672430eb41556291981f45ca900a0239ad007242d1cb4b4167af842db666" dependencies = [ "proc-macro2", "quote", @@ -4246,9 +4498,9 @@ dependencies = [ [[package]] name = "rustc-demangle" -version = "0.1.21" +version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ef03e0a2b150c7a90d01faf6254c9c48a41e95fb2a8c2ac1c6f0d2b9aefc342" +checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" [[package]] name = "rustc-hash" @@ -4299,16 +4551,16 @@ dependencies = [ [[package]] name = "rustix" -version = "0.37.3" +version = "0.37.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62b24138615de35e32031d041a09032ef3487a616d901ca4db224e7d557efae2" +checksum = "acf8729d8542766f1b2cf77eb034d52f40d375bb8b615d0b147089946e16613d" dependencies = [ "bitflags 1.3.2", - "errno 0.3.0", + "errno 0.3.1", "io-lifetimes", "libc", - "linux-raw-sys 0.3.0", - "windows-sys 0.45.0", + "linux-raw-sys 0.3.8", + "windows-sys 0.48.0", ] [[package]] @@ -4467,6 +4719,14 @@ dependencies = [ "pest", ] +[[package]] +name = "send-trace-to-jaeger" +version = "0.1.0" +dependencies = [ + "reqwest", + "serde_json", +] + [[package]] name = "sentry" version = "0.27.0" @@ -4549,9 +4809,9 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.158" +version = "1.0.163" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "771d4d9c4163ee138805e12c710dd365e4f44be8be0503cb1bb9eb989425d9c9" +checksum = "2113ab51b87a539ae008b5c6c02dc020ffa39afd2d83cffcb3f4eb2722cebec2" dependencies = [ "serde_derive", ] @@ -4576,22 +4836,32 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_cbor" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bef2ebfde456fb76bbcf9f59315333decc4fda0b2b44b420243c11e0f5ec1f5" +dependencies = [ + "half", + "serde", +] + [[package]] name = "serde_derive" -version = "1.0.158" +version = "1.0.163" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e801c1712f48475582b7696ac71e0ca34ebb30e09338425384269d9717c62cad" +checksum = "8c805777e3930c8883389c602315a24224bcc738b63905ef87cd1420353ea93e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.8", + "syn 2.0.18", ] [[package]] name = "serde_json" -version = "1.0.94" +version = "1.0.96" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c533a59c9d8a93a09c6ab31f0fd5e5f4dd1b8fc9434804029839884765d04ea" +checksum = "057d394a50403bcac12672b2b18fb387ab6d289d957dab67dd201875391e52f1" dependencies = [ "indexmap", "itoa", @@ -4641,6 +4911,46 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_with" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "331bb8c3bf9b92457ab7abecf07078c13f7d270ba490103e84e8b014490cd0b0" +dependencies = [ + "base64 0.13.1", + "chrono", + "hex", + "indexmap", + "serde", + "serde_json", + "serde_with_macros", + "time 0.3.20", +] + +[[package]] +name = "serde_with_macros" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "859011bddcc11f289f07f467cc1fe01c7a941daa4d8f6c40d4d1c92eb6d9319c" +dependencies = [ + "darling", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "serde_yaml" +version = "0.8.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "578a7433b776b56a35785ed5ce9a7e777ac0598aac5a6dd1b4b18a307c7fc71b" +dependencies = [ + "indexmap", + "ryu", + "serde", + "yaml-rust", +] + [[package]] name = "serde_yaml" version = "0.9.19" @@ -4711,6 +5021,15 @@ dependencies = [ "lazy_static", ] +[[package]] +name = "shellexpand" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ccc8076840c4da029af4f87e4e8daeb0fca6b87bbb02e10cb60b791450e11e4" +dependencies = [ + "dirs", +] + [[package]] name = "signal-hook" version = "0.3.15" @@ -4741,6 +5060,12 @@ dependencies = [ "libc", ] +[[package]] +name = "simd-adler32" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "238abfbb77c1915110ad968465608b68e869e0772622c9656714e73e5a1a522f" + [[package]] name = "simdutf8" version = "0.1.4" @@ -4768,6 +5093,12 @@ dependencies = [ "autocfg", ] +[[package]] +name = "slice-group-by" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03b634d87b960ab1a38c4fe143b508576f075e7c978bfad18217645ebfdfa2ec" + [[package]] name = "sluice" version = "0.5.5" @@ -4833,11 +5164,20 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" +dependencies = [ + "lock_api", +] + [[package]] name = "st-map" -version = "0.1.6" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc9c9f3a1df5f73b7392bd9773108fef41ad9126f0282412fd5904389f0c0c4f" +checksum = "f09d891835f076b0d4a58dd4478fb54d47aa3da1f7a4c6e89ad6c791357ab5ed" dependencies = [ "arrayvec", "static-map-macro", @@ -4873,9 +5213,9 @@ dependencies = [ [[package]] name = "static-map-macro" -version = "0.2.3" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "752564de9cd8937fdbc1c55d47ac391758c352ab3755607cc391b659fe87d56b" +checksum = "b862d598fbc9f7085b017890e2e61433f501e7467f2c585323e1aa3c07ef8599" dependencies = [ "pmutil", "proc-macro2", @@ -4966,9 +5306,9 @@ dependencies = [ [[package]] name = "string_enum" -version = "0.3.3" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41491e23e7db79343236a6ced96325ff132eb09e29ac4c5b8132b9c55aaaae89" +checksum = "0090512bdfee4b56d82480d66c0fd8a6f53f0fe0f97e075e949b252acdd482e0" dependencies = [ "pmutil", "proc-macro2", @@ -4985,9 +5325,9 @@ checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" [[package]] name = "styled_components" -version = "0.53.11" +version = "0.57.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5e4d3762e21cd415d838b60b9007e2de112203f096f1c68aef32eb3465ef5d8" +checksum = "bdd3b5976764b6a393329adeae799ccad95416b9a5cd4f9d2076c2737bfd13c3" dependencies = [ "Inflector", "once_cell", @@ -4999,9 +5339,9 @@ dependencies = [ [[package]] name = "styled_jsx" -version = "0.30.11" +version = "0.34.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a95db8d0df1301fa85243cbd0fbe3328a12d1e0d48eac0638d3dc295d4d7811c" +checksum = "9154a21e45febf6553717c0f962749669dcf0febd2880543d35ef3f05ef825c1" dependencies = [ "easy-error", "swc_core", @@ -5010,9 +5350,9 @@ dependencies = [ [[package]] name = "subtle" -version = "2.4.1" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" +checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" [[package]] name = "supports-color" @@ -5044,9 +5384,9 @@ dependencies = [ [[package]] name = "swc" -version = "0.255.23" +version = "0.261.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23d93b381ac343f8548ef10a400aaf91604e94258e5c11753cece061275ed4c1" +checksum = "56428a57e7898982bee4530923a85ba17ad2b555a1502bcdccf6dc7ae483621f" dependencies = [ "ahash", "anyhow", @@ -5096,10 +5436,11 @@ dependencies = [ [[package]] name = "swc_atoms" -version = "0.4.39" +version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ebef84c2948cd0d1ba25acbf1b4bd9d80ab6f057efdbe35d8449b8d54699401" +checksum = "93d0307dc4bfd107d49c7528350c372758cfca94fb503629b9a056e6a1572860" dependencies = [ + "bytecheck", "once_cell", "rkyv", "rustc-hash", @@ -5111,9 +5452,9 @@ dependencies = [ [[package]] name = "swc_bundler" -version = "0.208.19" +version = "0.214.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30d90393e5ac143a687f422f288bc706e3139a862d4c790cf301086aabd0cdf9" +checksum = "1a20b862690e5c6c5e70fc0d56c156175514520c98e89220d408fc0e0d62759a" dependencies = [ "ahash", "anyhow", @@ -5158,15 +5499,16 @@ dependencies = [ [[package]] name = "swc_common" -version = "0.29.37" +version = "0.31.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5005cd73617e18592faa31298225b26f1c407b84a681d67efb735c3d3458e101" +checksum = "19c774005489d2907fb67909cf42af926e72edee1366512777c605ba2ef19c94" dependencies = [ "ahash", "anyhow", "ast_node", "atty", "better_scoped_tls", + "bytecheck", "cfg-if 1.0.0", "either", "from_variant", @@ -5191,9 +5533,9 @@ dependencies = [ [[package]] name = "swc_config" -version = "0.1.4" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4de36224eb9498fccd4e68971f0b83326ccf8592c2d424f257f3a1c76b2b211" +checksum = "89c8fc2c12bb1634c7c32fc3c9b6b963ad8f034cc62c4ecddcf215dc4f6f959d" dependencies = [ "indexmap", "serde", @@ -5203,9 +5545,9 @@ dependencies = [ [[package]] name = "swc_config_macro" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb64bc03d90fd5c90d6ab917bb2b1d7fbd31957df39e31ea24a3f554b4372251" +checksum = "7dadb9998d4f5fc36ef558ed5a092579441579ee8c6fcce84a5228cca9df4004" dependencies = [ "pmutil", "proc-macro2", @@ -5216,9 +5558,9 @@ dependencies = [ [[package]] name = "swc_core" -version = "0.69.23" +version = "0.76.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d13d1df11c7a0c2876ccf36bda91da3686310fb0ab853a22aac5496e02e5e9f" +checksum = "e8cd48cee857b467c1a7f3ff14ec26bfcdbe6a08f3218577e15d8157701da958" dependencies = [ "binding_macros", "swc", @@ -5256,15 +5598,13 @@ dependencies = [ "swc_trace_macro", "testing", "vergen", - "wasmer", - "wasmer-wasi", ] [[package]] name = "swc_css_ast" -version = "0.134.11" +version = "0.137.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b00eeb01472c11945107c881525e6ce89c2596cf7965e51f847a8029916ce7e9" +checksum = "df74aad6d6957b9e9e8b9dde5b4531d0a7f937b79089706bd71f9edcf98e8f34" dependencies = [ "is-macro", "serde", @@ -5275,12 +5615,12 @@ dependencies = [ [[package]] name = "swc_css_codegen" -version = "0.144.14" +version = "0.147.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbfa6ae6065fa3a75c3bd9d4e8747d692a57ab5ac3259c5b3e5811965cce92d4" +checksum = "7d78b141280c45b9f5686774249f37e7e378105da26420ffbf2c3e89c17844cf" dependencies = [ "auto_impl", - "bitflags 1.3.2", + "bitflags 2.2.1", "rustc-hash", "serde", "swc_atoms", @@ -5292,9 +5632,9 @@ dependencies = [ [[package]] name = "swc_css_codegen_macros" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe27425548d11afee43ddbe1d0cd882cb5e042f61b1503651dae2219c92333f5" +checksum = "01c132d9ba562343f7c49d776c4a09b362a4a4104b7cb0a0f7b785986a492e1b" dependencies = [ "pmutil", "proc-macro2", @@ -5305,11 +5645,11 @@ dependencies = [ [[package]] name = "swc_css_compat" -version = "0.20.14" +version = "0.23.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "608c5e294e2fcbea240831e02863c68e765d2508c42cc3bda492a18198e3081f" +checksum = "c25d3a2b9059531a42c1af84ff4d2a4367f5f684038ed13583cfc8fcfbc41602" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.2.1", "once_cell", "serde", "serde_json", @@ -5322,9 +5662,9 @@ dependencies = [ [[package]] name = "swc_css_modules" -version = "0.21.16" +version = "0.25.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d3b768156027bb4f57cefa4eec66d2f2b122eb81937c62f2f820123b7617727" +checksum = "4c4b5030bf022da6406012823ef8bf56ac0b97579e1b2c287960077a98b58fd2" dependencies = [ "rustc-hash", "serde", @@ -5338,11 +5678,11 @@ dependencies = [ [[package]] name = "swc_css_parser" -version = "0.143.12" +version = "0.146.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab4dc464bb7b97db5cb057164af91d1a374bffa48170d67604c7f3158639ed27" +checksum = "1002e94d2650f470c07ce48bbfc72080163480648766a197fc7b436f10eff10f" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.2.1", "lexical", "serde", "swc_atoms", @@ -5352,9 +5692,9 @@ dependencies = [ [[package]] name = "swc_css_prefixer" -version = "0.146.14" +version = "0.149.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97fb14aeb39b8b43c2c84b4ec8ed3d7af419204385621dcd837a9d0cf8da9cbb" +checksum = "ada0aafa05a52012e4409976dd3352f1986701b7b7756946700543b4b2ebec9c" dependencies = [ "once_cell", "preset_env_base", @@ -5369,9 +5709,9 @@ dependencies = [ [[package]] name = "swc_css_utils" -version = "0.131.12" +version = "0.134.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a18df9c717eec8ff9760a27c7337c507a10b23ec301dbc23249dadf7ba78524" +checksum = "b0e46ae5bccba0e45c824bcfcca97bcf125ac76fa9f1e0ad065d51721115f54d" dependencies = [ "once_cell", "serde", @@ -5384,9 +5724,9 @@ dependencies = [ [[package]] name = "swc_css_visit" -version = "0.133.11" +version = "0.136.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc2080e5a67f015365661e5bd26cd7d8cf766395eaa9c4dc95ef58054af624e3" +checksum = "44ec79268934300e8baa5129f39cbf7bbe1399c2122a4d2f2729ef463d22c93e" dependencies = [ "serde", "swc_atoms", @@ -5397,11 +5737,12 @@ dependencies = [ [[package]] name = "swc_ecma_ast" -version = "0.100.3" +version = "0.104.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b135a8de6b20bcc99711a95e6c7c2ffd75e2ce7ef530e67eec4093bd3d063e0" +checksum = "b5cf9dd351d0c285dcd36535267953a18995d4dda0cbe34ac9d1df61aa415b26" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.2.1", + "bytecheck", "is-macro", "num-bigint", "rkyv", @@ -5415,9 +5756,9 @@ dependencies = [ [[package]] name = "swc_ecma_codegen" -version = "0.135.8" +version = "0.139.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5eea38f0aa2bdafb48927cb30a714ad6cc27c17cd40a867ab1f2c0782e6080e6" +checksum = "11c6af8e6d6714ecd7ef5cfba322aa1b436f78d9a82b0c3ff16aeaf97b65cd6d" dependencies = [ "memchr", "num-bigint", @@ -5434,9 +5775,9 @@ dependencies = [ [[package]] name = "swc_ecma_codegen_macros" -version = "0.7.1" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0159c99f81f52e48fe692ef7af1b0990b45d3006b14c6629be0b1ffee1b23aea" +checksum = "bf4ee0caee1018808d94ecd09490cb7affd3d504b19aa11c49238f5fc4b54901" dependencies = [ "pmutil", "proc-macro2", @@ -5447,9 +5788,9 @@ dependencies = [ [[package]] name = "swc_ecma_ext_transforms" -version = "0.99.8" +version = "0.103.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bdc9c335e425617120ec2f2af01c59541571afd7d834b9d7c312faf9d8acc7c4" +checksum = "8d82a08464b92e01068231938c97f2cfb4ec3a0cb3e5cc2fde9215b96ef47234" dependencies = [ "phf", "swc_atoms", @@ -5461,9 +5802,9 @@ dependencies = [ [[package]] name = "swc_ecma_lints" -version = "0.77.13" +version = "0.82.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "463fd2faab68aad3197670627ec7d9a9250cfe28641afa8a8c7aa8d21d6014df" +checksum = "3c3ea8c34004c75da37b7c4f70d1f043f4de455e6a6e78440bfa4d24a3bd201d" dependencies = [ "ahash", "auto_impl", @@ -5482,9 +5823,9 @@ dependencies = [ [[package]] name = "swc_ecma_loader" -version = "0.41.39" +version = "0.43.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "681c1fbb762c82700a5bd23dc39bad892a287ea9fb2121cf56e77f1ddc89afeb" +checksum = "fe45f1e5dcc1b005544ff78253b787dea5dfd5e2f712b133964cdc3545c954a4" dependencies = [ "ahash", "anyhow", @@ -5504,9 +5845,9 @@ dependencies = [ [[package]] name = "swc_ecma_minifier" -version = "0.175.19" +version = "0.181.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89c2cd0cb9f66b75be8ba3ae6122a7989afe0f45af5ea72a1ab6755240e6183c" +checksum = "4006ddfdcdcf8eb3a83f077ae03b6ee5a94bd9d55d52198bbed2228a6acafcbe" dependencies = [ "ahash", "arrayvec", @@ -5540,12 +5881,11 @@ dependencies = [ [[package]] name = "swc_ecma_parser" -version = "0.130.7" +version = "0.134.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b79a4d3b941551a586d2dc06bd05ef654e500ce1e1da2425a3a97b98cecd282b" +checksum = "f0a3fcfe3d83dd445cbd9321882e47b467594433d9a21c4d6c37a27f534bb89e" dependencies = [ "either", - "enum_kind", "lexical", "num-bigint", "serde", @@ -5561,9 +5901,9 @@ dependencies = [ [[package]] name = "swc_ecma_preset_env" -version = "0.189.17" +version = "0.195.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33f5d2be1bdf27dec511d2108c0bc25f0f955a248b2d307b352a45eac7fcf117" +checksum = "cfa9f2862180c733b633572be7d42b690cf2d878280a199a9b5f7cfba2cfd89e" dependencies = [ "ahash", "anyhow", @@ -5586,9 +5926,9 @@ dependencies = [ [[package]] name = "swc_ecma_quote_macros" -version = "0.41.7" +version = "0.45.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bf398b83e9b77ee80fca2bb079cd3495f3d2e1b52ccb7645f1b33b395d6410e" +checksum = "eff371cf036e2c5fb7bf404f7c1a30a22ee4b64d04fb9354a9b057cbd4d59c9b" dependencies = [ "anyhow", "pmutil", @@ -5616,9 +5956,9 @@ dependencies = [ [[package]] name = "swc_ecma_transforms" -version = "0.212.17" +version = "0.218.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad7490393ee05987fe77719bd965ce853f760e20dac1dab53129b8d636dc46c1" +checksum = "1ef09fbed21e8bcdf22bbfd88e5ec83c2acc2c831c39ca2fde01e1f040a85167" dependencies = [ "swc_atoms", "swc_common", @@ -5636,12 +5976,12 @@ dependencies = [ [[package]] name = "swc_ecma_transforms_base" -version = "0.122.13" +version = "0.127.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96253f9d410d18a9aae6c7f59ddc697dd78dcd130f5d1a8750cc5b8f5d71472e" +checksum = "6232e641bef05c462bc7da34a3771f9b3f1f3352349ae0cd72b8eee8b0f5d5e0" dependencies = [ "better_scoped_tls", - "bitflags 1.3.2", + "bitflags 2.2.1", "indexmap", "once_cell", "phf", @@ -5660,9 +6000,9 @@ dependencies = [ [[package]] name = "swc_ecma_transforms_classes" -version = "0.111.13" +version = "0.116.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fc315b53be4d9004134001b46258b32fee64ebc5dd964f72c2b1258324246a7" +checksum = "f086829a3e645382f5609c9c6dce1d29e5204b3c81f82fe8d65d3bf17bcca68b" dependencies = [ "swc_atoms", "swc_common", @@ -5674,9 +6014,9 @@ dependencies = [ [[package]] name = "swc_ecma_transforms_compat" -version = "0.148.15" +version = "0.153.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5a1a4a3c413bfd03e38e8ee9fb9bb761f478ebe4f8b1f51e154375fcc1ca17a" +checksum = "8f1ce5cb1e660b972799a3f28587e01d665981661753cb8f3eb7db89e77dae28" dependencies = [ "ahash", "arrayvec", @@ -5701,9 +6041,9 @@ dependencies = [ [[package]] name = "swc_ecma_transforms_macros" -version = "0.5.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebf907935ec5492256b523ae7935a824d9fdc0368dcadc41375bad0dca91cd8b" +checksum = "984d5ac69b681fc5438f9abf82b0fda34fe04e119bc75f8213b7e01128c7c9a2" dependencies = [ "pmutil", "proc-macro2", @@ -5714,14 +6054,14 @@ dependencies = [ [[package]] name = "swc_ecma_transforms_module" -version = "0.165.15" +version = "0.170.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f53c50506abc0db9a768b190d28dbc6968844d6f6f7fe98967f01bf4c0890ba" +checksum = "fa0dfc1f9d2dfa4a95cb10dea599ddf61506e35f5eafbac25cd14bc33f413a7c" dependencies = [ "Inflector", "ahash", "anyhow", - "bitflags 1.3.2", + "bitflags 2.2.1", "indexmap", "is-macro", "path-clean", @@ -5742,9 +6082,9 @@ dependencies = [ [[package]] name = "swc_ecma_transforms_optimization" -version = "0.181.17" +version = "0.187.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08181f21f6bafb718ef3bed83817545f53af69852550177de19cc20d618a95b7" +checksum = "8d27c12926427f235d149e60f9a9e67a2181fe1eb418c12b53b8e0778c5052a2" dependencies = [ "ahash", "dashmap", @@ -5768,11 +6108,12 @@ dependencies = [ [[package]] name = "swc_ecma_transforms_proposal" -version = "0.156.15" +version = "0.161.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "478c09b747e7829d22f6fe393fb002487133483967d4bd051d9b69a1d5d65a8a" +checksum = "416fbb84f84644ef0e81df80bf44fd575bbb297a78887e359e16a61f6dc5af86" dependencies = [ "either", + "rustc-hash", "serde", "smallvec", "swc_atoms", @@ -5787,9 +6128,9 @@ dependencies = [ [[package]] name = "swc_ecma_transforms_react" -version = "0.167.17" +version = "0.173.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a423b55598ab93ecd4e2a232b9f9a33a0e742b9ba2229a00972ead82bf0a6693" +checksum = "d39a0de45fa34ee797a1c80497c8b9dcb6cf6e56b455c163453399894c58a812" dependencies = [ "ahash", "base64 0.13.1", @@ -5797,7 +6138,6 @@ dependencies = [ "indexmap", "once_cell", "rayon", - "regex", "serde", "sha-1", "string_enum", @@ -5814,9 +6154,9 @@ dependencies = [ [[package]] name = "swc_ecma_transforms_testing" -version = "0.125.13" +version = "0.130.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f07bfbd7b8739ad54b564b2c19476978cd4d48ada980307a20469021c3343938" +checksum = "71e2f974e4f1d78309ea24cce2631a664eb367347c9d2e6677f96c7b8b6b176d" dependencies = [ "ansi_term", "anyhow", @@ -5840,9 +6180,9 @@ dependencies = [ [[package]] name = "swc_ecma_transforms_typescript" -version = "0.171.17" +version = "0.177.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39ba6c548f2b4ad7e1b71c85c4771242a800886547933129f41a7877a5c47332" +checksum = "dd32915040ed6ffdfeafb7a63276529df9ed83d8b6e7a6ce22d95ce98d137a21" dependencies = [ "serde", "swc_atoms", @@ -5856,9 +6196,9 @@ dependencies = [ [[package]] name = "swc_ecma_usage_analyzer" -version = "0.9.9" +version = "0.13.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ab0dcc471e8a980062c21257070ed522f48e77f83e61f2522f8a26f96f6ce89" +checksum = "62aacc5022f52ae332c6545b9248b70285be1847cf85d48b0640d05f68ff971f" dependencies = [ "ahash", "indexmap", @@ -5874,9 +6214,9 @@ dependencies = [ [[package]] name = "swc_ecma_utils" -version = "0.113.8" +version = "0.117.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d422284424a29a95ce5d896ab4f8da35316cd0291e15759c0aae30abd2947be" +checksum = "ad791bbfdafcebd878584021e050964c8ab68aba7eeac9d0ee4afba4c284a629" dependencies = [ "indexmap", "num_cpus", @@ -5893,9 +6233,9 @@ dependencies = [ [[package]] name = "swc_ecma_visit" -version = "0.86.4" +version = "0.90.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2cb3aaa504f9a520cb73e8d361d30aaceeb8643cc2f048e0dc1808d213ef76a9" +checksum = "6ce3ac941ae1d6c7e683aa375fc71fbf58df58b441f614d757fbb10554936ca2" dependencies = [ "num-bigint", "swc_atoms", @@ -5907,9 +6247,9 @@ dependencies = [ [[package]] name = "swc_emotion" -version = "0.29.11" +version = "0.33.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b32d130dc10d63b2f6ccf8d59c693748f0b41ed80ae79df56476f69ac687c9b" +checksum = "d37eface98570bfeb180eb3fd2dcb22c3598af05cae31beac4c06274b8130766" dependencies = [ "base64 0.13.1", "byteorder", @@ -5937,9 +6277,9 @@ dependencies = [ [[package]] name = "swc_error_reporters" -version = "0.13.38" +version = "0.15.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5652942f29f76b08bc2a23228e87c8dff1f037de17d18166753e90f4baacf61" +checksum = "4e4ce9ba211e75848f6aff1c64ee16c71006bd93e45a37f4e149c22625f26d8c" dependencies = [ "anyhow", "miette", @@ -5950,21 +6290,21 @@ dependencies = [ [[package]] name = "swc_fast_graph" -version = "0.17.38" +version = "0.19.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3a720ad8028d6c6e992039c862ed7318d143dee3994929793f59067fd69600b" +checksum = "6291149aec4ba55076fd54a12ceb84cac1f703b2f571c3b2f19aa66ab9ec3009" dependencies = [ - "ahash", "indexmap", "petgraph", + "rustc-hash", "swc_common", ] [[package]] name = "swc_graph_analyzer" -version = "0.18.41" +version = "0.20.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b25ac475500b0776f1bb82da02eff867819b3c653130023ea957cbd1e91befa8" +checksum = "6575adec8b200801d429ffa79166224a6e298292a1b307750f4763aec5aa16c3" dependencies = [ "ahash", "auto_impl", @@ -5975,9 +6315,9 @@ dependencies = [ [[package]] name = "swc_macros_common" -version = "0.3.6" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4be988307882648d9bc7c71a6a73322b7520ef0211e920489a98f8391d8caa2" +checksum = "3e582c3e3c2269238524923781df5be49e011dbe29cf7683a2215d600a562ea6" dependencies = [ "pmutil", "proc-macro2", @@ -5987,9 +6327,9 @@ dependencies = [ [[package]] name = "swc_node_comments" -version = "0.16.37" +version = "0.18.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c762f79bc1f940df95655603298b3ea382765185e091360d7f895475a5437a92" +checksum = "800d308508c0517a04f115c769cf398c23a62bcb8bfa186b7d15c42861b7448a" dependencies = [ "ahash", "dashmap", @@ -5999,9 +6339,9 @@ dependencies = [ [[package]] name = "swc_nodejs_common" -version = "0.0.5" +version = "0.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c00871ef9d32aad437acced2eeffc96a97c5f2776bb90ad6497968a8d626b04" +checksum = "c405e950d345754892691ec2bd30482592672f79db90188392d069a829d3b3ff" dependencies = [ "anyhow", "napi", @@ -6013,9 +6353,9 @@ dependencies = [ [[package]] name = "swc_plugin_proxy" -version = "0.29.3" +version = "0.33.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb64bf10458ef02e97ca7e43b75a3519373f97bf77728c50148799d87a14658c" +checksum = "6fd90309939333beb2cc4fc338b5f7f1aa588173e6452161d5edecfa8210e649" dependencies = [ "better_scoped_tls", "rkyv", @@ -6027,9 +6367,9 @@ dependencies = [ [[package]] name = "swc_plugin_runner" -version = "0.91.7" +version = "0.95.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c790a1870b2f5460f72622ff7a2f72c16597608683e3bfa7feb762bc6392df0" +checksum = "c9d8dc9624bfd27ec09d4dcf56d9e0ed5faaef056629f5d190fc69fde46ec489" dependencies = [ "anyhow", "enumset", @@ -6044,15 +6384,14 @@ dependencies = [ "wasmer", "wasmer-cache", "wasmer-compiler-cranelift", - "wasmer-engine-universal", - "wasmer-wasi", + "wasmer-wasix", ] [[package]] name = "swc_relay" -version = "0.1.1" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1023f06fac2cfb3531b96edfa9e17cb66b1bbd7e91b13c981e72e3486ab310d" +checksum = "ae0d066c10af82a7c3cfcd23d2d7f982291816af20aca01b19a132d8d798b997" dependencies = [ "once_cell", "regex", @@ -6065,9 +6404,9 @@ dependencies = [ [[package]] name = "swc_timer" -version = "0.17.42" +version = "0.19.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d11afada7873b24725061271e1b3e49f2f8f625535fee2b4c55603b6f1a5fa0b" +checksum = "d0dd693178fc91bfac6f380f312f9adcb2dbe753e439ecf929289dfe7a30a893" dependencies = [ "tracing", ] @@ -6085,9 +6424,9 @@ dependencies = [ [[package]] name = "swc_visit" -version = "0.5.4" +version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "470a1963cf182fdcbbac46e3a7fd2caf7329da0e568d3668202da9501c880e16" +checksum = "5f412dd4fbc58f509a04e64f5c8038333142fc139e8232f01b883db0094b3b51" dependencies = [ "either", "swc_visit_macros", @@ -6095,9 +6434,9 @@ dependencies = [ [[package]] name = "swc_visit_macros" -version = "0.5.5" +version = "0.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6098b717cfd4c85f5cddec734af191dbce461c39975ed567c32ac6d0c6d61a6d" +checksum = "4cfc226380ba54a5feed2c12f3ccd33f1ae8e959160290e5d2d9b4e918b6472a" dependencies = [ "Inflector", "pmutil", @@ -6120,9 +6459,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.8" +version = "2.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bcc02725fd69ab9f26eab07fad303e2497fad6fb9eba4f96c4d1687bdf704ad9" +checksum = "32d41677bcbe24c20c52e7c70b0d8db04134c5d1066bf98662e2871ad200ea3e" dependencies = [ "proc-macro2", "quote", @@ -6145,6 +6484,12 @@ dependencies = [ "libc", ] +[[package]] +name = "tap" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" + [[package]] name = "target-lexicon" version = "0.12.6" @@ -6153,15 +6498,25 @@ checksum = "8ae9980cab1db3fceee2f6c6f643d5d8de2997c58ee8d25fb0cc8a9e9e7348e5" [[package]] name = "tempfile" -version = "3.4.0" +version = "3.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af18f7ae1acd354b992402e9ec5864359d693cd8a79dcbef59f76891701c1e95" +checksum = "b9fbec84f381d5795b08656e4912bec604d162bff9291d6189a78f4c8ab87998" dependencies = [ "cfg-if 1.0.0", "fastrand", - "redox_syscall 0.2.16", - "rustix 0.36.11", - "windows-sys 0.42.0", + "redox_syscall 0.3.5", + "rustix 0.37.19", + "windows-sys 0.45.0", +] + +[[package]] +name = "term_size" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e4129646ca0ed8f45d09b929036bafad5377103edd06e50bf574b353d2b08d9" +dependencies = [ + "libc", + "winapi 0.3.9", ] [[package]] @@ -6183,11 +6538,20 @@ dependencies = [ "winapi 0.3.9", ] +[[package]] +name = "termios" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "411c5bf740737c7918b8b1fe232dca4dc9f8e754b8ad5e20966814001ed0ac6b" +dependencies = [ + "libc", +] + [[package]] name = "testing" -version = "0.31.40" +version = "0.33.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dda8d4f62089d08b0575a92273f2c824ca6e3958cd23ad2a0eb3f25438a0e9c9" +checksum = "0901b02da634d6e420bc20716d86c2ee679ee852e126b23b6a478d6c83361956" dependencies = [ "ansi_term", "difference", @@ -6253,7 +6617,7 @@ checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.8", + "syn 2.0.18", ] [[package]] @@ -6375,14 +6739,13 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.26.0" +version = "1.28.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03201d01c3c27a29c8a5cee5b55a93ddae1ccf6f08f65365c2c918f8c1b76f64" +checksum = "94d7b1cfd2aa4011f2de74c2c4c63665e27a71006b0a192dcd2710272e73dfa2" dependencies = [ "autocfg", "bytes", "libc", - "memchr", "mio 0.8.6", "num_cpus", "parking_lot", @@ -6391,7 +6754,7 @@ dependencies = [ "socket2", "tokio-macros", "tracing", - "windows-sys 0.45.0", + "windows-sys 0.48.0", ] [[package]] @@ -6406,13 +6769,13 @@ dependencies = [ [[package]] name = "tokio-macros" -version = "1.8.2" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d266c00fde287f55d3f1c3e96c500c362a2b8c695076ec180f27918820bc6df8" +checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.18", ] [[package]] @@ -6483,12 +6846,29 @@ dependencies = [ ] [[package]] -name = "tonic" -version = "0.8.3" +name = "toml_datetime" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f219fad3b929bef19b1f86fbc0358d35daed8f2cac972037ac0dc10bbb8d5fb" -dependencies = [ - "async-stream", +checksum = "3ab8ed2edee10b50132aed5f331333428b011c99402b5a534154ed15746f9622" + +[[package]] +name = "toml_edit" +version = "0.19.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "239410c8609e8125456927e6707163a3b1fdb40561e4b803bc041f466ccfdc13" +dependencies = [ + "indexmap", + "toml_datetime", + "winnow", +] + +[[package]] +name = "tonic" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f219fad3b929bef19b1f86fbc0358d35daed8f2cac972037ac0dc10bbb8d5fb" +dependencies = [ + "async-stream", "async-trait", "axum", "base64 0.13.1", @@ -6561,13 +6941,13 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.23" +version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4017f8f45139870ca7e672686113917c71c7a6e02d4924eda67186083c03081a" +checksum = "0f57e3ca2a01450b1a921183a9c9cbfda207fd822cef4ccb00a65402cbba7a74" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.18", ] [[package]] @@ -6583,9 +6963,9 @@ dependencies = [ [[package]] name = "tracing-core" -version = "0.1.30" +version = "0.1.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24eb03ba0eab1fd845050058ce5e616558e8f8d8fca633e6b163fe25c797213a" +checksum = "0955b8137a1df6f1a2e9a37d8a6656291ff0297c1a97c24e0d8425fe2312f79a" dependencies = [ "once_cell", "valuable", @@ -6612,22 +6992,35 @@ dependencies = [ "tracing-core", ] +[[package]] +name = "tracing-serde" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc6b213177105856957181934e4920de57730fc69bf42c37ee5bb664d406d9e1" +dependencies = [ + "serde", + "tracing-core", +] + [[package]] name = "tracing-subscriber" -version = "0.3.16" +version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6176eae26dd70d0c919749377897b54a9276bd7061339665dd68777926b5a70" +checksum = "30a651bc37f915e81f087d86e62a18eec5f79550c7faff886f7090b4ea757c77" dependencies = [ "matchers", "nu-ansi-term", "once_cell", "regex", + "serde", + "serde_json", "sharded-slab", "smallvec", "thread_local", "tracing", "tracing-core", "tracing-log", + "tracing-serde", ] [[package]] @@ -6684,55 +7077,10 @@ dependencies = [ "utf-8", ] -[[package]] -name = "turbo-binding" -version = "0.1.0" -source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230331.3#1414a695ace2fb52a204fdee374be4dd0a98c8f5" -dependencies = [ - "auto-hash-map", - "mdxjs", - "modularize_imports", - "node-file-trace", - "styled_components", - "styled_jsx", - "swc_core", - "swc_emotion", - "swc_relay", - "testing", - "turbo-malloc", - "turbo-tasks", - "turbo-tasks-build", - "turbo-tasks-bytes", - "turbo-tasks-env", - "turbo-tasks-fetch", - "turbo-tasks-fs", - "turbo-tasks-hash", - "turbo-tasks-memory", - "turbo-tasks-testing", - "turbopack", - "turbopack-cli-utils", - "turbopack-core", - "turbopack-create-test-app", - "turbopack-dev", - "turbopack-dev-server", - "turbopack-ecmascript", - "turbopack-env", - "turbopack-node", - "turbopack-test-utils", -] - -[[package]] -name = "turbo-malloc" -version = "0.1.0" -source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230331.3#1414a695ace2fb52a204fdee374be4dd0a98c8f5" -dependencies = [ - "mimalloc", -] - [[package]] name = "turbo-tasks" version = "0.1.0" -source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230331.3#1414a695ace2fb52a204fdee374be4dd0a98c8f5" +source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230608.1#a00ba87b486db48038e1d6e74775a29b920ef03b" dependencies = [ "anyhow", "auto-hash-map", @@ -6754,6 +7102,7 @@ dependencies = [ "stable_deref_trait", "thiserror", "tokio", + "tracing", "turbo-tasks-build", "turbo-tasks-hash", "turbo-tasks-macros", @@ -6762,7 +7111,7 @@ dependencies = [ [[package]] name = "turbo-tasks-build" version = "0.1.0" -source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230331.3#1414a695ace2fb52a204fdee374be4dd0a98c8f5" +source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230608.1#a00ba87b486db48038e1d6e74775a29b920ef03b" dependencies = [ "anyhow", "cargo-lock", @@ -6774,7 +7123,7 @@ dependencies = [ [[package]] name = "turbo-tasks-bytes" version = "0.1.0" -source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230331.3#1414a695ace2fb52a204fdee374be4dd0a98c8f5" +source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230608.1#a00ba87b486db48038e1d6e74775a29b920ef03b" dependencies = [ "anyhow", "bytes", @@ -6789,7 +7138,7 @@ dependencies = [ [[package]] name = "turbo-tasks-env" version = "0.1.0" -source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230331.3#1414a695ace2fb52a204fdee374be4dd0a98c8f5" +source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230608.1#a00ba87b486db48038e1d6e74775a29b920ef03b" dependencies = [ "anyhow", "dotenvy", @@ -6803,7 +7152,7 @@ dependencies = [ [[package]] name = "turbo-tasks-fetch" version = "0.1.0" -source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230331.3#1414a695ace2fb52a204fdee374be4dd0a98c8f5" +source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230608.1#a00ba87b486db48038e1d6e74775a29b920ef03b" dependencies = [ "anyhow", "indexmap", @@ -6820,7 +7169,7 @@ dependencies = [ [[package]] name = "turbo-tasks-fs" version = "0.1.0" -source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230331.3#1414a695ace2fb52a204fdee374be4dd0a98c8f5" +source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230608.1#a00ba87b486db48038e1d6e74775a29b920ef03b" dependencies = [ "anyhow", "auto-hash-map", @@ -6841,6 +7190,7 @@ dependencies = [ "serde_json", "serde_path_to_error", "tokio", + "tracing", "turbo-tasks", "turbo-tasks-build", "turbo-tasks-hash", @@ -6849,7 +7199,7 @@ dependencies = [ [[package]] name = "turbo-tasks-hash" version = "0.1.0" -source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230331.3#1414a695ace2fb52a204fdee374be4dd0a98c8f5" +source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230608.1#a00ba87b486db48038e1d6e74775a29b920ef03b" dependencies = [ "base16", "hex", @@ -6861,7 +7211,7 @@ dependencies = [ [[package]] name = "turbo-tasks-macros" version = "0.1.0" -source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230331.3#1414a695ace2fb52a204fdee374be4dd0a98c8f5" +source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230608.1#a00ba87b486db48038e1d6e74775a29b920ef03b" dependencies = [ "anyhow", "convert_case 0.6.0", @@ -6875,17 +7225,25 @@ dependencies = [ [[package]] name = "turbo-tasks-macros-shared" version = "0.1.0" -source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230331.3#1414a695ace2fb52a204fdee374be4dd0a98c8f5" +source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230608.1#a00ba87b486db48038e1d6e74775a29b920ef03b" dependencies = [ "proc-macro2", "quote", "syn 1.0.109", ] +[[package]] +name = "turbo-tasks-malloc" +version = "0.1.0" +source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230608.1#a00ba87b486db48038e1d6e74775a29b920ef03b" +dependencies = [ + "mimalloc", +] + [[package]] name = "turbo-tasks-memory" version = "0.1.0" -source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230331.3#1414a695ace2fb52a204fdee374be4dd0a98c8f5" +source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230608.1#a00ba87b486db48038e1d6e74775a29b920ef03b" dependencies = [ "anyhow", "auto-hash-map", @@ -6898,16 +7256,17 @@ dependencies = [ "priority-queue", "rustc-hash", "tokio", - "turbo-malloc", + "tracing", "turbo-tasks", "turbo-tasks-build", "turbo-tasks-hash", + "turbo-tasks-malloc", ] [[package]] name = "turbo-tasks-testing" version = "0.1.0" -source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230331.3#1414a695ace2fb52a204fdee374be4dd0a98c8f5" +source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230608.1#a00ba87b486db48038e1d6e74775a29b920ef03b" dependencies = [ "anyhow", "auto-hash-map", @@ -6919,10 +7278,11 @@ dependencies = [ [[package]] name = "turbopack" version = "0.1.0" -source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230331.3#1414a695ace2fb52a204fdee374be4dd0a98c8f5" +source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230608.1#a00ba87b486db48038e1d6e74775a29b920ef03b" dependencies = [ "anyhow", "async-recursion", + "futures", "indexmap", "lazy_static", "regex", @@ -6931,37 +7291,119 @@ dependencies = [ "tokio", "turbo-tasks", "turbo-tasks-build", + "turbo-tasks-env", "turbo-tasks-fs", "turbopack-core", "turbopack-css", "turbopack-ecmascript", + "turbopack-ecmascript-plugins", "turbopack-env", + "turbopack-image", "turbopack-json", "turbopack-mdx", "turbopack-node", "turbopack-static", ] +[[package]] +name = "turbopack-bench" +version = "0.1.0" +source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230608.1#a00ba87b486db48038e1d6e74775a29b920ef03b" +dependencies = [ + "anyhow", + "chromiumoxide", + "criterion", + "dunce", + "futures", + "mime", + "nix", + "once_cell", + "owo-colors", + "parking_lot", + "portpicker", + "rand", + "regex", + "serde", + "serde_json", + "tempfile", + "tokio", + "tungstenite 0.17.3", + "turbo-tasks", + "turbo-tasks-testing", + "turbopack-create-test-app", + "url", + "webbrowser", +] + +[[package]] +name = "turbopack-binding" +version = "0.1.0" +source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230608.1#a00ba87b486db48038e1d6e74775a29b920ef03b" +dependencies = [ + "auto-hash-map", + "mdxjs", + "modularize_imports", + "node-file-trace", + "styled_components", + "styled_jsx", + "swc_core", + "swc_emotion", + "swc_relay", + "testing", + "turbo-tasks", + "turbo-tasks-build", + "turbo-tasks-bytes", + "turbo-tasks-env", + "turbo-tasks-fetch", + "turbo-tasks-fs", + "turbo-tasks-hash", + "turbo-tasks-malloc", + "turbo-tasks-memory", + "turbo-tasks-testing", + "turbopack", + "turbopack-bench", + "turbopack-cli-utils", + "turbopack-core", + "turbopack-dev", + "turbopack-dev-server", + "turbopack-ecmascript", + "turbopack-ecmascript-plugins", + "turbopack-ecmascript-runtime", + "turbopack-env", + "turbopack-image", + "turbopack-node", + "turbopack-static", + "turbopack-test-utils", +] + [[package]] name = "turbopack-cli-utils" version = "0.1.0" -source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230331.3#1414a695ace2fb52a204fdee374be4dd0a98c8f5" +source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230608.1#a00ba87b486db48038e1d6e74775a29b920ef03b" dependencies = [ "anyhow", "clap 4.1.11", + "crossbeam-channel", "crossterm", + "ctrlc", + "once_cell", "owo-colors", + "postcard", "serde", + "serde_json", + "tracing", + "tracing-subscriber", "turbo-tasks", "turbo-tasks-build", "turbo-tasks-fs", "turbopack-core", + "turbopack-ecmascript", ] [[package]] name = "turbopack-core" version = "0.1.0" -source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230331.3#1414a695ace2fb52a204fdee374be4dd0a98c8f5" +source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230608.1#a00ba87b486db48038e1d6e74775a29b920ef03b" dependencies = [ "anyhow", "async-trait", @@ -6978,6 +7420,7 @@ dependencies = [ "serde_qs", "sourcemap", "swc_core", + "tracing", "turbo-tasks", "turbo-tasks-build", "turbo-tasks-env", @@ -6988,7 +7431,7 @@ dependencies = [ [[package]] name = "turbopack-create-test-app" version = "0.1.0" -source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230331.3#1414a695ace2fb52a204fdee374be4dd0a98c8f5" +source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230608.1#a00ba87b486db48038e1d6e74775a29b920ef03b" dependencies = [ "anyhow", "clap 4.1.11", @@ -7001,7 +7444,7 @@ dependencies = [ [[package]] name = "turbopack-css" version = "0.1.0" -source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230331.3#1414a695ace2fb52a204fdee374be4dd0a98c8f5" +source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230608.1#a00ba87b486db48038e1d6e74775a29b920ef03b" dependencies = [ "anyhow", "async-trait", @@ -7023,7 +7466,7 @@ dependencies = [ [[package]] name = "turbopack-dev" version = "0.1.0" -source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230331.3#1414a695ace2fb52a204fdee374be4dd0a98c8f5" +source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230608.1#a00ba87b486db48038e1d6e74775a29b920ef03b" dependencies = [ "anyhow", "indexmap", @@ -7031,18 +7474,23 @@ dependencies = [ "serde", "serde_json", "serde_qs", + "swc_core", + "tracing", "turbo-tasks", "turbo-tasks-build", "turbo-tasks-fs", "turbo-tasks-hash", + "turbopack", "turbopack-core", + "turbopack-css", "turbopack-ecmascript", + "turbopack-ecmascript-runtime", ] [[package]] name = "turbopack-dev-server" version = "0.1.0" -source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230331.3#1414a695ace2fb52a204fdee374be4dd0a98c8f5" +source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230608.1#a00ba87b486db48038e1d6e74775a29b920ef03b" dependencies = [ "anyhow", "async-compression", @@ -7058,9 +7506,11 @@ dependencies = [ "serde", "serde_json", "serde_qs", + "socket2", "tokio", "tokio-stream", "tokio-util", + "tracing", "turbo-tasks", "turbo-tasks-build", "turbo-tasks-bytes", @@ -7075,7 +7525,7 @@ dependencies = [ [[package]] name = "turbopack-ecmascript" version = "0.1.0" -source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230331.3#1414a695ace2fb52a204fdee374be4dd0a98c8f5" +source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230608.1#a00ba87b486db48038e1d6e74775a29b920ef03b" dependencies = [ "anyhow", "async-trait", @@ -7093,10 +7543,7 @@ dependencies = [ "serde", "serde_json", "serde_qs", - "styled_components", - "styled_jsx", "swc_core", - "swc_emotion", "tokio", "tracing", "turbo-tasks", @@ -7108,12 +7555,53 @@ dependencies = [ "url", ] +[[package]] +name = "turbopack-ecmascript-plugins" +version = "0.1.0" +source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230608.1#a00ba87b486db48038e1d6e74775a29b920ef03b" +dependencies = [ + "anyhow", + "async-trait", + "indexmap", + "modularize_imports", + "serde", + "serde_json", + "styled_components", + "styled_jsx", + "swc_core", + "swc_emotion", + "swc_relay", + "turbo-tasks", + "turbo-tasks-build", + "turbo-tasks-fs", + "turbopack-core", + "turbopack-ecmascript", +] + +[[package]] +name = "turbopack-ecmascript-runtime" +version = "0.1.0" +source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230608.1#a00ba87b486db48038e1d6e74775a29b920ef03b" +dependencies = [ + "anyhow", + "indoc", + "serde", + "swc_core", + "turbo-tasks", + "turbo-tasks-build", + "turbo-tasks-fs", + "turbopack", + "turbopack-core", + "turbopack-ecmascript", +] + [[package]] name = "turbopack-env" version = "0.1.0" -source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230331.3#1414a695ace2fb52a204fdee374be4dd0a98c8f5" +source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230608.1#a00ba87b486db48038e1d6e74775a29b920ef03b" dependencies = [ "anyhow", + "indexmap", "serde", "turbo-tasks", "turbo-tasks-build", @@ -7123,10 +7611,30 @@ dependencies = [ "turbopack-ecmascript", ] +[[package]] +name = "turbopack-image" +version = "0.1.0" +source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230608.1#a00ba87b486db48038e1d6e74775a29b920ef03b" +dependencies = [ + "anyhow", + "base64 0.21.0", + "image", + "indexmap", + "mime", + "once_cell", + "regex", + "serde", + "serde_with", + "turbo-tasks", + "turbo-tasks-build", + "turbo-tasks-fs", + "turbopack-core", +] + [[package]] name = "turbopack-json" version = "0.1.0" -source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230331.3#1414a695ace2fb52a204fdee374be4dd0a98c8f5" +source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230608.1#a00ba87b486db48038e1d6e74775a29b920ef03b" dependencies = [ "anyhow", "serde", @@ -7141,7 +7649,7 @@ dependencies = [ [[package]] name = "turbopack-mdx" version = "0.1.0" -source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230331.3#1414a695ace2fb52a204fdee374be4dd0a98c8f5" +source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230608.1#a00ba87b486db48038e1d6e74775a29b920ef03b" dependencies = [ "anyhow", "mdxjs", @@ -7156,7 +7664,7 @@ dependencies = [ [[package]] name = "turbopack-node" version = "0.1.0" -source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230331.3#1414a695ace2fb52a204fdee374be4dd0a98c8f5" +source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230608.1#a00ba87b486db48038e1d6e74775a29b920ef03b" dependencies = [ "anyhow", "async-stream", @@ -7174,12 +7682,12 @@ dependencies = [ "serde_json", "serde_qs", "tokio", + "tracing", "turbo-tasks", "turbo-tasks-build", "turbo-tasks-bytes", "turbo-tasks-env", "turbo-tasks-fs", - "turbo-tasks-hash", "turbopack-cli-utils", "turbopack-core", "turbopack-dev-server", @@ -7191,7 +7699,7 @@ dependencies = [ [[package]] name = "turbopack-static" version = "0.1.0" -source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230331.3#1414a695ace2fb52a204fdee374be4dd0a98c8f5" +source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230608.1#a00ba87b486db48038e1d6e74775a29b920ef03b" dependencies = [ "anyhow", "serde", @@ -7207,7 +7715,7 @@ dependencies = [ [[package]] name = "turbopack-swc-utils" version = "0.1.0" -source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230331.3#1414a695ace2fb52a204fdee374be4dd0a98c8f5" +source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230608.1#a00ba87b486db48038e1d6e74775a29b920ef03b" dependencies = [ "swc_core", "turbo-tasks", @@ -7218,7 +7726,7 @@ dependencies = [ [[package]] name = "turbopack-test-utils" version = "0.1.0" -source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230331.3#1414a695ace2fb52a204fdee374be4dd0a98c8f5" +source = "git+https://github.com/vercel/turbo.git?tag=turbopack-230608.1#a00ba87b486db48038e1d6e74775a29b920ef03b" dependencies = [ "anyhow", "once_cell", @@ -7303,9 +7811,9 @@ checksum = "d70b6494226b36008c8366c288d77190b3fad2eb4c10533139c1c1f461127f1a" [[package]] name = "unicode-ident" -version = "1.0.8" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4" +checksum = "b15811caf2415fb889178633e7724bad2509101cde276048e013b9def5e51fa0" [[package]] name = "unicode-joining-type" @@ -7319,7 +7827,7 @@ version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c5faade31a542b8b35855fff6e8def199853b2da8da256da52f52f1316ee3137" dependencies = [ - "hashbrown 0.12.3", + "hashbrown", "regex", ] @@ -7388,9 +7896,9 @@ checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" [[package]] name = "uuid" -version = "1.3.0" +version = "1.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1674845326ee10d37ca60470760d4288a6f80f304007d92e5c53bab78c9cfd79" +checksum = "345444e32442451b267fc254ae85a209c64be56d2890e601a0c37ff0c3c5ecd2" dependencies = [ "getrandom", "serde", @@ -7404,13 +7912,9 @@ checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" [[package]] name = "value-bag" -version = "1.0.0-alpha.9" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2209b78d1249f7e6f3293657c9779fe31ced465df091bbd433a1cf88e916ec55" -dependencies = [ - "ctor", - "version_check", -] +checksum = "a4d330786735ea358f3bc09eea4caa098569c1c93f342d9aca0514915022fe7e" [[package]] name = "vcpkg" @@ -7440,29 +7944,169 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" [[package]] -name = "waker-fn" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d5b2c62b4012a3e1eca5a7e077d13b3bf498c4073e33ccd58626607748ceeca" - -[[package]] -name = "walkdir" -version = "2.3.3" +name = "virtual-fs" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36df944cda56c7d8d8b7496af378e6b16de9284591917d307c9b4d313c44e698" +checksum = "1ba2b45886b577c5a11b5d3165b0410620ff508b0acfa91e5b024935de792e8e" dependencies = [ - "same-file", - "winapi-util", + "anyhow", + "async-trait", + "bytes", + "derivative", + "filetime", + "fs_extra", + "getrandom", + "indexmap", + "lazy_static", + "libc", + "pin-project-lite", + "slab", + "thiserror", + "tokio", + "tracing", + "webc", ] [[package]] -name = "want" -version = "0.3.0" +name = "virtual-net" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" +checksum = "e043eb813b35633445d602acf13df921a8a1ac8833818fb8f891e2f6223fedd7" dependencies = [ - "log", - "try-lock", + "async-trait", + "bytes", + "thiserror", + "tracing", +] + +[[package]] +name = "wai-bindgen-gen-core" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1aa3dc41b510811122b3088197234c27e08fcad63ef936306dd8e11e2803876c" +dependencies = [ + "anyhow", + "wai-parser", +] + +[[package]] +name = "wai-bindgen-gen-rust" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19bc05e8380515c4337c40ef03b2ff233e391315b178a320de8640703d522efe" +dependencies = [ + "heck 0.3.3", + "wai-bindgen-gen-core", +] + +[[package]] +name = "wai-bindgen-gen-rust-wasm" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6f35ce5e74086fac87f3a7bd50f643f00fe3559adb75c88521ecaa01c8a6199" +dependencies = [ + "heck 0.3.3", + "wai-bindgen-gen-core", + "wai-bindgen-gen-rust", +] + +[[package]] +name = "wai-bindgen-gen-wasmer" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f61484185d8c520a86d5a7f7f8265f446617c2f9774b2e20a52de19b6e53432" +dependencies = [ + "heck 0.3.3", + "wai-bindgen-gen-core", + "wai-bindgen-gen-rust", +] + +[[package]] +name = "wai-bindgen-rust" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e5601c6f448c063e83a5e931b8fefcdf7e01ada424ad42372c948d2e3d67741" +dependencies = [ + "bitflags 1.3.2", + "wai-bindgen-rust-impl", +] + +[[package]] +name = "wai-bindgen-rust-impl" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bdeeb5c1170246de8425a3e123e7ef260dc05ba2b522a1d369fe2315376efea4" +dependencies = [ + "proc-macro2", + "syn 1.0.109", + "wai-bindgen-gen-core", + "wai-bindgen-gen-rust-wasm", +] + +[[package]] +name = "wai-bindgen-wasmer" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e1e0eda6f3b18f1b630eabc3d82b3b8ca74749b89e73b7bba0999726ebfae04" +dependencies = [ + "anyhow", + "bitflags 1.3.2", + "once_cell", + "thiserror", + "tracing", + "wai-bindgen-wasmer-impl", + "wasmer", +] + +[[package]] +name = "wai-bindgen-wasmer-impl" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b3488ed88d4dd0e3bf85bad4e27dac6cb31aae5d122a5dda2424803c8dc863a" +dependencies = [ + "proc-macro2", + "syn 1.0.109", + "wai-bindgen-gen-core", + "wai-bindgen-gen-wasmer", +] + +[[package]] +name = "wai-parser" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9bd0acb6d70885ea0c343749019ba74f015f64a9d30542e66db69b49b7e28186" +dependencies = [ + "anyhow", + "id-arena", + "pulldown-cmark", + "unicode-normalization", + "unicode-xid", +] + +[[package]] +name = "waker-fn" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d5b2c62b4012a3e1eca5a7e077d13b3bf498c4073e33ccd58626607748ceeca" + +[[package]] +name = "walkdir" +version = "2.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36df944cda56c7d8d8b7496af378e6b16de9284591917d307c9b4d313c44e698" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] +name = "want" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" +dependencies = [ + "log", + "try-lock", ] [[package]] @@ -7492,8 +8136,9 @@ dependencies = [ "serde", "serde-wasm-bindgen", "serde_json", + "swc_core", "tracing", - "turbo-binding", + "turbopack-binding", "wasm-bindgen", "wasm-bindgen-futures", ] @@ -7523,6 +8168,29 @@ dependencies = [ "wasm-bindgen-shared", ] +[[package]] +name = "wasm-bindgen-downcast" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5dac026d43bcca6e7ce1c0956ba68f59edf6403e8e930a5d891be72c31a44340" +dependencies = [ + "js-sys", + "once_cell", + "wasm-bindgen", + "wasm-bindgen-downcast-macros", +] + +[[package]] +name = "wasm-bindgen-downcast-macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5020cfa87c7cecefef118055d44e3c1fc122c7ec25701d528ee458a0b45f38f" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "wasm-bindgen-futures" version = "0.4.34" @@ -7575,50 +8243,39 @@ dependencies = [ [[package]] name = "wasmer" -version = "2.3.0" +version = "3.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea8d8361c9d006ea3d7797de7bd6b1492ffd0f91a22430cfda6c1658ad57bedf" +checksum = "78caedecd8cb71ed47ccca03b68d69414a3d278bb031e6f93f15759344efdd52" dependencies = [ + "bytes", "cfg-if 1.0.0", + "derivative", "indexmap", "js-sys", - "loupe", "more-asserts", + "rustc-demangle", + "serde", + "serde-wasm-bindgen", "target-lexicon", "thiserror", "wasm-bindgen", - "wasmer-artifact", + "wasm-bindgen-downcast", "wasmer-compiler", "wasmer-compiler-cranelift", "wasmer-derive", - "wasmer-engine", - "wasmer-engine-dylib", - "wasmer-engine-universal", "wasmer-types", "wasmer-vm", - "wasmparser", + "wasmparser 0.83.0", + "wasmparser 0.95.0", "wat", "winapi 0.3.9", ] -[[package]] -name = "wasmer-artifact" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7aaf9428c29c1d8ad2ac0e45889ba8a568a835e33fd058964e5e500f2f7ce325" -dependencies = [ - "enumset", - "loupe", - "thiserror", - "wasmer-compiler", - "wasmer-types", -] - [[package]] name = "wasmer-cache" -version = "2.3.0" +version = "3.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0def391ee1631deac5ac1e6ce919c07a5ccb936ad0fd44708cdc2365c49561a4" +checksum = "7f0de969b05cc3c11196beeb46e5868a3712a187d777ee94113f7258c2ec121c" dependencies = [ "blake3", "hex", @@ -7628,33 +8285,37 @@ dependencies = [ [[package]] name = "wasmer-compiler" -version = "2.3.0" +version = "3.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e67a6cd866aed456656db2cfea96c18baabbd33f676578482b85c51e1ee19d2c" +checksum = "726a8450541af4a57c34af7b6973fdbfc79f896cc7e733429577dfd1d1687180" dependencies = [ + "backtrace", + "cfg-if 1.0.0", + "enum-iterator 0.7.0", "enumset", - "loupe", - "rkyv", - "serde", - "serde_bytes", + "lazy_static", + "leb128", + "memmap2", + "more-asserts", + "region", "smallvec", - "target-lexicon", "thiserror", "wasmer-types", - "wasmparser", + "wasmer-vm", + "wasmparser 0.95.0", + "winapi 0.3.9", ] [[package]] name = "wasmer-compiler-cranelift" -version = "2.3.0" +version = "3.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48be2f9f6495f08649e4f8b946a2cbbe119faf5a654aa1457f9504a99d23dae0" +checksum = "a1e5633f90f372563ebbdf3f9799c7b29ba11c90e56cf9b54017112d2e656c95" dependencies = [ "cranelift-codegen", "cranelift-entity", "cranelift-frontend", "gimli 0.26.2", - "loupe", "more-asserts", "rayon", "smallvec", @@ -7666,9 +8327,9 @@ dependencies = [ [[package]] name = "wasmer-derive" -version = "2.3.0" +version = "3.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00e50405cc2a2f74ff574584710a5f2c1d5c93744acce2ca0866084739284b51" +checksum = "97901fdbaae383dbb90ea162cc3a76a9fa58ac39aec7948b4c0b9bbef9307738" dependencies = [ "proc-macro-error", "proc-macro2", @@ -7676,186 +8337,118 @@ dependencies = [ "syn 1.0.109", ] -[[package]] -name = "wasmer-engine" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f98f010978c244db431b392aeab0661df7ea0822343334f8f2a920763548e45" -dependencies = [ - "backtrace", - "enumset", - "lazy_static", - "loupe", - "memmap2", - "more-asserts", - "rustc-demangle", - "serde", - "serde_bytes", - "target-lexicon", - "thiserror", - "wasmer-artifact", - "wasmer-compiler", - "wasmer-types", - "wasmer-vm", -] - -[[package]] -name = "wasmer-engine-dylib" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad0358af9c154724587731175553805648d9acb8f6657880d165e378672b7e53" -dependencies = [ - "cfg-if 1.0.0", - "enum-iterator 0.7.0", - "enumset", - "leb128", - "libloading", - "loupe", - "object 0.28.4", - "rkyv", - "serde", - "tempfile", - "tracing", - "wasmer-artifact", - "wasmer-compiler", - "wasmer-engine", - "wasmer-object", - "wasmer-types", - "wasmer-vm", - "which", -] - -[[package]] -name = "wasmer-engine-universal" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "440dc3d93c9ca47865a4f4edd037ea81bf983b5796b59b3d712d844b32dbef15" -dependencies = [ - "cfg-if 1.0.0", - "enumset", - "leb128", - "loupe", - "region", - "rkyv", - "wasmer-compiler", - "wasmer-engine", - "wasmer-engine-universal-artifact", - "wasmer-types", - "wasmer-vm", - "winapi 0.3.9", -] - -[[package]] -name = "wasmer-engine-universal-artifact" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68f1db3f54152657eb6e86c44b66525ff7801dad8328fe677da48dd06af9ad41" -dependencies = [ - "enum-iterator 0.7.0", - "enumset", - "loupe", - "rkyv", - "thiserror", - "wasmer-artifact", - "wasmer-compiler", - "wasmer-types", -] - -[[package]] -name = "wasmer-object" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d831335ff3a44ecf451303f6f891175c642488036b92ceceb24ac8623a8fa8b" -dependencies = [ - "object 0.28.4", - "thiserror", - "wasmer-compiler", - "wasmer-types", -] - [[package]] name = "wasmer-types" -version = "2.3.0" +version = "3.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39df01ea05dc0a9bab67e054c7cb01521e53b35a7bb90bd02eca564ed0b2667f" +checksum = "67f1f2839f4f61509550e4ddcd0e658e19f3af862b51c79fda15549d735d659b" dependencies = [ - "backtrace", + "bytecheck", "enum-iterator 0.7.0", + "enumset", "indexmap", - "loupe", "more-asserts", "rkyv", - "serde", - "thiserror", -] - -[[package]] -name = "wasmer-vfs" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9302eae3edc53cb540c2d681e7f16d8274918c1ce207591f04fed351649e97c0" -dependencies = [ - "libc", - "slab", + "target-lexicon", "thiserror", - "tracing", ] [[package]] name = "wasmer-vm" -version = "2.3.0" +version = "3.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30d965fa61f4dc4cdb35a54daaf7ecec3563fbb94154a6c35433f879466247dd" +checksum = "043118ec4f16d1714fed3aab758b502b864bd865e1d5188626c9ad290100563f" dependencies = [ "backtrace", "cc", "cfg-if 1.0.0", "corosensei", + "dashmap", + "derivative", "enum-iterator 0.7.0", + "fnv", "indexmap", "lazy_static", "libc", - "loupe", "mach", - "memoffset 0.6.5", + "memoffset 0.8.0", "more-asserts", "region", - "rkyv", "scopeguard", - "serde", "thiserror", - "wasmer-artifact", "wasmer-types", "winapi 0.3.9", ] [[package]] -name = "wasmer-wasi" -version = "2.3.0" +name = "wasmer-wasix" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fadbe31e3c1b6f3e398ad172b169152ae1a743ae6efd5f9ffb34019983319d99" +checksum = "c216facb6a1aae257e38f2018a27b270765aa9d386166e28afecd4004c306cbc" dependencies = [ + "anyhow", + "async-trait", + "bincode", + "bytes", "cfg-if 1.0.0", - "generational-arena", + "chrono", + "cooked-waker", + "derivative", + "futures", "getrandom", + "heapless", + "hex", + "http", + "lazy_static", "libc", + "linked_hash_set", + "once_cell", + "pin-project", + "rand", + "serde", + "serde_derive", + "serde_json", + "serde_yaml 0.8.26", + "sha2", + "shellexpand", + "term_size", + "termios", "thiserror", + "tokio", "tracing", + "urlencoding", + "virtual-fs", + "virtual-net", + "wai-bindgen-wasmer", + "waker-fn", "wasm-bindgen", "wasmer", - "wasmer-vfs", - "wasmer-wasi-types", + "wasmer-types", + "wasmer-wasix-types", + "webc", + "weezl", "winapi 0.3.9", ] [[package]] -name = "wasmer-wasi-types" -version = "2.3.0" +name = "wasmer-wasix-types" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22dc83aadbdf97388de3211cb6f105374f245a3cf2a5c65a16776e7a087a8468" +checksum = "a34aaac6706d29f89a771f2a58bd7e93628ef65344a39d993bdd717c62aafc27" dependencies = [ + "anyhow", + "bitflags 1.3.2", "byteorder", + "cfg-if 1.0.0", + "num_enum", "time 0.2.27", + "wai-bindgen-gen-core", + "wai-bindgen-gen-rust", + "wai-bindgen-gen-rust-wasm", + "wai-bindgen-rust", + "wai-parser", + "wasmer", + "wasmer-derive", "wasmer-types", ] @@ -7865,11 +8458,21 @@ version = "0.83.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "718ed7c55c2add6548cca3ddd6383d738cd73b892df400e96b9aa876f0141d7a" +[[package]] +name = "wasmparser" +version = "0.95.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2ea896273ea99b15132414be1da01ab0d8836415083298ecaffbe308eaac87a" +dependencies = [ + "indexmap", + "url", +] + [[package]] name = "wast" -version = "55.0.0" +version = "56.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4984d3e1406571f4930ba5cf79bd70f75f41d0e87e17506e0bd19b0e5d085f05" +checksum = "6b54185c051d7bbe23757d50fe575880a2426a2f06d2e9f6a10fd9a4a42920c0" dependencies = [ "leb128", "memchr", @@ -7879,9 +8482,9 @@ dependencies = [ [[package]] name = "wat" -version = "1.0.61" +version = "1.0.62" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af2b53f4da14db05d32e70e9c617abdf6620c575bd5dd972b7400037b4df2091" +checksum = "56681922808216ab86d96bb750f70d500b5a7800e41564290fd46bb773581299" dependencies = [ "wast", ] @@ -7913,6 +8516,32 @@ dependencies = [ "web-sys", ] +[[package]] +name = "webc" +version = "5.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06bee486f9207604f99bfa3c95afcd03272d95db5872c6c1b11470be4390d514" +dependencies = [ + "anyhow", + "base64 0.21.0", + "byteorder", + "bytes", + "indexmap", + "leb128", + "lexical-sort", + "memmap2", + "once_cell", + "path-clean", + "rand", + "serde", + "serde_cbor", + "serde_json", + "sha2", + "thiserror", + "url", + "walkdir", +] + [[package]] name = "webpki" version = "0.22.0" @@ -7932,6 +8561,12 @@ dependencies = [ "webpki", ] +[[package]] +name = "weezl" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9193164d4de03a926d909d3bc7c30543cecb35400c02114792c2cae20d5e2dbb" + [[package]] name = "which" version = "4.4.0" @@ -7988,11 +8623,11 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "windows" -version = "0.46.0" +version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdacb41e6a96a052c6cb63a144f24900236121c6f63f4f8219fef5977ecb0c25" +checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f" dependencies = [ - "windows-targets", + "windows-targets 0.48.0", ] [[package]] @@ -8014,12 +8649,12 @@ version = "0.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" dependencies = [ - "windows_aarch64_gnullvm", + "windows_aarch64_gnullvm 0.42.2", "windows_aarch64_msvc 0.42.2", "windows_i686_gnu 0.42.2", "windows_i686_msvc 0.42.2", "windows_x86_64_gnu 0.42.2", - "windows_x86_64_gnullvm", + "windows_x86_64_gnullvm 0.42.2", "windows_x86_64_msvc 0.42.2", ] @@ -8029,7 +8664,16 @@ version = "0.45.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" dependencies = [ - "windows-targets", + "windows-targets 0.42.2", +] + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.0", ] [[package]] @@ -8038,21 +8682,42 @@ version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" dependencies = [ - "windows_aarch64_gnullvm", + "windows_aarch64_gnullvm 0.42.2", "windows_aarch64_msvc 0.42.2", "windows_i686_gnu 0.42.2", "windows_i686_msvc 0.42.2", "windows_x86_64_gnu 0.42.2", - "windows_x86_64_gnullvm", + "windows_x86_64_gnullvm 0.42.2", "windows_x86_64_msvc 0.42.2", ] +[[package]] +name = "windows-targets" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5" +dependencies = [ + "windows_aarch64_gnullvm 0.48.0", + "windows_aarch64_msvc 0.48.0", + "windows_i686_gnu 0.48.0", + "windows_i686_msvc 0.48.0", + "windows_x86_64_gnu 0.48.0", + "windows_x86_64_gnullvm 0.48.0", + "windows_x86_64_msvc 0.48.0", +] + [[package]] name = "windows_aarch64_gnullvm" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" + [[package]] name = "windows_aarch64_msvc" version = "0.33.0" @@ -8065,6 +8730,12 @@ version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" + [[package]] name = "windows_i686_gnu" version = "0.33.0" @@ -8077,6 +8748,12 @@ version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" +[[package]] +name = "windows_i686_gnu" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" + [[package]] name = "windows_i686_msvc" version = "0.33.0" @@ -8089,6 +8766,12 @@ version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" +[[package]] +name = "windows_i686_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" + [[package]] name = "windows_x86_64_gnu" version = "0.33.0" @@ -8101,12 +8784,24 @@ version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" + [[package]] name = "windows_x86_64_gnullvm" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" + [[package]] name = "windows_x86_64_msvc" version = "0.33.0" @@ -8119,6 +8814,21 @@ version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" + +[[package]] +name = "winnow" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae8970b36c66498d8ff1d66685dc86b91b29db0c7739899012f63a63814b4b28" +dependencies = [ + "memchr", +] + [[package]] name = "winreg" version = "0.10.1" @@ -8138,6 +8848,24 @@ dependencies = [ "winapi-build", ] +[[package]] +name = "wyz" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" +dependencies = [ + "tap", +] + +[[package]] +name = "yaml-rust" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85" +dependencies = [ + "linked-hash-map", +] + [[package]] name = "yansi" version = "0.5.1" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000000000..fd6a9f4fb0292 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,132 @@ +[workspace] + +members = [ + "scripts/send-trace-to-jaeger", + "packages/next-swc/crates/core", + "packages/next-swc/crates/napi", + "packages/next-swc/crates/wasm", + "packages/next-swc/crates/next-build", + "packages/next-swc/crates/next-core", + "packages/next-swc/crates/next-dev", + "packages/next-swc/crates/next-dev-tests", + "packages/next-swc/crates/next-transform-font", + "packages/next-swc/crates/next-transform-dynamic", + "packages/next-swc/crates/next-transform-strip-page-exports", +] + +[profile.dev.package.swc_css_prefixer] +opt-level = 2 + +# This is a workaround for wasm timeout issue +[profile.dev.package."*"] +debug-assertions = false + +[profile.release] +lto = true + +[workspace.dependencies] +# Workspace crates +next-build = { path = "packages/next-swc/crates/next-build" } +next-core = { path = "packages/next-swc/crates/next-core", default-features = false } +next-dev = { path = "packages/next-swc/crates/next-dev", default-features = false, features = [ + "serializable", +] } +next-dev-tests = { path = "packages/next-swc/crates/next-dev-tests" } +next-transform-font = { path = "packages/next-swc/crates/next-transform-font" } +next-transform-dynamic = { path = "packages/next-swc/crates/next-transform-dynamic" } +next-transform-strip-page-exports = { path = "packages/next-swc/crates/next-transform-strip-page-exports" } + +# SWC crates +# Keep consistent with preset_env_base through swc_core +swc_core = { version = "0.76.41" } +testing = { version = "0.33.13" } + +# Turbo crates +turbopack-binding = { git = "https://github.com/vercel/turbo.git", tag = "turbopack-230608.1" } +# [TODO]: need to refactor embed_directory! macro usages, as well as resolving turbo_tasks::function, macros.. +turbo-tasks = { git = "https://github.com/vercel/turbo.git", tag = "turbopack-230608.1" } +# [TODO]: need to refactor embed_directory! macro usage in next-core +turbo-tasks-fs = { git = "https://github.com/vercel/turbo.git", tag = "turbopack-230608.1" } + +# General Deps + +# Be careful when selecting tls backend, including change default tls backend. +# If you changed, must verify with ALL build targets with next-swc to ensure +# it works. next-swc have various platforms, some doesn't support native (using openssl-sys) +# and some aren't buildable with rustls. +reqwest = { version = "0.11.14", default-features = false } + +chromiumoxide = { version = "0.4.0", features = [ + "tokio-runtime", +], default-features = false } +# For matching on errors from chromiumoxide. Keep in +# sync with chromiumoxide's tungstenite requirement. +tungstenite = "0.17.3" + +# flate2_zlib requires zlib, use flate2_rust +allsorts = { version = "0.14.0", default_features = false, features = [ + "outline", + "flate2_rust", +] } +anyhow = "1.0.69" +assert_cmd = "2.0.8" +async-compression = { version = "0.3.13", default-features = false, features = [ + "gzip", + "tokio", +] } +async-trait = "0.1.64" +atty = "0.2.14" +chrono = "0.4.23" +clap = "4.1.6" +clap_complete = "4.1.2" +concurrent-queue = "2.1.0" +console = "0.15.5" +console-subscriber = "0.1.8" +criterion = "0.4.0" +crossbeam-channel = "0.5.8" +dashmap = "5.4.0" +dialoguer = "0.10.3" +dunce = "1.0.3" +futures = "0.3.26" +futures-retry = "0.6.0" +httpmock = { version = "0.6.7", default-features = false } +indexmap = "1.9.2" +indicatif = "0.17.3" +indoc = "2.0.0" +itertools = "0.10.5" +lazy_static = "1.4.0" +log = "0.4.17" +mime = "0.3.16" +nohash-hasher = "0.2.0" +once_cell = "1.17.1" +owo-colors = "3.5.0" +parking_lot = "0.12.1" +pathdiff = "0.2.1" +pin-project-lite = "0.2.9" +postcard = "1.0.4" +predicates = "2.1.5" +pretty_assertions = "1.3.0" +proc-macro2 = "1.0.51" +qstring = "0.7.2" +quote = "1.0.23" +rand = "0.8.5" +regex = "1.7.0" +rstest = "0.16.0" +rustc-hash = "1.1.0" +semver = "1.0.16" +serde = { version = "1.0.152", features = ["derive"] } +serde_json = "1.0.93" +serde_qs = "0.11.0" +serde_yaml = "0.9.17" +syn = "1.0.107" +tempfile = "3.3.0" +thiserror = "1.0.38" +tiny-gradient = "0.1.0" +tokio = "1.25.0" +tokio-util = { version = "0.7.7", features = ["io"] } +tracing = "0.1.37" +tracing-subscriber = "0.3.16" +url = "2.2.2" +urlencoding = "2.1.2" +webbrowser = "0.8.7" +dhat = { version = "0.3.2" } diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 7f9f4d0412c39..70f62eba5723e 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -35,7 +35,6 @@ variables: PNPM_CACHE_FOLDER: $(Pipeline.Workspace)/.pnpm-store PNPM_VERSION: 7.24.3 NEXT_TELEMETRY_DISABLED: '1' - node_14_version: ^14.19.0 node_16_version: ^16.8.0 stages: @@ -47,7 +46,7 @@ stages: steps: - task: NodeTool@0 inputs: - versionSpec: $(node_14_version) + versionSpec: $(node_16_version) displayName: 'Install Node.js' - bash: | @@ -85,7 +84,7 @@ stages: - task: NodeTool@0 inputs: - versionSpec: $(node_14_version) + versionSpec: $(node_16_version) displayName: 'Install Node.js' - bash: | diff --git a/bench/rendering/pages/stateless-big.js b/bench/rendering/pages/stateless-big.js index 87f340d66d7e9..fdf9f92eefb3d 100644 --- a/bench/rendering/pages/stateless-big.js +++ b/bench/rendering/pages/stateless-big.js @@ -5,7 +5,7 @@ export default () => { } const items = () => { - var out = new Array(10000) + const out = new Array(10000) for (let i = 0; i < out.length; i++) { out[i] =
  • This is row {i + 1}
  • } diff --git a/contributing/.vercel.approvers b/contributing/.vercel.approvers new file mode 100644 index 0000000000000..2ed372107ca9f --- /dev/null +++ b/contributing/.vercel.approvers @@ -0,0 +1,2 @@ +@vercel/devex +@vercel/next-js:optional \ No newline at end of file diff --git a/contributing/docs/adding-documentation.md b/contributing/docs/adding-documentation.md index 37ea3654600ad..5067e7d3f5373 100644 --- a/contributing/docs/adding-documentation.md +++ b/contributing/docs/adding-documentation.md @@ -1,40 +1,3 @@ -# Updating Documentation Paths +# Contributing to Documentation -Our documentation currently leverages a [manifest file](/docs/manifest.json), which is how documentation entries are checked. - -When adding a new entry under an existing category you only need to add an entry with `{title: '', path: '/docs/path/to/file.md'}`. The "title" is what is shown on the sidebar. - -When moving the location/url of an entry, the "title" field can be removed from the existing entry and the ".md" extension removed from the "path", then a "redirect" field with the shape of `{permanent: true/false, destination: '/some-url'}` can be added. A new entry should be added with the "title" and "path" fields if the document is renamed within the [`docs` folder](/docs) that points to the new location in the folder, e.g. `/docs/some-url.md` - -Example of moving documentation file: - -Before: - -```json -[ - { - "path": "/docs/original.md", - "title": "Hello world" - } -] -``` - -After: - -```json -[ - { - "path": "/docs/original", - "redirect": { - "permanent": false, - "destination": "/new" - } - }, - { - "path": "/docs/new.md", - "title": "Hello world" - } -] -``` - -Note: the manifest is checked automatically in the "lint" step in CI when opening a PR. +See the [Docs Contribution Guide](https://nextjs.org/docs/community/contribution-guide) to learn how to contribute to the Next.js Documentation. diff --git a/contributing/examples/adding-examples.md b/contributing/examples/adding-examples.md index 978857e0ef8e8..9fbbaac8c6828 100644 --- a/contributing/examples/adding-examples.md +++ b/contributing/examples/adding-examples.md @@ -35,7 +35,7 @@ Description Deploy the example using [Vercel](https://vercel.com?utm_source=github&utm_medium=readme&utm_campaign=next-example): -[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/git/external?repository-url=https://github.com/vercel/next.js/tree/canary/examples/DIRECTORY_NAME&project-name=DIRECTORY_NAME&repository-name=DIRECTORY_NAME) +[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?repository-url=https://github.com/vercel/next.js/tree/canary/examples/DIRECTORY_NAME&project-name=DIRECTORY_NAME&repository-name=DIRECTORY_NAME) ## How to use diff --git a/docs/.vercel.approvers b/docs/.vercel.approvers new file mode 100644 index 0000000000000..2ed372107ca9f --- /dev/null +++ b/docs/.vercel.approvers @@ -0,0 +1,2 @@ +@vercel/devex +@vercel/next-js:optional \ No newline at end of file diff --git a/docs/01-getting-started/01-installation.mdx b/docs/01-getting-started/01-installation.mdx new file mode 100644 index 0000000000000..046d9feda5325 --- /dev/null +++ b/docs/01-getting-started/01-installation.mdx @@ -0,0 +1,134 @@ +--- +title: Installation +description: Create a new Next.js application with `create-next-app`. Set up TypeScript, styles, and configure your `next.config.js` file. +related: + title: Next Steps + description: For more information on what to do next, we recommend the following sections + links: + - getting-started/react-essentials + - app/building-your-application + - app/building-your-application/configuring/typescript +--- + +System Requirements: + +- [Node.js 16.8](https://nodejs.org/) or later. +- macOS, Windows (including WSL), and Linux are supported. + +## Automatic Installation + +We recommend creating a new Next.js app using `create-next-app`, which sets up everything automatically for you. To create a project, run: + +```bash filename="Terminal" +npx create-next-app@latest +``` + +On installation, you'll see the following prompts: + +```txt filename="Terminal" +What is your project named? my-app +Would you like to use TypeScript with this project? No / Yes +Would you like to use ESLint with this project? No / Yes +Would you like to use Tailwind CSS with this project? No / Yes +Would you like to use `src/` directory with this project? No / Yes +Use App Router (recommended)? No / Yes +Would you like to customize the default import alias? No / Yes +``` + +Next.js now ships with TypeScript, ESLint, and Tailwind CSS configuration by default. You can also choose to use the `src` directory for your application code. + +After the prompts, `create-next-app` will create a folder with your project name and install the required dependencies. + +> **Note:** While you can use the [Pages Router](/docs/pages) in your new project. We recommend starting new applications with the App Router to leverage React's latest features. + +## Manual Installation + +To manually create a new Next.js app, install the required packages: + +```bash filename="Terminal" +npm install next@latest react@latest react-dom@latest +``` + +Open `package.json` and add the following `scripts`: + +```json filename="package.json" +{ + "scripts": { + "dev": "next dev", + "build": "next build", + "start": "next start", + "lint": "next lint" + } +} +``` + +These scripts refer to the different stages of developing an application: + +- `dev`: runs [`next dev`](/docs/app/api-reference/next-cli#development) to start Next.js in development mode. +- `build`: runs [`next build`](/docs/app/api-reference/next-cli#build) to build the application for production usage. +- `start`: runs [`next start`](/docs/app/api-reference/next-cli#production) to start a Next.js production server. +- `lint`: runs [`next lint`](/docs/app/api-reference/next-cli#lint) to set up Next.js' built-in ESLint configuration. + +### Create the `app` folder + +Next, create an `app` folder and add a `layout.tsx` and `page.tsx` file. These will be rendered when the user visits the root of your application. + +App Folder Structure + +Create a [root layout](/docs/app/building-your-application/routing/pages-and-layouts#root-layout-required) inside `app/layout.tsx` with the required `` and `` tags: + +```tsx filename="app/layout.tsx" switcher +export default function RootLayout({ + children, +}: { + children: React.ReactNode +}) { + return ( + + {children} + + ) +} +``` + +```jsx filename="app/layout.js" switcher +export default function RootLayout({ children }) { + return ( + + {children} + + ) +} +``` + +Finally, create a home page `app/page.tsx` with some initial content: + +```tsx filename="app/page.tsx" switcher +export default function Page() { + return

    Hello, Next.js!

    +} +``` + +```jsx filename="app/page.js" switcher +export default function Page() { + return

    Hello, Next.js!

    +} +``` + +> **Good to know**: If you forget to create `layout.tsx`, Next.js will automatically create this file for you when running the development server with `next dev`. + +### Create the `public` folder + +You can optionally create a `public` folder to store static assets such as images, fonts, etc. Files inside `public` directory can then be referenced by your code starting from the base URL (`/`). + +## Run the Development Server + +1. Run `npm run dev` to start the development server. +2. Visit `http://localhost:3000` to view your application. +3. Edit `app/layout.tsx` or `app/page.tsx` and save to see the updated result in your browser. diff --git a/docs/01-getting-started/02-project-structure.mdx b/docs/01-getting-started/02-project-structure.mdx new file mode 100644 index 0000000000000..ca755f3d86375 --- /dev/null +++ b/docs/01-getting-started/02-project-structure.mdx @@ -0,0 +1,149 @@ +--- +title: Next.js Project Structure +nav_title: Project Structure +description: A list of folders and files conventions in a Next.js project +--- + +This page provides an overview of the file and folder structure of a Next.js project. It covers top-level files and folders, configuration files, and routing conventions within the `app` and `pages` directories. + +## Top-level files + +| | | +| ------------------------------------------------------------------------------------------- | --------------------------------------- | +| **Next.js** | | +| [`next.config.js`](/docs/app/api-reference/next-config-js) | Configuration file for Next.js | +| [`middleware.ts`](/docs/app/building-your-application/routing/middleware) | Next.js request middleware | +| [`.env`](/docs/app/building-your-application/configuring/environment-variables) | Environment variables | +| [`.env.local`](/docs/app/building-your-application/configuring/environment-variables) | Local environment variables | +| [`.env.production`](/docs/app/building-your-application/configuring/environment-variables) | Production environment variables | +| [`.env.development`](/docs/app/building-your-application/configuring/environment-variables) | Development environment variables | +| `.next-env.d.ts` | TypeScript declaration file for Next.js | +| **Ecosystem** | | +| [`package.json`](/docs/getting-started/installation#manual-installation) | Project dependencies and scripts | +| `.gitignore` | Git files and folders to ignore | +| `tsconfig.json` | Configuration file for TypeScript | +| `jsconfig.json` | Configuration file for JavaScript | +| [`.eslintrc.json`](/docs/app/building-your-application/configuring/eslint) | Configuration file for ESLint | + +## Top-level folders + +| | | +| ----------------------------------------------------------------------- | ---------------------------------- | +| [`app`](/docs/app/building-your-application/routing) | App Router | +| [`pages`](/docs/pages/building-your-application/routing) | Pages Router | +| [`public`](/docs/getting-started/installation#create-the-public-folder) | Static assets to be served | +| [`src`](/docs/app/building-your-application/configuring/src-directory) | Optional application source folder | + +## `app` Routing Conventions + +### Routing Files + +| | | | +| ------------------------------------------------------------------------------- | ------------------- | ---------------------------- | +| [`layout`](/docs/app/api-reference/file-conventions/layout) | `.js` `.jsx` `.tsx` | Layout | +| [`page`](/docs/app/api-reference/file-conventions/page) | `.js` `.jsx` `.tsx` | Page | +| [`loading`](/docs/app/api-reference/file-conventions/loading) | `.js` `.jsx` `.tsx` | Loading UI | +| [`not-found`](/docs/app/api-reference/file-conventions/not-found) | `.js` `.jsx` `.tsx` | Not found UI | +| [`error`](/docs/app/api-reference/file-conventions/error) | `.js` `.jsx` `.tsx` | Error UI | +| [`global-error`](/docs/app/api-reference/file-conventions/error#global-errorjs) | `.js` `.jsx` `.tsx` | Global error UI | +| [`route`](/docs/app/api-reference/file-conventions/route) | `.js` `.ts` | API endpoint | +| [`template`](/docs/app/api-reference/file-conventions/template) | `.js` `.jsx` `.tsx` | Re-rendered layout | +| [`default`](/docs/app/api-reference/file-conventions/default) | `.js` `.jsx` `.tsx` | Parallel route fallback page | + +### Nested Routes + +| | | +| ---------------------------------------------------------------------------- | -------------------- | +| [`folder`](/docs/app/building-your-application/routing#route-segments) | Route segment | +| [`folder/folder`](/docs/app/building-your-application/routing#nested-routes) | Nested route segment | + +### Dynamic Routes + +| | | +| --------------------------------------------------------------------------------------------------------- | --------------------------- | +| [`[folder]`](/docs/app/building-your-application/routing/dynamic-routes#convention) | Dynamic route segment | +| [`[...folder]`](/docs/app/building-your-application/routing/dynamic-routes) | Catch-all segments | +| [`[[...folder]]`](/docs/app/building-your-application/routing/dynamic-routes#optional-catch-all-segments) | Optional catch-all segments | + +### Route Groups and Private Folders + +| | | +| ----------------------------------------------------------------------------------- | ------------------------------------------------ | +| [`(folder)`](/docs/app/building-your-application/routing/route-groups#convention) | Group routes without affecting routing | +| [`_folder`](/docs/app/building-your-application/routing/colocation#private-folders) | Opt folder and all child segments out of routing | + +### Parallel and Intercepted Routes + +| | | +| ---------------------------------------------------------------------------------------------- | -------------------------- | +| [`@folder`](/docs/app/building-your-application/routing/parallel-routes#convention) | Named slot | +| [`(.)folder`](/docs/app/building-your-application/routing/intercepting-routes#convention) | Intercept same level | +| [`(..)folder`](/docs/app/building-your-application/routing/intercepting-routes#convention) | Intercept one level above | +| [`(..)(..)folder`](/docs/app/building-your-application/routing/intercepting-routes#convention) | Intercept two levels above | +| [`(...)folder`](/docs/app/building-your-application/routing/intercepting-routes#convention) | Intercept from root | + +### Metadata File Conventions + +#### App Icons + +| | | | +| --------------------------------------------------------------------------------------------------------------- | ----------------------------------- | ------------------------ | +| [`favicon`](/docs/app/api-reference/file-conventions/metadata/app-icons#favicon) | `.ico` | Favicon file | +| [`icon`](/docs/app/api-reference/file-conventions/metadata/app-icons#icon) | `.png` `.svg` | App Icon file | +| [`icon`](/docs/app/api-reference/file-conventions/metadata/app-icons#generate-icons-using-code-js-ts-tsx) | `.ico` `.jpg` `.jpeg` `.png` `.svg` | Generated App Icon | +| [`apple-icon`](/docs/app/api-reference/file-conventions/metadata/app-icons#apple-icon) | `.jpg` `.jpeg`, `.png` | Apple App Icon file | +| [`apple-icon`](/docs/app/api-reference/file-conventions/metadata/app-icons#generate-icons-using-code-js-ts-tsx) | `.js` `.ts` `.tsx` | Generated Apple App Icon | + +#### Open Graph and Twitter Images + +| | | | +| --------------------------------------------------------------------------------------------------------------------------- | ---------------------------- | -------------------------- | +| [`opengraph-image`](/docs/app/api-reference/file-conventions/metadata/opengraph-image#opengraph-image) | `.jpg` `.jpeg` `.png` `.gif` | Open Graph image file | +| [`opengraph-image`](/docs/app/api-reference/file-conventions/metadata/opengraph-image#generate-images-using-code-js-ts-tsx) | `.js` `.ts` `.tsx` | Generated Open Graph image | +| [`twitter-image`](/docs/app/api-reference/file-conventions/metadata/opengraph-image#twitter-image) | `.jpg` `.jpeg` `.png` `.gif` | Twitter image file | +| [`twitter-image`](/docs/app/api-reference/file-conventions/metadata/opengraph-image#generate-images-using-code-js-ts-tsx) | `.js` `.ts` `.tsx` | Generated Twitter image | + +#### SEO + +| | | | +| ------------------------------------------------------------------------------------------- | ----------- | --------------------- | +| [`sitemap`](/docs/app/api-reference/file-conventions/metadata/sitemap#static-sitemapxml) | `.xml` | Sitemap file | +| [`sitemap`](/docs/app/api-reference/file-conventions/metadata/sitemap#generate-a-sitemap) | `.js` `.ts` | Generated Sitemap | +| [`robots`](/docs/app/api-reference/file-conventions/metadata/robots#static-robotstxt) | `.txt` | Robots file | +| [`robots`](/docs/app/api-reference/file-conventions/metadata/robots#generate-a-robots-file) | `.js` `.ts` | Generated Robots file | + +## `pages` Routing Conventions + +### Special Files + +| | | | +| ----------------------------------------------------------------------------------------------------------- | ------------------- | ----------------- | +| [`_app`](/docs/pages/building-your-application/routing/custom-app) | `.js` `.jsx` `.tsx` | Custom App | +| [`_document`](/docs/pages/building-your-application/routing/custom-document) | `.js` `.jsx` `.tsx` | Custom Document | +| [`_error`](/docs/pages/building-your-application/routing/custom-error#more-advanced-error-page-customizing) | `.js` `.jsx` `.tsx` | Custom Error Page | +| [`404`](/docs/pages/building-your-application/routing/custom-error#404-page) | `.js` `.jsx` `.tsx` | 404 Error Page | +| [`500`](/docs/pages/building-your-application/routing/custom-error#500-page) | `.js` `.jsx` `.tsx` | 500 Error Page | + +### Routes + +| | | | +| ---------------------------------------------------------------------------------------------- | ------------------- | ----------- | +| **Folder convention** | | | +| [`index`](/docs/pages/building-your-application/routing/pages-and-layouts#index-routes) | `.js` `.jsx` `.tsx` | Home page | +| [`folder/index`](/docs/pages/building-your-application/routing/pages-and-layouts#index-routes) | `.js` `.jsx` `.tsx` | Nested page | +| **File convention** | | | +| [`index`](/docs/pages/building-your-application/routing/pages-and-layouts#index-routes) | `.js` `.jsx` `.tsx` | Home page | +| [`file`](/docs/pages/building-your-application/routing/pages-and-layouts) | `.js` `.jsx` `.tsx` | Nested page | + +### Dynamic Routes + +| | | | +| ----------------------------------------------------------------------------------------------------------------- | ------------------- | --------------------------- | +| **Folder convention** | | | +| [`[folder]/index`](/docs/pages/building-your-application/routing/dynamic-routes) | `.js` `.jsx` `.tsx` | Dynamic route segment | +| [`[...folder]/index`](/docs/pages/building-your-application/routing/dynamic-routes#catch-all-segments) | `.js` `.jsx` `.tsx` | Catch-all segments | +| [`[[...folder]]/index`](/docs/pages/building-your-application/routing/dynamic-routes#optional-catch-all-segments) | `.js` `.jsx` `.tsx` | Optional catch-all segments | +| **File convention** | | | +| [`[file]`](/docs/pages/building-your-application/routing/dynamic-routes) | `.js` `.jsx` `.tsx` | Dynamic route segment | +| [`[...file]`](/docs/pages/building-your-application/routing/dynamic-routes#catch-all-segments) | `.js` `.jsx` `.tsx` | Catch-all segments | +| [`[[...file]]`](/docs/pages/building-your-application/routing/dynamic-routes#optional-catch-all-segments) | `.js` `.jsx` `.tsx` | Optional catch-all segments | diff --git a/docs/01-getting-started/03-react-essentials.mdx b/docs/01-getting-started/03-react-essentials.mdx new file mode 100644 index 0000000000000..0a0401bc44258 --- /dev/null +++ b/docs/01-getting-started/03-react-essentials.mdx @@ -0,0 +1,863 @@ +--- +title: React Essentials +description: An overview of essential React features for building Next.js Applications, including Server Components. +--- + +To build applications with Next.js, it helps to be familiar with React's newer features such as Server Components. This page will go through the differences between Server and Client Components, when to use them, and recommended patterns. + +If you're new to React, we also recommend referring to the [React Docs](https://react.dev/learn). Here are some great resources for learning: + +- [React Tutorial](https://react.dev/learn/tutorial-tic-tac-toe) +- [Thinking in React](https://react.dev/learn/thinking-in-react) +- [Learn React](https://react.dev/learn/describing-the-ui) + +## Server Components + +Server and Client Components allow developers to build applications that span the server and client, combining the rich interactivity of client-side apps with the improved performance of traditional server rendering. + +### Thinking in Server Components + +Similar to how React changed the way we think about building UIs, React Server Components introduce a new mental model for building hybrid applications that leverage the [server and the client](/docs/app/building-your-application/rendering#rendering-environments). + +Instead of React rendering your whole application client-side (such as in the case of Single-Page Applications), React now gives you the flexibility to choose where to render your components based on their purpose. + +For example, consider a [page](/docs/app/building-your-application/routing/pages-and-layouts#pages) in your application: + +Thinking in Server Components + +If we were to split the page into smaller components, you'll notice that the majority of components are non-interactive and can be rendered on the server as Server Components. For smaller pieces of interactive UI, we can _sprinkle in_ Client Components. This aligns with Next.js server-first approach. + +### Why Server Components? + +So, you may be thinking, why Server Components? What are the advantages of using them over Client Components? + +Server Components allow developers to better leverage server infrastructure. For example, you can move data fetching to the server, closer to your database, and keep large dependencies that previously would impact the client JavaScript bundle size on the server, leading to improved performance. Server Components make writing a React application feel similar to PHP or Ruby on Rails, but with the power and flexibility of React and the components model for templating UI. + +With Server Components, the initial page load is faster, and the client-side JavaScript bundle size is reduced. The base client-side runtime is **cacheable** and **predictable** in size, and does _not_ increase as your application grows. Additional JavaScript is _only added_ as client-side interactivity is used in your application through [Client Components](#client-components). + +When a route is loaded with Next.js, the initial HTML is rendered on the server. This HTML is then **progressively enhanced** in the browser, allowing the client to take over the application and add interactivity, by asynchronously loading the Next.js and React client-side runtime. + +To make the transition to Server Components easier, all components inside the [App Router](/docs/app/building-your-application/routing#the-app-router) are Server Components by default, including [special files](/docs/app/building-your-application/routing#file-conventions) and [colocated components](/docs/app/building-your-application/routing#colocation). This allows you to automatically adopt them with no extra work, and achieve great performance out of the box. You can also optionally opt-in to Client Components using the ['use client' directive](#the-use-client-directive). + +## Client Components + +Client Components enable you to add client-side interactivity to your application. In Next.js, they are [pre-rendered](/docs/app/building-your-application/rendering#component-level-client-and-server-rendering) on the server and [hydrated](/docs/app/building-your-application/rendering#component-level-client-and-server-rendering) on the client. You can think of Client Components as how components in the [Pages Router](/docs/pages) have always worked. + +### The "use client" directive + +The [`"use client"` directive](https://github.com/reactjs/rfcs/pull/227) is a convention to declare a boundary between a Server and Client Component module graph. + +```tsx filename="app/counter.tsx" highlight={1} switcher +'use client' + +import { useState } from 'react' + +export default function Counter() { + const [count, setCount] = useState(0) + + return ( +
    +

    You clicked {count} times

    + +
    + ) +} +``` + +```jsx filename="app/counter.jsx" highlight={1} switcher +'use client' + +import { useState } from 'react' + +export default function Counter() { + const [count, setCount] = useState(0) + + return ( +
    +

    You clicked {count} times

    + +
    + ) +} +``` + +Use Client Directive and Network Boundary + +`"use client"` _sits_ between server-only and client code. It's placed at the top of a file, above imports, to define the _cut-off_ point where it crosses the boundary from the server-only to the client part. Once `"use client"` is defined in a file, all other modules imported into it, including child components, are considered part of the client bundle. + +Since Server Components are the default, all components are part of the Server Component module graph unless defined or imported in a module that starts with the `"use client"` directive. + +> **Good to know:** +> +> - Components in the Server Component module graph are guaranteed to be only rendered on the server. +> - Components in the Client Component module graph are primarily rendered on the client, but with Next.js, they can also be pre-rendered on the server and hydrated on the client. +> - The `"use client"` directive must be defined at the **top of a file** before any imports. +> - `"use client"` does **not** need to be defined in every file. The Client module boundary only needs to be defined once, at the "entry point", for all modules imported into it to be considered a Client Component. + +## When to use Server and Client Components? + +To simplify the decision between Server and Client Components, we recommend using Server Components (default in the `app` directory) until you have a use case for a Client Component. + +This table summarizes the different use cases for Server and Client Components: + +| What do you need to do? | Server Component | Client Component | +| ---------------------------------------------------------------------------------- | ------------------- | ------------------- | +| Fetch data. | | | +| Access backend resources (directly) | | | +| Keep sensitive information on the server (access tokens, API keys, etc) | | | +| Keep large dependencies on the server / Reduce client-side JavaScript | | | +| Add interactivity and event listeners (`onClick()`, `onChange()`, etc) | | | +| Use State and Lifecycle Effects (`useState()`, `useReducer()`, `useEffect()`, etc) | | | +| Use browser-only APIs | | | +| Use custom hooks that depend on state, effects, or browser-only APIs | | | +| Use [React Class components](https://react.dev/reference/react/Component) | | | + +## Patterns + +### Moving Client Components to the Leaves + +To improve the performance of your application, we recommend moving Client Components to the leaves of your component tree where possible. + +For example, you may have a Layout that has static elements (e.g. logo, links, etc) and an interactive search bar that uses state. + +Instead of making the whole layout a Client Component, move the interactive logic to a Client Component (e.g. ``) and keep your layout as a Server Component. This means you don't have to send all the component Javascript of the layout to the client. + +```tsx filename="app/layout.tsx" switcher +// SearchBar is a Client Component +import SearchBar from './searchbar' +// Logo is a Server Component +import Logo from './logo' + +// Layout is a Server Component by default +export default function Layout({ children }: { children: React.ReactNode }) { + return ( + <> + +
    {children}
    + + ) +} +``` + +```jsx filename="app/layout.js" switcher +// SearchBar is a Client Component +import SearchBar from './searchbar' +// Logo is a Server Component +import Logo from './logo' + +// Layout is a Server Component by default +export default function Layout({ children }) { + return ( + <> + +
    {children}
    + + ) +} +``` + +### Composing Client and Server Components + +Server and Client Components can be combined in the same component tree. + +Behind the scenes, React handles rendering as follows: + +- On the server, React renders **all** Server Components **before** sending the result to the client. + - This includes Server Components nested inside Client Components. + - Client Components encountered during this stage are skipped. +- On the client, React renders Client Components and _slots in_ the rendered result of Server Components, merging the work done on the server and client. + - If any Server Components are nested inside a Client Component, their rendered content will be placed correctly within the Client Component. + +> **Good to know:** In Next.js, during the initial page load, both the rendered result of Server Components from the above step and Client Components are [pre-rendered on the server as HTML](/docs/app/building-your-application/rendering#static-and-dynamic-rendering-on-the-server) to produce a faster initial page load. + +### Nesting Server Components inside Client Components + +Given the rendering flow outlined above, there is a restriction around importing a Server Component into a Client Component, as this approach would require an additional server round trip. + +#### Unsupported Pattern: Importing Server Components into Client Components + +The following pattern is not supported. You cannot import a Server Component into a Client Component: + +```tsx filename="app/example-client-component.tsx" switcher highlight={5,18} +'use client' + +// This pattern will **not** work! +// You cannot import a Server Component into a Client Component. +import ExampleServerComponent from './example-server-component' + +export default function ExampleClientComponent({ + children, +}: { + children: React.ReactNode +}) { + const [count, setCount] = useState(0) + + return ( + <> + + + + + ) +} +``` + +```jsx filename="app/example-client-component.js" switcher highlight={5,14} +'use client' + +// This pattern will **not** work! +// You cannot import a Server Component into a Client Component. +import ExampleServerComponent from './example-server-component' + +export default function ExampleClientComponent({ children }) { + const [count, setCount] = useState(0) + + return ( + <> + + + + + ) +} +``` + +#### Recommended Pattern: Passing Server Components to Client Components as Props + +Instead, when designing Client Components you can use React props to mark _"holes"_ for Server Components. + +The Server Component will be rendered on the server, and when the Client Component is rendered on the client, the _"hole"_ will be filled in with the rendered result of the Server Component. + +A common pattern is to use the React `children` prop to create the _"hole"_. We can refactor `` to accept a generic `children` prop and move the import and explicit nesting of `` up to a parent component. + +```tsx filename="app/example-client-component.tsx" switcher highlight={6,16} +'use client' + +import { useState } from 'react' + +export default function ExampleClientComponent({ + children, +}: { + children: React.ReactNode +}) { + const [count, setCount] = useState(0) + + return ( + <> + + + {children} + + ) +} +``` + +```jsx filename="app/example-client-component.js" switcher highlight={5,12} +'use client' + +import { useState } from 'react' + +export default function ExampleClientComponent({ children }) { + const [count, setCount] = useState(0) + + return ( + <> + + + {children} + + ) +} +``` + +Now, `` has no knowledge of what `children` is. Infact, from its perspective it doesn't even know that `children` will eventually be filled in by the result of a Server Component. + +The only responsibility `ExampleClientComponent` has is to decide where whatever `children` will eventually be should be placed. + +In a parent Server Component, you can import both the `` and `` and pass `` as a child of ``: + +```tsx filename="app/page.tsx" highlight={11} switcher +// This pattern works: +// You can pass a Server Component as a child or prop of a +// Client Component. +import ExampleClientComponent from './example-client-component' +import ExampleServerComponent from './example-server-component' + +// Pages in Next.js are Server Components by default +export default function Page() { + return ( + + + + ) +} +``` + +```jsx filename="app/page.js" highlight={11} switcher +// This pattern works: +// You can pass a Server Component as a child or prop of a +// Client Component. +import ExampleClientComponent from './example-client-component' +import ExampleServerComponent from './example-server-component' + +// Pages in Next.js are Server Components by default +export default function Page() { + return ( + + + + ) +} +``` + +With this approach, the rendering of `` and `` are decoupled and can be rendered independently - aligning with Server Components, which are rendered on the server before Client Components. + +> **Good to know** +> +> - This pattern is **already applied** in [layouts and pages](/docs/app/building-your-application/routing/pages-and-layouts) with the `children` prop so you don't have to create an additional wrapper component. +> - Passing React components (JSX) to other components is not a new concept and has always been part of the React composition model. +> - This composition strategy works across Server and Client Components because the component that receives the prop has no knowledge of **what** the prop is. It is only responsible for where the thing that it is passed should be placed. +> - This allows the passed prop to be rendered independently, in this case, on the server, well before the Client Component is rendered on the client. +> - The very same strategy of "lifting content up" has been used to avoid state changes in a parent component re-rendering an imported nested child component. +> - You're not limited to the `children` prop. You can use any prop to pass JSX. + +### Passing props from Server to Client Components (Serialization) + +Props passed from the Server to Client Components need to be [serializable](https://developer.mozilla.org/en-US/docs/Glossary/Serialization). This means that values such as functions, Dates, etc, cannot be passed directly to Client Components. + +> **Where is the Network Boundary?** +> +> In the App Router, the network boundary is between Server Components and Client Components. This is different from the Pages where the boundary is between `getStaticProps`/`getServerSideProps` and Page Components. Data fetched inside Server Components do not need to be serialized as it doesn't cross the network boundary unless it is passed to a Client Component. Learn more about [data fetching](/docs/app/building-your-application/data-fetching#fetching-data-on-the-server) with Server Components. + +### Keeping Server-Only Code out of Client Components (Poisoning) + +Since JavaScript modules can be shared between both Server and Client Components, it's possible for code that was only ever intended to be run on the server to sneak its way into the client. + +For example, take the following data-fetching function: + +```ts filename="lib/data.ts" switcher +export async function getData() { + const res = await fetch('https://external-service.com/data', { + headers: { + authorization: process.env.API_KEY, + }, + }) + + return res.json() +} +``` + +```js filename="lib/data.js" switcher +export async function getData() { + const res = await fetch('https://external-service.com/data', { + headers: { + authorization: process.env.API_KEY, + }, + }) + + return res.json() +} +``` + +At first glance, it appears that `getData` works on both the server and the client. But because the environment variable `API_KEY` is not prefixed with `NEXT_PUBLIC`, it's a private variable that can only be accessed on the server. Next.js replaces private environment variables with the empty string in client code to prevent leaking secure information. + +As a result, even though `getData()` can be imported and executed on the client, it won't work as expected. And while making the variable public would make the function work on the client, it would leak sensitive information. + +So, this function was written with the intention that it would only ever be executed on the server. + +### The "server only" package + +To prevent this sort of unintended client usage of server code, we can use the `server-only` package to give other developers a build-time error if they ever accidentally import one of these modules into a Client Component. + +To use `server-only`, first install the package: + +```bash filename="Terminal" +npm install server-only +``` + +Then import the package into any module that contains server-only code: + +```js filename="lib/data.js" +import 'server-only' + +export async function getData() { + const res = await fetch('https://external-service.com/data', { + headers: { + authorization: process.env.API_KEY, + }, + }) + + return res.json() +} +``` + +Now, any Client Component that imports `getData()` will receive a build-time error explaining that this module can only be used on the server. + +The corresponding package `client-only` can be used to mark modules that contain client-only code – for example, code that accesses the `window` object. + +### Data Fetching + +Although it's possible to fetch data in Client Components, we recommend fetching data in Server Components unless you have a specific reason for fetching data on the client. Moving data fetching to the server leads to better performance and user experience. + +[Learn more about data fetching](/docs/app/building-your-application/data-fetching). + +### Third-party packages + +Since Server Components are new, third-party packages in the ecosystem are just beginning to add the `"use client"` directive to components that use client-only features like `useState`, `useEffect`, and `createContext`. + +Today, many components from `npm` packages that use client-only features do not yet have the directive. These third-party components will work as expected within your own [Client Components](#the-use-client-directive) since they have the `"use client"` directive, but they won't work within Server Components. + +For example, let's say you've installed the hypothetical `acme-carousel` package which has an `` component. This component uses `useState`, but it doesn't yet have the `"use client"` directive. + +If you use `` within a Client Component, it will work as expected: + +```tsx filename="app/gallery.tsx" switcher +'use client' + +import { useState } from 'react' +import { Carousel } from 'acme-carousel' + +export default function Gallery() { + let [isOpen, setIsOpen] = useState(false) + + return ( +
    + + + {/* Works, since Carousel is used within a Client Component */} + {isOpen && } +
    + ) +} +``` + +```jsx filename="app/gallery.js" switcher +'use client' + +import { useState } from 'react' +import { Carousel } from 'acme-carousel' + +export default function Gallery() { + let [isOpen, setIsOpen] = useState(false) + + return ( +
    + + + {/* Works, since Carousel is used within a Client Component */} + {isOpen && } +
    + ) +} +``` + +However, if you try to use it directly within a Server Component, you'll see an error: + +```tsx filename="app/page.tsx" switcher +import { Carousel } from 'acme-carousel' + +export default function Page() { + return ( +
    +

    View pictures

    + + {/* Error: `useState` can not be used within Server Components */} + +
    + ) +} +``` + +```jsx filename="app/page.js" switcher +import { Carousel } from 'acme-carousel' + +export default function Page() { + return ( +
    +

    View pictures

    + + {/* Error: `useState` can not be used within Server Components */} + +
    + ) +} +``` + +This is because Next.js doesn't know `` is using client-only features. + +To fix this, you can wrap third-party components that rely on client-only features in your own Client Components: + +```tsx filename="app/carousel.tsx" switcher +'use client' + +import { Carousel } from 'acme-carousel' + +export default Carousel +``` + +```jsx filename="app/carousel.js" switcher +'use client' + +import { Carousel } from 'acme-carousel' + +export default Carousel +``` + +Now, you can use `` directly within a Server Component: + +```tsx filename="app/page.tsx" switcher +import Carousel from './carousel' + +export default function Page() { + return ( +
    +

    View pictures

    + + {/* Works, since Carousel is a Client Component */} + +
    + ) +} +``` + +```jsx filename="app/page.js" switcher +import Carousel from './carousel' + +export default function Page() { + return ( +
    +

    View pictures

    + + {/* Works, since Carousel is a Client Component */} + +
    + ) +} +``` + +We don't expect you to need to wrap most third-party components since it's likely you'll be using them within Client Components. However, one exception is provider components, since they rely on React state and context, and are typically needed at the root of an application. [Learn more about third-party context providers below](#rendering-third-party-context-providers-in-server-components). + +#### Library Authors + +- In a similar fashion, library authors creating packages to be consumed by other developers can use the `"use client"` directive to mark client entry points of their package. This allows users of the package to import package components directly into their Server Components without having to create a wrapping boundary. +- You can optimize your package by using ['use client' deeper in the tree](#moving-client-components-to-the-leaves), allowing the imported modules to be part of the Server Component module graph. +- It's worth noting some bundlers might strip out `"use client"` directives. You can find an example of how to configure esbuild to include the `"use client"` directive in the [React Wrap Balancer](https://github.com/shuding/react-wrap-balancer/blob/main/tsup.config.ts#L10-L13) and [Vercel Analytics](https://github.com/vercel/analytics/blob/main/packages/web/tsup.config.js#L26-L30) repositories. + +## Context + +Most React applications rely on [context](https://react.dev/reference/react/useContext) to share data between components, either directly via [`createContext`](https://react.dev/reference/react/useContext), or indirectly via provider components imported from third-party libraries. + +In Next.js 13, context is fully supported within Client Components, but it **cannot** be created or consumed directly within Server Components. This is because Server Components have no React state (since they're not interactive), and context is primarily used for rerendering interactive components deep in the tree after some React state has been updated. + +We'll discuss alternatives for sharing data between Server Components, but first, let's take a look at how to use context within Client Components. + +### Using context in Client Components + +All of the context APIs are fully supported within Client Components: + +```tsx filename="app/sidebar.tsx" switcher +'use client' + +import { createContext, useContext, useState } from 'react' + +const SidebarContext = createContext() + +export function Sidebar() { + const [isOpen, setIsOpen] = useState() + + return ( + + + + ) +} + +function SidebarNav() { + let { isOpen } = useContext(SidebarContext) + + return ( +
    +

    Home

    + + {isOpen && } +
    + ) +} +``` + +```jsx filename="app/sidebar.js" switcher +'use client' + +import { createContext, useContext, useState } from 'react' + +const SidebarContext = createContext() + +export function Sidebar() { + const [isOpen, setIsOpen] = useState() + + return ( + + + + ) +} + +function SidebarNav() { + let { isOpen } = useContext(SidebarContext) + + return ( +
    +

    Home

    + + {isOpen && } +
    + ) +} +``` + +However, context providers are typically rendered near the root of an application to share global concerns, like the current theme. Because context is not supported in Server Components, trying to create a context at the root of your application will cause an error: + +```tsx filename="app/layout.tsx" switcher +import { createContext } from 'react' + +// createContext is not supported in Server Components +export const ThemeContext = createContext({}) + +export default function RootLayout({ children }) { + return ( + + + {children} + + + ) +} +``` + +```jsx filename="app/layout.js" switcher +import { createContext } from 'react' + +// createContext is not supported in Server Components +export const ThemeContext = createContext({}) + +export default function RootLayout({ children }) { + return ( + + + {children} + + + ) +} +``` + +To fix this, create your context and render its provider inside of a Client Component: + +```tsx filename="app/theme-provider.tsx" switcher +'use client' + +import { createContext } from 'react' + +export const ThemeContext = createContext({}) + +export default function ThemeProvider({ children }) { + return {children} +} +``` + +```jsx filename="app/theme-provider.js" switcher +'use client' + +import { createContext } from 'react' + +export const ThemeContext = createContext({}) + +export default function ThemeProvider({ children }) { + return {children} +} +``` + +Your Server Component will now be able to directly render your provider since it's been marked as a Client Component: + +```tsx filename="app/layout.tsx" switcher +import ThemeProvider from './theme-provider' + +export default function RootLayout({ + children, +}: { + children: React.ReactNode +}) { + return ( + + + {children} + + + ) +} +``` + +```jsx filename="app/layout.js" switcher +import ThemeProvider from './theme-provider' + +export default function RootLayout({ children }) { + return ( + + + {children} + + + ) +} +``` + +With the provider rendered at the root, all other Client Components throughout your app will be able to consume this context. + +> Note: You should render providers as deep as possible in the tree – notice how `ThemeProvider` only wraps `{children}` instead of the entire `` document. This makes it easier for Next.js to optimize the static parts of your Server Components. + +### Rendering third-party context providers in Server Components + +Third-party npm packages often include Providers that need to be rendered near the root of your application. If these providers include the `"use client"` directive, they can be rendered directly inside of your Server Components. However, since Server Components are so new, many third-party providers won't have added the directive yet. + +If you try to render a third-party provider that doesn't have `"use client"`, it will cause an error: + +```tsx filename="app/layout.tsx" switcher +import { ThemeProvider } from 'acme-theme' + +export default function RootLayout({ children }) { + return ( + + + {/* Error: `createContext` can't be used in Server Components */} + {children} + + + ) +} +``` + +```jsx filename="app/layout.js" switcher +import { ThemeProvider } from 'acme-theme' + +export default function RootLayout({ children }) { + return ( + + + {/* Error: `createContext` can't be used in Server Components */} + {children} + + + ) +} +``` + +To fix this, wrap third-party providers in your own Client Component: + +```jsx filename="app/providers.js" +'use client' + +import { ThemeProvider } from 'acme-theme' +import { AuthProvider } from 'acme-auth' + +export function Providers({ children }) { + return ( + + {children} + + ) +} +``` + +Now, you can import and render `` directly within your root layout. + +```jsx filename="app/layout.js" +import { Providers } from './providers' + +export default function RootLayout({ children }) { + return ( + + + {children} + + + ) +} +``` + +With the providers rendered at the root, all the components and hooks from these libraries will work as expected within your own Client Components. + +Once a third-party library has added `"use client"` to its client code, you'll be able to remove the wrapper Client Component. + +### Sharing data between Server Components + +Since Server Components are not interactive and therefore do not read from React state, you don't need React context to share data. Instead, you can use native JavaScript patterns for common data that multiple Server Components need to access. For example, a module can be used to share a database connection across multiple components: + +```ts filename="utils/database.ts" switcher +export const db = new DatabaseConnection() +``` + +```js filename="utils/database.js" switcher +export const db = new DatabaseConnection() +``` + +```tsx filename="app/users/layout.tsx" switcher +import { db } from '@utils/database' + +export async function UsersLayout() { + let users = await db.query() + // ... +} +``` + +```jsx filename="app/users/layout.js" switcher +import { db } from '@utils/database' + +export async function UsersLayout() { + let users = await db.query() + // ... +} +``` + +```tsx filename="app/users/[id]/page.tsx" switcher +import { db } from '@utils/database' + +export async function DashboardPage() { + let user = await db.query() + // ... +} +``` + +```jsx filename="app/users/[id]/page.js" switcher +import { db } from '@utils/database' + +export async function DashboardPage() { + let user = await db.query() + // ... +} +``` + +In the above example, both the layout and page need to make database queries. Each of these components shares access to the database by importing the `@utils/database` module. This JavaScript pattern is called global singletons. + +### Sharing fetch requests between Server Components + +When fetching data, you may want to share the result of a `fetch` between a `page` or `layout` and some of its children components. This is an unnecessary coupling between the components and can lead to passing `props` back and forth between components. + +Instead, we recommend colocating data fetching alongside the component that consumes the data. [`fetch` requests are automatically deduped](/docs/app/building-your-application/data-fetching#automatic-fetch-request-deduping) in Server Components, so each route segment can request exactly the data it needs without worrying about duplicate requests. Next.js will read the same value from the `fetch` cache. diff --git a/docs/01-getting-started/index.mdx b/docs/01-getting-started/index.mdx new file mode 100644 index 0000000000000..2c04700d7b5b8 --- /dev/null +++ b/docs/01-getting-started/index.mdx @@ -0,0 +1,8 @@ +--- +title: Getting Started +description: Learn how to create full-stack web applications with Next.js. +--- + +Get started with Next.js by creating a new application using the [Installation](/docs/getting-started/installation) guide. + +If you are new to React, we recommend going through the [React Essentials](/docs/getting-started/react-essentials) guides for an introduction of the basic concepts. diff --git a/docs/02-app/01-building-your-application/01-routing/01-defining-routes.mdx b/docs/02-app/01-building-your-application/01-routing/01-defining-routes.mdx new file mode 100644 index 0000000000000..0cad98f02477e --- /dev/null +++ b/docs/02-app/01-building-your-application/01-routing/01-defining-routes.mdx @@ -0,0 +1,58 @@ +--- +title: Defining Routes +description: Learn how to create your first route in Next.js. +related: + description: Learn more about creating pages and layouts. + links: + - app/building-your-application/routing/pages-and-layouts +--- + +> We recommend reading the [Routing Fundamentals](/docs/app/building-your-application/routing) page before continuing. + +This page will guide you through how to define and organize routes in your Next.js application. + +## Creating Routes + +Next.js uses a file-system based router where **folders** are used to define routes. + +Each folder represents a [**route** segment](/docs/app/building-your-application/routing#route-segments) that maps to a **URL** segment. To create a [nested route](/docs/app/building-your-application/routing#nested-routes), you can nest folders inside each other. + +Route segments to path segments + +A special [`page.js` file](/docs/app/building-your-application/routing/pages-and-layouts#pages) is used to make route segments publicly accessible. + +Defining Routes + +In this example, the `/dashboard/analytics` URL path is _not_ publicly accessible because it does not have a corresponding `page.js` file. This folder could be used to store components, stylesheets, images, or other colocated files. + +> **Good to know:** `.js`, `.jsx`, or `.tsx` file extensions can be used for special files. + +## Creating UI + +[Special file conventions](/docs/app/building-your-application/routing#file-conventions) are used to create UI for each route segment. The most common are [pages](/docs/app/building-your-application/routing/pages-and-layouts#pages) to show UI unique to a route, and [layouts](/docs/app/building-your-application/routing/pages-and-layouts#layouts) to show UI that is shared across multiple routes. + +For example, to create your first page, add a `page.js` file inside the `app` directory and export a React component: + +```tsx filename="app/page.tsx" switcher +export default function Page() { + return

    Hello, Next.js!

    +} +``` + +```jsx filename="app/page.js" switcher +export default function Page() { + return

    Hello, Next.js!

    +} +``` diff --git a/docs/02-app/01-building-your-application/01-routing/02-pages-and-layouts.mdx b/docs/02-app/01-building-your-application/01-routing/02-pages-and-layouts.mdx new file mode 100644 index 0000000000000..3ba1c5c16d7e9 --- /dev/null +++ b/docs/02-app/01-building-your-application/01-routing/02-pages-and-layouts.mdx @@ -0,0 +1,272 @@ +--- +title: Pages and Layouts +description: Create your first page and shared layout with the App Router. +--- + +> We recommend reading the [Routing Fundamentals](/docs/app/building-your-application/routing) and [Defining Routes](/docs/app/building-your-application/routing/defining-routes) pages before continuing. + +The App Router inside Next.js 13 introduced new file conventions to easily create [pages](#pages), [shared layouts](#layouts), and [templates](#templates). This page will guide you through how to use these special files in your Next.js application. + +## Pages + +A page is UI that is **unique** to a route. You can define pages by exporting a component from a `page.js` file. Use nested folders to [define a route](/docs/app/building-your-application/routing/defining-routes) and a `page.js` file to make the route publicly accessible. + +Create your first page by adding a `page.js` file inside the `app` directory: + +page.js special file + +```tsx filename="app/page.tsx" switcher +// `app/page.tsx` is the UI for the `/` URL +export default function Page() { + return

    Hello, Home page!

    +} +``` + +```jsx filename="app/page.js" switcher +// `app/page.js` is the UI for the `/` URL +export default function Page() { + return

    Hello, Home page!

    +} +``` + +```tsx filename="app/dashboard/page.tsx" switcher +// `app/dashboard/page.tsx` is the UI for the `/dashboard` URL +export default function Page() { + return

    Hello, Dashboard Page!

    +} +``` + +```jsx filename="app/dashboard/page.js" switcher +// `app/dashboard/page.js` is the UI for the `/dashboard` URL +export default function Page() { + return

    Hello, Dashboard Page!

    +} +``` + +> **Good to know:** +> +> - A page is always the [leaf](/docs/app/building-your-application/routing#terminology) of the [route subtree](/docs/app/building-your-application/routing#terminology). +> - `.js`, `.jsx`, or `.tsx` file extensions can be used for Pages. +> - A `page.js` file is required to make a route segment publicly accessible. +> - Pages are [Server Components](/docs/getting-started/react-essentials) by default but can be set to a [Client Component](/docs/getting-started/react-essentials#client-components). +> - Pages can fetch data. View the [Data Fetching](/docs/app/building-your-application/data-fetching) section for more information. + +## Layouts + +A layout is UI that is **shared** between multiple pages. On navigation, layouts preserve state, remain interactive, and do not re-render. Layouts can also be [nested](#nesting-layouts). + +You can define a layout by `default` exporting a React component from a `layout.js` file. The component should accept a `children` prop that will be populated with a child layout (if it exists) or a child page during rendering. + +layout.js special file + +```tsx filename="app/dashboard/layout.tsx" switcher +export default function DashboardLayout({ + children, // will be a page or nested layout +}: { + children: React.ReactNode +}) { + return ( +
    + {/* Include shared UI here e.g. a header or sidebar */} + + + {children} +
    + ) +} +``` + +```jsx filename="app/dashboard/layout.js" switcher +export default function DashboardLayout({ + children, // will be a page or nested layout +}) { + return ( +
    + {/* Include shared UI here e.g. a header or sidebar */} + + + {children} +
    + ) +} +``` + +> **Good to know:** +> +> - The top-most layout is called the [Root Layout](#root-layout-required). This **required** layout is shared across all pages in an application. Root layouts must contain `html` and `body` tags. +> - Any route segment can optionally define its own [Layout](#nesting-layouts). These layouts will be shared across all pages in that segment. +> - Layouts in a route are **nested** by default. Each parent layout wraps child layouts below it using the React `children` prop. +> - You can use [Route Groups](/docs/app/building-your-application/routing/route-groups) to opt specific route segments in and out of shared layouts. +> - Layouts are [Server Components](/docs/getting-started/react-essentials) by default but can be set to a [Client Component](/docs/getting-started/react-essentials#client-components). +> - Layouts can fetch data. View the [Data Fetching](/docs/app/building-your-application/data-fetching) section for more information. +> - Passing data between a parent layout and its children is not possible. However, you can fetch the same data in a route more than once, and React will [automatically dedupe the requests](/docs/app/building-your-application/data-fetching#automatic-fetch-request-deduping) without affecting performance. +> - Layouts do not have access to the current route segment(s). To access route segments, you can use [`useSelectedLayoutSegment`](/docs/app/api-reference/functions/use-selected-layout-segment) or [`useSelectedLayoutSegments`](/docs/app/api-reference/functions/use-selected-layout-segments) in a Client Component. +> - `.js`, `.jsx`, or `.tsx` file extensions can be used for Layouts. +> - A `layout.js` and `page.js` file can be defined in the same folder. The layout will wrap the page. + +### Root Layout (Required) + +The root layout is defined at the top level of the `app` directory and applies to all routes. This layout enables you to modify the initial HTML returned from the server. + +```tsx filename="app/layout.tsx" switcher +export default function RootLayout({ + children, +}: { + children: React.ReactNode +}) { + return ( + + {children} + + ) +} +``` + +```jsx filename="app/layout.js" switcher +export default function RootLayout({ children }) { + return ( + + {children} + + ) +} +``` + +> **Good to know:** +> +> - The `app` directory **must** include a root layout. +> - The root layout must define `` and `` tags since Next.js does not automatically create them. +> - You can use the [built-in SEO support](/docs/app/building-your-application/optimizing/metadata) to manage `` HTML elements, for example, the `` element. +> - You can use [route groups](/docs/app/building-your-application/routing/route-groups) to create multiple root layouts. See an [example here](/docs/app/building-your-application/routing/route-groups#creating-multiple-root-layouts). +> - The root layout is a [Server Component](/docs/getting-started/react-essentials) by default and **can not** be set to a [Client Component](/docs/getting-started/react-essentials#client-components). + +> **Migrating from the `pages` directory:** The root layout replaces the [`_app.js`](/docs/pages/building-your-application/routing/custom-app) and [`_document.js`](/docs/pages/building-your-application/routing/custom-document) files. [View the migration guide](/docs/app/building-your-application/upgrading/app-router-migration#migrating-_documentjs-and-_appjs). + +### Nesting Layouts + +Layouts defined inside a folder (e.g. `app/dashboard/layout.js`) apply to specific route segments (e.g. `acme.com/dashboard`) and render when those segments are active. By default, layouts in the file hierarchy are **nested**, which means they wrap child layouts via their `children` prop. + +<Image + alt="Nested Layout" + srcLight="/docs/light/nested-layout.png" + srcDark="/docs/dark/nested-layout.png" + width="1600" + height="606" +/> + +```tsx filename="app/dashboard/layout.tsx" switcher +export default function DashboardLayout({ + children, +}: { + children: React.ReactNode +}) { + return <section>{children}</section> +} +``` + +```jsx filename="app/dashboard/layout.js" switcher +export default function DashboardLayout({ children }) { + return <section>{children}</section> +} +``` + +If you were to combine the two layouts above, the root layout (`app/layout.js`) would wrap the dashboard layout (`app/dashboard/layout.js`), which would wrap route segments inside `app/dashboard/*`. + +The two layouts would be nested as such: + +<Image + alt="Nested Layouts" + srcLight="/docs/light/nested-layouts-ui.png" + srcDark="/docs/dark/nested-layouts-ui.png" + width="1600" + height="1026" +/> + +You can use [Route Groups](/docs/app/building-your-application/routing/route-groups) to opt specific route segments in and out of shared layouts. + +## Templates + +Templates are similar to layouts in that they wrap each child layout or page. Unlike layouts that persist across routes and maintain state, templates create a new instance for each of their children on navigation. This means that when a user navigates between routes that share a template, a new instance of the component is mounted, DOM elements are recreated, state is **not** preserved, and effects are re-synchronized. + +There may be cases where you need those specific behaviors, and templates would be a more suitable option than layouts. For example: + +- Enter/exit animations using CSS or animation libraries. +- Features that rely on `useEffect` (e.g logging page views) and `useState` (e.g a per-page feedback form). +- To change the default framework behavior. For example, Suspense Boundaries inside layouts only show the fallback the first time the Layout is loaded and not when switching pages. For templates, the fallback is shown on each navigation. + +> **Recommendation:** We recommend using Layouts unless you have a specific reason to use Template. + +A template can be defined by exporting a default React component from a `template.js` file. The component should accept a `children` prop which will be nested segments. + +<Image + alt="template.js special file" + srcLight="/docs/light/template-special-file.png" + srcDark="/docs/dark/template-special-file.png" + width="1600" + height="444" +/> + +```tsx filename="app/template.tsx" switcher +export default function Template({ children }: { children: React.ReactNode }) { + return <div>{children}</div> +} +``` + +```jsx filename="app/template.js" switcher +export default function Template({ children }) { + return <div>{children}</div> +} +``` + +The rendered output of a route segment with a layout and a template will be as such: + +```jsx filename="Output" +<Layout> + {/* Note that the template is given a unique key. */} + <Template key={routeParam}>{children}</Template> +</Layout> +``` + +## Modifying `<head>` + +In the `app` directory, you can modify the `<head>` HTML elements such as `title` and `meta` using the [built-in SEO support](/docs/app/building-your-application/optimizing/metadata). + +Metadata can be defined by exporting a [`metadata` object](/docs/app/api-reference/functions/generate-metadata#the-metadata-object) or [`generateMetadata` function](/docs/app/api-reference/functions/generate-metadata#generatemetadata-function) in a [`layout.js`](/docs/app/api-reference/file-conventions/layout) or [`page.js`](/docs/app/api-reference/file-conventions/page) file. + +```tsx filename="app/page.tsx" switcher +import { Metadata } from 'next' + +export const metadata: Metadata = { + title: 'Next.js', +} + +export default function Page() { + return '...' +} +``` + +```jsx filename="app/page.js" switcher +export const metadata = { + title: 'Next.js', +} + +export default function Page() { + return '...' +} +``` + +> **Good to know:** You should **not** manually add `<head>` tags such as `<title>` and `<meta>` to root layouts. Instead, you should use the [Metadata API](/docs/app/api-reference/functions/generate-metadata) which automatically handles advanced requirements such as streaming and de-duplicating `<head>` elements. + +[Learn more about available metadata options in the API reference.](/docs/app/api-reference/functions/generate-metadata) diff --git a/docs/02-app/01-building-your-application/01-routing/03-linking-and-navigating.mdx b/docs/02-app/01-building-your-application/01-routing/03-linking-and-navigating.mdx new file mode 100644 index 0000000000000..9ff32f1036b18 --- /dev/null +++ b/docs/02-app/01-building-your-application/01-routing/03-linking-and-navigating.mdx @@ -0,0 +1,194 @@ +--- +title: Linking and Navigating +description: Learn how navigation works in Next.js, and how to use the Link Component and `useRouter` hook. +--- + +The Next.js router uses [server-centric routing](/docs/app/building-your-application/routing#server-centric-routing-with-client-side-navigation) with [client-side navigation](#how-navigation-works). It supports [instant loading states](/docs/app/building-your-application/routing/loading-ui-and-streaming) and [concurrent rendering](https://react.dev/reference/react/startTransition). This means navigation maintains client-side state, avoids expensive re-renders, is interruptible, and doesn't cause race conditions. + +There are two ways to navigate between routes: + +- [`<Link>` Component](#link-component) +- [`useRouter` Hook](#userouter-hook) + +This page will go through how to use `<Link>`, `useRouter()`, and dive deeper into how navigation works. + +## `<Link>` Component + +`<Link>` is a React component that extends the HTML `<a>` element to provide [prefetching](#prefetching) and client-side navigation between routes. It is the primary way to navigate between routes in Next.js. + +To use `<Link>`, import it from `next/link`, and pass a `href` prop to the component: + +```tsx filename="app/page.tsx" switcher +import Link from 'next/link' + +export default function Page() { + return <Link href="/dashboard">Dashboard</Link> +} +``` + +```jsx filename="app/page.js" switcher +import Link from 'next/link' + +export default function Page() { + return <Link href="/dashboard">Dashboard</Link> +} +``` + +There are optional props you can pass to `<Link>`. See the [API reference](/docs/app/api-reference/components/link) for more information. + +## Examples + +### Linking to Dynamic Segments + +When linking to [dynamic segments](/docs/app/building-your-application/routing/dynamic-routes), you can use [template literals and interpolation](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals) to generate a list of links. For example, to generate a list of blog posts: + +```jsx filename="app/blog/PostList.jsx" +import Link from 'next/link' + +export default function PostList({ posts }) { + return ( + <ul> + {posts.map((post) => ( + <li key={post.id}> + <Link href={`/blog/${post.slug}`}>{post.title}</Link> + </li> + ))} + </ul> + ) +} +``` + +### Checking Active Links + +You can use [`usePathname()`](/docs/app/api-reference/functions/use-pathname) to determine if a link is active. For example, to add a class to the active link, you can check if the current `pathname` matches the `href` of the link: + +```jsx filename="app/ui/Navigation.jsx" +'use client' + +import { usePathname } from 'next/navigation' +import { Link } from 'next/link' + +export function Navigation({ navLinks }) { + const pathname = usePathname() + + return ( + <> + {navLinks.map((link) => { + const isActive = pathname.startsWith(link.href) + + return ( + <Link + className={isActive ? 'text-blue' : 'text-black'} + href={link.href} + key={link.name} + > + {link.name} + </Link> + ) + })} + </> + ) +} +``` + +### Scrolling to an `id` + +The default behavior of `<Link>` is to [scroll to the top of the route segment that has changed](#focus-and-scroll-management). When there is an `id` defined in `href`, it will scroll to the specific `id`, similarly to a normal `<a>` tag. + +To prevent scrolling to the top of the route segment, set `scroll={false}` and pass the add a hashed `id` to `href`: + +```jsx +<Link href="/#hashid" scroll={false}> + Scroll to specific id. +</Link> +``` + +## `useRouter()` Hook + +The `useRouter` hook allows you to programmatically change routes inside [Client Components](/docs/getting-started/react-essentials). + +To use `useRouter`, import it from `next/navigation`, and call the hook inside your Client Component: + +```jsx filename="app/page.jsx" +'use client' + +import { useRouter } from 'next/navigation' + +export default function Page() { + const router = useRouter() + + return ( + <button type="button" onClick={() => router.push('/dashboard')}> + Dashboard + </button> + ) +} +``` + +The `useRouter` provides methods such as `push()`, `refresh()`, and more. See the [API reference](/docs/app/api-reference/functions/use-router) for more information. + +> **Recommendation:** Use the `<Link>` component to navigate between routes unless you have a specific requirement for using `useRouter`. + +## How Navigation Works + +- A route transition is initiated using `<Link>` or calling `router.push()`. +- The router updates the URL in the browser's address bar. +- The router avoids unnecessary work by re-using segments that haven't changed (e.g. shared layouts) from the [client-side cache](#client-side-caching-of-rendered-server-components). This is also referred to as [partial rendering](/docs/app/building-your-application/routing#partial-rendering). +- If the [conditions of soft navigation](#conditions-for-soft-navigation) are met, the router fetches the new segment from the cache rather than the server. If not, the router performs a [hard navigation](#hard-navigation) and fetches the Server Component payload from the server. +- If created, [loading UI](/docs/app/building-your-application/routing/loading-ui-and-streaming) is shown from the server while the payload is being fetched. +- The router uses the cached or fresh payload to render the new segments on the client. + +### Client-side Caching of Rendered Server Components + +> **Good to know:** This client-side cache is different from the server-side [Next.js HTTP cache](/docs/app/building-your-application/data-fetching#caching-data). + +The new router has an **in-memory client-side cache** that stores the **rendered result** of Server Components (payload). The cache is split by route segments which allows invalidation at any level and ensures consistency across concurrent renders. + +As users navigate around the app, the router will store the payload of previously fetched segments **and** [prefetched](#prefetching) segments in the cache. + +This means, for certain cases, the router can re-use the cache instead of making a new request to the server. This improves performance by avoiding re-fetching data and re-rendering components unnecessarily. + +### Invalidating the Cache + +[Server Actions](/docs/app/building-your-application/data-fetching/server-actions) can be used to revalidate data on-demand by path ([`revalidatePath`](/docs/app/api-reference/functions/revalidatePath)) or by cache tag ([`revalidateTag`](/docs/app/api-reference/functions/revalidateTag)). + +### Prefetching + +Prefetching is a way to preload a route in the background before it's visited. The rendered result of prefetched routes is added to the router's client-side cache. This makes navigating to a prefetched route near-instant. + +By default, routes are prefetched as they become visible in the viewport when using the `<Link>` component. This can happen when the page first loads or through scrolling. Routes can also be programmatically prefetched using the `prefetch` method of the [`useRouter()` hook](/docs/app/api-reference/functions/use-router#userouter). + +**Static and Dynamic Routes**: + +- If the route is static, all the Server Component payloads for the route segments will be prefetched. +- If the route is dynamic, the payload from the first shared layout down until the first `loading.js` file is prefetched. This reduces the cost of prefetching the whole route dynamically and allows [instant loading states](/docs/app/building-your-application/routing/loading-ui-and-streaming#instant-loading-states) for dynamic routes. + +> **Good to know:** +> +> - Prefetching is only enabled in production. +> - Prefetching can be disabled by passing `prefetch={false}` to `<Link>`. + +### Soft Navigation + +On navigation, the cache for changed segments is reused (if it exists), and no new requests are made to the server for data. + +#### Conditions for Soft Navigation + +On navigation, Next.js will use soft navigation if the route you are navigating to has been [**prefetched**](#prefetching), and either doesn't include [dynamic segments](/docs/app/building-your-application/routing/dynamic-routes) **or** has the same dynamic parameters as the current route. + +For example, consider the following route that includes a dynamic `[team]` segment: `/dashboard/[team]/*`. The cached segments below `/dashboard/[team]/*` will only be invalidated when the `[team]` parameter changes. + +- Navigating from `/dashboard/team-red/*` to `/dashboard/team-red/*` will be a soft navigation. +- Navigating from `/dashboard/team-red/*` to `/dashboard/team-blue/*` will be a hard navigation. + +### Hard Navigation + +On navigation, the cache is invalidated and the server refetches data and re-renders the changed segments. + +### Back/Forward Navigation + +Back and forward navigation ([popstate event](https://developer.mozilla.org/en-US/docs/Web/API/Window/popstate_event)) has a soft navigation behavior. This means, the client-side cache is re-used and navigation is near-instant. + +### Focus and Scroll Management + +By default, Next.js will set focus and scroll into view the segment that's changed on navigation. diff --git a/docs/02-app/01-building-your-application/01-routing/04-route-groups.mdx b/docs/02-app/01-building-your-application/01-routing/04-route-groups.mdx new file mode 100644 index 0000000000000..4da0507893810 --- /dev/null +++ b/docs/02-app/01-building-your-application/01-routing/04-route-groups.mdx @@ -0,0 +1,78 @@ +--- +title: Route Groups +description: Route Groups can be used to partition your Next.js application into different sections. +--- + +In the `app` directory, nested folders are normally mapped to URL paths. However, you can mark a folder as a **Route Group** to prevent the folder from being included in the route's URL path. + +This allows you to organize your route segments and project files into logical groups without affecting the URL path structure. + +Route groups are useful for: + +- [Organizing routes into groups](#organize-routes-without-affecting-the-url-path) e.g. by site section, intent, or team. +- Enabling [nested layouts](/docs/app/building-your-application/routing/pages-and-layouts) in the same route segment level: + - [Creating multiple nested layouts in the same segment, including multiple root layouts](#creating-multiple-root-layouts) + - [Adding a layout to a subset of routes in a common segment](#opting-specific-segments-into-a-layout) + +## Convention + +A route group can be created by wrapping a folder's name in parenthesis: `(folderName)` + +## Examples + +### Organize routes without affecting the URL path + +To organize routes without affecting the URL, create a group to keep related routes together. The folders in parenthesis will be omitted from the URL (e.g. `(marketing)` or `(shop)`). + +<Image + alt="Organizing Routes with Route Groups" + srcLight="/docs/light/route-group-organisation.png" + srcDark="/docs/dark/route-group-organisation.png" + width="1600" + height="930" +/> + +Even though routes inside `(marketing)` and `(shop)` share the same URL hierarchy, you can create a different layout for each group by adding a `layout.js` file inside their folders. + +<Image + alt="Route Groups with Multiple Layouts" + srcLight="/docs/light/route-group-multiple-layouts.png" + srcDark="/docs/dark/route-group-multiple-layouts.png" + width="1600" + height="768" +/> + +### Opting specific segments into a layout + +To opt specific routes into a layout, create a new route group (e.g. `(shop)`) and move the routes that share the same layout into the group (e.g. `account` and `cart`). The routes outside of the group will not share the layout (e.g. `checkout`). + +<Image + alt="Route Groups with Opt-in Layouts" + srcLight="/docs/light/route-group-opt-in-layouts.png" + srcDark="/docs/dark/route-group-opt-in-layouts.png" + width="1600" + height="930" +/> + +### Creating multiple root layouts + +To create multiple [root layouts](/docs/app/building-your-application/routing/pages-and-layouts#root-layout-required), remove the top-level `layout.js` file, and add a `layout.js` file inside each route groups. This is useful for partitioning an application into sections that have a completely different UI or experience. The `<html>` and `<body>` tags need to be added to each root layout. + +<Image + alt="Route Groups with Multiple Root Layouts" + srcLight="/docs/light/route-group-multiple-root-layouts.png" + srcDark="/docs/dark/route-group-multiple-root-layouts.png" + width="1600" + height="687" +/> + +In the example above, both `(marketing)` and `(shop)` have their own root layout. + +--- + +> **Good to know:** +> +> - The naming of route groups has no special significance other than for organization. They do not affect the URL path. +> - Routes that include a route group **should not** resolve to the same URL path as other routes. For example, since route groups don't affect URL structure, `(marketing)/about/page.js` and `(shop)/about/page.js` would both resolve to `/about` and cause an error. +> - If you use multiple root layouts without a top-level `layout.js` file, your home `page.js` file should be defined in one of the route groups, For example: `app/(marketing)/page.js`. +> - Navigating **across multiple root layouts** will cause a **full page load** (as opposed to a client-side navigation). For example, navigating from `/cart` that uses `app/(shop)/layout.js` to `/blog` that uses `app/(marketing)/layout.js` will cause a full page load. This **only** applies to multiple root layouts. diff --git a/docs/02-app/01-building-your-application/01-routing/05-dynamic-routes.mdx b/docs/02-app/01-building-your-application/01-routing/05-dynamic-routes.mdx new file mode 100644 index 0000000000000..df0007cb6fdb9 --- /dev/null +++ b/docs/02-app/01-building-your-application/01-routing/05-dynamic-routes.mdx @@ -0,0 +1,119 @@ +--- +title: Dynamic Routes +description: Dynamic Routes can be used to programmatically generate route segments from dynamic data. +related: + title: Next Steps + description: For more information on what to do next, we recommend the following sections + links: + - app/building-your-application/routing/linking-and-navigating + - app/api-reference/functions/generate-static-params +--- + +When you don't know the exact segment names ahead of time and want to create routes from dynamic data, you can use Dynamic Segments that are filled in at request time or [prerendered](#generating-static-params) at build time. + +## Convention + +A Dynamic Segment can be created by wrapping a folder's name in square brackets: `[folderName]`. For example, `[id]` or `[slug]`. + +Dynamic Segments are passed as the `params` prop to [`layout`](/docs/app/api-reference/file-conventions/layout), [`page`](/docs/app/api-reference/file-conventions/page), [`route`](/docs/app/building-your-application/routing/router-handlers), and [`generateMetadata`](/docs/app/api-reference/functions/generate-metadata#generatemetadata-function) functions. + +## Example + +For example, a blog could include the following route `app/blog/[slug]/page.js` where `[slug]` is the Dynamic Segment for blog posts. + +```tsx filename="app/blog/[slug]/page.js" +export default function Page({ params }) { + return <div>My Post</div> +} +``` + +| Route | Example URL | `params` | +| ------------------------- | ----------- | --------------- | +| `app/blog/[slug]/page.js` | `/blog/a` | `{ slug: 'a' }` | +| `app/blog/[slug]/page.js` | `/blog/b` | `{ slug: 'b' }` | +| `app/blog/[slug]/page.js` | `/blog/c` | `{ slug: 'c' }` | + +See the [generateStaticParams()](#generating-static-params) page to learn how to generate the params for the segment. + +> **Note:** Dynamic Segments are equivalent to [Dynamic Routes](/docs/app/building-your-application/routing/dynamic-routes) in the `pages` directory. + +## Generating Static Params + +The `generateStaticParams` function can be used in combination with [dynamic route segments](/docs/app/building-your-application/routing/dynamic-routes) to [**statically generate**](/docs/app/building-your-application/rendering/static-and-dynamic-rendering#static-rendering-default) routes at build time instead of on-demand at request time. + +```tsx filename="app/blog/[slug]/page.tsx" switcher +export async function generateStaticParams() { + const posts = await fetch('https://.../posts').then((res) => res.json()) + + return posts.map((post) => ({ + slug: post.slug, + })) +} +``` + +```jsx filename="app/blog/[slug]/page.js" switcher +export async function generateStaticParams() { + const posts = await fetch('https://.../posts').then((res) => res.json()) + + return posts.map((post) => ({ + slug: post.slug, + })) +} +``` + +The primary benefit of the `generateStaticParams` function is its smart retrieval of data. If content is fetched within the `generateStaticParams` function using a `fetch` request, the requests are [automatically deduplicated](/docs/app/building-your-application/data-fetching#automatic-fetch-request-deduping). This means a `fetch` request with the same arguments across multiple `generateStaticParams`, Layouts, and Pages will only be made once, which decreases build times. + +Use the [migration guide](/docs/app/building-your-application/upgrading/app-router-migration#dynamic-paths-getstaticpaths) if you are migrating from the `pages` directory. + +See [`generateStaticParams` server function documentation](/docs/app/api-reference/functions/generate-static-params) for more information and advanced use cases. + +## Catch-all Segments + +Dynamic Segments can be extended to **catch-all** subsequent segments by adding an ellipsis inside the brackets `[...folderName]`. + +For example, `app/shop/[...slug]/page.js` will match `/shop/clothes`, but also `/shop/clothes/tops`, `/shop/clothes/tops/t-shirts`, and so on. + +| Route | Example URL | `params` | +| ---------------------------- | ------------- | --------------------------- | +| `app/shop/[...slug]/page.js` | `/shop/a` | `{ slug: ['a'] }` | +| `app/shop/[...slug]/page.js` | `/shop/a/b` | `{ slug: ['a', 'b'] }` | +| `app/shop/[...slug]/page.js` | `/shop/a/b/c` | `{ slug: ['a', 'b', 'c'] }` | + +## Optional Catch-all Segments + +Catch-all Segments can be made **optional** by including the parameter in double square brackets: `[[...folderName]]`. + +For example, `app/shop/[[...slug]]/page.js` will **also** match `/shop`, in addition to `/shop/clothes`, `/shop/clothes/tops`, `/shop/clothes/tops/t-shirts`. + +The difference between **catch-all** and **optional catch-all** segments is that with optional, the route without the parameter is also matched (`/shop` in the example above). + +| Route | Example URL | `params` | +| ------------------------------ | ------------- | --------------------------- | +| `app/shop/[[...slug]]/page.js` | `/shop` | `{}` | +| `app/shop/[[...slug]]/page.js` | `/shop/a` | `{ slug: ['a'] }` | +| `app/shop/[[...slug]]/page.js` | `/shop/a/b` | `{ slug: ['a', 'b'] }` | +| `app/shop/[[...slug]]/page.js` | `/shop/a/b/c` | `{ slug: ['a', 'b', 'c'] }` | + +## TypeScript + +When using TypeScript, you can add types for `params` depending on your configured route segment. + +```tsx filename="app/blog/[slug]/page.tsx" switcher +export default function Page({ params }: { params: { slug: string } }) { + return <h1>My Page</h1> +} +``` + +```jsx filename="app/blog/[slug]/page.js" switcher +export default function Page({ params }) { + return <h1>My Page</h1> +} +``` + +| Route | `params` Type Definition | +| ----------------------------------- | ---------------------------------------- | +| `app/blog/[slug]/page.js` | `{ slug: string }` | +| `app/shop/[...slug]/page.js` | `{ slug: string[] }` | +| `app/[categoryId]/[itemId]/page.js` | `{ categoryId: string, itemId: string }` | + +> **Note**: This may be done automatically by the [TypeScript plugin](/docs/app/building-your-application/configuring/typescript#typescript-plugin) in the future. diff --git a/docs/02-app/01-building-your-application/01-routing/06-loading-ui-and-streaming.mdx b/docs/02-app/01-building-your-application/01-routing/06-loading-ui-and-streaming.mdx new file mode 100644 index 0000000000000..375f202af2ef0 --- /dev/null +++ b/docs/02-app/01-building-your-application/01-routing/06-loading-ui-and-streaming.mdx @@ -0,0 +1,174 @@ +--- +title: Loading UI and Streaming +description: Built on top of Suspense, Loading UI allows you to create a fallback for specific route segments, and automatically stream content as it becomes ready. +--- + +The special file `loading.js` helps you create meaningful Loading UI with [React Suspense](https://react.dev/reference/react/Suspense). With this convention, you can show an [instant loading state](#instant-loading-states) from the server while the content of a route segment loads, the new content is automatically swapped in once rendering is complete. + +<Image + alt="Loading UI" + srcLight="/docs/light/loading-ui.png" + srcDark="/docs/dark/loading-ui.png" + width="1600" + height="691" +/> + +## Instant Loading States + +An instant loading state is fallback UI that is shown immediately upon navigation. You can pre-render loading indicators such as skeletons and spinners, or a small but meaningful part of future screens such as a cover photo, title, etc. This helps users understand the app is responding and provides a better user experience. + +Create a loading state by adding a `loading.js` file inside a folder. + +<Image + alt="loading.js special file" + srcLight="/docs/light/loading-special-file.png" + srcDark="/docs/dark/loading-special-file.png" + width="1600" + height="606" +/> + +```tsx filename="app/dashboard/loading.tsx" switcher +export default function Loading() { + // You can add any UI inside Loading, including a Skeleton. + return <LoadingSkeleton /> +} +``` + +```jsx filename="app/dashboard/loading.js" switcher +export default function Loading() { + // You can add any UI inside Loading, including a Skeleton. + return <LoadingSkeleton /> +} +``` + +In the same folder, `loading.js` will be nested inside `layout.js`. It will automatically wrap the `page.js` file and any children below in a `<Suspense>` boundary. + +<Image + alt="loading.js overview" + srcLight="/docs/light/loading-overview.png" + srcDark="/docs/dark/loading-overview.png" + width="1600" + height="768" +/> + +> **Good to know:** +> +> - Navigation is immediate, even with [server-centric routing](/docs/app/building-your-application/routing#server-centric-routing-with-client-side-navigation). +> - Navigation is interruptible, meaning changing routes does not need to wait for the content of the route to fully load before navigating to another route. +> - Shared layouts remain interactive while new route segments load. + +> **Recommendation:** Use the `loading.js` convention for route segments (layouts and pages) as Next.js optimizes this functionality. + +## Streaming with Suspense + +In addition to `loading.js`, you can also manually create Suspense Boundaries for your own UI components. The App Router supports streaming with [Suspense](https://react.dev/reference/react/Suspense) for both [Node.js and Edge runtimes](/docs/app/building-your-application/rendering/edge-and-nodejs-runtimes). + +### What is Streaming? + +To learn how Streaming works in React and Next.js, it's helpful to understand **Server-Side Rendering (SSR)** and its limitations. + +With SSR, there's a series of steps that need to be completed before a user can see and interact with a page: + +1. First, all data for a given page is fetched on the server. +2. The server then renders the HTML for the page. +3. The HTML, CSS, and JavaScript for the page are sent to the client. +4. A non-interactive user interface is shown using the generated HTML, and CSS. +5. Finally, React [hydrates](https://react.dev/reference/react-dom/client/hydrateRoot#hydrating-server-rendered-html) the user interface to make it interactive. + +<Image + alt="Chart showing Server Rendering without Streaming" + srcLight="/docs/light/server-rendering-without-streaming-chart.png" + srcDark="/docs/dark/server-rendering-without-streaming-chart.png" + width="1600" + height="612" +/> + +These steps are sequential and blocking, meaning the server can only render the HTML for a page once all the data has been fetched. And, on the client, React can only hydrate the UI once the code for all components in the page has been downloaded. + +SSR with React and Next.js helps improve the perceived loading performance by showing a non-interactive page to the user as soon as possible. + +<Image + alt="Server Rendering without Streaming" + srcLight="/docs/light/server-rendering-without-streaming.png" + srcDark="/docs/dark/server-rendering-without-streaming.png" + width="1600" + height="748" +/> + +However, it can still be slow as all data fetching on server needs to be completed before the page can be shown to the user. + +**Streaming** allows you to break down the page's HTML into smaller chunks and progressively send those chunks from the server to the client. + +<Image + alt="How Server Rendering with Streaming Works" + srcLight="/docs/light/server-rendering-with-streaming.png" + srcDark="/docs/dark/server-rendering-with-streaming.png" + width="1600" + height="785" +/> + +This enables parts of the page to be displayed sooner, without waiting for all the data to load before any UI can be rendered. + +Streaming works well with React's component model because each component can be considered a chunk. Components that have higher priority (e.g. product information) or that don't rely on data can be sent first (e.g. layout), and React can start hydration earlier. Components that have lower priority (e.g. reviews, related products) can be sent in the same server request after their data has been fetched. + +<Image + alt="Chart showing Server Rendering with Streaming" + srcLight="/docs/light/server-rendering-with-streaming-chart.png" + srcDark="/docs/dark/server-rendering-with-streaming-chart.png" + width="1600" + height="730" +/> + +Streaming is particularly beneficial when you want to prevent long data requests from blocking the page from rendering as it can reduce the [Time To First Byte (TTFB)](https://web.dev/ttfb/) and [First Contentful Paint (FCP)](https://web.dev/first-contentful-paint/). It also helps improve [Time to Interactive (TTI)](https://developer.chrome.com/en/docs/lighthouse/performance/interactive/), especially on slower devices. + +### Example + +`<Suspense>` works by wrapping a component that performs an asynchronous action (e.g. fetch data), showing fallback UI (e.g. skeleton, spinner) while it's happening, and then swapping in your component once the action completes. + +```tsx filename="app/dashboard/page.tsx" switcher +import { Suspense } from 'react' +import { PostFeed, Weather } from './Components' + +export default function Posts() { + return ( + <section> + <Suspense fallback={<p>Loading feed...</p>}> + <PostFeed /> + </Suspense> + <Suspense fallback={<p>Loading weather...</p>}> + <Weather /> + </Suspense> + </section> + ) +} +``` + +```jsx filename="app/dashboard/page.js" switcher +import { Suspense } from 'react' +import { PostFeed, Weather } from './Components' + +export default function Posts() { + return ( + <section> + <Suspense fallback={<p>Loading feed...</p>}> + <PostFeed /> + </Suspense> + <Suspense fallback={<p>Loading weather...</p>}> + <Weather /> + </Suspense> + </section> + ) +} +``` + +By using Suspense, you get the benefits of: + +1. **Streaming Server Rendering** - Progressively rendering HTML from the server to the client. +2. **Selective Hydration** - React prioritizes what components to make interactive first based on user interaction. + +For more Suspense examples and use cases, please see the [React Documentation](https://react.dev/reference/react/Suspense). + +### SEO + +- Next.js will wait for data fetching inside [`generateMetadata`](/docs/app/api-reference/functions/generate-metadata) to complete before streaming UI to the client. This guarantees the first part of a streamed response includes `<head>` tags. +- Since streaming is server-rendered, it does not impact SEO. You can use the [Mobile Friendly Test](https://search.google.com/test/mobile-friendly) tool from Google to see how your page appears to Google's web crawlers and view the serialized HTML ([source](https://web.dev/rendering-on-the-web/#seo-considerations)). diff --git a/docs/02-app/01-building-your-application/01-routing/07-error-handling.mdx b/docs/02-app/01-building-your-application/01-routing/07-error-handling.mdx new file mode 100644 index 0000000000000..3f47ae2c6e570 --- /dev/null +++ b/docs/02-app/01-building-your-application/01-routing/07-error-handling.mdx @@ -0,0 +1,215 @@ +--- +title: Error Handling +description: Handle runtime errors by automatically wrapping route segments and their nested children in a React Error Boundary. +--- + +The `error.js` file convention allows you to gracefully handle runtime errors in [nested routes](/docs/app/building-your-application/routing#nested-routes). + +- Automatically wrap a route segment and its nested children in a [React Error Boundary](https://react.dev/reference/react/Component#catching-rendering-errors-with-an-error-boundary). +- Create error UI tailored to specific segments using the file-system hierarchy to adjust granularity. +- Isolate errors to affected segments while keeping the rest of the app functional. +- Add functionality to attempt to recover from an error without a full page reload. + +Create error UI by adding an `error.js` file inside a route segment and exporting a React component: + +<Image + alt="error.js special file" + srcLight="/docs/light/error-special-file.png" + srcDark="/docs/dark/error-special-file.png" + width="1600" + height="606" +/> + +```tsx filename="app/dashboard/error.tsx" switcher +'use client' // Error components must be Client Components + +import { useEffect } from 'react' + +export default function Error({ + error, + reset, +}: { + error: Error + reset: () => void +}) { + useEffect(() => { + // Log the error to an error reporting service + console.error(error) + }, [error]) + + return ( + <div> + <h2>Something went wrong!</h2> + <button + onClick={ + // Attempt to recover by trying to re-render the segment + () => reset() + } + > + Try again + </button> + </div> + ) +} +``` + +```jsx filename="app/dashboard/error.js" switcher +'use client' // Error components must be Client Components + +import { useEffect } from 'react' + +export default function Error({ error, reset }) { + useEffect(() => { + // Log the error to an error reporting service + console.error(error) + }, [error]) + + return ( + <div> + <h2>Something went wrong!</h2> + <button + onClick={ + // Attempt to recover by trying to re-render the segment + () => reset() + } + > + Try again + </button> + </div> + ) +} +``` + +### How `error.js` Works + +<Image + alt="How error.js works" + srcLight="/docs/light/error-overview.png" + srcDark="/docs/dark/error-overview.png" + width="1600" + height="903" +/> + +- `error.js` automatically creates an [React Error Boundary](https://react.dev/reference/react/Component#catching-rendering-errors-with-an-error-boundary) that **wraps** a nested child segment or `page.js` component. +- The React component exported from the `error.js` file is used as the **fallback** component. +- If an error is thrown within the error boundary, the error is **contained**, and the fallback component is **rendered**. +- When the fallback error component is active, layouts **above** the error boundary **maintain** their state and **remain** interactive, and the error component can display functionality to recover from the error. + +### Recovering From Errors + +The cause of an error can sometimes be temporary. In these cases, simply trying again might resolve the issue. + +An error component can use the `reset()` function to prompt the user to attempt to recover from the error. When executed, the function will try to re-render the Error boundary's contents. If successful, the fallback error component is replaced with the result of the re-render. + +```tsx filename="app/dashboard/error.tsx" switcher +'use client' + +export default function Error({ + error, + reset, +}: { + error: Error + reset: () => void +}) { + return ( + <div> + <h2>Something went wrong!</h2> + <button onClick={() => reset()}>Try again</button> + </div> + ) +} +``` + +```jsx filename="app/dashboard/error.js" switcher +'use client' + +export default function Error({ error, reset }) { + return ( + <div> + <h2>Something went wrong!</h2> + <button onClick={() => reset()}>Try again</button> + </div> + ) +} +``` + +### Nested Routes + +React components created through [special files](/docs/app/building-your-application/routing#file-conventions) are rendered in a [specific nested hierarchy](/docs/app/building-your-application/routing#component-hierarchy). + +For example, a nested route with two segments that both include `layout.js` and `error.js` files are rendered in the following _simplified_ component hierarchy: + +<Image + alt="Nested Error Component Hierarchy" + srcLight="/docs/light/nested-error-component-hierarchy.png" + srcDark="/docs/dark/nested-error-component-hierarchy.png" + width="1600" + height="687" +/> + +The nested component hierarchy has implications for the behavior of `error.js` files across a nested route: + +- Errors bubble up to the nearest parent error boundary. This means an `error.js` file will handle errors for all its nested child segments. More or less granular error UI can be achieved by placing `error.js` files at different levels in the nested folders of a route. +- An `error.js` boundary will **not** handle errors thrown in a `layout.js` component in the **same** segment because the error boundary is nested **inside** that layouts component. + +### Handling Errors in Layouts + +`error.js` boundaries do **not** catch errors thrown in `layout.js` or `template.js` components of the same segment. This [intentional hierarchy](#nested-routes) keeps important UI that is shared between sibling routes (such as navigation) visible and functional when an error occurs. + +To handle errors within a specific layout or template, place an `error.js` file in the layouts parent segment. + +To handle errors within the root layout or template, use a variation of `error.js` called `global-error.js`. + +### Handling Errors in Root Layouts + +The root `app/error.js` boundary does **not** catch errors thrown in the root `app/layout.js` or `app/template.js` component. + +To specifically handle errors in these root components, use a variation of `error.js` called `app/global-error.js` located in the root `app` directory. + +Unlike the root `error.js`, the `global-error.js` error boundary wraps the **entire** application, and its fallback component replaces the root layout when active. Because of this, it is important to note that `global-error.js` **must** define its own `<html>` and `<body>` tags. + +`global-error.js` is the least granular error UI and can be considered "catch-all" error handling for the whole application. It is unlikely to be triggered often as root components are typically less dynamic, and other `error.js` boundaries will catch most errors. + +Even if a `global-error.js` is defined, it is still recommended to define a root `error.js` whose fallback component will be rendered **within** the root layout, which includes globally shared UI and branding. + +```tsx filename="app/global-error.tsx" switcher +'use client' + +export default function GlobalError({ + error, + reset, +}: { + error: Error + reset: () => void +}) { + return ( + <html> + <body> + <h2>Something went wrong!</h2> + <button onClick={() => reset()}>Try again</button> + </body> + </html> + ) +} +``` + +```jsx filename="app/global-error.js" switcher +'use client' + +export default function GlobalError({ error, reset }) { + return ( + <html> + <body> + <h2>Something went wrong!</h2> + <button onClick={() => reset()}>Try again</button> + </body> + </html> + ) +} +``` + +### Handling Server Errors + +If an error is thrown during [data fetching](/docs/app/building-your-application/data-fetching/fetching) or inside a Server Component, Next.js will forward the resulting `Error` object to the nearest `error.js` file as the `error` prop. + +When running `next dev`, the `error` will be serialized and forwarded from the Server Component to the client `error.js`. To ensure security when running `next start` in production, a generic error message is forwarded to `error` along with a `.digest` which contains a hash of the error message. This hash can be used to correspond to server logs. diff --git a/docs/02-app/01-building-your-application/01-routing/08-parallel-routes.mdx b/docs/02-app/01-building-your-application/01-routing/08-parallel-routes.mdx new file mode 100644 index 0000000000000..74252ebfddcd4 --- /dev/null +++ b/docs/02-app/01-building-your-application/01-routing/08-parallel-routes.mdx @@ -0,0 +1,319 @@ +--- +title: Parallel Routes +description: Simultaneously render one or more pages in the same view that can be navigated independently. A pattern for highly dynamic applications. +--- + +Parallel Routing allows you to simultaneously or conditionally render one or more pages in the same layout. For highly dynamic sections of an app, such as dashboards and feeds on social sites, Parallel Routing can be used to implement complex routing patterns. + +For example, you can simultaneously render the team and analytics pages. + +<Image + alt="Parallel Routes Diagram" + srcLight="/docs/light/parallel-routes.png" + srcDark="/docs/dark/parallel-routes.png" + width="1600" + height="952" +/> + +Parallel Routing allows you to define independent error and loading states for each route as they're being streamed in independently. + +<Image + alt="Parallel routes enable custom error and loading states" + srcLight="/docs/light/parallel-routes-cinematic-universe.png" + srcDark="/docs/dark/parallel-routes-cinematic-universe.png" + width="1600" + height="1218" +/> + +Parallel Routing also allow you to conditionally render a slot based on certain conditions, such as authentication state. This enables fully separated code on the same URL. + +<Image + alt="Conditional routes diagram" + srcLight="/docs/light/conditional-routes-ui.png" + srcDark="/docs/dark/conditional-routes-ui.png" + width="1600" + height="898" +/> + +## Convention + +Parallel routes are created using named **slots**. Slots are defined with the `@folder` convention, and are passed to the same-level layout as props. + +> Slots are _not_ route segments and _do not affect the URL structure_. The file path `/@team/members` would be accessible at `/members`. + +For example, the following file structure defines two explicit slots: `@analytics` and `@team`. + +<Image + alt="Parallel Routes File-system Structure" + srcLight="/docs/light/parallel-routes-file-system.png" + srcDark="/docs/dark/parallel-routes-file-system.png" + width="1600" + height="687" +/> + +The folder structure above means that the component in `app/layout.js` now accepts the `@analytics` and `@team` slots props, and can render them in parallel alongside the `children` prop: + +```tsx filename="app/layout.tsx" switcher +export default function Layout(props: { + children: React.ReactNode + analytics: React.ReactNode + team: React.ReactNode +}) { + return ( + <> + {props.children} + {props.team} + {props.analytics} + </> + ) +} +``` + +```jsx filename="app/layout.js" switcher +export default function Layout(props) { + return ( + <> + {props.children} + {props.team} + {props.analytics} + </> + ) +} +``` + +> **Good to know**: The `children` prop is an implicit slot that does not need to be mapped to a folder. This means `app/page.js` is equivalent to `app/@children/page.js`. + +## Unmatched Routes + +By default, the content rendered within a slot will match the current URL. + +In the case of an unmatched slot, the content that Next.js renders differs based on the routing technique and folder structure. + +### `default.js` + +You can define a `default.js` file to render as a fallback when Next.js cannot recover a slot's active state based on the current URL. + +Consider the following folder structure. The `@team` slot has a `settings` directory, but `@analytics` does not. + +<Image + alt="Parallel Routes unmatched routes" + srcLight="/docs/light/parallel-routes-unmatched-routes.png" + srcDark="/docs/dark/parallel-routes-unmatched-routes.png" + width="1600" + height="930" +/> + +If you were to navigate from the root `/` to `/settings`, the content that gets rendered is different based on the type of navigation and the availability of the `default.js` file. + +| | With `@analytics/default.js` | Without `@analytics/default.js` | +| --------------- | ---------------------------------------------------- | ------------------------------------------------- | +| Soft Navigation | `@team/settings/page.js` and `@analytics/page.js` | `@team/settings/page.js` and `@analytics/page.js` | +| Hard Navigation | `@team/settings/page.js` and `@analytics/default.js` | 404 | + +#### Soft Navigation + +On a [soft navigation](/docs/app/building-your-application/routing/linking-and-navigating#soft-navigation) - Next.js will render the slot's previously active state, even if it doesn't match the current URL. + +#### Hard Navigation + +On a [hard navigation](/docs/app/building-your-application/routing/linking-and-navigating#hard-navigation) - a navigation that requires a full page reload - Next.js will first try to render the unmatched slot's `default.js` file. If that's not available, a 404 gets rendered. + +> The 404 for unmatched routes helps ensure that you don't accidentally render a route that shouldn't be parallel rendered. + +## `useSelectedLayoutSegment(s)` + +Both [`useSelectedLayoutSegment`](/docs/app/api-reference/functions/use-selected-layout-segment) and [`useSelectedLayoutSegments`](/docs/app/api-reference/functions/use-selected-layout-segments) accept a `parallelRoutesKey`, which allows you read the active route segment within that slot. + +```tsx filename="app/layout.tsx" switcher +'use client' +import { useSelectedLayoutSegment } from 'next/navigation' + +export default async function Layout(props: { + //... + authModal: React.ReactNode +}) { + const loginSegments = useSelectedLayoutSegment('authModal') + // ... +} +``` + +```jsx filename="app/layout.js" switcher +'use client' +import { useSelectedLayoutSegment } from 'next/navigation' + +export default async function Layout(props) { + const loginSegments = useSelectedLayoutSegment('authModal') + // ... +} +``` + +When a user navigates to `@authModal/login`, or `/login` in the URL bar, `loginSegments` will be equal to the string `"login"`. + +## Examples + +### Modals + +Parallel Routing can be used to render modals. + +<Image + alt="Parallel Routes Diagram" + srcLight="/docs/light/parallel-routes-auth-modal.png" + srcDark="/docs/dark/parallel-routes-auth-modal.png" + width="1600" + height="687" +/> + +The `@authModal` slot renders a`<Modal>` component that can be shown by navigating to a matching route, for example `/login`. + +```tsx filename="app/layout.tsx" switcher +export default async function Layout(props: { + // ... + authModal: React.ReactNode +}) { + return ( + <> + {/* ... */} + {props.authModal} + </> + ) +} +``` + +```jsx filename="app/layout.js" switcher +export default async function Layout(props) { + return ( + <> + {/* ... */} + {props.authModal} + </> + ) +} +``` + +```tsx filename="app/@authModal/login/page.tsx" switcher +import { Modal } from 'components/modal' + +export default function Login() { + return ( + <Modal> + <h1>Login</h1> + {/* ... */} + </Modal> + ) +} +``` + +```jsx filename="app/@authModal/login/page.js" switcher +import { Modal } from 'components/modal' + +export default function Login() { + return ( + <Modal> + <h1>Login</h1> + {/* ... */} + </Modal> + ) +} +``` + +To ensure that the contents of the modal don't get rendered when it's not active, you can create a `default.js` file that returns `null`. + +```tsx filename="app/@authModal/login/default.tsx" switcher +export default function Default() { + return null +} +``` + +```jsx filename="app/@authModal/login/default.js" switcher +export default function Default() { + return null +} +``` + +#### Dismissing a modal + +If a modal was initiated through client navigation, e.g. by using `<Link href="/login">`, you can dismiss the modal by calling `router.back()` or by using a `Link` component. + +```tsx filename="app/@authModal/login/page.tsx" highlight="5" switcher +'use client' +import { useRouter } from 'next/navigation' +import { Modal } from 'components/modal' + +export default async function Login() { + const router = useRouter() + return ( + <Modal> + <span onClick={() => router.back()}>Close modal</span> + <h1>Login</h1> + ... + </Modal> + ) +} +``` + +```jsx filename="app/@authModal/login/page.jsx" highlight="5" switcher +'use client' +import { useRouter } from 'next/navigation' +import { Modal } from 'components/modal' + +export default async function Login() { + const router = useRouter() + return ( + <Modal> + <span onClick={() => router.back()}>Close modal</span> + <h1>Login</h1> + ... + </Modal> + ) +} +``` + +> More information on modals is covered in the [Intercepting Routes](/docs/app/building-your-application/routing/intercepting-routes) section. + +If you want to navigate elsewhere and dismiss a modal, you can also use a catch-all route. + +<Image + alt="Parallel Routes Diagram" + srcLight="/docs/light/parallel-routes-catchall.png" + srcDark="/docs/dark/parallel-routes-catchall.png" + width="1600" + height="768" +/> + +```tsx filename="app/@authModal/[...catchAll]/page.js" +export default function CatchAll() { + return null +} +``` + +> Catch-all routes take presedence over `default.js`. + +### Conditional Routes + +Parallel Routes can be used to implement conditional routing. For example, you can render a `@dashboard` or `@login` route depending on the authentication state. + +```tsx filename="app/layout.tsx" switcher +import { getUser } from '@/lib/auth' + +export default function Layout({ params, dashboard, login }) { + const isLoggedIn = getUser() + return isLoggedIn ? dashboard : login +} +``` + +```jsx filename="app/layout.js" switcher +import { getUser } from '@/lib/auth' + +export default function Layout({ params, dashboard, login }) { + const isLoggedIn = getUser() + return isLoggedIn ? dashboard : login +} +``` + +<Image + alt="Parallel routes authentication example" + srcLight="/docs/light/conditional-routes-ui.png" + srcDark="/docs/dark/conditional-routes-ui.png" + width="1600" + height="898" +/> diff --git a/docs/02-app/01-building-your-application/01-routing/09-intercepting-routes.mdx b/docs/02-app/01-building-your-application/01-routing/09-intercepting-routes.mdx new file mode 100644 index 0000000000000..9ae0950ce4f88 --- /dev/null +++ b/docs/02-app/01-building-your-application/01-routing/09-intercepting-routes.mdx @@ -0,0 +1,81 @@ +--- +title: Intercepting Routes +description: Use intercepting routes to load a new route within the current layout while masking the browser URL, useful for advanced routing patterns such as modals. +related: + title: Next Steps + description: Learn how to use modals with Intercepted and Parallel Routes. + links: + - app/building-your-application/routing/parallel-routes +--- + +Intercepting routes allows you to load a route within the current layout while keeping the context for the current page. This routing paradigm can be useful when you want to "intercept" a certain route to show a different route. + +For example, when clicking on a photo from within a feed, a modal overlaying the feed should show up with the photo. In this case, Next.js intercepts the `/feed` route and "masks" this URL to show `/photo/123` instead. + +<Image + alt="Intercepting routes soft navigation" + srcLight="/docs/light/intercepting-routes-soft-navigate.png" + srcDark="/docs/dark/intercepting-routes-soft-navigate.png" + width="1600" + height="617" +/> + +However, when navigating to the photo directly by for example when clicking a shareable URL or by refreshing the page, the entire photo page should render instead of the modal. No route interception should occur. + +<Image + alt="Intercepting routes hard navigation" + srcLight="/docs/light/intercepting-routes-hard-navigate.png" + srcDark="/docs/dark/intercepting-routes-hard-navigate.png" + width="1600" + height="604" +/> + +## Convention + +Intercepting routes can be defined with the `(..)` convention, which is similar to relative path convention `../` but for segments. + +You can use: + +- `(.)` to match segments on the **same level** +- `(..)` to match segments **one level above** +- `(..)(..)` to match segments **two levels above** +- `(...)` to match segments from the **root** `app` directory + +For example, you can intercept the `photo` segment from within the `feed` segment by creating a `(..)photo` directory. + +<Image + alt="Intercepting routes folder structure" + srcLight="/docs/light/intercepted-routes-files.png" + srcDark="/docs/dark/intercepted-routes-files.png" + width="1600" + height="604" +/> + +> Note that the `(..)` convention is based on _route segments_, not the file-system. + +## Examples + +### Modals + +Intercepting Routes can be used together with [Parallel Routes](/docs/app/building-your-application/routing/parallel-routes) to create modals. + +Using this pattern to create modals overcomes some common challenges when working with modals, by allowing you to: + +- Make the modal content **shareable through a URL** +- **Preserve context** when the page is refreshed, instead of closing the modal +- **Close the modal on backwards navigation** rather than going to the previous route +- **Reopen the modal on forwards navigation** + +<Image + alt="Intercepting routes modal example" + srcLight="/docs/light/intercepted-routes-modal-example.png" + srcDark="/docs/dark/intercepted-routes-modal-example.png" + width="1600" + height="976" +/> + +> In the above example, the path to the `photo` segment can use the `(..)` matcher since `@modal` is a _slot_ and not a _segment_. This means that the `photo` route is only one _segment_ level higher, despite being two _file-system_ levels higher. + +Other examples could include opening a login modal in a top navbar while also having a dedicated `/login` page, or opening a shopping cart in a side modal. + +[View an example](https://github.com/vercel-labs/nextgram) of modals with Intercepted and Parallel Routes. diff --git a/docs/02-app/01-building-your-application/01-routing/10-router-handlers.mdx b/docs/02-app/01-building-your-application/01-routing/10-router-handlers.mdx new file mode 100644 index 0000000000000..3bc5b740e53d0 --- /dev/null +++ b/docs/02-app/01-building-your-application/01-routing/10-router-handlers.mdx @@ -0,0 +1,589 @@ +--- +title: Route Handlers +description: Create custom request handlers for a given route using the Web's Request and Response APIs. +related: + title: API Reference + description: Learn more about the route.js file. + links: + - app/api-reference/file-conventions/route +--- + +Route Handlers allow you to create custom request handlers for a given route using the Web [Request](https://developer.mozilla.org/en-US/docs/Web/API/Request) and [Response](https://developer.mozilla.org/en-US/docs/Web/API/Response) APIs. + +<Image + alt="Route.js Special File" + srcLight="/docs/light/route-special-file.png" + srcDark="/docs/dark/route-special-file.png" + width="1600" + height="444" +/> + +> **Good to know:** Route Handlers are only available inside the `app` directory. They are the equivalent of [API Routes](/docs/pages/building-your-application/routing/api-routes) inside the `pages` directory meaning you **do not** need to use API Routes and Route Handlers together. + +## Convention + +Route Handlers are defined in a [`route.js|ts` file](/docs/app/api-reference/file-conventions/route) inside the `app` directory: + +```ts filename="app/api/route.ts" switcher +export async function GET(request: Request) {} +``` + +```js filename="app/api/route.js" switcher +export async function GET(request) {} +``` + +Route Handlers can be nested inside the `app` directory, similar to `page.js` and `layout.js`. But there **cannot** be a `route.js` file at the same route segment level as `page.js`. + +### Supported HTTP Methods + +The following [HTTP methods](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods) are supported: `GET`, `POST`, `PUT`, `PATCH`, `DELETE`, `HEAD`, and `OPTIONS`. If an unsupported method is called, Next.js will return a `405 Method Not Allowed` response. + +### Extended `NextRequest` and `NextResponse` APIs + +In addition to supporting native [Request](https://developer.mozilla.org/en-US/docs/Web/API/Request) and [Response](https://developer.mozilla.org/en-US/docs/Web/API/Response). Next.js extends them with +[`NextRequest`](/docs/app/api-reference/functions/next-request) and [`NextResponse`](/docs/app/api-reference/functions/next-response) to provide convenient helpers for advanced use cases. + +## Behavior + +### Static Route Handlers + +Route Handlers are [statically evaluated](/docs/app/building-your-application/data-fetching#static-and-dynamic-data-fetching) by default when using the `GET` method with the `Response` object. + +```ts filename="app/items/route.ts" switcher +import { NextResponse } from 'next/server' + +export async function GET() { + const res = await fetch('https://data.mongodb-api.com/...', { + headers: { + 'Content-Type': 'application/json', + 'API-Key': process.env.DATA_API_KEY, + }, + }) + const data = await res.json() + + return NextResponse.json({ data }) +} +``` + +```js filename="app/items/route.js" switcher +import { NextResponse } from 'next/server' + +export async function GET() { + const res = await fetch('https://data.mongodb-api.com/...', { + headers: { + 'Content-Type': 'application/json', + 'API-Key': process.env.DATA_API_KEY, + }, + }) + const data = await res.json() + + return NextResponse.json({ data }) +} +``` + +> **TypeScript Warning:** Although `Response.json()` is valid, native TypeScript types currently shows an error, you can use [`NextResponse.json()`](/docs/app/api-reference/functions/next-response#json) for typed responses instead. + +### Dynamic Route Handlers + +Route handlers are evaluated dynamically when: + +- Using the `Request` object with the `GET` method. +- Using any of the other HTTP methods. +- Using [Dynamic Functions](/#dynamic-functions) like `cookies` and `headers`. +- The [Segment Config Options](#segment-config-options) manually specifies dynamic mode. + +For example: + +```ts filename="app/products/api/route.ts" switcher +import { NextResponse } from 'next/server' + +export async function GET(request: Request) { + const { searchParams } = new URL(request.url) + const id = searchParams.get('id') + const res = await fetch(`https://data.mongodb-api.com/product/${id}`, { + headers: { + 'Content-Type': 'application/json', + 'API-Key': process.env.DATA_API_KEY, + }, + }) + const product = await res.json() + + return NextResponse.json({ product }) +} +``` + +```js filename="app/products/api/route.js" switcher +import { NextResponse } from 'next/server' + +export async function GET(request) { + const { searchParams } = new URL(request.url) + const id = searchParams.get('id') + const res = await fetch(`https://data.mongodb-api.com/product/${id}`, { + headers: { + 'Content-Type': 'application/json', + 'API-Key': process.env.DATA_API_KEY, + }, + }) + const product = await res.json() + + return NextResponse.json({ product }) +} +``` + +Similarly, the `POST` method will cause the Route Handler to be evaluated dynamically. + +```ts filename="app/items/route.ts" switcher +import { NextResponse } from 'next/server' + +export async function POST() { + const res = await fetch('https://data.mongodb-api.com/...', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'API-Key': process.env.DATA_API_KEY, + }, + body: JSON.stringify({ time: new Date().toISOString() }), + }) + + const data = await res.json() + + return NextResponse.json(data) +} +``` + +```js filename="app/items/route.js" switcher +import { NextResponse } from 'next/server' + +export async function POST() { + const res = await fetch('https://data.mongodb-api.com/...', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'API-Key': process.env.DATA_API_KEY, + }, + body: JSON.stringify({ time: new Date().toISOString() }), + }) + + const data = await res.json() + + return NextResponse.json(data) +} +``` + +> **Note:** Previously, API Routes could have been used for use cases like handling form submissions. Route Handlers are likely not the solution for these uses cases. We will be recommending the use of [mutations](/docs/app/building-your-application/data-fetching/server-actions) for this when ready. + +### Route Resolution + +You can consider a `route` the lowest level routing primitive. + +- They **do not** participate in layouts or client-side navigations like `page`. +- There **cannot** be a `route.js` file at the same route as `page.js`. + +| Page | Route | Result | +| -------------------- | ------------------ | ---------------------------- | +| `app/page.js` | `app/route.js` | <Cross size={18} /> Conflict | +| `app/page.js` | `app/api/route.js` | <Check size={18} /> Valid | +| `app/[user]/page.js` | `app/api/route.js` | <Check size={18} /> Valid | + +Each `route.js` or `page.js` file takes over all HTTP verbs for that route. + +```tsx filename="app/page.js" +export default function Page() { + return <h1>Hello, Next.js!</h1> +} + +// ❌ Conflict +// `app/route.js` +export async function POST(request) {} +``` + +## Examples + +The following examples show how to combine Route Handlers with other Next.js APIs and features. + +### Revalidating Static Data + +You can [revalidate static data](/docs/app/building-your-application/data-fetching/revalidating) fetches using the [`next.revalidate`](/docs/app/building-your-application/data-fetching/fetching#revalidating-data) option: + +```ts filename="app/items/route.ts" switcher +import { NextResponse } from 'next/server' + +export async function GET() { + const res = await fetch('https://data.mongodb-api.com/...', { + next: { revalidate: 60 }, // Revalidate every 60 seconds + }) + const data = await res.json() + + return NextResponse.json(data) +} +``` + +```js filename="app/items/route.js" switcher +import { NextResponse } from 'next/server' + +export async function GET() { + const res = await fetch('https://data.mongodb-api.com/...', { + next: { revalidate: 60 }, // Revalidate every 60 seconds + }) + const data = await res.json() + + return NextResponse.json(data) +} +``` + +Alternatively, you can use the [`revalidate` segment config option](/docs/app/api-reference/file-conventions/route-segment-config#revalidate): + +```ts +export const revalidate = 60 +``` + +### Dynamic Functions + +Route Handlers can be used with dynamic functions from Next.js, like [`cookies`](/docs/app/api-reference/functions/cookies) and [`headers`](/docs/app/api-reference/functions/headers). + +#### Cookies + +You can read cookies with [`cookies`](/docs/app/api-reference/functions/cookies) from `next/headers`. This server function can be called directly in a Route Handler, or nested inside of another function. + +This `cookies` instance is read-only. To set cookies, you need to return a new `Response` using the [`Set-Cookie`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie) header. + +```ts filename="app/api/route.ts" switcher +import { cookies } from 'next/headers' + +export async function GET(request: Request) { + const cookieStore = cookies() + const token = cookieStore.get('token') + + return new Response('Hello, Next.js!', { + status: 200, + headers: { 'Set-Cookie': `token=${token}` }, + }) +} +``` + +```js filename="app/api/route.js" switcher +import { cookies } from 'next/headers' + +export async function GET(request) { + const cookieStore = cookies() + const token = cookieStore.get('token') + + return new Response('Hello, Next.js!', { + status: 200, + headers: { 'Set-Cookie': `token=${token}` }, + }) +} +``` + +Alternatively, you can use abstractions on top of the underlying Web APIs to read cookies ([`NextRequest`](/docs/app/api-reference/functions/next-request)): + +```ts filename="app/api/route.ts" switcher +import { type NextRequest } from 'next/server' + +export async function GET(request: NextRequest) { + const token = request.cookies.get('token') +} +``` + +```js filename="app/api/route.js" switcher +export async function GET(request) { + const token = request.cookies.get('token') +} +``` + +#### Headers + +You can read headers with [`headers`](/docs/app/api-reference/functions/headers) from `next/headers`. This server function can be called directly in a Route Handler, or nested inside of another function. + +This `headers` instance is read-only. To set headers, you need to return a new `Response` with new `headers`. + +```ts filename="app/api/route.ts" switcher +import { headers } from 'next/headers' + +export async function GET(request: Request) { + const headersList = headers() + const referer = headersList.get('referer') + + return new Response('Hello, Next.js!', { + status: 200, + headers: { referer: referer }, + }) +} +``` + +```js filename="app/api/route.js" switcher +import { headers } from 'next/headers' + +export async function GET(request) { + const headersList = headers() + const referer = headersList.get('referer') + + return new Response('Hello, Next.js!', { + status: 200, + headers: { referer: referer }, + }) +} +``` + +Alternatively, you can use abstractions on top of the underlying Web APIs to read headers ([`NextRequest`](/docs/app/api-reference/functions/next-request)): + +```ts filename="app/api/route.ts" switcher +import { type NextRequest } from 'next/server' + +export async function GET(request: NextRequest) { + const requestHeaders = new Headers(request.headers) +} +``` + +```js filename="app/api/route.js" switcher +export async function GET(request) { + const requestHeaders = new Headers(request.headers) +} +``` + +### Redirects + +```ts filename="app/api/route.ts" switcher +import { redirect } from 'next/navigation' + +export async function GET(request: Request) { + redirect('https://nextjs.org/') +} +``` + +```js filename="app/api/route.js" switcher +import { redirect } from 'next/navigation' + +export async function GET(request) { + redirect('https://nextjs.org/') +} +``` + +### Dynamic Route Segments + +> We recommend reading the [Defining Routes](/docs/app/building-your-application/routing/defining-routes) page before continuing. + +Route Handlers can use [Dynamic Segments](/docs/app/building-your-application/routing/dynamic-routes) to create request handlers from dynamic data. + +```ts filename="app/items/[slug]/route.ts" switcher +export async function GET( + request: Request, + { params }: { params: { slug: string } } +) { + const slug = params.slug // 'a', 'b', or 'c' +} +``` + +```js filename="app/items/[slug]/route.js" switcher +export async function GET(request, { params }) { + const slug = params.slug // 'a', 'b', or 'c' +} +``` + +| Route | Example URL | `params` | +| --------------------------- | ----------- | --------------- | +| `app/items/[slug]/route.js` | `/items/a` | `{ slug: 'a' }` | +| `app/items/[slug]/route.js` | `/items/b` | `{ slug: 'b' }` | +| `app/items/[slug]/route.js` | `/items/c` | `{ slug: 'c' }` | + +### Streaming + +```ts filename="app/api/route.ts" switcher +// https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream#convert_async_iterator_to_stream +function iteratorToStream(iterator: any) { + return new ReadableStream({ + async pull(controller) { + const { value, done } = await iterator.next() + + if (done) { + controller.close() + } else { + controller.enqueue(value) + } + }, + }) +} + +function sleep(time: number) { + return new Promise((resolve) => { + setTimeout(resolve, time) + }) +} + +const encoder = new TextEncoder() + +async function* makeIterator() { + yield encoder.encode('<p>One</p>') + await sleep(200) + yield encoder.encode('<p>Two</p>') + await sleep(200) + yield encoder.encode('<p>Three</p>') +} + +export async function GET() { + const iterator = makeIterator() + const stream = iteratorToStream(iterator) + + return new Response(stream) +} +``` + +```js filename="app/api/route.js" switcher +// https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream#convert_async_iterator_to_stream +function iteratorToStream(iterator) { + return new ReadableStream({ + async pull(controller) { + const { value, done } = await iterator.next() + + if (done) { + controller.close() + } else { + controller.enqueue(value) + } + }, + }) +} + +function sleep(time) { + return new Promise((resolve) => { + setTimeout(resolve, time) + }) +} + +const encoder = new TextEncoder() + +async function* makeIterator() { + yield encoder.encode('<p>One</p>') + await sleep(200) + yield encoder.encode('<p>Two</p>') + await sleep(200) + yield encoder.encode('<p>Three</p>') +} + +export async function GET() { + const iterator = makeIterator() + const stream = iteratorToStream(iterator) + + return new Response(stream) +} +``` + +### Request Body + +You can read the `Request` body using the standard Web API methods: + +```ts filename="app/items/route.ts" switcher +import { NextResponse } from 'next/server' + +export async function POST(request: Request) { + const res = await request.json() + return NextResponse.json({ res }) +} +``` + +```js filename="app/items/route.js" switcher +import { NextResponse } from 'next/server' + +export async function POST(request) { + const res = await request.json() + return NextResponse.json({ res }) +} +``` + +### CORS + +You can set CORS headers on a `Response` using the standard Web API methods: + +```ts filename="app/api/route.ts" switcher +export async function GET(request: Request) { + return new Response('Hello, Next.js!', { + status: 200, + headers: { + 'Access-Control-Allow-Origin': '*', + 'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS', + 'Access-Control-Allow-Headers': 'Content-Type, Authorization', + }, + }) +} +``` + +```js filename="app/api/route.js" switcher +export async function GET(request) { + return new Response('Hello, Next.js!', { + status: 200, + headers: { + 'Access-Control-Allow-Origin': '*', + 'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS', + 'Access-Control-Allow-Headers': 'Content-Type, Authorization', + }, + }) +} +``` + +### Edge and Node.js Runtimes + +Route Handlers have an isomorphic Web API to support both Edge and Node.js runtimes seamlessly, including support for streaming. Since Route Handlers use the same [route segment configuration](/docs/app/api-reference/file-conventions/route-segment-config) as Pages and Layouts, they support long-awaited features like general-purpose [statically regenerated](/docs/app/building-your-application/data-fetching/revalidating) Route Handlers. + +You can use the `runtime` segment config option to specify the runtime: + +```ts +export const runtime = 'edge' // 'nodejs' is the default +``` + +### Non-UI Responses + +You can use Route Handlers to return non-UI content. Note that [`sitemap.xml`](/docs/app/api-reference/file-conventions/metadata/sitemap#generate-a-sitemap), [`robots.txt`](/docs/app/api-reference/file-conventions/metadata/robots#generate-a-robots-file), [`app icons`](/docs/app/api-reference/file-conventions/metadata/app-icons#generate-icons-using-code-js-ts-tsx), and [open graph images](/docs/app/api-reference/file-conventions/metadata/opengraph-image) all have built-in support. + +```ts filename="app/rss.xml/route.ts" switcher +export async function GET() { + return new Response(`<?xml version="1.0" encoding="UTF-8" ?> +<rss version="2.0"> + +<channel> + <title>Next.js Documentation + https://nextjs.org/docs + The React Framework for the Web + + +`) +} +``` + +```js filename="app/rss.xml/route.js" switcher +export async function GET() { + return new Response(` + + + + Next.js Documentation + https://nextjs.org/docs + The React Framework for the Web + + +`) +} +``` + +### Segment Config Options + +Route Handlers use the same [route segment configuration](/docs/app/api-reference/file-conventions/route-segment-config) as pages and layouts. + +```ts filename="app/items/route.ts" switcher +export const dynamic = 'auto' +export const dynamicParams = true +export const revalidate = false +export const fetchCache = 'auto' +export const runtime = 'nodejs' +export const preferredRegion = 'auto' +``` + +```js filename="app/items/route.js" switcher +export const dynamic = 'auto' +export const dynamicParams = true +export const revalidate = false +export const fetchCache = 'auto' +export const runtime = 'nodejs' +export const preferredRegion = 'auto' +``` + +See the [API reference](/docs/app/api-reference/file-conventions/route-segment-config) for more details. diff --git a/docs/02-app/01-building-your-application/01-routing/11-middleware.mdx b/docs/02-app/01-building-your-application/01-routing/11-middleware.mdx new file mode 100644 index 0000000000000..c85bbe75f7833 --- /dev/null +++ b/docs/02-app/01-building-your-application/01-routing/11-middleware.mdx @@ -0,0 +1,326 @@ +--- +title: Middleware +description: Learn how to use Middleware to run code before a request is completed. +--- + +Middleware allows you to run code before a request is completed. Then, based on the incoming request, you can modify the response by rewriting, redirecting, modifying the request or response headers, or responding directly. + +Middleware runs before cached content and routes are matched. See [Matching Paths](#matching-paths) for more details. + +## Convention + +Use the file `middleware.ts` (or `.js`) in the root of your project to define Middleware. For example, at the same level as `pages` or `app`, or inside `src` if applicable. + +## Example + +```ts filename="middleware.ts" +import { NextResponse } from 'next/server' +import type { NextRequest } from 'next/server' + +// This function can be marked `async` if using `await` inside +export function middleware(request: NextRequest) { + return NextResponse.redirect(new URL('/home', request.url)) +} + +// See "Matching Paths" below to learn more +export const config = { + matcher: '/about/:path*', +} +``` + +## Matching Paths + +Middleware will be invoked for **every route in your project**. The following is the execution order: + +1. `headers` from `next.config.js` +2. `redirects` from `next.config.js` +3. Middleware (`rewrites`, `redirects`, etc.) +4. `beforeFiles` (`rewrites`) from `next.config.js` +5. Filesystem routes (`public/`, `_next/static/`, `pages/`, `app/`, etc.) +6. `afterFiles` (`rewrites`) from `next.config.js` +7. Dynamic Routes (`/blog/[slug]`) +8. `fallback` (`rewrites`) from `next.config.js` + +There are two ways to define which paths Middleware will run on: + +1. [Custom matcher config](#matcher) +2. [Conditional statements](#conditional-statements) + +### Matcher + +`matcher` allows you to filter Middleware to run on specific paths. + +```js filename="middleware.js" +export const config = { + matcher: '/about/:path*', +} +``` + +You can match a single path or multiple paths with an array syntax: + +```js filename="middleware.js" +export const config = { + matcher: ['/about/:path*', '/dashboard/:path*'], +} +``` + +The `matcher` config allows full regex so matching like negative lookaheads or character matching is supported. An example of a negative lookahead to match all except specific paths can be seen here: + +```js filename="middleware.js" +export const config = { + matcher: [ + /* + * Match all request paths except for the ones starting with: + * - api (API routes) + * - _next/static (static files) + * - _next/image (image optimization files) + * - favicon.ico (favicon file) + */ + '/((?!api|_next/static|_next/image|favicon.ico).*)', + ], +} +``` + +> **Note**: The `matcher` values need to be constants so they can be statically analyzed at build-time. Dynamic values such as variables will be ignored. + +Configured matchers: + +1. MUST start with `/` +2. Can include named parameters: `/about/:path` matches `/about/a` and `/about/b` but not `/about/a/c` +3. Can have modifiers on named parameters (starting with `:`): `/about/:path*` matches `/about/a/b/c` because `*` is _zero or more_. `?` is _zero or one_ and `+` _one or more_ +4. Can use regular expression enclosed in parenthesis: `/about/(.*)` is the same as `/about/:path*` + +Read more details on [path-to-regexp](https://github.com/pillarjs/path-to-regexp#path-to-regexp-1) documentation. + +> **Note**: For backward compatibility, Next.js always considers `/public` as `/public/index`. Therefore, a matcher of `/public/:path` will match. + +### Conditional Statements + +```ts filename="middleware.ts" +import { NextResponse } from 'next/server' +import type { NextRequest } from 'next/server' + +export function middleware(request: NextRequest) { + if (request.nextUrl.pathname.startsWith('/about')) { + return NextResponse.rewrite(new URL('/about-2', request.url)) + } + + if (request.nextUrl.pathname.startsWith('/dashboard')) { + return NextResponse.rewrite(new URL('/dashboard/user', request.url)) + } +} +``` + +## NextResponse + +The `NextResponse` API allows you to: + +- `redirect` the incoming request to a different URL +- `rewrite` the response by displaying a given URL +- Set request headers for API Routes, `getServerSideProps`, and `rewrite` destinations +- Set response cookies +- Set response headers + +To produce a response from Middleware, you can: + +1. `rewrite` to a route ([Page](/docs/pages/building-your-application/routing/pages-and-layouts) or [Edge API Route](/docs/pages/building-your-application/routing/api-routes)) that produces a response +2. return a `NextResponse` directly. See [Producing a Response](#producing-a-response) + +## Using Cookies + +Cookies are regular headers. On a `Request`, they are stored in the `Cookie` header. On a `Response` they are in the `Set-Cookie` header. Next.js provides a convenient way to access and manipulate these cookies through the `cookies` extension on `NextRequest` and `NextResponse`. + +1. For incoming requests, `cookies` comes with the following methods: `get`, `getAll`, `set`, and `delete` cookies. You can check for the existence of a cookie with `has` or remove all cookies with `clear`. +2. For outgoing responses, `cookies` have the following methods `get`, `getAll`, `set`, and `delete`. + +```ts filename="middleware.ts" +import { NextResponse } from 'next/server' +import type { NextRequest } from 'next/server' + +export function middleware(request: NextRequest) { + // Assume a "Cookie:nextjs=fast" header to be present on the incoming request + // Getting cookies from the request using the `RequestCookies` API + let cookie = request.cookies.get('nextjs')?.value + console.log(cookie) // => 'fast' + const allCookies = request.cookies.getAll() + console.log(allCookies) // => [{ name: 'nextjs', value: 'fast' }] + + request.cookies.has('nextjs') // => true + request.cookies.delete('nextjs') + request.cookies.has('nextjs') // => false + + // Setting cookies on the response using the `ResponseCookies` API + const response = NextResponse.next() + response.cookies.set('vercel', 'fast') + response.cookies.set({ + name: 'vercel', + value: 'fast', + path: '/', + }) + cookie = response.cookies.get('vercel') + console.log(cookie) // => { name: 'vercel', value: 'fast', Path: '/' } + // The outgoing response will have a `Set-Cookie:vercel=fast;path=/test` header. + + return response +} +``` + +## Setting Headers + +You can set request and response headers using the `NextResponse` API (setting _request_ headers is available since Next.js v13.0.0). + +```ts filename="middleware.ts" switcher +import { NextResponse } from 'next/server' +import type { NextRequest } from 'next/server' + +export function middleware(request: NextRequest) { + // Clone the request headers and set a new header `x-hello-from-middleware1` + const requestHeaders = new Headers(request.headers) + requestHeaders.set('x-hello-from-middleware1', 'hello') + + // You can also set request headers in NextResponse.rewrite + const response = NextResponse.next({ + request: { + // New request headers + headers: requestHeaders, + }, + }) + + // Set a new response header `x-hello-from-middleware2` + response.headers.set('x-hello-from-middleware2', 'hello') + return response +} +``` + +```js filename="middleware.js" switcher +import { NextResponse } from 'next/server' + +export function middleware(request) { + // Clone the request headers and set a new header `x-hello-from-middleware1` + const requestHeaders = new Headers(request.headers) + requestHeaders.set('x-hello-from-middleware1', 'hello') + + // You can also set request headers in NextResponse.rewrite + const response = NextResponse.next({ + request: { + // New request headers + headers: requestHeaders, + }, + }) + + // Set a new response header `x-hello-from-middleware2` + response.headers.set('x-hello-from-middleware2', 'hello') + return response +} +``` + +> **Note**: Avoid setting large headers as it might cause [431 Request Header Fields Too Large](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/431) error depending on your backend web server configuration. + +## Producing a Response + +You can respond from Middleware directly by returning a `Response` or `NextResponse` instance. (This is available since [Next.js v13.1.0](https://nextjs.org/blog/next-13-1#nextjs-advanced-middleware)) + +```ts filename="middleware.ts" switcher +import { NextRequest, NextResponse } from 'next/server' +import { isAuthenticated } from '@lib/auth' + +// Limit the middleware to paths starting with `/api/` +export const config = { + matcher: '/api/:function*', +} + +export function middleware(request: NextRequest) { + // Call our authentication function to check the request + if (!isAuthenticated(request)) { + // Respond with JSON indicating an error message + return new NextResponse( + JSON.stringify({ success: false, message: 'authentication failed' }), + { status: 401, headers: { 'content-type': 'application/json' } } + ) + } +} +``` + +```js filename="middleware.js" switcher +import { NextResponse } from 'next/server' +import { isAuthenticated } from '@lib/auth' + +// Limit the middleware to paths starting with `/api/` +export const config = { + matcher: '/api/:function*', +} + +export function middleware(request) { + // Call our authentication function to check the request + if (!isAuthenticated(request)) { + // Respond with JSON indicating an error message + return new NextResponse( + JSON.stringify({ success: false, message: 'authentication failed' }), + { status: 401, headers: { 'content-type': 'application/json' } } + ) + } +} +``` + +## Advanced Middleware Flags + +In `v13.1` of Next.js two additional flags were introduced for middleware, `skipMiddlewareUrlNormalize` and `skipTrailingSlashRedirect` to handle advanced use cases. + +`skipTrailingSlashRedirect` allows disabling Next.js default redirects for adding or removing trailing slashes allowing custom handling inside middleware which can allow maintaining the trailing slash for some paths but not others allowing easier incremental migrations. + +```js filename="next.config.js" +module.exports = { + skipTrailingSlashRedirect: true, +} +``` + +```js filename="middleware.js" +const legacyPrefixes = ['/docs', '/blog'] + +export default async function middleware(req) { + const { pathname } = req.nextUrl + + if (legacyPrefixes.some((prefix) => pathname.startsWith(prefix))) { + return NextResponse.next() + } + + // apply trailing slash handling + if ( + !pathname.endsWith('/') && + !pathname.match(/((?!\.well-known(?:\/.*)?)(?:[^/]+\/)*[^/]+\.\w+)/) + ) { + req.nextUrl.pathname += '/' + return NextResponse.redirect(req.nextUrl) + } +} +``` + +`skipMiddlewareUrlNormalize` allows disabling the URL normalizing Next.js does to make handling direct visits and client-transitions the same. There are some advanced cases where you need full control using the original URL which this unlocks. + +```js filename="next.config.js" +module.exports = { + skipMiddlewareUrlNormalize: true, +} +``` + +```js filename="middleware.js" +export default async function middleware(req) { + const { pathname } = req.nextUrl + + // GET /_next/data/build-id/hello.json + + console.log(pathname) + // with the flag this now /_next/data/build-id/hello.json + // without the flag this would be normalized to /hello +} +``` + +## Version History + +| Version | Changes | +| --------- | --------------------------------------------------------------------------------------------- | +| `v13.1.0` | Advanced Middleware flags added | +| `v13.0.0` | Middleware can modify request headers, response headers, and send responses | +| `v12.2.0` | Middleware is stable, please see the [upgrade guide](/docs/messages/middleware-upgrade-guide) | +| `v12.0.9` | Enforce absolute URLs in Edge Runtime ([PR](https://github.com/vercel/next.js/pull/33410)) | +| `v12.0.0` | Middleware (Beta) added | diff --git a/docs/02-app/01-building-your-application/01-routing/12-colocation.mdx b/docs/02-app/01-building-your-application/01-routing/12-colocation.mdx new file mode 100644 index 0000000000000..67500ffc3eeca --- /dev/null +++ b/docs/02-app/01-building-your-application/01-routing/12-colocation.mdx @@ -0,0 +1,180 @@ +--- +title: Project Organization and File Colocation +nav_title: Project Organization +description: Learn how to organize your Next.js project and colocate files. +related: + links: + - app/building-your-application/routing/defining-routes + - app/building-your-application/routing/route-groups + - app/building-your-application/configuring/src-directory + - app/building-your-application/configuring/absolute-imports-and-module-aliases +--- + +Apart from [routing folder and file conventions](/docs/getting-started/project-structure#app-routing-conventions), Next.js is **unopinionated** about how you organize and colocate your project files. + +This page shares default behavior and features you can use to organize your project. + +- [Safe colocation by default](#safe-colocation-by-default) +- [Project organization features](#project-organization-features) +- [Project organization strategies](#project-organization-strategies) + +## Safe colocation by default + +In the `app` directory, [nested folder hierarchy](/docs/app/building-your-application/routing#route-segments) defines route structure. + +Each folder represents a route segment that is mapped to a corresponding segment in a URL path. + +However, even though route structure is defined through folders, a route is **not publically accessible** until a `page.js` or `route.js` file is added to a route segment. + +A diagram showing how a route is not publically accessible until a page.js or route.js file is added to a route segment. + +And, even when a route is made publically accessible, only the **content returned** by `page.js` or `route.js` is sent to the client. + +A diagram showing how page.js and route.js files make routes publically accessible. + +This means that **project files** can be **safely colocated** inside route segments in the `app` directory without accidentally being routable. + +A diagram showing colocated project files are not routable even when a segment contains a page.js or route.js file. + +> **Good to know**: +> +> - This is different from the `pages` directory, where any file in `pages` is considered a route. +> - While you **can** colocate your project files in `app` you don't **have** to. If you prefer, you can [keep them outside the `app` directory](#store-project-files-outside-of-app). + +## Project organization features + +Next.js provides several features to help you organize your project. + +### Private Folders + +Private folders can be created by prefixing a folder with an underscore: `_folderName` + +This indicates the folder is a private implementation detail and should not be considered by the routing system, thereby **opting the folder and all its subfolders** out of routing. + +An example folder structure using private folders + +Since files in the `app` directory can be [safely colocated by default](#safe-colocation-by-default), private folders are not required for colocation. However, they can be useful for: + +- Separating UI logic from routing logic. +- Consistently organizing internal files across a project and the Next.js ecosystem. +- Sorting and grouping files in code editors. +- Avoiding potential naming conflicts with future Next.js file conventions. + +> **Good to know** +> +> - While not a framework convention, you might also consider marking files outside private folders as "private" using the same underscore pattern. +> - You can create URL segments that start with an underscore by prefixing the folder name with `%5F` (the URL-encoded form of an underscore): `%5FfolderName`. +> - If you don't use private folders, it would be helpful to know Next.js [special file conventions](/docs/getting-started/project-structure#routing-files) to prevent unexpected naming conflicts. + +### Route Groups + +Route groups can be created by wrapping a folder in parenthesis: `(folderName)` + +This indicates the folder is for organizational purposes and should **not be included** in the route's URL path. + +An example folder structure using route groups + +Route groups are useful for: + +- [Organizing routes into groups](/docs/app/building-your-application/routing/route-groups#organize-routes-without-affecting-the-url-path) e.g. by site section, intent, or team. +- Enabling nested layouts in the same route segment level: + - [Creating multiple nested layouts in the same segment, including multiple root layouts](/docs/app/building-your-application/routing/route-groups#creating-multiple-root-layouts) + - [Adding a layout to a subset of routes in a common segment](/docs/app/building-your-application/routing/route-groups#opting-specific-segments-into-a-layout) + +### `src` Directory + +Next.js supports storing application code (including `app`) inside an optional [`src` directory](/docs/app/building-your-application/configuring/src-directory). This separates application code from project configuration files which mostly live in the root of a project. + +An example folder structure with the `src` directory + +### Module Path Aliases + +Next.js supports [Module Path Aliases](/docs/app/building-your-application/configuring/absolute-imports-and-module-aliases) which make it easier to read and maintain imports across deeply nested project files. + +```tsx filename="app/dashboard/settings/analytics/page.js" +// before +import { Button } from '../../../components/button' + +// after +import { Button } from '@/components/button' +``` + +## Project organization strategies + +There is no "right" or "wrong" way when it comes to organizing your own files and folders in a Next.js project. + +The following section lists a very high-level overview of common strategies. The simplest takeaway is to choose a strategy that works for you and your team and be consistent across the project. + +> **Note:** In our examples below, we're using `components` and `lib` folders as generalized placeholders, their naming has no special framework significance and your projects might use other folders like `ui`, `utils`, `hooks`, `styles`, etc. + +### Store project files outside of `app` + +This strategy stores all application code in shared folders in the **root of your project** and keeps the `app` directory purely for routing purposes. + +An example folder structure with project files outside of app + +### Store project files in top-level folders inside of `app` + +This strategy stores all application code in shared folders in the **root of the `app` directory**. + +An example folder structure with project files inside app + +### Split project files by feature or route + +This strategy stores globally shared application code in the root `app` directory and **splits** more specific application code into the route segments that use them. + +An example folder structure with project files split by feature or route diff --git a/docs/02-app/01-building-your-application/01-routing/13-internationalization.mdx b/docs/02-app/01-building-your-application/01-routing/13-internationalization.mdx new file mode 100644 index 0000000000000..5e1aa1eb309b8 --- /dev/null +++ b/docs/02-app/01-building-your-application/01-routing/13-internationalization.mdx @@ -0,0 +1,153 @@ +--- +title: Internationalization +description: Add support for multiple languages with internationalized routing and localized content. +--- + +Next.js enables you to configure the routing and rendering of content to support multiple languages. Making your site adaptive to different locales includes translated content (localization) and internationalized routes. + +## Terminology + +- **Locale:** An identifier for a set of language and formatting preferences. This usually includes the preferred language of the user and possibly their geographic region. + - `en-US`: English as spoken in the United States + - `nl-NL`: Dutch as spoken in the Netherlands + - `nl`: Dutch, no specific region + +## Routing Overview + +It’s recommended to use the user’s language preferences in the browser to select which locale to use. Changing your preferred language will modify the incoming `Accept-Language` header to your application. + +For example, using the following libraries, you can look at an incoming `Request` to determine which locale to select, based on the `Headers`, locales you plan to support, and the default locale. + +```jsx filename="middleware.js" +import { match } from '@formatjs/intl-localematcher' +import Negotiator from 'negotiator' + +let headers = { 'accept-language': 'en-US,en;q=0.5' } +let languages = new Negotiator({ headers }).languages() +let locales = ['en-US', 'nl-NL', 'nl'] +let defaultLocale = 'en-US' + +match(languages, locales, defaultLocale) // -> 'en-US' +``` + +Routing can be internationalized by either the sub-path (`/fr/products`) or domain (`my-site.fr/products`). With this information, you can now redirect the user based on the locale inside [Middleware](/docs/app/building-your-application/routing/middleware). + +```jsx filename="middleware.js" +import { NextResponse } from 'next/server' + +let locales = ['en-US', 'nl-NL', 'nl'] + +// Get the preferred locale, similar to above or using a library +function getLocale(request) { ... } + +export function middleware(request) { + // Check if there is any supported locale in the pathname + const pathname = request.nextUrl.pathname + const pathnameIsMissingLocale = locales.every( + (locale) => !pathname.startsWith(`/${locale}/`) && pathname !== `/${locale}` + ) + + // Redirect if there is no locale + if (pathnameIsMissingLocale) { + const locale = getLocale(request) + + // e.g. incoming request is /products + // The new URL is now /en-US/products + return NextResponse.redirect( + new URL(`/${locale}/${pathname}`, request.url) + ) + } +} + +export const config = { + matcher: [ + // Skip all internal paths (_next) + '/((?!_next).*)', + // Optional: only run on root (/) URL + // '/' + ], +} +``` + +Finally, ensure all special files inside `app/` are nested under `app/[lang]`. This enables the Next.js router to dynamically handle different locales in the route, and forward the `lang` parameter to every layout and page. For example: + +```jsx filename="app/[lang]/page.js" +// You now have access to the current locale +// e.g. /en-US/products -> `lang` is "en-US" +export default async function Page({ params: { lang } }) { + return ... +} +``` + +The root layout can also be nested in the new folder (e.g. `app/[lang]/layout.js`). + +## Localization + +Changing displayed content based on the user’s preferred locale, or localization, is not something specific to Next.js. The patterns described below would work the same with any web application. + +Let’s assume we want to support both English and Dutch content inside our application. We might maintain two different “dictionaries”, which are objects that give us a mapping from some key to a localized string. For example: + +```jsx filename="dictionaries/en.json" +{ + "products": { + "cart": "Add to Cart" + } +} +``` + +```jsx filename="dictionaries/nl.json" +{ + "products": { + "cart": "Toevoegen aan Winkelwagen" + } +} +``` + +We can then create a `getDictionary` function to load the translations for the requested locale: + +```jsx filename="app/[lang]/dictionaries.js" +import 'server-only' + +const dictionaries = { + en: () => import('./dictionaries/en.json').then((module) => module.default), + nl: () => import('./dictionaries/nl.json').then((module) => module.default), +} + +export const getDictionary = async (locale) => dictionaries[locale]() +``` + +Given the currently selected language, we can fetch the dictionary inside of a layout or page. + +```jsx filename="app/[lang]/page.js" +import { getDictionary } from './dictionaries' + +export default async function Page({ params: { lang } }) { + const dict = await getDictionary(lang) // en + return // Add to Cart +} +``` + +Because all layouts and pages in the `app/` directory default to [Server Components](/docs/getting-started/react-essentials#server-components), we do not need to worry about the size of the translation files affecting our client-side JavaScript bundle size. This code will **only run on the server**, and only the resulting HTML will be sent to the browser. + +## Static Generation + +To generate static routes for a given set of locales, we can use `generateStaticParams` with any page or layout. This can be global, for example, in the root layout: + +```jsx filename="app/[lang]/layout.js" +export async function generateStaticParams() { + return [{ lang: 'en-US' }, { lang: 'de' }] +} + +export default function Root({ children, params }) { + return ( + + {children} + + ) +} +``` + +## Examples + +- [Minimal i18n routing and translations](https://github.com/vercel/next.js/tree/canary/examples/app-dir-i18n-routing) +- [next-intl](https://next-intl-docs.vercel.app/docs/next-13) diff --git a/docs/02-app/01-building-your-application/01-routing/index.mdx b/docs/02-app/01-building-your-application/01-routing/index.mdx new file mode 100644 index 0000000000000..143af084afa89 --- /dev/null +++ b/docs/02-app/01-building-your-application/01-routing/index.mdx @@ -0,0 +1,184 @@ +--- +title: Routing Fundamentals +nav_title: Routing +description: Learn the fundamentals of routing for front-end applications. +--- + +The skeleton of every application is routing. This page will introduce you to the **fundamental concepts** of routing for the web and how to handle routing in Next.js. + +## Terminology + +First, you will see these terms being used throughout the documentation. Here's a quick reference: + +Terminology for Component Tree + +- **Tree:** A convention for visualizing a hierarchical structure. For example, a component tree with parent and children components, a folder structure, etc. +- **Subtree:** Part of a tree, starting at a new root (first) and ending at the leaves (last). +- **Root**: The first node in a tree or subtree, such as a root layout. +- **Leaf:** Nodes in a subtree that have no children, such as the last segment in a URL path. + +Terminology for URL Anatomy + +- **URL Segment:** Part of the URL path delimited by slashes. +- **URL Path:** Part of the URL that comes after the domain (composed of segments). + +## The `app` Router + +In version 13, Next.js introduced a new **App Router** built on [React Server Components](/docs/getting-started/react-essentials#server-components), which supports shared layouts, nested routing, loading states, error handling, and more. + +The App Router works in a new directory named `app`. The `app` directory works alongside the `pages` directory to allow for incremental adoption. This allows you to opt some routes of your application into the new behavior while keeping other routes in the `pages` directory for previous behavior. If your application uses the `pages` directory, please also see the [Pages Router](/docs/pages/building-your-application/routing) documentation. + +> **Good to know:** The App Router takes priority over the Pages Router. Routes across directories should not resolve to the same URL path and will cause a build-time error to prevent a conflict. + +Next.js App Directory + +By default, components inside `app` are [React Server Components](/docs/getting-started/react-essentials#server-components). This is a performance optimization and allows you to easily adopt them, and you can also use [Client Components](/docs/getting-started/react-essentials#client-components). + +> **Recommendation:** Check out the [Server and Client Components](/docs/getting-started/react-essentials) page if you're new to Server Components. + +## Roles of Folders and Files + +Next.js uses a file-system based router where: + +- **Folders** are used to define routes. A route is a single path of nested folders, following the file-system hierarchy from the **root folder** down to a final **leaf folder** that includes a `page.js` file. See [Defining Routes](/docs/app/building-your-application/routing/defining-routes). +- **Files** are used to create UI that is shown for a route segment. See [special files](#file-conventions). + +## Route Segments + +Each folder in a route represents a **route segment**. Each route segment is mapped to a corresponding **segment** in a **URL path**. + +How Route Segments Map to URL Segments + +## Nested Routes + +To create a nested route, you can nest folders inside each other. For example, you can add a new `/dashboard/settings` route by nesting two new folders in the `app` directory. + +The `/dashboard/settings` route is composed of three segments: + +- `/` (Root segment) +- `dashboard` (Segment) +- `settings` (Leaf segment) + +## File Conventions + +Next.js provides a set of special files to create UI with specific behavior in nested routes: + +| | | +| ------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------- | +| [`layout`](/docs/app/building-your-application/routing/pages-and-layouts#layouts) | Shared UI for a segment and its children | +| [`page`](/docs/app/building-your-application/routing/pages-and-layouts#pages) | Unique UI of a route and make routes publicly accessible | +| [`loading`](/docs/app/building-your-application/routing/loading-ui-and-streaming) | Loading UI for a segment and its children | +| [`not-found`](/docs/app/api-reference/file-conventions/not-found) | Not found UI for a segment and its children | +| [`error`](/docs/app/building-your-application/routing/error-handling) | Error UI for a segment and its children | +| [`global-error`](/docs/app/building-your-application/routing/error-handling) | Global Error UI | +| [`route`](/docs/app/building-your-application/routing/router-handlers) | Server-side API endpoint | +| [`template`](/docs/app/building-your-application/routing/pages-and-layouts#templates) | Specialized re-rendered Layout UI | +| [`default`](/docs/app/api-reference/file-conventions/default) | Fallback UI for [Parallel Routes](/docs/app/building-your-application/routing/parallel-routes) | + +> **Good to know:** `.js`, `.jsx`, or `.tsx` file extensions can be used for special files. + +## Component Hierarchy + +The React components defined in special files of a route segment are rendered in a specific hierarchy: + +- `layout.js` +- `template.js` +- `error.js` (React error boundary) +- `loading.js` (React suspense boundary) +- `not-found.js` (React error boundary) +- `page.js` or nested `layout.js` + +Component Hierarchy for File Conventions + +In a nested route, the components of a segment will be nested **inside** the components of its parent segment. + +Nested File Conventions Component Hierarchy + +## Colocation + +In addition to special files, you have the option to colocate your own files (e.g. components, styles, tests etc) inside folders in the `app` directory. + +This is because while folders define routes, only the contents returned by `page.js` or `route.js` are publically addressable. + +An example folder structure with colocated files + +Learn more about [Project Organization and Colocation](/docs/app/building-your-application/routing/colocation). + +## Server-Centric Routing with Client-side Navigation + +Unlike the `pages` directory which uses client-side routing, the App Router uses **server-centric routing** to align with [Server Components](/docs/getting-started/react-essentials#server-components) and [data fetching on the server](/docs/app/building-your-application/data-fetching/fetching). With server-centric routing, the client does not have to download a route map and the same request for Server Components can be used to look up routes. This optimization is useful for all applications, but has a larger impact on applications with many routes. + +Although routing is server-centric, the router uses **client-side navigation** with the [Link Component](/docs/app/building-your-application/routing/linking-and-navigating#link-component) - resembling the behavior of a Single-Page Application. This means when a user navigates to a new route, the browser will not reload the page. Instead, the URL will be updated and Next.js will [only render the segments that change](#partial-rendering). + +Additionally, as users navigate around the app, the router will store the result of the React Server Component payload in an **in-memory client-side cache**. The cache is split by route segments which allows invalidation at any level and ensures consistency across [React's concurrent renders](https://react.dev/blog/2022/03/29/react-v18#what-is-concurrent-react). This means that for certain cases, the cache of a previously fetched segment can be re-used, further improving performance. + +Learn more about [Linking and Navigating](/docs/app/building-your-application/routing/linking-and-navigating). + +## Partial Rendering + +When navigating between sibling routes (e.g. `/dashboard/settings` and `/dashboard/analytics` below), Next.js will only fetch and render the layouts and pages in routes that change. It will **not** re-fetch or re-render anything above the segments in the subtree. This means that in routes that share a layout, the layout will be preserved when a user navigates between sibling pages. + +How partial rendering works + +Without partial rendering, each navigation would cause the full page to re-render on the server. Rendering only the segment that’s updating reduces the amount of data transferred and execution time, leading to improved performance. + +## Advanced Routing Patterns + +The App Router also provides a set conventions to help you implement more advanced routing patterns. These include: + +- [Parallel Routes](/docs/app/building-your-application/routing/parallel-routes): Allow you to simultaneously show two or more pages in the same view that can be navigated independently. You can use them for split views that have their own sub-navigation. E.g. Dashboards. +- [Intercepting Routes](/docs/app/building-your-application/routing/intercepting-routes): Allow you to intercept a route and show it in the context of another route. You can use these when keeping the context for the current page is important. E.g. Seeing all tasks while editing one task or expanding a photo in a feed. + +These patterns allow you to build richer and more complex UIs, democratizing features that were historically complex for small teams and individual developers to implement. + +## Next Steps + +Now that you understand the fundamentals of routing in Next.js, follow the links below to create your first routes: diff --git a/docs/02-app/01-building-your-application/02-rendering/01-static-and-dynamic-rendering.mdx b/docs/02-app/01-building-your-application/02-rendering/01-static-and-dynamic-rendering.mdx new file mode 100644 index 0000000000000..e9d38195b84a3 --- /dev/null +++ b/docs/02-app/01-building-your-application/02-rendering/01-static-and-dynamic-rendering.mdx @@ -0,0 +1,56 @@ +--- +title: Static and Dynamic Rendering +nav_title: Static and Dynamic +description: Learn about static and dynamic rendering in Next.js. +--- + +In Next.js, a route can be statically or dynamically rendered. + +- In a **static** route, components are rendered on the server at build time. The result of the work is cached and reused on subsequent requests. +- In a **dynamic** route, components are rendered on the server at request time. + +## Static Rendering (Default) + +By default, Next.js statically renders routes to improve performance. This means all the rendering work is done ahead of time and can be served from a Content Delivery Network (CDN) geographically closer to the user. + +## Static Data Fetching (Default) + +By default, Next.js will cache the result of `fetch()` requests that do not specifically opt out of caching behavior. This means that fetch requests that do not set a `cache` option will use the `force-cache` option. + +If any fetch requests in the route use the `revalidate` option, the route will be re-rendered statically during revalidation. + +To learn more about caching data fetching requests, see the [Caching and Revalidating](/docs/app/building-your-application/data-fetching/caching) page. + +## Dynamic Rendering + +During static rendering, if a dynamic function or a dynamic `fetch()` request (no caching) is discovered, Next.js will switch to dynamically rendering the whole route at request time. Any cached data requests can still be re-used during dynamic rendering. + +This table summarizes how [dynamic functions](#dynamic-functions) and [caching](#static-data-fetching-default) affect the rendering behavior of a route: + +| Data Fetching | Dynamic Functions | Rendering | +| --------------- | ----------------- | --------- | +| Static (Cached) | No | Static | +| Static (Cached) | Yes | Dynamic | +| Not Cached | No | Dynamic | +| Not Cached | Yes | Dynamic | + +Note how dynamic functions always opt the route into dynamic rendering, regardless of whether the data fetching is cached or not. In other words, static rendering is dependent not only on the data fetching behavior, but also on the dynamic functions used in the route. + +> **Note:** In the future, Next.js will introduce hybrid server-side rendering where layouts and pages in a route can be independently statically or dynamically rendered, instead of the whole route. + +### Dynamic Functions + +Dynamic functions rely on information that can only be known at request time such as a user's cookies, current requests headers, or the URL's search params. In Next.js, these dynamic functions are: + +- Using [`cookies()`](/docs/app/api-reference/functions/cookies) or [`headers()`](/docs/app/api-reference/functions/headers) in a Server Component will opt the whole route into dynamic rendering at request time. +- Using [`useSearchParams()`](/docs/app/api-reference/functions/use-params) in Client Components will skip static rendering and instead render all Client Components up to the nearest parent Suspense boundary on the client. + - We recommend wrapping the Client Component that uses `useSearchParams()` in a `` boundary. This will allow any Client Components above it to be statically rendered. [Example](/docs/app/api-reference/functions/use-search-params#static-rendering). +- Using the [`searchParams`](/docs/app/api-reference/file-conventions/page#searchparams-optional) [Pages](/docs/app/api-reference/file-conventions/page) prop will opt the page into dynamic rendering at request time. + +## Dynamic Data Fetching + +Dynamic data fetches are `fetch()` requests that specifically opt out of caching behavior by setting the `cache` option to `'no-store'` or `revalidate` to `0`. + +The caching options for all `fetch` requests in a layout or page can also be set using the [segment config](/docs/app/api-reference/file-conventions/route-segment-config) object. + +To learn more about Dynamic Data Fetching, see the [Data Fetching](/docs/app/building-your-application/data-fetching/fetching) page. diff --git a/docs/02-app/01-building-your-application/02-rendering/02-edge-and-nodejs-runtimes.mdx b/docs/02-app/01-building-your-application/02-rendering/02-edge-and-nodejs-runtimes.mdx new file mode 100644 index 0000000000000..55b798b6693bd --- /dev/null +++ b/docs/02-app/01-building-your-application/02-rendering/02-edge-and-nodejs-runtimes.mdx @@ -0,0 +1,71 @@ +--- +title: Edge and Node.js Runtimes +description: Learn about the switchable runtimes (Edge and Node.js) in Next.js. +--- + +In the context of Next.js, "runtime" refers to the set of libraries, APIs, and general functionality available to your code during execution. + +Next.js has two server runtimes where you can render parts of your application code: + +- [Node.js Runtime](#nodejs-runtime) +- [Edge Runtime](#edge-runtime) + +Each runtime has its own set of APIs. Please refer to the [Node.js Docs](https://nodejs.org/docs/latest/api/) and [Edge Docs](/docs/app/api-reference/edge) for the full list of available APIs. Both runtimes can also support [streaming](/docs/app/building-your-application/routing/loading-ui-and-streaming) depending on your deployment infrastructure. + +By default, the `app` directory uses the Node.js runtime. However, you can opt into different runtimes (e.g. Edge) on a per-route basis. + +## Runtime Differences + +There are many considerations to make when choosing a runtime. This table shows the major differences at a glance. If you want a more in-depth analysis of the differences, check out the sections below. + +| | Node | Serverless | Edge | +| --------------------------------------------------------------------------------------------------------------------------------------------------- | ------ | ---------- | ---------------- | +| [Cold Boot](https://vercel.com/docs/concepts/get-started/compute#cold-and-hot-boots?utm_source=next-site&utm_medium=docs&utm_campaign=next-website) | / | ~250ms | Instant | +| [HTTP Streaming](/docs/app/building-your-application/routing/loading-ui-and-streaming) | Yes | Yes | Yes | +| IO | All | All | `fetch` | +| Scalability | / | High | Highest | +| Security | Normal | High | High | +| Latency | Normal | Low | Lowest | +| npm Packages | All | All | A smaller subset | + +### Edge Runtime + +In Next.js, the lightweight Edge Runtime is a subset of available Node.js APIs. + +The Edge Runtime is ideal if you need to deliver dynamic, personalized content at low latency with small, simple functions. The Edge Runtime's speed comes from its minimal use of resources, but that can be limiting in many scenarios. + +For example, code executed in the Edge Runtime [on Vercel cannot exceed between 1 MB and 4 MB](https://vercel.com/docs/concepts/limits/overview#edge-middleware-and-edge-functions-size), this limit includes imported packages, fonts and files, and will vary depending on your deployment infrastructure. + +### Node.js Runtime + +Using the Node.js runtime gives you access to all Node.js APIs, and all npm packages that rely on them. However, it's not as fast to start up as routes using the Edge runtime. + +Deploying your Next.js application to a Node.js server will require managing, scaling, and configuring your infrastructure. Alternatively, you can consider deploying your Next.js application to a serverless platform like Vercel, which will handle this for you. + +### Serverless Node.js + +Serverless is ideal if you need a scalable solution that can handle more complex computational loads than the Edge Runtime. With Serverless Functions on Vercel, for example, your overall code size is [50MB](https://vercel.com/docs/concepts/limits/overview#serverless-function-size) including imported packages, fonts, and files. + +The downside compared to routes using the [Edge](https://vercel.com/docs/concepts/functions/edge-functions) is that it can take hundreds of milliseconds for Serverless Functions to boot up before they begin processing requests. Depending on the amount of traffic your site receives, this could be a frequent occurrence as the functions are not frequently "warm". + + + +## Examples + +### Segment Runtime Option + +You can specify a runtime for individual route segments in your Next.js application. To do so, [declare a variable called `runtime` and export it](/docs/app/api-reference/file-conventions/route-segment-config). The variable must be a string, and must have a value of either `'nodejs'` or `'edge'` runtime. + +The following example demonstrates a page route segment that exports a `runtime` with a value of `'edge'`: + +```tsx filename="app/page.tsx" switcher +export const runtime = 'edge' // 'nodejs' (default) | 'edge' +``` + +```jsx filename="app/page.js" switcher +export const runtime = 'edge' // 'nodejs' (default) | 'edge' +``` + +If the segment runtime is _not_ set, the default `nodejs` runtime will be used. You do not need to use the `runtime` option if you do not plan to change from the Node.js runtime. + + diff --git a/docs/02-app/01-building-your-application/02-rendering/index.mdx b/docs/02-app/01-building-your-application/02-rendering/index.mdx new file mode 100644 index 0000000000000..5d84de7a46785 --- /dev/null +++ b/docs/02-app/01-building-your-application/02-rendering/index.mdx @@ -0,0 +1,73 @@ +--- +title: Rendering +description: Learn the differences between Next.js rendering environments, strategies, and runtimes. +--- + +Rendering converts the code you write into user interfaces. + +React 18 and Next.js 13 introduced new ways to render your application. This page will help you understand the differences between rendering environments, strategies, runtimes, and how to opt into them. + +## Rendering Environments + +There are two environments where your application code can be rendered: the client and the server. + +Client and Server Environemnts + +- The **client** refers to the browser on a user's device that sends a request to a server for your application code. It then turns the response from the server into an interface the user can interact with. +- The **server** refers to the computer in a data center that stores your application code, receives requests from a client, does some computation, and sends back an appropriate response. + +> **Note:** Server can refer to computers in regions where your application is deployed to, the [Edge Network](https://vercel.com/docs/concepts/edge-network/overview) where your application code is distributed, or [Content Delivery Networks (CDNs)](https://developer.mozilla.org/en-US/docs/Glossary/CDN) where the result of the rendering work can be cached. + +## Component-level Client and Server Rendering + +Before React 18, the primary way to render your application **using React** was entirely on the client. + +Next.js provided an easier way to break down your application into **pages** and prerender on the server by generating HTML and sending it to the client to be [hydrated](https://react.dev/reference/react-dom/hydrate#hydrating-server-rendered-html) by React. However, this led to additional JavaScript needed on the client to make the initial HTML interactive. + +Now, with [Server and Client Components](/docs/getting-started/react-essentials), React can render on the client **and** the server meaning you can choose the rendering environment at the component level. + +By default, the [`app` router uses **Server Components**](/docs/getting-started/react-essentials#server-components), allowing you to easily render components on the server and reducing the amount of JavaScript sent to the client. + +## Static and Dynamic Rendering on the Server + +In addition to client-side and server-side rendering with React components, Next.js gives you the option to optimize rendering on the server with **Static** and **Dynamic** Rendering. + +### Static Rendering + +With **Static Rendering**, both Server _and_ Client Components can be prerendered on the server at **build time**. The result of the work is [cached](/docs/app/building-your-application/data-fetching/caching) and reused on subsequent requests. The cached result can also be [revalidated](/docs/app/building-your-application/data-fetching#revalidating-data). + +> **Note:** This is equivalent to [Static Site Generation (SSG)](/docs/pages/building-your-application/rendering/static-site-generation) and [Incremental Static Regeneration (ISR)](/docs/pages/building-your-application/rendering/incremental-static-regeneration) in the [Pages Router](/docs/pages/building-your-application/rendering). + +Server and Client Components are rendered differently during Static Rendering: + +- Client Components have their HTML and JSON prerendered and cached on the server. The cached result is then sent to the client for hydration. +- Server Components are rendered on the server by React, and their payload is used to generate HTML. The same rendered payload is also used to hydrate the components on the client, resulting in no JavaScript needed on the client. + +### Dynamic Rendering + +With Dynamic Rendering, both Server _and_ Client Components are rendered on the server at **request time**. The result of the work is not cached. + +> **Note:** This is equivalent to [Server-Side Rendering (`getServerSideProps()`)](/docs/pages/building-your-application/rendering/server-side-rendering) in the [Pages Router](/docs/pages/building-your-application/rendering). + +To learn more about static and dynamic behavior, see the [Static and Dynamic Rendering](/docs/app/building-your-application/rendering/static-and-dynamic-rendering) page. To learn more about caching, see the [Caching](/docs/app/building-your-application/data-fetching/caching) sections. + +## Edge and Node.js Runtimes + +On the server, there are two runtimes where your pages can be rendered: + +- The **Node.js Runtime** (default) has access to all Node.js APIs and compatible packages from the ecosystem. +- The **Edge Runtime** is based on [Web APIs](/docs/app/api-reference/edge). + +Both runtimes support [streaming](/docs/app/building-your-application/routing/loading-ui-and-streaming) from the server, depending on your deployment infrastructure. + +To learn how to switch between runtimes, see the [Edge and Node.js Runtimes](/docs/app/building-your-application/rendering/edge-and-nodejs-runtimes) page. + +## Next Steps + +Now that you understand the fundamentals of rendering, you can learn more about implementing the different rendering strategies and runtimes: diff --git a/docs/02-app/01-building-your-application/03-data-fetching/01-fetching.mdx b/docs/02-app/01-building-your-application/03-data-fetching/01-fetching.mdx new file mode 100644 index 0000000000000..dd8b434fb8ac8 --- /dev/null +++ b/docs/02-app/01-building-your-application/03-data-fetching/01-fetching.mdx @@ -0,0 +1,401 @@ +--- +title: Data Fetching +nav_title: Fetching +description: Learn how to fetch data in your Next.js application. +--- + +The Next.js App Router allows you to fetch data directly in your React components by marking the function as `async` and using `await` for the [Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise). + +Data fetching is built on top of the [`fetch()` Web API](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API) and React Server Components. When using `fetch()`, requests are [automatically deduped](/docs/app/building-your-application/data-fetching#automatic-fetch-request-deduping) by default. + +Next.js extends the `fetch` options object to allow each request to set its own [caching and revalidating](/docs/app/building-your-application/data-fetching/caching). + +## `async` and `await` in Server Components + +You can use `async` and `await` to fetch data in Server Components. + +```tsx filename="app/page.tsx" switcher +async function getData() { + const res = await fetch('https://api.example.com/...') + // The return value is *not* serialized + // You can return Date, Map, Set, etc. + + // Recommendation: handle errors + if (!res.ok) { + // This will activate the closest `error.js` Error Boundary + throw new Error('Failed to fetch data') + } + + return res.json() +} + +export default async function Page() { + const data = await getData() + + return
    +} +``` + +```jsx filename="app/page.js" switcher +async function getData() { + const res = await fetch('https://api.example.com/...') + // The return value is *not* serialized + // You can return Date, Map, Set, etc. + + // Recommendation: handle errors + if (!res.ok) { + // This will activate the closest `error.js` Error Boundary + throw new Error('Failed to fetch data') + } + + return res.json() +} + +export default async function Page() { + const data = await getData() + + return
    +} +``` + +> **Good to know:** +> +> To use an `async` Server Component with TypeScript, ensure you are using TypeScript `5.1.3` or higher and `@types/react` `18.2.8` or higher. + +### Server Component Functions + +Next.js provides helpful server functions you may need when fetching data in Server Components: + +- [`cookies()`](/docs/app/api-reference/functions/cookies) +- [`headers()`](/docs/app/api-reference/functions/headers) + +## `use` in Client Components + +`use` is a new React function that **accepts a promise** conceptually similar to `await`. `use` **handles the promise** returned by a function in a way that is compatible with components, hooks, and Suspense. Learn more about `use` in the [React RFC](https://github.com/acdlite/rfcs/blob/first-class-promises/text/0000-first-class-support-for-promises.md#usepromise). + +Wrapping `fetch` in `use` is currently **not** recommended in Client Components and may trigger multiple re-renders. For now, if you need to fetch data in a Client Component, we recommend using a third-party library such as [SWR](https://swr.vercel.app/) or [React Query](https://tanstack.com/query/v4). + +> **Note:** We'll be adding more examples once `fetch` and `use` work in Client Components. + +## Static Data Fetching + +By default, `fetch` will automatically fetch and [cache data](/docs/app/building-your-application/data-fetching/caching) indefinitely. + +```ts +fetch('https://...') // cache: 'force-cache' is the default +``` + +### Revalidating Data + +To revalidate [cached data](/docs/app/building-your-application/data-fetching/caching) at a timed interval, you can use the `next.revalidate` option in `fetch()` to set the `cache` lifetime of a resource (in seconds). + +```ts +fetch('https://...', { next: { revalidate: 10 } }) +``` + +See [Revalidating Data](/docs/app/building-your-application/data-fetching/revalidating) for more information. + +> **Good to know:** +> +> Caching at the fetch level with `revalidate` or `cache: 'force-cache'` stores the data across requests in a shared cache. You should avoid using it for user-specific data (i.e. requests that derive data from `cookies()` or `headers()`) + +## Dynamic Data Fetching + +To fetch fresh data on every `fetch` request, use the `cache: 'no-store'` option. + +```ts +fetch('https://...', { cache: 'no-store' }) +``` + +## Data Fetching Patterns + +### Parallel Data Fetching + +To minimize client-server waterfalls, we recommend this pattern to fetch data in parallel: + +```tsx filename="app/artist/[username]/page.tsx" switcher +import Albums from './albums' + +async function getArtist(username: string) { + const res = await fetch(`https://api.example.com/artist/${username}`) + return res.json() +} + +async function getArtistAlbums(username: string) { + const res = await fetch(`https://api.example.com/artist/${username}/albums`) + return res.json() +} + +export default async function Page({ + params: { username }, +}: { + params: { username: string } +}) { + // Initiate both requests in parallel + const artistData = getArtist(username) + const albumsData = getArtistAlbums(username) + + // Wait for the promises to resolve + const [artist, albums] = await Promise.all([artistData, albumsData]) + + return ( + <> +

    {artist.name}

    + + + ) +} +``` + +```jsx filename="app/artist/[username]/page.js" switcher +import Albums from './albums' + +async function getArtist(username) { + const res = await fetch(`https://api.example.com/artist/${username}`) + return res.json() +} + +async function getArtistAlbums(username) { + const res = await fetch(`https://api.example.com/artist/${username}/albums`) + return res.json() +} + +export default async function Page({ params: { username } }) { + // Initiate both requests in parallel + const artistData = getArtist(username) + const albumsData = getArtistAlbums(username) + + // Wait for the promises to resolve + const [artist, albums] = await Promise.all([artistData, albumsData]) + + return ( + <> +

    {artist.name}

    + + + ) +} +``` + +By starting the fetch prior to calling `await` in the Server Component, each request can eagerly start to fetch requests at the same time. This sets the components up so you can avoid waterfalls. + +We can save time by initiating both requests in parallel, however, the user won't see the rendered result until both promises are resolved. + +To improve the user experience, you can add a [suspense boundary](/docs/app/building-your-application/routing/loading-ui-and-streaming) to break up the rendering work and show part of the result as soon as possible: + +```tsx filename="artist/[username]/page.tsx" switcher +import { getArtist, getArtistAlbums, type Album } from './api' + +export default async function Page({ + params: { username }, +}: { + params: { username: string } +}) { + // Initiate both requests in parallel + const artistData = getArtist(username) + const albumData = getArtistAlbums(username) + + // Wait for the artist's promise to resolve first + const artist = await artistData + + return ( + <> +

    {artist.name}

    + {/* Send the artist information first, + and wrap albums in a suspense boundary */} + Loading...}> + + + + ) +} + +// Albums Component +async function Albums({ promise }: { promise: Promise }) { + // Wait for the albums promise to resolve + const albums = await promise + + return ( +
      + {albums.map((album) => ( +
    • {album.name}
    • + ))} +
    + ) +} +``` + +```jsx filename="artist/[username]/page.js" switcher +import { getArtist, getArtistAlbums } from './api' + +export default async function Page({ params: { username } }) { + // Initiate both requests in parallel + const artistData = getArtist(username) + const albumData = getArtistAlbums(username) + + // Wait for the artist's promise to resolve first + const artist = await artistData + + return ( + <> +

    {artist.name}

    + {/* Send the artist information first, + and wrap albums in a suspense boundary */} + Loading...}> + + + + ) +} + +// Albums Component +async function Albums({ promise }) { + // Wait for the albums promise to resolve + const albums = await promise + + return ( +
      + {albums.map((album) => ( +
    • {album.name}
    • + ))} +
    + ) +} +``` + +Take a look at the [preloading pattern](/docs/app/building-your-application/data-fetching/caching#preload-pattern-with-cache) for more information on improving components structure. + +### Sequential Data Fetching + +To fetch data sequentially, you can `fetch` directly inside the component that needs it, or you can `await` the result of `fetch` inside the component that needs it: + +```tsx filename="app/artist/page.tsx" switcher +// ... + +async function Playlists({ artistID }: { artistID: string }) { + // Wait for the playlists + const playlists = await getArtistPlaylists(artistID) + + return ( +
      + {playlists.map((playlist) => ( +
    • {playlist.name}
    • + ))} +
    + ) +} + +export default async function Page({ + params: { username }, +}: { + params: { username: string } +}) { + // Wait for the artist + const artist = await getArtist(username) + + return ( + <> +

    {artist.name}

    + Loading...}> + + + + ) +} +``` + +```jsx filename="app/artist/page.js" switcher +// ... + +async function Playlists({ artistID }) { + // Wait for the playlists + const playlists = await getArtistPlaylists(artistID) + + return ( +
      + {playlists.map((playlist) => ( +
    • {playlist.name}
    • + ))} +
    + ) +} + +export default async function Page({ params: { username } }) { + // Wait for the artist + const artist = await getArtist(username) + + return ( + <> +

    {artist.name}

    + Loading...}> + + + + ) +} +``` + +By fetching data inside the component, each fetch request and nested segment in the route cannot start fetching data and rendering until the previous request or segment has completed. + +### Blocking Rendering in a Route + +By fetching data in a [layout](/docs/app/building-your-application/routing/pages-and-layouts), rendering for all route segments beneath it can only start once the data has finished loading. + +In the `pages` directory, pages using server-rendering would show the browser loading spinner until `getServerSideProps` had finished, then render the React component for that page. This can be described as "all or nothing" data fetching. Either you had the entire data for your page, or none. + +In the `app` directory, you have additional options to explore: + +1. First, you can use `loading.js` to show an instant loading state from the server while streaming in the result from your data fetching function. +2. Second, you can move data fetching _lower_ in the component tree to only block rendering for the parts of the page that need it. For example, moving data fetching to a specific component rather than fetching it at the root layout. + +Whenever possible, it's best to fetch data in the segment that uses it. This also allows you to show a loading state for only the part of the page that is loading, and not the entire page. + +## Data Fetching without `fetch()` + +You might not always have the ability to use and configure `fetch` requests directly if you're using a third-party library such as an ORM or database client. + +In cases where you cannot use `fetch` but still want to control the caching or revalidating behavior of a layout or page, you can rely on the [default caching behavior](#default-caching-behavior) of the segment or use the [segment cache configuration](#segment-cache-configuration). + +### Default Caching Behavior + +Any data fetching libraries that do not use `fetch` directly **will not** affect caching of a route, and will be static or dynamic depending on the route segment. + +If the segment is static (default), the output of the request will be cached and revalidated (if configured) alongside the rest of the segment. If the segment is dynamic, the output of the request will _not_ be cached and will be re-fetched on every request when the segment is rendered. + +> **Good to know:** Dynamic functions like [`cookies()`](/docs/app/api-reference/functions/cookies) and [`headers()`](/docs/app/api-reference/functions/headers) will make the route segment dynamic. + +### Segment Cache Configuration + +As a temporary solution, until the caching behavior of third-party queries can be configured, you can use [segment configuration](/docs/app/api-reference/file-conventions/route-segment-config#revalidate) to customize the cache behavior of the entire segment. + +```ts filename="app/page.tsx" switcher +import prisma from './lib/prisma' + +export const revalidate = 3600 // revalidate every hour + +async function getPosts() { + const posts = await prisma.post.findMany() + return posts +} + +export default async function Page() { + const posts = await getPosts() + // ... +} +``` + +```jsx filename="app/page.js" switcher +import prisma from './lib/prisma' + +export const revalidate = 3600 // revalidate every hour + +async function getPosts() { + const posts = await prisma.post.findMany() + return posts +} + +export default async function Page() { + const posts = await getPosts() + // ... +} +``` diff --git a/docs/02-app/01-building-your-application/03-data-fetching/02-caching.mdx b/docs/02-app/01-building-your-application/03-data-fetching/02-caching.mdx new file mode 100644 index 0000000000000..0d413b86267ce --- /dev/null +++ b/docs/02-app/01-building-your-application/03-data-fetching/02-caching.mdx @@ -0,0 +1,284 @@ +--- +title: Caching Data +nav_title: Caching +description: Learn about caching routes in Next.js. +--- + +Next.js has built-in support for caching data, both on a per-request basis (recommended) or for an entire route segment. + +Fetch Request Deduplication + +## Per-request Caching + +### `fetch()` + +By default, all `fetch()` requests are cached and deduplicated automatically. This means that if you make the same request twice, the second request will reuse the result from the first request. + +```tsx filename="app/page.tsx" switcher +async function getComments() { + const res = await fetch('https://...') // The result is cached + return res.json() +} + +// This function is called twice, but the result is only fetched once +const comments = await getComments() // cache MISS + +// The second call could be anywhere in your application +const comments = await getComments() // cache HIT +``` + +```jsx filename="app/page.js" switcher +async function getComments() { + const res = await fetch('https://...') // The result is cached + return res.json() +} + +// This function is called twice, but the result is only fetched once +const comments = await getComments() // cache MISS + +// The second call could be anywhere in your application +const comments = await getComments() // cache HIT +``` + +Requests are **not** cached if: + +- Dynamic methods (`next/headers`, `export const POST`, or similar) are used and the fetch is a `POST` request (or uses `Authorization` or `cookie` headers) +- `fetchCache` is configured to skip cache by default +- `revalidate: 0` or `cache: 'no-store'` is configured on individual `fetch` + +Requests made using `fetch` can specify a `revalidate` option to control the revalidation frequency of the request. + +```tsx filename="app/page.tsx" switcher +export default async function Page() { + // revalidate this data every 10 seconds at most + const res = await fetch('https://...', { next: { revalidate: 10 } }) + const data = res.json() + // ... +} +``` + +```jsx filename="app/page.js" switcher +export default async function Page() { + // revalidate this data every 10 seconds at most + const res = await fetch('https://...', { next: { revalidate: 10 } }) + const data = res.json() + // ... +} +``` + +### React `cache()` + +React allows you to [`cache()`](https://github.com/acdlite/rfcs/blob/first-class-promises/text/0000-first-class-support-for-promises.md) and deduplicate requests, memoizing the result of the wrapped function call. The same function called with the same arguments will reuse a cached value instead of re-running the function. + +```tsx filename="utils/getUser.ts" switcher +import { cache } from 'react' + +export const getUser = cache(async (id: string) => { + const user = await db.user.findUnique({ id }) + return user +}) +``` + +```jsx filename="utils/getUser.js" switcher +import { cache } from 'react' + +export const getUser = cache(async (id) => { + const user = await db.user.findUnique({ id }) + return user +}) +``` + +```tsx filename="app/user/[id]/layout.tsx" switcher +import { getUser } from '@utils/getUser' + +export default async function UserLayout({ params: { id } }) { + const user = await getUser(id) + // ... +} +``` + +```jsx filename="app/user/[id]/layout.js" switcher +import { getUser } from '@utils/getUser' + +export default async function UserLayout({ params: { id } }) { + const user = await getUser(id) + // ... +} +``` + +```tsx filename="app/user/[id]/page.tsx" switcher +import { getUser } from '@utils/getUser' + +export default async function Page({ + params: { id }, +}: { + params: { id: string } +}) { + const user = await getUser(id) + // ... +} +``` + +```jsx filename="app/user/[id]/page.js" switcher +import { getUser } from '@utils/getUser' + +export default async function Page({ params: { id } }) { + const user = await getUser(id) + // ... +} +``` + +Although the `getUser()` function is called twice in the example above, only one query will be made to the database. This is because `getUser()` is wrapped in `cache()`, so the second request can reuse the result from the first request. + +> **Good to know:** +> +> - `fetch()` caches requests automatically, so you don't need to wrap functions that use `fetch()` with `cache()`. See [automatic request deduping](/docs/app/building-your-application/data-fetching#automatic-fetch-request-deduping) for more information. +> - In this new model, we recommend **fetching data directly in the component that needs it**, even if you're requesting the same data in multiple components, rather than passing the data between components as props. +> - We recommend using the [`server-only` package](/docs/getting-started/react-essentials#keeping-server-only-code-out-of-client-components-poisoning) to make sure server data fetching functions are never used on the client. + +### `POST` requests and `cache()` + +`POST` requests are automatically deduplicated when using `fetch` – unless they are inside of `POST` Route Handler or come after reading `headers()`/`cookies()`. For example, if you are using GraphQL and `POST` requests in the above cases, you can use `cache` to deduplicate requests. The `cache` arguments must be flat and only include primitives. Deep objects won't match for deduplication. + +```tsx filename="utils/getUser.ts" switcher +import { cache } from 'react' + +export const getUser = cache(async (id: string) => { + const res = await fetch('...', { method: 'POST', body: '...' }) + // ... +}) +``` + +```jsx filename="utils/getUser.js" switcher +import { cache } from 'react' + +export const getUser = cache(async (id) => { + const res = await fetch('...', { method: 'POST', body: '...' }) + // ... +}) +``` + +### Preload pattern with `cache()` + +As a pattern, we suggest optionally exposing a `preload()` export in utilities or components that do data fetching. + +```tsx filename="components/User.tsx" switcher +import { getUser } from '@utils/getUser' + +export const preload = (id: string) => { + // void evaluates the given expression and returns undefined + // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/void + void getUser(id) +} +export default async function User({ id }: { id: string }) { + const result = await getUser(id) + // ... +} +``` + +```jsx filename="components/User.js" switcher +import { getUser } from '@utils/getUser' + +export const preload = (id) => { + // void evaluates the given expression and returns undefined + // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/void + void getUser(id) +} +export default async function User({ id }) { + const result = await getUser(id) + // ... +} +``` + +By calling `preload`, you can eagerly start fetching data you're likely going to need. + +```tsx filename="app/user/[id]/page.tsx" switcher +import User, { preload } from '@components/User' + +export default async function Page({ + params: { id }, +}: { + params: { id: string } +}) { + preload(id) // starting loading the user data now + const condition = await fetchCondition() + return condition ? : null +} +``` + +```jsx filename="app/user/[id]/page.js" switcher +import User, { preload } from '@components/User' + +export default async function Page({ params: { id } }) { + preload(id) // starting loading the user data now + const condition = await fetchCondition() + return condition ? : null +} +``` + +> **Good to know**: +> +> - The `preload()` function can have any name. It's a pattern, not an API. +> - This pattern is completely optional and something you can use to optimize on a case-by-case basis. +> This pattern is a further optimization on top of [parallel data fetching](/docs/app/building-your-application/data-fetching/fetching#parallel-data-fetching). Now you don't have to pass promises down as props and can instead rely on the preload pattern. + +### Combining `cache`, `preload`, and `server-only` + +You can combine the `cache` function, the `preload` pattern, and the `server-only` package to create a data fetching utility that can be used throughout your app. + +```tsx filename="utils/getUser.ts" switcher +import { cache } from 'react' +import 'server-only' + +export const preload = (id: string) => { + void getUser(id) +} + +export const getUser = cache(async (id: string) => { + // ... +}) +``` + +```jsx filename="utils/getUser.js" switcher +import { cache } from 'react' +import 'server-only' + +export const preload = (id) => { + void getUser(id) +} + +export const getUser = cache(async (id) => { + // ... +}) +``` + +With this approach, you can eagerly fetch data, cache responses, and guarantee that this data fetching [only happens on the server](/docs/getting-started/react-essentials#keeping-server-only-code-out-of-client-components-poisoning). + +The `getUser.ts` exports can be used by layouts, pages, or components to give them control over when a user's data is fetched. + +## Segment-level Caching + +> **Note:** We recommend using per-request caching for improved granularity and control over caching. + +Segment-level caching allows you to cache and revalidate data used in route segments. + +This mechanism allows different segments of a path to control the cache lifetime of the entire route. Each `page.tsx` and `layout.tsx` in the route hierarchy can export a `revalidate` value that sets the revalidation time for the route. + +```tsx filename="app/page.tsx" switcher +export const revalidate = 60 // revalidate this segment every 60 seconds +``` + +```jsx filename="app/page.js" switcher +export const revalidate = 60 // revalidate this segment every 60 seconds +``` + +> **Good to know:** +> +> - If a page, layout, and fetch request inside components all specify a [`revalidate`](/docs/app/api-reference/file-conventions/route-segment-config#revalidate) frequency, the lowest value of the three will be used. +> - Advanced: You can set `fetchCache` to `'only-cache'` or `'force-cache'` to ensure that all `fetch` requests opt into caching but the revalidation frequency might still be lowered by individual `fetch` requests. See [`fetchCache`](/docs/app/api-reference/file-conventions/route-segment-config) for more information. diff --git a/docs/02-app/01-building-your-application/03-data-fetching/03-revalidating.mdx b/docs/02-app/01-building-your-application/03-data-fetching/03-revalidating.mdx new file mode 100644 index 0000000000000..ca405ef167a80 --- /dev/null +++ b/docs/02-app/01-building-your-application/03-data-fetching/03-revalidating.mdx @@ -0,0 +1,103 @@ +--- +title: Revalidating Data +nav_title: Revalidating +description: Learn about revalidating data in Next.js using Incremental Static Regeneration. +--- + +Next.js allows you to update specific static routes **without needing to rebuild your entire site**. Revalidation (also known as [Incremental Static Regeneration](/docs/pages/building-your-application/data-fetching/incremental-static-regeneration)) allows you to retain the benefits of static while scaling to millions of pages. + +There are two types of revalidation in Next.js: + +- **Background**: Revalidates the data at a specific time interval. +- **On-demand**: Revalidates the data based on an event such as an update. + +## Background Revalidation + +To revalidate cached data at a specific interval, you can use the `next.revalidate` option in `fetch()` to set the `cache` lifetime of a resource (in seconds). + +```jsx +fetch('https://...', { next: { revalidate: 60 } }) +``` + +If you want to revalidate data that does not use `fetch` (i.e. using an external package or query builder), you can use the [route segment config](/docs/app/api-reference/file-conventions/route-segment-config#revalidate). + +```tsx filename="app/page.tsx" switcher +export const revalidate = 60 // revalidate this page every 60 seconds +``` + +```jsx filename="app/page.js" switcher +export const revalidate = 60 // revalidate this page every 60 seconds +``` + +In addition to `fetch`, you can also revalidate data using [`cache`](/docs/app/building-your-application/data-fetching/caching#per-request-caching). + +### How it works + +1. When a request is made to the route that was statically rendered at build time, it will initially show the cached data. +2. Any requests to the route after the initial request and before 60 seconds are also cached and instantaneous. +3. After the 60-second window, the next request will still show the cached (stale) data. +4. Next.js will trigger a regeneration of the data in the background. +5. Once the route generates successfully, Next.js will invalidate the cache and show the updated route. If the background regeneration fails, the old data would still be unaltered. + +When a request is made to a route segment that hasn’t been generated, Next.js will dynamically render the route on the first request. Future requests will serve the static route segments from the cache. + +> **Note**: Check if your upstream data provider has caching enabled by default. You might need to disable (e.g. `useCdn: false`), otherwise a revalidation won't be able to pull fresh data to update the ISR cache. Caching can occur at a CDN (for an endpoint being requested) when it returns the `Cache-Control` header. ISR on Vercel [persists the cache globally and handles rollbacks](https://vercel.com/docs/concepts/incremental-static-regeneration/overview). + +## On-demand Revalidation + +If you set a `revalidate` time of `60`, all visitors will see the same generated version of your site for one minute. The only way to invalidate the cache is if someone visits the page after the minute has passed. + +The Next.js App Router supports revalidating content on-demand based on a route or cache tag. This allows you to manually purge the Next.js cache for specific fetches, making it easier to update your site when: + +- Content from your headless CMS is created or updated. +- Ecommerce metadata changes (price, description, category, reviews, etc). + +### Using On-Demand Revalidation + +Data can be revalidated on-demand by path ([`revalidatePath`](/docs/app/api-reference/functions/revalidatePath)) or by cache tag ([`revalidateTag`](/docs/app/api-reference/functions/revalidateTag)). + +For example, the following `fetch` adds the cache tag `collection`: + +```tsx filename="app/page.tsx" switcher +export default async function Page() { + const res = await fetch('https://...', { next: { tags: ['collection'] } }) + const data = await res.json() + // ... +} +``` + +```jsx filename="app/page.js" switcher +export default async function Page() { + const res = await fetch('https://...', { next: { tags: ['collection'] } }) + const data = await res.json() + // ... +} +``` + +This cached data can then be revalidated on-demand by calling `revalidateTag` in a [Route Handler](/docs/app/building-your-application/routing/router-handlers). + +```tsx filename="app/api/revalidate/route.ts" switcher +import { NextRequest, NextResponse } from 'next/server' +import { revalidateTag } from 'next/cache' + +export async function GET(request: NextRequest) { + const tag = request.nextUrl.searchParams.get('tag') + revalidateTag(tag) + return NextResponse.json({ revalidated: true, now: Date.now() }) +} +``` + +```jsx filename="app/api/revalidate/route.js" switcher +import { NextResponse } from 'next/server' +import { revalidateTag } from 'next/cache' + +export async function GET(request) { + const tag = request.nextUrl.searchParams.get('tag') + revalidateTag(tag) + return NextResponse.json({ revalidated: true, now: Date.now() }) +} +``` + +## Error Handling and Revalidation + +If an error is thrown while attempting to revalidate data, the last successfully generated data will continue to be served from the cache. On the next subsequent request, Next.js will retry revalidating the data. diff --git a/docs/02-app/01-building-your-application/03-data-fetching/04-server-actions.mdx b/docs/02-app/01-building-your-application/03-data-fetching/04-server-actions.mdx new file mode 100644 index 0000000000000..2a40d89671d0d --- /dev/null +++ b/docs/02-app/01-building-your-application/03-data-fetching/04-server-actions.mdx @@ -0,0 +1,548 @@ +--- +title: Server Actions +nav_title: Server Actions +description: Use Server Actions to mutate data in your Next.js application. +related: + title: Next Steps + description: For more information on what to do next, we recommend the following sections + links: + - app/api-reference/functions/cookies +--- + +[Server Actions](#server-actions) are an **alpha** feature in Next.js, built on top of React [Actions](#actions). They enable server-side data mutations, reduced client-side JavaScript, and progressively enhanced forms. They can be defined inside Server Components and/or called from Client Components: + +**With Server Components:** + +```jsx filename="app/add-to-cart.jsx" highlight={5, 6, 13} +import { cookies } from 'next/headers' + +// Server action defined inside a Server Component +export default function AddToCart({ productId }) { + async function addItem(data) { + 'use server' + + const cartId = cookies().get('cartId')?.value + await saveToDb({ cartId, data }) + } + + return ( +
    + +
    + ) +} +``` + +**With Client Components:** + +```jsx filename="app/actions.js" highlight={1, 3} +'use server' + +async function addItem(data) { + const cartId = cookies().get('cartId')?.value + await saveToDb({ cartId, data }) +} +``` + +```jsx filename="app/add-to-cart.jsx" highlight={1, 3, 8} +'use client' + +import { addItem } from './actions.js' + +// Server Action being called inside a Client Component +export default function AddToCart({ productId }) { + return ( +
    + +
    + ) +} +``` + +## Convention + +You can enable Server Actions in your Next.js project by enabling the **experimental** `serverActions` flag. + +```js filename="next.config.js" +module.exports = { + experimental: { + serverActions: true, + }, +} +``` + +### Creation + +Server Actions can be defined in two places: + +- Inside the component that uses it (Server Components only) +- In a separate file (Client and Server Components), for reusability. You can define multiple Server Actions in a single file. + +#### With Server Components + +Create a Server Action by defining an asynchronous function with the `"use server"` directive at the top of the function body. This function should have **serializable arguments** and a **serializable return value** based on the React Server Components protocol. + +```jsx filename="app/server-component.jsx" highlight={2} +export default function ServerComponent() { + async function myAction() { + 'use server' + // ... + } +} +``` + +#### With Client Components + +If you're using a Server Action inside a Client Component, create your action in a separate file with the "use server" directive at the top of the file. Then, import the Server Action into your Client Component: + +```jsx filename="app/actions.js" highlight={1} +'use server' + +export async function myAction() { + // ... +} +``` + +```jsx filename="app/client-component.jsx" highlight={1} +|"use client" + +import { myAction } from './actions'; + +export default function ClientComponent() { + return ( +
    + +
    + ); +} +``` + +> **Note:** When using a top-level `"use server"` directive, all exports below will be considered Server Actions. You can have multiple Server Actions in a single file. + +### Invocation + +You can invoke Server Actions using the following methods: + +- Using `action`: React's `action` prop allows invoking a Server Action on a `
    ` element. +- Using `formAction`: React's `formAction` prop allows handling ` +
    + ) +} +``` + +> **Note:** An `action` is similar to the HTML primitive [`action`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/form#action) + +#### `formAction` + +You can use `formAction` prop to handle **Form Actions** on elements such as `button`, `input type="submit"`, and `input type="image"`. The `formAction` prop takes presedence over the form's `action`. + +```jsx filename="app/form" highlight={15} +export default function Form() { + async function handleSubmit() { + 'use server' + // ... + } + + async function submitImage() { + 'use server' + // ... + } + + return ( +
    + + + +
    + ) +} +``` + +> **Note:** A `formAction` is the HTML primitive [`formaction`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/button#formaction). React now allows you to pass functions to this attribute. + +#### Custom invocation using `startTransition` + +You can also invoke Server Actions without using `action` or `formAction`. You can achieve this by using `startTransition` provided by the `useTransition` hook, which can be useful if you want to use Server Actions outside of forms, buttons, or inputs. + +> **Note:** Using `startTransition` disables the out-of-the-box Progressive Enhancement. + +```jsx filename="app/components/example-client-component.js" highlight={3, 7, 10} +'use client' + +import { useTransition } from 'react' +import { addItem } from '../actions' + +function ExampleClientComponent({ id }) { + let [isPending, startTransition] = useTransition() + + return ( + + ) +} +``` + +```jsx filename="app/actions.js" +'use server' + +export async function addItem(id) { + await addItemToDb(id) + // Marks all product pages for revalidating + revalidatePath('/product/[id]') +} +``` + +#### Custom invocation without `startTransition` + +If you aren't doing [Server Mutations](#server-mutations), you can directly pass the function as a prop like any other function. + +```tsx filename="app/posts/[id]/page.tsx" switcher +import kv from '@vercel/kv' +import LikeButton from './like-button' + +export default function Page({ params }: { params: { id: string } }) { + async function increment() { + 'use server' + await kv.incr(`post:id:${params.id}`) + } + + return +} +``` + +```jsx filename="app/posts/[id]/page.js" switcher +import kv from '@vercel/kv' +import LikeButton from './like-button' + +export default function Page({ params }) { + async function increment() { + 'use server' + await kv.incr(`post:id:${params.id}`) + } + + return +} +``` + +```tsx filename="app/post/[id]/like-button.tsx" switcher +'use client' + +export default function LikeButton({ + increment, +}: { + increment: () => Promise +}) { + return ( + + ) +} +``` + +```jsx filename="app/post/[id]/like-button.js" switcher +'use client' + +export default function LikeButton({ increment }) { + return ( + + ) +} +``` + +### Enhancements + +#### Experimental `useOptimistic` + +The experimental `useOptimistic` hook provides a way to implement optimistic updates in your application. Optimistic updates are a technique that enhances user experience by making the app appear more responsive. + +When a Server Action is invoked, the UI is updated immediately to reflect the expected outcome, instead of waiting for the Server Action's response. + +```jsx filename="app/thread.js" +'use client' + +import { experimental_useOptimistic as useOptimistic } from 'react' +import { send } from './actions.js' + +export function Thread({ messages }) { + const [optimisticMessages, addOptimisticMessage] = useOptimistic( + messages, + (state, newMessage) => [...state, { message: newMessage, sending: true }] + ) + const formRef = useRef() + + return ( +
    + {optimisticMessages.map((m) => ( +
    + {m.message} + {m.sending ? 'Sending...' : ''} +
    + ))} +
    { + const message = formData.get('message') + formRef.current.reset() + addOptimisticMessage(message) + await send(message) + }} + ref={formRef} + > + +
    +
    + ) +} +``` + +#### Experimental `useFormStatus` + +The **experimental** `useFormStatus` hook can be used within Form Actions, and provides the `pending` property. + +```jsx filename="app/form.js" +'use client' + +import { experimental_useFormStatus as useFormStatus } from 'react-dom' + +function Submit() { + const { pending } = useFormStatus() + + return ( + + Submit + + ) +} +``` + +#### Progressive Enhancement + +Progressive Enhancement allows a `
    ` to function properly without JavaScript, or with JavaScript disabled. This allows users to interact with the form and submit data even if the JavaScript for the form hasn't been loaded yet or if it fails to load. + +Both Server Form Actions and Client Form Actions support Progressive Enhancement, using one of two strategies: + +- If a **Server Action** is passed directly to a ``, the form is interactive **even if JavaScript is disabled**. +- If a **Client Action** is passed to a ``, the form is still interactive, but the action will be placed in a queue until the form has hydrated. The `` is prioritized with Selective Hydration, so it happens quickly. + +```jsx filename="app/components/example-client-component.js" +'use client' + +import { useState } from 'react' +import { handleSubmit } from './actions.js' + +export default function ExampleClientComponent({ myAction }) { + const [input, setInput] = useState() + + return ( + setInput(e.target.value)}> + {/* ... */} +
    + ) +} +``` + +In both cases, the form is interactive before hydration occurs. Although Server Actions have the additional benefit of not relying on client JavaScript, you can still compose additional behavior with Client Actions where desired without sacrificing interactivity. + +## Examples + +### Usage with Client Components + +#### Import + +Server Actions **cannot** be _defined_ within Client Components, but they can be _imported_. To use Server Actions in Client Components, you can import the action from a file containing a top-level `"use server"` directive. + +```jsx filename="app/actions.js" highlight={1} +'use server' + +export async function addItem() { + // ... +} +``` + +```jsx filename="app/components/example-client-component.js" highlight={3, 7, 10} +'use client' + +import { useTransition } from 'react' +import { addItem } from '../actions' + +function ExampleClientComponent({ id }) { + let [isPending, startTransition] = useTransition() + + return ( + + ) +} +``` + +#### Props + +Although [importing](#import) Server Actions is recommended, in some cases you might want to pass down a Server Action to a Client Component as a prop. + +For example, you might want to use a dynamically generated value within the action. In that case, passing a Server Action down as a prop might be a viable solution. + +```jsx filename="app/components/example-server-component.js" +import { ExampleClientComponent } from './components/example-client-component.js' + +function ExampleServerComponent({ id }) { + async function updateItem(data) { + 'use server' + modifyItem({ id, data }) + } + + return +} +``` + +```jsx filename="app/components/example-client-component.js" +'use client' + +function ExampleClientComponent({ updateItem }) { + async function action(formData: FormData) { + await updateItem(formData) + } + + return ( +
    + + +
    + ) +} +``` + +### On-demand Revalidation + +Server Actions can be used to revalidate data on-demand by path ([`revalidatePath`](/docs/app/api-reference/functions/revalidatePath)) or by cache tag ([`revalidateTag`](/docs/app/api-reference/functions/revalidateTag)). + +```jsx +import { revalidateTag } from 'next/cache' + +async function revalidate() { + 'use server' + revalidateTag('blog-posts') +} +``` + +### Validation + +The data passed to a Server Action can be validated or sanitized before invoking the action. For example, you can create a wrapper function that receives the action as its argument, and returns a function that invokes the action if it's valid. + +```jsx filename="app/actions.js" +'use server' + +import { withValidate } from 'lib/form-validation' + +export const action = withValidate((data) => { + // ... +}) +``` + +```jsx filename="lib/form-validation" +export function withValidate(action) { + return (formData: FormData) => { + 'use server' + + const isValidData = verifyData(formData) + + if (!isValidData) { + throw new Error('Invalid input.') + } + + const data = process(formData) + return action(data) + } +} +``` + +### Using headers + +You can read incoming request headers such as `cookies` and `headers` within a Server Action. + +```jsx highlight={6} +import { cookies } from 'next/headers' + +async function addItem(data) { + 'use server' + + const cartId = cookies().get('cartId')?.value + + await saveToDb({ cartId, data }) +} +``` + +Additionally, you can modify cookies within a Server Action. + +```jsx highlight={7, 9, 10, 11, 12, 13, 14} +import { cookies } from 'next/headers'; + +async function create(data) { + 'use server'; + + const cart = await createCart(): + cookies().set('cartId', cart.id) + // or + cookies().set({ + name: 'cartId', + value: cart.id, + httpOnly: true, + path: '/' + }) +} +``` + +## Glossary + +### Actions + +Performs asynchronous side effects in response to user interaction, with built-in solutions for error handling and [optimistic updates](#experimental-useoptimistic). Similar to the HTML primitive [`action`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/form#action). + +### Form Actions + +[Actions](#actions) integrated into the web standard `
    ` API, and enable out-of-the-box progressive enhancement and [loading states](#experimental-useformstatus). Similar to the HTML primitive [`formaction`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/button#formation). + +### Server Functions + +Functions that run on the server, but can be called on the client. + +### Server Actions + +[Server Functions](#server-functions) called as an action. + +### Server Mutations + +[Server Actions](#server-actions) that mutates your data and calls `redirect`, `revalidatePath`, or `revalidateTag`. diff --git a/docs/02-app/01-building-your-application/03-data-fetching/index.mdx b/docs/02-app/01-building-your-application/03-data-fetching/index.mdx new file mode 100644 index 0000000000000..bc78eeeb2f52e --- /dev/null +++ b/docs/02-app/01-building-your-application/03-data-fetching/index.mdx @@ -0,0 +1,163 @@ +--- +title: Data Fetching +description: Learn the fundamentals of data fetching with React and Next.js. +--- + +The Next.js App Router introduces a new, simplified data fetching system built on React and the Web platform. This page will go through the fundamental concepts and patterns to help you manage your data's lifecycle. + +Here's a quick overview of the recommendations on this page: + +1. [Fetch data on the server](#fetching-data-on-the-server) using Server Components. +2. [Fetch data in parallel](#parallel-and-sequential-data-fetching) to minimize waterfalls and reduce loading times. +3. For Layouts and Pages, [fetch data where it's used](#automatic-fetch-request-deduping). Next.js will automatically dedupe requests in a tree. +4. Use [Loading UI, Streaming and Suspense](#streaming-and-suspense) to progressively render a page and show a result to the user while the rest of the content loads. + +## The `fetch()` API + +The new data fetching system is built on top of the native [`fetch()` Web API](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API) and makes use of `async` and `await` in Server Components. + +- React extends `fetch` to provide [automatic request deduping](#automatic-fetch-request-deduping). +- Next.js extends the `fetch` options object to allow each request to set its own [caching and revalidating](/docs/app/building-your-application/data-fetching/caching) rules. + +[Learn how to use `fetch` in Next.js](/docs/app/building-your-application/data-fetching/fetching). + +## Fetching Data on the Server + +Whenever possible, we recommend fetching data in [Server Components](/docs/getting-started/react-essentials#server-components). Server Components **always fetch data on the server**. This allows you to: + +- Have direct access to backend data resources (e.g. databases). +- Keep your application more secure by preventing sensitive information, such as access tokens and API keys, from being exposed to the client. +- Fetch data and render in the same environment. This reduces both the back-and-forth communication between client and server, as well as the work on the main thread on the client. +- Perform multiple data fetches with single round-trip instead of multiple individual requests on the client. +- Reduce client-server [waterfalls](#parallel-and-sequential-data-fetching). +- Depending on your region, data fetching can also happen closer to your data source, reducing latency and improving performance. + +> **Good to know:** It's still possible to fetch data client-side. We recommend using a third-party library such as [SWR](https://swr.vercel.app/) or [React Query](https://tanstack.com/query/v4/) with Client Components. In the future, it'll also be possible to fetch data in Client Components using React's [`use()` hook](/docs/app/building-your-application/data-fetching/fetching#use-in-client-components). + +## Fetching Data at the Component Level + +In the App Router, you can fetch data inside [layouts](/docs/app/building-your-application/routing/pages-and-layouts#layouts), [pages](/docs/app/building-your-application/routing/pages-and-layouts#pages), and components. Data fetching is also compatible with [Streaming and Suspense](#streaming-and-suspense). + +> **Good to know:** For layouts, it's not possible to pass data between a parent layout and its `children` components. We recommend **fetching data directly inside the layout that needs it**, even if you're requesting the same data multiple times in a route. Behind the scenes, React and Next.js will [cache and dedupe](#automatic-fetch-request-deduping) requests to avoid the same data being fetched more than once. + +## Parallel and Sequential Data Fetching + +When fetching data inside components, you need to be aware of two data fetching patterns: Parallel and Sequential. + +Sequential and Parallel Data Fetching + +- With **parallel data fetching**, requests in a route are eagerly initiated and will load data at the same time. This reduces client-server waterfalls and the total time it takes to load data. +- With **sequential data fetching**, requests in a route are dependent on each other and create waterfalls. There may be cases where you want this pattern because one fetch depends on the result of the other, or you want a condition to be satisfied before the next fetch to save resources. However, this behavior can also be unintentional and lead to longer loading times. + +[Learn how to implement parallel and sequential data fetching](/docs/app/building-your-application/data-fetching/fetching#data-fetching-patterns). + +## Automatic `fetch()` Request Deduping + +If you need to fetch the same data (e.g. current user) in multiple components in a tree, Next.js will automatically cache `fetch` requests (`GET`) that have the same input in a temporary cache. This optimization prevents the same data from being fetched more than once during a rendering pass. + +Fetch Request Deduplication + +- On the server, the cache lasts the lifetime of a server request until the rendering process completes. + - This optimization applies to `fetch` requests made in Layouts, Pages, Server Components, `generateMetadata` and `generateStaticParams`. + - This optimization also applies during [static generation](/docs/app/building-your-application/rendering#static-rendering). +- On the client, the cache lasts the duration of a session (which could include multiple client-side re-renders) before a full page reload. + +> **Good to know:** +> +> - `POST` requests are not automatically deduplicated. [Learn more about caching](/docs/app/building-your-application/data-fetching/caching). +> - If you're unable to use `fetch`, React provides a [`cache` function](/docs/app/building-your-application/data-fetching/caching#react-cache) to allow you to manually cache data for the duration of the request. + +## Static and Dynamic Data Fetching + +There are two types of data: **Static** and **Dynamic**. + +- **Static Data** is data that doesn't change often. For example, a blog post. +- **Dynamic Data** is data that changes often or can be specific to users. For example, a shopping cart list. + +Dynamic and Static Data Fetching + +By default, Next.js automatically does static fetches. This means that the data will be fetched at build time, cached, and reused on each request. As a developer, you have control over how the static data is [cached](#caching-data) and [revalidated](#revalidating-data). + +There are two benefits to using static data: + +1. It reduces the load on your database by minimizing the number of requests made. +2. The data is automatically cached for improved loading performance. + +However, if your data is personalized to the user or you want to always fetch the latest data, you can mark requests as _dynamic_ and fetch data on each request without caching. + +[Learn how to do Static and Dynamic data fetching](/docs/app/building-your-application/data-fetching/fetching#static-data-fetching). + +## Caching Data + +Caching is the process of storing data in a location (e.g. [Content Delivery Network](https://vercel.com/docs/concepts/edge-network/overview)) so it doesn't need to be re-fetched from the original source on each request. + +Static Site Generation + +The **Next.js Cache** is a persistent [HTTP cache](https://developer.mozilla.org/en-US/docs/Web/HTTP/Caching) that can be globally distributed. This means the cache can scale automatically and be shared across multiple regions depending on your platform (e.g. [Vercel](https://vercel.com/docs/concepts/next.js/overview)). + +Next.js extends the [options object](https://developer.mozilla.org/en-US/docs/Web/API/fetch#:~:text=preflight%20requests.-,cache,-A%20string%20indicating) of the `fetch()` function to allow each request on the server to set its own persistent caching behavior. Together with [component-level data fetching](#fetching-data-at-the-component-level), this allows you to configure caching within your application code directly where the data is being used. + +During server rendering, when Next.js comes across a fetch, it will check the cache to see if the data is already available. If it is, it will return the cached data. If not, it will fetch and store data for future requests. + +> **Good to know:** If you're unable to use `fetch`, React provides a [`cache` function](/docs/app/building-your-application/data-fetching/caching#react-cache) to allow you to manually cache data for the duration of the request. + +[Learn more about caching in Next.js](/docs/app/building-your-application/data-fetching/caching). + +### Revalidating Data + +Revalidation is the process of purging the cache and re-fetching the latest data. This is useful when your data changes and you want to ensure your application shows the latest version without having to rebuild your entire application. + +Next.js provides two types of revalidation: + +- [**Background**](/docs/app/building-your-application/data-fetching/revalidating#background-revalidation): Revalidates the data at a specific time interval. +- [**On-demand**](/docs/app/building-your-application/data-fetching/revalidating#on-demand-revalidation): Revalidates the data whenever there is an update. + +[Learn how to revalidate data](/docs/app/building-your-application/data-fetching/revalidating). + +### Streaming and Suspense + +Streaming and [Suspense](https://react.dev/reference/react/Suspense) are new React features that allow you to progressively render and incrementally stream rendered units of the UI to the client. + +With Server Components and [nested layouts](/docs/app/building-your-application/routing/pages-and-layouts), you're able to instantly render parts of the page that do not specifically require data, and show a [loading state](/docs/app/building-your-application/routing/loading-ui-and-streaming) for parts of the page that are fetching data. This means the user does not have to wait for the entire page to load before they can start interacting with it. + +Server Rendering with Streaming + +To learn more about Streaming and Suspense, see the [Loading UI](/docs/app/building-your-application/routing/loading-ui-and-streaming) and [Streaming and Suspense](/docs/app/building-your-application/routing/loading-ui-and-streaming#streaming-with-suspense) pages. + +## Old Methods + +Previous Next.js data fetching methods such as [`getServerSideProps`](/docs/pages/building-your-application/data-fetching/get-server-side-props), [`getStaticProps`](/docs/pages/building-your-application/data-fetching/get-static-props), and [`getInitialProps`](/docs/pages/api-reference/functions/get-initial-props) are **not** supported in the new App Router. However, you can still use them in the [Pages Router](/docs/pages/building-your-application/data-fetching). + +## Next Steps + +Now that you understand the fundamentals of data fetching in Next.js, you can learn more about managing data in your application: diff --git a/docs/02-app/01-building-your-application/04-styling/01-css-modules.mdx b/docs/02-app/01-building-your-application/04-styling/01-css-modules.mdx new file mode 100644 index 0000000000000..51685bb4a0d72 --- /dev/null +++ b/docs/02-app/01-building-your-application/04-styling/01-css-modules.mdx @@ -0,0 +1,286 @@ +--- +title: CSS Modules +description: Style your Next.js Application with CSS Modules. +--- + + + +
    + + Examples + + +- [Basic CSS Example](https://github.com/vercel/next.js/tree/canary/examples/basic-css) + +
    + +
    + +Next.js has built-in support for CSS Modules using the `.module.css` extension. + +CSS Modules locally scope CSS by automatically creating a unique class name. This allows you to use the same class name in different files without worrying about collisions. This behavior makes CSS Modules the ideal way to include component-level CSS. + +## Example + + +CSS Modules can be imported into any file inside the `app` directory: + +```tsx filename="app/dashboard/layout.tsx" switcher +import styles from './styles.module.css' + +export default function DashboardLayout({ + children, +}: { + children: React.ReactNode +}) { + return
    {children}
    +} +``` + +```jsx filename="app/dashboard/layout.js" switcher +import styles from './styles.module.css' + +export default function DashboardLayout({ + children, +}: { + children: React.ReactNode, +}) { + return
    {children}
    +} +``` + +```css filename="app/dashboard/styles.module.css" +.dashboard { + padding: 24px; +} +``` + +
    + + + +For example, consider a reusable `Button` component in the `components/` folder: + +First, create `components/Button.module.css` with the following content: + +```css filename="Button.module.css" +/* +You do not need to worry about .error {} colliding with any other `.css` or +`.module.css` files! +*/ +.error { + color: white; + background-color: red; +} +``` + +Then, create `components/Button.js`, importing and using the above CSS file: + +```jsx filename="components/Button.js" +import styles from './Button.module.css' + +export function Button() { + return ( + + ) +} +``` + + + +CSS Modules are an _optional feature_ and are **only enabled for files with the `.module.css` extension**. +Regular `` stylesheets and global CSS files are still supported. + +In production, all CSS Module files will be automatically concatenated into **many minified and code-split** `.css` files. +These `.css` files represent hot execution paths in your application, ensuring the minimal amount of CSS is loaded for your application to paint. + +## Global Styles + + +Global styles can be imported into any layout, page, or component inside the `app` directory. + +> **Good to know:** This is different from the `pages` directory, where you can only import global styles inside the `_app.js` file. + +For example, consider a stylesheet named `app/global.css`: + +```css +body { + padding: 20px 20px 60px; + max-width: 680px; + margin: 0 auto; +} +``` + +Inside the root layout (`app/layout.js`), import the `global.css` stylesheet to apply the styles to every route in your application: + +```tsx filename="app/layout.tsx" switcher +// These styles apply to every route in the application +import './global.css' + +export default function RootLayout({ + children, +}: { + children: React.ReactNode +}) { + return ( + + {children} + + ) +} +``` + +```jsx filename="app/layout.js" switcher +// These styles apply to every route in the application +import './global.css' + +export default function RootLayout({ children }) { + return ( + + {children} + + ) +} +``` + + + + + +To add a stylesheet to your application, import the CSS file within `pages/_app.js`. + +For example, consider the following stylesheet named `styles.css`: + +```css filename="styles.css" +body { + font-family: 'SF Pro Text', 'SF Pro Icons', 'Helvetica Neue', 'Helvetica', + 'Arial', sans-serif; + padding: 20px 20px 60px; + max-width: 680px; + margin: 0 auto; +} +``` + +Create a [`pages/_app.js` file](/docs/pages/building-your-application/routing/custom-app) if not already present. +Then, [`import`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import) the `styles.css` file. + +```jsx filename="pages/_app.js" +import '../styles.css' + +// This default export is required in a new `pages/_app.js` file. +export default function MyApp({ Component, pageProps }) { + return +} +``` + +These styles (`styles.css`) will apply to all pages and components in your application. +Due to the global nature of stylesheets, and to avoid conflicts, you may **only import them inside [`pages/_app.js`](/docs/pages/building-your-application/routing/custom-app)**. + +In development, expressing stylesheets this way allows your styles to be hot reloaded as you edit them—meaning you can keep application state. + +In production, all CSS files will be automatically concatenated into a single minified `.css` file. + + + +## External Stylesheets + + + +Stylesheets published by external packages can be imported anywhere in the `app` directory, including colocated components: + +```tsx filename="app/layout.tsx" switcher +import 'bootstrap/dist/css/bootstrap.css' + +export default function RootLayout({ + children, +}: { + children: React.ReactNode +}) { + return ( + + {children} + + ) +} +``` + +```jsx filename="app/layout.js" switcher +import 'bootstrap/dist/css/bootstrap.css' + +export default function RootLayout({ children }) { + return ( + + {children} + + ) +} +``` + +> **Note:** External stylesheets must be directly imported from an npm package or downloaded and colocated with your codebase. You cannot use ``. + + + + + +Next.js allows you to import CSS files from a JavaScript file. +This is possible because Next.js extends the concept of [`import`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import) beyond JavaScript. + +### Import styles from `node_modules` + +Since Next.js **9.5.4**, importing a CSS file from `node_modules` is permitted anywhere in your application. + +For global stylesheets, like `bootstrap` or `nprogress`, you should import the file inside `pages/_app.js`. +For example: + +```jsx filename="pages/_app.js" +import 'bootstrap/dist/css/bootstrap.css' + +export default function MyApp({ Component, pageProps }) { + return +} +``` + +For importing CSS required by a third-party component, you can do so in your component. For example: + +```tsx filename="components/example-dialog.js" +import { useState } from 'react' +import { Dialog } from '@reach/dialog' +import VisuallyHidden from '@reach/visually-hidden' +import '@reach/dialog/styles.css' + +function ExampleDialog(props) { + const [showDialog, setShowDialog] = useState(false) + const open = () => setShowDialog(true) + const close = () => setShowDialog(false) + + return ( +
    + + + +

    Hello there. I am a dialog

    +
    +
    + ) +} +``` + +
    + +## Additional Features + +Next.js includes additional features to improve the authoring experience of adding styles: + +- When running locally with `next dev`, local stylesheets (either global or CSS modules) will take advantage of [Fast Refresh](/docs/architecture/fast-refresh) to instantly reflect changes as edits are saved. +- When building for production with `next build`, CSS files will be bundled into fewer minified `.css` files to reduce the number of network requests needed to retrieve styles. +- If you disable JavaScript, styles will still be loaded in the production build (`next start`). However, JavaScript is still required for `next dev` to enable [Fast Refresh](/docs/architecture/fast-refresh). diff --git a/docs/02-app/01-building-your-application/04-styling/02-tailwind-css.mdx b/docs/02-app/01-building-your-application/04-styling/02-tailwind-css.mdx new file mode 100644 index 0000000000000..0c90c840a7cae --- /dev/null +++ b/docs/02-app/01-building-your-application/04-styling/02-tailwind-css.mdx @@ -0,0 +1,126 @@ +--- +title: Tailwind CSS +description: Style your Next.js Application using Tailwind CSS. +--- + + + +
    + + Examples + + +- [With Tailwind CSS](https://github.com/vercel/next.js/tree/canary/examples/with-tailwindcss) + +
    + +
    + +[Tailwind CSS](https://tailwindcss.com/) is a utility-first CSS framework that works exceptionally well with Next.js. + +## Installing Tailwind + +Install the Tailwind CSS packages and run the `init` command to generate both the `tailwind.config.js` and `postcss.config.js` files: + +```bash filename="Terminal" +npm install -D tailwindcss postcss autoprefixer +npx tailwindcss init -p +``` + +## Configuring Tailwind + +Inside `tailwind.config.js`, add paths to the files that will use Tailwind CSS class names: + +```js filename="tailwind.config.js" +/** @type {import('tailwindcss').Config} */ +module.exports = { + content: [ + './app/**/*.{js,ts,jsx,tsx,mdx}', // Note the addition of the `app` directory. + './pages/**/*.{js,ts,jsx,tsx,mdx}', + './components/**/*.{js,ts,jsx,tsx,mdx}', + + // Or if using `src` directory: + './src/**/*.{js,ts,jsx,tsx,mdx}', + ], + theme: { + extend: {}, + }, + plugins: [], +} +``` + +You do not need to modify `postcss.config.js`. + +## Importing Styles + +Add the [Tailwind CSS directives](https://tailwindcss.com/docs/functions-and-directives#directives) that Tailwind will use to inject its generated styles to a [Global Stylesheet](/docs/app/building-your-application/styling/css-modules#global-styles) in your application, for example: + +```css filename="app/globals.css" +@tailwind base; +@tailwind components; +@tailwind utilities; +``` + +Inside the [root layout](/docs/app/building-your-application/routing/pages-and-layouts#root-layout-required) (`app/layout.tsx`), import the `globals.css` stylesheet to apply the styles to every route in your application. + +```tsx filename="app/layout.tsx" switcher +import type { Metadata } from 'next' + +// These styles apply to every route in the application +import './globals.css' + +export const metadata: Metadata = { + title: 'Create Next App', + description: 'Generated by create next app', +} + +export default function RootLayout({ + children, +}: { + children: React.ReactNode +}) { + return ( + + {children} + + ) +} +``` + +```jsx filename="app/layout.js" switcher +// These styles apply to every route in the application +import './globals.css' + +export const metadata = { + title: 'Create Next App', + description: 'Generated by create next app', +} + +export default function RootLayout({ children }) { + return ( + + {children} + + ) +} +``` + +## Using Classes + +After installing Tailwind CSS and adding the global styles, you can use Tailwind's utility classes in your application. + +```tsx filename="app/page.tsx" switcher +export default function Page() { + return

    Hello, Next.js!

    +} +``` + +```jsx filename="app/page.js" switcher +export default function Page() { + return

    Hello, Next.js!

    +} +``` + +## Usage with Turbopack + +As of Next.js 13.1, Tailwind CSS and PostCSS are supported with [Turbopack](https://turbo.build/pack/docs/features/css#tailwind-css). diff --git a/docs/02-app/01-building-your-application/04-styling/03-css-in-js.mdx b/docs/02-app/01-building-your-application/04-styling/03-css-in-js.mdx new file mode 100644 index 0000000000000..ca40c285c7429 --- /dev/null +++ b/docs/02-app/01-building-your-application/04-styling/03-css-in-js.mdx @@ -0,0 +1,310 @@ +--- +title: CSS-in-JS +description: Use CSS-in-JS libraries with Next.js +--- + + + +> **Warning:** CSS-in-JS libraries which require runtime JavaScript are not currently supported in Server Components. Using CSS-in-JS with newer React features like Server Components and Streaming requires library authors to support the latest version of React, including [concurrent rendering](https://react.dev/blog/2022/03/29/react-v18#what-is-concurrent-react). +> +> We're working with the React team on upstream APIs to handle CSS and JavaScript assets with support for React Server Components and streaming architecture. + +The following libraries are supported in Client Components in the `app` directory: + +- [`styled-jsx`](#styled-jsx) +- [`styled-components`](#styled-components) +- [`tamagui`](https://tamagui.dev/docs/guides/next-js#server-components) +- [`style9`](https://github.com/johanholmerin/style9) +- [`vanilla-extract`](https://github.com/SuttonJack/vanilla-extract-app-dir) + +The following are currently working on support: + +- [`emotion`](https://github.com/emotion-js/emotion/issues/2928) +- [Material UI](https://github.com/mui/material-ui/issues/34905#issuecomment-1401306594) +- [Chakra UI](https://github.com/chakra-ui/chakra-ui/issues/7180) + +> **Note:** We're testing out different CSS-in-JS libraries and we'll be adding more examples for libraries that support React 18 features and/or the `app` directory. + +If you want to style Server Components, we recommend using [CSS Modules](/docs/app/building-your-application/styling/css-modules) or other solutions that output CSS files, like PostCSS or [Tailwind CSS](/docs/app/building-your-application/styling/tailwind-css). + +## Configuring CSS-in-JS in `app` + +Configuring CSS-in-JS is a three-step opt-in process that involves: + +1. A **style registry** to collect all CSS rules in a render. +2. The new `useServerInsertedHTML` hook to inject rules before any content that might use them. +3. A Client Component that wraps your app with the style registry during initial server-side rendering. + +### `styled-jsx` + +Using `styled-jsx` in Client Components requires using `v5.1.0`. First, create a new registry: + +```tsx filename="app/registry.tsx" switcher +'use client' + +import React, { useState } from 'react' +import { useServerInsertedHTML } from 'next/navigation' +import { StyleRegistry, createStyleRegistry } from 'styled-jsx' + +export default function StyledJsxRegistry({ + children, +}: { + children: React.ReactNode +}) { + // Only create stylesheet once with lazy initial state + // x-ref: https://reactjs.org/docs/hooks-reference.html#lazy-initial-state + const [jsxStyleRegistry] = useState(() => createStyleRegistry()) + + useServerInsertedHTML(() => { + const styles = jsxStyleRegistry.styles() + jsxStyleRegistry.flush() + return <>{styles} + }) + + return {children} +} +``` + +```jsx filename="app/registry.js" switcher +'use client' + +import React, { useState } from 'react' +import { useServerInsertedHTML } from 'next/navigation' +import { StyleRegistry, createStyleRegistry } from 'styled-jsx' + +export default function StyledJsxRegistry({ children }) { + // Only create stylesheet once with lazy initial state + // x-ref: https://reactjs.org/docs/hooks-reference.html#lazy-initial-state + const [jsxStyleRegistry] = useState(() => createStyleRegistry()) + + useServerInsertedHTML(() => { + const styles = jsxStyleRegistry.styles() + jsxStyleRegistry.flush() + return <>{styles} + }) + + return {children} +} +``` + +Then, wrap your [root layout](/docs/app/building-your-application/routing/pages-and-layouts#root-layout-required) with the registry: + +```tsx filename="app/layout.tsx" switcher +import StyledJsxRegistry from './registry' + +export default function RootLayout({ + children, +}: { + children: React.ReactNode +}) { + return ( + + + {children} + + + ) +} +``` + +```jsx filename="app/layout.js" switcher +import StyledJsxRegistry from './registry' + +export default function RootLayout({ children }) { + return ( + + + {children} + + + ) +} +``` + +[View an example here](https://github.com/vercel/app-playground/tree/main/app/styling/styled-jsx). + +### Styled Components + +Below is an example of how to configure `styled-components@v6.0.0-rc.1` or greater: + +First, use the `styled-components` API to create a global registry component to collect all CSS style rules generated during a render, and a function to return those rules. Then use the `useServerInsertedHTML` hook to inject the styles collected in the registry into the `` HTML tag in the root layout. + +```tsx filename="lib/registry.tsx" switcher +'use client' + +import React, { useState } from 'react' +import { useServerInsertedHTML } from 'next/navigation' +import { ServerStyleSheet, StyleSheetManager } from 'styled-components' + +export default function StyledComponentsRegistry({ + children, +}: { + children: React.ReactNode +}) { + // Only create stylesheet once with lazy initial state + // x-ref: https://reactjs.org/docs/hooks-reference.html#lazy-initial-state + const [styledComponentsStyleSheet] = useState(() => new ServerStyleSheet()) + + useServerInsertedHTML(() => { + const styles = styledComponentsStyleSheet.getStyleElement() + styledComponentsStyleSheet.instance.clearTag() + return <>{styles} + }) + + if (typeof window !== 'undefined') return <>{children} + + return ( + + {children} + + ) +} +``` + +```jsx filename="lib/registry.js" switcher +'use client' + +import React, { useState } from 'react' +import { useServerInsertedHTML } from 'next/navigation' +import { ServerStyleSheet, StyleSheetManager } from 'styled-components' + +export default function StyledComponentsRegistry({ children }) { + // Only create stylesheet once with lazy initial state + // x-ref: https://reactjs.org/docs/hooks-reference.html#lazy-initial-state + const [styledComponentsStyleSheet] = useState(() => new ServerStyleSheet()) + + useServerInsertedHTML(() => { + const styles = styledComponentsStyleSheet.getStyleElement() + styledComponentsStyleSheet.instance.clearTag() + return <>{styles} + }) + + if (typeof window !== 'undefined') return <>{children} + + return ( + + {children} + + ) +} +``` + +Wrap the `children` of the root layout with the style registry component: + +```tsx filename="app/layout.tsx" switcher +import StyledComponentsRegistry from './lib/registry' + +export default function RootLayout({ + children, +}: { + children: React.ReactNode +}) { + return ( + + + {children} + + + ) +} +``` + +```jsx filename="app/layout.js" switcher +import StyledComponentsRegistry from './lib/registry' + +export default function RootLayout({ children }) { + return ( + + + {children} + + + ) +} +``` + +[View an example here](https://github.com/vercel/app-playground/tree/main/app/styling/styled-components). + +> **Good to know:** +> +> - During server rendering, styles will be extracted to a global registry and flushed to the `` of your HTML. This ensures the style rules are placed before any content that might use them. In the future, we may use an upcoming React feature to determine where to inject the styles. +> - During streaming, styles from each chunk will be collected and appended to existing styles. After client-side hydration is complete, `styled-components` will take over as usual and inject any further dynamic styles. +> - We specifically use a Client Component at the top level of the tree for the style registry because it's more efficient to extract CSS rules this way. It avoids re-generating styles on subsequent server renders, and prevents them from being sent in the Server Component payload. + + + + + +
    + + Examples + + +- [Styled JSX](https://github.com/vercel/next.js/tree/canary/examples/with-styled-jsx) +- [Styled Components](https://github.com/vercel/next.js/tree/canary/examples/with-styled-components) +- [Emotion](https://github.com/vercel/next.js/tree/canary/examples/with-emotion) +- [Linaria](https://github.com/vercel/next.js/tree/canary/examples/with-linaria) +- [Tailwind CSS + Emotion](https://github.com/vercel/next.js/tree/canary/examples/with-tailwindcss-emotion) +- [Styletron](https://github.com/vercel/next.js/tree/canary/examples/with-styletron) +- [Cxs](https://github.com/vercel/next.js/tree/canary/examples/with-cxs) +- [Aphrodite](https://github.com/vercel/next.js/tree/canary/examples/with-aphrodite) +- [Fela](https://github.com/vercel/next.js/tree/canary/examples/with-fela) +- [Stitches](https://github.com/vercel/next.js/tree/canary/examples/with-stitches) + +
    + +It's possible to use any existing CSS-in-JS solution.The simplest one is inline styles: + +```jsx +function HiThere() { + return

    hi there

    +} + +export default HiThere +``` + +We bundle [styled-jsx](https://github.com/vercel/styled-jsx) to provide support for isolated scoped CSS. +The aim is to support "shadow CSS" similar to Web Components, which unfortunately [do not support server-rendering and are JS-only](https://github.com/w3c/webcomponents/issues/71). + +See the above examples for other popular CSS-in-JS solutions (like Styled Components). + +A component using `styled-jsx` looks like this: + +```jsx +function HelloWorld() { + return ( +
    + Hello world +

    scoped!

    + + +
    + ) +} + +export default HelloWorld +``` + +Please see the [styled-jsx documentation](https://github.com/vercel/styled-jsx) for more examples. + +### Disabling JavaScript + +Yes, if you disable JavaScript the CSS will still be loaded in the production build (`next start`). During development, we require JavaScript to be enabled to provide the best developer experience with [Fast Refresh](https://nextjs.org/blog/next-9-4#fast-refresh). + +
    diff --git a/docs/02-app/01-building-your-application/04-styling/04-sass.mdx b/docs/02-app/01-building-your-application/04-styling/04-sass.mdx new file mode 100644 index 0000000000000..326fc39c49a61 --- /dev/null +++ b/docs/02-app/01-building-your-application/04-styling/04-sass.mdx @@ -0,0 +1,79 @@ +--- +title: Sass +description: Style your Next.js application using Sass. +--- + +Next.js has built-in support for Sass using both the `.scss` and `.sass` extensions. You can use component-level Sass via CSS Modules and the `.module.scss`or `.module.sass` extension. + +First, install [`sass`](https://github.com/sass/sass): + +```bash filename="Terminal" +npm install --save-dev sass +``` + +> **Good to know**: +> +> Sass supports [two different syntax](https://sass-lang.com/documentation/syntax), each with their own extension. +> The `.scss` extension requires you use the [SCSS syntax](https://sass-lang.com/documentation/syntax#scss), +> while the `.sass` extension requires you use the [Indented Syntax ("Sass")](https://sass-lang.com/documentation/syntax#the-indented-syntax). +> +> If you're not sure which to choose, start with the `.scss` extension which is a superset of CSS, and doesn't require you learn the +> Indented Syntax ("Sass"). + +### Customizing Sass Options + +If you want to configure the Sass compiler, use `sassOptions` in `next.config.js`. + +```js filename="next.config.js" +const path = require('path') + +module.exports = { + sassOptions: { + includePaths: [path.join(__dirname, 'styles')], + }, +} +``` + +### Sass Variables + +Next.js supports Sass variables exported from CSS Module files. + +For example, using the exported `primaryColor` Sass variable: + +```scss filename="app/variables.module.scss" +$primary-color: #64ff00; + +:export { + primaryColor: $primary-color; +} +``` + + + +```jsx filename="app/page.js" +// maps to root `/` URL + +import variables from './variables.module.scss' + +export default function Page() { + return

    Hello, Next.js!

    +} +``` + +
    + + + +```jsx filename="pages/_app.js" +import variables from '../styles/variables.module.scss' + +export default function MyApp({ Component, pageProps }) { + return ( + + + + ) +} +``` + + diff --git a/docs/02-app/01-building-your-application/04-styling/index.mdx b/docs/02-app/01-building-your-application/04-styling/index.mdx new file mode 100644 index 0000000000000..c02464f5d6253 --- /dev/null +++ b/docs/02-app/01-building-your-application/04-styling/index.mdx @@ -0,0 +1,14 @@ +--- +title: Styling +description: Learn the different ways you can style your Next.js application. +--- + +Next.js supports different ways of styling your application, including: + +- **Global CSS**: Simple to use and familiar for those experienced with traditional CSS, but can lead to larger CSS bundles and difficulty managing styles as the application grows. +- **CSS Modules**: Create locally scoped CSS classes to avoid naming conflicts and improve maintainability. +- **Tailwind CSS**: A utility-first CSS framework that allows for rapid custom designs by composing utility classes. +- **Sass**: A popular CSS preprocessor that extends CSS with features like variables, nested rules, and mixins. +- **CSS-in-JS**: Embed CSS directly in your JavaScript components, enabling dynamic and scoped styling. + +Learn more about each approach by exploring their respective documentation: diff --git a/docs/02-app/01-building-your-application/05-optimizing/01-images.mdx b/docs/02-app/01-building-your-application/05-optimizing/01-images.mdx new file mode 100644 index 0000000000000..bad78323aeab2 --- /dev/null +++ b/docs/02-app/01-building-your-application/05-optimizing/01-images.mdx @@ -0,0 +1,250 @@ +--- +title: Image Optimization +nav_title: Images +description: Optimize your images with the built-in `next/image` component. +related: + title: API Reference + description: Learn more about the next/image API. + links: + - app/api-reference/components/image +--- + +
    + + Examples + + +- [Image Component](https://github.com/vercel/next.js/tree/canary/examples/image-component) + +
    + +The Next.js Image component extends the HTML `` element with: + +- **Size Optimization:** Automatically serve correctly sized images for each device, using modern image formats like WebP and AVIF. +- **Visual Stability:** Prevent [layout shift](/learn/seo/web-performance/cls) automatically when images are loading. +- **Faster Page Loads:** Images are only loaded when they enter the viewport using native browser lazy loading, with optional blur-up placeholders. +- **Asset Flexibility:** On-demand image resizing, even for images stored on remote servers + +## Usage + +```jsx +import Image from 'next/image' +``` + +You can then define the `src` for your image (either local or remote). + +### Local Images + +To use a local image, `import` your `.jpg`, `.png`, or `.webp` image files. + +Next.js will [automatically determine](#image-sizing) the `width` and `height` of your image based on the imported file. These values are used to prevent [Cumulative Layout Shift](https://nextjs.org/learn/seo/web-performance/cls) while your image is loading. + + + +```jsx filename="app/page.js" +import Image from 'next/image' +import profilePic from './me.png' + +export default function Page() { + return ( + Picture of the author + ) +} +``` + + + + + +```jsx filename="pages/index.js" +import Image from 'next/image' +import profilePic from '../public/me.png' + +export default function Page() { + return ( + Picture of the author + ) +} +``` + + + +> **Warning:** Dynamic `await import()` or `require()` are _not_ supported. The `import` must be static so it can be analyzed at build time. + +### Remote Images + +To use a remote image, the `src` property should be a URL string. + +Since Next.js does not have access to remote files during the build process, you'll need to provide the [`width`](/docs/app/api-reference/components/image#width), [`height`](/docs/app/api-reference/components/image#height) and optional [`blurDataURL`](/docs/app/api-reference/components/image#blurdataurl) props manually. + +The `width` and `height` attributes are used to infer the correct aspect ratio of image and avoid layout shift from the image loading in. The `width` and `height` do _not_ determine the rendered size of the image file. Learn more about [Image Sizing](#image-sizing). + +```jsx filename="app/page.js" +import Image from 'next/image' + +export default function Page() { + return ( + Picture of the author + ) +} +``` + +To safely allow optimizing images, define a list of supported URL patterns in `next.config.js`. Be as specific as possible to prevent malicious usage. For example, the following configuration will only allow images from a specific AWS S3 bucket: + +```js filename="next.config.js" +module.exports = { + images: { + remotePatterns: [ + { + protocol: 'https', + hostname: 's3.amazonaws.com', + port: '', + pathname: '/my-bucket/**', + }, + ], + }, +} +``` + +Learn more about [`remotePatterns`](/docs/app/api-reference/components/image#remotepatterns) configuration. If you want to use relative URLs for the image `src`, use a [`loader`](/docs/app/api-reference/components/image#loader). + +### Domains + +Sometimes you may want to optimize a remote image, but still use the built-in Next.js Image Optimization API. To do this, leave the `loader` at its default setting and enter an absolute URL for the Image `src` prop. + +To protect your application from malicious users, you must define a list of remote hostnames you intend to use with the `next/image` component. + +> Learn more about [`remotePatterns`](/docs/app/api-reference/components/image#remotepatterns) configuration. + +### Loaders + +Note that in the [example earlier](#remote-images), a partial URL (`"/me.png"`) is provided for a remote image. This is possible because of the loader architecture. + +A loader is a function that generates the URLs for your image. It modifies the provided `src`, and generates multiple URLs to request the image at different sizes. These multiple URLs are used in the automatic [srcset](https://developer.mozilla.org/en-US/docs/Web/API/HTMLImageElement/srcset) generation, so that visitors to your site will be served an image that is the right size for their viewport. + +The default loader for Next.js applications uses the built-in Image Optimization API, which optimizes images from anywhere on the web, and then serves them directly from the Next.js web server. If you would like to serve your images directly from a CDN or image server, you can write your own loader function with a few lines of JavaScript. + +You can define a loader per-image with the [`loader` prop](/docs/app/api-reference/components/image#loader), or at the application level with the [`loaderFile` configuration](/docs/app/api-reference/components/image#loaderfile). + +## Priority + +You should add the `priority` property to the image that will be the [Largest Contentful Paint (LCP) element](https://web.dev/lcp/#what-elements-are-considered) for each page. Doing so allows Next.js to specially prioritize the image for loading (e.g. through preload tags or priority hints), leading to a meaningful boost in LCP. + +The LCP element is typically the largest image or text block visible within the viewport of the page. When you run `next dev`, you'll see a console warning if the LCP element is an `` without the `priority` property. + +Once you've identified the LCP image, you can add the property like this: + + + +```jsx filename="app/page.js" +import Image from 'next/image' + +export default function Home() { + return ( + <> +

    My Homepage

    + Picture of the author +

    Welcome to my homepage!

    + + ) +} +``` + +
    + + + +```jsx filename="app/page.js" +import Image from 'next/image' +import profilePic from '../public/me.png' + +export default function Page() { + return Picture of the author +} +``` + + + +See more about priority in the [`next/image` component documentation](/docs/app/api-reference/components/image#priority). + +## Image Sizing + +One of the ways that images most commonly hurt performance is through _layout shift_, where the image pushes other elements around on the page as it loads in. This performance problem is so annoying to users that it has its own Core Web Vital, called [Cumulative Layout Shift](https://web.dev/cls/). The way to avoid image-based layout shifts is to [always size your images](https://web.dev/optimize-cls/#images-without-dimensions). This allows the browser to reserve precisely enough space for the image before it loads. + +Because `next/image` is designed to guarantee good performance results, it cannot be used in a way that will contribute to layout shift, and **must** be sized in one of three ways: + +1. Automatically, using a [static import](#local-images) +2. Explicitly, by including a [`width`](/docs/app/api-reference/components/image#width) and [`height`](/docs/app/api-reference/components/image#height) property +3. Implicitly, by using [fill](/docs/app/api-reference/components/image#fill) which causes the image to expand to fill its parent element. + +> **What if I don't know the size of my images?** +> +> If you are accessing images from a source without knowledge of the images' sizes, there are several things you can do: +> +> **Use `fill`** +> +> The [`fill`](/docs/app/api-reference/components/image#fill) prop allows your image to be sized by its parent element. Consider using CSS to give the image's parent element space on the page along [`sizes`](/docs/app/api-reference/components/image#sizes) prop to match any media query break points. You can also use [`object-fit`](https://developer.mozilla.org/en-US/docs/Web/CSS/object-fit) with `fill`, `contain`, or `cover`, and [`object-position`](https://developer.mozilla.org/en-US/docs/Web/CSS/object-position) to define how the image should occupy that space. +> +> **Normalize your images** +> +> If you're serving images from a source that you control, consider modifying your image pipeline to normalize the images to a specific size. +> +> **Modify your API calls** +> +> If your application is retrieving image URLs using an API call (such as to a CMS), you may be able to modify the API call to return the image dimensions along with the URL. + +If none of the suggested methods works for sizing your images, the `next/image` component is designed to work well on a page alongside standard `` elements. + +## Styling + +Styling the Image component is similar to styling a normal `` element, but there are a few guidelines to keep in mind: + +- Use `className` or `style`, not `styled-jsx`. + - In most cases, we recommend using the `className` prop. This can be an imported [CSS Module](/docs/app/building-your-application/styling/css-modules), a [global stylesheet](/docs/app/building-your-application/styling/css-modules#global-styles), etc. + - You can also use the `style` prop to assign inline styles. + - You cannot use [styled-jsx](/docs/app/building-your-application/styling/css-in-js) because it's scoped to the current component (unless you mark the style as `global`). +- When using `fill`, the parent element must have `position: relative` + - This is necessary for the proper rendering of the image element in that layout mode. +- When using `fill`, the parent element must have `display: block` + - This is the default for `
    ` elements but should be specified otherwise. + +For examples, see the [Image Component Demo](https://image-component.nextjs.gallery). + +### Examples + +For examples of the Image component used with the various styles, see the [Image Component Demo](https://image-component.nextjs.gallery). + +## Other Properties + +[**View all properties available to the `next/image` component.**](/docs/app/api-reference/components/image) + +## Configuration + +The `next/image` component and Next.js Image Optimization API can be configured in the [`next.config.js` file](/docs/app/api-reference/next-config-js). These configurations allow you to [enable remote images](/docs/app/api-reference/components/image#remotepatterns), [define custom image breakpoints](/docs/app/api-reference/components/image#devicesizes), [change caching behavior](/docs/app/api-reference/components/image#caching-behavior) and more. + +[**Read the full image configuration documentation for more information.**](/docs/app/api-reference/components/image#configuration-options) diff --git a/docs/02-app/01-building-your-application/05-optimizing/02-fonts.mdx b/docs/02-app/01-building-your-application/05-optimizing/02-fonts.mdx new file mode 100644 index 0000000000000..58c249781677a --- /dev/null +++ b/docs/02-app/01-building-your-application/05-optimizing/02-fonts.mdx @@ -0,0 +1,639 @@ +--- +title: Font Optimization +nav_title: Fonts +description: Optimize your application's web fonts with the built-in `next/font` loaders. +related: + title: API Reference + description: Learn more about the next/font API. + links: + - app/api-reference/components/font +--- + +[**`next/font`**](/docs/app/api-reference/components/font) will automatically optimize your fonts (including custom fonts) and remove external network requests for improved privacy and performance. + +> **🎥 Watch:** Learn more about how to use `next/font` → [YouTube (6 minutes)](https://www.youtube.com/watch?v=L8_98i_bMMA). + +`next/font` includes **built-in automatic self-hosting** for _any_ font file. This means you can optimally load web fonts with zero layout shift, thanks to the underlying CSS `size-adjust` property used. + +This new font system also allows you to conveniently use all Google Fonts with performance and privacy in mind. CSS and font files are downloaded at build time and self-hosted with the rest of your static assets. **No requests are sent to Google by the browser.** + +## Google Fonts + +Automatically self-host any Google Font. Fonts are included in the deployment and served from the same domain as your deployment. **No requests are sent to Google by the browser.** + +Get started by importing the font you would like to use from `next/font/google` as a function. We recommend using [variable fonts](https://fonts.google.com/variablefonts) for the best performance and flexibility. + + + +```tsx filename="app/layout.tsx" switcher +import { Inter } from 'next/font/google' + +// If loading a variable font, you don't need to specify the font weight +const inter = Inter({ + subsets: ['latin'], + display: 'swap', +}) + +export default function RootLayout({ + children, +}: { + children: React.ReactNode +}) { + return ( + + {children} + + ) +} +``` + +```jsx filename="app/layout.js" switcher +import { Inter } from 'next/font/google' + +// If loading a variable font, you don't need to specify the font weight +const inter = Inter({ + subsets: ['latin'], + display: 'swap', +}) + +export default function RootLayout({ children }) { + return ( + + {children} + + ) +} +``` + +If you can't use a variable font, you will **need to specify a weight**: + +```tsx filename="app/layout.tsx" switcher +import { Roboto } from 'next/font/google' + +const roboto = Roboto({ + weight: '400', + subsets: ['latin'], + display: 'swap', +}) + +export default function RootLayout({ + children, +}: { + children: React.ReactNode +}) { + return ( + + {children} + + ) +} +``` + +```jsx filename="app/layout.js" switcher +import { Roboto } from 'next/font/google' + +const roboto = Roboto({ + weight: '400', + subsets: ['latin'], + display: 'swap', +}) + +export default function RootLayout({ children }) { + return ( + + {children} + + ) +} +``` + + + + + +To use the font in all your pages, add it to [`_app.js` file](/docs/pages/building-your-application/routing/custom-app) under `/pages` as shown below: + +```jsx filename="pages/_app.js" +import { Inter } from 'next/font/google' + +// If loading a variable font, you don't need to specify the font weight +const inter = Inter({ subsets: ['latin'] }) + +export default function MyApp({ Component, pageProps }) { + return ( +
    + +
    + ) +} +``` + +If you can't use a variable font, you will **need to specify a weight**: + +```jsx filename="pages/_app.js" +import { Roboto } from 'next/font/google' + +const roboto = Roboto({ + weight: '400', + subsets: ['latin'], +}) + +export default function MyApp({ Component, pageProps }) { + return ( +
    + +
    + ) +} +``` + +
    + +You can specify multiple weights and/or styles by using an array: + +```jsx filename="app/layout.js" +const roboto = Roboto({ + weight: ['400', '700'], + style: ['normal', 'italic'], + subsets: ['latin'], + display: 'swap', +}) +``` + +> **Good to know**: Use an underscore (\_) for font names with multiple words. E.g. `Roboto Mono` should be imported as `Roboto_Mono`. + + + +### Apply the font in `` + +You can also use the font without a wrapper and `className` by injecting it inside the `` as follows: + +```jsx filename="pages/_app.js" +import { Inter } from 'next/font/google' + +const inter = Inter({ subsets: ['latin'] }) + +export default function MyApp({ Component, pageProps }) { + return ( + <> + + + + ) +} +``` + +### Single page usage + +To use the font on a single page, add it to the specific page as shown below: + +```jsx filename="pages/index.js" +import { Inter } from 'next/font/google' + +const inter = Inter({ subsets: ['latin'] }) + +export default function Home() { + return ( +
    +

    Hello World

    +
    + ) +} +``` + +
    + +### Specifying a subset + +Google Fonts are automatically [subset](https://fonts.google.com/knowledge/glossary/subsetting). This reduces the size of the font file and improves performance. You'll need to define which of these subsets you want to preload. Failing to specify any subsets while [`preload`](/docs/app/api-reference/components/font#preload) is `true` will result in a warning. + +This can be done by adding it to the function call: + + + +```tsx filename="app/layout.tsx" switcher +const inter = Inter({ subsets: ['latin'] }) +``` + +```jsx filename="app/layout.js" switcher +const inter = Inter({ subsets: ['latin'] }) +``` + + + + + +```jsx filename="pages/_app.js" +const inter = Inter({ subsets: ['latin'] }) +``` + + + +View the [Font API Reference](#google-fonts) for more information. + +### Using Multiple Fonts + +You can import and use multiple fonts in your application. There are two approaches you can take. + +The first approach is to create a utility function that exports a font, imports it, and applies its `className` where needed. This ensures the font is preloaded only when it's rendered: + +```tsx filename="app/fonts.ts" switcher +import { Inter, Roboto_Mono } from 'next/font/google' + +export const inter = Inter({ + subsets: ['latin'], + display: 'swap', +}) + +export const roboto_mono = Roboto_Mono({ + subsets: ['latin'], + display: 'swap', +}) +``` + +```jsx filename="app/fonts.js" switcher +import { Inter, Roboto_Mono } from 'next/font/google' + +export const inter = Inter({ + subsets: ['latin'], + display: 'swap', +}) + +export const roboto_mono = Roboto_Mono({ + subsets: ['latin'], + display: 'swap', +}) +``` + + + +```tsx filename="app/layout.tsx" switcher +import { inter } from './fonts' + +export default function Layout({ children }: { children: React.ReactNode }) { + return ( + + +
    {children}
    + + + ) +} +``` + +```jsx filename="app/layout.js" switcher +import { inter } from './fonts' + +export default function Layout({ children }) { + return ( + + +
    {children}
    + + + ) +} +``` + +```tsx filename="app/page.tsx" switcher +import { roboto_mono } from './fonts' + +export default function Page() { + return ( + <> +

    My page

    + + ) +} +``` + +```jsx filename="app/page.js" switcher +import { roboto_mono } from './fonts' + +export default function Page() { + return ( + <> +

    My page

    + + ) +} +``` + +
    + +In the example above, `Inter` will be applied globally, and `Roboto Mono` can be imported and applied as needed. + +Alternatively, you can create a [CSS variable](/docs/app/api-reference/components/font#variable) and use it with your preferred CSS solution: + + + +```tsx filename="app/layout.tsx" switcher +import { Inter, Roboto_Mono } from 'next/font/google' +import styles from './global.css' + +const inter = Inter({ + subsets: ['latin'], + variable: '--font-inter', + display: 'swap', +}) + +const roboto_mono = Roboto_Mono({ + subsets: ['latin'], + variable: '--font-roboto-mono', + display: 'swap', +}) + +export default function RootLayout({ + children, +}: { + children: React.ReactNode +}) { + return ( + + +

    My App

    +
    {children}
    + + + ) +} +``` + +```jsx filename="app/layout.js" switcher +import { Inter, Roboto_Mono } from 'next/font/google' + +const inter = Inter({ + subsets: ['latin'], + variable: '--font-inter', + display: 'swap', +}) + +const roboto_mono = Roboto_Mono({ + subsets: ['latin'], + variable: '--font-roboto-mono', + display: 'swap', +}) + +export default function RootLayout({ children }) { + return ( + + +

    My App

    +
    {children}
    + + + ) +} +``` + +
    + +```css filename="app/global.css" +html { + font-family: var(--font-inter); +} + +h1 { + font-family: var(--font-roboto-mono); +} +``` + +In the example above, `Inter` will be applied globally, and any `

    ` tags will be styled with `Roboto Mono`. + +> **Recommendation**: Use multiple fonts conservatively since each new font is an additional resource the client has to download. + +## Local Fonts + +Import `next/font/local` and specify the `src` of your local font file. We recommend using [variable fonts](https://fonts.google.com/variablefonts) for the best performance and flexibility. + + + +```tsx filename="app/layout.tsx" switcher +import localFont from 'next/font/local' + +// Font files can be colocated inside of `app` +const myFont = localFont({ + src: './my-font.woff2', + display: 'swap', +}) + +export default function RootLayout({ + children, +}: { + children: React.ReactNode +}) { + return ( + + {children} + + ) +} +``` + +```jsx filename="app/layout.js" switcher +import localFont from 'next/font/local' + +// Font files can be colocated inside of `app` +const myFont = localFont({ + src: './my-font.woff2', + display: 'swap', +}) + +export default function RootLayout({ children }) { + return ( + + {children} + + ) +} +``` + + + + + +```jsx filename="pages/_app.js" +import localFont from 'next/font/local' + +// Font files can be colocated inside of `pages` +const myFont = localFont({ src: './my-font.woff2' }) + +export default function MyApp({ Component, pageProps }) { + return ( +
    + +
    + ) +} +``` + +
    + +If you want to use multiple files for a single font family, `src` can be an array: + +```js +const roboto = localFont({ + src: [ + { + path: './Roboto-Regular.woff2', + weight: '400', + style: 'normal', + }, + { + path: './Roboto-Italic.woff2', + weight: '400', + style: 'italic', + }, + { + path: './Roboto-Bold.woff2', + weight: '700', + style: 'normal', + }, + { + path: './Roboto-BoldItalic.woff2', + weight: '700', + style: 'italic', + }, + ], +}) +``` + +View the [Font API Reference](#local-fonts) for more information. + +## With Tailwind CSS + +`next/font` can be used with [Tailwind CSS](https://tailwindcss.com/) through a [CSS variable](/docs/app/api-reference/components/font#css-variables). + +In the example below, we use the font `Inter` from `next/font/google` (you can use any font from Google or Local Fonts). Load your font with the `variable` option to define your CSS variable name and assign it to `inter`. Then, use `inter.variable` to add the CSS variable to your HTML document. + + + +```tsx filename="app/layout.tsx" switcher +import { Inter, Roboto_Mono } from 'next/font/google' + +const inter = Inter({ + subsets: ['latin'], + display: 'swap', + variable: '--font-inter', +}) + +const roboto_mono = Roboto_Mono({ + subsets: ['latin'], + display: 'swap', + variable: '--font-roboto-mono', +}) + +export default function RootLayout({ + children, +}: { + children: React.ReactNode +}) { + return ( + + {children} + + ) +} +``` + +```jsx filename="app/layout.js" switcher +import { Inter, Roboto_Mono } from 'next/font/google' + +const inter = Inter({ + subsets: ['latin'], + display: 'swap', + variable: '--font-inter', +}) + +const roboto_mono = Roboto_Mono({ + subsets: ['latin'], + display: 'swap', + variable: '--font-roboto-mono', +}) + +export default function RootLayout({ children }) { + return ( + + {children} + + ) +} +``` + + + + + +```js filename="pages/_app.js" +import { Inter } from 'next/font/google' + +const inter = Inter({ + subsets: ['latin'], + variable: '--font-inter', +}) + +export default function MyApp({ Component, pageProps }) { + return ( +
    + +
    + ) +} +``` + +
    + +Finally, add the CSS variable to your [Tailwind CSS config](/docs/app/building-your-application/styling/tailwind-css#configuring-tailwind): + +```js filename="tailwind.config.js" +/** @type {import('tailwindcss').Config} */ +module.exports = { + content: [ + './pages/**/*.{js,ts,jsx,tsx}', + './components/**/*.{js,ts,jsx,tsx}', + ], + theme: { + extend: { + fontFamily: { + sans: ['var(--font-inter)'], + mono: ['var(--font-roboto-mono)'], + }, + }, + }, + plugins: [], +} +``` + +You can now use the `font-sans` and `font-mono` utility classes to apply the font to your elements. + +## Preloading + + +When a font function is called on a page of your site, it is not globally available and preloaded on all routes. Rather, the font is only preloaded on the related routes based on the type of file where it is used: + +- If it's a [unique page](/docs/app/building-your-application/routing/pages-and-layouts#pages), it is preloaded on the unique route for that page. +- If it's a [layout](/docs/app/building-your-application/routing/pages-and-layouts#layouts), it is preloaded on all the routes wrapped by the layout. +- If it's the [root layout](/docs/app/building-your-application/routing/pages-and-layouts#root-layout-required), it is preloaded on all routes. + + + + + +When a font function is called on a page of your site, it is not globally available and preloaded on all routes. Rather, the font is only preloaded on the related route/s based on the type of file where it is used: + +- if it's a [unique page](/docs/pages/building-your-application/routing/pages-and-layouts), it is preloaded on the unique route for that page +- if it's in the [custom App](/docs/pages/building-your-application/routing/custom-app), it is preloaded on all the routes of the site under `/pages` + + + +## Reusing fonts + +Every time you call the `localFont` or Google font function, that font is hosted as one instance in your application. Therefore, if you load the same font function in multiple files, multiple instances of the same font are hosted. In this situation, it is recommended to do the following: + +- Call the font loader function in one shared file +- Export it as a constant +- Import the constant in each file where you would like to use this font diff --git a/docs/02-app/01-building-your-application/05-optimizing/03-scripts.mdx b/docs/02-app/01-building-your-application/05-optimizing/03-scripts.mdx new file mode 100644 index 0000000000000..4a95dc0a5164a --- /dev/null +++ b/docs/02-app/01-building-your-application/05-optimizing/03-scripts.mdx @@ -0,0 +1,373 @@ +--- +title: Script Optimization +nav_title: Scripts +description: Optimize 3rd party scripts with the built-in Script component. +related: + title: API Reference + description: Learn more about the next/script API. + links: + - app/api-reference/components/script +--- + + + +### Layout Scripts + +To load a third-party script for multiple routes, import `next/script` and include the script directly in your layout component: + +```tsx filename="app/dashboard/layout.tsx" switcher +import Script from 'next/script' + +export default function DashboardLayout({ + children, +}: { + children: React.ReactNode +}) { + return ( + <> +
    {children}
    + +``` + +Or by using the `dangerouslySetInnerHTML` property: + +```jsx + +``` + +The HTML [script](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script) tag is used to embed any client-side JavaScript. It can either contain inline scripting statements (as shown in the example above) or point to an external script file via the `src` attribute. +This example validates the name and roll number of a user. The `validateFormWithJS()` function does not allow an empty name field, and the roll number must be at least three digits long. The validation is performed when you hit the Submit button. You are not redirected to the next page until the given values are correct. + +![js-validation](https://assets.vercel.com/image/upload/dpr_auto,q_auto,f_auto/nextjs/guides/building-forms/js-validation.jpg) + +#### Form Validation Using Regular Expressions + +JavaScript validation with Regular Expressions uses the `pattern` HTML attribute. A regular expression (commonly known as RegEx) is an object that describes a pattern of characters. You can only apply the `pattern` attribute to the `` element. This way, you can validate the input value using Regular Expressions (RegEx) by defining your own rules. Once again, if the value does not match the defined pattern, the input will give an error. +The below example shows using the `pattern` attribute on an `input` element: + +```html + + + + + + +``` + +The password form field must only contain digits (0 to 9), lowercase alphabets (a to z) and it must be no more than 15 characters in length. No other characters (#,$,&, etc.) are allowed. The rule in RegEx is written as `[a-z0-9]{1,15}`. + +![form-validate-regex](https://assets.vercel.com/image/upload/dpr_auto,q_auto,f_auto/nextjs/guides/building-forms/form-validate-regex.jpg) + +> To learn more about HTML forms, check out the [MDN Web Docs](https://developer.mozilla.org/en-US/docs/Learn/Forms). + +## Part 2: Project Setup + +In the following section you will be creating forms in React using Next.js. + +Create a new Next.js app. You can use the [create-next-app](/docs/pages/api-reference/create-next-app) for a quick start. In your command line terminal, run the following: + +``` +npx create-next-app +``` + +Answer the questions to create your project, and give it a name, this example uses [`next-forms`](https://github.com/vercel/next.js/tree/canary/examples/next-forms). Next `cd` into this directory, and run `npm run dev` or `yarn dev` command to start the development server. + +Open the URL printed in the terminal to ensure that your app is running successfully. + +## Part 3: Setting up a Next.js Form API Route + +Both the client and the server will be built using Next.js. For the server part, create an API endpoint where you will send the form data. + +Next.js offers a file-based system for routing that's built on the [concept of pages](/docs/pages/building-your-application/routing/pages-and-layouts). Any file inside the folder `pages/api` is mapped to `/api/*` and will be treated as an API endpoint instead of a page. This [API endpoint](/docs/pages/building-your-application/routing/api-routes) is going to be server-side only. + +Go to `pages/api`, create a file called `form.js` and paste this code written in Node.js: + +```js +export default function handler(req, res) { + // Get data submitted in request's body. + const body = req.body + + // Optional logging to see the responses + // in the command line where next.js app is running. + console.log('body: ', body) + + // Guard clause checks for first and last name, + // and returns early if they are not found + if (!body.first || !body.last) { + // Sends a HTTP bad request error code + return res.status(400).json({ data: 'First or last name not found' }) + } + + // Found the name. + // Sends a HTTP success code + res.status(200).json({ data: `${body.first} ${body.last}` }) +} +``` + +This form `handler` function will receive the request `req` from the client (i.e. submitted form data). And in return, it'll send a response `res` as JSON that will have both the first and the last name. You can access this API endpoint at `http://localhost:3000/api/form` or replace the localhost URL with an actual Vercel deployment when you deploy. + +> Moreover, you can also attach this API to a database like MongoDB or Google Sheets. This way, your submitted form data will be securely stored for later use. For this guide, no database is used. Instead, the same data is returned to the user to demo how it's done. + +### Form Submission without JavaScript + +You can now use `/api/form` relative endpoint inside the `action` attribute of the form. You are sending form data to the server when the form is submitted via `POST` HTTP method (which is used to send data). + +```html +
    + + + + + +
    +``` + +If you submit this form, it will submit the data to the forms API endpoint `/api/form`. The server then responds, generally handling the data and loading the URL defined by the action attribute, causing a new page load. So in this case you'll be redirected to `http://localhost:3000/api/form` with the following response from the server. + +![form-no-js](https://assets.vercel.com/image/upload/dpr_auto,q_auto,f_auto/nextjs/guides/building-forms/form-no-js.jpg) + +## Part 4: Configuring Forms in Next.js + +You have created a Next.js API Route for form submission. Now it's time to configure the client (the form itself) inside Next.js using React. The first step will be extending your knowledge of HTML forms and converting it into React (using [JSX](https://react.dev/learn/writing-markup-with-jsx)). + +Here's the same form in a [React function component](https://react.dev/reference/react/Component) written using [JSX](https://react.dev/learn/writing-markup-with-jsx). + +```js +export default function Form() { + return ( +
    + + + + + + + +
    + ) +} +``` + +Here's what changed: + +- The `for` attribute is changed to `htmlFor`. (Since `for` is a keyword associated with the "for" loop in JavaScript, React elements use `htmlFor` instead.) +- The `action` attribute now has a relative URL which is the form API endpoint. + +This completes the basic structure of your Next.js-based form. + +> You can view the entire source code of [next-forms](https://github.com/vercel/next.js/tree/canary/examples/next-forms) example repo that we're creating here as a working example. Feel free to clone it and start right away. This demo is built with create-next-app, and you can preview the basic form CSS styles inside `/styles/global.css` file. + +![forms with nextjs](https://assets.vercel.com/image/upload/dpr_auto,q_auto,f_auto/nextjs/guides/building-forms/forms-with-nextjs.png) + +## Part 5: Form Submission without JavaScript + +JavaScript brings interactivity to our web applications, but sometimes you need to control the JavaScript bundle from being too large, or your sites visitors might have JavaScript disabled. + +There are several reasons why users disable JavaScript: + +- Addressing bandwidth constraints +- Increasing device (phone or laptop) battery life +- For privacy so they won’t be tracked with analytical scripts + +Regardless of the reason, disabling JavaScript will impact site functionality partially, if not completely. + +Next open the `next-forms` directory. Inside the `/pages` directory, create a file `no-js-form.js`. + +> **Quick Tip**: In Next.js, a page is a React Component exported from a `.js`, `.jsx`, `.ts`, or `.tsx` file in the Pages Router. Each page is associated with a route based on its file name. +> +> Example: If you create `pages/no-js-form.js`, it will be accessible at `your-domain.tld/no-js-form`. + +Let's use the same code from above: + +```js +export default function PageWithoutJSbasedForm() { + return ( +
    + + + + + + + +
    + ) +} +``` + +With JavaScript disabled, when you hit the Submit button, an event is triggered, which collects the form data and sends it to our forms API endpoint as defined in the `action` attribute and using `POST` HTTP `method`. You'll be redirected to the `/api/form` endpoint since that's how form `action` works. + +The form data will be submitted on the server as a request `req` to the form handler function written above. It will process the data and return a JSON string as a response `res` with your submitted name included. + +> To improve the experience here, as a response you can redirect the user to a page and thank them for submitting the form. + +## Part 6: Form Submission with JavaScript Enabled + +Inside `/pages`, you'll create another file called `js-form.js`. This will create a `/js-form` page on your Next.js app. + +Now, as soon as the form is submitted, we prevent the form's default behavior of reloading the page. We'll take the form data, convert it to JSON string, and send it to our server, the API endpoint. Finally, our server will respond with the name submitted. All of this with a basic JavaScript `handleSubmit()` function. + +Here's what this function looks like. It's well documented for you to understand each step: + +```js +export default function PageWithJSbasedForm() { + // Handles the submit event on form submit. + const handleSubmit = async (event) => { + // Stop the form from submitting and refreshing the page. + event.preventDefault() + + // Get data from the form. + const data = { + first: event.target.first.value, + last: event.target.last.value, + } + + // Send the data to the server in JSON format. + const JSONdata = JSON.stringify(data) + + // API endpoint where we send form data. + const endpoint = '/api/form' + + // Form the request for sending data to the server. + const options = { + // The method is POST because we are sending data. + method: 'POST', + // Tell the server we're sending JSON. + headers: { + 'Content-Type': 'application/json', + }, + // Body of the request is the JSON data we created above. + body: JSONdata, + } + + // Send the form data to our forms API on Vercel and get a response. + const response = await fetch(endpoint, options) + + // Get the response data from server as JSON. + // If server returns the name submitted, that means the form works. + const result = await response.json() + alert(`Is this your full name: ${result.data}`) + } + return ( + // We pass the event to the handleSubmit() function on submit. +
    + + + + + + + +
    + ) +} +``` + +It's a Next.js page with a React function component called `PageWithJSbasedForm` with a `
    ` element written in JSX. There's no action on the `` element. Instead, we use the `onSubmit` event handler to send data to our `{handleSubmit}` function. + +The `handleSubmit()` function processes your form data through a series of steps: + +- The `event.preventDefault()` stops the `` element from refreshing the entire page. +- We created a JavaScript object called `data` with the `first` and `last` values from the form. +- JSON is a language-agnostic data transfer format. So we use `JSON.stringify(data)` to convert the data to JSON. +- We then use `fetch()` to send the data to our `/api/form` endpoint using JSON and HTTP `POST` method. +- Server sends back a response with the name submitted. Woohoo! 🥳 + +## Conclusion + +This guide has covered the following: + +- The basic HTML `form` element +- Understanding forms with React.js +- Validating forms data with and without JavaScript +- Using Next.js API Routes to handle `req` and `res` from the client and server + +For more details go through [Next.js Learn Course](https://nextjs.org/learn/basics/create-nextjs-app). diff --git a/docs/03-pages/01-building-your-application/03-data-fetching/index.mdx b/docs/03-pages/01-building-your-application/03-data-fetching/index.mdx new file mode 100644 index 0000000000000..91041ad6f9459 --- /dev/null +++ b/docs/03-pages/01-building-your-application/03-data-fetching/index.mdx @@ -0,0 +1,26 @@ +--- +title: Data Fetching +description: Next.js allows you to fetch data in multiple ways, with pre-rendering, server-side rendering or static-site generation, and incremental static regeneration. Learn how to manage your application data in Next.js. +--- + +Data fetching in Next.js allows you to render your content in different ways, depending on your application's use case. These include pre-rendering with **Server-side Rendering** or **Static Generation**, and updating or creating content at runtime with **Incremental Static Regeneration**. + +## Examples + +- [WordPress Example](https://github.com/vercel/next.js/tree/canary/examples/cms-wordpress)([Demo](https://next-blog-wordpress.vercel.app)) +- [Blog Starter using markdown files](https://github.com/vercel/next.js/tree/canary/examples/blog-starter) ([Demo](https://next-blog-starter.vercel.app/)) +- [DatoCMS Example](https://github.com/vercel/next.js/tree/canary/examples/cms-datocms) ([Demo](https://next-blog-datocms.vercel.app/)) +- [TakeShape Example](https://github.com/vercel/next.js/tree/canary/examples/cms-takeshape) ([Demo](https://next-blog-takeshape.vercel.app/)) +- [Sanity Example](https://github.com/vercel/next.js/tree/canary/examples/cms-sanity) ([Demo](https://next-blog-sanity.vercel.app/)) +- [Prismic Example](https://github.com/vercel/next.js/tree/canary/examples/cms-prismic) ([Demo](https://next-blog-prismic.vercel.app/)) +- [Contentful Example](https://github.com/vercel/next.js/tree/canary/examples/cms-contentful) ([Demo](https://next-blog-contentful.vercel.app/)) +- [Strapi Example](https://github.com/vercel/next.js/tree/canary/examples/cms-strapi) ([Demo](https://next-blog-strapi.vercel.app/)) +- [Prepr Example](https://github.com/vercel/next.js/tree/canary/examples/cms-prepr) ([Demo](https://next-blog-prepr.vercel.app/)) +- [Agility CMS Example](https://github.com/vercel/next.js/tree/canary/examples/cms-agilitycms) ([Demo](https://next-blog-agilitycms.vercel.app/)) +- [Cosmic Example](https://github.com/vercel/next.js/tree/canary/examples/cms-cosmic) ([Demo](https://next-blog-cosmic.vercel.app/)) +- [ButterCMS Example](https://github.com/vercel/next.js/tree/canary/examples/cms-buttercms) ([Demo](https://next-blog-buttercms.vercel.app/)) +- [Storyblok Example](https://github.com/vercel/next.js/tree/canary/examples/cms-storyblok) ([Demo](https://next-blog-storyblok.vercel.app/)) +- [GraphCMS Example](https://github.com/vercel/next.js/tree/canary/examples/cms-graphcms) ([Demo](https://next-blog-graphcms.vercel.app/)) +- [Kontent Example](https://github.com/vercel/next.js/tree/canary/examples/cms-kontent-ai) ([Demo](https://next-blog-kontent.vercel.app/)) +- [Static Tweet Demo](https://static-tweet.vercel.app/) +- [Enterspeed Example](https://github.com/vercel/next.js/tree/canary/examples/cms-enterspeed) ([Demo](https://next-blog-demo.enterspeed.com/)) diff --git a/docs/03-pages/01-building-your-application/04-styling/01-css-modules.mdx b/docs/03-pages/01-building-your-application/04-styling/01-css-modules.mdx new file mode 100644 index 0000000000000..d2f7302987b1a --- /dev/null +++ b/docs/03-pages/01-building-your-application/04-styling/01-css-modules.mdx @@ -0,0 +1,7 @@ +--- +title: CSS Modules +description: Style your Next.js Application using CSS Modules. +source: app/building-your-application/styling/css-modules +--- + +{/* DO NOT EDIT. The content of this doc is generated from the source above. To edit the content of this page, navigate to the source page in your editor. You can use the `Content` component to add content that is specific to the Pages Router. Any shared content should not be wrapped in a component. */} diff --git a/docs/03-pages/01-building-your-application/04-styling/02-tailwind-css.mdx b/docs/03-pages/01-building-your-application/04-styling/02-tailwind-css.mdx new file mode 100644 index 0000000000000..770fe95f6201d --- /dev/null +++ b/docs/03-pages/01-building-your-application/04-styling/02-tailwind-css.mdx @@ -0,0 +1,7 @@ +--- +title: Tailwind CSS +description: Style your Next.js Application using Tailwind CSS. +source: app/building-your-application/styling/tailwind-css +--- + +{/* DO NOT EDIT. The content of this doc is generated from the source above. To edit the content of this page, navigate to the source page in your editor. You can use the `Content` component to add content that is specific to the Pages Router. Any shared content should not be wrapped in a component. */} diff --git a/docs/03-pages/01-building-your-application/04-styling/03-css-in-js.mdx b/docs/03-pages/01-building-your-application/04-styling/03-css-in-js.mdx new file mode 100644 index 0000000000000..ab065b6569338 --- /dev/null +++ b/docs/03-pages/01-building-your-application/04-styling/03-css-in-js.mdx @@ -0,0 +1,7 @@ +--- +title: CSS-in-JS +description: Use CSS-in-JS libraries with Next.js +source: app/building-your-application/styling/css-in-js +--- + +{/* DO NOT EDIT. The content of this doc is generated from the source above. To edit the content of this page, navigate to the source page in your editor. You can use the `Content` component to add content that is specific to the Pages Router. Any shared content should not be wrapped in a component. */} diff --git a/docs/03-pages/01-building-your-application/04-styling/04-sass.mdx b/docs/03-pages/01-building-your-application/04-styling/04-sass.mdx new file mode 100644 index 0000000000000..6735fd06b619d --- /dev/null +++ b/docs/03-pages/01-building-your-application/04-styling/04-sass.mdx @@ -0,0 +1,7 @@ +--- +title: Sass +description: Learn how to use Sass in your Next.js application. +source: app/building-your-application/styling/sass +--- + +{/* DO NOT EDIT. The content of this doc is generated from the source above. To edit the content of this page, navigate to the source page in your editor. You can use the `Content` component to add content that is specific to the Pages Router. Any shared content should not be wrapped in a component. */} diff --git a/docs/03-pages/01-building-your-application/04-styling/index.mdx b/docs/03-pages/01-building-your-application/04-styling/index.mdx new file mode 100644 index 0000000000000..e636afb81057c --- /dev/null +++ b/docs/03-pages/01-building-your-application/04-styling/index.mdx @@ -0,0 +1,7 @@ +--- +title: Styling +description: Learn the different ways you can style your Next.js application. +source: app/building-your-application/styling +--- + +{/* DO NOT EDIT. The content of this doc is generated from the source above. To edit the content of this page, navigate to the source page in your editor. You can use the `Content` component to add content that is specific to the Pages Router. Any shared content should not be wrapped in a component. */} diff --git a/docs/03-pages/01-building-your-application/05-optimizing/01-images.mdx b/docs/03-pages/01-building-your-application/05-optimizing/01-images.mdx new file mode 100644 index 0000000000000..13eedb0e5fcaa --- /dev/null +++ b/docs/03-pages/01-building-your-application/05-optimizing/01-images.mdx @@ -0,0 +1,8 @@ +--- +title: Image Optimization +nav_title: Images +description: Optimize your images with the built-in `next/image` component. +source: app/building-your-application/optimizing/images +--- + +{/* DO NOT EDIT. The content of this doc is generated from the source above. To edit the content of this page, navigate to the source page in your editor. You can use the `Content` component to add content that is specific to the Pages Router. Any shared content should not be wrapped in a component. */} diff --git a/docs/03-pages/01-building-your-application/05-optimizing/02-fonts.mdx b/docs/03-pages/01-building-your-application/05-optimizing/02-fonts.mdx new file mode 100644 index 0000000000000..6089cc11a4f46 --- /dev/null +++ b/docs/03-pages/01-building-your-application/05-optimizing/02-fonts.mdx @@ -0,0 +1,8 @@ +--- +title: Font Optimization +nav_title: Fonts +description: Optimize your application's web fonts with the built-in `next/font` loaders. +source: 'app/building-your-application/optimizing/fonts' +--- + +{/* DO NOT EDIT. The content of this doc is generated from the source above. To edit the content of this page, navigate to the source page in your editor. You can use the `Content` component to add content that is specific to the Pages Router. Any shared content should not be wrapped in a component. */} diff --git a/docs/03-pages/01-building-your-application/05-optimizing/03-scripts.mdx b/docs/03-pages/01-building-your-application/05-optimizing/03-scripts.mdx new file mode 100644 index 0000000000000..79068e14b6b40 --- /dev/null +++ b/docs/03-pages/01-building-your-application/05-optimizing/03-scripts.mdx @@ -0,0 +1,8 @@ +--- +title: Script Optimization +nav_title: Scripts +description: Optimize 3rd party scripts with the built-in Script component. +source: app/building-your-application/optimizing/scripts +--- + +{/* DO NOT EDIT. The content of this doc is generated from the source above. To edit the content of this page, navigate to the source page in your editor. You can use the `Content` component to add content that is specific to the Pages Router. Any shared content should not be wrapped in a component. */} diff --git a/docs/03-pages/01-building-your-application/05-optimizing/05-static-assets.mdx b/docs/03-pages/01-building-your-application/05-optimizing/05-static-assets.mdx new file mode 100644 index 0000000000000..afc6e9580c4d7 --- /dev/null +++ b/docs/03-pages/01-building-your-application/05-optimizing/05-static-assets.mdx @@ -0,0 +1,7 @@ +--- +title: Static Assets +description: Next.js allows you to serve static files, like images, in the public directory. You can learn how it works here. +source: app/building-your-application/optimizing/static-assets +--- + +{/* DO NOT EDIT. The content of this doc is generated from the source above. To edit the content of this page, navigate to the source page in your editor. You can use the `Content` component to add content that is specific to the Pages Router. Any shared content should not be wrapped in a component. */} diff --git a/docs/03-pages/01-building-your-application/05-optimizing/06-lazy-loading.mdx b/docs/03-pages/01-building-your-application/05-optimizing/06-lazy-loading.mdx new file mode 100644 index 0000000000000..7aa343a36dcc5 --- /dev/null +++ b/docs/03-pages/01-building-your-application/05-optimizing/06-lazy-loading.mdx @@ -0,0 +1,5 @@ +--- +title: Lazy Loading +description: Lazy load imported libraries and React Components to improve your application's loading performance. +source: app/building-your-application/optimizing/lazy-loading +--- diff --git a/docs/03-pages/01-building-your-application/05-optimizing/07-analytics.mdx b/docs/03-pages/01-building-your-application/05-optimizing/07-analytics.mdx new file mode 100644 index 0000000000000..5e08847c9e61b --- /dev/null +++ b/docs/03-pages/01-building-your-application/05-optimizing/07-analytics.mdx @@ -0,0 +1,7 @@ +--- +title: Analytics +description: Measure and track page performance using Next.js Speed Insights +source: app/building-your-application/optimizing/analytics +--- + +{/* DO NOT EDIT. The content of this doc is generated from the source above. To edit the content of this page, navigate to the source page in your editor. You can use the `Content` component to add content that is specific to the Pages Router. Any shared content should not be wrapped in a component. */} diff --git a/docs/03-pages/01-building-your-application/05-optimizing/08-open-telemetry.mdx b/docs/03-pages/01-building-your-application/05-optimizing/08-open-telemetry.mdx new file mode 100644 index 0000000000000..f7406580e27a3 --- /dev/null +++ b/docs/03-pages/01-building-your-application/05-optimizing/08-open-telemetry.mdx @@ -0,0 +1,7 @@ +--- +title: OpenTelemetry +description: Learn how to instrument your Next.js app with OpenTelemetry. +source: app/building-your-application/optimizing/open-telemetry +--- + +{/* DO NOT EDIT. The content of this doc is generated from the source above. To edit the content of this page, navigate to the source page in your editor. You can use the `Content` component to add content that is specific to the Pages Router. Any shared content should not be wrapped in a component. */} diff --git a/docs/03-pages/01-building-your-application/05-optimizing/09-instrumentation.mdx b/docs/03-pages/01-building-your-application/05-optimizing/09-instrumentation.mdx new file mode 100644 index 0000000000000..db7edca877cfe --- /dev/null +++ b/docs/03-pages/01-building-your-application/05-optimizing/09-instrumentation.mdx @@ -0,0 +1,7 @@ +--- +title: Instrumentation +description: Learn how to use instrumentation to run code at server startup in your Next.js app +source: app/building-your-application/optimizing/instrumentation +--- + +{/* DO NOT EDIT. The content of this doc is generated from the source above. To edit the content of this page, navigate to the source page in your editor. You can use the `Content` component to add content that is specific to the Pages Router. Any shared content should not be wrapped in a component. */} diff --git a/docs/03-pages/01-building-your-application/05-optimizing/10-testing.mdx b/docs/03-pages/01-building-your-application/05-optimizing/10-testing.mdx new file mode 100644 index 0000000000000..9b1a67a14282e --- /dev/null +++ b/docs/03-pages/01-building-your-application/05-optimizing/10-testing.mdx @@ -0,0 +1,530 @@ +--- +title: Testing +description: Learn how to set up Next.js with three commonly used testing tools — Cypress, Playwright, Jest, and React Testing Library. +--- + +
    + + Examples + + +- [Next.js with Cypress](https://github.com/vercel/next.js/tree/canary/examples/with-cypress) +- [Next.js with Playwright](https://github.com/vercel/next.js/tree/canary/examples/with-playwright) +- [Next.js with Jest and React Testing Library](https://github.com/vercel/next.js/tree/canary/examples/with-jest) +- [Next.js with Vitest](https://github.com/vercel/next.js/tree/canary/examples/with-vitest) + +
    + +Learn how to set up Next.js with commonly used testing tools: [Cypress](#cypress), [Playwright](#playwright), and [Jest with React Testing Library](#jest-and-react-testing-library). + +## Cypress + +Cypress is a test runner used for **End-to-End (E2E)** and **Component Testing**. + +### Quickstart + +You can use `create-next-app` with the [with-cypress example](https://github.com/vercel/next.js/tree/canary/examples/with-cypress) to quickly get started. + +```bash filename="Terminal" +npx create-next-app@latest --example with-cypress with-cypress-app +``` + +### Manual setup + +To get started with Cypress, install the `cypress` package: + +```bash filename="Terminal" +npm install --save-dev cypress +``` + +Add Cypress to the `package.json` scripts field: + +```json +"scripts": { + "dev": "next dev", + "build": "next build", + "start": "next start", + "cypress": "cypress open", +} +``` + +Run Cypress for the first time to generate examples that use their recommended folder structure: + +```bash filename="Terminal" +npm run cypress +``` + +You can look through the generated examples and the [Writing Your First Test](https://docs.cypress.io/guides/getting-started/writing-your-first-test) section of the Cypress Documentation to help you get familiar with Cypress. + +### Should I use E2E or Component Tests? + +The [Cypress docs contain a guide](https://docs.cypress.io/guides/core-concepts/testing-types) on the difference between these two types of tests and when it is appropriate to use each. + +### Creating your first Cypress E2E test + +Assuming the following two Next.js pages: + +```jsx filename="pages/index.js" +import Link from 'next/link' + +export default function Home() { + return ( + + ) +} +``` + +```jsx filename="pages/about.js" +export default function About() { + return ( +
    +

    About Page

    + Homepage +
    + ) +} +``` + +Add a test to check your navigation is working correctly: + +```jsx filename="cypress/e2e/app.cy.js" +describe('Navigation', () => { + it('should navigate to the about page', () => { + // Start from the index page + cy.visit('http://localhost:3000/') + + // Find a link with an href attribute containing "about" and click it + cy.get('a[href*="about"]').click() + + // The new url should include "/about" + cy.url().should('include', '/about') + + // The new page should contain an h1 with "About page" + cy.get('h1').contains('About Page') + }) +}) +``` + +You can use `cy.visit("/")` instead of `cy.visit("http://localhost:3000/")` if you add `baseUrl: 'http://localhost:3000'` to the `cypress.config.js` configuration file. + +### Creating your first Cypress component test + +Component tests build and mount a specific component without having to bundle your whole application or launch a server. This allows for more performant tests that still provide visual feedback and the same API used for Cypress E2E tests. + +> **Note**: Since component tests do not launch a Next.js server, capabilities like `` and `getServerSideProps` which rely on a server being available will not function out-of-the-box. See the [Cypress Next.js docs](https://docs.cypress.io/guides/component-testing/react/overview#Nextjs) for examples of getting these features working within component tests. + +Assuming the same components from the previous section, add a test to validate a component is rendering the expected output: + +```jsx filename="pages/about.cy.js" +import AboutPage from './about.js' + +describe('', () => { + it('should render and display expected content', () => { + // Mount the React component for the About page + cy.mount() + + // The new page should contain an h1 with "About page" + cy.get('h1').contains('About Page') + + // Validate that a link with the expected URL is present + // *Following* the link is better suited to an E2E test + cy.get('a[href="/"]').should('be.visible') + }) +}) +``` + +### Running your Cypress tests + +#### E2E Tests + +Since Cypress E2E tests are testing a real Next.js application they require the Next.js server to be running prior to starting Cypress. We recommend running your tests against your production code to more closely resemble how your application will behave. + +Run `npm run build` and `npm run start`, then run `npm run cypress -- --e2e` in another terminal window to start Cypress and run your E2E testing suite. + +> **Note**: Alternatively, you can install the `start-server-and-test` package and add it to the `package.json` scripts field: `"test": "start-server-and-test start http://localhost:3000 cypress"` to start the Next.js production server in conjunction with Cypress. Remember to rebuild your application after new changes. + +#### Component Tests + +Run `npm run cypress -- --component` to start Cypress and execute your component testing suite. + +### Getting ready for Continuous Integration (CI) + +You will have noticed that running Cypress so far has opened an interactive browser which is not ideal for CI environments. You can also run Cypress headlessly using the `cypress run` command: + +```json filename="package.json" + +"scripts": { + //... + "e2e": "start-server-and-test dev http://localhost:3000 \"cypress open --e2e\"", + "e2e:headless": "start-server-and-test dev http://localhost:3000 \"cypress run --e2e\"", + "component": "cypress open --component", + "component:headless": "cypress run --component" +} +``` + +You can learn more about Cypress and Continuous Integration from these resources: + +- [Cypress Continuous Integration Docs](https://docs.cypress.io/guides/continuous-integration/introduction) +- [Cypress GitHub Actions Guide](https://on.cypress.io/github-actions) +- [Official Cypress GitHub Action](https://github.com/cypress-io/github-action) + +## Playwright + +Playwright is a testing framework that lets you automate Chromium, Firefox, and WebKit with a single API. You can use it to write **End-to-End (E2E)** and **Integration** tests across all platforms. + +### Quickstart + +The fastest way to get started is to use `create-next-app` with the [with-playwright example](https://github.com/vercel/next.js/tree/canary/examples/with-playwright). This will create a Next.js project complete with Playwright all set up. + +```bash filename="Terminal" +npx create-next-app@latest --example with-playwright with-playwright-app +``` + +### Manual setup + +You can also use `npm init playwright` to add Playwright to an existing `NPM` project. + +To manually get started with Playwright, install the `@playwright/test` package: + +```bash filename="Terminal" +npm install --save-dev @playwright/test +``` + +Add Playwright to the `package.json` scripts field: + +```json +"scripts": { + "dev": "next dev", + "build": "next build", + "start": "next start", + "test:e2e": "playwright test", +} +``` + +### Creating your first Playwright end-to-end test + +Assuming the following two Next.js pages: + +```jsx filename="pages/index.js" +import Link from 'next/link' + +export default function Home() { + return ( + + ) +} +``` + +```jsx filename="pages/about.js" +export default function About() { + return ( +
    +

    About Page

    +
    + ) +} +``` + +Add a test to verify that your navigation is working correctly: + +```ts filename="e2e/example.spec.ts" switcher +import { test, expect } from '@playwright/test' + +test('should navigate to the about page', async ({ page }) => { + // Start from the index page (the baseURL is set via the webServer in the playwright.config.ts) + await page.goto('http://localhost:3000/') + // Find an element with the text 'About Page' and click on it + await page.click('text=About') + // The new URL should be "/about" (baseURL is used there) + await expect(page).toHaveURL('http://localhost:3000/about') + // The new page should contain an h1 with "About Page" + await expect(page.locator('h1')).toContainText('About Page') +}) +``` + +```js filename="e2e/example.spec.js" switcher +import { test, expect } from '@playwright/test' + +test('should navigate to the about page', async ({ page }) => { + // Start from the index page (the baseURL is set via the webServer in the playwright.config.ts) + await page.goto('http://localhost:3000/') + // Find an element with the text 'About Page' and click on it + await page.click('text=About') + // The new URL should be "/about" (baseURL is used there) + await expect(page).toHaveURL('http://localhost:3000/about') + // The new page should contain an h1 with "About Page" + await expect(page.locator('h1')).toContainText('About Page') +}) +``` + +You can use `page.goto("/")` instead of `page.goto("http://localhost:3000/")`, if you add [`"baseURL": "http://localhost:3000"`](https://playwright.dev/docs/api/class-testoptions#test-options-base-url) to the `playwright.config.ts` configuration file. + +### Running your Playwright tests + +Since Playwright is testing a real Next.js application, it requires the Next.js server to be running prior to starting Playwright. It is recommended to run your tests against your production code to more closely resemble how your application will behave. + +Run `npm run build` and `npm run start`, then run `npm run test:e2e` in another terminal window to run the Playwright tests. + +> **Note**: Alternatively, you can use the [`webServer`](https://playwright.dev/docs/test-webserver/) feature to let Playwright start the development server and wait until it's fully available. + +### Running Playwright on Continuous Integration (CI) + +Playwright will by default run your tests in the [headless mode](https://playwright.dev/docs/ci#running-headed). To install all the Playwright dependencies, run `npx playwright install-deps`. + +You can learn more about Playwright and Continuous Integration from these resources: + +- [Getting started with Playwright](https://playwright.dev/docs/intro) +- [Use a development server](https://playwright.dev/docs/test-webserver/) +- [Playwright on your CI provider](https://playwright.dev/docs/ci) + +## Jest and React Testing Library + +Jest and React Testing Library are frequently used together for **Unit Testing**. There are three ways you can start using Jest within your Next.js application: + +1. Using one of our [quickstart examples](#quickstart-2) +2. With the [Next.js Rust Compiler](#setting-up-jest-with-the-rust-compiler) +3. With [Babel](#setting-up-jest-with-babel) + +The following sections will go through how you can set up Jest with each of these options: + +### Quickstart + +You can use `create-next-app` with the [with-jest](https://github.com/vercel/next.js/tree/canary/examples/with-jest) example to quickly get started with Jest and React Testing Library: + +```bash filename="Terminal" +npx create-next-app@latest --example with-jest with-jest-app +``` + +### Setting up Jest (with the Rust Compiler) + +Since the release of [Next.js 12](https://nextjs.org/blog/next-12), Next.js now has built-in configuration for Jest. + +To set up Jest, install `jest`, `jest-environment-jsdom`, `@testing-library/react`, `@testing-library/jest-dom`: + +```bash filename="Terminal" +npm install --save-dev jest jest-environment-jsdom @testing-library/react @testing-library/jest-dom +``` + +Create a `jest.config.mjs` file in your project's root directory and add the following: + +```jsx filename="jest.config.mjs" +import nextJest from 'next/jest.js' + +const createJestConfig = nextJest({ + // Provide the path to your Next.js app to load next.config.js and .env files in your test environment + dir: './', +}) + +// Add any custom config to be passed to Jest +/** @type {import('jest').Config} */ +const config = { + // Add more setup options before each test is run + // setupFilesAfterEnv: ['/jest.setup.js'], + + testEnvironment: 'jest-environment-jsdom', +} + +// createJestConfig is exported this way to ensure that next/jest can load the Next.js config which is async +export default createJestConfig(config) +``` + +Under the hood, `next/jest` is automatically configuring Jest for you, including: + +- Setting up `transform` using [SWC](/docs/architecture/nextjs-compiler) +- Auto mocking stylesheets (`.css`, `.module.css`, and their scss variants), image imports and [`next/font`](/docs/pages/building-your-application/optimizing/fonts) +- Loading `.env` (and all variants) into `process.env` +- Ignoring `node_modules` from test resolving and transforms +- Ignoring `.next` from test resolving +- Loading `next.config.js` for flags that enable SWC transforms + +> **Note**: To test environment variables directly, load them manually in a separate setup script or in your `jest.config.js` file. For more information, please see [Test Environment Variables](/docs/pages/building-your-application/configuring/environment-variables#test-environment-variables). + +### Setting up Jest (with Babel) + +If you opt out of the [Rust Compiler](/docs/architecture/nextjs-compiler), you will need to manually configure Jest and install `babel-jest` and `identity-obj-proxy` in addition to the packages above. + +Here are the recommended options to configure Jest for Next.js: + +```jsx filename="jest.config.js" +module.exports = { + collectCoverage: true, + // on node 14.x coverage provider v8 offers good speed and more or less good report + coverageProvider: 'v8', + collectCoverageFrom: [ + '**/*.{js,jsx,ts,tsx}', + '!**/*.d.ts', + '!**/node_modules/**', + '!/out/**', + '!/.next/**', + '!/*.config.js', + '!/coverage/**', + ], + moduleNameMapper: { + // Handle CSS imports (with CSS modules) + // https://jestjs.io/docs/webpack#mocking-css-modules + '^.+\\.module\\.(css|sass|scss)$': 'identity-obj-proxy', + + // Handle CSS imports (without CSS modules) + '^.+\\.(css|sass|scss)$': '/__mocks__/styleMock.js', + + // Handle image imports + // https://jestjs.io/docs/webpack#handling-static-assets + '^.+\\.(png|jpg|jpeg|gif|webp|avif|ico|bmp|svg)$/i': `/__mocks__/fileMock.js`, + + // Handle module aliases + '^@/components/(.*)$': '/components/$1', + }, + // Add more setup options before each test is run + // setupFilesAfterEnv: ['/jest.setup.js'], + testPathIgnorePatterns: ['/node_modules/', '/.next/'], + testEnvironment: 'jsdom', + transform: { + // Use babel-jest to transpile tests with the next/babel preset + // https://jestjs.io/docs/configuration#transform-objectstring-pathtotransformer--pathtotransformer-object + '^.+\\.(js|jsx|ts|tsx)$': ['babel-jest', { presets: ['next/babel'] }], + }, + transformIgnorePatterns: [ + '/node_modules/', + '^.+\\.module\\.(css|sass|scss)$', + ], +} +``` + +You can learn more about each configuration option in the [Jest docs](https://jestjs.io/docs/configuration). + +**Handling stylesheets and image imports** + +Stylesheets and images aren't used in the tests but importing them may cause errors, so they will need to be mocked. Create the mock files referenced in the configuration above - `fileMock.js` and `styleMock.js` - inside a `__mocks__` directory: + +```js filename="__mocks__/fileMock.js" +module.exports = { + src: '/img.jpg', + height: 24, + width: 24, + blurDataURL: 'data:image/png;base64,imagedata', +} +``` + +```js filename="__mocks__/styleMock.js" +module.exports = {} +``` + +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** + +`@testing-library/jest-dom` includes a set of convenient [custom matchers](https://github.com/testing-library/jest-dom#custom-matchers) such as `.toBeInTheDocument()` making it easier to write tests. You can import the custom matchers for every test by adding the following option to the Jest configuration file: + +```js filename="jest.config.js" +setupFilesAfterEnv: ['/jest.setup.js'] +``` + +Then, inside `jest.setup.js`, add the following import: + +```jsx filename="jest.setup.js" +import '@testing-library/jest-dom/extend-expect' +``` + +If you need to add more setup options before each test, it's common to add them to the `jest.setup.js` file above. + +**Optional: Absolute Imports and Module Path Aliases** + +If your project is using [Module Path Aliases](/docs/pages/building-your-application/configuring/absolute-imports-and-module-aliases), you will need to configure Jest to resolve the imports by matching the paths option in the `jsconfig.json` file with the `moduleNameMapper` option in the `jest.config.js` file. For example: + +```json filename="tsconfig.json" or jsconfig.json +{ + "compilerOptions": { + "baseUrl": ".", + "paths": { + "@/components/*": ["components/*"] + } + } +} +``` + +```jsx filename="jest.config.js" +moduleNameMapper: { + '^@/components/(.*)$': '/components/$1', +} +``` + +### Creating your tests: + +**Add a test script to package.json** + +Add the Jest executable in watch mode to the `package.json` scripts: + +```jsx +"scripts": { + "dev": "next dev", + "build": "next build", + "start": "next start", + "test": "jest --watch" +} +``` + +`jest --watch` will re-run tests when a file is changed. For more Jest CLI options, please refer to the [Jest Docs](https://jestjs.io/docs/cli#reference). + +**Create your first tests** + +Your project is now ready to run tests. Follow Jest's convention by adding tests to the `__tests__` folder in your project's root directory. + +For example, we can add a test to check if the `` component successfully renders a heading: + +```jsx filename="__tests__/index.test.jsx" +import { render, screen } from '@testing-library/react' +import Home from '../pages/index' +import '@testing-library/jest-dom' + +describe('Home', () => { + it('renders a heading', () => { + render() + + const heading = screen.getByRole('heading', { + name: /welcome to next\.js!/i, + }) + + expect(heading).toBeInTheDocument() + }) +}) +``` + +Optionally, add a [snapshot test](https://jestjs.io/docs/snapshot-testing) to keep track of any unexpected changes to your `` component: + +```jsx filename="__tests__/snapshot.js" +import { render } from '@testing-library/react' +import Home from '../pages/index' + +it('renders homepage unchanged', () => { + const { container } = render() + expect(container).toMatchSnapshot() +}) +``` + +> **Note**: Test files should not be included inside the Pages Router because any files inside the Pages Router are considered routes. + +**Running your test suite** + +Run `npm run test` to run your test suite. After your tests pass or fail, you will notice a list of interactive Jest commands that will be helpful as you add more tests. + +For further reading, you may find these resources helpful: + +- [Jest Docs](https://jestjs.io/docs/getting-started) +- [React Testing Library Docs](https://testing-library.com/docs/react-testing-library/intro/) +- [Testing Playground](https://testing-playground.com/) - use good testing practices to match elements. + +## Community Packages and Examples + +The Next.js community has created packages and articles you may find helpful: + +- [next-router-mock](https://github.com/scottrippey/next-router-mock) for Storybook. +- [Test Preview Vercel Deploys with Cypress](https://glebbahmutov.com/blog/develop-preview-test/) by Gleb Bahmutov. + +For more information on what to read next, we recommend: + + - pages/basic-features/environment-variables#test-environment-variables diff --git a/docs/03-pages/01-building-your-application/05-optimizing/index.mdx b/docs/03-pages/01-building-your-application/05-optimizing/index.mdx new file mode 100644 index 0000000000000..48d0e0e3f2130 --- /dev/null +++ b/docs/03-pages/01-building-your-application/05-optimizing/index.mdx @@ -0,0 +1,8 @@ +--- +title: Optimizations +nav_title: Optimizing +description: Optimize your Next.js application for best performance and user experience. +source: app/building-your-application/optimizing +--- + +{/* DO NOT EDIT. The content of this doc is generated from the source above. To edit the content of this page, navigate to the source page in your editor. You can use the `Content` component to add content that is specific to the Pages Router. Any shared content should not be wrapped in a component. */} diff --git a/docs/03-pages/01-building-your-application/06-configuring/01-typescript.mdx b/docs/03-pages/01-building-your-application/06-configuring/01-typescript.mdx new file mode 100644 index 0000000000000..2309f3cf22cc9 --- /dev/null +++ b/docs/03-pages/01-building-your-application/06-configuring/01-typescript.mdx @@ -0,0 +1,7 @@ +--- +title: TypeScript +description: Next.js provides a TypeScript-first development experience for building your React application. +source: app/building-your-application/configuring/typescript +--- + +{/* DO NOT EDIT. The content of this doc is generated from the source above. To edit the content of this page, navigate to the source page in your editor. You can use the `Content` component to add content that is specific to the Pages Router. Any shared content should not be wrapped in a component. */} diff --git a/docs/03-pages/01-building-your-application/06-configuring/02-eslint.mdx b/docs/03-pages/01-building-your-application/06-configuring/02-eslint.mdx new file mode 100644 index 0000000000000..13640fc39e303 --- /dev/null +++ b/docs/03-pages/01-building-your-application/06-configuring/02-eslint.mdx @@ -0,0 +1,7 @@ +--- +title: ESLint +description: Next.js reports ESLint errors and warnings during builds by default. Learn how to opt-out of this behavior here. +source: app/building-your-application/configuring/eslint +--- + +{/* DO NOT EDIT. The content of this doc is generated from the source above. To edit the content of this page, navigate to the source page in your editor. You can use the `Content` component to add content that is specific to the Pages Router. Any shared content should not be wrapped in a component. */} diff --git a/docs/03-pages/01-building-your-application/06-configuring/03-environment-variables.mdx b/docs/03-pages/01-building-your-application/06-configuring/03-environment-variables.mdx new file mode 100644 index 0000000000000..c5644c60cfc70 --- /dev/null +++ b/docs/03-pages/01-building-your-application/06-configuring/03-environment-variables.mdx @@ -0,0 +1,7 @@ +--- +title: Environment Variables +description: Learn to add and access environment variables in your Next.js application. +source: app/building-your-application/configuring/environment-variables +--- + +{/* DO NOT EDIT. The content of this doc is generated from the source above. To edit the content of this page, navigate to the source page in your editor. You can use the `Content` component to add content that is specific to the Pages Router. Any shared content should not be wrapped in a component. */} diff --git a/docs/03-pages/01-building-your-application/06-configuring/04-absolute-imports-and-module-aliases.mdx b/docs/03-pages/01-building-your-application/06-configuring/04-absolute-imports-and-module-aliases.mdx new file mode 100644 index 0000000000000..2732297c6b481 --- /dev/null +++ b/docs/03-pages/01-building-your-application/06-configuring/04-absolute-imports-and-module-aliases.mdx @@ -0,0 +1,7 @@ +--- +title: Absolute Imports and Module Path Aliases +description: Configure module path aliases that allow you to remap certain import paths. +source: app/building-your-application/configuring/absolute-imports-and-module-aliases +--- + +{/* DO NOT EDIT. The content of this doc is generated from the source above. To edit the content of this page, navigate to the source page in your editor. You can use the `Content` component to add content that is specific to the Pages Router. Any shared content should not be wrapped in a component. */} diff --git a/docs/03-pages/01-building-your-application/06-configuring/05-src-directory.mdx b/docs/03-pages/01-building-your-application/06-configuring/05-src-directory.mdx new file mode 100644 index 0000000000000..2312ab75e9e48 --- /dev/null +++ b/docs/03-pages/01-building-your-application/06-configuring/05-src-directory.mdx @@ -0,0 +1,7 @@ +--- +title: src Directory +description: Save pages under the `src` directory as an alternative to the root `pages` directory. +source: app/building-your-application/configuring/src-directory +--- + +{/* DO NOT EDIT. The content of this doc is generated from the source above. To edit the content of this page, navigate to the source page in your editor. You can use the `Content` component to add content that is specific to the Pages Router. Any shared content should not be wrapped in a component. */} diff --git a/docs/03-pages/01-building-your-application/06-configuring/06-mdx.mdx b/docs/03-pages/01-building-your-application/06-configuring/06-mdx.mdx new file mode 100644 index 0000000000000..ef85277dc4fa4 --- /dev/null +++ b/docs/03-pages/01-building-your-application/06-configuring/06-mdx.mdx @@ -0,0 +1,7 @@ +--- +title: MDX +description: Learn how to configure MDX to write JSX in your markdown files. +source: app/building-your-application/configuring/mdx +--- + +{/* DO NOT EDIT. The content of this doc is generated from the source above. To edit the content of this page, navigate to the source page in your editor. You can use the `Content` component to add content that is specific to the Pages Router. Any shared content should not be wrapped in a component. */} diff --git a/docs/03-pages/01-building-your-application/06-configuring/07-amp.mdx b/docs/03-pages/01-building-your-application/06-configuring/07-amp.mdx new file mode 100644 index 0000000000000..9da9b6ed1700f --- /dev/null +++ b/docs/03-pages/01-building-your-application/06-configuring/07-amp.mdx @@ -0,0 +1,156 @@ +--- +title: AMP +description: With minimal config, and without leaving React, you can start adding AMP and improve the performance and speed of your pages. +--- + +
    + + Examples + + - [AMP](https://github.com/vercel/next.js/tree/canary/examples/amp) +
    + +With Next.js you can turn any React page into an AMP page, with minimal config, and without leaving React. + +You can read more about AMP in the official [amp.dev](https://amp.dev/) site. + +## Enabling AMP + +To enable AMP support for a page, and to learn more about the different AMP configs, read the [API documentation for `next/amp`](/docs/pages/building-your-application/configuring/amp). + +## Caveats + +- Only CSS-in-JS is supported. [CSS Modules](/docs/pages/building-your-application/styling) aren't supported by AMP pages at the moment. You can [contribute CSS Modules support to Next.js](https://github.com/vercel/next.js/issues/10549). + +## Adding AMP Components + +The AMP community provides [many components](https://amp.dev/documentation/components/) to make AMP pages more interactive. Next.js will automatically import all components used on a page and there is no need to manually import AMP component scripts: + +```jsx +export const config = { amp: true } + +function MyAmpPage() { + const date = new Date() + + return ( +
    +

    Some time: {date.toJSON()}

    + + . + +
    + ) +} + +export default MyAmpPage +``` + +The above example uses the [`amp-timeago`](https://amp.dev/documentation/components/amp-timeago/?format=websites) component. + +By default, the latest version of a component is always imported. If you want to customize the version, you can use `next/head`, as in the following example: + +```jsx +import Head from 'next/head' + +export const config = { amp: true } + +function MyAmpPage() { + const date = new Date() + + return ( +
    + + -``` - -Or by using the `dangerouslySetInnerHTML` property: - -```jsx - -``` - -The HTML [script](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script) tag is used to embed any client-side JavaScript. It can either contain inline scripting statements (as shown in the example above) or point to an external script file via the `src` attribute. -This example validates the name and roll number of a user. The `validateFormWithJS()` function does not allow an empty name field, and the roll number must be at least three digits long. The validation is performed when you hit the Submit button. You are not redirected to the next page until the given values are correct. - -![js-validation](https://assets.vercel.com/image/upload/dpr_auto,q_auto,f_auto/nextjs/guides/building-forms/js-validation.jpg) - -#### Form Validation Using Regular Expressions - -JavaScript validation with Regular Expressions uses the `pattern` HTML attribute. A regular expression (commonly known as RegEx) is an object that describes a pattern of characters. You can only apply the `pattern` attribute to the `` element. This way, you can validate the input value using Regular Expressions (RegEx) by defining your own rules. Once again, if the value does not match the defined pattern, the input will give an error. -The below example shows using the `pattern` attribute on an `input` element: - -```html - - - - - - -``` - -The password form field must only contain digits (0 to 9), lowercase alphabets (a to z) and it must be no more than 15 characters in length. No other characters (#,$,&, etc.) are allowed. The rule in RegEx is written as `[a-z0-9]{1,15}`. - -![form-validate-regex](https://assets.vercel.com/image/upload/dpr_auto,q_auto,f_auto/nextjs/guides/building-forms/form-validate-regex.jpg) - -> To learn more about HTML forms, check out the [MDN Web Docs](https://developer.mozilla.org/en-US/docs/Learn/Forms). - -## Part 2: Project Setup - -In the following section you will be creating forms in React using Next.js. - -Create a new Next.js app. You can use the [create-next-app](https://nextjs.org/docs/getting-started#setup) for a quick start. In your command line terminal, run the following: - -``` -npx create-next-app -``` - -Answer the questions to create your project, and give it a name, this example uses [`next-forms`](https://github.com/vercel/next.js/tree/canary/examples/next-forms). Next `cd` into this directory, and run `npm run dev` or `yarn dev` command to start the development server. - -Open the URL printed in the terminal to ensure that your app is running successfully. - -## Part 3: Setting up a Next.js Form API Route - -Both the client and the server will be built using Next.js. For the server part, create an API endpoint where you will send the form data. - -Next.js offers a file-based system for routing that's built on the [concept of pages](/docs/basic-features/pages). Any file inside the folder `pages/api` is mapped to `/api/*` and will be treated as an API endpoint instead of a page. This [API endpoint](/docs/api-routes/introduction) is going to be server-side only. - -Go to `pages/api`, create a file called `form.js` and paste this code written in Node.js: - -```js -export default function handler(req, res) { - // Get data submitted in request's body. - const body = req.body - - // Optional logging to see the responses - // in the command line where next.js app is running. - console.log('body: ', body) - - // Guard clause checks for first and last name, - // and returns early if they are not found - if (!body.first || !body.last) { - // Sends a HTTP bad request error code - return res.status(400).json({ data: 'First or last name not found' }) - } - - // Found the name. - // Sends a HTTP success code - res.status(200).json({ data: `${body.first} ${body.last}` }) -} -``` - -This form `handler` function will receive the request `req` from the client (i.e. submitted form data). And in return, it'll send a response `res` as JSON that will have both the first and the last name. You can access this API endpoint at `http://localhost:3000/api/form` or replace the localhost URL with an actual Vercel deployment when you deploy. - -> Moreover, you can also attach this API to a database like MongoDB or Google Sheets. This way, your submitted form data will be securely stored for later use. For this guide, no database is used. Instead, the same data is returned to the user to demo how it's done. - -### Form Submission without JavaScript - -You can now use `/api/form` relative endpoint inside the `action` attribute of the form. You are sending form data to the server when the form is submitted via `POST` HTTP method (which is used to send data). - -```html -
    - - - - - -
    -``` - -If you submit this form, it will submit the data to the forms API endpoint `/api/form`. The server then responds, generally handling the data and loading the URL defined by the action attribute, causing a new page load. So in this case you'll be redirected to `http://localhost:3000/api/form` with the following response from the server. - -![form-no-js](https://assets.vercel.com/image/upload/dpr_auto,q_auto,f_auto/nextjs/guides/building-forms/form-no-js.jpg) - -## Part 4: Configuring Forms in Next.js - -You have created a Next.js API Route for form submission. Now it's time to configure the client (the form itself) inside Next.js using React. The first step will be extending your knowledge of HTML forms and converting it into React (using [JSX](https://reactjs.org/docs/introducing-jsx.html)). - -Here's the same form in a [React function component](https://reactjs.org/docs/components-and-props.html) written using [JSX](https://reactjs.org/docs/introducing-jsx.html). - -```js -export default function Form() { - return ( -
    - - - - - - - -
    - ) -} -``` - -Here's what changed: - -- The `for` attribute is changed to `htmlFor`. (Since `for` is a keyword associated with the "for" loop in JavaScript, React elements use `htmlFor` instead.) -- The `action` attribute now has a relative URL which is the form API endpoint. - -This completes the basic structure of your Next.js-based form. - -> You can view the entire source code of [next-forms](https://github.com/vercel/next.js/tree/canary/examples/next-forms) example repo that we're creating here as a working example. Feel free to clone it and start right away. This demo is built with create-next-app, and you can preview the basic form CSS styles inside `/styles/global.css` file. - -![forms with nextjs](https://assets.vercel.com/image/upload/dpr_auto,q_auto,f_auto/nextjs/guides/building-forms/forms-with-nextjs.png) - -## Part 5: Form Submission without JavaScript - -JavaScript brings interactivity to our web applications, but sometimes you need to control the JavaScript bundle from being too large, or your sites visitors might have JavaScript disabled. - -There are several reasons why users disable JavaScript: - -- Addressing bandwidth constraints -- Increasing device (phone or laptop) battery life -- For privacy so they won’t be tracked with analytical scripts - -Regardless of the reason, disabling JavaScript will impact site functionality partially, if not completely. - -Next open the `next-forms` directory. Inside the `/pages` directory, create a file `no-js-form.js`. - -> **Quick Tip**: In Next.js, a page is a React Component exported from a `.js`, `.jsx`, `.ts`, or `.tsx` file in the pages directory. Each page is associated with a route based on its file name. -> -> Example: If you create `pages/no-js-form.js`, it will be accessible at `your-domain.tld/no-js-form`. - -Let's use the same code from above: - -```js -export default function PageWithoutJSbasedForm() { - return ( -
    - - - - - - - -
    - ) -} -``` - -With JavaScript disabled, when you hit the Submit button, an event is triggered, which collects the form data and sends it to our forms API endpoint as defined in the `action` attribute and using `POST` HTTP `method`. You'll be redirected to the `/api/form` endpoint since that's how form `action` works. - -The form data will be submitted on the server as a request `req` to the form handler function written above. It will process the data and return a JSON string as a response `res` with your submitted name included. - -> To improve the experience here, as a response you can redirect the user to a page and thank them for submitting the form. - -## Part 6: Form Submission with JavaScript Enabled - -Inside `/pages`, you'll create another file called `js-form.js`. This will create a `/js-form` page on your Next.js app. - -Now, as soon as the form is submitted, we prevent the form's default behavior of reloading the page. We'll take the form data, convert it to JSON string, and send it to our server, the API endpoint. Finally, our server will respond with the name submitted. All of this with a basic JavaScript `handleSubmit()` function. - -Here's what this function looks like. It's well documented for you to understand each step: - -```js -export default function PageWithJSbasedForm() { - // Handles the submit event on form submit. - const handleSubmit = async (event) => { - // Stop the form from submitting and refreshing the page. - event.preventDefault() - - // Get data from the form. - const data = { - first: event.target.first.value, - last: event.target.last.value, - } - - // Send the data to the server in JSON format. - const JSONdata = JSON.stringify(data) - - // API endpoint where we send form data. - const endpoint = '/api/form' - - // Form the request for sending data to the server. - const options = { - // The method is POST because we are sending data. - method: 'POST', - // Tell the server we're sending JSON. - headers: { - 'Content-Type': 'application/json', - }, - // Body of the request is the JSON data we created above. - body: JSONdata, - } - - // Send the form data to our forms API on Vercel and get a response. - const response = await fetch(endpoint, options) - - // Get the response data from server as JSON. - // If server returns the name submitted, that means the form works. - const result = await response.json() - alert(`Is this your full name: ${result.data}`) - } - return ( - // We pass the event to the handleSubmit() function on submit. -
    - - - - - - - -
    - ) -} -``` - -It's a Next.js page with a React function component called `PageWithJSbasedForm` with a `
    ` element written in JSX. There's no action on the `` element. Instead, we use the `onSubmit` event handler to send data to our `{handleSubmit}` function. - -The `handleSubmit()` function processes your form data through a series of steps: - -- The `event.preventDefault()` stops the `` element from refreshing the entire page. -- We created a JavaScript object called `data` with the `first` and `last` values from the form. -- JSON is a language-agnostic data transfer format. So we use `JSON.stringify(data)` to convert the data to JSON. -- We then use `fetch()` to send the data to our `/api/form` endpoint using JSON and HTTP `POST` method. -- Server sends back a response with the name submitted. Woohoo! 🥳 - -## Conclusion - -This guide has covered the following: - -- The basic HTML `form` element -- Understanding forms with React.js -- Validating forms data with and without JavaScript -- Using Next.js API Routes to handle `req` and `res` from the client and server - -For more details go through [Next.js Learn Course](https://nextjs.org/learn/basics/create-nextjs-app). diff --git a/docs/index.mdx b/docs/index.mdx new file mode 100644 index 0000000000000..000f47beb6bbe --- /dev/null +++ b/docs/index.mdx @@ -0,0 +1,57 @@ +--- +title: Introduction +description: Welcome to the Next.js Documentation. +--- + +Welcome to the Next.js documentation! + +## What is Next.js? + +Next.js is a framework for building web applications. + +With Next.js, you can build user interfaces using React components. Then, Next.js provides additional structure, features, and optimizations for your application. + +Under the hood, Next.js also abstracts and automatically configures tooling for you, like bundling, compiling, and more. This allows you to focus on building your application instead of spending time setting up tooling. + +Whether you're an individual developer or part of a larger team, Next.js can help you build interactive, dynamic, and fast web applications. + +## Main Features + +Some of the main Next.js features include: + +| Feature | Description | +| ------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| [Routing](/docs/app/building-your-application/routing) | A file-system based router built on top of Server Components that supports layouts, nested routing, loading states, error handling, and more. | +| [Rendering](/docs/app/building-your-application/rendering) | Client-side and Server-side Rendering with Client and Server Components. Further optimized with Static and Dynamic Rendering on the server with Next.js. Streaming on Edge and Node.js runtimes. | +| [Data Fetching](/docs/app/building-your-application/data-fetching) | Simplified data fetching with async/await support in React Components and the `fetch()`s API that aligns with React and the Web Platform. | +| [Styling](/docs/app/building-your-application/styling) | Support for your preferred styling methods, including CSS Modules, Tailwind CSS, and CSS-in-JS | +| [Optimizations](/docs/app/building-your-application/optimizing) | Image, Fonts, and Script Optimizations to improve your application's Core Web Vitals and User Experience. | +| [Typescript](/docs/app/building-your-application/configuring/typescript) | Improved support for TypeScript, with better type checking and more efficient compilation, as well as custom TypeScript Plugin and type checker. | +| [API Reference](/docs/app/api-reference) | Updates to the API design throughout Next.js. Please refer to the API Reference Section for new APIs. | + +## How to Use These Docs + +The sections and pages of the docs are organized sequentially, from basic to advanced, so you can follow them step-by-step when building your Next.js application. However, you can read them in any order or skip to the pages that apply to your use case. + +At the top of the sidebar, you'll notice a dropdown menu that allows you to switch between the **App Router** the **Pages Router** features. Since there are features that are unique to each directory, it's important to keep track of which tab is selected. + +On the right side of the page, you'll see a table of contents that makes it easier to navigate between sections of a page. The breadcrumbs at the top of the page will also indicate whether you're viewing App Router docs or Pages Router docs. + +To get started, checkout the [Installation](/docs/getting-started/installation). If you're new to React or Server Components, we recommend reading the [React Essentials](/docs/getting-started/react-essentials) page. + +## Pre-Requisite Knowledge + +Although our docs are designed to be beginner-friendly, we need to establish a baseline so that the docs can stay focused on Next.js functionality. We'll make sure to provide links to relevant documentation whenever we introduce a new concept. + +To get the most out of our docs, it's recommended that you have a basic understanding of HTML, CSS, and React. If you need to brush up on your React skills, check out these resources: + +- [React: Official React Documentation](https://react.dev/learn) +- [React Essentials](/docs/getting-started/react-essentials) + +## Accessibility + +For optimal accessibility when using a screen reader while reading the docs, we recommend using Firefox and NVDA, or Safari and VoiceOver. + +## Join our Community + +If you have questions about anything related to Next.js, you're always welcome to ask our community on [GitHub Discussions](https://github.com/vercel/next.js/discussions), [Discord](https://discord.com/invite/bUG2bvbtHy), [Twitter](https://twitter.com/nextjs), and [Reddit](https://www.reddit.com/r/nextjs). diff --git a/docs/manifest.json b/docs/manifest.json deleted file mode 100644 index 23f039b67d761..0000000000000 --- a/docs/manifest.json +++ /dev/null @@ -1,588 +0,0 @@ -{ - "routes": [ - { - "title": "Documentation", - "heading": true, - "routes": [ - { - "title": "Getting Started", - "path": "/docs/getting-started.md" - }, - { - "title": "Basic Features", - "open": true, - "routes": [ - { - "title": "Pages", - "path": "/docs/basic-features/pages.md" - }, - { - "title": "Data Fetching", - "routes": [ - { - "path": "/docs/basic-features/data-fetching", - "redirect": { - "destination": "/docs/basic-features/data-fetching/overview" - } - }, - { - "path": "/docs/basic-features/data-fetching/index", - "redirect": { - "destination": "/docs/basic-features/data-fetching/overview" - } - }, - { - "title": "Overview", - "path": "/docs/basic-features/data-fetching/overview.md" - }, - { - "title": "getServerSideProps", - "path": "/docs/basic-features/data-fetching/get-server-side-props.md" - }, - { - "title": "getStaticProps", - "path": "/docs/basic-features/data-fetching/get-static-props.md" - }, - { - "title": "getStaticPaths", - "path": "/docs/basic-features/data-fetching/get-static-paths.md" - }, - { - "title": "Incremental Static Regeneration", - "path": "/docs/basic-features/data-fetching/incremental-static-regeneration.md" - }, - { - "title": "Client side", - "path": "/docs/basic-features/data-fetching/client-side.md" - } - ] - }, - { - "title": "Built-in CSS Support", - "path": "/docs/basic-features/built-in-css-support.md" - }, - { - "title": "Layouts", - "path": "/docs/basic-features/layouts.md" - }, - { - "title": "Image Optimization", - "path": "/docs/basic-features/image-optimization.md" - }, - { - "title": "Font Optimization", - "path": "/docs/basic-features/font-optimization.md" - }, - { - "title": "Static File Serving", - "path": "/docs/basic-features/static-file-serving.md" - }, - { - "title": "Fast Refresh", - "path": "/docs/basic-features/fast-refresh.md" - }, - { - "title": "ESLint", - "path": "/docs/basic-features/eslint.md" - }, - { - "title": "TypeScript", - "path": "/docs/basic-features/typescript.md" - }, - { - "title": "Environment Variables", - "path": "/docs/basic-features/environment-variables.md" - }, - { - "title": "Supported Browsers and Features", - "path": "/docs/basic-features/supported-browsers-features.md" - }, - { - "title": "Handling Scripts", - "path": "/docs/basic-features/script.md" - } - ] - }, - { - "title": "Routing", - "routes": [ - { - "title": "Introduction", - "path": "/docs/routing/introduction.md" - }, - { - "title": "Dynamic Routes", - "path": "/docs/routing/dynamic-routes.md" - }, - { - "title": "Imperatively", - "path": "/docs/routing/imperatively.md" - }, - { - "title": "Shallow Routing", - "path": "/docs/routing/shallow-routing.md" - } - ] - }, - { - "title": "API Routes", - "routes": [ - { - "path": "/docs/api-routes/api-middlewares", - "redirect": { - "destination": "/docs/api-routes/request-helpers", - "permanent": true - } - }, - { - "title": "Introduction", - "path": "/docs/api-routes/introduction.md" - }, - { - "title": "Dynamic API Routes", - "path": "/docs/api-routes/dynamic-api-routes.md" - }, - { - "title": "Request Helpers", - "path": "/docs/api-routes/request-helpers.md" - }, - { - "title": "Response Helpers", - "path": "/docs/api-routes/response-helpers.md" - }, - { - "title": "Edge API Routes", - "path": "/docs/api-routes/edge-api-routes.md" - } - ] - }, - { - "path": "/docs/middleware", - "redirect": { - "destination": "/docs/advanced-features/middleware", - "permanent": true - } - }, - { - "title": "Going to Production", - "path": "/docs/going-to-production.md" - }, - { - "title": "Deployment", - "path": "/docs/deployment.md" - }, - { - "title": "Authentication", - "path": "/docs/authentication.md" - }, - { - "title": "Testing", - "path": "/docs/testing.md" - }, - { - "title": "Accessibility", - "path": "/docs/accessibility.md" - }, - { - "title": "Guides", - "routes": [ - { - "title": "Building Forms", - "path": "/docs/guides/building-forms.md" - } - ] - }, - { - "title": "Advanced Features", - "routes": [ - { - "title": "Next.js Compiler", - "path": "/docs/advanced-features/compiler.md" - }, - { - "title": "Turbopack", - "path": "/docs/advanced-features/turbopack.md" - }, - { - "title": "Preview Mode", - "path": "/docs/advanced-features/preview-mode.md" - }, - { - "title": "Dynamic Import", - "path": "/docs/advanced-features/dynamic-import.md" - }, - { - "title": "Automatic Static Optimization", - "path": "/docs/advanced-features/automatic-static-optimization.md" - }, - { - "title": "Static HTML Export", - "path": "/docs/advanced-features/static-html-export.md" - }, - { - "title": "Absolute Imports and Module Path Aliases", - "path": "/docs/advanced-features/module-path-aliases.md" - }, - { - "title": "Using MDX", - "path": "/docs/advanced-features/using-mdx.md" - }, - { - "title": "AMP Support", - "routes": [ - { - "title": "Introduction", - "path": "/docs/advanced-features/amp-support/introduction.md" - }, - { - "title": "Adding AMP Components", - "path": "/docs/advanced-features/amp-support/adding-amp-components.md" - }, - { - "title": "AMP Validation", - "path": "/docs/advanced-features/amp-support/amp-validation.md" - }, - { - "title": "AMP in Static HTML export", - "path": "/docs/advanced-features/amp-support/amp-in-static-html-export.md" - }, - { - "title": "TypeScript", - "path": "/docs/advanced-features/amp-support/typescript.md" - } - ] - }, - { - "title": "Customizing Babel Config", - "path": "/docs/advanced-features/customizing-babel-config.md" - }, - { - "title": "Customizing PostCSS Config", - "path": "/docs/advanced-features/customizing-postcss-config.md" - }, - { - "title": "Custom Server", - "path": "/docs/advanced-features/custom-server.md" - }, - { - "title": "Custom `App`", - "path": "/docs/advanced-features/custom-app.md" - }, - { - "title": "Custom `Document`", - "path": "/docs/advanced-features/custom-document.md" - }, - { - "title": "Custom Error Page", - "path": "/docs/advanced-features/custom-error-page.md" - }, - { - "title": "`src` Directory", - "path": "/docs/advanced-features/src-directory.md" - }, - { - "title": "CI Build Caching", - "path": "/docs/advanced-features/ci-build-caching.md" - }, - { - "title": "Multi Zones", - "path": "/docs/advanced-features/multi-zones.md" - }, - { - "title": "Measuring performance", - "path": "/docs/advanced-features/measuring-performance.md" - }, - { - "title": "Middleware", - "path": "/docs/advanced-features/middleware.md" - }, - { - "title": "Debugging", - "path": "/docs/advanced-features/debugging.md" - }, - { - "title": "Error Handling", - "path": "/docs/advanced-features/error-handling.md" - }, - { - "title": "Source Maps", - "path": "/docs/advanced-features/source-maps.md" - }, - { - "title": "Codemods", - "path": "/docs/advanced-features/codemods.md" - }, - { - "title": "Internationalized Routing", - "path": "/docs/advanced-features/i18n-routing.md" - }, - { - "title": "Output File Tracing", - "path": "/docs/advanced-features/output-file-tracing.md" - }, - { - "title": "Security Headers", - "path": "/docs/advanced-features/security-headers.md" - }, - { - "title": "React 18", - "routes": [ - { - "path": "/docs/advanced-features/react-18", - "redirect": { - "destination": "/docs/advanced-features/react-18/overview" - } - }, - { - "title": "Overview", - "path": "/docs/advanced-features/react-18/overview.md" - }, - { - "title": "Streaming SSR", - "path": "/docs/advanced-features/react-18/streaming.md" - }, - { - "title": "React Server Components", - "path": "/docs/advanced-features/react-18/server-components.md" - }, - { - "title": "Switchable Runtime", - "path": "/docs/advanced-features/react-18/switchable-runtime.md" - } - ] - } - ] - }, - { - "title": "Upgrade Guide", - "path": "/docs/upgrading.md" - }, - { - "title": "Migrating to Next.js", - "routes": [ - { - "title": "Incrementally Adopting Next.js", - "path": "/docs/migrating/incremental-adoption.md" - }, - { - "title": "Migrating from Gatsby", - "path": "/docs/migrating/from-gatsby.md" - }, - { - "title": "Migrating from Create React App", - "path": "/docs/migrating/from-create-react-app.md" - }, - { - "title": "Migrating from React Router", - "path": "/docs/migrating/from-react-router.md" - } - ] - }, - { - "title": "FAQ", - "path": "/docs/faq.md" - } - ] - }, - { - "title": "API Reference", - "heading": true, - "routes": [ - { - "title": "CLI", - "path": "/docs/api-reference/cli.md" - }, - { - "title": "Create Next App", - "path": "/docs/api-reference/create-next-app.md" - }, - { - "title": "next/router", - "path": "/docs/api-reference/next/router.md" - }, - { - "title": "next/link", - "path": "/docs/api-reference/next/link.md" - }, - { - "title": "next/image", - "path": "/docs/api-reference/next/image.md" - }, - { - "title": "next/script", - "path": "/docs/api-reference/next/script.md" - }, - { - "title": "next/head", - "path": "/docs/api-reference/next/head.md" - }, - { - "title": "next/amp", - "path": "/docs/api-reference/next/amp.md" - }, - { - "title": "next/server", - "path": "/docs/api-reference/next/server.md" - }, - { - "title": "next/font", - "path": "/docs/api-reference/next/font.md" - }, - { - "path": "/docs/api-reference/next/streaming", - "redirect": { - "destination": "/docs/advanced-features/react-18" - } - }, - { - "path": "/docs/api-reference/next/future/image", - "redirect": { - "destination": "/docs/api-reference/next/image" - } - }, - { - "title": "next/legacy/image", - "path": "/docs/api-reference/next/legacy/image.md" - }, - { - "title": "Edge Runtime", - "path": "/docs/api-reference/edge-runtime.md" - }, - { - "title": "Data Fetching", - "routes": [ - { - "title": "getInitialProps", - "path": "/docs/api-reference/data-fetching/get-initial-props.md" - }, - { - "title": "getServerSideProps", - "path": "/docs/api-reference/data-fetching/get-server-side-props.md" - }, - { - "title": "getStaticPaths", - "path": "/docs/api-reference/data-fetching/get-static-paths.md" - }, - { - "title": "getStaticProps", - "path": "/docs/api-reference/data-fetching/get-static-props.md" - } - ] - }, - { - "title": "Static Optimization Indicator", - "path": "/docs/api-reference/next.config.js/static-optimization-indicator.md" - }, - { - "title": "next.config.js", - "routes": [ - { - "title": "Introduction", - "path": "/docs/api-reference/next.config.js/introduction.md" - }, - { - "title": "Environment Variables", - "path": "/docs/api-reference/next.config.js/environment-variables.md" - }, - { - "title": "Base Path", - "path": "/docs/api-reference/next.config.js/basepath.md" - }, - { - "title": "Rewrites", - "path": "/docs/api-reference/next.config.js/rewrites.md" - }, - { - "title": "Redirects", - "path": "/docs/api-reference/next.config.js/redirects.md" - }, - { - "title": "Custom Headers", - "path": "/docs/api-reference/next.config.js/headers.md" - }, - { - "title": "Custom Page Extensions", - "path": "/docs/api-reference/next.config.js/custom-page-extensions.md" - }, - { - "title": "CDN Support with Asset Prefix", - "path": "/docs/api-reference/next.config.js/cdn-support-with-asset-prefix.md" - }, - { - "title": "Custom Image Loader Config", - "path": "/docs/api-reference/next.config.js/custom-image-loader-config.md" - }, - { - "title": "Custom Webpack Config", - "path": "/docs/api-reference/next.config.js/custom-webpack-config.md" - }, - { - "title": "Compression", - "path": "/docs/api-reference/next.config.js/compression.md" - }, - { - "title": "Runtime Configuration", - "path": "/docs/api-reference/next.config.js/runtime-configuration.md" - }, - { - "title": "Disabling x-powered-by", - "path": "/docs/api-reference/next.config.js/disabling-x-powered-by.md" - }, - { - "title": "Disabling ETag Generation", - "path": "/docs/api-reference/next.config.js/disabling-etag-generation.md" - }, - { - "title": "Disabling HTTP Keep-Alive", - "path": "/docs/api-reference/next.config.js/disabling-http-keep-alive.md" - }, - { - "title": "Setting a custom build directory", - "path": "/docs/api-reference/next.config.js/setting-a-custom-build-directory.md" - }, - { - "title": "Configuring the Build ID", - "path": "/docs/api-reference/next.config.js/configuring-the-build-id.md" - }, - { - "title": "Configuring onDemandEntries", - "path": "/docs/api-reference/next.config.js/configuring-onDemandEntries.md" - }, - { - "title": "Ignoring ESLint", - "path": "/docs/api-reference/next.config.js/ignoring-eslint.md" - }, - { - "title": "Ignoring TypeScript Errors", - "path": "/docs/api-reference/next.config.js/ignoring-typescript-errors.md" - }, - { - "title": "exportPathMap", - "path": "/docs/api-reference/next.config.js/exportPathMap.md" - }, - { - "title": "Trailing Slash", - "path": "/docs/api-reference/next.config.js/trailing-slash.md" - }, - { - "title": "React Strict Mode", - "path": "/docs/api-reference/next.config.js/react-strict-mode.md" - }, - { - "title": "URL Imports", - "path": "/docs/api-reference/next.config.js/url-imports.md" - }, - { - "title": "Build indicator", - "path": "/docs/api-reference/next.config.js/build-indicator.md" - }, - { - "title": "Turbopack-specific options", - "path": "/docs/api-reference/next.config.js/turbopack.md" - } - ] - } - ] - } - ] -} diff --git a/docs/migrating/from-create-react-app.md b/docs/migrating/from-create-react-app.md deleted file mode 100644 index 6e49541072941..0000000000000 --- a/docs/migrating/from-create-react-app.md +++ /dev/null @@ -1,241 +0,0 @@ ---- -description: Learn how to transition an existing Create React App project to Next.js. ---- - -# Migrating from Create React App - -This guide will help you understand how to transition from an existing non-ejected Create React App project to Next.js. Migrating to Next.js will allow you to: - -- Choose which [data fetching](/docs/basic-features/data-fetching/overview.md) strategy you want on a per-page basis. -- Use [Incremental Static Regeneration](/docs/basic-features/data-fetching/incremental-static-regeneration.md) to update _existing_ pages by re-rendering them in the background as traffic comes in. -- Use [API Routes](/docs/api-routes/introduction.md). - -And more! Let’s walk through a series of steps to complete the migration. - -## Updating `package.json` and dependencies - -The first step towards migrating to Next.js is to update `package.json` and dependencies. You should: - -- Remove `react-scripts` (but keep `react` and `react-dom`). If you're using React Router, you can also remove `react-router-dom`. -- Install `next`. -- Add Next.js related commands to `scripts`. One is `next dev`, which runs a development server at `localhost:3000`. You should also add `next build` and `next start` for creating and starting a production build. - -Here's an example `package.json`: - -```json -{ - "scripts": { - "dev": "next dev", - "build": "next build", - "start": "next start" - }, - "dependencies": { - "next": "latest", - "react": "latest", - "react-dom": "latest" - } -} -``` - -## Static Assets and Compiled Output - -Create React App uses the `public` directory for the [entry HTML file](https://create-react-app.dev/docs/using-the-public-folder) as well as static assets, but Next.js only uses it for static assets. When migrating from Create React App, the location of the `public` directory remains the same. - -- Move any images, fonts, or other static assets to `public`. -- Convert `index.html` (the entry point of your application) to Next.js. Any `` code should be moved to a [custom `_document.js`](/docs/advanced-features/custom-document.md). Any shared layout between all pages should be moved to a [custom `_app.js`](/docs/advanced-features/custom-app.md). -- See [Styling](#styling) for CSS/Sass files. -- Add `.next` to `.gitignore`. - -## Creating Routes & Linking - -With Create React App, you're likely using React Router. Instead of using a third-party library, Next.js includes its own [file-system based routing](/docs/routing/introduction.md). - -- Create a [`pages`](/docs/basic-features/pages.md) directory at the root of your project. -- Then, move the `src/App.js` file to `pages/index.js`. This file is the [index page](https://nextjs.org/docs/routing/introduction#index-routes) of your Next.js application. Populate this file with code that is used to display the index route in your Create React App. -- Convert all other `Route` components to new files in the `pages` directory. -- For routes that require dynamic content (e.g. `/blog/:slug`), you can use [Dynamic Routes](/docs/routing/dynamic-routes.md) with Next.js (e.g. `pages/blog/[slug].js`). The value of `slug` is accessible through a [query parameter](/docs/routing/dynamic-routes.md). For example, the route `/blog/first-post` would forward the query object `{ 'slug': 'first-post' }` to `pages/blog/[slug].js` ([learn more here](/docs/basic-features/data-fetching/get-static-paths.md)). - -For more information, see [Migrating from React Router](/docs/migrating/from-react-router.md). - -## Styling - -Next.js has built-in support for [CSS](/docs/basic-features/built-in-css-support.md), [Sass](/docs/basic-features/built-in-css-support.md#sass-support) and [CSS-in-JS](/docs/basic-features/built-in-css-support.md#css-in-js). - -With Create React App, you can import `.css` files directly inside React components. Next.js allows you to do the same, but requires these files to be [CSS Modules](/docs/basic-features/built-in-css-support.md). For global styles, you'll need a [custom `_app.js`](/docs/advanced-features/custom-app.md) to add a [global stylesheet](/docs/basic-features/built-in-css-support.md#adding-a-global-stylesheet). - -## Safely Accessing Web APIs - -With client-side rendered applications (like Create React App), you can access `window`, `localStorage`, `navigator`, and other [Web APIs](https://developer.mozilla.org/en-US/docs/Web/API) out of the box. - -Since Next.js uses [pre-rendering](/docs/basic-features/pages.md#pre-rendering), you'll need to safely access those Web APIs only when you're on the client-side. For example, the following code snippet will allow access to `window` only on the client-side. - -```jsx -if (typeof window !== 'undefined') { - // You now have access to `window` -} -``` - -A recommended way of accessing Web APIs safely is by using the [`useEffect`](https://reactjs.org/docs/hooks-effect.html) hook, which only executes client-side: - -```jsx -import { useEffect } from 'react' - -useEffect(() => { - // You now have access to `window` -}, []) -``` - -## Image Component and Image Optimization - -Since version **10.0.0**, Next.js has a built-in [Image Component and Automatic Image Optimization](/docs/basic-features/image-optimization.md). - -The Next.js Image Component, [`next/image`](/docs/api-reference/next/image.md), is an extension of the HTML `` element, evolved for the modern web. - -The Automatic Image Optimization allows for resizing, optimizing, and serving images in modern formats like [WebP](https://developer.mozilla.org/en-US/docs/Web/Media/Formats/Image_types) when the browser supports it. This avoids shipping large images to devices with a smaller viewport. It also allows Next.js to automatically adopt future image formats and serve them to browsers that support those formats. - -Instead of optimizing images at build time, Next.js optimizes images on-demand, as users request them. Your build times aren't increased, whether shipping 10 images or 10 million images. - -```jsx -import Image from 'next/image' - -export default function Home() { - return ( - <> -

    My Homepage

    - Picture of the author -

    Welcome to my homepage!

    - - ) -} -``` - -## Environment Variables - -Next.js has support for `.env` [Environment Variables](/docs/basic-features/environment-variables.md) similar to Create React App. The main difference is the prefix used to expose environment variables on the client-side. - -- Change all environment variables with the `REACT_APP_` prefix to `NEXT_PUBLIC_`. -- Server-side environment variables will be available at build-time and in [API Routes](/docs/api-routes/introduction.md). - -## Search Engine Optimization - -Most Create React App examples use `react-helmet` to assist with adding `meta` tags for proper SEO. With Next.js, we use [`next/head`](/docs/api-reference/next/head.md) to add `meta` tags to your `` element. For example, here's an SEO component with Create React App: - -```jsx -// src/components/seo.js - -import { Helmet } from 'react-helmet' - -export default function SEO({ description, title, siteTitle }) { - return ( - - ) -} -``` - -And here's the same example using Next.js. - -```jsx -// src/components/seo.js - -import Head from 'next/head' - -export default function SEO({ description, title, siteTitle }) { - return ( - - {`${title} | ${siteTitle}`} - - - - - - - - - - - ) -} -``` - -## Single-Page App (SPA) - -If you want to move your existing Create React App to Next.js and keep a Single-Page App, you can move your old application's entry point to an [Optional Catch-All Route](/docs/routing/dynamic-routes.md#optional-catch-all-routes) named `pages/[[...app]].js`. - -```jsx -// pages/[[...app]].js - -import { useState, useEffect } from 'react' -import CreateReactAppEntryPoint from '../components/app' - -function App() { - const [isMounted, setIsMounted] = useState(false) - - useEffect(() => { - setIsMounted(true) - }, []) - - if (!isMounted) { - return null - } - - return -} - -export default App -``` - -## Ejected Create React App - -If you've ejected Create React App, here are some things to consider: - -- If you have custom file loaders set up for CSS, Sass, or other assets, this is all built-in with Next.js. -- If you've manually added [new JavaScript features](/docs/basic-features/supported-browsers-features.md#javascript-language-features) (e.g. Optional Chaining) or [Polyfills](/docs/basic-features/supported-browsers-features.md#polyfills), check to see what's included by default with Next.js. -- If you have a custom code splitting setup, you can remove that. Next.js has automatic code splitting on a [per-page basis](/docs/basic-features/pages.md). -- You can [customize your PostCSS setup](/docs/advanced-features/customizing-postcss-config.md#default-behavior) with Next.js without ejecting from the framework. -- You should reference the default [Babel config](/docs/advanced-features/customizing-babel-config.md) and [Webpack config](/docs/api-reference/next.config.js/custom-webpack-config.md) of Next.js to see what's included by default. - -## Learn More - -You can learn more about Next.js by completing our [starter tutorial](https://nextjs.org/learn/basics/create-nextjs-app). If you have questions or if this guide didn't work for you, feel free to reach out to our community on [GitHub Discussions](https://github.com/vercel/next.js/discussions). diff --git a/docs/migrating/from-gatsby.md b/docs/migrating/from-gatsby.md deleted file mode 100644 index 718fc928e7d52..0000000000000 --- a/docs/migrating/from-gatsby.md +++ /dev/null @@ -1,319 +0,0 @@ ---- -description: Learn how to transition an existing Gatsby project to Next.js. ---- - -# Migrating from Gatsby - -This guide will help you understand how to transition from an existing Gatsby project to Next.js. Migrating to Next.js will allow you to: - -- Choose which [data fetching](/docs/basic-features/data-fetching/overview.md) strategy you want on a per-page basis. -- Use [Incremental Static Regeneration](/docs/basic-features/data-fetching/incremental-static-regeneration.md) to update _existing_ pages by re-rendering them in the background as traffic comes in. -- Use [API Routes](/docs/api-routes/introduction.md). - -And more! Let’s walk through a series of steps to complete the migration. - -## Updating `package.json` and dependencies - -The first step towards migrating to Next.js is to update `package.json` and dependencies. You should: - -- Remove all Gatsby-related packages (but keep `react` and `react-dom`). -- Install `next`. -- Add Next.js related commands to `scripts`. One is `next dev`, which runs a development server at `localhost:3000`. You should also add `next build` and `next start` for creating and starting a production build. - -Here's an example `package.json` ([view diff](https://github.com/leerob/gatsby-to-nextjs/pull/1/files#diff-b9cfc7f2cdf78a7f4b91a753d10865a2)): - -```json -{ - "scripts": { - "dev": "next dev", - "build": "next build", - "start": "next start" - }, - "dependencies": { - "next": "latest", - "react": "latest", - "react-dom": "latest" - } -} -``` - -## Static Assets and Compiled Output - -Gatsby uses the `public` directory for the compiled output, whereas Next.js uses it for static assets. Here are the steps for migration ([view diff](https://github.com/leerob/gatsby-to-nextjs/pull/1/files#diff-a084b794bc0759e7a6b77810e01874f2)): - -- Remove `.cache/` and `public` from `.gitignore` and delete both directories. -- Rename Gatsby’s `static` directory as `public`. -- Add `.next` to `.gitignore`. - -## Creating Routes - -Both Gatsby and Next support a `pages` directory, which uses [file-system based routing](/docs/routing/introduction.md). Gatsby's directory is `src/pages`, which is also [supported by Next.js](/docs/advanced-features/src-directory.md). - -Gatsby creates dynamic routes using the `createPages` API inside of `gatsby-node.js`. With Next, we can use [Dynamic Routes](/docs/routing/dynamic-routes.md) inside of `pages` to achieve the same effect. Rather than having a `template` directory, you can use the React component inside your dynamic route file. For example: - -- **Gatsby:** `createPages` API inside `gatsby-node.js` for each blog post, then have a template file at `src/templates/blog-post.js`. -- **Next:** Create `pages/blog/[slug].js` which contains the blog post template. The value of `slug` is accessible through a [query parameter](/docs/routing/dynamic-routes.md). For example, the route `/blog/first-post` would forward the query object `{ 'slug': 'first-post' }` to `pages/blog/[slug].js` ([learn more here](/docs/basic-features/data-fetching/get-static-paths.md)). - -## Styling - -With Gatsby, global CSS imports are included in `gatsby-browser.js`. With Next, you should create a [custom `_app.js`](/docs/advanced-features/custom-app.md) for global CSS. When migrating, you can copy over your CSS imports directly and update the relative file path, if necessary. Next.js has [built-in CSS support](/docs/basic-features/built-in-css-support.md). - -## Links - -The Gatsby `Link` and Next.js [`Link`](/docs/api-reference/next/link.md) component have a slightly different API. - -```jsx -// Gatsby - -import { Link } from 'gatsby' - -export default function Home() { - return blog -} -``` - -```jsx -// Next.js - -import Link from 'next/link' - -export default function Home() { - return blog -} -``` - -Update any import statements, switch `to` to `href`. - -## Data Fetching - -The largest difference between Gatsby and Next.js is how data fetching is implemented. Gatsby is opinionated with GraphQL being the default strategy for retrieving data across your application. With Next.js, you get to choose which strategy you want (GraphQL is one supported option). - -Gatsby uses the `graphql` tag to query data in the pages of your site. This may include local data, remote data, or information about your site configuration. Gatsby only allows the creation of static pages. With Next.js, you can choose on a [per-page basis](/docs/basic-features/pages.md) which [data fetching strategy](/docs/basic-features/data-fetching/overview.md) you want. For example, `getServerSideProps` allows you to do server-side rendering. If you wanted to generate a static page, you'd export `getStaticProps` / `getStaticPaths` inside the page, rather than using `pageQuery`. For example: - -```js -// src/pages/[slug].js - -// Install remark and remark-html -import { remark } from 'remark' -import html from 'remark-html' -import { getPostBySlug, getAllPosts } from '../lib/blog' - -export async function getStaticProps({ params }) { - const post = getPostBySlug(params.slug) - const markdown = await remark() - .use(html) - .process(post.content || '') - const content = markdown.toString() - - return { - props: { - ...post, - content, - }, - } -} - -export async function getStaticPaths() { - const posts = getAllPosts() - - return { - paths: posts.map((post) => { - return { - params: { - slug: post.slug, - }, - } - }), - fallback: false, - } -} -``` - -You'll commonly see Gatsby plugins used for reading the file system (`gatsby-source-filesystem`), handling markdown files (`gatsby-transformer-remark`), and so on. For example, the popular starter blog example has [15 Gatsby specific packages](https://github.com/gatsbyjs/gatsby-starter-blog/blob/master/package.json). Next takes a different approach. It includes common features directly inside the framework, and gives the user full control over integrations with external packages. For example, rather than abstracting reading from the file system to a plugin, you can use the native Node.js `fs` package inside `getStaticProps` / `getStaticPaths` to read from the file system. - -```js -// src/lib/blog.js - -// Install gray-matter and date-fns -import matter from 'gray-matter' -import { parseISO, format } from 'date-fns' -import fs from 'fs' -import { join } from 'path' - -// Add markdown files in `src/content/blog` -const postsDirectory = join(process.cwd(), 'src', 'content', 'blog') - -export function getPostBySlug(slug) { - const realSlug = slug.replace(/\.md$/, '') - const fullPath = join(postsDirectory, `${realSlug}.md`) - const fileContents = fs.readFileSync(fullPath, 'utf8') - const { data, content } = matter(fileContents) - const date = format(parseISO(data.date), 'MMMM dd, yyyy') - - return { slug: realSlug, frontmatter: { ...data, date }, content } -} - -export function getAllPosts() { - const slugs = fs.readdirSync(postsDirectory) - const posts = slugs.map((slug) => getPostBySlug(slug)) - - return posts -} -``` - -## Image Component and Image Optimization - -Next.js has a built-in [Image Component and Automatic Image Optimization](/docs/basic-features/image-optimization.md). - -The Next.js Image Component, [`next/image`](/docs/api-reference/next/image.md), is an extension of the HTML `` element, evolved for the modern web. - -The Automatic Image Optimization allows for resizing, optimizing, and serving images in modern formats like [WebP](https://developer.mozilla.org/en-US/docs/Web/Media/Formats/Image_types) when the browser supports it. This avoids shipping large images to devices with a smaller viewport. It also allows Next.js to automatically adopt future image formats and serve them to browsers that support those formats. - -### Migrating from Gatsby Image - -Instead of optimizing images at build time, Next.js optimizes images on-demand, as users request them. Unlike static site generators and static-only solutions, your build times aren't increased, whether shipping 10 images or 10 million images. - -This means you can remove common Gatsby plugins like: - -- `gatsby-image` -- `gatsby-transformer-sharp` -- `gatsby-plugin-sharp` - -Instead, use the built-in [`next/image`](/docs/api-reference/next/image.md) component and [Automatic Image Optimization](/docs/basic-features/image-optimization.md). - -> The `next/image` component's default loader is not supported when using [`output: 'export'`](/docs/advanced-features/static-html-export.md). However, other loader options will work. - -```jsx -import Image from 'next/image' -import profilePic from '../public/me.png' - -export default function Home() { - return ( - <> -

    My Homepage

    - Picture of the author -

    Welcome to my homepage!

    - - ) -} -``` - -## Site Configuration - -With Gatsby, your site's metadata (name, description, etc.) is located inside `gatsby-config.js`. This is then exposed through the GraphQL API and consumed through a `pageQuery` or a static query inside a component. - -With Next.js, we recommend creating a config file similar to below. You can then import this file anywhere without having to use GraphQL to access your site's metadata. - -```js -// src/config.js - -export default { - title: 'Starter Blog', - author: { - name: 'Lee Robinson', - summary: 'who loves Next.js.', - }, - description: 'A starter blog converting Gatsby -> Next.', - social: { - twitter: 'leeerob', - }, -} -``` - -## Search Engine Optimization - -Most Gatsby examples use `react-helmet` to assist with adding `meta` tags for proper SEO. With Next.js, we use [`next/head`](/docs/api-reference/next/head.md) to add `meta` tags to your `` element. For example, here's an SEO component with Gatsby: - -```js -// src/components/seo.js - -import { Helmet } from 'react-helmet' - -export default function SEO({ description, title, siteTitle }) { - return ( - - ) -} -``` - -And here's the same example using Next.js, including reading from a site config file. - -```js -// src/components/seo.js - -import Head from 'next/head' -import config from '../config' - -export default function SEO({ description, title }) { - const siteTitle = config.title - - return ( - - {`${title} | ${siteTitle}`} - - - - - - - - - - - ) -} -``` - -## Learn more - -Take a look at [this pull request](https://github.com/leerob/gatsby-to-nextjs/pull/1) for more details on how an app can be migrated from Gatsby to Next.js. If you have questions or if this guide didn't work for you, feel free to reach out to our community on [GitHub Discussions](https://github.com/vercel/next.js/discussions). diff --git a/docs/migrating/from-react-router.md b/docs/migrating/from-react-router.md deleted file mode 100644 index 52986dc5b2edd..0000000000000 --- a/docs/migrating/from-react-router.md +++ /dev/null @@ -1,175 +0,0 @@ ---- -description: Learn how to migrate from React Router to file-system based routes with Next.js. ---- - -# Migrating from React Router - -This guide will help you understand how to transition from [React Router](https://reactrouter.com) to [file-system based](/docs/routing/introduction.md) routes with Next.js. Using [`next/link`](/docs/api-reference/next/link.md) and [`next/router`](/docs/api-reference/next/router.md) will allow you to: - -- Decrease bundle size by removing React Router as a dependency. -- Define your application routes through the file system. -- Utilize the latest improvements to the Next.js framework. - -## Basics - -First, uninstall React Router. You'll be migrating to the built-in routing with Next.js. - -```jsx -npm uninstall react-router-dom -``` - -The `Link` component for performing client-side route transitions is slightly different from React Router. - -```jsx -// Before (React Router) -import { Link } from 'react-router-dom' - -export default function App() { - return About -} - -// After (Next.js) -import Link from 'next/link' - -export default function App() { - return ( - - About - - ) -} -``` - -Most React applications that use React Router have a top-level navigation file, containing a list of routes. For example: - -```jsx -import { BrowserRouter as Router, Switch, Route } from 'react-router-dom' - -export default function App() { - return ( - - - -

    About

    -
    - -

    Blog

    -
    - -

    Home

    -
    -
    -
    - ) -} -``` - -With Next.js, you can express the same application structure in the file system. When a file is added to the [`pages`](/docs/basic-features/pages.md) directory it's automatically available as a route. - -- `pages/about.js` → `/about` -- `pages/blog.js` → `/blog` -- `pages/index.js` → `/` - -## Nested Routes - -In the example below, routes like `/blog/my-post` would render the `Post` component. If a slug was not provided, it would render the list of all blog posts. - -```jsx -import { - BrowserRouter as Router, - Switch, - Route, - useRouteMatch, - useParams, -} from 'react-router-dom' - -export default function Blog() { - // Nested route under /blog - const match = useRouteMatch() - - return ( - - - - - - -

    All Blog Posts

    -
    -
    -
    - ) -} - -function Post() { - const { slug } = useParams() - return

    Post Slug: {slug}

    -} -``` - -Rather than using the `:slug` syntax inside your `Route` component, Next.js uses the `[slug]` syntax in the file name for [Dynamic Routes](/docs/routing/dynamic-routes.md). We can transform this to Next.js by creating two new files, `pages/blog/index.js` (showing all pages) and `pages/blog/[slug].js` (showing an individual post). - -```jsx -// pages/blog/index.js - -export default function Blog() { - return

    All Blog Posts

    -} - -// pages/blog/[slug].js - -import { useRouter } from 'next/router' - -export default function Post() { - const router = useRouter() - const { slug } = router.query - - return

    Post Slug: {slug}

    -} -``` - -## Server Rendering - -Next.js has built-in support for [Server-side Rendering](/docs/basic-features/pages#server-side-rendering.md). This means you can remove any instances of `StaticRouter` in your code. - -## Code Splitting - -Next.js has built-in support for [Code Splitting](https://v5.reactrouter.com/web/guides/code-splitting). This means you can remove any instances of: - -- `@loadable/server`, `@loadable/babel-plugin`, and `@loadable/webpack-plugin` -- Modifications to your `.babelrc` for `@loadable/babel-plugin` - -Each file inside your `pages/` directory will be code split into its own JavaScript bundle during the build process. Next.js [also supports](/docs/basic-features/supported-browsers-features.md#javascript-language-features) ES2020 dynamic `import()` for JavaScript. With it you can import JavaScript modules dynamically and work with them. They also work with SSR. - -For more information, read about [Dynamic Imports](https://nextjs.org/docs/advanced-features/dynamic-import). - -## Scroll Restoration - -Next.js has built-in support for [Scroll Restoration](https://v5.reactrouter.com/web/guides/scroll-restoration). This means you can remove any custom `ScrollToTop` components you have defined. - -The default behavior of `next/link` and `next/router` is to scroll to the top of the page. You can also [disable this](https://nextjs.org/docs/api-reference/next/link#disable-scrolling-to-the-top-of-the-page) if you prefer. - -## Learn More - -For more information on what to do next, we recommend the following sections: - - - - - - diff --git a/docs/migrating/incremental-adoption.md b/docs/migrating/incremental-adoption.md deleted file mode 100644 index e7bf9ae047635..0000000000000 --- a/docs/migrating/incremental-adoption.md +++ /dev/null @@ -1,93 +0,0 @@ ---- -description: Learn different strategies for incrementally adopting Next.js into your development workflow. ---- - -# Incrementally Adopting Next.js - -
    - Examples - -
    - -Next.js has been designed for gradual adoption. With Next.js, you can continue using your existing code and add as much (or as little) React as you need. By starting small and incrementally adding more pages, you can prevent derailing feature work by avoiding a complete rewrite. - -## Strategies - -### Subpath - -The first strategy is to configure your server or proxy such that, everything under a specific subpath points to a Next.js app. For example, your existing website might be at `example.com`, and you might configure your proxy such that `example.com/store` serves a Next.js e-commerce store. - -Using [`basePath`](/docs/api-reference/next.config.js/basepath.md), you can configure your Next.js application's assets and links to automatically work with your new subpath `/store`. Since each page in Next.js is its own [standalone route](/docs/routing/introduction.md), pages like `pages/products.js` will route to `example.com/store/products` in your application. - -```jsx -// next.config.js - -module.exports = { - basePath: '/store', -} -``` - -To learn more about `basePath`, take a look at our [documentation](/docs/api-reference/next.config.js/basepath.md). - -### Rewrites - -The second strategy is to create a new Next.js app that points to the root URL of your domain. Then, you can use [`rewrites`](/docs/api-reference/next.config.js/rewrites.md) inside `next.config.js` to have some subpaths to be proxied to your existing app. - -For example, let's say you created a Next.js app to be served from `example.com` with the following `next.config.js`. Now, requests for the pages you’ve added to this Next.js app (e.g. `/about` if you’ve added `pages/about.js`) will be handled by Next.js, and requests for any other route (e.g. `/dashboard`) will be proxied to `proxy.example.com`. - -> **Note:** If you use [fallback: true/'blocking'](/docs/api-reference/data-fetching/get-static-paths#fallback-true) in `getStaticPaths`, the catch-all fallback `rewrites` defined in `next.config.js` will not be run. They are instead caught by the `getStaticPaths` fallback. - -```jsx -// next.config.js - -module.exports = { - async rewrites() { - return { - // After checking all Next.js pages (including dynamic routes) - // and static files we proxy any other requests - fallback: [ - { - source: '/:path*', - destination: `https://proxy.example.com/:path*`, - }, - ], - } - - // For versions of Next.js < v10.1 you can use a no-op rewrite instead - return [ - // we need to define a no-op rewrite to trigger checking - // all pages/static files before we attempt proxying - { - source: '/:path*', - destination: '/:path*', - }, - { - source: '/:path*', - destination: `https://proxy.example.com/:path*`, - }, - ] - }, -} -``` - -To learn more about rewrites, take a look at our [documentation](/docs/api-reference/next.config.js/rewrites.md). - -> **Note:** If you are incrementally migrating to a dynamic route (e.g. `[slug].js`) and using `fallback: true` or `fallback: 'blocking'` along with a fallback `rewrite`, ensure you consider the case where pages are not found. When Next.js matches the dynamic route it stops checking any further routes. Using `notFound: true` in `getStaticProps` will return the 404 page without applying the fallback `rewrite`. If this is not desired, you can use `getServerSideProps` with `stale-while-revalidate` Cache-Control headers when returning your props. Then, you can _manually_ proxy to your existing backend using something like [http-proxy](https://github.com/vercel/next.js/discussions/38839#discussioncomment-3744442) instead of returning `notFound: true`. - -### Micro-Frontends with Monorepos and Subdomains - -Next.js and [Vercel](https://vercel.com) make it straightforward to adopt micro frontends and deploy as a [monorepo](https://vercel.com/blog/monorepos-are-changing-how-teams-build-software?utm_source=next-site&utm_medium=docs&utm_campaign=next-website). This allows you to use [subdomains](https://demo.vercel.pub/platforms-starter-kit) to adopt new applications incrementally. Some benefits of micro-frontends: - -- Smaller, more cohesive and maintainable codebases. -- More scalable organizations with decoupled, autonomous teams. -- The ability to upgrade, update, or even rewrite parts of the frontend in a more incremental fashion. - -Once your monorepo is set up, push changes to your Git repository as usual and you'll see the commits deployed to the Vercel projects you've connected. - -## Conclusion - -To learn more, read about [subpaths](/docs/api-reference/next.config.js/basepath.md) and [rewrites](/docs/api-reference/next.config.js/rewrites.md) or [deploy a Next.js monorepo](https://vercel.com/templates/next.js/monorepo-turborepo). diff --git a/docs/routing/dynamic-routes.md b/docs/routing/dynamic-routes.md deleted file mode 100644 index 87c02e856791d..0000000000000 --- a/docs/routing/dynamic-routes.md +++ /dev/null @@ -1,154 +0,0 @@ ---- -description: Dynamic Routes are pages that allow you to add custom params to your URLs. Start creating Dynamic Routes and learn more here. ---- - -# Dynamic Routes - -
    - Examples - -
    - -Defining routes by using predefined paths is not always enough for complex applications. In Next.js you can add brackets to a page (`[param]`) to create a dynamic route (a.k.a. url slugs, pretty urls, and others). - -Consider the following page `pages/post/[pid].js`: - -```jsx -import { useRouter } from 'next/router' - -const Post = () => { - const router = useRouter() - const { pid } = router.query - - return

    Post: {pid}

    -} - -export default Post -``` - -Any route like `/post/1`, `/post/abc`, etc. will be matched by `pages/post/[pid].js`. The matched path parameter will be sent as a query parameter to the page, and it will be merged with the other query parameters. - -For example, the route `/post/abc` will have the following `query` object: - -```json -{ "pid": "abc" } -``` - -Similarly, the route `/post/abc?foo=bar` will have the following `query` object: - -```json -{ "foo": "bar", "pid": "abc" } -``` - -However, route parameters will override query parameters with the same name. For example, the route `/post/abc?pid=123` will have the following `query` object: - -```json -{ "pid": "abc" } -``` - -Multiple dynamic route segments work the same way. The page `pages/post/[pid]/[comment].js` will match the route `/post/abc/a-comment` and its `query` object will be: - -```json -{ "pid": "abc", "comment": "a-comment" } -``` - -Client-side navigations to dynamic routes are handled with [`next/link`](/docs/api-reference/next/link.md). If we wanted to have links to the routes used above it will look like this: - -```jsx -import Link from 'next/link' - -function Home() { - return ( -
      -
    • - Go to pages/post/[pid].js -
    • -
    • - Also goes to pages/post/[pid].js -
    • -
    • - - Go to pages/post/[pid]/[comment].js - -
    • -
    - ) -} - -export default Home -``` - -Read our docs for [Linking between pages](/docs/routing/introduction.md#linking-between-pages) to learn more. - -### Catch all routes - -
    - Examples - -
    - -Dynamic routes can be extended to catch all paths by adding three dots (`...`) inside the brackets. For example: - -- `pages/post/[...slug].js` matches `/post/a`, but also `/post/a/b`, `/post/a/b/c` and so on. - -> **Note**: You can use names other than `slug`, such as: `[...param]` - -Matched parameters will be sent as a query parameter (`slug` in the example) to the page, and it will always be an array, so, the path `/post/a` will have the following `query` object: - -```json -{ "slug": ["a"] } -``` - -And in the case of `/post/a/b`, and any other matching path, new parameters will be added to the array, like so: - -```json -{ "slug": ["a", "b"] } -``` - -### Optional catch all routes - -Catch all routes can be made optional by including the parameter in double brackets (`[[...slug]]`). - -For example, `pages/post/[[...slug]].js` will match `/post`, `/post/a`, `/post/a/b`, and so on. - -The main difference between catch all and optional catch all routes is that with optional, the route without the parameter is also matched (`/post` in the example above). - -The `query` objects are as follows: - -```json -{ } // GET `/post` (empty object) -{ "slug": ["a"] } // `GET /post/a` (single-element array) -{ "slug": ["a", "b"] } // `GET /post/a/b` (multi-element array) -``` - -## Caveats - -- Predefined routes take precedence over dynamic routes, and dynamic routes over catch all routes. Take a look at the following examples: - - `pages/post/create.js` - Will match `/post/create` - - `pages/post/[pid].js` - Will match `/post/1`, `/post/abc`, etc. But not `/post/create` - - `pages/post/[...slug].js` - Will match `/post/1/2`, `/post/a/b/c`, etc. But not `/post/create`, `/post/abc` -- Pages that are statically optimized by [Automatic Static Optimization](/docs/advanced-features/automatic-static-optimization.md) will be hydrated without their route parameters provided, i.e `query` will be an empty object (`{}`). - - After hydration, Next.js will trigger an update to your application to provide the route parameters in the `query` object. - -## Related - -For more information on what to do next, we recommend the following sections: - - - - diff --git a/docs/routing/imperatively.md b/docs/routing/imperatively.md deleted file mode 100644 index f3c4e589bb83b..0000000000000 --- a/docs/routing/imperatively.md +++ /dev/null @@ -1,30 +0,0 @@ ---- -description: Client-side navigations are also possible using the Next.js Router instead of the Link component. Learn more here. ---- - -# Imperatively - -
    - Examples - -
    - -[`next/link`](/docs/api-reference/next/link.md) should be able to cover most of your routing needs, but you can also do client-side navigations without it, take a look at the [documentation for `next/router`](/docs/api-reference/next/router.md). - -The following example shows how to do basic page navigations with [`useRouter`](/docs/api-reference/next/router.md#useRouter): - -```jsx -import { useRouter } from 'next/router' - -export default function ReadMore() { - const router = useRouter() - - return ( - - ) -} -``` diff --git a/docs/routing/introduction.md b/docs/routing/introduction.md deleted file mode 100644 index c19a59486c9b1..0000000000000 --- a/docs/routing/introduction.md +++ /dev/null @@ -1,160 +0,0 @@ ---- -description: Next.js has a built-in, opinionated, and file-system based Router. You can learn how it works here. ---- - -# Routing - -Next.js has a file-system based router built on the [concept of pages](/docs/basic-features/pages.md). - -When a file is added to the `pages` directory, it's automatically available as a route. - -The files inside the `pages` directory can be used to define most common patterns. - -#### Index routes - -The router will automatically route files named `index` to the root of the directory. - -- `pages/index.js` → `/` -- `pages/blog/index.js` → `/blog` - -#### Nested routes - -The router supports nested files. If you create a nested folder structure, files will automatically be routed in the same way still. - -- `pages/blog/first-post.js` → `/blog/first-post` -- `pages/dashboard/settings/username.js` → `/dashboard/settings/username` - -#### Dynamic route segments - -To match a dynamic segment, you can use the bracket syntax. This allows you to match named parameters. - -- `pages/blog/[slug].js` → `/blog/:slug` (`/blog/hello-world`) -- `pages/[username]/settings.js` → `/:username/settings` (`/foo/settings`) -- `pages/post/[...all].js` → `/post/*` (`/post/2020/id/title`) - -> Check out the [Dynamic Routes documentation](/docs/routing/dynamic-routes.md) to learn more about how they work. - -## Linking between pages - -The Next.js router allows you to do client-side route transitions between pages, similar to a single-page application. - -A React component called `Link` is provided to do this client-side route transition. - -```jsx -import Link from 'next/link' - -function Home() { - return ( -
      -
    • - Home -
    • -
    • - About Us -
    • -
    • - Blog Post -
    • -
    - ) -} - -export default Home -``` - -The example above uses multiple links. Each one maps a path (`href`) to a known page: - -- `/` → `pages/index.js` -- `/about` → `pages/about.js` -- `/blog/hello-world` → `pages/blog/[slug].js` - -Any `` in the viewport (initially or through scroll) will be prefetched by default (including the corresponding data) for pages using [Static Generation](/docs/basic-features/data-fetching/get-static-props.md). The corresponding data for [server-rendered](/docs/basic-features/data-fetching/get-server-side-props.md) routes is fetched _only when_ the `` is clicked. - -### Linking to dynamic paths - -You can also use interpolation to create the path, which comes in handy for [dynamic route segments](#dynamic-route-segments). For example, to show a list of posts which have been passed to the component as a prop: - -```jsx -import Link from 'next/link' - -function Posts({ posts }) { - return ( -
      - {posts.map((post) => ( -
    • - - {post.title} - -
    • - ))} -
    - ) -} - -export default Posts -``` - -> [`encodeURIComponent`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/encodeURIComponent) is used in the example to keep the path utf-8 compatible. - -Alternatively, using a URL Object: - -```jsx -import Link from 'next/link' - -function Posts({ posts }) { - return ( -
      - {posts.map((post) => ( -
    • - - {post.title} - -
    • - ))} -
    - ) -} - -export default Posts -``` - -Now, instead of using interpolation to create the path, we use a URL object in `href` where: - -- `pathname` is the name of the page in the `pages` directory. `/blog/[slug]` in this case. -- `query` is an object with the dynamic segment. `slug` in this case. - -## Injecting the router - -
    - Examples - -
    - -To access the [`router` object](/docs/api-reference/next/router.md#router-object) in a React component you can use [`useRouter`](/docs/api-reference/next/router.md#useRouter) or [`withRouter`](/docs/api-reference/next/router.md#withRouter). - -In general we recommend using [`useRouter`](/docs/api-reference/next/router.md#useRouter). - -## Learn more - -The router is divided in multiple parts: - - - - diff --git a/docs/routing/shallow-routing.md b/docs/routing/shallow-routing.md deleted file mode 100644 index dbb3f13250ab5..0000000000000 --- a/docs/routing/shallow-routing.md +++ /dev/null @@ -1,65 +0,0 @@ ---- -description: You can use shallow routing to change the URL without triggering a new page change. Learn more here. ---- - -# Shallow Routing - -
    - Examples - -
    - -Shallow routing allows you to change the URL without running data fetching methods again, that includes [`getServerSideProps`](/docs/basic-features/data-fetching/get-server-side-props.md), [`getStaticProps`](/docs/basic-features/data-fetching/get-static-props.md), and [`getInitialProps`](/docs/api-reference/data-fetching/get-initial-props.md). - -You'll receive the updated `pathname` and the `query` via the [`router` object](/docs/api-reference/next/router.md#router-object) (added by [`useRouter`](/docs/api-reference/next/router.md#useRouter) or [`withRouter`](/docs/api-reference/next/router.md#withRouter)), without losing state. - -To enable shallow routing, set the `shallow` option to `true`. Consider the following example: - -```jsx -import { useEffect } from 'react' -import { useRouter } from 'next/router' - -// Current URL is '/' -function Page() { - const router = useRouter() - - useEffect(() => { - // Always do navigations after the first render - router.push('/?counter=10', undefined, { shallow: true }) - }, []) - - useEffect(() => { - // The counter changed! - }, [router.query.counter]) -} - -export default Page -``` - -The URL will get updated to `/?counter=10`. and the page won't get replaced, only the state of the route is changed. - -You can also watch for URL changes via [`componentDidUpdate`](https://reactjs.org/docs/react-component.html#componentdidupdate) as shown below: - -```jsx -componentDidUpdate(prevProps) { - const { pathname, query } = this.props.router - // verify props have changed to avoid an infinite loop - if (query.counter !== prevProps.router.query.counter) { - // fetch data based on the new query - } -} -``` - -## Caveats - -Shallow routing **only** works for URL changes in the current page. For example, let's assume we have another page called `pages/about.js`, and you run this: - -```jsx -router.push('/?counter=10', '/about?counter=10', { shallow: true }) -``` - -Since that's a new page, it'll unload the current page, load the new one and wait for data fetching even though we asked to do shallow routing. - -When shallow routing is used with middleware it will not ensure the new page matches the current page like previously done without middleware. This is due to middleware being able to rewrite dynamically and can't be verified client-side without a data fetch which is skipped with shallow, so a shallow route change must always be treated as shallow. diff --git a/docs/testing.md b/docs/testing.md deleted file mode 100644 index 7439adb10139b..0000000000000 --- a/docs/testing.md +++ /dev/null @@ -1,541 +0,0 @@ ---- -description: Learn how to set up Next.js with three commonly used testing tools — Cypress, Playwright, Jest, and React Testing Library. ---- - -# Testing - -
    - Examples - -
    - -Learn how to set up Next.js with commonly used testing tools: [Cypress](https://nextjs.org/docs/testing#cypress), [Playwright](https://nextjs.org/docs/testing#playwright), and [Jest with React Testing Library](https://nextjs.org/docs/testing#jest-and-react-testing-library). - -## Cypress - -Cypress is a test runner used for **End-to-End (E2E)** and **Component Testing**. - -### Quickstart - -You can use `create-next-app` with the [with-cypress example](https://github.com/vercel/next.js/tree/canary/examples/with-cypress) to quickly get started. - -```bash -npx create-next-app@latest --example with-cypress with-cypress-app -``` - -### Manual setup - -To get started with Cypress, install the `cypress` package: - -```bash -npm install --save-dev cypress -``` - -Add Cypress to the `package.json` scripts field: - -```json -"scripts": { - "dev": "next dev", - "build": "next build", - "start": "next start", - "cypress": "cypress open", -} -``` - -Run Cypress for the first time to generate examples that use their recommended folder structure: - -```bash -npm run cypress -``` - -You can look through the generated examples and the [Writing Your First Test](https://docs.cypress.io/guides/getting-started/writing-your-first-test) section of the Cypress Documentation to help you get familiar with Cypress. - -### Should I use E2E or Component Tests? - -The [Cypress docs contain a guide](https://docs.cypress.io/guides/core-concepts/testing-types) on the difference between these two types of tests and when it is appropriate to use each. - -### Creating your first Cypress E2E test - -Assuming the following two Next.js pages: - -```jsx -// pages/index.js -import Link from 'next/link' - -export default function Home() { - return ( - - ) -} -``` - -```jsx -// pages/about.js -export default function About() { - return ( -
    -

    About Page

    - Homepage -
    - ) -} -``` - -Add a test to check your navigation is working correctly: - -```jsx -// cypress/e2e/app.cy.js - -describe('Navigation', () => { - it('should navigate to the about page', () => { - // Start from the index page - cy.visit('http://localhost:3000/') - - // Find a link with an href attribute containing "about" and click it - cy.get('a[href*="about"]').click() - - // The new url should include "/about" - cy.url().should('include', '/about') - - // The new page should contain an h1 with "About page" - cy.get('h1').contains('About Page') - }) -}) -``` - -You can use `cy.visit("/")` instead of `cy.visit("http://localhost:3000/")` if you add `baseUrl: 'http://localhost:3000'` to the `cypress.config.js` configuration file. - -### Creating your first Cypress component test - -Component tests build and mount a specific component without having to bundle your whole application or launch a server. This allows for more performant tests that still provide visual feedback and the same API used for Cypress E2E tests. - -> **Note:** Since component tests do not launch a Next.js server, capabilities like `` and `getServerSideProps` which rely on a server being available will not function out-of-the-box. See the [Cypress Next.js docs](https://docs.cypress.io/guides/component-testing/react/overview#Nextjs) for examples of getting these features working within component tests. - -Assuming the same components from the previous section, add a test to validate a component is rendering the expected output: - -```jsx -// pages/about.cy.js -import AboutPage from './about.js' - -describe('', () => { - it('should render and display expected content', () => { - // Mount the React component for the About page - cy.mount() - - // The new page should contain an h1 with "About page" - cy.get('h1').contains('About Page') - - // Validate that a link with the expected URL is present - // *Following* the link is better suited to an E2E test - cy.get('a[href="/"]').should('be.visible') - }) -}) -``` - -### Running your Cypress tests - -#### E2E Tests - -Since Cypress E2E tests are testing a real Next.js application they require the Next.js server to be running prior to starting Cypress. We recommend running your tests against your production code to more closely resemble how your application will behave. - -Run `npm run build` and `npm run start`, then run `npm run cypress -- --e2e` in another terminal window to start Cypress and run your E2E testing suite. - -> **Note:** Alternatively, you can install the `start-server-and-test` package and add it to the `package.json` scripts field: `"test": "start-server-and-test start http://localhost:3000 cypress"` to start the Next.js production server in conjunction with Cypress. Remember to rebuild your application after new changes. - -#### Component Tests - -Run `npm run cypress -- --component` to start Cypress and execute your component testing suite. - -### Getting ready for Continuous Integration (CI) - -You will have noticed that running Cypress so far has opened an interactive browser which is not ideal for CI environments. You can also run Cypress headlessly using the `cypress run` command: - -```json -// package.json - -"scripts": { - //... - "e2e": "start-server-and-test dev http://localhost:3000 \"cypress open --e2e\"", - "e2e:headless": "start-server-and-test dev http://localhost:3000 \"cypress run --e2e\"", - "component": "cypress open --component", - "component:headless": "cypress run --component" -} -``` - -You can learn more about Cypress and Continuous Integration from these resources: - -- [Cypress Continuous Integration Docs](https://docs.cypress.io/guides/continuous-integration/introduction) -- [Cypress GitHub Actions Guide](https://on.cypress.io/github-actions) -- [Official Cypress GitHub Action](https://github.com/cypress-io/github-action) - -## Playwright - -Playwright is a testing framework that lets you automate Chromium, Firefox, and WebKit with a single API. You can use it to write **End-to-End (E2E)** and **Integration** tests across all platforms. - -### Quickstart - -The fastest way to get started is to use `create-next-app` with the [with-playwright example](https://github.com/vercel/next.js/tree/canary/examples/with-playwright). This will create a Next.js project complete with Playwright all set up. - -```bash -npx create-next-app@latest --example with-playwright with-playwright-app -``` - -### Manual setup - -You can also use `npm init playwright` to add Playwright to an existing `NPM` project. - -To manually get started with Playwright, install the `@playwright/test` package: - -```bash -npm install --save-dev @playwright/test -``` - -Add Playwright to the `package.json` scripts field: - -```json -"scripts": { - "dev": "next dev", - "build": "next build", - "start": "next start", - "test:e2e": "playwright test", -} -``` - -### Creating your first Playwright end-to-end test - -Assuming the following two Next.js pages: - -```jsx -// pages/index.js -import Link from 'next/link' - -export default function Home() { - return ( - - ) -} -``` - -```jsx -// pages/about.js -export default function About() { - return ( -
    -

    About Page

    -
    - ) -} -``` - -Add a test to verify that your navigation is working correctly: - -```jsx -// e2e/example.spec.ts - -import { test, expect } from '@playwright/test' - -test('should navigate to the about page', async ({ page }) => { - // Start from the index page (the baseURL is set via the webServer in the playwright.config.ts) - await page.goto('http://localhost:3000/') - // Find an element with the text 'About Page' and click on it - await page.click('text=About') - // The new URL should be "/about" (baseURL is used there) - await expect(page).toHaveURL('http://localhost:3000/about') - // The new page should contain an h1 with "About Page" - await expect(page.locator('h1')).toContainText('About Page') -}) -``` - -You can use `page.goto("/")` instead of `page.goto("http://localhost:3000/")`, if you add [`"baseURL": "http://localhost:3000"`](https://playwright.dev/docs/api/class-testoptions#test-options-base-url) to the `playwright.config.ts` configuration file. - -### Running your Playwright tests - -Since Playwright is testing a real Next.js application, it requires the Next.js server to be running prior to starting Playwright. It is recommended to run your tests against your production code to more closely resemble how your application will behave. - -Run `npm run build` and `npm run start`, then run `npm run test:e2e` in another terminal window to run the Playwright tests. - -> **Note:** Alternatively, you can use the [`webServer`](https://playwright.dev/docs/test-advanced#launching-a-development-web-server-during-the-tests) feature to let Playwright start the development server and wait until it's fully available. - -### Running Playwright on Continuous Integration (CI) - -Playwright will by default run your tests in the [headless mode](https://playwright.dev/docs/ci#running-headed). To install all the Playwright dependencies, run `npx playwright install-deps`. - -You can learn more about Playwright and Continuous Integration from these resources: - -- [Getting started with Playwright](https://playwright.dev/docs/intro) -- [Use a development server](https://playwright.dev/docs/test-advanced#launching-a-development-web-server-during-the-tests) -- [Playwright on your CI provider](https://playwright.dev/docs/ci) - -## Jest and React Testing Library - -Jest and React Testing Library are frequently used together for **Unit Testing**. There are three ways you can start using Jest within your Next.js application: - -1. Using one of our [quickstart examples](https://nextjs.org/docs/testing#quickstart-2) -2. With the [Next.js Rust Compiler](https://nextjs.org/docs/testing#setting-up-jest-with-the-rust-compiler) -3. With [Babel](https://nextjs.org/docs/testing#setting-up-jest-with-babel) - -The following sections will go through how you can set up Jest with each of these options: - -### Quickstart - -You can use `create-next-app` with the [with-jest](https://github.com/vercel/next.js/tree/canary/examples/with-jest) example to quickly get started with Jest and React Testing Library: - -```bash -npx create-next-app@latest --example with-jest with-jest-app -``` - -### Setting up Jest (with the Rust Compiler) - -Since the release of [Next.js 12](https://nextjs.org/blog/next-12), Next.js now has built-in configuration for Jest. - -To set up Jest, install `jest`, `jest-environment-jsdom`, `@testing-library/react`, `@testing-library/jest-dom`: - -```bash -npm install --save-dev jest jest-environment-jsdom @testing-library/react @testing-library/jest-dom -``` - -Create a `jest.config.mjs` file in your project's root directory and add the following: - -```jsx -// jest.config.mjs -import nextJest from 'next/jest.js' - -const createJestConfig = nextJest({ - // Provide the path to your Next.js app to load next.config.js and .env files in your test environment - dir: './', -}) - -// Add any custom config to be passed to Jest -/** @type {import('jest').Config} */ -const config = { - // Add more setup options before each test is run - // setupFilesAfterEnv: ['/jest.setup.js'], - - testEnvironment: 'jest-environment-jsdom', -} - -// createJestConfig is exported this way to ensure that next/jest can load the Next.js config which is async -export default createJestConfig(config) -``` - -Under the hood, `next/jest` is automatically configuring Jest for you, including: - -- Setting up `transform` using [SWC](https://nextjs.org/docs/advanced-features/compiler) -- Auto mocking stylesheets (`.css`, `.module.css`, and their scss variants), image imports and [`next/font`](https://nextjs.org/docs/basic-features/font-optimization) -- Loading `.env` (and all variants) into `process.env` -- Ignoring `node_modules` from test resolving and transforms -- Ignoring `.next` from test resolving -- Loading `next.config.js` for flags that enable SWC transforms - -> **Note**: To test environment variables directly, load them manually in a separate setup script or in your `jest.config.js` file. For more information, please see [Test Environment Variables](https://nextjs.org/docs/basic-features/environment-variables#test-environment-variables). - -### Setting up Jest (with Babel) - -If you opt out of the [Rust Compiler](https://nextjs.org/docs/advanced-features/compiler), you will need to manually configure Jest and install `babel-jest` and `identity-obj-proxy` in addition to the packages above. - -Here are the recommended options to configure Jest for Next.js: - -```jsx -// jest.config.js -module.exports = { - collectCoverage: true, - // on node 14.x coverage provider v8 offers good speed and more or less good report - coverageProvider: 'v8', - collectCoverageFrom: [ - '**/*.{js,jsx,ts,tsx}', - '!**/*.d.ts', - '!**/node_modules/**', - '!/out/**', - '!/.next/**', - '!/*.config.js', - '!/coverage/**', - ], - moduleNameMapper: { - // Handle CSS imports (with CSS modules) - // https://jestjs.io/docs/webpack#mocking-css-modules - '^.+\\.module\\.(css|sass|scss)$': 'identity-obj-proxy', - - // Handle CSS imports (without CSS modules) - '^.+\\.(css|sass|scss)$': '/__mocks__/styleMock.js', - - // Handle image imports - // https://jestjs.io/docs/webpack#handling-static-assets - '^.+\\.(png|jpg|jpeg|gif|webp|avif|ico|bmp|svg)$/i': `/__mocks__/fileMock.js`, - - // Handle module aliases - '^@/components/(.*)$': '/components/$1', - }, - // Add more setup options before each test is run - // setupFilesAfterEnv: ['/jest.setup.js'], - testPathIgnorePatterns: ['/node_modules/', '/.next/'], - testEnvironment: 'jsdom', - transform: { - // Use babel-jest to transpile tests with the next/babel preset - // https://jestjs.io/docs/configuration#transform-objectstring-pathtotransformer--pathtotransformer-object - '^.+\\.(js|jsx|ts|tsx)$': ['babel-jest', { presets: ['next/babel'] }], - }, - transformIgnorePatterns: [ - '/node_modules/', - '^.+\\.module\\.(css|sass|scss)$', - ], -} -``` - -You can learn more about each configuration option in the [Jest docs](https://jestjs.io/docs/configuration). - -**Handling stylesheets and image imports** - -Stylesheets and images aren't used in the tests but importing them may cause errors, so they will need to be mocked. Create the mock files referenced in the configuration above - `fileMock.js` and `styleMock.js` - inside a `__mocks__` directory: - -```js -// __mocks__/fileMock.js -module.exports = { - src: '/img.jpg', - height: 24, - width: 24, - blurDataURL: 'data:image/png;base64,imagedata', -} -``` - -```js -// __mocks__/styleMock.js -module.exports = {} -``` - -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** - -`@testing-library/jest-dom` includes a set of convenient [custom matchers](https://github.com/testing-library/jest-dom#custom-matchers) such as `.toBeInTheDocument()` making it easier to write tests. You can import the custom matchers for every test by adding the following option to the Jest configuration file: - -```js -// jest.config.js -setupFilesAfterEnv: ['/jest.setup.js'] -``` - -Then, inside `jest.setup.js`, add the following import: - -```jsx -// jest.setup.js -import '@testing-library/jest-dom/extend-expect' -``` - -If you need to add more setup options before each test, it's common to add them to the `jest.setup.js` file above. - -**Optional: Absolute Imports and Module Path Aliases** - -If your project is using [Module Path Aliases](https://nextjs.org/docs/advanced-features/module-path-aliases), you will need to configure Jest to resolve the imports by matching the paths option in the `jsconfig.json` file with the `moduleNameMapper` option in the `jest.config.js` file. For example: - -```json -// tsconfig.json or jsconfig.json -{ - "compilerOptions": { - "baseUrl": ".", - "paths": { - "@/components/*": ["components/*"] - } - } -} -``` - -```jsx -// jest.config.js -moduleNameMapper: { - '^@/components/(.*)$': '/components/$1', -} -``` - -### Creating your tests: - -**Add a test script to package.json** - -Add the Jest executable in watch mode to the `package.json` scripts: - -```jsx -"scripts": { - "dev": "next dev", - "build": "next build", - "start": "next start", - "test": "jest --watch" -} -``` - -`jest --watch` will re-run tests when a file is changed. For more Jest CLI options, please refer to the [Jest Docs](https://jestjs.io/docs/cli#reference). - -**Create your first tests** - -Your project is now ready to run tests. Follow Jest's convention by adding tests to the `__tests__` folder in your project's root directory. - -For example, we can add a test to check if the `` component successfully renders a heading: - -```jsx -// __tests__/index.test.jsx - -import { render, screen } from '@testing-library/react' -import Home from '../pages/index' -import '@testing-library/jest-dom' - -describe('Home', () => { - it('renders a heading', () => { - render() - - const heading = screen.getByRole('heading', { - name: /welcome to next\.js!/i, - }) - - expect(heading).toBeInTheDocument() - }) -}) -``` - -Optionally, add a [snapshot test](https://jestjs.io/docs/snapshot-testing) to keep track of any unexpected changes to your `` component: - -```jsx -// __tests__/snapshot.js - -import { render } from '@testing-library/react' -import Home from '../pages/index' - -it('renders homepage unchanged', () => { - const { container } = render() - expect(container).toMatchSnapshot() -}) -``` - -> **Note**: Test files should not be included inside the pages directory because any files inside the pages directory are considered routes. - -**Running your test suite** - -Run `npm run test` to run your test suite. After your tests pass or fail, you will notice a list of interactive Jest commands that will be helpful as you add more tests. - -For further reading, you may find these resources helpful: - -- [Jest Docs](https://jestjs.io/docs/getting-started) -- [React Testing Library Docs](https://testing-library.com/docs/react-testing-library/intro/) -- [Testing Playground](https://testing-playground.com/) - use good testing practices to match elements. - -## Community Packages and Examples - -The Next.js community has created packages and articles you may find helpful: - -- [next-router-mock](https://github.com/scottrippey/next-router-mock) for Storybook. -- [Test Preview Vercel Deploys with Cypress](https://glebbahmutov.com/blog/develop-preview-test/) by Gleb Bahmutov. - -For more information on what to read next, we recommend: - - diff --git a/docs/upgrading.md b/docs/upgrading.md deleted file mode 100644 index 69596c4b4f35e..0000000000000 --- a/docs/upgrading.md +++ /dev/null @@ -1,576 +0,0 @@ ---- -description: Learn how to upgrade Next.js. ---- - -# Upgrade Guide - -## Upgrading from 12 to 13 - -To update to Next.js version 13, run the following command using your preferred package manager: - -```bash -npm i next@latest react@latest react-dom@latest eslint-config-next@latest -# or -yarn add next@latest react@latest react-dom@latest eslint-config-next@latest -# or -pnpm up next react react-dom eslint-config-next --latest -``` - -### v13 Summary - -- The [Supported Browsers](/docs/basic-features/supported-browsers-features.md) have been changed to drop Internet Explorer and target modern browsers. -- The minimum Node.js version has been bumped from 12.22.0 to 14.6.0, since 12.x has reached end-of-life. -- The minimum React version has been bumped from 17.0.2 to 18.2.0. -- The `swcMinify` configuration property was changed from `false` to `true`. See [Next.js Compiler](/docs/advanced-features/compiler.md) for more info. -- The `next/image` import was renamed to `next/legacy/image`. The `next/future/image` import was renamed to `next/image`. A [codemod is available](/docs/advanced-features/codemods.md#next-image-to-legacy-image) to safely and automatically rename your imports. -- The `next/link` child can no longer be ``. Add the `legacyBehavior` prop to use the legacy behavior or remove the `` to upgrade. A [codemod is available](/docs/advanced-features/codemods.md#new-link) to automatically upgrade your code. -- The `target` configuration property has been removed and superseded by [Output File Tracing](https://nextjs.org/docs/advanced-features/output-file-tracing). - -## Migrating shared features - -Next.js 13 introduces a new [`app` directory](https://beta.nextjs.org/docs/routing/fundamentals) with new features and conventions. However, upgrading to Next.js 13 does **not** require using the new [`app` directory](https://beta.nextjs.org/docs/routing/fundamentals#the-app-directory). - -You can continue using `pages` with new features that work in both directories, such as the updated [Image component](#image-component), [Link component](#link-component), [Script component](#script-component), and [Font optimization](#font-optimization). - -### `` Component - -Next.js 12 introduced many improvements to the Image Component with a temporary import: `next/future/image`. These improvements included less client-side JavaScript, easier ways to extend and style images, better accessibility, and native browser lazy loading. - -Starting in Next.js 13, this new behavior is now the default for `next/image`. - -There are two codemods to help you migrate to the new Image Component: - -- [next-image-to-legacy-image](/docs/advanced-features/codemods.md#next-image-to-legacy-image): This codemod will safely and automatically rename `next/image` imports to `next/legacy/image` to maintain the same behavior as Next.js 12. We recommend running this codemod to quickly update to Next.js 13 automatically. -- [next-image-experimental](/docs/advanced-features/codemods.md#next-image-experimental-experimental): After running the previous codemod, you can optionally run this experimental codemod to upgrade `next/legacy/image` to the new `next/image`, which will remove unused props and add inline styles. Please note this codemod is experimental and only covers static usage (such as ``) but not dynamic usage (such as ``). - -Alternatively, you can manually update by following the [migration guide](/docs/advanced-features/codemods.md#next-image-experimental-experimental) and also see the [legacy comparison](/docs/api-reference/next/legacy/image.md#comparison). - -### `` Component - -The [`` Component](/docs/api-reference/next/link.md) no longer requires manually adding an `` tag as a child. This behavior was added as an experimental option in [version 12.2](https://nextjs.org/blog/next-12-2) and is now the default. In Next.js 13, `` always renders `` and allows you to forward props to the underlying tag. - -For example: - -```jsx -import Link from 'next/link' - -// Next.js 12: `` has to be nested otherwise it's excluded - - About - - -// Next.js 13: `` always renders `` under the hood - - About - -``` - -To upgrade your links to Next.js 13, you can use the [`new-link` codemod](/docs/advanced-features/codemods.md#new-link). - -### `'); +var completeSegmentData1 = stringToPrecomputedChunk('")),te(a,b,c),a=a.responseState.generateStaticMarkup?!0:b.push("\x3c!--/$--\x3e"),a;if(0a.progressiveChunkSize)return d.rootSegmentID=a.nextSegmentId++,a.completedBoundaries.push(d),Cb(b,a.responseState,d.id),te(a,b,c),b.push("\x3c!--/$--\x3e");(c=a.resources.boundaryResources)&&d.resources.forEach(Dc,c);a.responseState.generateStaticMarkup||b.push("\x3c!--$--\x3e");c=d.completedSegments;if(1!==c.length)throw Error(l(391));ue(a,b,c[0]); +a=a.responseState.generateStaticMarkup?!0:b.push("\x3c!--/$--\x3e");return a}function ve(a,b,c){Db(b,a.responseState,c.formatContext,c.id);ue(a,b,c);return Eb(b,c.formatContext)} +function we(a,b,c){a.resources.boundaryResources=c.resources;for(var d=c.completedSegments,e=0;e"):b.push('">');return Bb(b,a)&&d} +function xe(a,b,c,d){if(2===d.status)return!0;var e=d.id;if(-1===e){if(-1===(d.id=c.rootSegmentID))throw Error(l(392));return ve(a,b,d)}ve(a,b,d);a=a.responseState;(c=0===a.streamingFormat)?(b.push(a.startInlineScript),0===(a.instructions&1)?(a.instructions|=1,b.push('$RS=function(a,b){a=document.getElementById(a);b=document.getElementById(b);for(a.parentNode.removeChild(a);a.firstChild;)b.parentNode.insertBefore(a.firstChild,b);b.parentNode.removeChild(b)};;$RS("')):b.push('$RS("')):b.push('');return b} +function se(a,b){try{var c,d=a.completedRootSegment;if(null!==d)if(0===a.pendingRootTasks)Ub(b,a.resources,a.responseState,0===a.allPendingTasks),ue(a,b,d),a.completedRootSegment=null,Bb(b,a.responseState);else return;else Wb(b,a.resources,a.responseState);var e=a.clientRenderedBoundaries;for(c=0;c"):!d.push('">')){a.destination=null;c++;e.splice(0,c);return}}e.splice(0,c);var A=a.completedBoundaries;for(c=0;c")),a.htmlChunks&&(b.push("")),b.push(null))}}function Cc(a){if(!1===a.flushScheduled&&0===a.pingedTasks.length&&null!==a.destination){var b=a.destination;a.flushScheduled=!0;se(a,b)}}function ye(a,b){try{var c=a.abortableTasks;if(0 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) { + args[_key - 1] = arguments[_key]; + } + + printWarning('warn', format, args); + } + } +} +function error(format) { + { + { + for (var _len2 = arguments.length, args = new Array(_len2 > 1 ? _len2 - 1 : 0), _key2 = 1; _key2 < _len2; _key2++) { + args[_key2 - 1] = arguments[_key2]; + } + + printWarning('error', format, args); + } + } +} + +function printWarning(level, format, args) { + // When changing this logic, you might want to also + // update consoleWithStackDev.www.js as well. + { + var ReactDebugCurrentFrame = ReactSharedInternals.ReactDebugCurrentFrame; + var stack = ReactDebugCurrentFrame.getStackAddendum(); + + if (stack !== '') { + format += '%s'; + args = args.concat([stack]); + } // eslint-disable-next-line react-internal/safe-string-coercion + + + var argsWithFormat = args.map(function (item) { + return String(item); + }); // Careful: RN currently depends on this prefix + + argsWithFormat.unshift('Warning: ' + format); // We intentionally don't use spread (or .apply) directly because it + // breaks IE9: https://github.com/facebook/react/issues/13610 + // eslint-disable-next-line react-internal/no-production-logging + + Function.prototype.apply.call(console[level], console, argsWithFormat); + } +} + +function scheduleWork(callback) { + callback(); +} +function beginWriting(destination) {} +function writeChunk(destination, chunk) { + writeChunkAndReturn(destination, chunk); +} +function writeChunkAndReturn(destination, chunk) { + return destination.push(chunk); +} +function completeWriting(destination) {} +function close(destination) { + destination.push(null); +} +function stringToChunk(content) { + return content; +} +function stringToPrecomputedChunk(content) { + return content; +} +function clonePrecomputedChunk(chunk) { + return chunk; +} +function closeWithError(destination, error) { + // $FlowFixMe[incompatible-call]: This is an Error object or the destination accepts other types. + destination.destroy(error); +} + +function _defineProperty(obj, key, value) { + if (key in obj) { + Object.defineProperty(obj, key, { + value: value, + enumerable: true, + configurable: true, + writable: true + }); + } else { + obj[key] = value; + } + + return obj; +} + +function _inheritsLoose(subClass, superClass) { + subClass.prototype = Object.create(superClass.prototype); + subClass.prototype.constructor = subClass; + subClass.__proto__ = superClass; +} + +var assign = Object.assign; + +/* + * The `'' + value` pattern (used in perf-sensitive code) throws for Symbol + * and Temporal.* types. See https://github.com/facebook/react/pull/22064. + * + * The functions in this module will throw an easier-to-understand, + * easier-to-debug exception with a clear errors message message explaining the + * problem. (Instead of a confusing exception thrown inside the implementation + * of the `value` object). + */ +// $FlowFixMe[incompatible-return] only called in DEV, so void return is not possible. +function typeName(value) { + { + // toStringTag is needed for namespaced types like Temporal.Instant + var hasToStringTag = typeof Symbol === 'function' && Symbol.toStringTag; + var type = hasToStringTag && value[Symbol.toStringTag] || value.constructor.name || 'Object'; // $FlowFixMe[incompatible-return] + + return type; + } +} // $FlowFixMe[incompatible-return] only called in DEV, so void return is not possible. + + +function willCoercionThrow(value) { + { + try { + testStringCoercion(value); + return false; + } catch (e) { + return true; + } + } +} + +function testStringCoercion(value) { + // If you ended up here by following an exception call stack, here's what's + // happened: you supplied an object or symbol value to React (as a prop, key, + // DOM attribute, CSS property, string ref, etc.) and when React tried to + // coerce it to a string using `'' + value`, an exception was thrown. + // + // The most common types that will cause this exception are `Symbol` instances + // and Temporal objects like `Temporal.Instant`. But any object that has a + // `valueOf` or `[Symbol.toPrimitive]` method that throws will also cause this + // exception. (Library authors do this to prevent users from using built-in + // numeric operators like `+` or comparison operators like `>=` because custom + // methods are needed to perform accurate arithmetic or comparison.) + // + // To fix the problem, coerce this object or symbol value to a string before + // passing it to React. The most reliable way is usually `String(value)`. + // + // To find which value is throwing, check the browser or debugger console. + // Before this exception was thrown, there should be `console.error` output + // that shows the type (Symbol, Temporal.PlainDate, etc.) that caused the + // problem and how that type was used: key, atrribute, input value prop, etc. + // In most cases, this console output also shows the component and its + // ancestor components where the exception happened. + // + // eslint-disable-next-line react-internal/safe-string-coercion + return '' + value; +} + +function checkAttributeStringCoercion(value, attributeName) { + { + if (willCoercionThrow(value)) { + error('The provided `%s` attribute is an unsupported type %s.' + ' This value must be coerced to a string before before using it here.', attributeName, typeName(value)); + + return testStringCoercion(value); // throw (to help callers find troubleshooting comments) + } + } +} +function checkCSSPropertyStringCoercion(value, propName) { + { + if (willCoercionThrow(value)) { + error('The provided `%s` CSS property is an unsupported type %s.' + ' This value must be coerced to a string before before using it here.', propName, typeName(value)); + + return testStringCoercion(value); // throw (to help callers find troubleshooting comments) + } + } +} +function checkHtmlStringCoercion(value) { + { + if (willCoercionThrow(value)) { + error('The provided HTML markup uses a value of unsupported type %s.' + ' This value must be coerced to a string before before using it here.', typeName(value)); + + return testStringCoercion(value); // throw (to help callers find troubleshooting comments) + } + } +} + +// ----------------------------------------------------------------------------- +var enableFloat = true; // Enables unstable_useMemoCache hook, intended as a compilation target for + +// $FlowFixMe[method-unbinding] +var hasOwnProperty = Object.prototype.hasOwnProperty; + +/* eslint-disable max-len */ + +var ATTRIBUTE_NAME_START_CHAR = ":A-Z_a-z\\u00C0-\\u00D6\\u00D8-\\u00F6\\u00F8-\\u02FF\\u0370-\\u037D\\u037F-\\u1FFF\\u200C-\\u200D\\u2070-\\u218F\\u2C00-\\u2FEF\\u3001-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFFD"; +/* eslint-enable max-len */ + +var ATTRIBUTE_NAME_CHAR = ATTRIBUTE_NAME_START_CHAR + "\\-.0-9\\u00B7\\u0300-\\u036F\\u203F-\\u2040"; +var VALID_ATTRIBUTE_NAME_REGEX = new RegExp('^[' + ATTRIBUTE_NAME_START_CHAR + '][' + ATTRIBUTE_NAME_CHAR + ']*$'); +var illegalAttributeNameCache = {}; +var validatedAttributeNameCache = {}; +function isAttributeNameSafe(attributeName) { + if (hasOwnProperty.call(validatedAttributeNameCache, attributeName)) { + return true; + } + + if (hasOwnProperty.call(illegalAttributeNameCache, attributeName)) { + return false; + } + + if (VALID_ATTRIBUTE_NAME_REGEX.test(attributeName)) { + validatedAttributeNameCache[attributeName] = true; + return true; + } + + illegalAttributeNameCache[attributeName] = true; + + { + error('Invalid attribute name: `%s`', attributeName); + } + + return false; +} + +/** + * CSS properties which accept numbers but are not in units of "px". + */ +var unitlessNumbers = new Set(['animationIterationCount', 'aspectRatio', 'borderImageOutset', 'borderImageSlice', 'borderImageWidth', 'boxFlex', 'boxFlexGroup', 'boxOrdinalGroup', 'columnCount', 'columns', 'flex', 'flexGrow', 'flexPositive', 'flexShrink', 'flexNegative', 'flexOrder', 'gridArea', 'gridRow', 'gridRowEnd', 'gridRowSpan', 'gridRowStart', 'gridColumn', 'gridColumnEnd', 'gridColumnSpan', 'gridColumnStart', 'fontWeight', 'lineClamp', 'lineHeight', 'opacity', 'order', 'orphans', 'scale', 'tabSize', 'widows', 'zIndex', 'zoom', 'fillOpacity', // SVG-related properties +'floodOpacity', 'stopOpacity', 'strokeDasharray', 'strokeDashoffset', 'strokeMiterlimit', 'strokeOpacity', 'strokeWidth', 'MozAnimationIterationCount', // Known Prefixed Properties +'MozBoxFlex', // TODO: Remove these since they shouldn't be used in modern code +'MozBoxFlexGroup', 'MozLineClamp', 'msAnimationIterationCount', 'msFlex', 'msZoom', 'msFlexGrow', 'msFlexNegative', 'msFlexOrder', 'msFlexPositive', 'msFlexShrink', 'msGridColumn', 'msGridColumnSpan', 'msGridRow', 'msGridRowSpan', 'WebkitAnimationIterationCount', 'WebkitBoxFlex', 'WebKitBoxFlexGroup', 'WebkitBoxOrdinalGroup', 'WebkitColumnCount', 'WebkitColumns', 'WebkitFlex', 'WebkitFlexGrow', 'WebkitFlexPositive', 'WebkitFlexShrink', 'WebkitLineClamp']); +function isUnitlessNumber (name) { + return unitlessNumbers.has(name); +} + +var aliases = new Map([['acceptCharset', 'accept-charset'], ['htmlFor', 'for'], ['httpEquiv', 'http-equiv'], // HTML and SVG attributes, but the SVG attribute is case sensitive.], +['crossOrigin', 'crossorigin'], // This is a list of all SVG attributes that need special casing. +// Regular attributes that just accept strings.], +['accentHeight', 'accent-height'], ['alignmentBaseline', 'alignment-baseline'], ['arabicForm', 'arabic-form'], ['baselineShift', 'baseline-shift'], ['capHeight', 'cap-height'], ['clipPath', 'clip-path'], ['clipRule', 'clip-rule'], ['colorInterpolation', 'color-interpolation'], ['colorInterpolationFilters', 'color-interpolation-filters'], ['colorProfile', 'color-profile'], ['colorRendering', 'color-rendering'], ['dominantBaseline', 'dominant-baseline'], ['enableBackground', 'enable-background'], ['fillOpacity', 'fill-opacity'], ['fillRule', 'fill-rule'], ['floodColor', 'flood-color'], ['floodOpacity', 'flood-opacity'], ['fontFamily', 'font-family'], ['fontSize', 'font-size'], ['fontSizeAdjust', 'font-size-adjust'], ['fontStretch', 'font-stretch'], ['fontStyle', 'font-style'], ['fontVariant', 'font-variant'], ['fontWeight', 'font-weight'], ['glyphName', 'glyph-name'], ['glyphOrientationHorizontal', 'glyph-orientation-horizontal'], ['glyphOrientationVertical', 'glyph-orientation-vertical'], ['horizAdvX', 'horiz-adv-x'], ['horizOriginX', 'horiz-origin-x'], ['imageRendering', 'image-rendering'], ['letterSpacing', 'letter-spacing'], ['lightingColor', 'lighting-color'], ['markerEnd', 'marker-end'], ['markerMid', 'marker-mid'], ['markerStart', 'marker-start'], ['overlinePosition', 'overline-position'], ['overlineThickness', 'overline-thickness'], ['paintOrder', 'paint-order'], ['panose-1', 'panose-1'], ['pointerEvents', 'pointer-events'], ['renderingIntent', 'rendering-intent'], ['shapeRendering', 'shape-rendering'], ['stopColor', 'stop-color'], ['stopOpacity', 'stop-opacity'], ['strikethroughPosition', 'strikethrough-position'], ['strikethroughThickness', 'strikethrough-thickness'], ['strokeDasharray', 'stroke-dasharray'], ['strokeDashoffset', 'stroke-dashoffset'], ['strokeLinecap', 'stroke-linecap'], ['strokeLinejoin', 'stroke-linejoin'], ['strokeMiterlimit', 'stroke-miterlimit'], ['strokeOpacity', 'stroke-opacity'], ['strokeWidth', 'stroke-width'], ['textAnchor', 'text-anchor'], ['textDecoration', 'text-decoration'], ['textRendering', 'text-rendering'], ['transformOrigin', 'transform-origin'], ['underlinePosition', 'underline-position'], ['underlineThickness', 'underline-thickness'], ['unicodeBidi', 'unicode-bidi'], ['unicodeRange', 'unicode-range'], ['unitsPerEm', 'units-per-em'], ['vAlphabetic', 'v-alphabetic'], ['vHanging', 'v-hanging'], ['vIdeographic', 'v-ideographic'], ['vMathematical', 'v-mathematical'], ['vectorEffect', 'vector-effect'], ['vertAdvY', 'vert-adv-y'], ['vertOriginX', 'vert-origin-x'], ['vertOriginY', 'vert-origin-y'], ['wordSpacing', 'word-spacing'], ['writingMode', 'writing-mode'], ['xmlnsXlink', 'xmlns:xlink'], ['xHeight', 'x-height']]); +function getAttributeAlias (name) { + return aliases.get(name) || name; +} + +var hasReadOnlyValue = { + button: true, + checkbox: true, + image: true, + hidden: true, + radio: true, + reset: true, + submit: true +}; +function checkControlledValueProps(tagName, props) { + { + if (!(hasReadOnlyValue[props.type] || props.onChange || props.onInput || props.readOnly || props.disabled || props.value == null)) { + error('You provided a `value` prop to a form field without an ' + '`onChange` handler. This will render a read-only field. If ' + 'the field should be mutable use `defaultValue`. Otherwise, ' + 'set either `onChange` or `readOnly`.'); + } + + if (!(props.onChange || props.readOnly || props.disabled || props.checked == null)) { + error('You provided a `checked` prop to a form field without an ' + '`onChange` handler. This will render a read-only field. If ' + 'the field should be mutable use `defaultChecked`. Otherwise, ' + 'set either `onChange` or `readOnly`.'); + } + } +} + +var ariaProperties = { + 'aria-current': 0, + // state + 'aria-description': 0, + 'aria-details': 0, + 'aria-disabled': 0, + // state + 'aria-hidden': 0, + // state + 'aria-invalid': 0, + // state + 'aria-keyshortcuts': 0, + 'aria-label': 0, + 'aria-roledescription': 0, + // Widget Attributes + 'aria-autocomplete': 0, + 'aria-checked': 0, + 'aria-expanded': 0, + 'aria-haspopup': 0, + 'aria-level': 0, + 'aria-modal': 0, + 'aria-multiline': 0, + 'aria-multiselectable': 0, + 'aria-orientation': 0, + 'aria-placeholder': 0, + 'aria-pressed': 0, + 'aria-readonly': 0, + 'aria-required': 0, + 'aria-selected': 0, + 'aria-sort': 0, + 'aria-valuemax': 0, + 'aria-valuemin': 0, + 'aria-valuenow': 0, + 'aria-valuetext': 0, + // Live Region Attributes + 'aria-atomic': 0, + 'aria-busy': 0, + 'aria-live': 0, + 'aria-relevant': 0, + // Drag-and-Drop Attributes + 'aria-dropeffect': 0, + 'aria-grabbed': 0, + // Relationship Attributes + 'aria-activedescendant': 0, + 'aria-colcount': 0, + 'aria-colindex': 0, + 'aria-colspan': 0, + 'aria-controls': 0, + 'aria-describedby': 0, + 'aria-errormessage': 0, + 'aria-flowto': 0, + 'aria-labelledby': 0, + 'aria-owns': 0, + 'aria-posinset': 0, + 'aria-rowcount': 0, + 'aria-rowindex': 0, + 'aria-rowspan': 0, + 'aria-setsize': 0 +}; + +var warnedProperties$1 = {}; +var rARIA$1 = new RegExp('^(aria)-[' + ATTRIBUTE_NAME_CHAR + ']*$'); +var rARIACamel$1 = new RegExp('^(aria)[A-Z][' + ATTRIBUTE_NAME_CHAR + ']*$'); + +function validateProperty$1(tagName, name) { + { + if (hasOwnProperty.call(warnedProperties$1, name) && warnedProperties$1[name]) { + return true; + } + + if (rARIACamel$1.test(name)) { + var ariaName = 'aria-' + name.slice(4).toLowerCase(); + var correctName = ariaProperties.hasOwnProperty(ariaName) ? ariaName : null; // If this is an aria-* attribute, but is not listed in the known DOM + // DOM properties, then it is an invalid aria-* attribute. + + if (correctName == null) { + error('Invalid ARIA attribute `%s`. ARIA attributes follow the pattern aria-* and must be lowercase.', name); + + warnedProperties$1[name] = true; + return true; + } // aria-* attributes should be lowercase; suggest the lowercase version. + + + if (name !== correctName) { + error('Invalid ARIA attribute `%s`. Did you mean `%s`?', name, correctName); + + warnedProperties$1[name] = true; + return true; + } + } + + if (rARIA$1.test(name)) { + var lowerCasedName = name.toLowerCase(); + var standardName = ariaProperties.hasOwnProperty(lowerCasedName) ? lowerCasedName : null; // If this is an aria-* attribute, but is not listed in the known DOM + // DOM properties, then it is an invalid aria-* attribute. + + if (standardName == null) { + warnedProperties$1[name] = true; + return false; + } // aria-* attributes should be lowercase; suggest the lowercase version. + + + if (name !== standardName) { + error('Unknown ARIA attribute `%s`. Did you mean `%s`?', name, standardName); + + warnedProperties$1[name] = true; + return true; + } + } + } + + return true; +} + +function validateProperties$2(type, props) { + { + var invalidProps = []; + + for (var key in props) { + var isValid = validateProperty$1(type, key); + + if (!isValid) { + invalidProps.push(key); + } + } + + var unknownPropString = invalidProps.map(function (prop) { + return '`' + prop + '`'; + }).join(', '); + + if (invalidProps.length === 1) { + error('Invalid aria prop %s on <%s> tag. ' + 'For details, see https://reactjs.org/link/invalid-aria-props', unknownPropString, type); + } else if (invalidProps.length > 1) { + error('Invalid aria props %s on <%s> tag. ' + 'For details, see https://reactjs.org/link/invalid-aria-props', unknownPropString, type); + } + } +} + +var didWarnValueNull = false; +function validateProperties$1(type, props) { + { + if (type !== 'input' && type !== 'textarea' && type !== 'select') { + return; + } + + if (props != null && props.value === null && !didWarnValueNull) { + didWarnValueNull = true; + + if (type === 'select' && props.multiple) { + error('`value` prop on `%s` should not be null. ' + 'Consider using an empty array when `multiple` is set to `true` ' + 'to clear the component or `undefined` for uncontrolled components.', type); + } else { + error('`value` prop on `%s` should not be null. ' + 'Consider using an empty string to clear the component or `undefined` ' + 'for uncontrolled components.', type); + } + } + } +} + +function isCustomElement(tagName, props) { + if (tagName.indexOf('-') === -1) { + return false; + } + + switch (tagName) { + // These are reserved SVG and MathML elements. + // We don't mind this list too much because we expect it to never grow. + // The alternative is to track the namespace in a few places which is convoluted. + // https://w3c.github.io/webcomponents/spec/custom/#custom-elements-core-concepts + case 'annotation-xml': + case 'color-profile': + case 'font-face': + case 'font-face-src': + case 'font-face-uri': + case 'font-face-format': + case 'font-face-name': + case 'missing-glyph': + return false; + + default: + return true; + } +} + +// When adding attributes to the HTML or SVG allowed attribute list, be sure to +// also add them to this module to ensure casing and incorrect name +// warnings. +var possibleStandardNames = { + // HTML + accept: 'accept', + acceptcharset: 'acceptCharset', + 'accept-charset': 'acceptCharset', + accesskey: 'accessKey', + action: 'action', + allowfullscreen: 'allowFullScreen', + alt: 'alt', + as: 'as', + async: 'async', + autocapitalize: 'autoCapitalize', + autocomplete: 'autoComplete', + autocorrect: 'autoCorrect', + autofocus: 'autoFocus', + autoplay: 'autoPlay', + autosave: 'autoSave', + capture: 'capture', + cellpadding: 'cellPadding', + cellspacing: 'cellSpacing', + challenge: 'challenge', + charset: 'charSet', + checked: 'checked', + children: 'children', + cite: 'cite', + class: 'className', + classid: 'classID', + classname: 'className', + cols: 'cols', + colspan: 'colSpan', + content: 'content', + contenteditable: 'contentEditable', + contextmenu: 'contextMenu', + controls: 'controls', + controlslist: 'controlsList', + coords: 'coords', + crossorigin: 'crossOrigin', + dangerouslysetinnerhtml: 'dangerouslySetInnerHTML', + data: 'data', + datetime: 'dateTime', + default: 'default', + defaultchecked: 'defaultChecked', + defaultvalue: 'defaultValue', + defer: 'defer', + dir: 'dir', + disabled: 'disabled', + disablepictureinpicture: 'disablePictureInPicture', + disableremoteplayback: 'disableRemotePlayback', + download: 'download', + draggable: 'draggable', + enctype: 'encType', + enterkeyhint: 'enterKeyHint', + fetchpriority: 'fetchPriority', + for: 'htmlFor', + form: 'form', + formmethod: 'formMethod', + formaction: 'formAction', + formenctype: 'formEncType', + formnovalidate: 'formNoValidate', + formtarget: 'formTarget', + frameborder: 'frameBorder', + headers: 'headers', + height: 'height', + hidden: 'hidden', + high: 'high', + href: 'href', + hreflang: 'hrefLang', + htmlfor: 'htmlFor', + httpequiv: 'httpEquiv', + 'http-equiv': 'httpEquiv', + icon: 'icon', + id: 'id', + imagesizes: 'imageSizes', + imagesrcset: 'imageSrcSet', + innerhtml: 'innerHTML', + inputmode: 'inputMode', + integrity: 'integrity', + is: 'is', + itemid: 'itemID', + itemprop: 'itemProp', + itemref: 'itemRef', + itemscope: 'itemScope', + itemtype: 'itemType', + keyparams: 'keyParams', + keytype: 'keyType', + kind: 'kind', + label: 'label', + lang: 'lang', + list: 'list', + loop: 'loop', + low: 'low', + manifest: 'manifest', + marginwidth: 'marginWidth', + marginheight: 'marginHeight', + max: 'max', + maxlength: 'maxLength', + media: 'media', + mediagroup: 'mediaGroup', + method: 'method', + min: 'min', + minlength: 'minLength', + multiple: 'multiple', + muted: 'muted', + name: 'name', + nomodule: 'noModule', + nonce: 'nonce', + novalidate: 'noValidate', + open: 'open', + optimum: 'optimum', + pattern: 'pattern', + placeholder: 'placeholder', + playsinline: 'playsInline', + poster: 'poster', + preload: 'preload', + profile: 'profile', + radiogroup: 'radioGroup', + readonly: 'readOnly', + referrerpolicy: 'referrerPolicy', + rel: 'rel', + required: 'required', + reversed: 'reversed', + role: 'role', + rows: 'rows', + rowspan: 'rowSpan', + sandbox: 'sandbox', + scope: 'scope', + scoped: 'scoped', + scrolling: 'scrolling', + seamless: 'seamless', + selected: 'selected', + shape: 'shape', + size: 'size', + sizes: 'sizes', + span: 'span', + spellcheck: 'spellCheck', + src: 'src', + srcdoc: 'srcDoc', + srclang: 'srcLang', + srcset: 'srcSet', + start: 'start', + step: 'step', + style: 'style', + summary: 'summary', + tabindex: 'tabIndex', + target: 'target', + title: 'title', + type: 'type', + usemap: 'useMap', + value: 'value', + width: 'width', + wmode: 'wmode', + wrap: 'wrap', + // SVG + about: 'about', + accentheight: 'accentHeight', + 'accent-height': 'accentHeight', + accumulate: 'accumulate', + additive: 'additive', + alignmentbaseline: 'alignmentBaseline', + 'alignment-baseline': 'alignmentBaseline', + allowreorder: 'allowReorder', + alphabetic: 'alphabetic', + amplitude: 'amplitude', + arabicform: 'arabicForm', + 'arabic-form': 'arabicForm', + ascent: 'ascent', + attributename: 'attributeName', + attributetype: 'attributeType', + autoreverse: 'autoReverse', + azimuth: 'azimuth', + basefrequency: 'baseFrequency', + baselineshift: 'baselineShift', + 'baseline-shift': 'baselineShift', + baseprofile: 'baseProfile', + bbox: 'bbox', + begin: 'begin', + bias: 'bias', + by: 'by', + calcmode: 'calcMode', + capheight: 'capHeight', + 'cap-height': 'capHeight', + clip: 'clip', + clippath: 'clipPath', + 'clip-path': 'clipPath', + clippathunits: 'clipPathUnits', + cliprule: 'clipRule', + 'clip-rule': 'clipRule', + color: 'color', + colorinterpolation: 'colorInterpolation', + 'color-interpolation': 'colorInterpolation', + colorinterpolationfilters: 'colorInterpolationFilters', + 'color-interpolation-filters': 'colorInterpolationFilters', + colorprofile: 'colorProfile', + 'color-profile': 'colorProfile', + colorrendering: 'colorRendering', + 'color-rendering': 'colorRendering', + contentscripttype: 'contentScriptType', + contentstyletype: 'contentStyleType', + cursor: 'cursor', + cx: 'cx', + cy: 'cy', + d: 'd', + datatype: 'datatype', + decelerate: 'decelerate', + descent: 'descent', + diffuseconstant: 'diffuseConstant', + direction: 'direction', + display: 'display', + divisor: 'divisor', + dominantbaseline: 'dominantBaseline', + 'dominant-baseline': 'dominantBaseline', + dur: 'dur', + dx: 'dx', + dy: 'dy', + edgemode: 'edgeMode', + elevation: 'elevation', + enablebackground: 'enableBackground', + 'enable-background': 'enableBackground', + end: 'end', + exponent: 'exponent', + externalresourcesrequired: 'externalResourcesRequired', + fill: 'fill', + fillopacity: 'fillOpacity', + 'fill-opacity': 'fillOpacity', + fillrule: 'fillRule', + 'fill-rule': 'fillRule', + filter: 'filter', + filterres: 'filterRes', + filterunits: 'filterUnits', + floodopacity: 'floodOpacity', + 'flood-opacity': 'floodOpacity', + floodcolor: 'floodColor', + 'flood-color': 'floodColor', + focusable: 'focusable', + fontfamily: 'fontFamily', + 'font-family': 'fontFamily', + fontsize: 'fontSize', + 'font-size': 'fontSize', + fontsizeadjust: 'fontSizeAdjust', + 'font-size-adjust': 'fontSizeAdjust', + fontstretch: 'fontStretch', + 'font-stretch': 'fontStretch', + fontstyle: 'fontStyle', + 'font-style': 'fontStyle', + fontvariant: 'fontVariant', + 'font-variant': 'fontVariant', + fontweight: 'fontWeight', + 'font-weight': 'fontWeight', + format: 'format', + from: 'from', + fx: 'fx', + fy: 'fy', + g1: 'g1', + g2: 'g2', + glyphname: 'glyphName', + 'glyph-name': 'glyphName', + glyphorientationhorizontal: 'glyphOrientationHorizontal', + 'glyph-orientation-horizontal': 'glyphOrientationHorizontal', + glyphorientationvertical: 'glyphOrientationVertical', + 'glyph-orientation-vertical': 'glyphOrientationVertical', + glyphref: 'glyphRef', + gradienttransform: 'gradientTransform', + gradientunits: 'gradientUnits', + hanging: 'hanging', + horizadvx: 'horizAdvX', + 'horiz-adv-x': 'horizAdvX', + horizoriginx: 'horizOriginX', + 'horiz-origin-x': 'horizOriginX', + ideographic: 'ideographic', + imagerendering: 'imageRendering', + 'image-rendering': 'imageRendering', + in2: 'in2', + in: 'in', + inlist: 'inlist', + intercept: 'intercept', + k1: 'k1', + k2: 'k2', + k3: 'k3', + k4: 'k4', + k: 'k', + kernelmatrix: 'kernelMatrix', + kernelunitlength: 'kernelUnitLength', + kerning: 'kerning', + keypoints: 'keyPoints', + keysplines: 'keySplines', + keytimes: 'keyTimes', + lengthadjust: 'lengthAdjust', + letterspacing: 'letterSpacing', + 'letter-spacing': 'letterSpacing', + lightingcolor: 'lightingColor', + 'lighting-color': 'lightingColor', + limitingconeangle: 'limitingConeAngle', + local: 'local', + markerend: 'markerEnd', + 'marker-end': 'markerEnd', + markerheight: 'markerHeight', + markermid: 'markerMid', + 'marker-mid': 'markerMid', + markerstart: 'markerStart', + 'marker-start': 'markerStart', + markerunits: 'markerUnits', + markerwidth: 'markerWidth', + mask: 'mask', + maskcontentunits: 'maskContentUnits', + maskunits: 'maskUnits', + mathematical: 'mathematical', + mode: 'mode', + numoctaves: 'numOctaves', + offset: 'offset', + opacity: 'opacity', + operator: 'operator', + order: 'order', + orient: 'orient', + orientation: 'orientation', + origin: 'origin', + overflow: 'overflow', + overlineposition: 'overlinePosition', + 'overline-position': 'overlinePosition', + overlinethickness: 'overlineThickness', + 'overline-thickness': 'overlineThickness', + paintorder: 'paintOrder', + 'paint-order': 'paintOrder', + panose1: 'panose1', + 'panose-1': 'panose1', + pathlength: 'pathLength', + patterncontentunits: 'patternContentUnits', + patterntransform: 'patternTransform', + patternunits: 'patternUnits', + pointerevents: 'pointerEvents', + 'pointer-events': 'pointerEvents', + points: 'points', + pointsatx: 'pointsAtX', + pointsaty: 'pointsAtY', + pointsatz: 'pointsAtZ', + prefix: 'prefix', + preservealpha: 'preserveAlpha', + preserveaspectratio: 'preserveAspectRatio', + primitiveunits: 'primitiveUnits', + property: 'property', + r: 'r', + radius: 'radius', + refx: 'refX', + refy: 'refY', + renderingintent: 'renderingIntent', + 'rendering-intent': 'renderingIntent', + repeatcount: 'repeatCount', + repeatdur: 'repeatDur', + requiredextensions: 'requiredExtensions', + requiredfeatures: 'requiredFeatures', + resource: 'resource', + restart: 'restart', + result: 'result', + results: 'results', + rotate: 'rotate', + rx: 'rx', + ry: 'ry', + scale: 'scale', + security: 'security', + seed: 'seed', + shaperendering: 'shapeRendering', + 'shape-rendering': 'shapeRendering', + slope: 'slope', + spacing: 'spacing', + specularconstant: 'specularConstant', + specularexponent: 'specularExponent', + speed: 'speed', + spreadmethod: 'spreadMethod', + startoffset: 'startOffset', + stddeviation: 'stdDeviation', + stemh: 'stemh', + stemv: 'stemv', + stitchtiles: 'stitchTiles', + stopcolor: 'stopColor', + 'stop-color': 'stopColor', + stopopacity: 'stopOpacity', + 'stop-opacity': 'stopOpacity', + strikethroughposition: 'strikethroughPosition', + 'strikethrough-position': 'strikethroughPosition', + strikethroughthickness: 'strikethroughThickness', + 'strikethrough-thickness': 'strikethroughThickness', + string: 'string', + stroke: 'stroke', + strokedasharray: 'strokeDasharray', + 'stroke-dasharray': 'strokeDasharray', + strokedashoffset: 'strokeDashoffset', + 'stroke-dashoffset': 'strokeDashoffset', + strokelinecap: 'strokeLinecap', + 'stroke-linecap': 'strokeLinecap', + strokelinejoin: 'strokeLinejoin', + 'stroke-linejoin': 'strokeLinejoin', + strokemiterlimit: 'strokeMiterlimit', + 'stroke-miterlimit': 'strokeMiterlimit', + strokewidth: 'strokeWidth', + 'stroke-width': 'strokeWidth', + strokeopacity: 'strokeOpacity', + 'stroke-opacity': 'strokeOpacity', + suppresscontenteditablewarning: 'suppressContentEditableWarning', + suppresshydrationwarning: 'suppressHydrationWarning', + surfacescale: 'surfaceScale', + systemlanguage: 'systemLanguage', + tablevalues: 'tableValues', + targetx: 'targetX', + targety: 'targetY', + textanchor: 'textAnchor', + 'text-anchor': 'textAnchor', + textdecoration: 'textDecoration', + 'text-decoration': 'textDecoration', + textlength: 'textLength', + textrendering: 'textRendering', + 'text-rendering': 'textRendering', + to: 'to', + transform: 'transform', + transformorigin: 'transformOrigin', + 'transform-origin': 'transformOrigin', + typeof: 'typeof', + u1: 'u1', + u2: 'u2', + underlineposition: 'underlinePosition', + 'underline-position': 'underlinePosition', + underlinethickness: 'underlineThickness', + 'underline-thickness': 'underlineThickness', + unicode: 'unicode', + unicodebidi: 'unicodeBidi', + 'unicode-bidi': 'unicodeBidi', + unicoderange: 'unicodeRange', + 'unicode-range': 'unicodeRange', + unitsperem: 'unitsPerEm', + 'units-per-em': 'unitsPerEm', + unselectable: 'unselectable', + valphabetic: 'vAlphabetic', + 'v-alphabetic': 'vAlphabetic', + values: 'values', + vectoreffect: 'vectorEffect', + 'vector-effect': 'vectorEffect', + version: 'version', + vertadvy: 'vertAdvY', + 'vert-adv-y': 'vertAdvY', + vertoriginx: 'vertOriginX', + 'vert-origin-x': 'vertOriginX', + vertoriginy: 'vertOriginY', + 'vert-origin-y': 'vertOriginY', + vhanging: 'vHanging', + 'v-hanging': 'vHanging', + videographic: 'vIdeographic', + 'v-ideographic': 'vIdeographic', + viewbox: 'viewBox', + viewtarget: 'viewTarget', + visibility: 'visibility', + vmathematical: 'vMathematical', + 'v-mathematical': 'vMathematical', + vocab: 'vocab', + widths: 'widths', + wordspacing: 'wordSpacing', + 'word-spacing': 'wordSpacing', + writingmode: 'writingMode', + 'writing-mode': 'writingMode', + x1: 'x1', + x2: 'x2', + x: 'x', + xchannelselector: 'xChannelSelector', + xheight: 'xHeight', + 'x-height': 'xHeight', + xlinkactuate: 'xlinkActuate', + 'xlink:actuate': 'xlinkActuate', + xlinkarcrole: 'xlinkArcrole', + 'xlink:arcrole': 'xlinkArcrole', + xlinkhref: 'xlinkHref', + 'xlink:href': 'xlinkHref', + xlinkrole: 'xlinkRole', + 'xlink:role': 'xlinkRole', + xlinkshow: 'xlinkShow', + 'xlink:show': 'xlinkShow', + xlinktitle: 'xlinkTitle', + 'xlink:title': 'xlinkTitle', + xlinktype: 'xlinkType', + 'xlink:type': 'xlinkType', + xmlbase: 'xmlBase', + 'xml:base': 'xmlBase', + xmllang: 'xmlLang', + 'xml:lang': 'xmlLang', + xmlns: 'xmlns', + 'xml:space': 'xmlSpace', + xmlnsxlink: 'xmlnsXlink', + 'xmlns:xlink': 'xmlnsXlink', + xmlspace: 'xmlSpace', + y1: 'y1', + y2: 'y2', + y: 'y', + ychannelselector: 'yChannelSelector', + z: 'z', + zoomandpan: 'zoomAndPan' +}; + +var warnedProperties = {}; +var EVENT_NAME_REGEX = /^on./; +var INVALID_EVENT_NAME_REGEX = /^on[^A-Z]/; +var rARIA = new RegExp('^(aria)-[' + ATTRIBUTE_NAME_CHAR + ']*$') ; +var rARIACamel = new RegExp('^(aria)[A-Z][' + ATTRIBUTE_NAME_CHAR + ']*$') ; + +function validateProperty(tagName, name, value, eventRegistry) { + { + if (hasOwnProperty.call(warnedProperties, name) && warnedProperties[name]) { + return true; + } + + var lowerCasedName = name.toLowerCase(); + + if (lowerCasedName === 'onfocusin' || lowerCasedName === 'onfocusout') { + error('React uses onFocus and onBlur instead of onFocusIn and onFocusOut. ' + 'All React events are normalized to bubble, so onFocusIn and onFocusOut ' + 'are not needed/supported by React.'); + + warnedProperties[name] = true; + return true; + } + + { + // Actions are special because unlike events they can have other value types. + if (typeof value === 'function') { + if (tagName === 'form' && name === 'action') { + return true; + } + + if (tagName === 'input' && name === 'formAction') { + return true; + } + + if (tagName === 'button' && name === 'formAction') { + return true; + } + } + } // We can't rely on the event system being injected on the server. + + + if (eventRegistry != null) { + var registrationNameDependencies = eventRegistry.registrationNameDependencies, + possibleRegistrationNames = eventRegistry.possibleRegistrationNames; + + if (registrationNameDependencies.hasOwnProperty(name)) { + return true; + } + + var registrationName = possibleRegistrationNames.hasOwnProperty(lowerCasedName) ? possibleRegistrationNames[lowerCasedName] : null; + + if (registrationName != null) { + error('Invalid event handler property `%s`. Did you mean `%s`?', name, registrationName); + + warnedProperties[name] = true; + return true; + } + + if (EVENT_NAME_REGEX.test(name)) { + error('Unknown event handler property `%s`. It will be ignored.', name); + + warnedProperties[name] = true; + return true; + } + } else if (EVENT_NAME_REGEX.test(name)) { + // If no event plugins have been injected, we are in a server environment. + // So we can't tell if the event name is correct for sure, but we can filter + // out known bad ones like `onclick`. We can't suggest a specific replacement though. + if (INVALID_EVENT_NAME_REGEX.test(name)) { + error('Invalid event handler property `%s`. ' + 'React events use the camelCase naming convention, for example `onClick`.', name); + } + + warnedProperties[name] = true; + return true; + } // Let the ARIA attribute hook validate ARIA attributes + + + if (rARIA.test(name) || rARIACamel.test(name)) { + return true; + } + + if (lowerCasedName === 'innerhtml') { + error('Directly setting property `innerHTML` is not permitted. ' + 'For more information, lookup documentation on `dangerouslySetInnerHTML`.'); + + warnedProperties[name] = true; + return true; + } + + if (lowerCasedName === 'aria') { + error('The `aria` attribute is reserved for future use in React. ' + 'Pass individual `aria-` attributes instead.'); + + warnedProperties[name] = true; + return true; + } + + if (lowerCasedName === 'is' && value !== null && value !== undefined && typeof value !== 'string') { + error('Received a `%s` for a string attribute `is`. If this is expected, cast ' + 'the value to a string.', typeof value); + + warnedProperties[name] = true; + return true; + } + + if (typeof value === 'number' && isNaN(value)) { + error('Received NaN for the `%s` attribute. If this is expected, cast ' + 'the value to a string.', name); + + warnedProperties[name] = true; + return true; + } // Known attributes should match the casing specified in the property config. + + + if (possibleStandardNames.hasOwnProperty(lowerCasedName)) { + var standardName = possibleStandardNames[lowerCasedName]; + + if (standardName !== name) { + error('Invalid DOM property `%s`. Did you mean `%s`?', name, standardName); + + warnedProperties[name] = true; + return true; + } + } else if (name !== lowerCasedName) { + // Unknown attributes should have lowercase casing since that's how they + // will be cased anyway with server rendering. + error('React does not recognize the `%s` prop on a DOM element. If you ' + 'intentionally want it to appear in the DOM as a custom ' + 'attribute, spell it as lowercase `%s` instead. ' + 'If you accidentally passed it from a parent component, remove ' + 'it from the DOM element.', name, lowerCasedName); + + warnedProperties[name] = true; + return true; + } // Now that we've validated casing, do not validate + // data types for reserved props + + + switch (name) { + case 'dangerouslySetInnerHTML': + case 'children': + case 'style': + case 'suppressContentEditableWarning': + case 'suppressHydrationWarning': + case 'defaultValue': // Reserved + + case 'defaultChecked': + case 'innerHTML': + { + return true; + } + + case 'innerText': // Properties + + case 'textContent': + { + return true; + } + + } + + switch (typeof value) { + case 'boolean': + { + switch (name) { + case 'autoFocus': + case 'checked': + case 'multiple': + case 'muted': + case 'selected': + case 'contentEditable': + case 'spellCheck': + case 'draggable': + case 'value': + case 'autoReverse': + case 'externalResourcesRequired': + case 'focusable': + case 'preserveAlpha': + case 'allowFullScreen': + case 'async': + case 'autoPlay': + case 'controls': + case 'default': + case 'defer': + case 'disabled': + case 'disablePictureInPicture': + case 'disableRemotePlayback': + case 'formNoValidate': + case 'hidden': + case 'loop': + case 'noModule': + case 'noValidate': + case 'open': + case 'playsInline': + case 'readOnly': + case 'required': + case 'reversed': + case 'scoped': + case 'seamless': + case 'itemScope': + case 'capture': + case 'download': + { + // Boolean properties can accept boolean values + return true; + } + + default: + { + var prefix = name.toLowerCase().slice(0, 5); + + if (prefix === 'data-' || prefix === 'aria-') { + return true; + } + + if (value) { + error('Received `%s` for a non-boolean attribute `%s`.\n\n' + 'If you want to write it to the DOM, pass a string instead: ' + '%s="%s" or %s={value.toString()}.', value, name, name, value, name); + } else { + error('Received `%s` for a non-boolean attribute `%s`.\n\n' + 'If you want to write it to the DOM, pass a string instead: ' + '%s="%s" or %s={value.toString()}.\n\n' + 'If you used to conditionally omit it with %s={condition && value}, ' + 'pass %s={condition ? value : undefined} instead.', value, name, name, value, name, name, name); + } + + warnedProperties[name] = true; + return true; + } + } + } + + case 'function': + case 'symbol': + // eslint-disable-line + // Warn when a known attribute is a bad type + warnedProperties[name] = true; + return false; + + case 'string': + { + // Warn when passing the strings 'false' or 'true' into a boolean prop + if (value === 'false' || value === 'true') { + switch (name) { + case 'checked': + case 'selected': + case 'multiple': + case 'muted': + case 'allowFullScreen': + case 'async': + case 'autoPlay': + case 'controls': + case 'default': + case 'defer': + case 'disabled': + case 'disablePictureInPicture': + case 'disableRemotePlayback': + case 'formNoValidate': + case 'hidden': + case 'loop': + case 'noModule': + case 'noValidate': + case 'open': + case 'playsInline': + case 'readOnly': + case 'required': + case 'reversed': + case 'scoped': + case 'seamless': + case 'itemScope': + { + break; + } + + default: + { + return true; + } + } + + error('Received the string `%s` for the boolean attribute `%s`. ' + '%s ' + 'Did you mean %s={%s}?', value, name, value === 'false' ? 'The browser will interpret it as a truthy value.' : 'Although this works, it will not work as expected if you pass the string "false".', name, value); + + warnedProperties[name] = true; + return true; + } + } + } + + return true; + } +} + +function warnUnknownProperties(type, props, eventRegistry) { + { + var unknownProps = []; + + for (var key in props) { + var isValid = validateProperty(type, key, props[key], eventRegistry); + + if (!isValid) { + unknownProps.push(key); + } + } + + var unknownPropString = unknownProps.map(function (prop) { + return '`' + prop + '`'; + }).join(', '); + + if (unknownProps.length === 1) { + error('Invalid value for prop %s on <%s> tag. Either remove it from the element, ' + 'or pass a string or number value to keep it in the DOM. ' + 'For details, see https://reactjs.org/link/attribute-behavior ', unknownPropString, type); + } else if (unknownProps.length > 1) { + error('Invalid values for props %s on <%s> tag. Either remove them from the element, ' + 'or pass a string or number value to keep them in the DOM. ' + 'For details, see https://reactjs.org/link/attribute-behavior ', unknownPropString, type); + } + } +} + +function validateProperties(type, props, eventRegistry) { + if (isCustomElement(type) || typeof props.is === 'string') { + return; + } + + warnUnknownProperties(type, props, eventRegistry); +} + +// 'msTransform' is correct, but the other prefixes should be capitalized +var badVendoredStyleNamePattern = /^(?:webkit|moz|o)[A-Z]/; +var msPattern$1 = /^-ms-/; +var hyphenPattern = /-(.)/g; // style values shouldn't contain a semicolon + +var badStyleValueWithSemicolonPattern = /;\s*$/; +var warnedStyleNames = {}; +var warnedStyleValues = {}; +var warnedForNaNValue = false; +var warnedForInfinityValue = false; + +function camelize(string) { + return string.replace(hyphenPattern, function (_, character) { + return character.toUpperCase(); + }); +} + +function warnHyphenatedStyleName(name) { + { + if (warnedStyleNames.hasOwnProperty(name) && warnedStyleNames[name]) { + return; + } + + warnedStyleNames[name] = true; + + error('Unsupported style property %s. Did you mean %s?', name, // As Andi Smith suggests + // (http://www.andismith.com/blog/2012/02/modernizr-prefixed/), an `-ms` prefix + // is converted to lowercase `ms`. + camelize(name.replace(msPattern$1, 'ms-'))); + } +} + +function warnBadVendoredStyleName(name) { + { + if (warnedStyleNames.hasOwnProperty(name) && warnedStyleNames[name]) { + return; + } + + warnedStyleNames[name] = true; + + error('Unsupported vendor-prefixed style property %s. Did you mean %s?', name, name.charAt(0).toUpperCase() + name.slice(1)); + } +} + +function warnStyleValueWithSemicolon(name, value) { + { + if (warnedStyleValues.hasOwnProperty(value) && warnedStyleValues[value]) { + return; + } + + warnedStyleValues[value] = true; + + error("Style property values shouldn't contain a semicolon. " + 'Try "%s: %s" instead.', name, value.replace(badStyleValueWithSemicolonPattern, '')); + } +} + +function warnStyleValueIsNaN(name, value) { + { + if (warnedForNaNValue) { + return; + } + + warnedForNaNValue = true; + + error('`NaN` is an invalid value for the `%s` css style property.', name); + } +} + +function warnStyleValueIsInfinity(name, value) { + { + if (warnedForInfinityValue) { + return; + } + + warnedForInfinityValue = true; + + error('`Infinity` is an invalid value for the `%s` css style property.', name); + } +} + +function warnValidStyle(name, value) { + { + if (name.indexOf('-') > -1) { + warnHyphenatedStyleName(name); + } else if (badVendoredStyleNamePattern.test(name)) { + warnBadVendoredStyleName(name); + } else if (badStyleValueWithSemicolonPattern.test(value)) { + warnStyleValueWithSemicolon(name, value); + } + + if (typeof value === 'number') { + if (isNaN(value)) { + warnStyleValueIsNaN(name); + } else if (!isFinite(value)) { + warnStyleValueIsInfinity(name); + } + } + } +} + +// code copied and modified from escape-html +var matchHtmlRegExp = /["'&<>]/; +/** + * Escapes special characters and HTML entities in a given html string. + * + * @param {string} string HTML string to escape for later insertion + * @return {string} + * @public + */ + +function escapeHtml(string) { + { + checkHtmlStringCoercion(string); + } + + var str = '' + string; + var match = matchHtmlRegExp.exec(str); + + if (!match) { + return str; + } + + var escape; + var html = ''; + var index; + var lastIndex = 0; + + for (index = match.index; index < str.length; index++) { + switch (str.charCodeAt(index)) { + case 34: + // " + escape = '"'; + break; + + case 38: + // & + escape = '&'; + break; + + case 39: + // ' + escape = '''; // modified from escape-html; used to be ''' + + break; + + case 60: + // < + escape = '<'; + break; + + case 62: + // > + escape = '>'; + break; + + default: + continue; + } + + if (lastIndex !== index) { + html += str.slice(lastIndex, index); + } + + lastIndex = index + 1; + html += escape; + } + + return lastIndex !== index ? html + str.slice(lastIndex, index) : html; +} // end code copied and modified from escape-html + +/** + * Escapes text to prevent scripting attacks. + * + * @param {*} text Text value to escape. + * @return {string} An escaped string. + */ + + +function escapeTextForBrowser(text) { + if (typeof text === 'boolean' || typeof text === 'number') { + // this shortcircuit helps perf for types that we know will never have + // special characters, especially given that this function is used often + // for numeric dom ids. + return '' + text; + } + + return escapeHtml(text); +} + +var uppercasePattern = /([A-Z])/g; +var msPattern = /^ms-/; +/** + * Hyphenates a camelcased CSS property name, for example: + * + * > hyphenateStyleName('backgroundColor') + * < "background-color" + * > hyphenateStyleName('MozTransition') + * < "-moz-transition" + * > hyphenateStyleName('msTransition') + * < "-ms-transition" + * + * As Modernizr suggests (http://modernizr.com/docs/#prefixed), an `ms` prefix + * is converted to `-ms-`. + */ + +function hyphenateStyleName(name) { + return name.replace(uppercasePattern, '-$1').toLowerCase().replace(msPattern, '-ms-'); +} + +// and any newline or tab are filtered out as if they're not part of the URL. +// https://url.spec.whatwg.org/#url-parsing +// Tab or newline are defined as \r\n\t: +// https://infra.spec.whatwg.org/#ascii-tab-or-newline +// A C0 control is a code point in the range \u0000 NULL to \u001F +// INFORMATION SEPARATOR ONE, inclusive: +// https://infra.spec.whatwg.org/#c0-control-or-space + +/* eslint-disable max-len */ + +var isJavaScriptProtocol = /^[\u0000-\u001F ]*j[\r\n\t]*a[\r\n\t]*v[\r\n\t]*a[\r\n\t]*s[\r\n\t]*c[\r\n\t]*r[\r\n\t]*i[\r\n\t]*p[\r\n\t]*t[\r\n\t]*\:/i; +var didWarn = false; + +function sanitizeURL(url) { + // We should never have symbols here because they get filtered out elsewhere. + // eslint-disable-next-line react-internal/safe-string-coercion + var stringifiedURL = '' + url; + + { + if (!didWarn && isJavaScriptProtocol.test(stringifiedURL)) { + didWarn = true; + + error('A future version of React will block javascript: URLs as a security precaution. ' + 'Use event handlers instead if you can. If you need to generate unsafe HTML try ' + 'using dangerouslySetInnerHTML instead. React was passed %s.', JSON.stringify(stringifiedURL)); + } + } + + return url; +} + +var isArrayImpl = Array.isArray; // eslint-disable-next-line no-redeclare + +function isArray(a) { + return isArrayImpl(a); +} + +// The build script is at scripts/rollup/generate-inline-fizz-runtime.js. +// Run `yarn generate-inline-fizz-runtime` to generate. +var clientRenderBoundary = '$RX=function(b,c,d,e){var a=document.getElementById(b);a&&(b=a.previousSibling,b.data="$!",a=a.dataset,c&&(a.dgst=c),d&&(a.msg=d),e&&(a.stck=e),b._reactRetry&&b._reactRetry())};'; +var completeBoundary = '$RC=function(b,c,e){c=document.getElementById(c);c.parentNode.removeChild(c);var a=document.getElementById(b);if(a){b=a.previousSibling;if(e)b.data="$!",a.setAttribute("data-dgst",e);else{e=b.parentNode;a=b.nextSibling;var f=0;do{if(a&&8===a.nodeType){var d=a.data;if("/$"===d)if(0===f)break;else f--;else"$"!==d&&"$?"!==d&&"$!"!==d||f++}d=a.nextSibling;e.removeChild(a);a=d}while(a);for(;c.firstChild;)e.insertBefore(c.firstChild,a);b.data="$"}b._reactRetry&&b._reactRetry()}};'; +var completeBoundaryWithStyles = '$RM=new Map;\n$RR=function(r,t,w){for(var u=$RC,n=$RM,p=new Map,q=document,g,b,h=q.querySelectorAll("link[data-precedence],style[data-precedence]"),v=[],k=0;b=h[k++];)"not all"===b.getAttribute("media")?v.push(b):("LINK"===b.tagName&&n.set(b.getAttribute("href"),b),p.set(b.dataset.precedence,g=b));b=0;h=[];var l,a;for(k=!0;;){if(k){var f=w[b++];if(!f){k=!1;b=0;continue}var c=!1,m=0;var d=f[m++];if(a=n.get(d)){var e=a._p;c=!0}else{a=q.createElement("link");a.href=d;a.rel="stylesheet";for(a.dataset.precedence=\nl=f[m++];e=f[m++];)a.setAttribute(e,f[m++]);e=a._p=new Promise(function(x,y){a.onload=x;a.onerror=y});n.set(d,a)}d=a.getAttribute("media");!e||"l"===e.s||d&&!matchMedia(d).matches||h.push(e);if(c)continue}else{a=v[b++];if(!a)break;l=a.getAttribute("data-precedence");a.removeAttribute("media")}c=p.get(l)||g;c===g&&(g=a);p.set(l,a);c?c.parentNode.insertBefore(a,c.nextSibling):(c=q.head,c.insertBefore(a,c.firstChild))}Promise.all(h).then(u.bind(null,r,t,""),u.bind(null,r,t,"Resource failed to load"))};'; +var completeSegment = '$RS=function(a,b){a=document.getElementById(a);b=document.getElementById(b);for(a.parentNode.removeChild(a);a.firstChild;)b.parentNode.insertBefore(a.firstChild,b);b.parentNode.removeChild(b)};'; +var formReplaying = 'addEventListener("submit",function(a){if(!a.defaultPrevented){var c=a.target,d=a.submitter,e=c.action,b=d;if(d){var f=d.getAttribute("formAction");null!=f&&(e=f,b=null)}"javascript:throw new Error(\'A React form was unexpectedly submitted.\')"===e&&(a.preventDefault(),b?(a=document.createElement("input"),a.name=b.name,a.value=b.value,b.parentNode.insertBefore(a,b),b=new FormData(c),a.parentNode.removeChild(a)):b=new FormData(c),a=c.getRootNode(),(a.$$reactFormReplay=a.$$reactFormReplay||[]).push(c,\nd,b))}});'; + +function getValueDescriptorExpectingObjectForWarning(thing) { + return thing === null ? '`null`' : thing === undefined ? '`undefined`' : thing === '' ? 'an empty string' : "something with type \"" + typeof thing + "\""; +} +function getValueDescriptorExpectingEnumForWarning(thing) { + return thing === null ? '`null`' : thing === undefined ? '`undefined`' : thing === '' ? 'an empty string' : typeof thing === 'string' ? JSON.stringify(thing) : "something with type \"" + typeof thing + "\""; +} + +function compareResourcePropsForWarning(newProps, currentProps) { + { + var propDiffs = null; + var allProps = Array.from(new Set(Object.keys(currentProps).concat(Object.keys(newProps)))); + + for (var i = 0; i < allProps.length; i++) { + var propName = allProps[i]; + var newValue = newProps[propName]; + var currentValue = currentProps[propName]; + + if (newValue !== currentValue && !(newValue == null && currentValue == null)) { + if (newValue == null) { + if (propDiffs === null) { + propDiffs = { + missing: {}, + extra: {}, + different: {} + }; + } + + propDiffs.missing[propName] = currentValue; + } else if (currentValue == null) { + if (propDiffs === null) { + propDiffs = { + missing: {}, + extra: {}, + different: {} + }; + } + + propDiffs.extra[propName] = newValue; + } else { + if (propDiffs === null) { + propDiffs = { + missing: {}, + extra: {}, + different: {} + }; + } + + propDiffs.different[propName] = { + original: currentValue, + latest: newValue + }; + } + } + } + + return propDiffs; + } +} + +function describeDifferencesForStylesheets(newProps, currentProps) { + var diff = compareResourcePropsForWarning(newProps, currentProps); + if (!diff) return ''; + var description = ''; + + for (var propName in diff.missing) { + var propValue = diff.missing[propName]; + + if (propName === 'media') { + description += "\n \"" + propName + "\" missing for props, original value: " + getValueDescriptorExpectingEnumForWarning(propValue); + } + } + + for (var _propName in diff.extra) { + var _propValue = diff.extra[_propName]; + description += "\n \"" + _propName + "\" prop value: " + getValueDescriptorExpectingEnumForWarning(_propValue) + ", missing from original props"; + } + + for (var _propName2 in diff.different) { + var latestValue = diff.different[_propName2].latest; + var originalValue = diff.different[_propName2].original; + description += "\n \"" + _propName2 + "\" prop value: " + getValueDescriptorExpectingEnumForWarning(latestValue) + ", original value: " + getValueDescriptorExpectingEnumForWarning(originalValue); + } + + return description; +} +function describeDifferencesForStylesheetOverPreinit(newProps, currentProps) { + var diff = compareResourcePropsForWarning(newProps, currentProps); + if (!diff) return ''; + var description = ''; + + for (var propName in diff.extra) { + var propValue = diff.extra[propName]; + + if (propName === 'precedence' || propName === 'crossOrigin' || propName === 'integrity') { + description += "\n \"" + propName + "\" prop value: " + getValueDescriptorExpectingEnumForWarning(propValue) + ", option missing"; + } else { + description += "\n \"" + propName + "\" prop value: " + getValueDescriptorExpectingEnumForWarning(propValue) + ", option not available with ReactDOM.preinit()"; + } + } + + for (var _propName3 in diff.different) { + var latestValue = diff.different[_propName3].latest; + var originalValue = diff.different[_propName3].original; + + if (_propName3 === 'precedence' && originalValue === 'default') { + description += "\n \"" + _propName3 + "\" prop value: " + getValueDescriptorExpectingEnumForWarning(latestValue) + ", missing from options"; + } else { + description += "\n \"" + _propName3 + "\" prop value: " + getValueDescriptorExpectingEnumForWarning(latestValue) + ", option value: " + getValueDescriptorExpectingEnumForWarning(originalValue); + } + } + + return description; +} +function describeDifferencesForPreinitOverStylesheet(newProps, currentProps) { + var diff = compareResourcePropsForWarning(newProps, currentProps); + if (!diff) return ''; + var description = ''; + + for (var propName in diff.missing) { + var propValue = diff.missing[propName]; + + if (propName === 'precedence' && propValue !== 'default') { + description += "\n \"" + propName + "\" missing from options, prop value: " + getValueDescriptorExpectingEnumForWarning(propValue); + } + } + + for (var _propName4 in diff.extra) { + var _propValue2 = diff.extra[_propName4]; + + if (_propName4 === 'precedence' || _propName4 === 'crossOrigin' || _propName4 === 'integrity') { + description += "\n \"" + _propName4 + "\" option value: " + getValueDescriptorExpectingEnumForWarning(_propValue2) + ", missing from props"; + } + } + + for (var _propName5 in diff.different) { + var latestValue = diff.different[_propName5].latest; + var originalValue = diff.different[_propName5].original; + description += "\n \"" + _propName5 + "\" option value: " + getValueDescriptorExpectingEnumForWarning(latestValue) + ", prop value: " + getValueDescriptorExpectingEnumForWarning(originalValue); + } + + return description; +} +function describeDifferencesForPreinits(newProps, currentProps) { + var diff = compareResourcePropsForWarning(newProps, currentProps); + if (!diff) return ''; + var description = ''; + + for (var propName in diff.missing) { + var propValue = diff.missing[propName]; + + if (propName === 'precedence' && propValue !== 'default') { + description += "\n \"" + propName + "\" missing from options, original option value: " + getValueDescriptorExpectingEnumForWarning(propValue); + } + } + + for (var _propName6 in diff.extra) { + var _propValue3 = diff.extra[_propName6]; + + if (_propName6 === 'precedence' && _propValue3 !== 'default' || _propName6 === 'crossOrigin' || _propName6 === 'integrity') { + description += "\n \"" + _propName6 + "\" option value: " + getValueDescriptorExpectingEnumForWarning(_propValue3) + ", missing from original options"; + } + } + + for (var _propName7 in diff.different) { + var latestValue = diff.different[_propName7].latest; + var originalValue = diff.different[_propName7].original; + description += "\n \"" + _propName7 + "\" option value: " + getValueDescriptorExpectingEnumForWarning(latestValue) + ", original option value: " + getValueDescriptorExpectingEnumForWarning(originalValue); + } + + return description; +} +var preloadOptionsForComparison = ['as', 'crossOrigin', 'integrity', 'media']; +function describeDifferencesForPreloads(newProps, currentProps) { + var diff = compareResourcePropsForWarning(newProps, currentProps); + if (!diff) return ''; + var description = ''; + + for (var propName in diff.missing) { + var propValue = diff.missing[propName]; + + if (preloadOptionsForComparison.includes(propName)) { + description += "\n \"" + propName + "\" missing from options, original option value: " + getValueDescriptorExpectingEnumForWarning(propValue); + } + } + + for (var _propName8 in diff.extra) { + var _propValue4 = diff.extra[_propName8]; + + if (preloadOptionsForComparison.includes(_propName8)) { + description += "\n \"" + _propName8 + "\" option value: " + getValueDescriptorExpectingEnumForWarning(_propValue4) + ", missing from original options"; + } + } + + for (var _propName9 in diff.different) { + var latestValue = diff.different[_propName9].latest; + var originalValue = diff.different[_propName9].original; + + if (preloadOptionsForComparison.includes(_propName9)) { + description += "\n \"" + _propName9 + "\" option value: " + getValueDescriptorExpectingEnumForWarning(latestValue) + ", original option value: " + getValueDescriptorExpectingEnumForWarning(originalValue); + } + } + + return description; +} +function describeDifferencesForPreloadOverImplicitPreload(newProps, currentProps) { + var diff = compareResourcePropsForWarning(newProps, currentProps); + if (!diff) return ''; + var description = ''; + + for (var propName in diff.missing) { + var propValue = diff.missing[propName]; + + if (preloadOptionsForComparison.includes(propName)) { + description += "\n \"" + propName + "\" missing from options, underlying prop value: " + getValueDescriptorExpectingEnumForWarning(propValue); + } + } + + for (var _propName10 in diff.extra) { + var _propValue5 = diff.extra[_propName10]; + + if (preloadOptionsForComparison.includes(_propName10)) { + description += "\n \"" + _propName10 + "\" option value: " + getValueDescriptorExpectingEnumForWarning(_propValue5) + ", missing from underlying props"; + } + } + + for (var _propName11 in diff.different) { + var latestValue = diff.different[_propName11].latest; + var originalValue = diff.different[_propName11].original; + + if (preloadOptionsForComparison.includes(_propName11)) { + description += "\n \"" + _propName11 + "\" option value: " + getValueDescriptorExpectingEnumForWarning(latestValue) + ", underlying prop value: " + getValueDescriptorExpectingEnumForWarning(originalValue); + } + } + + return description; +} +function describeDifferencesForScripts(newProps, currentProps) { + var diff = compareResourcePropsForWarning(newProps, currentProps); + if (!diff) return ''; + var description = ''; + + for (var propName in diff.missing) { + var propValue = diff.missing[propName]; + description += "\n \"" + propName + "\" missing for props, original value: " + getValueDescriptorExpectingEnumForWarning(propValue); + } + + for (var _propName12 in diff.extra) { + var _propValue6 = diff.extra[_propName12]; + description += "\n \"" + _propName12 + "\" prop value: " + getValueDescriptorExpectingEnumForWarning(_propValue6) + ", missing from original props"; + } + + for (var _propName13 in diff.different) { + var latestValue = diff.different[_propName13].latest; + var originalValue = diff.different[_propName13].original; + description += "\n \"" + _propName13 + "\" prop value: " + getValueDescriptorExpectingEnumForWarning(latestValue) + ", original value: " + getValueDescriptorExpectingEnumForWarning(originalValue); + } + + return description; +} +function describeDifferencesForScriptOverPreinit(newProps, currentProps) { + var diff = compareResourcePropsForWarning(newProps, currentProps); + if (!diff) return ''; + var description = ''; + + for (var propName in diff.extra) { + var propValue = diff.extra[propName]; + + if (propName === 'crossOrigin' || propName === 'integrity') { + description += "\n \"" + propName + "\" prop value: " + getValueDescriptorExpectingEnumForWarning(propValue) + ", option missing"; + } else { + description += "\n \"" + propName + "\" prop value: " + getValueDescriptorExpectingEnumForWarning(propValue) + ", option not available with ReactDOM.preinit()"; + } + } + + for (var _propName14 in diff.different) { + var latestValue = diff.different[_propName14].latest; + var originalValue = diff.different[_propName14].original; + description += "\n \"" + _propName14 + "\" prop value: " + getValueDescriptorExpectingEnumForWarning(latestValue) + ", option value: " + getValueDescriptorExpectingEnumForWarning(originalValue); + } + + return description; +} +function describeDifferencesForPreinitOverScript(newProps, currentProps) { + var diff = compareResourcePropsForWarning(newProps, currentProps); + if (!diff) return ''; + var description = ''; + + for (var propName in diff.extra) { + var propValue = diff.extra[propName]; + + if (propName === 'crossOrigin' || propName === 'integrity') { + description += "\n \"" + propName + "\" option value: " + getValueDescriptorExpectingEnumForWarning(propValue) + ", missing from props"; + } + } + + for (var _propName15 in diff.different) { + var latestValue = diff.different[_propName15].latest; + var originalValue = diff.different[_propName15].original; + description += "\n \"" + _propName15 + "\" option value: " + getValueDescriptorExpectingEnumForWarning(latestValue) + ", prop value: " + getValueDescriptorExpectingEnumForWarning(originalValue); + } + + return description; +} + +// same object across all transitions. + +var sharedNotPendingObject = { + pending: false, + data: null, + method: null, + action: null +}; +var NotPending = Object.freeze(sharedNotPendingObject) ; + +var ReactDOMSharedInternals = ReactDOM.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED; + +var ReactDOMCurrentDispatcher = ReactDOMSharedInternals.Dispatcher; +var ReactDOMServerDispatcher = { + prefetchDNS: prefetchDNS, + preconnect: preconnect, + preload: preload, + preinit: preinit +}; +function prepareHostDispatcher() { + ReactDOMCurrentDispatcher.current = ReactDOMServerDispatcher; +} // Used to distinguish these contexts from ones used in other renderers. +var ScriptStreamingFormat = 0; +var DataStreamingFormat = 1; +var NothingSent +/* */ += 0; +var SentCompleteSegmentFunction +/* */ += 1; +var SentCompleteBoundaryFunction +/* */ += 2; +var SentClientRenderFunction +/* */ += 4; +var SentStyleInsertionFunction +/* */ += 8; +var SentFormReplayingRuntime +/* */ += 16; // Per response, global state that is not contextual to the rendering subtree. + +var dataElementQuotedEnd = stringToPrecomputedChunk('">'); +var startInlineScript = stringToPrecomputedChunk(''); +var startScriptSrc = stringToPrecomputedChunk(''); +/** + * This escaping function is designed to work with bootstrapScriptContent only. + * because we know we are escaping the entire script. We can avoid for instance + * escaping html comment string sequences that are valid javascript as well because + * if there are no sebsequent '); +var completeSegmentData1 = stringToPrecomputedChunk('")),ue(a,b,c),a=a.responseState.generateStaticMarkup?!0:b.push("\x3c!--/$--\x3e"),a;if(0a.progressiveChunkSize)return d.rootSegmentID=a.nextSegmentId++,a.completedBoundaries.push(d),Db(b,a.responseState,d.id),ue(a,b,c),b.push("\x3c!--/$--\x3e");(c=a.resources.boundaryResources)&&d.resources.forEach(Dc,c);a.responseState.generateStaticMarkup||b.push("\x3c!--$--\x3e");c=d.completedSegments;if(1!==c.length)throw Error("A previously unvisited boundary must have exactly one root segment. This is a bug in React."); +ve(a,b,c[0]);a=a.responseState.generateStaticMarkup?!0:b.push("\x3c!--/$--\x3e");return a}function we(a,b,c){Eb(b,a.responseState,c.formatContext,c.id);ve(a,b,c);return Fb(b,c.formatContext)} +function xe(a,b,c){a.resources.boundaryResources=c.resources;for(var d=c.completedSegments,e=0;e"):b.push('">');return Cb(b,a)&&d} +function ye(a,b,c,d){if(2===d.status)return!0;var e=d.id;if(-1===e){if(-1===(d.id=c.rootSegmentID))throw Error("A root segment ID must have been assigned by now. This is a bug in React.");return we(a,b,d)}we(a,b,d);a=a.responseState;(c=0===a.streamingFormat)?(b.push(a.startInlineScript),0===(a.instructions&1)?(a.instructions|=1,b.push('$RS=function(a,b){a=document.getElementById(a);b=document.getElementById(b);for(a.parentNode.removeChild(a);a.firstChild;)b.parentNode.insertBefore(a.firstChild,b);b.parentNode.removeChild(b)};;$RS("')): +b.push('$RS("')):b.push('');return b} +function te(a,b){try{var c,d=a.completedRootSegment;if(null!==d)if(0===a.pendingRootTasks)Ub(b,a.resources,a.responseState,0===a.allPendingTasks),ve(a,b,d),a.completedRootSegment=null,Cb(b,a.responseState);else return;else Wb(b,a.resources,a.responseState);var e=a.clientRenderedBoundaries;for(c=0;c"):!d.push('">')){a.destination=null;c++;e.splice(0,c);return}}e.splice(0,c);var z=a.completedBoundaries;for(c=0;c")),a.htmlChunks&&(b.push("")),b.push(null))}}function Zb(a){if(!1===a.flushScheduled&&0===a.pingedTasks.length&&null!==a.destination){var b=a.destination;a.flushScheduled=!0;te(a,b)}}function ze(a,b){if(1===a.status)a.status=2,b.destroy(a.fatalError);else if(2!==a.status&&null===a.destination){a.destination=b;try{te(a,b)}catch(c){ie(a,c),je(a,c)}}} +function Ae(a,b){try{var c=a.abortableTasks;if(0 1 ? _len2 - 1 : 0), _key2 = 1; _key2 < _len2; _key2++) { + args[_key2 - 1] = arguments[_key2]; + } + + printWarning('error', format, args); + } + } +} + +function printWarning(level, format, args) { + // When changing this logic, you might want to also + // update consoleWithStackDev.www.js as well. + { + var ReactDebugCurrentFrame = ReactSharedInternals.ReactDebugCurrentFrame; + var stack = ReactDebugCurrentFrame.getStackAddendum(); + + if (stack !== '') { + format += '%s'; + args = args.concat([stack]); + } // eslint-disable-next-line react-internal/safe-string-coercion + + + var argsWithFormat = args.map(function (item) { + return String(item); + }); // Careful: RN currently depends on this prefix + + argsWithFormat.unshift('Warning: ' + format); // We intentionally don't use spread (or .apply) directly because it + // breaks IE9: https://github.com/facebook/react/issues/13610 + // eslint-disable-next-line react-internal/no-production-logging + + Function.prototype.apply.call(console[level], console, argsWithFormat); + } +} + +var ReactCurrentDispatcher = ReactSharedInternals.ReactCurrentDispatcher; // Since the "not pending" value is always the same, we can reuse the + +function resolveDispatcher() { + // Copied from react/src/ReactHooks.js. It's the same thing but in a + // different package. + var dispatcher = ReactCurrentDispatcher.current; + + { + if (dispatcher === null) { + error('Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for' + ' one of the following reasons:\n' + '1. You might have mismatching versions of React and the renderer (such as React DOM)\n' + '2. You might be breaking the Rules of Hooks\n' + '3. You might have more than one copy of React in the same app\n' + 'See https://reactjs.org/link/invalid-hook-call for tips about how to debug and fix this problem.'); + } + } // Will result in a null access error if accessed outside render phase. We + // intentionally don't throw our own error because this is in a hot path. + // Also helps ensure this is inlined. + + + return dispatcher; +} + +function useFormStatus() { + { + var dispatcher = resolveDispatcher(); // $FlowFixMe[not-a-function] We know this exists because of the feature check above. + + return dispatcher.useHostTransitionStatus(); + } +} + +function createPortal() { + throw new Error('createPortal was called on the server. Portals are not currently' + ' supported on the server. Update your program to conditionally call' + ' createPortal on the client only.'); +} +function flushSync() { + throw new Error('flushSync was called on the server. This is likely caused by a' + ' function being called during render or in module scope that was' + ' intended to be called from an effect or event handler. Update your' + ' to not call flushSync no the server.'); +} + +exports.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED = Internals; +exports.createPortal = createPortal; +exports.experimental_useFormStatus = useFormStatus; +exports.flushSync = flushSync; +exports.preconnect = preconnect; +exports.prefetchDNS = prefetchDNS; +exports.preinit = preinit; +exports.preload = preload; +exports.version = ReactVersion; + })(); +} diff --git a/packages/next/src/compiled/react-dom-experimental/cjs/react-dom-server-rendering-stub.production.min.js b/packages/next/src/compiled/react-dom-experimental/cjs/react-dom-server-rendering-stub.production.min.js new file mode 100644 index 0000000000000..faf9900ec692d --- /dev/null +++ b/packages/next/src/compiled/react-dom-experimental/cjs/react-dom-server-rendering-stub.production.min.js @@ -0,0 +1,12 @@ +/** + * @license React + * react-dom-server-rendering-stub.production.min.js + * + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ +'use strict';var d=require("next/dist/compiled/react-experimental"),e={usingClientEntryPoint:!1,Events:null,Dispatcher:{current:null}};function f(c){for(var b="https://reactjs.org/docs/error-decoder.html?invariant="+c,a=1;a 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) { + args[_key - 1] = arguments[_key]; + } + + printWarning('warn', format, args); + } + } +} +function error(format) { + { + { + for (var _len2 = arguments.length, args = new Array(_len2 > 1 ? _len2 - 1 : 0), _key2 = 1; _key2 < _len2; _key2++) { + args[_key2 - 1] = arguments[_key2]; + } + + printWarning('error', format, args); + } + } +} + +function printWarning(level, format, args) { + // When changing this logic, you might want to also + // update consoleWithStackDev.www.js as well. + { + var ReactDebugCurrentFrame = ReactSharedInternals.ReactDebugCurrentFrame; + var stack = ReactDebugCurrentFrame.getStackAddendum(); + + if (stack !== '') { + format += '%s'; + args = args.concat([stack]); + } // eslint-disable-next-line react-internal/safe-string-coercion + + + var argsWithFormat = args.map(function (item) { + return String(item); + }); // Careful: RN currently depends on this prefix + + argsWithFormat.unshift('Warning: ' + format); // We intentionally don't use spread (or .apply) directly because it + // breaks IE9: https://github.com/facebook/react/issues/13610 + // eslint-disable-next-line react-internal/no-production-logging + + Function.prototype.apply.call(console[level], console, argsWithFormat); + } +} + +function scheduleWork(callback) { + callback(); +} +var VIEW_SIZE = 512; +var currentView = null; +var writtenBytes = 0; +function beginWriting(destination) { + currentView = new Uint8Array(VIEW_SIZE); + writtenBytes = 0; +} +function writeChunk(destination, chunk) { + if (chunk.length === 0) { + return; + } + + if (chunk.length > VIEW_SIZE) { + { + if (precomputedChunkSet.has(chunk)) { + error('A large precomputed chunk was passed to writeChunk without being copied.' + ' Large chunks get enqueued directly and are not copied. This is incompatible with precomputed chunks because you cannot enqueue the same precomputed chunk twice.' + ' Use "cloneChunk" to make a copy of this large precomputed chunk before writing it. This is a bug in React.'); + } + } // this chunk may overflow a single view which implies it was not + // one that is cached by the streaming renderer. We will enqueu + // it directly and expect it is not re-used + + + if (writtenBytes > 0) { + destination.enqueue(new Uint8Array(currentView.buffer, 0, writtenBytes)); + currentView = new Uint8Array(VIEW_SIZE); + writtenBytes = 0; + } + + destination.enqueue(chunk); + return; + } + + var bytesToWrite = chunk; + var allowableBytes = currentView.length - writtenBytes; + + if (allowableBytes < bytesToWrite.length) { + // this chunk would overflow the current view. We enqueue a full view + // and start a new view with the remaining chunk + if (allowableBytes === 0) { + // the current view is already full, send it + destination.enqueue(currentView); + } else { + // fill up the current view and apply the remaining chunk bytes + // to a new view. + currentView.set(bytesToWrite.subarray(0, allowableBytes), writtenBytes); // writtenBytes += allowableBytes; // this can be skipped because we are going to immediately reset the view + + destination.enqueue(currentView); + bytesToWrite = bytesToWrite.subarray(allowableBytes); + } + + currentView = new Uint8Array(VIEW_SIZE); + writtenBytes = 0; + } + + currentView.set(bytesToWrite, writtenBytes); + writtenBytes += bytesToWrite.length; +} +function writeChunkAndReturn(destination, chunk) { + writeChunk(destination, chunk); // in web streams there is no backpressure so we can alwas write more + + return true; +} +function completeWriting(destination) { + if (currentView && writtenBytes > 0) { + destination.enqueue(new Uint8Array(currentView.buffer, 0, writtenBytes)); + currentView = null; + writtenBytes = 0; + } +} +function close(destination) { + destination.close(); +} +var textEncoder = new TextEncoder(); +function stringToChunk(content) { + return textEncoder.encode(content); +} +var precomputedChunkSet = new Set() ; +function stringToPrecomputedChunk(content) { + var precomputedChunk = textEncoder.encode(content); + + { + precomputedChunkSet.add(precomputedChunk); + } + + return precomputedChunk; +} +function clonePrecomputedChunk(precomputedChunk) { + return precomputedChunk.length > VIEW_SIZE ? precomputedChunk.slice() : precomputedChunk; +} +function closeWithError(destination, error) { + // $FlowFixMe[method-unbinding] + if (typeof destination.error === 'function') { + // $FlowFixMe[incompatible-call]: This is an Error object or the destination accepts other types. + destination.error(error); + } else { + // Earlier implementations doesn't support this method. In that environment you're + // supposed to throw from a promise returned but we don't return a promise in our + // approach. We could fork this implementation but this is environment is an edge + // case to begin with. It's even less common to run this in an older environment. + // Even then, this is not where errors are supposed to happen and they get reported + // to a global callback in addition to this anyway. So it's fine just to close this. + destination.close(); + } +} + +function _defineProperty(obj, key, value) { + if (key in obj) { + Object.defineProperty(obj, key, { + value: value, + enumerable: true, + configurable: true, + writable: true + }); + } else { + obj[key] = value; + } + + return obj; +} + +var assign = Object.assign; + +/* + * The `'' + value` pattern (used in perf-sensitive code) throws for Symbol + * and Temporal.* types. See https://github.com/facebook/react/pull/22064. + * + * The functions in this module will throw an easier-to-understand, + * easier-to-debug exception with a clear errors message message explaining the + * problem. (Instead of a confusing exception thrown inside the implementation + * of the `value` object). + */ +// $FlowFixMe[incompatible-return] only called in DEV, so void return is not possible. +function typeName(value) { + { + // toStringTag is needed for namespaced types like Temporal.Instant + var hasToStringTag = typeof Symbol === 'function' && Symbol.toStringTag; + var type = hasToStringTag && value[Symbol.toStringTag] || value.constructor.name || 'Object'; // $FlowFixMe[incompatible-return] + + return type; + } +} // $FlowFixMe[incompatible-return] only called in DEV, so void return is not possible. + + +function willCoercionThrow(value) { + { + try { + testStringCoercion(value); + return false; + } catch (e) { + return true; + } + } +} + +function testStringCoercion(value) { + // If you ended up here by following an exception call stack, here's what's + // happened: you supplied an object or symbol value to React (as a prop, key, + // DOM attribute, CSS property, string ref, etc.) and when React tried to + // coerce it to a string using `'' + value`, an exception was thrown. + // + // The most common types that will cause this exception are `Symbol` instances + // and Temporal objects like `Temporal.Instant`. But any object that has a + // `valueOf` or `[Symbol.toPrimitive]` method that throws will also cause this + // exception. (Library authors do this to prevent users from using built-in + // numeric operators like `+` or comparison operators like `>=` because custom + // methods are needed to perform accurate arithmetic or comparison.) + // + // To fix the problem, coerce this object or symbol value to a string before + // passing it to React. The most reliable way is usually `String(value)`. + // + // To find which value is throwing, check the browser or debugger console. + // Before this exception was thrown, there should be `console.error` output + // that shows the type (Symbol, Temporal.PlainDate, etc.) that caused the + // problem and how that type was used: key, atrribute, input value prop, etc. + // In most cases, this console output also shows the component and its + // ancestor components where the exception happened. + // + // eslint-disable-next-line react-internal/safe-string-coercion + return '' + value; +} + +function checkAttributeStringCoercion(value, attributeName) { + { + if (willCoercionThrow(value)) { + error('The provided `%s` attribute is an unsupported type %s.' + ' This value must be coerced to a string before before using it here.', attributeName, typeName(value)); + + return testStringCoercion(value); // throw (to help callers find troubleshooting comments) + } + } +} +function checkCSSPropertyStringCoercion(value, propName) { + { + if (willCoercionThrow(value)) { + error('The provided `%s` CSS property is an unsupported type %s.' + ' This value must be coerced to a string before before using it here.', propName, typeName(value)); + + return testStringCoercion(value); // throw (to help callers find troubleshooting comments) + } + } +} +function checkHtmlStringCoercion(value) { + { + if (willCoercionThrow(value)) { + error('The provided HTML markup uses a value of unsupported type %s.' + ' This value must be coerced to a string before before using it here.', typeName(value)); + + return testStringCoercion(value); // throw (to help callers find troubleshooting comments) + } + } +} + +// ----------------------------------------------------------------------------- +var enableFloat = true; // Enables unstable_useMemoCache hook, intended as a compilation target for + +// $FlowFixMe[method-unbinding] +var hasOwnProperty = Object.prototype.hasOwnProperty; + +/* eslint-disable max-len */ + +var ATTRIBUTE_NAME_START_CHAR = ":A-Z_a-z\\u00C0-\\u00D6\\u00D8-\\u00F6\\u00F8-\\u02FF\\u0370-\\u037D\\u037F-\\u1FFF\\u200C-\\u200D\\u2070-\\u218F\\u2C00-\\u2FEF\\u3001-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFFD"; +/* eslint-enable max-len */ + +var ATTRIBUTE_NAME_CHAR = ATTRIBUTE_NAME_START_CHAR + "\\-.0-9\\u00B7\\u0300-\\u036F\\u203F-\\u2040"; +var VALID_ATTRIBUTE_NAME_REGEX = new RegExp('^[' + ATTRIBUTE_NAME_START_CHAR + '][' + ATTRIBUTE_NAME_CHAR + ']*$'); +var illegalAttributeNameCache = {}; +var validatedAttributeNameCache = {}; +function isAttributeNameSafe(attributeName) { + if (hasOwnProperty.call(validatedAttributeNameCache, attributeName)) { + return true; + } + + if (hasOwnProperty.call(illegalAttributeNameCache, attributeName)) { + return false; + } + + if (VALID_ATTRIBUTE_NAME_REGEX.test(attributeName)) { + validatedAttributeNameCache[attributeName] = true; + return true; + } + + illegalAttributeNameCache[attributeName] = true; + + { + error('Invalid attribute name: `%s`', attributeName); + } + + return false; +} + +/** + * CSS properties which accept numbers but are not in units of "px". + */ +var unitlessNumbers = new Set(['animationIterationCount', 'aspectRatio', 'borderImageOutset', 'borderImageSlice', 'borderImageWidth', 'boxFlex', 'boxFlexGroup', 'boxOrdinalGroup', 'columnCount', 'columns', 'flex', 'flexGrow', 'flexPositive', 'flexShrink', 'flexNegative', 'flexOrder', 'gridArea', 'gridRow', 'gridRowEnd', 'gridRowSpan', 'gridRowStart', 'gridColumn', 'gridColumnEnd', 'gridColumnSpan', 'gridColumnStart', 'fontWeight', 'lineClamp', 'lineHeight', 'opacity', 'order', 'orphans', 'scale', 'tabSize', 'widows', 'zIndex', 'zoom', 'fillOpacity', // SVG-related properties +'floodOpacity', 'stopOpacity', 'strokeDasharray', 'strokeDashoffset', 'strokeMiterlimit', 'strokeOpacity', 'strokeWidth', 'MozAnimationIterationCount', // Known Prefixed Properties +'MozBoxFlex', // TODO: Remove these since they shouldn't be used in modern code +'MozBoxFlexGroup', 'MozLineClamp', 'msAnimationIterationCount', 'msFlex', 'msZoom', 'msFlexGrow', 'msFlexNegative', 'msFlexOrder', 'msFlexPositive', 'msFlexShrink', 'msGridColumn', 'msGridColumnSpan', 'msGridRow', 'msGridRowSpan', 'WebkitAnimationIterationCount', 'WebkitBoxFlex', 'WebKitBoxFlexGroup', 'WebkitBoxOrdinalGroup', 'WebkitColumnCount', 'WebkitColumns', 'WebkitFlex', 'WebkitFlexGrow', 'WebkitFlexPositive', 'WebkitFlexShrink', 'WebkitLineClamp']); +function isUnitlessNumber (name) { + return unitlessNumbers.has(name); +} + +var aliases = new Map([['acceptCharset', 'accept-charset'], ['htmlFor', 'for'], ['httpEquiv', 'http-equiv'], // HTML and SVG attributes, but the SVG attribute is case sensitive.], +['crossOrigin', 'crossorigin'], // This is a list of all SVG attributes that need special casing. +// Regular attributes that just accept strings.], +['accentHeight', 'accent-height'], ['alignmentBaseline', 'alignment-baseline'], ['arabicForm', 'arabic-form'], ['baselineShift', 'baseline-shift'], ['capHeight', 'cap-height'], ['clipPath', 'clip-path'], ['clipRule', 'clip-rule'], ['colorInterpolation', 'color-interpolation'], ['colorInterpolationFilters', 'color-interpolation-filters'], ['colorProfile', 'color-profile'], ['colorRendering', 'color-rendering'], ['dominantBaseline', 'dominant-baseline'], ['enableBackground', 'enable-background'], ['fillOpacity', 'fill-opacity'], ['fillRule', 'fill-rule'], ['floodColor', 'flood-color'], ['floodOpacity', 'flood-opacity'], ['fontFamily', 'font-family'], ['fontSize', 'font-size'], ['fontSizeAdjust', 'font-size-adjust'], ['fontStretch', 'font-stretch'], ['fontStyle', 'font-style'], ['fontVariant', 'font-variant'], ['fontWeight', 'font-weight'], ['glyphName', 'glyph-name'], ['glyphOrientationHorizontal', 'glyph-orientation-horizontal'], ['glyphOrientationVertical', 'glyph-orientation-vertical'], ['horizAdvX', 'horiz-adv-x'], ['horizOriginX', 'horiz-origin-x'], ['imageRendering', 'image-rendering'], ['letterSpacing', 'letter-spacing'], ['lightingColor', 'lighting-color'], ['markerEnd', 'marker-end'], ['markerMid', 'marker-mid'], ['markerStart', 'marker-start'], ['overlinePosition', 'overline-position'], ['overlineThickness', 'overline-thickness'], ['paintOrder', 'paint-order'], ['panose-1', 'panose-1'], ['pointerEvents', 'pointer-events'], ['renderingIntent', 'rendering-intent'], ['shapeRendering', 'shape-rendering'], ['stopColor', 'stop-color'], ['stopOpacity', 'stop-opacity'], ['strikethroughPosition', 'strikethrough-position'], ['strikethroughThickness', 'strikethrough-thickness'], ['strokeDasharray', 'stroke-dasharray'], ['strokeDashoffset', 'stroke-dashoffset'], ['strokeLinecap', 'stroke-linecap'], ['strokeLinejoin', 'stroke-linejoin'], ['strokeMiterlimit', 'stroke-miterlimit'], ['strokeOpacity', 'stroke-opacity'], ['strokeWidth', 'stroke-width'], ['textAnchor', 'text-anchor'], ['textDecoration', 'text-decoration'], ['textRendering', 'text-rendering'], ['transformOrigin', 'transform-origin'], ['underlinePosition', 'underline-position'], ['underlineThickness', 'underline-thickness'], ['unicodeBidi', 'unicode-bidi'], ['unicodeRange', 'unicode-range'], ['unitsPerEm', 'units-per-em'], ['vAlphabetic', 'v-alphabetic'], ['vHanging', 'v-hanging'], ['vIdeographic', 'v-ideographic'], ['vMathematical', 'v-mathematical'], ['vectorEffect', 'vector-effect'], ['vertAdvY', 'vert-adv-y'], ['vertOriginX', 'vert-origin-x'], ['vertOriginY', 'vert-origin-y'], ['wordSpacing', 'word-spacing'], ['writingMode', 'writing-mode'], ['xmlnsXlink', 'xmlns:xlink'], ['xHeight', 'x-height']]); +function getAttributeAlias (name) { + return aliases.get(name) || name; +} + +var hasReadOnlyValue = { + button: true, + checkbox: true, + image: true, + hidden: true, + radio: true, + reset: true, + submit: true +}; +function checkControlledValueProps(tagName, props) { + { + if (!(hasReadOnlyValue[props.type] || props.onChange || props.onInput || props.readOnly || props.disabled || props.value == null)) { + error('You provided a `value` prop to a form field without an ' + '`onChange` handler. This will render a read-only field. If ' + 'the field should be mutable use `defaultValue`. Otherwise, ' + 'set either `onChange` or `readOnly`.'); + } + + if (!(props.onChange || props.readOnly || props.disabled || props.checked == null)) { + error('You provided a `checked` prop to a form field without an ' + '`onChange` handler. This will render a read-only field. If ' + 'the field should be mutable use `defaultChecked`. Otherwise, ' + 'set either `onChange` or `readOnly`.'); + } + } +} + +var ariaProperties = { + 'aria-current': 0, + // state + 'aria-description': 0, + 'aria-details': 0, + 'aria-disabled': 0, + // state + 'aria-hidden': 0, + // state + 'aria-invalid': 0, + // state + 'aria-keyshortcuts': 0, + 'aria-label': 0, + 'aria-roledescription': 0, + // Widget Attributes + 'aria-autocomplete': 0, + 'aria-checked': 0, + 'aria-expanded': 0, + 'aria-haspopup': 0, + 'aria-level': 0, + 'aria-modal': 0, + 'aria-multiline': 0, + 'aria-multiselectable': 0, + 'aria-orientation': 0, + 'aria-placeholder': 0, + 'aria-pressed': 0, + 'aria-readonly': 0, + 'aria-required': 0, + 'aria-selected': 0, + 'aria-sort': 0, + 'aria-valuemax': 0, + 'aria-valuemin': 0, + 'aria-valuenow': 0, + 'aria-valuetext': 0, + // Live Region Attributes + 'aria-atomic': 0, + 'aria-busy': 0, + 'aria-live': 0, + 'aria-relevant': 0, + // Drag-and-Drop Attributes + 'aria-dropeffect': 0, + 'aria-grabbed': 0, + // Relationship Attributes + 'aria-activedescendant': 0, + 'aria-colcount': 0, + 'aria-colindex': 0, + 'aria-colspan': 0, + 'aria-controls': 0, + 'aria-describedby': 0, + 'aria-errormessage': 0, + 'aria-flowto': 0, + 'aria-labelledby': 0, + 'aria-owns': 0, + 'aria-posinset': 0, + 'aria-rowcount': 0, + 'aria-rowindex': 0, + 'aria-rowspan': 0, + 'aria-setsize': 0 +}; + +var warnedProperties$1 = {}; +var rARIA$1 = new RegExp('^(aria)-[' + ATTRIBUTE_NAME_CHAR + ']*$'); +var rARIACamel$1 = new RegExp('^(aria)[A-Z][' + ATTRIBUTE_NAME_CHAR + ']*$'); + +function validateProperty$1(tagName, name) { + { + if (hasOwnProperty.call(warnedProperties$1, name) && warnedProperties$1[name]) { + return true; + } + + if (rARIACamel$1.test(name)) { + var ariaName = 'aria-' + name.slice(4).toLowerCase(); + var correctName = ariaProperties.hasOwnProperty(ariaName) ? ariaName : null; // If this is an aria-* attribute, but is not listed in the known DOM + // DOM properties, then it is an invalid aria-* attribute. + + if (correctName == null) { + error('Invalid ARIA attribute `%s`. ARIA attributes follow the pattern aria-* and must be lowercase.', name); + + warnedProperties$1[name] = true; + return true; + } // aria-* attributes should be lowercase; suggest the lowercase version. + + + if (name !== correctName) { + error('Invalid ARIA attribute `%s`. Did you mean `%s`?', name, correctName); + + warnedProperties$1[name] = true; + return true; + } + } + + if (rARIA$1.test(name)) { + var lowerCasedName = name.toLowerCase(); + var standardName = ariaProperties.hasOwnProperty(lowerCasedName) ? lowerCasedName : null; // If this is an aria-* attribute, but is not listed in the known DOM + // DOM properties, then it is an invalid aria-* attribute. + + if (standardName == null) { + warnedProperties$1[name] = true; + return false; + } // aria-* attributes should be lowercase; suggest the lowercase version. + + + if (name !== standardName) { + error('Unknown ARIA attribute `%s`. Did you mean `%s`?', name, standardName); + + warnedProperties$1[name] = true; + return true; + } + } + } + + return true; +} + +function validateProperties$2(type, props) { + { + var invalidProps = []; + + for (var key in props) { + var isValid = validateProperty$1(type, key); + + if (!isValid) { + invalidProps.push(key); + } + } + + var unknownPropString = invalidProps.map(function (prop) { + return '`' + prop + '`'; + }).join(', '); + + if (invalidProps.length === 1) { + error('Invalid aria prop %s on <%s> tag. ' + 'For details, see https://reactjs.org/link/invalid-aria-props', unknownPropString, type); + } else if (invalidProps.length > 1) { + error('Invalid aria props %s on <%s> tag. ' + 'For details, see https://reactjs.org/link/invalid-aria-props', unknownPropString, type); + } + } +} + +var didWarnValueNull = false; +function validateProperties$1(type, props) { + { + if (type !== 'input' && type !== 'textarea' && type !== 'select') { + return; + } + + if (props != null && props.value === null && !didWarnValueNull) { + didWarnValueNull = true; + + if (type === 'select' && props.multiple) { + error('`value` prop on `%s` should not be null. ' + 'Consider using an empty array when `multiple` is set to `true` ' + 'to clear the component or `undefined` for uncontrolled components.', type); + } else { + error('`value` prop on `%s` should not be null. ' + 'Consider using an empty string to clear the component or `undefined` ' + 'for uncontrolled components.', type); + } + } + } +} + +function isCustomElement(tagName, props) { + if (tagName.indexOf('-') === -1) { + return false; + } + + switch (tagName) { + // These are reserved SVG and MathML elements. + // We don't mind this list too much because we expect it to never grow. + // The alternative is to track the namespace in a few places which is convoluted. + // https://w3c.github.io/webcomponents/spec/custom/#custom-elements-core-concepts + case 'annotation-xml': + case 'color-profile': + case 'font-face': + case 'font-face-src': + case 'font-face-uri': + case 'font-face-format': + case 'font-face-name': + case 'missing-glyph': + return false; + + default: + return true; + } +} + +// When adding attributes to the HTML or SVG allowed attribute list, be sure to +// also add them to this module to ensure casing and incorrect name +// warnings. +var possibleStandardNames = { + // HTML + accept: 'accept', + acceptcharset: 'acceptCharset', + 'accept-charset': 'acceptCharset', + accesskey: 'accessKey', + action: 'action', + allowfullscreen: 'allowFullScreen', + alt: 'alt', + as: 'as', + async: 'async', + autocapitalize: 'autoCapitalize', + autocomplete: 'autoComplete', + autocorrect: 'autoCorrect', + autofocus: 'autoFocus', + autoplay: 'autoPlay', + autosave: 'autoSave', + capture: 'capture', + cellpadding: 'cellPadding', + cellspacing: 'cellSpacing', + challenge: 'challenge', + charset: 'charSet', + checked: 'checked', + children: 'children', + cite: 'cite', + class: 'className', + classid: 'classID', + classname: 'className', + cols: 'cols', + colspan: 'colSpan', + content: 'content', + contenteditable: 'contentEditable', + contextmenu: 'contextMenu', + controls: 'controls', + controlslist: 'controlsList', + coords: 'coords', + crossorigin: 'crossOrigin', + dangerouslysetinnerhtml: 'dangerouslySetInnerHTML', + data: 'data', + datetime: 'dateTime', + default: 'default', + defaultchecked: 'defaultChecked', + defaultvalue: 'defaultValue', + defer: 'defer', + dir: 'dir', + disabled: 'disabled', + disablepictureinpicture: 'disablePictureInPicture', + disableremoteplayback: 'disableRemotePlayback', + download: 'download', + draggable: 'draggable', + enctype: 'encType', + enterkeyhint: 'enterKeyHint', + fetchpriority: 'fetchPriority', + for: 'htmlFor', + form: 'form', + formmethod: 'formMethod', + formaction: 'formAction', + formenctype: 'formEncType', + formnovalidate: 'formNoValidate', + formtarget: 'formTarget', + frameborder: 'frameBorder', + headers: 'headers', + height: 'height', + hidden: 'hidden', + high: 'high', + href: 'href', + hreflang: 'hrefLang', + htmlfor: 'htmlFor', + httpequiv: 'httpEquiv', + 'http-equiv': 'httpEquiv', + icon: 'icon', + id: 'id', + imagesizes: 'imageSizes', + imagesrcset: 'imageSrcSet', + innerhtml: 'innerHTML', + inputmode: 'inputMode', + integrity: 'integrity', + is: 'is', + itemid: 'itemID', + itemprop: 'itemProp', + itemref: 'itemRef', + itemscope: 'itemScope', + itemtype: 'itemType', + keyparams: 'keyParams', + keytype: 'keyType', + kind: 'kind', + label: 'label', + lang: 'lang', + list: 'list', + loop: 'loop', + low: 'low', + manifest: 'manifest', + marginwidth: 'marginWidth', + marginheight: 'marginHeight', + max: 'max', + maxlength: 'maxLength', + media: 'media', + mediagroup: 'mediaGroup', + method: 'method', + min: 'min', + minlength: 'minLength', + multiple: 'multiple', + muted: 'muted', + name: 'name', + nomodule: 'noModule', + nonce: 'nonce', + novalidate: 'noValidate', + open: 'open', + optimum: 'optimum', + pattern: 'pattern', + placeholder: 'placeholder', + playsinline: 'playsInline', + poster: 'poster', + preload: 'preload', + profile: 'profile', + radiogroup: 'radioGroup', + readonly: 'readOnly', + referrerpolicy: 'referrerPolicy', + rel: 'rel', + required: 'required', + reversed: 'reversed', + role: 'role', + rows: 'rows', + rowspan: 'rowSpan', + sandbox: 'sandbox', + scope: 'scope', + scoped: 'scoped', + scrolling: 'scrolling', + seamless: 'seamless', + selected: 'selected', + shape: 'shape', + size: 'size', + sizes: 'sizes', + span: 'span', + spellcheck: 'spellCheck', + src: 'src', + srcdoc: 'srcDoc', + srclang: 'srcLang', + srcset: 'srcSet', + start: 'start', + step: 'step', + style: 'style', + summary: 'summary', + tabindex: 'tabIndex', + target: 'target', + title: 'title', + type: 'type', + usemap: 'useMap', + value: 'value', + width: 'width', + wmode: 'wmode', + wrap: 'wrap', + // SVG + about: 'about', + accentheight: 'accentHeight', + 'accent-height': 'accentHeight', + accumulate: 'accumulate', + additive: 'additive', + alignmentbaseline: 'alignmentBaseline', + 'alignment-baseline': 'alignmentBaseline', + allowreorder: 'allowReorder', + alphabetic: 'alphabetic', + amplitude: 'amplitude', + arabicform: 'arabicForm', + 'arabic-form': 'arabicForm', + ascent: 'ascent', + attributename: 'attributeName', + attributetype: 'attributeType', + autoreverse: 'autoReverse', + azimuth: 'azimuth', + basefrequency: 'baseFrequency', + baselineshift: 'baselineShift', + 'baseline-shift': 'baselineShift', + baseprofile: 'baseProfile', + bbox: 'bbox', + begin: 'begin', + bias: 'bias', + by: 'by', + calcmode: 'calcMode', + capheight: 'capHeight', + 'cap-height': 'capHeight', + clip: 'clip', + clippath: 'clipPath', + 'clip-path': 'clipPath', + clippathunits: 'clipPathUnits', + cliprule: 'clipRule', + 'clip-rule': 'clipRule', + color: 'color', + colorinterpolation: 'colorInterpolation', + 'color-interpolation': 'colorInterpolation', + colorinterpolationfilters: 'colorInterpolationFilters', + 'color-interpolation-filters': 'colorInterpolationFilters', + colorprofile: 'colorProfile', + 'color-profile': 'colorProfile', + colorrendering: 'colorRendering', + 'color-rendering': 'colorRendering', + contentscripttype: 'contentScriptType', + contentstyletype: 'contentStyleType', + cursor: 'cursor', + cx: 'cx', + cy: 'cy', + d: 'd', + datatype: 'datatype', + decelerate: 'decelerate', + descent: 'descent', + diffuseconstant: 'diffuseConstant', + direction: 'direction', + display: 'display', + divisor: 'divisor', + dominantbaseline: 'dominantBaseline', + 'dominant-baseline': 'dominantBaseline', + dur: 'dur', + dx: 'dx', + dy: 'dy', + edgemode: 'edgeMode', + elevation: 'elevation', + enablebackground: 'enableBackground', + 'enable-background': 'enableBackground', + end: 'end', + exponent: 'exponent', + externalresourcesrequired: 'externalResourcesRequired', + fill: 'fill', + fillopacity: 'fillOpacity', + 'fill-opacity': 'fillOpacity', + fillrule: 'fillRule', + 'fill-rule': 'fillRule', + filter: 'filter', + filterres: 'filterRes', + filterunits: 'filterUnits', + floodopacity: 'floodOpacity', + 'flood-opacity': 'floodOpacity', + floodcolor: 'floodColor', + 'flood-color': 'floodColor', + focusable: 'focusable', + fontfamily: 'fontFamily', + 'font-family': 'fontFamily', + fontsize: 'fontSize', + 'font-size': 'fontSize', + fontsizeadjust: 'fontSizeAdjust', + 'font-size-adjust': 'fontSizeAdjust', + fontstretch: 'fontStretch', + 'font-stretch': 'fontStretch', + fontstyle: 'fontStyle', + 'font-style': 'fontStyle', + fontvariant: 'fontVariant', + 'font-variant': 'fontVariant', + fontweight: 'fontWeight', + 'font-weight': 'fontWeight', + format: 'format', + from: 'from', + fx: 'fx', + fy: 'fy', + g1: 'g1', + g2: 'g2', + glyphname: 'glyphName', + 'glyph-name': 'glyphName', + glyphorientationhorizontal: 'glyphOrientationHorizontal', + 'glyph-orientation-horizontal': 'glyphOrientationHorizontal', + glyphorientationvertical: 'glyphOrientationVertical', + 'glyph-orientation-vertical': 'glyphOrientationVertical', + glyphref: 'glyphRef', + gradienttransform: 'gradientTransform', + gradientunits: 'gradientUnits', + hanging: 'hanging', + horizadvx: 'horizAdvX', + 'horiz-adv-x': 'horizAdvX', + horizoriginx: 'horizOriginX', + 'horiz-origin-x': 'horizOriginX', + ideographic: 'ideographic', + imagerendering: 'imageRendering', + 'image-rendering': 'imageRendering', + in2: 'in2', + in: 'in', + inlist: 'inlist', + intercept: 'intercept', + k1: 'k1', + k2: 'k2', + k3: 'k3', + k4: 'k4', + k: 'k', + kernelmatrix: 'kernelMatrix', + kernelunitlength: 'kernelUnitLength', + kerning: 'kerning', + keypoints: 'keyPoints', + keysplines: 'keySplines', + keytimes: 'keyTimes', + lengthadjust: 'lengthAdjust', + letterspacing: 'letterSpacing', + 'letter-spacing': 'letterSpacing', + lightingcolor: 'lightingColor', + 'lighting-color': 'lightingColor', + limitingconeangle: 'limitingConeAngle', + local: 'local', + markerend: 'markerEnd', + 'marker-end': 'markerEnd', + markerheight: 'markerHeight', + markermid: 'markerMid', + 'marker-mid': 'markerMid', + markerstart: 'markerStart', + 'marker-start': 'markerStart', + markerunits: 'markerUnits', + markerwidth: 'markerWidth', + mask: 'mask', + maskcontentunits: 'maskContentUnits', + maskunits: 'maskUnits', + mathematical: 'mathematical', + mode: 'mode', + numoctaves: 'numOctaves', + offset: 'offset', + opacity: 'opacity', + operator: 'operator', + order: 'order', + orient: 'orient', + orientation: 'orientation', + origin: 'origin', + overflow: 'overflow', + overlineposition: 'overlinePosition', + 'overline-position': 'overlinePosition', + overlinethickness: 'overlineThickness', + 'overline-thickness': 'overlineThickness', + paintorder: 'paintOrder', + 'paint-order': 'paintOrder', + panose1: 'panose1', + 'panose-1': 'panose1', + pathlength: 'pathLength', + patterncontentunits: 'patternContentUnits', + patterntransform: 'patternTransform', + patternunits: 'patternUnits', + pointerevents: 'pointerEvents', + 'pointer-events': 'pointerEvents', + points: 'points', + pointsatx: 'pointsAtX', + pointsaty: 'pointsAtY', + pointsatz: 'pointsAtZ', + prefix: 'prefix', + preservealpha: 'preserveAlpha', + preserveaspectratio: 'preserveAspectRatio', + primitiveunits: 'primitiveUnits', + property: 'property', + r: 'r', + radius: 'radius', + refx: 'refX', + refy: 'refY', + renderingintent: 'renderingIntent', + 'rendering-intent': 'renderingIntent', + repeatcount: 'repeatCount', + repeatdur: 'repeatDur', + requiredextensions: 'requiredExtensions', + requiredfeatures: 'requiredFeatures', + resource: 'resource', + restart: 'restart', + result: 'result', + results: 'results', + rotate: 'rotate', + rx: 'rx', + ry: 'ry', + scale: 'scale', + security: 'security', + seed: 'seed', + shaperendering: 'shapeRendering', + 'shape-rendering': 'shapeRendering', + slope: 'slope', + spacing: 'spacing', + specularconstant: 'specularConstant', + specularexponent: 'specularExponent', + speed: 'speed', + spreadmethod: 'spreadMethod', + startoffset: 'startOffset', + stddeviation: 'stdDeviation', + stemh: 'stemh', + stemv: 'stemv', + stitchtiles: 'stitchTiles', + stopcolor: 'stopColor', + 'stop-color': 'stopColor', + stopopacity: 'stopOpacity', + 'stop-opacity': 'stopOpacity', + strikethroughposition: 'strikethroughPosition', + 'strikethrough-position': 'strikethroughPosition', + strikethroughthickness: 'strikethroughThickness', + 'strikethrough-thickness': 'strikethroughThickness', + string: 'string', + stroke: 'stroke', + strokedasharray: 'strokeDasharray', + 'stroke-dasharray': 'strokeDasharray', + strokedashoffset: 'strokeDashoffset', + 'stroke-dashoffset': 'strokeDashoffset', + strokelinecap: 'strokeLinecap', + 'stroke-linecap': 'strokeLinecap', + strokelinejoin: 'strokeLinejoin', + 'stroke-linejoin': 'strokeLinejoin', + strokemiterlimit: 'strokeMiterlimit', + 'stroke-miterlimit': 'strokeMiterlimit', + strokewidth: 'strokeWidth', + 'stroke-width': 'strokeWidth', + strokeopacity: 'strokeOpacity', + 'stroke-opacity': 'strokeOpacity', + suppresscontenteditablewarning: 'suppressContentEditableWarning', + suppresshydrationwarning: 'suppressHydrationWarning', + surfacescale: 'surfaceScale', + systemlanguage: 'systemLanguage', + tablevalues: 'tableValues', + targetx: 'targetX', + targety: 'targetY', + textanchor: 'textAnchor', + 'text-anchor': 'textAnchor', + textdecoration: 'textDecoration', + 'text-decoration': 'textDecoration', + textlength: 'textLength', + textrendering: 'textRendering', + 'text-rendering': 'textRendering', + to: 'to', + transform: 'transform', + transformorigin: 'transformOrigin', + 'transform-origin': 'transformOrigin', + typeof: 'typeof', + u1: 'u1', + u2: 'u2', + underlineposition: 'underlinePosition', + 'underline-position': 'underlinePosition', + underlinethickness: 'underlineThickness', + 'underline-thickness': 'underlineThickness', + unicode: 'unicode', + unicodebidi: 'unicodeBidi', + 'unicode-bidi': 'unicodeBidi', + unicoderange: 'unicodeRange', + 'unicode-range': 'unicodeRange', + unitsperem: 'unitsPerEm', + 'units-per-em': 'unitsPerEm', + unselectable: 'unselectable', + valphabetic: 'vAlphabetic', + 'v-alphabetic': 'vAlphabetic', + values: 'values', + vectoreffect: 'vectorEffect', + 'vector-effect': 'vectorEffect', + version: 'version', + vertadvy: 'vertAdvY', + 'vert-adv-y': 'vertAdvY', + vertoriginx: 'vertOriginX', + 'vert-origin-x': 'vertOriginX', + vertoriginy: 'vertOriginY', + 'vert-origin-y': 'vertOriginY', + vhanging: 'vHanging', + 'v-hanging': 'vHanging', + videographic: 'vIdeographic', + 'v-ideographic': 'vIdeographic', + viewbox: 'viewBox', + viewtarget: 'viewTarget', + visibility: 'visibility', + vmathematical: 'vMathematical', + 'v-mathematical': 'vMathematical', + vocab: 'vocab', + widths: 'widths', + wordspacing: 'wordSpacing', + 'word-spacing': 'wordSpacing', + writingmode: 'writingMode', + 'writing-mode': 'writingMode', + x1: 'x1', + x2: 'x2', + x: 'x', + xchannelselector: 'xChannelSelector', + xheight: 'xHeight', + 'x-height': 'xHeight', + xlinkactuate: 'xlinkActuate', + 'xlink:actuate': 'xlinkActuate', + xlinkarcrole: 'xlinkArcrole', + 'xlink:arcrole': 'xlinkArcrole', + xlinkhref: 'xlinkHref', + 'xlink:href': 'xlinkHref', + xlinkrole: 'xlinkRole', + 'xlink:role': 'xlinkRole', + xlinkshow: 'xlinkShow', + 'xlink:show': 'xlinkShow', + xlinktitle: 'xlinkTitle', + 'xlink:title': 'xlinkTitle', + xlinktype: 'xlinkType', + 'xlink:type': 'xlinkType', + xmlbase: 'xmlBase', + 'xml:base': 'xmlBase', + xmllang: 'xmlLang', + 'xml:lang': 'xmlLang', + xmlns: 'xmlns', + 'xml:space': 'xmlSpace', + xmlnsxlink: 'xmlnsXlink', + 'xmlns:xlink': 'xmlnsXlink', + xmlspace: 'xmlSpace', + y1: 'y1', + y2: 'y2', + y: 'y', + ychannelselector: 'yChannelSelector', + z: 'z', + zoomandpan: 'zoomAndPan' +}; + +var warnedProperties = {}; +var EVENT_NAME_REGEX = /^on./; +var INVALID_EVENT_NAME_REGEX = /^on[^A-Z]/; +var rARIA = new RegExp('^(aria)-[' + ATTRIBUTE_NAME_CHAR + ']*$') ; +var rARIACamel = new RegExp('^(aria)[A-Z][' + ATTRIBUTE_NAME_CHAR + ']*$') ; + +function validateProperty(tagName, name, value, eventRegistry) { + { + if (hasOwnProperty.call(warnedProperties, name) && warnedProperties[name]) { + return true; + } + + var lowerCasedName = name.toLowerCase(); + + if (lowerCasedName === 'onfocusin' || lowerCasedName === 'onfocusout') { + error('React uses onFocus and onBlur instead of onFocusIn and onFocusOut. ' + 'All React events are normalized to bubble, so onFocusIn and onFocusOut ' + 'are not needed/supported by React.'); + + warnedProperties[name] = true; + return true; + } + + { + // Actions are special because unlike events they can have other value types. + if (typeof value === 'function') { + if (tagName === 'form' && name === 'action') { + return true; + } + + if (tagName === 'input' && name === 'formAction') { + return true; + } + + if (tagName === 'button' && name === 'formAction') { + return true; + } + } + } // We can't rely on the event system being injected on the server. + + + if (eventRegistry != null) { + var registrationNameDependencies = eventRegistry.registrationNameDependencies, + possibleRegistrationNames = eventRegistry.possibleRegistrationNames; + + if (registrationNameDependencies.hasOwnProperty(name)) { + return true; + } + + var registrationName = possibleRegistrationNames.hasOwnProperty(lowerCasedName) ? possibleRegistrationNames[lowerCasedName] : null; + + if (registrationName != null) { + error('Invalid event handler property `%s`. Did you mean `%s`?', name, registrationName); + + warnedProperties[name] = true; + return true; + } + + if (EVENT_NAME_REGEX.test(name)) { + error('Unknown event handler property `%s`. It will be ignored.', name); + + warnedProperties[name] = true; + return true; + } + } else if (EVENT_NAME_REGEX.test(name)) { + // If no event plugins have been injected, we are in a server environment. + // So we can't tell if the event name is correct for sure, but we can filter + // out known bad ones like `onclick`. We can't suggest a specific replacement though. + if (INVALID_EVENT_NAME_REGEX.test(name)) { + error('Invalid event handler property `%s`. ' + 'React events use the camelCase naming convention, for example `onClick`.', name); + } + + warnedProperties[name] = true; + return true; + } // Let the ARIA attribute hook validate ARIA attributes + + + if (rARIA.test(name) || rARIACamel.test(name)) { + return true; + } + + if (lowerCasedName === 'innerhtml') { + error('Directly setting property `innerHTML` is not permitted. ' + 'For more information, lookup documentation on `dangerouslySetInnerHTML`.'); + + warnedProperties[name] = true; + return true; + } + + if (lowerCasedName === 'aria') { + error('The `aria` attribute is reserved for future use in React. ' + 'Pass individual `aria-` attributes instead.'); + + warnedProperties[name] = true; + return true; + } + + if (lowerCasedName === 'is' && value !== null && value !== undefined && typeof value !== 'string') { + error('Received a `%s` for a string attribute `is`. If this is expected, cast ' + 'the value to a string.', typeof value); + + warnedProperties[name] = true; + return true; + } + + if (typeof value === 'number' && isNaN(value)) { + error('Received NaN for the `%s` attribute. If this is expected, cast ' + 'the value to a string.', name); + + warnedProperties[name] = true; + return true; + } // Known attributes should match the casing specified in the property config. + + + if (possibleStandardNames.hasOwnProperty(lowerCasedName)) { + var standardName = possibleStandardNames[lowerCasedName]; + + if (standardName !== name) { + error('Invalid DOM property `%s`. Did you mean `%s`?', name, standardName); + + warnedProperties[name] = true; + return true; + } + } else if (name !== lowerCasedName) { + // Unknown attributes should have lowercase casing since that's how they + // will be cased anyway with server rendering. + error('React does not recognize the `%s` prop on a DOM element. If you ' + 'intentionally want it to appear in the DOM as a custom ' + 'attribute, spell it as lowercase `%s` instead. ' + 'If you accidentally passed it from a parent component, remove ' + 'it from the DOM element.', name, lowerCasedName); + + warnedProperties[name] = true; + return true; + } // Now that we've validated casing, do not validate + // data types for reserved props + + + switch (name) { + case 'dangerouslySetInnerHTML': + case 'children': + case 'style': + case 'suppressContentEditableWarning': + case 'suppressHydrationWarning': + case 'defaultValue': // Reserved + + case 'defaultChecked': + case 'innerHTML': + { + return true; + } + + case 'innerText': // Properties + + case 'textContent': + { + return true; + } + + } + + switch (typeof value) { + case 'boolean': + { + switch (name) { + case 'autoFocus': + case 'checked': + case 'multiple': + case 'muted': + case 'selected': + case 'contentEditable': + case 'spellCheck': + case 'draggable': + case 'value': + case 'autoReverse': + case 'externalResourcesRequired': + case 'focusable': + case 'preserveAlpha': + case 'allowFullScreen': + case 'async': + case 'autoPlay': + case 'controls': + case 'default': + case 'defer': + case 'disabled': + case 'disablePictureInPicture': + case 'disableRemotePlayback': + case 'formNoValidate': + case 'hidden': + case 'loop': + case 'noModule': + case 'noValidate': + case 'open': + case 'playsInline': + case 'readOnly': + case 'required': + case 'reversed': + case 'scoped': + case 'seamless': + case 'itemScope': + case 'capture': + case 'download': + { + // Boolean properties can accept boolean values + return true; + } + + default: + { + var prefix = name.toLowerCase().slice(0, 5); + + if (prefix === 'data-' || prefix === 'aria-') { + return true; + } + + if (value) { + error('Received `%s` for a non-boolean attribute `%s`.\n\n' + 'If you want to write it to the DOM, pass a string instead: ' + '%s="%s" or %s={value.toString()}.', value, name, name, value, name); + } else { + error('Received `%s` for a non-boolean attribute `%s`.\n\n' + 'If you want to write it to the DOM, pass a string instead: ' + '%s="%s" or %s={value.toString()}.\n\n' + 'If you used to conditionally omit it with %s={condition && value}, ' + 'pass %s={condition ? value : undefined} instead.', value, name, name, value, name, name, name); + } + + warnedProperties[name] = true; + return true; + } + } + } + + case 'function': + case 'symbol': + // eslint-disable-line + // Warn when a known attribute is a bad type + warnedProperties[name] = true; + return false; + + case 'string': + { + // Warn when passing the strings 'false' or 'true' into a boolean prop + if (value === 'false' || value === 'true') { + switch (name) { + case 'checked': + case 'selected': + case 'multiple': + case 'muted': + case 'allowFullScreen': + case 'async': + case 'autoPlay': + case 'controls': + case 'default': + case 'defer': + case 'disabled': + case 'disablePictureInPicture': + case 'disableRemotePlayback': + case 'formNoValidate': + case 'hidden': + case 'loop': + case 'noModule': + case 'noValidate': + case 'open': + case 'playsInline': + case 'readOnly': + case 'required': + case 'reversed': + case 'scoped': + case 'seamless': + case 'itemScope': + { + break; + } + + default: + { + return true; + } + } + + error('Received the string `%s` for the boolean attribute `%s`. ' + '%s ' + 'Did you mean %s={%s}?', value, name, value === 'false' ? 'The browser will interpret it as a truthy value.' : 'Although this works, it will not work as expected if you pass the string "false".', name, value); + + warnedProperties[name] = true; + return true; + } + } + } + + return true; + } +} + +function warnUnknownProperties(type, props, eventRegistry) { + { + var unknownProps = []; + + for (var key in props) { + var isValid = validateProperty(type, key, props[key], eventRegistry); + + if (!isValid) { + unknownProps.push(key); + } + } + + var unknownPropString = unknownProps.map(function (prop) { + return '`' + prop + '`'; + }).join(', '); + + if (unknownProps.length === 1) { + error('Invalid value for prop %s on <%s> tag. Either remove it from the element, ' + 'or pass a string or number value to keep it in the DOM. ' + 'For details, see https://reactjs.org/link/attribute-behavior ', unknownPropString, type); + } else if (unknownProps.length > 1) { + error('Invalid values for props %s on <%s> tag. Either remove them from the element, ' + 'or pass a string or number value to keep them in the DOM. ' + 'For details, see https://reactjs.org/link/attribute-behavior ', unknownPropString, type); + } + } +} + +function validateProperties(type, props, eventRegistry) { + if (isCustomElement(type) || typeof props.is === 'string') { + return; + } + + warnUnknownProperties(type, props, eventRegistry); +} + +// 'msTransform' is correct, but the other prefixes should be capitalized +var badVendoredStyleNamePattern = /^(?:webkit|moz|o)[A-Z]/; +var msPattern$1 = /^-ms-/; +var hyphenPattern = /-(.)/g; // style values shouldn't contain a semicolon + +var badStyleValueWithSemicolonPattern = /;\s*$/; +var warnedStyleNames = {}; +var warnedStyleValues = {}; +var warnedForNaNValue = false; +var warnedForInfinityValue = false; + +function camelize(string) { + return string.replace(hyphenPattern, function (_, character) { + return character.toUpperCase(); + }); +} + +function warnHyphenatedStyleName(name) { + { + if (warnedStyleNames.hasOwnProperty(name) && warnedStyleNames[name]) { + return; + } + + warnedStyleNames[name] = true; + + error('Unsupported style property %s. Did you mean %s?', name, // As Andi Smith suggests + // (http://www.andismith.com/blog/2012/02/modernizr-prefixed/), an `-ms` prefix + // is converted to lowercase `ms`. + camelize(name.replace(msPattern$1, 'ms-'))); + } +} + +function warnBadVendoredStyleName(name) { + { + if (warnedStyleNames.hasOwnProperty(name) && warnedStyleNames[name]) { + return; + } + + warnedStyleNames[name] = true; + + error('Unsupported vendor-prefixed style property %s. Did you mean %s?', name, name.charAt(0).toUpperCase() + name.slice(1)); + } +} + +function warnStyleValueWithSemicolon(name, value) { + { + if (warnedStyleValues.hasOwnProperty(value) && warnedStyleValues[value]) { + return; + } + + warnedStyleValues[value] = true; + + error("Style property values shouldn't contain a semicolon. " + 'Try "%s: %s" instead.', name, value.replace(badStyleValueWithSemicolonPattern, '')); + } +} + +function warnStyleValueIsNaN(name, value) { + { + if (warnedForNaNValue) { + return; + } + + warnedForNaNValue = true; + + error('`NaN` is an invalid value for the `%s` css style property.', name); + } +} + +function warnStyleValueIsInfinity(name, value) { + { + if (warnedForInfinityValue) { + return; + } + + warnedForInfinityValue = true; + + error('`Infinity` is an invalid value for the `%s` css style property.', name); + } +} + +function warnValidStyle(name, value) { + { + if (name.indexOf('-') > -1) { + warnHyphenatedStyleName(name); + } else if (badVendoredStyleNamePattern.test(name)) { + warnBadVendoredStyleName(name); + } else if (badStyleValueWithSemicolonPattern.test(value)) { + warnStyleValueWithSemicolon(name, value); + } + + if (typeof value === 'number') { + if (isNaN(value)) { + warnStyleValueIsNaN(name); + } else if (!isFinite(value)) { + warnStyleValueIsInfinity(name); + } + } + } +} + +// code copied and modified from escape-html +var matchHtmlRegExp = /["'&<>]/; +/** + * Escapes special characters and HTML entities in a given html string. + * + * @param {string} string HTML string to escape for later insertion + * @return {string} + * @public + */ + +function escapeHtml(string) { + { + checkHtmlStringCoercion(string); + } + + var str = '' + string; + var match = matchHtmlRegExp.exec(str); + + if (!match) { + return str; + } + + var escape; + var html = ''; + var index; + var lastIndex = 0; + + for (index = match.index; index < str.length; index++) { + switch (str.charCodeAt(index)) { + case 34: + // " + escape = '"'; + break; + + case 38: + // & + escape = '&'; + break; + + case 39: + // ' + escape = '''; // modified from escape-html; used to be ''' + + break; + + case 60: + // < + escape = '<'; + break; + + case 62: + // > + escape = '>'; + break; + + default: + continue; + } + + if (lastIndex !== index) { + html += str.slice(lastIndex, index); + } + + lastIndex = index + 1; + html += escape; + } + + return lastIndex !== index ? html + str.slice(lastIndex, index) : html; +} // end code copied and modified from escape-html + +/** + * Escapes text to prevent scripting attacks. + * + * @param {*} text Text value to escape. + * @return {string} An escaped string. + */ + + +function escapeTextForBrowser(text) { + if (typeof text === 'boolean' || typeof text === 'number') { + // this shortcircuit helps perf for types that we know will never have + // special characters, especially given that this function is used often + // for numeric dom ids. + return '' + text; + } + + return escapeHtml(text); +} + +var uppercasePattern = /([A-Z])/g; +var msPattern = /^ms-/; +/** + * Hyphenates a camelcased CSS property name, for example: + * + * > hyphenateStyleName('backgroundColor') + * < "background-color" + * > hyphenateStyleName('MozTransition') + * < "-moz-transition" + * > hyphenateStyleName('msTransition') + * < "-ms-transition" + * + * As Modernizr suggests (http://modernizr.com/docs/#prefixed), an `ms` prefix + * is converted to `-ms-`. + */ + +function hyphenateStyleName(name) { + return name.replace(uppercasePattern, '-$1').toLowerCase().replace(msPattern, '-ms-'); +} + +// and any newline or tab are filtered out as if they're not part of the URL. +// https://url.spec.whatwg.org/#url-parsing +// Tab or newline are defined as \r\n\t: +// https://infra.spec.whatwg.org/#ascii-tab-or-newline +// A C0 control is a code point in the range \u0000 NULL to \u001F +// INFORMATION SEPARATOR ONE, inclusive: +// https://infra.spec.whatwg.org/#c0-control-or-space + +/* eslint-disable max-len */ + +var isJavaScriptProtocol = /^[\u0000-\u001F ]*j[\r\n\t]*a[\r\n\t]*v[\r\n\t]*a[\r\n\t]*s[\r\n\t]*c[\r\n\t]*r[\r\n\t]*i[\r\n\t]*p[\r\n\t]*t[\r\n\t]*\:/i; +var didWarn = false; + +function sanitizeURL(url) { + // We should never have symbols here because they get filtered out elsewhere. + // eslint-disable-next-line react-internal/safe-string-coercion + var stringifiedURL = '' + url; + + { + if (!didWarn && isJavaScriptProtocol.test(stringifiedURL)) { + didWarn = true; + + error('A future version of React will block javascript: URLs as a security precaution. ' + 'Use event handlers instead if you can. If you need to generate unsafe HTML try ' + 'using dangerouslySetInnerHTML instead. React was passed %s.', JSON.stringify(stringifiedURL)); + } + } + + return url; +} + +var isArrayImpl = Array.isArray; // eslint-disable-next-line no-redeclare + +function isArray(a) { + return isArrayImpl(a); +} + +// The build script is at scripts/rollup/generate-inline-fizz-runtime.js. +// Run `yarn generate-inline-fizz-runtime` to generate. +var clientRenderBoundary = '$RX=function(b,c,d,e){var a=document.getElementById(b);a&&(b=a.previousSibling,b.data="$!",a=a.dataset,c&&(a.dgst=c),d&&(a.msg=d),e&&(a.stck=e),b._reactRetry&&b._reactRetry())};'; +var completeBoundary = '$RC=function(b,c,e){c=document.getElementById(c);c.parentNode.removeChild(c);var a=document.getElementById(b);if(a){b=a.previousSibling;if(e)b.data="$!",a.setAttribute("data-dgst",e);else{e=b.parentNode;a=b.nextSibling;var f=0;do{if(a&&8===a.nodeType){var d=a.data;if("/$"===d)if(0===f)break;else f--;else"$"!==d&&"$?"!==d&&"$!"!==d||f++}d=a.nextSibling;e.removeChild(a);a=d}while(a);for(;c.firstChild;)e.insertBefore(c.firstChild,a);b.data="$"}b._reactRetry&&b._reactRetry()}};'; +var completeBoundaryWithStyles = '$RM=new Map;\n$RR=function(r,t,w){for(var u=$RC,n=$RM,p=new Map,q=document,g,b,h=q.querySelectorAll("link[data-precedence],style[data-precedence]"),v=[],k=0;b=h[k++];)"not all"===b.getAttribute("media")?v.push(b):("LINK"===b.tagName&&n.set(b.getAttribute("href"),b),p.set(b.dataset.precedence,g=b));b=0;h=[];var l,a;for(k=!0;;){if(k){var f=w[b++];if(!f){k=!1;b=0;continue}var c=!1,m=0;var d=f[m++];if(a=n.get(d)){var e=a._p;c=!0}else{a=q.createElement("link");a.href=d;a.rel="stylesheet";for(a.dataset.precedence=\nl=f[m++];e=f[m++];)a.setAttribute(e,f[m++]);e=a._p=new Promise(function(x,y){a.onload=x;a.onerror=y});n.set(d,a)}d=a.getAttribute("media");!e||"l"===e.s||d&&!matchMedia(d).matches||h.push(e);if(c)continue}else{a=v[b++];if(!a)break;l=a.getAttribute("data-precedence");a.removeAttribute("media")}c=p.get(l)||g;c===g&&(g=a);p.set(l,a);c?c.parentNode.insertBefore(a,c.nextSibling):(c=q.head,c.insertBefore(a,c.firstChild))}Promise.all(h).then(u.bind(null,r,t,""),u.bind(null,r,t,"Resource failed to load"))};'; +var completeSegment = '$RS=function(a,b){a=document.getElementById(a);b=document.getElementById(b);for(a.parentNode.removeChild(a);a.firstChild;)b.parentNode.insertBefore(a.firstChild,b);b.parentNode.removeChild(b)};'; +var formReplaying = 'addEventListener("submit",function(a){if(!a.defaultPrevented){var c=a.target,d=a.submitter,e=c.action,b=d;if(d){var f=d.getAttribute("formAction");null!=f&&(e=f,b=null)}"javascript:throw new Error(\'A React form was unexpectedly submitted.\')"===e&&(a.preventDefault(),b?(a=document.createElement("input"),a.name=b.name,a.value=b.value,b.parentNode.insertBefore(a,b),b=new FormData(c),a.parentNode.removeChild(a)):b=new FormData(c),a=c.getRootNode(),(a.$$reactFormReplay=a.$$reactFormReplay||[]).push(c,\nd,b))}});'; + +function getValueDescriptorExpectingObjectForWarning(thing) { + return thing === null ? '`null`' : thing === undefined ? '`undefined`' : thing === '' ? 'an empty string' : "something with type \"" + typeof thing + "\""; +} +function getValueDescriptorExpectingEnumForWarning(thing) { + return thing === null ? '`null`' : thing === undefined ? '`undefined`' : thing === '' ? 'an empty string' : typeof thing === 'string' ? JSON.stringify(thing) : "something with type \"" + typeof thing + "\""; +} + +function compareResourcePropsForWarning(newProps, currentProps) { + { + var propDiffs = null; + var allProps = Array.from(new Set(Object.keys(currentProps).concat(Object.keys(newProps)))); + + for (var i = 0; i < allProps.length; i++) { + var propName = allProps[i]; + var newValue = newProps[propName]; + var currentValue = currentProps[propName]; + + if (newValue !== currentValue && !(newValue == null && currentValue == null)) { + if (newValue == null) { + if (propDiffs === null) { + propDiffs = { + missing: {}, + extra: {}, + different: {} + }; + } + + propDiffs.missing[propName] = currentValue; + } else if (currentValue == null) { + if (propDiffs === null) { + propDiffs = { + missing: {}, + extra: {}, + different: {} + }; + } + + propDiffs.extra[propName] = newValue; + } else { + if (propDiffs === null) { + propDiffs = { + missing: {}, + extra: {}, + different: {} + }; + } + + propDiffs.different[propName] = { + original: currentValue, + latest: newValue + }; + } + } + } + + return propDiffs; + } +} + +function describeDifferencesForStylesheets(newProps, currentProps) { + var diff = compareResourcePropsForWarning(newProps, currentProps); + if (!diff) return ''; + var description = ''; + + for (var propName in diff.missing) { + var propValue = diff.missing[propName]; + + if (propName === 'media') { + description += "\n \"" + propName + "\" missing for props, original value: " + getValueDescriptorExpectingEnumForWarning(propValue); + } + } + + for (var _propName in diff.extra) { + var _propValue = diff.extra[_propName]; + description += "\n \"" + _propName + "\" prop value: " + getValueDescriptorExpectingEnumForWarning(_propValue) + ", missing from original props"; + } + + for (var _propName2 in diff.different) { + var latestValue = diff.different[_propName2].latest; + var originalValue = diff.different[_propName2].original; + description += "\n \"" + _propName2 + "\" prop value: " + getValueDescriptorExpectingEnumForWarning(latestValue) + ", original value: " + getValueDescriptorExpectingEnumForWarning(originalValue); + } + + return description; +} +function describeDifferencesForStylesheetOverPreinit(newProps, currentProps) { + var diff = compareResourcePropsForWarning(newProps, currentProps); + if (!diff) return ''; + var description = ''; + + for (var propName in diff.extra) { + var propValue = diff.extra[propName]; + + if (propName === 'precedence' || propName === 'crossOrigin' || propName === 'integrity') { + description += "\n \"" + propName + "\" prop value: " + getValueDescriptorExpectingEnumForWarning(propValue) + ", option missing"; + } else { + description += "\n \"" + propName + "\" prop value: " + getValueDescriptorExpectingEnumForWarning(propValue) + ", option not available with ReactDOM.preinit()"; + } + } + + for (var _propName3 in diff.different) { + var latestValue = diff.different[_propName3].latest; + var originalValue = diff.different[_propName3].original; + + if (_propName3 === 'precedence' && originalValue === 'default') { + description += "\n \"" + _propName3 + "\" prop value: " + getValueDescriptorExpectingEnumForWarning(latestValue) + ", missing from options"; + } else { + description += "\n \"" + _propName3 + "\" prop value: " + getValueDescriptorExpectingEnumForWarning(latestValue) + ", option value: " + getValueDescriptorExpectingEnumForWarning(originalValue); + } + } + + return description; +} +function describeDifferencesForPreinitOverStylesheet(newProps, currentProps) { + var diff = compareResourcePropsForWarning(newProps, currentProps); + if (!diff) return ''; + var description = ''; + + for (var propName in diff.missing) { + var propValue = diff.missing[propName]; + + if (propName === 'precedence' && propValue !== 'default') { + description += "\n \"" + propName + "\" missing from options, prop value: " + getValueDescriptorExpectingEnumForWarning(propValue); + } + } + + for (var _propName4 in diff.extra) { + var _propValue2 = diff.extra[_propName4]; + + if (_propName4 === 'precedence' || _propName4 === 'crossOrigin' || _propName4 === 'integrity') { + description += "\n \"" + _propName4 + "\" option value: " + getValueDescriptorExpectingEnumForWarning(_propValue2) + ", missing from props"; + } + } + + for (var _propName5 in diff.different) { + var latestValue = diff.different[_propName5].latest; + var originalValue = diff.different[_propName5].original; + description += "\n \"" + _propName5 + "\" option value: " + getValueDescriptorExpectingEnumForWarning(latestValue) + ", prop value: " + getValueDescriptorExpectingEnumForWarning(originalValue); + } + + return description; +} +function describeDifferencesForPreinits(newProps, currentProps) { + var diff = compareResourcePropsForWarning(newProps, currentProps); + if (!diff) return ''; + var description = ''; + + for (var propName in diff.missing) { + var propValue = diff.missing[propName]; + + if (propName === 'precedence' && propValue !== 'default') { + description += "\n \"" + propName + "\" missing from options, original option value: " + getValueDescriptorExpectingEnumForWarning(propValue); + } + } + + for (var _propName6 in diff.extra) { + var _propValue3 = diff.extra[_propName6]; + + if (_propName6 === 'precedence' && _propValue3 !== 'default' || _propName6 === 'crossOrigin' || _propName6 === 'integrity') { + description += "\n \"" + _propName6 + "\" option value: " + getValueDescriptorExpectingEnumForWarning(_propValue3) + ", missing from original options"; + } + } + + for (var _propName7 in diff.different) { + var latestValue = diff.different[_propName7].latest; + var originalValue = diff.different[_propName7].original; + description += "\n \"" + _propName7 + "\" option value: " + getValueDescriptorExpectingEnumForWarning(latestValue) + ", original option value: " + getValueDescriptorExpectingEnumForWarning(originalValue); + } + + return description; +} +var preloadOptionsForComparison = ['as', 'crossOrigin', 'integrity', 'media']; +function describeDifferencesForPreloads(newProps, currentProps) { + var diff = compareResourcePropsForWarning(newProps, currentProps); + if (!diff) return ''; + var description = ''; + + for (var propName in diff.missing) { + var propValue = diff.missing[propName]; + + if (preloadOptionsForComparison.includes(propName)) { + description += "\n \"" + propName + "\" missing from options, original option value: " + getValueDescriptorExpectingEnumForWarning(propValue); + } + } + + for (var _propName8 in diff.extra) { + var _propValue4 = diff.extra[_propName8]; + + if (preloadOptionsForComparison.includes(_propName8)) { + description += "\n \"" + _propName8 + "\" option value: " + getValueDescriptorExpectingEnumForWarning(_propValue4) + ", missing from original options"; + } + } + + for (var _propName9 in diff.different) { + var latestValue = diff.different[_propName9].latest; + var originalValue = diff.different[_propName9].original; + + if (preloadOptionsForComparison.includes(_propName9)) { + description += "\n \"" + _propName9 + "\" option value: " + getValueDescriptorExpectingEnumForWarning(latestValue) + ", original option value: " + getValueDescriptorExpectingEnumForWarning(originalValue); + } + } + + return description; +} +function describeDifferencesForPreloadOverImplicitPreload(newProps, currentProps) { + var diff = compareResourcePropsForWarning(newProps, currentProps); + if (!diff) return ''; + var description = ''; + + for (var propName in diff.missing) { + var propValue = diff.missing[propName]; + + if (preloadOptionsForComparison.includes(propName)) { + description += "\n \"" + propName + "\" missing from options, underlying prop value: " + getValueDescriptorExpectingEnumForWarning(propValue); + } + } + + for (var _propName10 in diff.extra) { + var _propValue5 = diff.extra[_propName10]; + + if (preloadOptionsForComparison.includes(_propName10)) { + description += "\n \"" + _propName10 + "\" option value: " + getValueDescriptorExpectingEnumForWarning(_propValue5) + ", missing from underlying props"; + } + } + + for (var _propName11 in diff.different) { + var latestValue = diff.different[_propName11].latest; + var originalValue = diff.different[_propName11].original; + + if (preloadOptionsForComparison.includes(_propName11)) { + description += "\n \"" + _propName11 + "\" option value: " + getValueDescriptorExpectingEnumForWarning(latestValue) + ", underlying prop value: " + getValueDescriptorExpectingEnumForWarning(originalValue); + } + } + + return description; +} +function describeDifferencesForScripts(newProps, currentProps) { + var diff = compareResourcePropsForWarning(newProps, currentProps); + if (!diff) return ''; + var description = ''; + + for (var propName in diff.missing) { + var propValue = diff.missing[propName]; + description += "\n \"" + propName + "\" missing for props, original value: " + getValueDescriptorExpectingEnumForWarning(propValue); + } + + for (var _propName12 in diff.extra) { + var _propValue6 = diff.extra[_propName12]; + description += "\n \"" + _propName12 + "\" prop value: " + getValueDescriptorExpectingEnumForWarning(_propValue6) + ", missing from original props"; + } + + for (var _propName13 in diff.different) { + var latestValue = diff.different[_propName13].latest; + var originalValue = diff.different[_propName13].original; + description += "\n \"" + _propName13 + "\" prop value: " + getValueDescriptorExpectingEnumForWarning(latestValue) + ", original value: " + getValueDescriptorExpectingEnumForWarning(originalValue); + } + + return description; +} +function describeDifferencesForScriptOverPreinit(newProps, currentProps) { + var diff = compareResourcePropsForWarning(newProps, currentProps); + if (!diff) return ''; + var description = ''; + + for (var propName in diff.extra) { + var propValue = diff.extra[propName]; + + if (propName === 'crossOrigin' || propName === 'integrity') { + description += "\n \"" + propName + "\" prop value: " + getValueDescriptorExpectingEnumForWarning(propValue) + ", option missing"; + } else { + description += "\n \"" + propName + "\" prop value: " + getValueDescriptorExpectingEnumForWarning(propValue) + ", option not available with ReactDOM.preinit()"; + } + } + + for (var _propName14 in diff.different) { + var latestValue = diff.different[_propName14].latest; + var originalValue = diff.different[_propName14].original; + description += "\n \"" + _propName14 + "\" prop value: " + getValueDescriptorExpectingEnumForWarning(latestValue) + ", option value: " + getValueDescriptorExpectingEnumForWarning(originalValue); + } + + return description; +} +function describeDifferencesForPreinitOverScript(newProps, currentProps) { + var diff = compareResourcePropsForWarning(newProps, currentProps); + if (!diff) return ''; + var description = ''; + + for (var propName in diff.extra) { + var propValue = diff.extra[propName]; + + if (propName === 'crossOrigin' || propName === 'integrity') { + description += "\n \"" + propName + "\" option value: " + getValueDescriptorExpectingEnumForWarning(propValue) + ", missing from props"; + } + } + + for (var _propName15 in diff.different) { + var latestValue = diff.different[_propName15].latest; + var originalValue = diff.different[_propName15].original; + description += "\n \"" + _propName15 + "\" option value: " + getValueDescriptorExpectingEnumForWarning(latestValue) + ", prop value: " + getValueDescriptorExpectingEnumForWarning(originalValue); + } + + return description; +} + +// same object across all transitions. + +var sharedNotPendingObject = { + pending: false, + data: null, + method: null, + action: null +}; +var NotPending = Object.freeze(sharedNotPendingObject) ; + +var ReactDOMSharedInternals = ReactDOM.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED; + +var ReactDOMCurrentDispatcher = ReactDOMSharedInternals.Dispatcher; +var ReactDOMServerDispatcher = { + prefetchDNS: prefetchDNS, + preconnect: preconnect, + preload: preload, + preinit: preinit +}; +function prepareHostDispatcher() { + ReactDOMCurrentDispatcher.current = ReactDOMServerDispatcher; +} // Used to distinguish these contexts from ones used in other renderers. +var ScriptStreamingFormat = 0; +var DataStreamingFormat = 1; +var NothingSent +/* */ += 0; +var SentCompleteSegmentFunction +/* */ += 1; +var SentCompleteBoundaryFunction +/* */ += 2; +var SentClientRenderFunction +/* */ += 4; +var SentStyleInsertionFunction +/* */ += 8; +var SentFormReplayingRuntime +/* */ += 16; // Per response, global state that is not contextual to the rendering subtree. + +var dataElementQuotedEnd = stringToPrecomputedChunk('">'); +var startInlineScript = stringToPrecomputedChunk(''); +var startScriptSrc = stringToPrecomputedChunk(''); +/** + * This escaping function is designed to work with bootstrapScriptContent only. + * because we know we are escaping the entire script. We can avoid for instance + * escaping html comment string sequences that are valid javascript as well because + * if there are no sebsequent '); +var completeSegmentData1 = stringToPrecomputedChunk('