mirror of
https://github.com/LukeHagar/vercel.git
synced 2025-12-11 12:57:46 +00:00
Compare commits
61 Commits
@vercel/py
...
@vercel/bu
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c224fb1d2e | ||
|
|
5014b1e82a | ||
|
|
6a8a84907b | ||
|
|
722adf0e10 | ||
|
|
bc5fd41158 | ||
|
|
e87d4c14d0 | ||
|
|
bc95d764b4 | ||
|
|
1b5ce5644f | ||
|
|
18d1703d5b | ||
|
|
5c6f9c0b66 | ||
|
|
809740b385 | ||
|
|
bf9acd023d | ||
|
|
f4ba9cdcf8 | ||
|
|
11584b0e9b | ||
|
|
3023122d4e | ||
|
|
0e774b6be0 | ||
|
|
119f80e961 | ||
|
|
9fe92d7de0 | ||
|
|
0817527f9e | ||
|
|
6476f4f786 | ||
|
|
a5ea04154b | ||
|
|
5a532a5b94 | ||
|
|
c1d8522950 | ||
|
|
50fc27ba57 | ||
|
|
3101d2497f | ||
|
|
4b0716b556 | ||
|
|
124846a3e6 | ||
|
|
30cd0ec898 | ||
|
|
dc974b6797 | ||
|
|
58c6755e0c | ||
|
|
f18575fcd0 | ||
|
|
d6412be6bc | ||
|
|
52e435aa5d | ||
|
|
303256343a | ||
|
|
88f3a816d9 | ||
|
|
3420ba0153 | ||
|
|
3fb97d1d27 | ||
|
|
f11c7024c4 | ||
|
|
5eb4cc6f5d | ||
|
|
b1adaf76ec | ||
|
|
1bf04ba421 | ||
|
|
15475c8a2c | ||
|
|
1c8f0a4c33 | ||
|
|
ba65da97e1 | ||
|
|
28b6b810be | ||
|
|
9fc9a10c82 | ||
|
|
aef56edc35 | ||
|
|
e6a3497fb1 | ||
|
|
763a6d1070 | ||
|
|
21f5e7375e | ||
|
|
256d0fd463 | ||
|
|
5b79603378 | ||
|
|
863199aa0a | ||
|
|
97e02ae506 | ||
|
|
bd9236f228 | ||
|
|
26bc51e0d6 | ||
|
|
21b87d2ee6 | ||
|
|
f765c24689 | ||
|
|
0f12005a68 | ||
|
|
5758838d09 | ||
|
|
9dab15dc88 |
4
.github/CODEOWNERS
vendored
4
.github/CODEOWNERS
vendored
@@ -5,13 +5,13 @@
|
||||
* @TooTallNate @EndangeredMassa @trek @onsclom @jeffsee55 @erikareads
|
||||
/.github/workflows @TooTallNate @EndangeredMassa @trek @onsclom @jeffsee55 @erikareads @ijjk
|
||||
/packages/fs-detectors @TooTallNate @EndangeredMassa @trek @onsclom @jeffsee55 @erikareads @agadzik @chloetedder
|
||||
/packages/next @TooTallNate @EndangeredMassa @trek @onsclom @jeffsee55 @erikareads @timneutkens @ijjk @ztanner @huozhi @Ethan-Arrowood @styfle
|
||||
/packages/next @TooTallNate @EndangeredMassa @trek @onsclom @jeffsee55 @erikareads @timneutkens @ijjk @ztanner @huozhi @Ethan-Arrowood
|
||||
/packages/routing-utils @TooTallNate @EndangeredMassa @trek @onsclom @jeffsee55 @erikareads @ijjk
|
||||
/packages/static-build @TooTallNate @EndangeredMassa @trek @onsclom @jeffsee55 @erikareads
|
||||
/packages/edge @TooTallNate @EndangeredMassa @trek @onsclom @jeffsee55 @erikareads @vercel/compute
|
||||
/examples @TooTallNate @EndangeredMassa @trek @onsclom @jeffsee55 @erikareads @leerob
|
||||
/examples/create-react-app @TooTallNate @EndangeredMassa @trek @onsclom @jeffsee55 @erikareads @Timer
|
||||
/examples/nextjs @TooTallNate @EndangeredMassa @trek @onsclom @jeffsee55 @erikareads @timneutkens @ijjk @ztanner @huozhi @Ethan-Arrowood @styfle
|
||||
/examples/nextjs @TooTallNate @EndangeredMassa @trek @onsclom @jeffsee55 @erikareads @timneutkens @ijjk @ztanner @huozhi @Ethan-Arrowood
|
||||
/packages/node @TooTallNate @EndangeredMassa @trek @onsclom @jeffsee55 @erikareads @Kikobeats
|
||||
|
||||
# Unrestricted Paths
|
||||
|
||||
29
.github/dependabot.yml
vendored
29
.github/dependabot.yml
vendored
@@ -78,7 +78,7 @@ updates:
|
||||
update-types:
|
||||
- 'minor'
|
||||
|
||||
- directory: /packages/static-build/test/fixtures/ionic-angular-v7
|
||||
- directory: /packages/static-build/test/fixtures/ionic-angular-v8
|
||||
schedule:
|
||||
interval: 'daily'
|
||||
open-pull-requests-limit: 1
|
||||
@@ -175,6 +175,33 @@ updates:
|
||||
update-types:
|
||||
- 'minor'
|
||||
|
||||
- directory: /packages/static-build/test/fixtures/parcel-v2
|
||||
schedule:
|
||||
interval: 'daily'
|
||||
open-pull-requests-limit: 1
|
||||
reviewers:
|
||||
- 'trek'
|
||||
- 'TooTallNate'
|
||||
- 'EndangeredMassa'
|
||||
- 'erikareads'
|
||||
- 'jeffsee55'
|
||||
- 'onsclom'
|
||||
commit-message:
|
||||
prefix: '[framework-fixtures]'
|
||||
package-ecosystem: 'npm'
|
||||
allow:
|
||||
- dependency-name: 'parcel'
|
||||
ignore:
|
||||
- dependency-name: 'parcel'
|
||||
update-types:
|
||||
['version-update:semver-major', 'version-update:semver-patch']
|
||||
groups:
|
||||
core:
|
||||
patterns:
|
||||
- 'parcel'
|
||||
update-types:
|
||||
- 'minor'
|
||||
|
||||
- directory: /packages/static-build/test/fixtures/preact-v10
|
||||
schedule:
|
||||
interval: 'daily'
|
||||
|
||||
124
.github/workflows/test-18.yml
vendored
Normal file
124
.github/workflows/test-18.yml
vendored
Normal file
@@ -0,0 +1,124 @@
|
||||
name: Tests Node.js v18
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
tags:
|
||||
- '!*'
|
||||
pull_request:
|
||||
|
||||
env:
|
||||
NODE_VERSION: '18'
|
||||
TURBO_REMOTE_ONLY: 'true'
|
||||
TURBO_TEAM: 'vercel'
|
||||
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: ${{ github.ref != 'refs/heads/main' }}
|
||||
|
||||
jobs:
|
||||
setup:
|
||||
name: Find Changes
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
tests: ${{ steps['set-tests'].outputs['tests'] }}
|
||||
dplUrl: ${{ steps.waitForTarball.outputs.url }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 2
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: ${{ env.NODE_VERSION }}
|
||||
- name: install pnpm@8.3.1
|
||||
run: npm i -g pnpm@8.3.1
|
||||
- run: pnpm install
|
||||
- id: set-tests
|
||||
run: |
|
||||
TESTS_ARRAY=$(node utils/chunk-tests-18.js $SCRIPT_NAME)
|
||||
echo "Files to test:"
|
||||
echo "$TESTS_ARRAY"
|
||||
echo "tests=$TESTS_ARRAY" >> $GITHUB_OUTPUT
|
||||
- uses: patrickedqvist/wait-for-vercel-preview@bfdff514ff78a669f2536e9f4dd4ef5813a704a2
|
||||
id: waitForTarball
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
max_timeout: 360
|
||||
check_interval: 5
|
||||
test:
|
||||
timeout-minutes: 120
|
||||
runs-on: ${{ matrix.runner }}
|
||||
name: ${{matrix.scriptName}} (${{matrix.packageName}}, ${{matrix.chunkNumber}}, ${{ matrix.runner }})
|
||||
if: ${{ needs.setup.outputs['tests'] != '[]' }}
|
||||
needs:
|
||||
- setup
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include: ${{ fromJson(needs.setup.outputs['tests']) }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 2
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: ${{ matrix.nodeVersion || env.NODE_VERSION }}
|
||||
|
||||
# yarn 1.22.21 introduced a Corepack bug when running tests.
|
||||
# this can be removed once https://github.com/yarnpkg/yarn/issues/9015 is resolved
|
||||
- name: install yarn@1.22.19
|
||||
run: npm i -g yarn@1.22.19
|
||||
|
||||
- name: install pnpm@8.3.1
|
||||
run: npm i -g pnpm@8.3.1
|
||||
|
||||
- run: pnpm install
|
||||
|
||||
- name: Build ${{matrix.packageName}} and all its dependencies
|
||||
run: node utils/gen.js && node_modules/.bin/turbo run build --cache-dir=".turbo" --log-order=stream --scope=${{matrix.packageName}} --include-dependencies --no-deps
|
||||
env:
|
||||
FORCE_COLOR: '1'
|
||||
- name: Test ${{matrix.packageName}}
|
||||
run: node utils/gen.js && node_modules/.bin/turbo run ${{matrix.testScript}} --summarize --cache-dir=".turbo" --log-order=stream --scope=${{matrix.packageName}} --no-deps -- ${{ join(matrix.testPaths, ' ') }}
|
||||
shell: bash
|
||||
env:
|
||||
JEST_JUNIT_OUTPUT_FILE: ${{github.workspace}}/.junit-reports/${{matrix.scriptName}}-${{matrix.packageName}}-${{matrix.chunkNumber}}-${{ matrix.runner }}.xml
|
||||
VERCEL_CLI_VERSION: ${{ needs.setup.outputs.dplUrl }}/tarballs/vercel.tgz
|
||||
VERCEL_TEST_TOKEN: ${{ secrets.VERCEL_TEST_TOKEN }}
|
||||
VERCEL_TEST_REGISTRATION_URL: ${{ secrets.VERCEL_TEST_REGISTRATION_URL }}
|
||||
FORCE_COLOR: '1'
|
||||
- name: 'Determine Turbo HIT or MISS'
|
||||
if: ${{ !cancelled() }}
|
||||
id: turbo-summary
|
||||
shell: bash
|
||||
run: |
|
||||
TURBO_MISS_COUNT=`node utils/determine-turbo-hit-or-miss.js`
|
||||
echo "MISS COUNT: $TURBO_MISS_COUNT"
|
||||
echo "misses=$TURBO_MISS_COUNT" >> $GITHUB_OUTPUT
|
||||
- name: 'Upload Test Report to Datadog'
|
||||
if: ${{ steps['turbo-summary'].outputs.misses != '0' && !cancelled() }}
|
||||
run: 'npx @datadog/datadog-ci@2.18.1 junit upload --service vercel-cli .junit-reports'
|
||||
env:
|
||||
DATADOG_API_KEY: ${{secrets.DATADOG_API_KEY_CLI}}
|
||||
DD_ENV: ci
|
||||
|
||||
summary:
|
||||
name: Summary
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 5
|
||||
if: always()
|
||||
needs:
|
||||
- test
|
||||
steps:
|
||||
- name: Check All
|
||||
run: |-
|
||||
for status in ${{ join(needs.*.result, ' ') }}
|
||||
do
|
||||
if [ "$status" != "success" ] && [ "$status" != "skipped" ]
|
||||
then
|
||||
echo "Some checks failed"
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
2
.github/workflows/test.yml
vendored
2
.github/workflows/test.yml
vendored
@@ -1,4 +1,4 @@
|
||||
name: Tests
|
||||
name: Tests Node.js v16
|
||||
|
||||
on:
|
||||
push:
|
||||
|
||||
@@ -37,7 +37,7 @@ You can use the `dev` script to run local changes as if you were invoking Vercel
|
||||
|
||||
When contributing to this repository, please first discuss the change you wish to make via [GitHub Discussions](https://github.com/vercel/vercel/discussions/new) with the owners of this repository before submitting a Pull Request.
|
||||
|
||||
Please read our [Code of Conduct](CODE_OF_CONDUCT.md) and follow it in all your interactions with the project.
|
||||
Please read our [Code of Conduct](./.github/CODE_OF_CONDUCT.md) and follow it in all your interactions with the project.
|
||||
|
||||
### Local development
|
||||
|
||||
|
||||
110
examples/nextjs/package-lock.json
generated
110
examples/nextjs/package-lock.json
generated
@@ -8,7 +8,7 @@
|
||||
"name": "nextjs",
|
||||
"version": "0.1.0",
|
||||
"dependencies": {
|
||||
"next": "14.2.2",
|
||||
"next": "14.2.3",
|
||||
"react": "^18",
|
||||
"react-dom": "^18"
|
||||
},
|
||||
@@ -17,7 +17,7 @@
|
||||
"@types/react": "^18",
|
||||
"@types/react-dom": "^18",
|
||||
"eslint": "^8",
|
||||
"eslint-config-next": "14.2.2",
|
||||
"eslint-config-next": "14.2.3",
|
||||
"postcss": "^8",
|
||||
"tailwindcss": "^3.4.1",
|
||||
"typescript": "^5"
|
||||
@@ -238,23 +238,23 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@next/env": {
|
||||
"version": "14.2.2",
|
||||
"resolved": "https://registry.npmjs.org/@next/env/-/env-14.2.2.tgz",
|
||||
"integrity": "sha512-sk72qRfM1Q90XZWYRoJKu/UWlTgihrASiYw/scb15u+tyzcze3bOuJ/UV6TBOQEeUaxOkRqGeuGUdiiuxc5oqw=="
|
||||
"version": "14.2.3",
|
||||
"resolved": "https://registry.npmjs.org/@next/env/-/env-14.2.3.tgz",
|
||||
"integrity": "sha512-W7fd7IbkfmeeY2gXrzJYDx8D2lWKbVoTIj1o1ScPHNzvp30s1AuoEFSdr39bC5sjxJaxTtq3OTCZboNp0lNWHA=="
|
||||
},
|
||||
"node_modules/@next/eslint-plugin-next": {
|
||||
"version": "14.2.2",
|
||||
"resolved": "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-14.2.2.tgz",
|
||||
"integrity": "sha512-q+Ec2648JtBpKiu/FSJm8HAsFXlNvioHeBCbTP12T1SGcHYwhqHULSfQgFkPgHDu3kzNp2Kem4J54bK4rPQ5SQ==",
|
||||
"version": "14.2.3",
|
||||
"resolved": "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-14.2.3.tgz",
|
||||
"integrity": "sha512-L3oDricIIjgj1AVnRdRor21gI7mShlSwU/1ZGHmqM3LzHhXXhdkrfeNY5zif25Bi5Dd7fiJHsbhoZCHfXYvlAw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"glob": "10.3.10"
|
||||
}
|
||||
},
|
||||
"node_modules/@next/swc-darwin-arm64": {
|
||||
"version": "14.2.2",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-14.2.2.tgz",
|
||||
"integrity": "sha512-3iPgMhzbalizGwHNFUcGnDhFPSgVBHQ8aqSTAMxB5BvJG0oYrDf1WOJZlbXBgunOEj/8KMVbejEur/FpvFsgFQ==",
|
||||
"version": "14.2.3",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-14.2.3.tgz",
|
||||
"integrity": "sha512-3pEYo/RaGqPP0YzwnlmPN2puaF2WMLM3apt5jLW2fFdXD9+pqcoTzRk+iZsf8ta7+quAe4Q6Ms0nR0SFGFdS1A==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -267,9 +267,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@next/swc-darwin-x64": {
|
||||
"version": "14.2.2",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-14.2.2.tgz",
|
||||
"integrity": "sha512-x7Afi/jt0ZBRUZHTi49yyej4o8znfIMHO4RvThuoc0P+uli8Jd99y5GKjxoYunPKsXL09xBXEM1+OQy2xEL0Ag==",
|
||||
"version": "14.2.3",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-14.2.3.tgz",
|
||||
"integrity": "sha512-6adp7waE6P1TYFSXpY366xwsOnEXM+y1kgRpjSRVI2CBDOcbRjsJ67Z6EgKIqWIue52d2q/Mx8g9MszARj8IEA==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -282,9 +282,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@next/swc-linux-arm64-gnu": {
|
||||
"version": "14.2.2",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-14.2.2.tgz",
|
||||
"integrity": "sha512-zbfPtkk7L41ODMJwSp5VbmPozPmMMQrzAc0HAUomVeVIIwlDGs/UCqLJvLNDt4jpWgc21SjjyIn762lNGrMaUA==",
|
||||
"version": "14.2.3",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-14.2.3.tgz",
|
||||
"integrity": "sha512-cuzCE/1G0ZSnTAHJPUT1rPgQx1w5tzSX7POXSLaS7w2nIUJUD+e25QoXD/hMfxbsT9rslEXugWypJMILBj/QsA==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -297,9 +297,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@next/swc-linux-arm64-musl": {
|
||||
"version": "14.2.2",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-14.2.2.tgz",
|
||||
"integrity": "sha512-wPbS3pI/JU16rm3XdLvvTmlsmm1nd+sBa2ohXgBZcShX4TgOjD4R+RqHKlI1cjo/jDZKXt6OxmcU0Iys0OC/yg==",
|
||||
"version": "14.2.3",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-14.2.3.tgz",
|
||||
"integrity": "sha512-0D4/oMM2Y9Ta3nGuCcQN8jjJjmDPYpHX9OJzqk42NZGJocU2MqhBq5tWkJrUQOQY9N+In9xOdymzapM09GeiZw==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -312,9 +312,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@next/swc-linux-x64-gnu": {
|
||||
"version": "14.2.2",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-14.2.2.tgz",
|
||||
"integrity": "sha512-NqWOHqqq8iC9tuHvZxjQ2tX+jWy2X9y8NX2mcB4sj2bIccuCxbIZrU/ThFPZZPauygajZuVQ6zediejQHwZHwQ==",
|
||||
"version": "14.2.3",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-14.2.3.tgz",
|
||||
"integrity": "sha512-ENPiNnBNDInBLyUU5ii8PMQh+4XLr4pG51tOp6aJ9xqFQ2iRI6IH0Ds2yJkAzNV1CfyagcyzPfROMViS2wOZ9w==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -327,9 +327,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@next/swc-linux-x64-musl": {
|
||||
"version": "14.2.2",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-14.2.2.tgz",
|
||||
"integrity": "sha512-lGepHhwb9sGhCcU7999+iK1ZZT+6rrIoVg40MP7DZski9GIZP80wORSbt5kJzh9v2x2ev2lxC6VgwMQT0PcgTA==",
|
||||
"version": "14.2.3",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-14.2.3.tgz",
|
||||
"integrity": "sha512-BTAbq0LnCbF5MtoM7I/9UeUu/8ZBY0i8SFjUMCbPDOLv+un67e2JgyN4pmgfXBwy/I+RHu8q+k+MCkDN6P9ViQ==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -342,9 +342,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@next/swc-win32-arm64-msvc": {
|
||||
"version": "14.2.2",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-14.2.2.tgz",
|
||||
"integrity": "sha512-TZSh/48SfcLEQ4rD25VVn2kdIgUWmMflRX3OiyPwGNXn3NiyPqhqei/BaqCYXViIQ+6QsG9R0C8LftMqy8JPMA==",
|
||||
"version": "14.2.3",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-14.2.3.tgz",
|
||||
"integrity": "sha512-AEHIw/dhAMLNFJFJIJIyOFDzrzI5bAjI9J26gbO5xhAKHYTZ9Or04BesFPXiAYXDNdrwTP2dQceYA4dL1geu8A==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -357,9 +357,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@next/swc-win32-ia32-msvc": {
|
||||
"version": "14.2.2",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-14.2.2.tgz",
|
||||
"integrity": "sha512-M0tBVNMEBJN2ZNQWlcekMn6pvLria7Sa2Fai5znm7CCJz4pP3lrvlSxhKdkCerk0D9E0bqx5yAo3o2Q7RrD4gA==",
|
||||
"version": "14.2.3",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-14.2.3.tgz",
|
||||
"integrity": "sha512-vga40n1q6aYb0CLrM+eEmisfKCR45ixQYXuBXxOOmmoV8sYST9k7E3US32FsY+CkkF7NtzdcebiFT4CHuMSyZw==",
|
||||
"cpu": [
|
||||
"ia32"
|
||||
],
|
||||
@@ -372,9 +372,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@next/swc-win32-x64-msvc": {
|
||||
"version": "14.2.2",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-14.2.2.tgz",
|
||||
"integrity": "sha512-a/20E/wtTJZ3Ykv3f/8F0l7TtgQa2LWHU2oNB9bsu0VjqGuGGHmm/q6waoUNQYTVPYrrlxxaHjJcDV6aiSTt/w==",
|
||||
"version": "14.2.3",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-14.2.3.tgz",
|
||||
"integrity": "sha512-Q1/zm43RWynxrO7lW4ehciQVj+5ePBhOK+/K2P7pLFX3JaJ/IZVC69SHidrmZSOkqz7ECIOhhy7XhAFG4JYyHA==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -1021,9 +1021,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/caniuse-lite": {
|
||||
"version": "1.0.30001610",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001610.tgz",
|
||||
"integrity": "sha512-QFutAY4NgaelojVMjY63o6XlZyORPaLfyMnsl3HgnWdJUcX6K0oaJymHjH8PT5Gk7sTm8rvC/c5COUQKXqmOMA==",
|
||||
"version": "1.0.30001612",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001612.tgz",
|
||||
"integrity": "sha512-lFgnZ07UhaCcsSZgWW0K5j4e69dK1u/ltrL9lTUiFOwNHs12S3UMIEYgBV0Z6C6hRDev7iRnMzzYmKabYdXF9g==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "opencollective",
|
||||
@@ -1571,12 +1571,12 @@
|
||||
}
|
||||
},
|
||||
"node_modules/eslint-config-next": {
|
||||
"version": "14.2.2",
|
||||
"resolved": "https://registry.npmjs.org/eslint-config-next/-/eslint-config-next-14.2.2.tgz",
|
||||
"integrity": "sha512-12/uFc0KX+wUs7EDpOUGKMXBXZJiBVGdK5/m/QgXOCg2mQ0bQWoKSWNrCeOg7Vum6Kw1d1TW453W6xh+GbHquw==",
|
||||
"version": "14.2.3",
|
||||
"resolved": "https://registry.npmjs.org/eslint-config-next/-/eslint-config-next-14.2.3.tgz",
|
||||
"integrity": "sha512-ZkNztm3Q7hjqvB1rRlOX8P9E/cXRL9ajRcs8jufEtwMfTVYRqnmtnaSu57QqHyBlovMuiB8LEzfLBkh5RYV6Fg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@next/eslint-plugin-next": "14.2.2",
|
||||
"@next/eslint-plugin-next": "14.2.3",
|
||||
"@rushstack/eslint-patch": "^1.3.3",
|
||||
"@typescript-eslint/parser": "^5.4.2 || ^6.0.0 || 7.0.0 - 7.2.0",
|
||||
"eslint-import-resolver-node": "^0.3.6",
|
||||
@@ -3117,11 +3117,11 @@
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/next": {
|
||||
"version": "14.2.2",
|
||||
"resolved": "https://registry.npmjs.org/next/-/next-14.2.2.tgz",
|
||||
"integrity": "sha512-oGwUaa2bCs47FbuxWMpOoXtBMPYpvTPgdZr3UAo+pu7Ns00z9otmYpoeV1HEiYL06AlRQQIA/ypK526KjJfaxg==",
|
||||
"version": "14.2.3",
|
||||
"resolved": "https://registry.npmjs.org/next/-/next-14.2.3.tgz",
|
||||
"integrity": "sha512-dowFkFTR8v79NPJO4QsBUtxv0g9BrS/phluVpMAt2ku7H+cbcBJlopXjkWlwxrk/xGqMemr7JkGPGemPrLLX7A==",
|
||||
"dependencies": {
|
||||
"@next/env": "14.2.2",
|
||||
"@next/env": "14.2.3",
|
||||
"@swc/helpers": "0.5.5",
|
||||
"busboy": "1.6.0",
|
||||
"caniuse-lite": "^1.0.30001579",
|
||||
@@ -3136,15 +3136,15 @@
|
||||
"node": ">=18.17.0"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@next/swc-darwin-arm64": "14.2.2",
|
||||
"@next/swc-darwin-x64": "14.2.2",
|
||||
"@next/swc-linux-arm64-gnu": "14.2.2",
|
||||
"@next/swc-linux-arm64-musl": "14.2.2",
|
||||
"@next/swc-linux-x64-gnu": "14.2.2",
|
||||
"@next/swc-linux-x64-musl": "14.2.2",
|
||||
"@next/swc-win32-arm64-msvc": "14.2.2",
|
||||
"@next/swc-win32-ia32-msvc": "14.2.2",
|
||||
"@next/swc-win32-x64-msvc": "14.2.2"
|
||||
"@next/swc-darwin-arm64": "14.2.3",
|
||||
"@next/swc-darwin-x64": "14.2.3",
|
||||
"@next/swc-linux-arm64-gnu": "14.2.3",
|
||||
"@next/swc-linux-arm64-musl": "14.2.3",
|
||||
"@next/swc-linux-x64-gnu": "14.2.3",
|
||||
"@next/swc-linux-x64-musl": "14.2.3",
|
||||
"@next/swc-win32-arm64-msvc": "14.2.3",
|
||||
"@next/swc-win32-ia32-msvc": "14.2.3",
|
||||
"@next/swc-win32-x64-msvc": "14.2.3"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@opentelemetry/api": "^1.1.0",
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
"dependencies": {
|
||||
"react": "^18",
|
||||
"react-dom": "^18",
|
||||
"next": "14.2.2"
|
||||
"next": "14.2.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"typescript": "^5",
|
||||
@@ -21,6 +21,6 @@
|
||||
"postcss": "^8",
|
||||
"tailwindcss": "^3.4.1",
|
||||
"eslint": "^8",
|
||||
"eslint-config-next": "14.2.2"
|
||||
"eslint-config-next": "14.2.3"
|
||||
}
|
||||
}
|
||||
|
||||
2
examples/package.json
vendored
2
examples/package.json
vendored
@@ -9,7 +9,7 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/jest": "27.4.1",
|
||||
"@vercel/build-utils": "7.12.0",
|
||||
"@vercel/build-utils": "8.1.0",
|
||||
"@vercel/frameworks": "3.0.2"
|
||||
},
|
||||
"version": null
|
||||
|
||||
@@ -9,18 +9,18 @@
|
||||
"typecheck": "tsc"
|
||||
},
|
||||
"dependencies": {
|
||||
"@remix-run/node": "^2.8.0",
|
||||
"@remix-run/react": "^2.8.0",
|
||||
"@remix-run/server-runtime": "^2.8.0",
|
||||
"@remix-run/node": "^2.9.2",
|
||||
"@remix-run/react": "^2.9.2",
|
||||
"@remix-run/server-runtime": "^2.9.2",
|
||||
"@vercel/analytics": "^1.2.2",
|
||||
"@vercel/remix": "^2.8.0",
|
||||
"@vercel/remix": "2.9.2-patch.2",
|
||||
"isbot": "^4",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@remix-run/dev": "^2.8.0",
|
||||
"@remix-run/eslint-config": "^2.8.0",
|
||||
"@remix-run/dev": "^2.9.2",
|
||||
"@remix-run/eslint-config": "^2.9.2",
|
||||
"@types/react": "^18.2.20",
|
||||
"@types/react-dom": "^18.2.7",
|
||||
"eslint": "^8.38.0",
|
||||
|
||||
@@ -1,5 +1,19 @@
|
||||
# @vercel-internals/types
|
||||
|
||||
## 1.0.32
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [[`5014b1e82`](https://github.com/vercel/vercel/commit/5014b1e82a46181baeb727ffe6d14000b6a4b1d7)]:
|
||||
- @vercel/build-utils@8.1.0
|
||||
|
||||
## 1.0.31
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [[`15475c8a2`](https://github.com/vercel/vercel/commit/15475c8a2c303a1dd189ba24044fac750280dd2e), [`21f5e7375`](https://github.com/vercel/vercel/commit/21f5e7375e4cb4ceed98ab56486d09a85fa3894d)]:
|
||||
- @vercel/build-utils@8.0.0
|
||||
|
||||
## 1.0.30
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"private": true,
|
||||
"name": "@vercel-internals/types",
|
||||
"version": "1.0.30",
|
||||
"version": "1.0.32",
|
||||
"types": "index.d.ts",
|
||||
"main": "index.d.ts",
|
||||
"files": [
|
||||
@@ -10,7 +10,7 @@
|
||||
"dependencies": {
|
||||
"@types/node": "14.14.31",
|
||||
"@vercel-internals/constants": "1.0.4",
|
||||
"@vercel/build-utils": "7.12.0",
|
||||
"@vercel/build-utils": "8.1.0",
|
||||
"@vercel/routing-utils": "3.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
@@ -33,7 +33,7 @@
|
||||
"source-map-support": "0.5.12",
|
||||
"ts-eager": "2.0.2",
|
||||
"ts-jest": "29.1.0",
|
||||
"turbo": "1.13.2",
|
||||
"turbo": "1.13.3",
|
||||
"typescript": "4.9.5"
|
||||
},
|
||||
"scripts": {
|
||||
|
||||
@@ -1,5 +1,22 @@
|
||||
# @vercel/build-utils
|
||||
|
||||
## 8.1.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
- Update pnpm version detection logic ([#11445](https://github.com/vercel/vercel/pull/11445))
|
||||
Add support for pnpm 9
|
||||
|
||||
## 8.0.0
|
||||
|
||||
### Major Changes
|
||||
|
||||
- Remove legacy `avoidTopLevelInstall` logic ([#11513](https://github.com/vercel/vercel/pull/11513))
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- [build-utils] Add `VERCEL_PROJECT_PRODUCTION_URL` framework env var prefix ([#11506](https://github.com/vercel/vercel/pull/11506))
|
||||
|
||||
## 7.12.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vercel/build-utils",
|
||||
"version": "7.12.0",
|
||||
"version": "8.1.0",
|
||||
"license": "Apache-2.0",
|
||||
"main": "./dist/index.js",
|
||||
"types": "./dist/index.d.js",
|
||||
|
||||
@@ -546,8 +546,7 @@ export function getEnvForPackageManager({
|
||||
detectedLockfile,
|
||||
detectedPackageManager,
|
||||
path: newPath,
|
||||
yarnNodeLinker,
|
||||
} = getPathForPackageManager({
|
||||
} = getPathOverrideForPackageManager({
|
||||
cliType,
|
||||
lockfileVersion,
|
||||
nodeVersion,
|
||||
@@ -558,36 +557,172 @@ export function getEnvForPackageManager({
|
||||
...env,
|
||||
};
|
||||
|
||||
if (newPath) {
|
||||
const alreadyInPath = (newPath: string) => {
|
||||
const oldPath = env.PATH ?? '';
|
||||
return oldPath.split(path.delimiter).includes(newPath);
|
||||
};
|
||||
|
||||
if (newPath && !alreadyInPath(newPath)) {
|
||||
// Ensure that the binaries of the detected package manager are at the
|
||||
// beginning of the `$PATH`.
|
||||
const oldPath = env.PATH + '';
|
||||
newEnv.PATH = `${newPath}${path.delimiter}${oldPath}`;
|
||||
}
|
||||
|
||||
if (yarnNodeLinker) {
|
||||
newEnv.YARN_NODE_LINKER = yarnNodeLinker;
|
||||
}
|
||||
if (detectedLockfile && detectedPackageManager) {
|
||||
// For pnpm we also show the version of the lockfile we found
|
||||
const versionString =
|
||||
cliType === 'pnpm' ? `version ${lockfileVersion} ` : '';
|
||||
|
||||
if (detectedLockfile && detectedPackageManager) {
|
||||
// For pnpm we also show the version of the lockfile we found
|
||||
const versionString =
|
||||
cliType === 'pnpm' ? `version ${lockfileVersion} ` : '';
|
||||
|
||||
console.log(
|
||||
`Detected \`${detectedLockfile}\` ${versionString}generated by ${detectedPackageManager}`
|
||||
);
|
||||
|
||||
if (cliType === 'bun') {
|
||||
console.warn(
|
||||
'Warning: Bun is used as a package manager at build time only, not at runtime with Functions'
|
||||
console.log(
|
||||
`Detected \`${detectedLockfile}\` ${versionString}generated by ${detectedPackageManager}`
|
||||
);
|
||||
|
||||
if (cliType === 'bun') {
|
||||
console.warn(
|
||||
'Warning: Bun is used as a package manager at build time only, not at runtime with Functions'
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (cliType === 'yarn' && !env.YARN_NODE_LINKER) {
|
||||
newEnv.YARN_NODE_LINKER = 'node-modules';
|
||||
}
|
||||
|
||||
return newEnv;
|
||||
}
|
||||
|
||||
type DetectedPnpmVersion =
|
||||
| 'not found'
|
||||
| 'pnpm 7'
|
||||
| 'pnpm 8'
|
||||
| 'pnpm 9'
|
||||
| 'corepack_enabled';
|
||||
|
||||
function detectPnpmVersion(
|
||||
lockfileVersion: number | undefined,
|
||||
corepackEnabled: boolean
|
||||
): DetectedPnpmVersion {
|
||||
switch (true) {
|
||||
case corepackEnabled:
|
||||
return 'corepack_enabled';
|
||||
case lockfileVersion === undefined:
|
||||
return 'not found';
|
||||
case lockfileVersion === 5.3 || lockfileVersion === 5.4:
|
||||
return 'pnpm 7';
|
||||
case lockfileVersion === 6.0 || lockfileVersion === 6.1:
|
||||
return 'pnpm 8';
|
||||
case lockfileVersion === 7.0 || lockfileVersion === 9.0:
|
||||
return 'pnpm 9';
|
||||
default:
|
||||
return 'not found';
|
||||
}
|
||||
}
|
||||
|
||||
function shouldUseNpm7(
|
||||
lockfileVersion: number | undefined,
|
||||
nodeVersion: NodeVersion | undefined
|
||||
): boolean {
|
||||
if (lockfileVersion === undefined) return false;
|
||||
return lockfileVersion >= 2 && (nodeVersion?.major || 0) < 16;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to get the binary paths that link to the used package manager.
|
||||
* Note: Make sure it doesn't contain any `console.log` calls.
|
||||
*/
|
||||
export function getPathOverrideForPackageManager({
|
||||
cliType,
|
||||
lockfileVersion,
|
||||
nodeVersion,
|
||||
env,
|
||||
}: {
|
||||
cliType: CliType;
|
||||
lockfileVersion: number | undefined;
|
||||
nodeVersion: NodeVersion | undefined;
|
||||
env: { [x: string]: string | undefined };
|
||||
}): {
|
||||
/**
|
||||
* Which lockfile was detected.
|
||||
*/
|
||||
detectedLockfile: string | undefined;
|
||||
/**
|
||||
* Detected package manager that generated the found lockfile.
|
||||
*/
|
||||
detectedPackageManager: string | undefined;
|
||||
/**
|
||||
* Value of $PATH that includes the binaries for the detected package manager.
|
||||
* Undefined if no $PATH are necessary.
|
||||
*/
|
||||
path: string | undefined;
|
||||
} {
|
||||
const no_override = {
|
||||
detectedLockfile: undefined,
|
||||
detectedPackageManager: undefined,
|
||||
path: undefined,
|
||||
};
|
||||
|
||||
const corepackEnabled = env.ENABLE_EXPERIMENTAL_COREPACK === '1';
|
||||
|
||||
switch (cliType) {
|
||||
case 'npm':
|
||||
switch (true) {
|
||||
case corepackEnabled:
|
||||
return no_override;
|
||||
case shouldUseNpm7(lockfileVersion, nodeVersion):
|
||||
return {
|
||||
path: '/node16/bin-npm7',
|
||||
detectedLockfile: 'package-lock.json',
|
||||
detectedPackageManager: 'npm 7+',
|
||||
};
|
||||
default:
|
||||
return no_override;
|
||||
}
|
||||
case 'pnpm':
|
||||
switch (detectPnpmVersion(lockfileVersion, corepackEnabled)) {
|
||||
case 'corepack_enabled':
|
||||
return no_override;
|
||||
case 'pnpm 7':
|
||||
// pnpm 7
|
||||
return {
|
||||
path: '/pnpm7/node_modules/.bin',
|
||||
detectedLockfile: 'pnpm-lock.yaml',
|
||||
detectedPackageManager: 'pnpm 7',
|
||||
};
|
||||
case 'pnpm 8':
|
||||
// pnpm 8
|
||||
return {
|
||||
path: '/pnpm8/node_modules/.bin',
|
||||
detectedLockfile: 'pnpm-lock.yaml',
|
||||
detectedPackageManager: 'pnpm 8',
|
||||
};
|
||||
case 'pnpm 9':
|
||||
// pnpm 9
|
||||
return {
|
||||
path: '/pnpm9/node_modules/.bin',
|
||||
detectedLockfile: 'pnpm-lock.yaml',
|
||||
detectedPackageManager: 'pnpm 9',
|
||||
};
|
||||
default:
|
||||
return no_override;
|
||||
}
|
||||
case 'bun':
|
||||
switch (true) {
|
||||
case corepackEnabled:
|
||||
return no_override;
|
||||
default:
|
||||
// Bun 1
|
||||
return {
|
||||
path: '/bun1',
|
||||
detectedLockfile: 'bun.lockb',
|
||||
detectedPackageManager: 'Bun',
|
||||
};
|
||||
}
|
||||
case 'yarn':
|
||||
return no_override;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to get the binary paths that link to the used package manager.
|
||||
* Note: Make sure it doesn't contain any `console.log` calls.
|
||||
@@ -622,70 +757,31 @@ export function getPathForPackageManager({
|
||||
*/
|
||||
yarnNodeLinker: string | undefined;
|
||||
} {
|
||||
let detectedLockfile: string | undefined;
|
||||
let detectedPackageManager: string | undefined;
|
||||
let pathValue: string | undefined;
|
||||
let yarnNodeLinker: string | undefined;
|
||||
const oldPath = env.PATH + '';
|
||||
const npm7 = '/node16/bin-npm7';
|
||||
const pnpm7 = '/pnpm7/node_modules/.bin';
|
||||
const pnpm8 = '/pnpm8/node_modules/.bin';
|
||||
const bun1 = '/bun1';
|
||||
const corepackEnabled = env.ENABLE_EXPERIMENTAL_COREPACK === '1';
|
||||
if (cliType === 'npm') {
|
||||
if (
|
||||
typeof lockfileVersion === 'number' &&
|
||||
lockfileVersion >= 2 &&
|
||||
(nodeVersion?.major || 0) < 16 &&
|
||||
!oldPath.includes(npm7) &&
|
||||
!corepackEnabled
|
||||
) {
|
||||
// npm 7
|
||||
pathValue = npm7;
|
||||
detectedLockfile = 'package-lock.json';
|
||||
detectedPackageManager = 'npm 7+';
|
||||
}
|
||||
} else if (cliType === 'pnpm') {
|
||||
if (
|
||||
typeof lockfileVersion === 'number' &&
|
||||
lockfileVersion === 5.4 &&
|
||||
!oldPath.includes(pnpm7) &&
|
||||
!corepackEnabled
|
||||
) {
|
||||
// pnpm 7
|
||||
pathValue = pnpm7;
|
||||
detectedLockfile = 'pnpm-lock.yaml';
|
||||
detectedPackageManager = 'pnpm 7';
|
||||
} else if (
|
||||
typeof lockfileVersion === 'number' &&
|
||||
lockfileVersion >= 6 &&
|
||||
!oldPath.includes(pnpm8) &&
|
||||
!corepackEnabled
|
||||
) {
|
||||
// pnpm 8
|
||||
pathValue = pnpm8;
|
||||
detectedLockfile = 'pnpm-lock.yaml';
|
||||
detectedPackageManager = 'pnpm 8';
|
||||
}
|
||||
} else if (cliType === 'bun') {
|
||||
if (!oldPath.includes(bun1) && !corepackEnabled) {
|
||||
// Bun 1
|
||||
pathValue = bun1;
|
||||
detectedLockfile = 'bun.lockb';
|
||||
detectedPackageManager = 'Bun';
|
||||
}
|
||||
} else {
|
||||
// Yarn v2 PnP mode may be activated, so force "node-modules" linker style
|
||||
if (!env.YARN_NODE_LINKER) {
|
||||
yarnNodeLinker = 'node-modules';
|
||||
}
|
||||
}
|
||||
return {
|
||||
detectedLockfile,
|
||||
detectedPackageManager,
|
||||
path: pathValue,
|
||||
yarnNodeLinker,
|
||||
const overrides = getPathOverrideForPackageManager({
|
||||
cliType,
|
||||
lockfileVersion,
|
||||
nodeVersion,
|
||||
env,
|
||||
});
|
||||
|
||||
const alreadyInPath = (newPath: string) => {
|
||||
const oldPath = env.PATH ?? '';
|
||||
return oldPath.split(path.delimiter).includes(newPath);
|
||||
};
|
||||
|
||||
switch (true) {
|
||||
case cliType === 'yarn' && !env.YARN_NODE_LINKER:
|
||||
return { ...overrides, yarnNodeLinker: 'node-modules' };
|
||||
case alreadyInPath(overrides.path ?? ''):
|
||||
return {
|
||||
detectedLockfile: undefined,
|
||||
detectedPackageManager: undefined,
|
||||
path: undefined,
|
||||
yarnNodeLinker: undefined,
|
||||
};
|
||||
default:
|
||||
return { ...overrides, yarnNodeLinker: undefined };
|
||||
}
|
||||
}
|
||||
|
||||
export async function runCustomInstallCommand({
|
||||
|
||||
@@ -19,6 +19,7 @@ export function getPrefixedEnvVars({
|
||||
'VERCEL_ENV',
|
||||
'VERCEL_REGION',
|
||||
'VERCEL_BRANCH_URL',
|
||||
'VERCEL_PROJECT_PRODUCTION_URL',
|
||||
];
|
||||
const newEnvs: Envs = {};
|
||||
if (envPrefix && envs.VERCEL_URL) {
|
||||
|
||||
@@ -66,7 +66,6 @@ export interface Meta {
|
||||
filesRemoved?: string[];
|
||||
env?: Env;
|
||||
buildEnv?: Env;
|
||||
avoidTopLevelInstall?: boolean;
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
"probes": [
|
||||
{
|
||||
"path": "/",
|
||||
"mustContain": "pnpm version: 6",
|
||||
"mustContain": "pnpm version: 7",
|
||||
"logMustContain": "pnpm run build"
|
||||
}
|
||||
]
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { delimiter } from 'path';
|
||||
import { getEnvForPackageManager, getPathForPackageManager } from '../src';
|
||||
import { getPathOverrideForPackageManager } from '../src/fs/run-user-scripts';
|
||||
|
||||
describe('Test `getEnvForPackageManager()`', () => {
|
||||
let consoleLogSpy: jest.SpyInstance;
|
||||
@@ -171,6 +172,44 @@ describe('Test `getEnvForPackageManager()`', () => {
|
||||
'Detected `pnpm-lock.yaml` version 5.4 generated by pnpm 7',
|
||||
consoleWarnOutput: null,
|
||||
},
|
||||
{
|
||||
name: 'should set path if pnpm 8 is detected',
|
||||
args: {
|
||||
cliType: 'pnpm',
|
||||
nodeVersion: { major: 18, range: '18.x', runtime: 'nodejs18.x' },
|
||||
lockfileVersion: 6.0,
|
||||
env: {
|
||||
FOO: 'bar',
|
||||
PATH: 'foo',
|
||||
},
|
||||
},
|
||||
want: {
|
||||
FOO: 'bar',
|
||||
PATH: `/pnpm8/node_modules/.bin${delimiter}foo`,
|
||||
},
|
||||
consoleLogOutput:
|
||||
'Detected `pnpm-lock.yaml` version 6 generated by pnpm 8',
|
||||
consoleWarnOutput: null,
|
||||
},
|
||||
{
|
||||
name: 'should set path if pnpm 8 is detected',
|
||||
args: {
|
||||
cliType: 'pnpm',
|
||||
nodeVersion: { major: 18, range: '18.x', runtime: 'nodejs18.x' },
|
||||
lockfileVersion: 9.0,
|
||||
env: {
|
||||
FOO: 'bar',
|
||||
PATH: 'foo',
|
||||
},
|
||||
},
|
||||
want: {
|
||||
FOO: 'bar',
|
||||
PATH: `/pnpm9/node_modules/.bin${delimiter}foo`,
|
||||
},
|
||||
consoleLogOutput:
|
||||
'Detected `pnpm-lock.yaml` version 9 generated by pnpm 9',
|
||||
consoleWarnOutput: null,
|
||||
},
|
||||
{
|
||||
name: 'should set path if bun v1 is detected',
|
||||
args: {
|
||||
@@ -226,22 +265,6 @@ describe('Test `getEnvForPackageManager()`', () => {
|
||||
consoleLogOutput: null,
|
||||
consoleWarnOutput: null,
|
||||
},
|
||||
{
|
||||
name: 'should not set path if pnpm 6 is detected',
|
||||
args: {
|
||||
cliType: 'pnpm',
|
||||
nodeVersion: { major: 14, range: '14.x', runtime: 'nodejs14.x' },
|
||||
lockfileVersion: 5.3,
|
||||
env: {
|
||||
FOO: 'bar',
|
||||
},
|
||||
},
|
||||
want: {
|
||||
FOO: 'bar',
|
||||
},
|
||||
consoleLogOutput: null,
|
||||
consoleWarnOutput: null,
|
||||
},
|
||||
])('$name', ({ args, want, consoleLogOutput, consoleWarnOutput }) => {
|
||||
expect(
|
||||
getEnvForPackageManager({
|
||||
@@ -268,6 +291,176 @@ describe('Test `getEnvForPackageManager()`', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('Test `getPathOverrideForPackageManager()`', () => {
|
||||
test.each<{
|
||||
name: string;
|
||||
args: Parameters<typeof getEnvForPackageManager>[0];
|
||||
want: unknown;
|
||||
}>([
|
||||
{
|
||||
name: 'should do nothing to env for npm < 6 and node < 16',
|
||||
args: {
|
||||
cliType: 'npm',
|
||||
nodeVersion: { major: 14, range: '14.x', runtime: 'nodejs14.x' },
|
||||
lockfileVersion: 1,
|
||||
env: {
|
||||
FOO: 'bar',
|
||||
},
|
||||
},
|
||||
want: {
|
||||
detectedLockfile: undefined,
|
||||
detectedPackageManager: undefined,
|
||||
path: undefined,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'should set path if npm 7+ is detected and node < 16',
|
||||
args: {
|
||||
cliType: 'npm',
|
||||
nodeVersion: { major: 14, range: '14.x', runtime: 'nodejs14.x' },
|
||||
lockfileVersion: 2,
|
||||
env: {
|
||||
FOO: 'bar',
|
||||
PATH: 'foo',
|
||||
},
|
||||
},
|
||||
want: {
|
||||
detectedLockfile: 'package-lock.json',
|
||||
detectedPackageManager: 'npm 7+',
|
||||
path: '/node16/bin-npm7',
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'should not set npm path if corepack enabled',
|
||||
args: {
|
||||
cliType: 'npm',
|
||||
nodeVersion: { major: 14, range: '14.x', runtime: 'nodejs14.x' },
|
||||
lockfileVersion: 2,
|
||||
env: {
|
||||
FOO: 'bar',
|
||||
ENABLE_EXPERIMENTAL_COREPACK: '1',
|
||||
},
|
||||
},
|
||||
want: {
|
||||
detectedLockfile: undefined,
|
||||
detectedPackageManager: undefined,
|
||||
path: undefined,
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
name: 'should not set path if node is 16 and npm 7+ is detected',
|
||||
args: {
|
||||
cliType: 'npm',
|
||||
nodeVersion: { major: 16, range: '16.x', runtime: 'nodejs16.x' },
|
||||
lockfileVersion: 2,
|
||||
env: {
|
||||
FOO: 'bar',
|
||||
PATH: 'foo',
|
||||
},
|
||||
},
|
||||
want: {
|
||||
detectedLockfile: undefined,
|
||||
detectedPackageManager: undefined,
|
||||
path: undefined,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'should set path if pnpm 7+ is detected',
|
||||
args: {
|
||||
cliType: 'pnpm',
|
||||
nodeVersion: { major: 16, range: '16.x', runtime: 'nodejs16.x' },
|
||||
lockfileVersion: 5.4,
|
||||
env: {
|
||||
FOO: 'bar',
|
||||
PATH: 'foo',
|
||||
},
|
||||
},
|
||||
want: {
|
||||
detectedLockfile: 'pnpm-lock.yaml',
|
||||
detectedPackageManager: 'pnpm 7',
|
||||
path: '/pnpm7/node_modules/.bin',
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'should set path if pnpm 8 is detected',
|
||||
args: {
|
||||
cliType: 'pnpm',
|
||||
nodeVersion: { major: 18, range: '18.x', runtime: 'nodejs18.x' },
|
||||
lockfileVersion: 6.1,
|
||||
env: {
|
||||
FOO: 'bar',
|
||||
PATH: 'foo',
|
||||
},
|
||||
},
|
||||
want: {
|
||||
detectedLockfile: 'pnpm-lock.yaml',
|
||||
detectedPackageManager: 'pnpm 8',
|
||||
path: '/pnpm8/node_modules/.bin',
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'should set path if pnpm 9 is detected',
|
||||
args: {
|
||||
cliType: 'pnpm',
|
||||
nodeVersion: { major: 18, range: '18.x', runtime: 'nodejs18.x' },
|
||||
lockfileVersion: 7.0,
|
||||
env: {
|
||||
FOO: 'bar',
|
||||
PATH: 'foo',
|
||||
},
|
||||
},
|
||||
want: {
|
||||
detectedLockfile: 'pnpm-lock.yaml',
|
||||
detectedPackageManager: 'pnpm 9',
|
||||
path: '/pnpm9/node_modules/.bin',
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'should set path if bun v1 is detected',
|
||||
args: {
|
||||
cliType: 'bun',
|
||||
nodeVersion: { major: 18, range: '18.x', runtime: 'nodejs18.x' },
|
||||
lockfileVersion: 0,
|
||||
env: {
|
||||
FOO: 'bar',
|
||||
PATH: '/usr/local/bin',
|
||||
},
|
||||
},
|
||||
want: {
|
||||
detectedLockfile: 'bun.lockb',
|
||||
detectedPackageManager: 'Bun',
|
||||
path: '/bun1',
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'should not set pnpm path if corepack is enabled',
|
||||
args: {
|
||||
cliType: 'pnpm',
|
||||
nodeVersion: { major: 16, range: '16.x', runtime: 'nodejs16.x' },
|
||||
lockfileVersion: 5.4,
|
||||
env: {
|
||||
FOO: 'bar',
|
||||
ENABLE_EXPERIMENTAL_COREPACK: '1',
|
||||
},
|
||||
},
|
||||
want: {
|
||||
detectedLockfile: undefined,
|
||||
detectedPackageManager: undefined,
|
||||
path: undefined,
|
||||
},
|
||||
},
|
||||
])('$name', ({ args, want }) => {
|
||||
expect(
|
||||
getPathOverrideForPackageManager({
|
||||
cliType: args.cliType,
|
||||
lockfileVersion: args.lockfileVersion,
|
||||
nodeVersion: args.nodeVersion,
|
||||
env: args.env,
|
||||
})
|
||||
).toStrictEqual(want);
|
||||
});
|
||||
});
|
||||
describe('Test `getPathForPackageManager()`', () => {
|
||||
test.each<{
|
||||
name: string;
|
||||
@@ -470,23 +663,6 @@ describe('Test `getPathForPackageManager()`', () => {
|
||||
yarnNodeLinker: undefined,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'should not set path if pnpm 6 is detected',
|
||||
args: {
|
||||
cliType: 'pnpm',
|
||||
nodeVersion: { major: 14, range: '14.x', runtime: 'nodejs14.x' },
|
||||
lockfileVersion: 5.3,
|
||||
env: {
|
||||
FOO: 'bar',
|
||||
},
|
||||
},
|
||||
want: {
|
||||
detectedLockfile: undefined,
|
||||
detectedPackageManager: undefined,
|
||||
path: undefined,
|
||||
yarnNodeLinker: undefined,
|
||||
},
|
||||
},
|
||||
])('$name', ({ args, want }) => {
|
||||
expect(
|
||||
getPathForPackageManager({
|
||||
|
||||
@@ -15,6 +15,7 @@ describe('Test `getPrefixedEnvVars()`', () => {
|
||||
VERCEL_URL: 'example.vercel.sh',
|
||||
VERCEL_ENV: 'production',
|
||||
VERCEL_BRANCH_URL: 'example-git-main-acme.vercel.app',
|
||||
VERCEL_PROJECT_PRODUCTION_URL: 'example.com',
|
||||
USER_ENV_VAR_NOT_VERCEL: 'example.com',
|
||||
VERCEL_ARTIFACTS_TOKEN: 'abc123',
|
||||
FOO: 'bar',
|
||||
@@ -24,6 +25,7 @@ describe('Test `getPrefixedEnvVars()`', () => {
|
||||
NEXT_PUBLIC_VERCEL_URL: 'example.vercel.sh',
|
||||
NEXT_PUBLIC_VERCEL_ENV: 'production',
|
||||
NEXT_PUBLIC_VERCEL_BRANCH_URL: 'example-git-main-acme.vercel.app',
|
||||
NEXT_PUBLIC_VERCEL_PROJECT_PRODUCTION_URL: 'example.com',
|
||||
TURBO_CI_VENDOR_ENV_KEY: 'NEXT_PUBLIC_VERCEL_',
|
||||
},
|
||||
},
|
||||
|
||||
@@ -1,5 +1,80 @@
|
||||
# vercel
|
||||
|
||||
## 34.1.11
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [[`5014b1e82`](https://github.com/vercel/vercel/commit/5014b1e82a46181baeb727ffe6d14000b6a4b1d7), [`18d1703d5`](https://github.com/vercel/vercel/commit/18d1703d5b4f2adc61fd56011f874c393fa57d0d), [`e87d4c14d`](https://github.com/vercel/vercel/commit/e87d4c14d0e718281f9ba91c9ec1cc6e142d383d), [`bc5fd4115`](https://github.com/vercel/vercel/commit/bc5fd41158ec9f36e5db1fe749589adcbaec6950)]:
|
||||
- @vercel/build-utils@8.1.0
|
||||
- @vercel/next@4.2.10
|
||||
- @vercel/redwood@2.0.9
|
||||
- @vercel/remix-builder@2.1.6
|
||||
- @vercel/node@3.1.1
|
||||
- @vercel/static-build@2.5.5
|
||||
|
||||
## 34.1.10
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [[`119f80e96`](https://github.com/vercel/vercel/commit/119f80e9611a7a5a755aa689502dcdab323194aa), [`11584b0e9`](https://github.com/vercel/vercel/commit/11584b0e9b55f312f34d0d6467ab498e472ac9df), [`3023122d4`](https://github.com/vercel/vercel/commit/3023122d4e0dd292340d9e9e61ef232baf6e610d), [`0e774b6be`](https://github.com/vercel/vercel/commit/0e774b6be0c832213a64124e1f4fc6d150e87d9f)]:
|
||||
- @vercel/next@4.2.9
|
||||
- @vercel/static-build@2.5.4
|
||||
|
||||
## 34.1.9
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [[`5a532a5b9`](https://github.com/vercel/vercel/commit/5a532a5b948994ba04783ac560357eed9f94a3f3), [`50fc27ba5`](https://github.com/vercel/vercel/commit/50fc27ba5773870956300bbbaffbe387d549bc12), [`c1d852295`](https://github.com/vercel/vercel/commit/c1d85229509dd319a1f11beb940a759113564d33), [`a5ea04154`](https://github.com/vercel/vercel/commit/a5ea04154ba26ee4e635d8953aa4f0d9d82d3a96)]:
|
||||
- @vercel/next@4.2.8
|
||||
- @vercel/node@3.1.0
|
||||
|
||||
## 34.1.8
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [[`52e435aa5`](https://github.com/vercel/vercel/commit/52e435aa5d7b4014d19477969ad5cbfbe94aa76f), [`124846a3e`](https://github.com/vercel/vercel/commit/124846a3e65a3bf1ae82327fd4ba2b132674fb39), [`dc974b679`](https://github.com/vercel/vercel/commit/dc974b6797de0b6e90373c92e1f2bbdafcfc6687), [`58c6755e0`](https://github.com/vercel/vercel/commit/58c6755e0c12cae2ce55978b7bf8722133151196)]:
|
||||
- @vercel/next@4.2.7
|
||||
- @vercel/static-build@2.5.3
|
||||
|
||||
## 34.1.7
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [[`3420ba015`](https://github.com/vercel/vercel/commit/3420ba0153dcabffef7114ba2361fb0f3c43a7b3)]:
|
||||
- @vercel/next@4.2.6
|
||||
|
||||
## 34.1.6
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [[`b1adaf76e`](https://github.com/vercel/vercel/commit/b1adaf76ec17d1bbfe30a2bf65405bd886fa9bcf), [`3fb97d1d2`](https://github.com/vercel/vercel/commit/3fb97d1d270e835ce34a687bd234ea53dfe446a2)]:
|
||||
- @vercel/next@4.2.5
|
||||
- @vercel/static-build@2.5.2
|
||||
|
||||
## 34.1.5
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [[`763a6d107`](https://github.com/vercel/vercel/commit/763a6d10709ca40405634d41863c2e524917ebe1), [`15475c8a2`](https://github.com/vercel/vercel/commit/15475c8a2c303a1dd189ba24044fac750280dd2e), [`21f5e7375`](https://github.com/vercel/vercel/commit/21f5e7375e4cb4ceed98ab56486d09a85fa3894d)]:
|
||||
- @vercel/ruby@2.1.0
|
||||
- @vercel/build-utils@8.0.0
|
||||
- @vercel/node@3.0.28
|
||||
- @vercel/static-build@2.5.1
|
||||
|
||||
## 34.1.4
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [[`5b7960337`](https://github.com/vercel/vercel/commit/5b79603378a50fd04d5be1c3a3e5cd605b61478f)]:
|
||||
- @vercel/next@4.2.4
|
||||
|
||||
## 34.1.3
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [[`5758838d0`](https://github.com/vercel/vercel/commit/5758838d090b9789ca6821e1122781352021109f)]:
|
||||
- @vercel/next@4.2.3
|
||||
|
||||
## 34.1.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "vercel",
|
||||
"version": "34.1.2",
|
||||
"version": "34.1.11",
|
||||
"preferGlobal": true,
|
||||
"license": "Apache-2.0",
|
||||
"description": "The command-line interface for Vercel",
|
||||
@@ -32,17 +32,17 @@
|
||||
"node": ">= 16"
|
||||
},
|
||||
"dependencies": {
|
||||
"@vercel/build-utils": "7.12.0",
|
||||
"@vercel/build-utils": "8.1.0",
|
||||
"@vercel/fun": "1.1.0",
|
||||
"@vercel/go": "3.1.1",
|
||||
"@vercel/hydrogen": "1.0.2",
|
||||
"@vercel/next": "4.2.2",
|
||||
"@vercel/node": "3.0.27",
|
||||
"@vercel/next": "4.2.10",
|
||||
"@vercel/node": "3.1.1",
|
||||
"@vercel/python": "4.2.0",
|
||||
"@vercel/redwood": "2.0.8",
|
||||
"@vercel/remix-builder": "2.1.5",
|
||||
"@vercel/ruby": "2.0.5",
|
||||
"@vercel/static-build": "2.5.0",
|
||||
"@vercel/redwood": "2.0.9",
|
||||
"@vercel/remix-builder": "2.1.6",
|
||||
"@vercel/ruby": "2.1.0",
|
||||
"@vercel/static-build": "2.5.5",
|
||||
"chokidar": "3.3.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
@@ -92,8 +92,8 @@
|
||||
"@types/yauzl-promise": "2.1.0",
|
||||
"@vercel-internals/constants": "1.0.4",
|
||||
"@vercel-internals/get-package-json": "1.0.0",
|
||||
"@vercel-internals/types": "1.0.30",
|
||||
"@vercel/client": "13.2.1",
|
||||
"@vercel-internals/types": "1.0.32",
|
||||
"@vercel/client": "13.2.3",
|
||||
"@vercel/error-utils": "2.0.2",
|
||||
"@vercel/frameworks": "3.0.2",
|
||||
"@vercel/fs-detectors": "5.2.3",
|
||||
|
||||
@@ -52,7 +52,7 @@ export const aliasCommand: Command = {
|
||||
name: 'next',
|
||||
description: 'Show next page of results',
|
||||
argument: 'MS',
|
||||
shorthand: 'n',
|
||||
shorthand: 'N',
|
||||
type: String,
|
||||
deprecated: false,
|
||||
},
|
||||
@@ -65,13 +65,14 @@ export const aliasCommand: Command = {
|
||||
},
|
||||
{
|
||||
name: 'limit',
|
||||
shorthand: 'n',
|
||||
shorthand: null,
|
||||
description:
|
||||
'Number of results to return per page (default: 20, max: 100)',
|
||||
argument: 'NUMBER',
|
||||
type: String,
|
||||
deprecated: false,
|
||||
},
|
||||
{ name: 'json', shorthand: null, type: Boolean, deprecated: false },
|
||||
],
|
||||
examples: [
|
||||
{
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
import { handleError } from '../../util/error';
|
||||
import Client from '../../util/client';
|
||||
import getArgs from '../../util/get-args';
|
||||
import { parseArguments } from '../../util/get-args';
|
||||
import getSubcommand from '../../util/get-subcommand';
|
||||
import { help } from '../help';
|
||||
import ls from './ls';
|
||||
import rm from './rm';
|
||||
import set from './set';
|
||||
import { aliasCommand } from './command';
|
||||
import { getFlagsSpecification } from '../../util/get-flags-specification';
|
||||
|
||||
const COMMAND_CONFIG = {
|
||||
default: ['set'],
|
||||
@@ -16,35 +17,33 @@ const COMMAND_CONFIG = {
|
||||
};
|
||||
|
||||
export default async function alias(client: Client) {
|
||||
let argv;
|
||||
let parsedArguments;
|
||||
|
||||
const flagsSpecification = getFlagsSpecification(aliasCommand.options);
|
||||
|
||||
try {
|
||||
argv = getArgs(client.argv.slice(2), {
|
||||
'--json': Boolean,
|
||||
'--yes': Boolean,
|
||||
'--next': Number,
|
||||
'--limit': Number,
|
||||
'-y': '--yes',
|
||||
'-N': '--next',
|
||||
});
|
||||
parsedArguments = parseArguments(client.argv.slice(2), flagsSpecification);
|
||||
} catch (err) {
|
||||
handleError(err);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (argv['--help']) {
|
||||
if (parsedArguments.flags['--help']) {
|
||||
client.output.print(help(aliasCommand, { columns: client.stderr.columns }));
|
||||
return 2;
|
||||
}
|
||||
|
||||
const { subcommand, args } = getSubcommand(argv._.slice(1), COMMAND_CONFIG);
|
||||
const { subcommand, args } = getSubcommand(
|
||||
parsedArguments.args.slice(1),
|
||||
COMMAND_CONFIG
|
||||
);
|
||||
|
||||
switch (subcommand) {
|
||||
case 'ls':
|
||||
return ls(client, argv, args);
|
||||
return ls(client, parsedArguments.flags, args);
|
||||
case 'rm':
|
||||
return rm(client, argv, args);
|
||||
return rm(client, parsedArguments.flags, args);
|
||||
default:
|
||||
return set(client, argv, args);
|
||||
return set(client, parsedArguments.flags, args);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@ export const bisectCommand: Command = {
|
||||
description: 'Automatically open each URL in the browser',
|
||||
argument: 'URL',
|
||||
shorthand: 'o',
|
||||
type: String,
|
||||
type: Boolean,
|
||||
deprecated: false,
|
||||
},
|
||||
{
|
||||
|
||||
@@ -8,7 +8,7 @@ import { URLSearchParams, parse } from 'url';
|
||||
import box from '../../util/output/box';
|
||||
import formatDate from '../../util/format-date';
|
||||
import link from '../../util/output/link';
|
||||
import getArgs from '../../util/get-args';
|
||||
import { parseArguments } from '../../util/get-args';
|
||||
import Client from '../../util/client';
|
||||
import { Deployment } from '@vercel-internals/types';
|
||||
import { normalizeURL } from '../../util/bisect/normalize-url';
|
||||
@@ -16,6 +16,8 @@ import getScope from '../../util/get-scope';
|
||||
import getDeployment from '../../util/get-deployment';
|
||||
import { help } from '../help';
|
||||
import { bisectCommand } from './command';
|
||||
import { getFlagsSpecification } from '../../util/get-flags-specification';
|
||||
import handleError from '../../util/handle-error';
|
||||
|
||||
interface Deployments {
|
||||
deployments: Deployment[];
|
||||
@@ -25,39 +27,37 @@ export default async function bisect(client: Client): Promise<number> {
|
||||
const scope = await getScope(client);
|
||||
const { contextName } = scope;
|
||||
|
||||
const argv = getArgs(client.argv.slice(2), {
|
||||
'--bad': String,
|
||||
'-b': '--bad',
|
||||
'--good': String,
|
||||
'-g': '--good',
|
||||
'--open': Boolean,
|
||||
'-o': '--open',
|
||||
'--path': String,
|
||||
'-p': '--path',
|
||||
'--run': String,
|
||||
'-r': '--run',
|
||||
});
|
||||
let parsedArgs = null;
|
||||
|
||||
if (argv['--help']) {
|
||||
const flagsSpecification = getFlagsSpecification(bisectCommand.options);
|
||||
|
||||
try {
|
||||
parsedArgs = parseArguments(client.argv.slice(2), flagsSpecification);
|
||||
} catch (error) {
|
||||
handleError(error);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (parsedArgs.flags['--help']) {
|
||||
output.print(help(bisectCommand, { columns: client.stderr.columns }));
|
||||
return 2;
|
||||
}
|
||||
|
||||
let bad =
|
||||
argv['--bad'] ||
|
||||
parsedArgs.flags['--bad'] ||
|
||||
(await client.input.text({
|
||||
message: `Specify a URL where the bug occurs:`,
|
||||
validate: val => (val ? true : 'A URL must be provided'),
|
||||
}));
|
||||
let good =
|
||||
argv['--good'] ||
|
||||
parsedArgs.flags['--good'] ||
|
||||
(await client.input.text({
|
||||
message: `Specify a URL where the bug does not occur:`,
|
||||
validate: val => (val ? true : 'A URL must be provided'),
|
||||
}));
|
||||
let subpath = argv['--path'] || '';
|
||||
let run = argv['--run'] || '';
|
||||
const openEnabled = argv['--open'] || false;
|
||||
let subpath = parsedArgs.flags['--path'] || '';
|
||||
let run = parsedArgs.flags['--run'] || '';
|
||||
const openEnabled = parsedArgs.flags['--open'] || false;
|
||||
|
||||
if (run) {
|
||||
run = resolve(run);
|
||||
|
||||
@@ -10,7 +10,7 @@ export const buildCommand: Command = {
|
||||
name: 'prod',
|
||||
description: 'Build a production deployment',
|
||||
shorthand: null,
|
||||
type: String,
|
||||
type: Boolean,
|
||||
deprecated: false,
|
||||
},
|
||||
{
|
||||
|
||||
@@ -43,7 +43,7 @@ import type { VercelConfig } from '@vercel/client';
|
||||
import pull from '../pull';
|
||||
import { staticFiles as getFiles } from '../../util/get-files';
|
||||
import Client from '../../util/client';
|
||||
import getArgs from '../../util/get-args';
|
||||
import { parseArguments } from '../../util/get-args';
|
||||
import cmd from '../../util/output/cmd';
|
||||
import * as cli from '../../util/pkg-name';
|
||||
import cliPkg from '../../util/pkg';
|
||||
@@ -66,12 +66,13 @@ import {
|
||||
import { importBuilders } from '../../util/build/import-builders';
|
||||
import { initCorepack, cleanupCorepack } from '../../util/build/corepack';
|
||||
import { sortBuilders } from '../../util/build/sort-builders';
|
||||
import { toEnumerableError } from '../../util/error';
|
||||
import { handleError, toEnumerableError } from '../../util/error';
|
||||
import { validateConfig } from '../../util/validate-config';
|
||||
import { setMonorepoDefaultSettings } from '../../util/build/monorepo';
|
||||
import { help } from '../help';
|
||||
import { buildCommand } from './command';
|
||||
import { scrubArgv } from '../../util/build/scrub-argv';
|
||||
import { getFlagsSpecification } from '../../util/get-flags-specification';
|
||||
|
||||
type BuildResult = BuildResultV2 | BuildResultV3;
|
||||
|
||||
@@ -133,22 +134,26 @@ export default async function main(client: Client): Promise<number> {
|
||||
process.env.__VERCEL_BUILD_RUNNING = '1';
|
||||
}
|
||||
|
||||
// Parse CLI args
|
||||
const argv = getArgs(client.argv.slice(2), {
|
||||
'--output': String,
|
||||
'--prod': Boolean,
|
||||
'--yes': Boolean,
|
||||
'-y': '--yes',
|
||||
});
|
||||
let parsedArgs = null;
|
||||
|
||||
if (argv['--help']) {
|
||||
const flagsSpecification = getFlagsSpecification(buildCommand.options);
|
||||
|
||||
// Parse CLI args
|
||||
try {
|
||||
parsedArgs = parseArguments(client.argv.slice(2), flagsSpecification);
|
||||
} catch (error) {
|
||||
handleError(error);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (parsedArgs.flags['--help']) {
|
||||
output.print(help(buildCommand, { columns: client.stderr.columns }));
|
||||
return 2;
|
||||
}
|
||||
|
||||
// Build `target` influences which environment variables will be used
|
||||
const target = argv['--prod'] ? 'production' : 'preview';
|
||||
const yes = Boolean(argv['--yes']);
|
||||
const target = parsedArgs.flags['--prod'] ? 'production' : 'preview';
|
||||
const yes = Boolean(parsedArgs.flags['--yes']);
|
||||
|
||||
try {
|
||||
await validateNpmrc(cwd);
|
||||
@@ -213,8 +218,8 @@ export default async function main(client: Client): Promise<number> {
|
||||
|
||||
// Delete output directory from potential previous build
|
||||
const defaultOutputDir = join(cwd, projectRootDirectory, OUTPUT_DIR);
|
||||
const outputDir = argv['--output']
|
||||
? resolve(argv['--output'])
|
||||
const outputDir = parsedArgs.flags['--output']
|
||||
? resolve(parsedArgs.flags['--output'])
|
||||
: defaultOutputDir;
|
||||
await Promise.all([
|
||||
fs.remove(outputDir),
|
||||
|
||||
2
packages/cli/test/integration-3.test.ts
vendored
2
packages/cli/test/integration-3.test.ts
vendored
@@ -1255,7 +1255,7 @@ test('fail to deploy a Lambda with an incorrect value for maxDuration', async ()
|
||||
|
||||
expect(output.exitCode, formatOutput(output)).toBe(1);
|
||||
expect(output.stderr).toMatch(
|
||||
/maxDuration must be between 1 second and 10 seconds/gm
|
||||
/maxDuration must be between \d+ second and \d+ seconds/gm
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
@@ -24,18 +24,16 @@ exports[`help command > alias help output snapshots > alias help column width 40
|
||||
|
||||
[2mOptions[22m:
|
||||
|
||||
-n, --limit <NUMBER> Number of
|
||||
results to
|
||||
return per page
|
||||
(default: 20,
|
||||
max: 100)
|
||||
-n, --next <MS> Show next page
|
||||
of results
|
||||
-y, --yes Skip the
|
||||
confirmation
|
||||
prompt when
|
||||
removing an
|
||||
alias
|
||||
--limit <NUMBER> Number of results to
|
||||
return per page
|
||||
(default: 20, max:
|
||||
100)
|
||||
-N, --next <MS> Show next page of
|
||||
results
|
||||
-y, --yes Skip the
|
||||
confirmation prompt
|
||||
when removing an
|
||||
alias
|
||||
|
||||
|
||||
[2mGlobal Options[22m:
|
||||
@@ -108,10 +106,9 @@ exports[`help command > alias help output snapshots > alias help column width 80
|
||||
|
||||
[2mOptions[22m:
|
||||
|
||||
-n, --limit <NUMBER> Number of results to return per page (default: 20, max:
|
||||
100)
|
||||
-n, --next <MS> Show next page of results
|
||||
-y, --yes Skip the confirmation prompt when removing an alias
|
||||
--limit <NUMBER> Number of results to return per page (default: 20, max: 100)
|
||||
-N, --next <MS> Show next page of results
|
||||
-y, --yes Skip the confirmation prompt when removing an alias
|
||||
|
||||
|
||||
[2mGlobal Options[22m:
|
||||
@@ -160,9 +157,9 @@ exports[`help command > alias help output snapshots > alias help column width 12
|
||||
|
||||
[2mOptions[22m:
|
||||
|
||||
-n, --limit <NUMBER> Number of results to return per page (default: 20, max: 100)
|
||||
-n, --next <MS> Show next page of results
|
||||
-y, --yes Skip the confirmation prompt when removing an alias
|
||||
--limit <NUMBER> Number of results to return per page (default: 20, max: 100)
|
||||
-N, --next <MS> Show next page of results
|
||||
-y, --yes Skip the confirmation prompt when removing an alias
|
||||
|
||||
|
||||
[2mGlobal Options[22m:
|
||||
|
||||
@@ -1,5 +1,19 @@
|
||||
# @vercel/client
|
||||
|
||||
## 13.2.3
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [[`5014b1e82`](https://github.com/vercel/vercel/commit/5014b1e82a46181baeb727ffe6d14000b6a4b1d7)]:
|
||||
- @vercel/build-utils@8.1.0
|
||||
|
||||
## 13.2.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [[`15475c8a2`](https://github.com/vercel/vercel/commit/15475c8a2c303a1dd189ba24044fac750280dd2e), [`21f5e7375`](https://github.com/vercel/vercel/commit/21f5e7375e4cb4ceed98ab56486d09a85fa3894d)]:
|
||||
- @vercel/build-utils@8.0.0
|
||||
|
||||
## 13.2.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vercel/client",
|
||||
"version": "13.2.1",
|
||||
"version": "13.2.3",
|
||||
"main": "dist/index.js",
|
||||
"typings": "dist/index.d.ts",
|
||||
"homepage": "https://vercel.com",
|
||||
@@ -37,7 +37,7 @@
|
||||
"typescript": "4.9.5"
|
||||
},
|
||||
"dependencies": {
|
||||
"@vercel/build-utils": "7.12.0",
|
||||
"@vercel/build-utils": "8.1.0",
|
||||
"@vercel/error-utils": "2.0.2",
|
||||
"@vercel/routing-utils": "3.1.0",
|
||||
"@zeit/fetch": "5.2.0",
|
||||
|
||||
@@ -37,7 +37,7 @@
|
||||
"@types/minimatch": "3.0.5",
|
||||
"@types/node": "14.18.33",
|
||||
"@types/semver": "7.3.10",
|
||||
"@vercel/build-utils": "7.12.0",
|
||||
"@vercel/build-utils": "8.1.0",
|
||||
"jest-junit": "16.0.0",
|
||||
"typescript": "4.9.5"
|
||||
}
|
||||
|
||||
19
packages/functions/CHANGELOG.md
Normal file
19
packages/functions/CHANGELOG.md
Normal file
@@ -0,0 +1,19 @@
|
||||
# @vercel/functions
|
||||
|
||||
## 1.0.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Convert package to CommonJS ([#11569](https://github.com/vercel/vercel/pull/11569))
|
||||
|
||||
## 1.0.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Don't throw error if context is missing ([`0817527f9`](https://github.com/vercel/vercel/commit/0817527f9e9d0d5fceb73f21e695089349a96d3e))
|
||||
|
||||
## 1.0.0
|
||||
|
||||
### Major Changes
|
||||
|
||||
- Initial release ([#11553](https://github.com/vercel/vercel/pull/11553))
|
||||
17
packages/functions/index.d.ts
vendored
Normal file
17
packages/functions/index.d.ts
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
/**
|
||||
* Extends the lifetime of the request handler for the lifetime of the given {@link Promise}
|
||||
* @see https://developer.mozilla.org/en-US/docs/Web/API/ExtendableEvent/waitUntil
|
||||
*
|
||||
* @param promise The promise to wait for.
|
||||
* @example
|
||||
*
|
||||
* ```
|
||||
* import { waitUntil } from '@vercel/functions';
|
||||
*
|
||||
* export function GET(request) {
|
||||
* waitUntil(fetch('https://vercel.com'));
|
||||
* return new Response('OK');
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
export function waitUntil(promise: Promise<unknown>): void;
|
||||
16
packages/functions/index.js
Normal file
16
packages/functions/index.js
Normal file
@@ -0,0 +1,16 @@
|
||||
/* global globalThis */
|
||||
|
||||
exports.waitUntil = promise => {
|
||||
if (
|
||||
promise === null ||
|
||||
typeof promise !== 'object' ||
|
||||
typeof promise.then !== 'function'
|
||||
) {
|
||||
throw new TypeError(
|
||||
`waitUntil can only be called with a Promise, got ${typeof promise}`
|
||||
);
|
||||
}
|
||||
|
||||
const ctx = globalThis[Symbol.for('@vercel/request-context')]?.get?.() ?? {};
|
||||
ctx.waitUntil?.(promise);
|
||||
};
|
||||
42
packages/functions/index.test.js
Normal file
42
packages/functions/index.test.js
Normal file
@@ -0,0 +1,42 @@
|
||||
/* global globalThis */
|
||||
|
||||
import { vi, expect, test } from 'vitest';
|
||||
|
||||
import { waitUntil } from '.';
|
||||
|
||||
test.each([
|
||||
{},
|
||||
() => {},
|
||||
function () {},
|
||||
NaN,
|
||||
1,
|
||||
false,
|
||||
undefined,
|
||||
null,
|
||||
[],
|
||||
'▲',
|
||||
])('waitUntil throws when called with %s', input => {
|
||||
expect(() => waitUntil(input)).toThrow(TypeError);
|
||||
expect(() => waitUntil(input)).toThrow(
|
||||
`waitUntil can only be called with a Promise, got ${typeof input}`
|
||||
);
|
||||
});
|
||||
|
||||
test.each([null, undefined, {}])(
|
||||
'waitUntil does not throw an error when context is %s',
|
||||
input => {
|
||||
const promise = Promise.resolve();
|
||||
globalThis[Symbol.for('@vercel/request-context')] = input;
|
||||
expect(() => waitUntil(promise)).not.toThrow();
|
||||
}
|
||||
);
|
||||
|
||||
test('waitUntil calls ctx.waitUntil when available', async () => {
|
||||
const promise = Promise.resolve();
|
||||
const waitUntilMock = vi.fn().mockReturnValue(promise);
|
||||
globalThis[Symbol.for('@vercel/request-context')] = {
|
||||
get: () => ({ waitUntil: waitUntilMock }),
|
||||
};
|
||||
waitUntil(promise);
|
||||
expect(waitUntilMock).toHaveBeenCalledWith(promise);
|
||||
});
|
||||
33
packages/functions/package.json
Normal file
33
packages/functions/package.json
Normal file
@@ -0,0 +1,33 @@
|
||||
{
|
||||
"name": "@vercel/functions",
|
||||
"description": "Runtime functions to be used with your Vercel Functions",
|
||||
"homepage": "https://vercel.com",
|
||||
"version": "1.0.2",
|
||||
"types": "index.d.ts",
|
||||
"main": "index.js",
|
||||
"repository": {
|
||||
"directory": "packages/functions",
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/vercel/vercel.git"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://github.com/vercel/vercel/issues"
|
||||
},
|
||||
"devDependencies": {
|
||||
"vitest": "1.3.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 16"
|
||||
},
|
||||
"files": [
|
||||
"index.d.ts",
|
||||
"index.js"
|
||||
],
|
||||
"scripts": {
|
||||
"test": "vitest"
|
||||
},
|
||||
"license": "Apache-2.0",
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,19 @@
|
||||
# @vercel/gatsby-plugin-vercel-builder
|
||||
|
||||
## 2.0.27
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [[`5014b1e82`](https://github.com/vercel/vercel/commit/5014b1e82a46181baeb727ffe6d14000b6a4b1d7)]:
|
||||
- @vercel/build-utils@8.1.0
|
||||
|
||||
## 2.0.26
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [[`15475c8a2`](https://github.com/vercel/vercel/commit/15475c8a2c303a1dd189ba24044fac750280dd2e), [`21f5e7375`](https://github.com/vercel/vercel/commit/21f5e7375e4cb4ceed98ab56486d09a85fa3894d)]:
|
||||
- @vercel/build-utils@8.0.0
|
||||
|
||||
## 2.0.25
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vercel/gatsby-plugin-vercel-builder",
|
||||
"version": "2.0.25",
|
||||
"version": "2.0.27",
|
||||
"main": "dist/index.js",
|
||||
"files": [
|
||||
"dist",
|
||||
@@ -20,7 +20,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@sinclair/typebox": "0.25.24",
|
||||
"@vercel/build-utils": "7.12.0",
|
||||
"@vercel/build-utils": "8.1.0",
|
||||
"@vercel/routing-utils": "3.1.0",
|
||||
"esbuild": "0.14.47",
|
||||
"etag": "1.8.1",
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
"@types/node-fetch": "^2.3.0",
|
||||
"@types/tar": "6.1.5",
|
||||
"@types/yauzl-promise": "2.1.0",
|
||||
"@vercel/build-utils": "7.12.0",
|
||||
"@vercel/build-utils": "8.1.0",
|
||||
"async-retry": "1.3.3",
|
||||
"execa": "^1.0.0",
|
||||
"fs-extra": "^7.0.0",
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
"devDependencies": {
|
||||
"@types/jest": "27.5.1",
|
||||
"@types/node": "14.18.33",
|
||||
"@vercel/build-utils": "7.12.0",
|
||||
"@vercel/build-utils": "8.1.0",
|
||||
"execa": "3.2.0",
|
||||
"fs-extra": "11.1.0",
|
||||
"jest-junit": "16.0.0"
|
||||
|
||||
@@ -1,5 +1,67 @@
|
||||
# @vercel/next
|
||||
|
||||
## 4.2.10
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- skip action rewrites for RSC requests ([#11576](https://github.com/vercel/vercel/pull/11576))
|
||||
|
||||
- Bump `@vercel/nft@0.27.0` ([#11580](https://github.com/vercel/vercel/pull/11580))
|
||||
|
||||
## 4.2.9
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Support incremental partial prerendering ([#11560](https://github.com/vercel/vercel/pull/11560))
|
||||
|
||||
- ensure `.action` outputs are created for edge functions ([#11568](https://github.com/vercel/vercel/pull/11568))
|
||||
|
||||
- ([#11566](https://github.com/vercel/vercel/pull/11566))
|
||||
|
||||
## 4.2.8
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Fix missing initial RSC headers ([#11552](https://github.com/vercel/vercel/pull/11552))
|
||||
|
||||
- Remove .prefetch.rsc rewrites for non-PPR ([#11540](https://github.com/vercel/vercel/pull/11540))
|
||||
|
||||
- [next] rename middleware manifest env ([#11549](https://github.com/vercel/vercel/pull/11549))
|
||||
|
||||
## 4.2.7
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Fix missing .rsc outputs for pages prerenders ([#11503](https://github.com/vercel/vercel/pull/11503))
|
||||
|
||||
- Fix base path app index RSC handling ([#11528](https://github.com/vercel/vercel/pull/11528))
|
||||
|
||||
- Fix interception routes PPR route handling ([#11527](https://github.com/vercel/vercel/pull/11527))
|
||||
|
||||
## 4.2.6
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- [next] Update test fixture version ([#11514](https://github.com/vercel/vercel/pull/11514))
|
||||
|
||||
## 4.2.5
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Only rewrite next-action requests to `.action` handlers ([#11504](https://github.com/vercel/vercel/pull/11504))
|
||||
|
||||
## 4.2.4
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- [next]: Revert .action handling for dynamic routes ([#11509](https://github.com/vercel/vercel/pull/11509))
|
||||
|
||||
## 4.2.3
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- [next] Reland add .action handling for dynamic routes ([#11487](https://github.com/vercel/vercel/pull/11487))
|
||||
|
||||
## 4.2.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vercel/next",
|
||||
"version": "4.2.2",
|
||||
"version": "4.2.10",
|
||||
"license": "Apache-2.0",
|
||||
"main": "./dist/index",
|
||||
"homepage": "https://vercel.com/docs/runtimes#official-runtimes/next-js",
|
||||
@@ -23,7 +23,7 @@
|
||||
"dist"
|
||||
],
|
||||
"dependencies": {
|
||||
"@vercel/nft": "0.26.4"
|
||||
"@vercel/nft": "0.27.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/aws-lambda": "8.10.19",
|
||||
@@ -40,7 +40,7 @@
|
||||
"@types/semver": "6.0.0",
|
||||
"@types/text-table": "0.2.1",
|
||||
"@types/webpack-sources": "3.2.0",
|
||||
"@vercel/build-utils": "7.12.0",
|
||||
"@vercel/build-utils": "8.1.0",
|
||||
"@vercel/routing-utils": "3.1.0",
|
||||
"async-sema": "3.0.1",
|
||||
"buffer-crc32": "0.2.13",
|
||||
|
||||
@@ -1928,6 +1928,7 @@ export const build: BuildV2 = async ({
|
||||
bypassToken: prerenderManifest.bypassToken || '',
|
||||
isServerMode,
|
||||
experimentalPPRRoutes,
|
||||
hasActionOutputSupport: false,
|
||||
}).then(arr =>
|
||||
localizeDynamicRoutes(
|
||||
arr,
|
||||
@@ -1958,6 +1959,7 @@ export const build: BuildV2 = async ({
|
||||
bypassToken: prerenderManifest.bypassToken || '',
|
||||
isServerMode,
|
||||
experimentalPPRRoutes,
|
||||
hasActionOutputSupport: false,
|
||||
}).then(arr =>
|
||||
arr.map(route => {
|
||||
route.src = route.src.replace('^', `^${dynamicPrefix}`);
|
||||
|
||||
@@ -51,6 +51,7 @@ import {
|
||||
normalizePrefetches,
|
||||
CreateLambdaFromPseudoLayersOptions,
|
||||
getPostponeResumePathname,
|
||||
LambdaGroup,
|
||||
MAX_UNCOMPRESSED_LAMBDA_SIZE,
|
||||
} from './utils';
|
||||
import {
|
||||
@@ -68,6 +69,7 @@ const CORRECT_NOT_FOUND_ROUTES_VERSION = 'v12.0.1';
|
||||
const CORRECT_MIDDLEWARE_ORDER_VERSION = 'v12.1.7-canary.29';
|
||||
const NEXT_DATA_MIDDLEWARE_RESOLVING_VERSION = 'v12.1.7-canary.33';
|
||||
const EMPTY_ALLOW_QUERY_FOR_PRERENDERED_VERSION = 'v12.2.0';
|
||||
const ACTION_OUTPUT_SUPPORT_VERSION = 'v14.2.2';
|
||||
const CORRECTED_MANIFESTS_VERSION = 'v12.2.0';
|
||||
|
||||
// Ideally this should be in a Next.js manifest so we can change it in
|
||||
@@ -199,6 +201,9 @@ export async function serverBuild({
|
||||
nextVersion,
|
||||
EMPTY_ALLOW_QUERY_FOR_PRERENDERED_VERSION
|
||||
);
|
||||
const hasActionOutputSupport =
|
||||
semver.gte(nextVersion, ACTION_OUTPUT_SUPPORT_VERSION) &&
|
||||
Boolean(process.env.NEXT_EXPERIMENTAL_STREAMING_ACTIONS);
|
||||
const projectDir = requiredServerFilesManifest.relativeAppDir
|
||||
? path.join(baseDir, requiredServerFilesManifest.relativeAppDir)
|
||||
: requiredServerFilesManifest.appDir || entryPath;
|
||||
@@ -213,7 +218,9 @@ export async function serverBuild({
|
||||
}
|
||||
|
||||
const experimental = {
|
||||
ppr: requiredServerFilesManifest.config.experimental?.ppr === true,
|
||||
ppr:
|
||||
requiredServerFilesManifest.config.experimental?.ppr === true ||
|
||||
requiredServerFilesManifest.config.experimental?.ppr === 'incremental',
|
||||
};
|
||||
|
||||
let appRscPrefetches: UnwrapPromise<ReturnType<typeof glob>> = {};
|
||||
@@ -223,8 +230,6 @@ export async function serverBuild({
|
||||
if (appPathRoutesManifest) {
|
||||
appDir = path.join(pagesDir, '../app');
|
||||
appBuildTraces = await glob('**/*.js.nft.json', appDir);
|
||||
|
||||
// TODO: maybe?
|
||||
appRscPrefetches = experimental.ppr
|
||||
? {}
|
||||
: await glob(`**/*${RSC_PREFETCH_SUFFIX}`, appDir);
|
||||
@@ -246,7 +251,7 @@ export async function serverBuild({
|
||||
if (rewrite.src && rewrite.dest) {
|
||||
rewrite.src = rewrite.src.replace(
|
||||
/\/?\(\?:\/\)\?/,
|
||||
'(?<rscsuff>(\\.prefetch)?\\.rsc)?(?:/)?'
|
||||
`(?<rscsuff>${experimental.ppr ? '(\\.prefetch)?' : ''}\\.rsc)?(?:/)?`
|
||||
);
|
||||
let destQueryIndex = rewrite.dest.indexOf('?');
|
||||
|
||||
@@ -926,11 +931,23 @@ export async function serverBuild({
|
||||
inversedAppPathManifest,
|
||||
});
|
||||
|
||||
const appRouterStreamingActionLambdaGroups: LambdaGroup[] = [];
|
||||
|
||||
for (const group of appRouterLambdaGroups) {
|
||||
if (!group.isPrerenders || group.isExperimentalPPR) {
|
||||
group.isStreaming = true;
|
||||
}
|
||||
group.isAppRouter = true;
|
||||
|
||||
// We create a streaming variant of the Prerender lambda group
|
||||
// to support actions that are part of a Prerender
|
||||
if (hasActionOutputSupport) {
|
||||
appRouterStreamingActionLambdaGroups.push({
|
||||
...group,
|
||||
isActionLambda: true,
|
||||
isStreaming: true,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
for (const group of appRouteHandlersLambdaGroups) {
|
||||
@@ -982,6 +999,13 @@ export async function serverBuild({
|
||||
pseudoLayerBytes: group.pseudoLayerBytes,
|
||||
uncompressedLayerBytes: group.pseudoLayerUncompressedBytes,
|
||||
})),
|
||||
appRouterStreamingPrerenderLambdaGroups:
|
||||
appRouterStreamingActionLambdaGroups.map(group => ({
|
||||
pages: group.pages,
|
||||
isPrerender: group.isPrerenders,
|
||||
pseudoLayerBytes: group.pseudoLayerBytes,
|
||||
uncompressedLayerBytes: group.pseudoLayerUncompressedBytes,
|
||||
})),
|
||||
appRouteHandlersLambdaGroups: appRouteHandlersLambdaGroups.map(
|
||||
group => ({
|
||||
pages: group.pages,
|
||||
@@ -999,6 +1023,7 @@ export async function serverBuild({
|
||||
const combinedGroups = [
|
||||
...pageLambdaGroups,
|
||||
...appRouterLambdaGroups,
|
||||
...appRouterStreamingActionLambdaGroups,
|
||||
...apiLambdaGroups,
|
||||
...appRouteHandlersLambdaGroups,
|
||||
];
|
||||
@@ -1208,6 +1233,11 @@ export async function serverBuild({
|
||||
|
||||
let outputName = path.posix.join(entryDirectory, pageName);
|
||||
|
||||
if (group.isActionLambda) {
|
||||
// give the streaming prerenders a .action suffix
|
||||
outputName = `${outputName}.action`;
|
||||
}
|
||||
|
||||
// If this is a PPR page, then we should prefix the output name.
|
||||
if (isPPR) {
|
||||
if (!revalidate) {
|
||||
@@ -1378,6 +1408,7 @@ export async function serverBuild({
|
||||
isServerMode: true,
|
||||
dynamicMiddlewareRouteMap: middleware.dynamicRouteMap,
|
||||
experimentalPPRRoutes,
|
||||
hasActionOutputSupport,
|
||||
}).then(arr =>
|
||||
localizeDynamicRoutes(
|
||||
arr,
|
||||
@@ -1557,10 +1588,23 @@ export async function serverBuild({
|
||||
|
||||
if (lambdas[pathname]) {
|
||||
lambdas[`${pathname}.rsc`] = lambdas[pathname];
|
||||
|
||||
if (experimental.ppr) {
|
||||
lambdas[`${pathname}${RSC_PREFETCH_SUFFIX}`] = lambdas[pathname];
|
||||
}
|
||||
}
|
||||
|
||||
if (edgeFunctions[pathname]) {
|
||||
edgeFunctions[`${pathname}.rsc`] = edgeFunctions[pathname];
|
||||
|
||||
if (experimental.ppr) {
|
||||
edgeFunctions[`${pathname}${RSC_PREFETCH_SUFFIX}`] =
|
||||
edgeFunctions[pathname];
|
||||
}
|
||||
|
||||
if (hasActionOutputSupport) {
|
||||
edgeFunctions[`${pathname}.action`] = edgeFunctions[pathname];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1863,7 +1907,7 @@ export async function serverBuild({
|
||||
|
||||
...(appDir
|
||||
? [
|
||||
...(rscPrefetchHeader
|
||||
...(rscPrefetchHeader && experimental.ppr
|
||||
? [
|
||||
{
|
||||
src: `^${path.posix.join('/', entryDirectory, '/')}`,
|
||||
@@ -1905,9 +1949,56 @@ export async function serverBuild({
|
||||
},
|
||||
]
|
||||
: []),
|
||||
|
||||
...(hasActionOutputSupport
|
||||
? [
|
||||
// Create rewrites for streaming prerenders (.action routes)
|
||||
// This contains separate rewrites for each possible "has" (action header, or content-type)
|
||||
// Also includes separate handling for index routes which should match to /index.action.
|
||||
// This follows the same pattern as the rewrites for .rsc files.
|
||||
{
|
||||
src: `^${path.posix.join('/', entryDirectory, '/?')}`,
|
||||
dest: path.posix.join('/', entryDirectory, '/index.action'),
|
||||
has: [
|
||||
{
|
||||
type: 'header',
|
||||
key: 'next-action',
|
||||
},
|
||||
],
|
||||
missing: [
|
||||
{
|
||||
type: 'header',
|
||||
key: rscHeader,
|
||||
},
|
||||
],
|
||||
continue: true,
|
||||
override: true,
|
||||
},
|
||||
{
|
||||
src: `^${path.posix.join(
|
||||
'/',
|
||||
entryDirectory,
|
||||
'/((?!.+\\.action).+?)(?:/)?$'
|
||||
)}`,
|
||||
dest: path.posix.join('/', entryDirectory, '/$1.action'),
|
||||
has: [
|
||||
{
|
||||
type: 'header',
|
||||
key: 'next-action',
|
||||
},
|
||||
],
|
||||
missing: [
|
||||
{
|
||||
type: 'header',
|
||||
key: rscHeader,
|
||||
},
|
||||
],
|
||||
continue: true,
|
||||
override: true,
|
||||
},
|
||||
]
|
||||
: []),
|
||||
{
|
||||
src: `^${path.posix.join('/', entryDirectory, '/')}`,
|
||||
src: `^${path.posix.join('/', entryDirectory, '/?')}`,
|
||||
has: [
|
||||
{
|
||||
type: 'header',
|
||||
@@ -1970,47 +2061,6 @@ export async function serverBuild({
|
||||
]
|
||||
: []),
|
||||
|
||||
...(rscPrefetchHeader && !experimental.ppr
|
||||
? [
|
||||
{
|
||||
src: path.posix.join(
|
||||
'/',
|
||||
entryDirectory,
|
||||
`/__index${RSC_PREFETCH_SUFFIX}`
|
||||
),
|
||||
dest: path.posix.join('/', entryDirectory, '/index.rsc'),
|
||||
has: [
|
||||
{
|
||||
type: 'header',
|
||||
key: rscPrefetchHeader,
|
||||
},
|
||||
],
|
||||
continue: true,
|
||||
override: true,
|
||||
},
|
||||
{
|
||||
src: `^${path.posix.join(
|
||||
'/',
|
||||
entryDirectory,
|
||||
`/(.+?)${RSC_PREFETCH_SUFFIX}(?:/)?$`
|
||||
)}`,
|
||||
dest: path.posix.join(
|
||||
'/',
|
||||
entryDirectory,
|
||||
`/$1${experimental.ppr ? RSC_PREFETCH_SUFFIX : '.rsc'}`
|
||||
),
|
||||
has: [
|
||||
{
|
||||
type: 'header',
|
||||
key: rscPrefetchHeader,
|
||||
},
|
||||
],
|
||||
continue: true,
|
||||
override: true,
|
||||
},
|
||||
]
|
||||
: []),
|
||||
|
||||
// These need to come before handle: miss or else they are grouped
|
||||
// with that routing section
|
||||
...afterFilesRewrites,
|
||||
|
||||
@@ -321,6 +321,7 @@ export async function getDynamicRoutes({
|
||||
isServerMode,
|
||||
dynamicMiddlewareRouteMap,
|
||||
experimentalPPRRoutes,
|
||||
hasActionOutputSupport,
|
||||
}: {
|
||||
entryPath: string;
|
||||
entryDirectory: string;
|
||||
@@ -333,6 +334,7 @@ export async function getDynamicRoutes({
|
||||
isServerMode?: boolean;
|
||||
dynamicMiddlewareRouteMap?: ReadonlyMap<string, RouteWithSrc>;
|
||||
experimentalPPRRoutes: ReadonlySet<string>;
|
||||
hasActionOutputSupport: boolean;
|
||||
}): Promise<RouteWithSrc[]> {
|
||||
if (routesManifest) {
|
||||
switch (routesManifest.version) {
|
||||
@@ -423,14 +425,25 @@ export async function getDynamicRoutes({
|
||||
});
|
||||
}
|
||||
|
||||
routes.push({
|
||||
...route,
|
||||
src: route.src.replace(
|
||||
new RegExp(escapeStringRegexp('(?:/)?$')),
|
||||
'(?:\\.rsc)(?:/)?$'
|
||||
),
|
||||
dest: route.dest?.replace(/($|\?)/, '.rsc$1'),
|
||||
});
|
||||
if (hasActionOutputSupport) {
|
||||
routes.push({
|
||||
...route,
|
||||
src: route.src.replace(
|
||||
new RegExp(escapeStringRegexp('(?:/)?$')),
|
||||
'(?<nxtsuffix>(?:\\.action|\\.rsc))(?:/)?$'
|
||||
),
|
||||
dest: route.dest?.replace(/($|\?)/, '$nxtsuffix$1'),
|
||||
});
|
||||
} else {
|
||||
routes.push({
|
||||
...route,
|
||||
src: route.src.replace(
|
||||
new RegExp(escapeStringRegexp('(?:/)?$')),
|
||||
'(?:\\.rsc)(?:/)?$'
|
||||
),
|
||||
dest: route.dest?.replace(/($|\?)/, '.rsc$1'),
|
||||
});
|
||||
}
|
||||
|
||||
routes.push(route);
|
||||
}
|
||||
@@ -1487,6 +1500,7 @@ export type LambdaGroup = {
|
||||
isStreaming?: boolean;
|
||||
isPrerenders?: boolean;
|
||||
isExperimentalPPR?: boolean;
|
||||
isActionLambda?: boolean;
|
||||
isPages?: boolean;
|
||||
isApiLambda: boolean;
|
||||
pseudoLayer: PseudoLayer;
|
||||
@@ -2451,6 +2465,7 @@ export const onPrerenderRoute =
|
||||
...(rscEnabled
|
||||
? {
|
||||
initialHeaders: {
|
||||
...initialHeaders,
|
||||
'content-type': rscContentTypeHeader,
|
||||
vary: rscVaryHeader,
|
||||
// If it contains a pre-render, then it was postponed.
|
||||
@@ -2463,6 +2478,22 @@ export const onPrerenderRoute =
|
||||
});
|
||||
}
|
||||
|
||||
// we need to ensure all prerenders have a matching .rsc output
|
||||
// otherwise routing could fall through unexpectedly for the
|
||||
// fallback: false case as it doesn't have a dynamic route
|
||||
// to catch the `.rsc` request for app -> pages routing
|
||||
if (outputPrerenderPathData?.endsWith('.json') && appDir) {
|
||||
const dummyOutput = new FileBlob({
|
||||
data: '{}',
|
||||
contentType: 'application/json',
|
||||
});
|
||||
const rscKey = `${outputPathPage}.rsc`;
|
||||
const prefetchRscKey = `${outputPathPage}${RSC_PREFETCH_SUFFIX}`;
|
||||
|
||||
prerenders[rscKey] = dummyOutput;
|
||||
prerenders[prefetchRscKey] = dummyOutput;
|
||||
}
|
||||
|
||||
++prerenderGroup;
|
||||
|
||||
if (routesManifest?.i18n && isBlocking) {
|
||||
@@ -2754,7 +2785,7 @@ interface EdgeFunctionInfoV2 extends BaseEdgeFunctionInfo {
|
||||
|
||||
interface EdgeFunctionInfoV3 extends BaseEdgeFunctionInfo {
|
||||
matchers: EdgeFunctionMatcher[];
|
||||
environments: Record<string, string>;
|
||||
env: Record<string, string>;
|
||||
}
|
||||
|
||||
interface EdgeFunctionMatcher {
|
||||
@@ -2989,7 +3020,7 @@ export async function getMiddlewareBundle({
|
||||
slug: 'nextjs',
|
||||
version: nextVersion,
|
||||
},
|
||||
environment: edgeFunction.environments,
|
||||
environment: edgeFunction.env,
|
||||
});
|
||||
})(),
|
||||
routeMatchers: getRouteMatchers(edgeFunction, routesManifest),
|
||||
@@ -3169,7 +3200,7 @@ export function upgradeMiddlewareManifestV1(
|
||||
return {
|
||||
...rest,
|
||||
matchers: [{ regexp }],
|
||||
environments: {},
|
||||
env: {},
|
||||
};
|
||||
}
|
||||
|
||||
@@ -3197,7 +3228,7 @@ export function upgradeMiddlewareManifestV2(
|
||||
const { ...rest } = v2Info;
|
||||
return {
|
||||
...rest,
|
||||
environments: {},
|
||||
env: {},
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
6
packages/next/test/fixtures/00-app-dir-actions-experimental-streaming/app/actions.js
vendored
Normal file
6
packages/next/test/fixtures/00-app-dir-actions-experimental-streaming/app/actions.js
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
'use server';
|
||||
|
||||
export async function increment(value) {
|
||||
await new Promise(resolve => setTimeout(resolve, 500));
|
||||
return value + 1;
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
'use client';
|
||||
|
||||
import { useState } from 'react';
|
||||
import { increment } from '../../../actions';
|
||||
|
||||
export default function Page() {
|
||||
const [count, setCount] = useState(0);
|
||||
async function updateCount() {
|
||||
const newCount = await increment(count);
|
||||
setCount(newCount);
|
||||
}
|
||||
|
||||
return (
|
||||
<form action={updateCount}>
|
||||
<div id="count">{count}</div>
|
||||
<button>Submit</button>
|
||||
</form>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
export const dynamic = 'force-dynamic';
|
||||
|
||||
export default function Layout({ children }) {
|
||||
return children;
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
'use client';
|
||||
|
||||
import { useState } from 'react';
|
||||
import { increment } from '../../../actions';
|
||||
|
||||
export default function Page() {
|
||||
const [count, setCount] = useState(0);
|
||||
async function updateCount() {
|
||||
const newCount = await increment(count);
|
||||
setCount(newCount);
|
||||
}
|
||||
|
||||
return (
|
||||
<form action={updateCount}>
|
||||
<div id="count">{count}</div>
|
||||
<button>Submit</button>
|
||||
</form>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
export const dynamic = 'force-static';
|
||||
|
||||
export default function Layout({ children }) {
|
||||
return children;
|
||||
}
|
||||
19
packages/next/test/fixtures/00-app-dir-actions-experimental-streaming/app/client/static/page.js
vendored
Normal file
19
packages/next/test/fixtures/00-app-dir-actions-experimental-streaming/app/client/static/page.js
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
'use client';
|
||||
|
||||
import { useState } from 'react';
|
||||
import { increment } from '../../actions';
|
||||
|
||||
export default function Page() {
|
||||
const [count, setCount] = useState(0);
|
||||
async function updateCount() {
|
||||
const newCount = await increment(count);
|
||||
setCount(newCount);
|
||||
}
|
||||
|
||||
return (
|
||||
<form action={updateCount}>
|
||||
<div id="count">{count}</div>
|
||||
<button>Submit</button>
|
||||
</form>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
'use client';
|
||||
|
||||
import { useState } from 'react';
|
||||
import { increment } from '../../../../actions';
|
||||
|
||||
export default function Page() {
|
||||
const [count, setCount] = useState(0);
|
||||
async function updateCount() {
|
||||
const newCount = await increment(count);
|
||||
setCount(newCount);
|
||||
}
|
||||
|
||||
return (
|
||||
<form action={updateCount}>
|
||||
<div id="count">{count}</div>
|
||||
<button>Submit</button>
|
||||
</form>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
export const dynamic = 'force-dynamic';
|
||||
|
||||
export default function Layout({ children }) {
|
||||
return children;
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
'use client';
|
||||
|
||||
import { useState } from 'react';
|
||||
import { increment } from '../../../../actions';
|
||||
|
||||
export default function Page() {
|
||||
const [count, setCount] = useState(0);
|
||||
async function updateCount() {
|
||||
const newCount = await increment(count);
|
||||
setCount(newCount);
|
||||
}
|
||||
|
||||
return (
|
||||
<form action={updateCount}>
|
||||
<div id="count">{count}</div>
|
||||
<button>Submit</button>
|
||||
</form>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
export const dynamic = 'force-static';
|
||||
|
||||
export default function Layout({ children }) {
|
||||
return children;
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
'use client';
|
||||
|
||||
import { useState } from 'react';
|
||||
import { increment } from '../../../actions';
|
||||
|
||||
export default function Page() {
|
||||
const [count, setCount] = useState(0);
|
||||
async function updateCount() {
|
||||
const newCount = await increment(count);
|
||||
setCount(newCount);
|
||||
}
|
||||
|
||||
return (
|
||||
<form action={updateCount}>
|
||||
<div id="count">{count}</div>
|
||||
<button>Submit</button>
|
||||
</form>
|
||||
);
|
||||
}
|
||||
5
packages/next/test/fixtures/00-app-dir-actions-experimental-streaming/app/edge/layout.js
vendored
Normal file
5
packages/next/test/fixtures/00-app-dir-actions-experimental-streaming/app/edge/layout.js
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
export const runtime = 'edge';
|
||||
|
||||
export default function Layout({ children }) {
|
||||
return <div>{children}</div>;
|
||||
}
|
||||
45
packages/next/test/fixtures/00-app-dir-actions-experimental-streaming/app/edge/other/page.js
vendored
Normal file
45
packages/next/test/fixtures/00-app-dir-actions-experimental-streaming/app/edge/other/page.js
vendored
Normal file
@@ -0,0 +1,45 @@
|
||||
'use client';
|
||||
|
||||
// @ts-ignore
|
||||
import { useCallback, useState } from 'react';
|
||||
|
||||
function request(method) {
|
||||
return fetch('/api/test', {
|
||||
method,
|
||||
headers: {
|
||||
'content-type': 'multipart/form-data;.*',
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export default function Home() {
|
||||
const [result, setResult] = useState('Press submit');
|
||||
const onClick = useCallback(async method => {
|
||||
const res = await request(method);
|
||||
const text = await res.text();
|
||||
|
||||
setResult(res.ok ? `${method} ${text}` : 'Error: ' + res.status);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="flex flex-col items-center justify-center h-screen">
|
||||
<div className="flex flex-row space-x-2 items-center justify-center">
|
||||
<button
|
||||
className="border border-white rounded-sm p-4 mb-4"
|
||||
onClick={() => onClick('GET')}
|
||||
>
|
||||
Submit GET
|
||||
</button>
|
||||
<button
|
||||
className="border border-white rounded-sm p-4 mb-4"
|
||||
onClick={() => onClick('POST')}
|
||||
>
|
||||
Submit POST
|
||||
</button>
|
||||
</div>
|
||||
<div className="text-white">{result}</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
export const dynamic = 'force-dynamic';
|
||||
|
||||
export default function Layout({ children }) {
|
||||
return children;
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
import { revalidatePath } from 'next/cache';
|
||||
|
||||
export default async function Page() {
|
||||
async function serverAction() {
|
||||
'use server';
|
||||
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||
revalidatePath('/dynamic');
|
||||
}
|
||||
|
||||
return (
|
||||
<form action={serverAction}>
|
||||
<button>Submit</button>
|
||||
</form>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
import { revalidatePath } from 'next/cache';
|
||||
|
||||
export default async function Page() {
|
||||
async function serverAction() {
|
||||
'use server';
|
||||
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||
revalidatePath('/dynamic');
|
||||
}
|
||||
|
||||
return (
|
||||
<form action={serverAction}>
|
||||
<button>Submit</button>
|
||||
</form>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
import { revalidatePath } from 'next/cache';
|
||||
|
||||
export default async function Page() {
|
||||
async function serverAction() {
|
||||
'use server';
|
||||
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||
revalidatePath('/dynamic');
|
||||
}
|
||||
|
||||
return (
|
||||
<form action={serverAction}>
|
||||
<button>Submit</button>
|
||||
</form>
|
||||
);
|
||||
}
|
||||
|
||||
export function generateStaticParams() {
|
||||
return [{ slug: 'pre-generated' }];
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
export const dynamic = 'force-static';
|
||||
|
||||
export default function Layout({ children }) {
|
||||
return children;
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
import { revalidatePath } from 'next/cache'
|
||||
|
||||
export default async function Page() {
|
||||
async function serverAction() {
|
||||
'use server';
|
||||
await new Promise((resolve) => setTimeout(resolve, 1000));
|
||||
revalidatePath('/dynamic');
|
||||
}
|
||||
|
||||
return (
|
||||
<form action={serverAction}>
|
||||
<button>Submit</button>
|
||||
</form>
|
||||
);
|
||||
}
|
||||
10
packages/next/test/fixtures/00-app-dir-actions-experimental-streaming/app/layout.js
vendored
Normal file
10
packages/next/test/fixtures/00-app-dir-actions-experimental-streaming/app/layout.js
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
export default function Root({ children }) {
|
||||
return (
|
||||
<html>
|
||||
<head>
|
||||
<title>Hello World</title>
|
||||
</head>
|
||||
<body>{children}</body>
|
||||
</html>
|
||||
);
|
||||
}
|
||||
45
packages/next/test/fixtures/00-app-dir-actions-experimental-streaming/app/other/page.js
vendored
Normal file
45
packages/next/test/fixtures/00-app-dir-actions-experimental-streaming/app/other/page.js
vendored
Normal file
@@ -0,0 +1,45 @@
|
||||
'use client';
|
||||
|
||||
// @ts-ignore
|
||||
import { useCallback, useState } from 'react';
|
||||
|
||||
function request(method) {
|
||||
return fetch('/api/test', {
|
||||
method,
|
||||
headers: {
|
||||
'content-type': 'multipart/form-data;.*',
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export default function Home() {
|
||||
const [result, setResult] = useState('Press submit');
|
||||
const onClick = useCallback(async method => {
|
||||
const res = await request(method);
|
||||
const text = await res.text();
|
||||
|
||||
setResult(res.ok ? `${method} ${text}` : 'Error: ' + res.status);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="flex flex-col items-center justify-center h-screen">
|
||||
<div className="flex flex-row space-x-2 items-center justify-center">
|
||||
<button
|
||||
className="border border-white rounded-sm p-4 mb-4"
|
||||
onClick={() => onClick('GET')}
|
||||
>
|
||||
Submit GET
|
||||
</button>
|
||||
<button
|
||||
className="border border-white rounded-sm p-4 mb-4"
|
||||
onClick={() => onClick('POST')}
|
||||
>
|
||||
Submit POST
|
||||
</button>
|
||||
</div>
|
||||
<div className="text-white">{result}</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
export const dynamic = 'force-dynamic';
|
||||
|
||||
export default function Layout({ children }) {
|
||||
return children;
|
||||
}
|
||||
15
packages/next/test/fixtures/00-app-dir-actions-experimental-streaming/app/rsc/dynamic/page.js
vendored
Normal file
15
packages/next/test/fixtures/00-app-dir-actions-experimental-streaming/app/rsc/dynamic/page.js
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
import { revalidatePath } from 'next/cache';
|
||||
|
||||
export default async function Page() {
|
||||
async function serverAction() {
|
||||
'use server';
|
||||
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||
revalidatePath('/dynamic');
|
||||
}
|
||||
|
||||
return (
|
||||
<form action={serverAction}>
|
||||
<button>Submit</button>
|
||||
</form>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
import { revalidatePath } from 'next/cache';
|
||||
|
||||
export default async function Page() {
|
||||
async function serverAction() {
|
||||
'use server';
|
||||
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||
revalidatePath('/dynamic');
|
||||
}
|
||||
|
||||
return (
|
||||
<form action={serverAction}>
|
||||
<button>Submit</button>
|
||||
</form>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
import { revalidatePath } from 'next/cache';
|
||||
|
||||
export default async function Page() {
|
||||
async function serverAction() {
|
||||
'use server';
|
||||
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||
revalidatePath('/dynamic');
|
||||
}
|
||||
|
||||
return (
|
||||
<form action={serverAction}>
|
||||
<button>Submit</button>
|
||||
</form>
|
||||
);
|
||||
}
|
||||
|
||||
export function generateStaticParams() {
|
||||
return [{ slug: 'pre-generated' }];
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
export const dynamic = 'force-static';
|
||||
|
||||
export default function Layout({ children }) {
|
||||
return children;
|
||||
}
|
||||
15
packages/next/test/fixtures/00-app-dir-actions-experimental-streaming/app/rsc/static/page.js
vendored
Normal file
15
packages/next/test/fixtures/00-app-dir-actions-experimental-streaming/app/rsc/static/page.js
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
import { revalidatePath } from 'next/cache'
|
||||
|
||||
export default async function Page() {
|
||||
async function serverAction() {
|
||||
'use server';
|
||||
await new Promise((resolve) => setTimeout(resolve, 1000));
|
||||
revalidatePath('/dynamic');
|
||||
}
|
||||
|
||||
return (
|
||||
<form action={serverAction}>
|
||||
<button>Submit</button>
|
||||
</form>
|
||||
);
|
||||
}
|
||||
304
packages/next/test/fixtures/00-app-dir-actions-experimental-streaming/index.test.js
vendored
Normal file
304
packages/next/test/fixtures/00-app-dir-actions-experimental-streaming/index.test.js
vendored
Normal file
@@ -0,0 +1,304 @@
|
||||
/* eslint-env jest */
|
||||
const path = require('path');
|
||||
const { deployAndTest } = require('../../utils');
|
||||
const fetch = require('../../../../../test/lib/deployment/fetch-retry');
|
||||
|
||||
const ctx = {};
|
||||
|
||||
function findActionId(page, runtime) {
|
||||
page = `app${page}/page`; // add /app prefix and /page suffix
|
||||
|
||||
for (const [actionId, details] of Object.entries(
|
||||
ctx.actionManifest[runtime]
|
||||
)) {
|
||||
if (details.workers[page]) {
|
||||
return actionId;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function generateFormDataPayload(actionId) {
|
||||
return {
|
||||
method: 'POST',
|
||||
body: `------WebKitFormBoundaryHcVuFa30AN0QV3uZ\r\nContent-Disposition: form-data; name=\"1_$ACTION_ID_${actionId}\"\r\n\r\n\r\n------WebKitFormBoundaryHcVuFa30AN0QV3uZ\r\nContent-Disposition: form-data; name=\"0\"\r\n\r\n[\"$K1\"]\r\n------WebKitFormBoundaryHcVuFa30AN0QV3uZ--\r\n`,
|
||||
headers: {
|
||||
'Content-Type':
|
||||
'multipart/form-data; boundary=----WebKitFormBoundaryHcVuFa30AN0QV3uZ',
|
||||
'Next-Action': actionId,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
describe(`${__dirname.split(path.sep).pop()}`, () => {
|
||||
beforeAll(async () => {
|
||||
const info = await deployAndTest(__dirname);
|
||||
|
||||
const actionManifest = await fetch(
|
||||
`${info.deploymentUrl}/server-reference-manifest.json`
|
||||
).then(res => res.json());
|
||||
|
||||
ctx.actionManifest = actionManifest;
|
||||
|
||||
Object.assign(ctx, info);
|
||||
});
|
||||
|
||||
describe.each(['node', 'edge'])('runtime: %s', runtime => {
|
||||
const basePath = runtime === 'edge' ? '/edge' : '';
|
||||
describe('client component', () => {
|
||||
it('should bypass the static cache for a server action', async () => {
|
||||
const path = `${basePath}/client/static`;
|
||||
const actionId = findActionId(path, runtime);
|
||||
|
||||
const res = await fetch(`${ctx.deploymentUrl}${path}`, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify([1337]),
|
||||
headers: {
|
||||
'Content-Type': 'text/plain;charset=UTF-8',
|
||||
'Next-Action': actionId,
|
||||
},
|
||||
});
|
||||
|
||||
expect(res.status).toEqual(200);
|
||||
const body = await res.text();
|
||||
expect(body).toContain('1338');
|
||||
expect(res.headers.get('x-matched-path')).toBe(path + '.action');
|
||||
if (runtime === 'node') {
|
||||
expect(res.headers.get('x-vercel-cache')).toBe('MISS');
|
||||
} else {
|
||||
// in edge runtime, x-vercel-cache is not returned on MISSes for some reason.
|
||||
// this checks to ensure it was routed to the edge function instead.
|
||||
expect(res.headers.get('x-edge-runtime')).toBe('1');
|
||||
}
|
||||
});
|
||||
|
||||
it('should bypass the static cache for a server action on a page with dynamic params', async () => {
|
||||
const path = `${basePath}/client/static/[dynamic-static]`;
|
||||
const actionId = findActionId(path, runtime);
|
||||
|
||||
const res = await fetch(`${ctx.deploymentUrl}${path}`, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify([1337]),
|
||||
headers: {
|
||||
'Content-Type': 'text/plain;charset=UTF-8',
|
||||
'Next-Action': actionId,
|
||||
},
|
||||
});
|
||||
|
||||
expect(res.status).toEqual(200);
|
||||
const body = await res.text();
|
||||
expect(body).toContain('1338');
|
||||
expect(res.headers.get('x-matched-path')).toBe(path + '.action');
|
||||
if (runtime === 'node') {
|
||||
expect(res.headers.get('x-vercel-cache')).toBe('MISS');
|
||||
} else {
|
||||
expect(res.headers.get('x-edge-runtime')).toBe('1');
|
||||
}
|
||||
});
|
||||
|
||||
it('should bypass the static cache for a multipart request (no action header)', async () => {
|
||||
const path = `${basePath}/client/static`;
|
||||
const actionId = findActionId(path, runtime);
|
||||
|
||||
const res = await fetch(`${ctx.deploymentUrl}${path}`, {
|
||||
method: 'POST',
|
||||
body: `------WebKitFormBoundaryHcVuFa30AN0QV3uZ\r\nContent-Disposition: form-data; name=\"1_$ACTION_ID_${actionId}\"\r\n\r\n\r\n------WebKitFormBoundaryHcVuFa30AN0QV3uZ\r\nContent-Disposition: form-data; name=\"0\"\r\n\r\n[\"$K1\"]\r\n------WebKitFormBoundaryHcVuFa30AN0QV3uZ--\r\n`,
|
||||
headers: {
|
||||
'Content-Type':
|
||||
'multipart/form-data; boundary=----WebKitFormBoundaryHcVuFa30AN0QV3uZ',
|
||||
},
|
||||
});
|
||||
|
||||
expect(res.status).toEqual(200);
|
||||
expect(res.headers.get('content-type')).toBe(
|
||||
'text/html; charset=utf-8'
|
||||
);
|
||||
if (runtime === 'node') {
|
||||
// This is a "BYPASS" because no action ID was provided, so it'll fall back to
|
||||
// `experimentalBypassFor` handling.
|
||||
expect(res.headers.get('x-vercel-cache')).toBe('BYPASS');
|
||||
} else {
|
||||
expect(res.headers.get('x-edge-runtime')).toBe('1');
|
||||
}
|
||||
expect(res.headers.get('x-matched-path')).toBe(path);
|
||||
});
|
||||
|
||||
it('should properly invoke the action on a dynamic page', async () => {
|
||||
const path = `${basePath}/client/dynamic/[id]`;
|
||||
const actionId = findActionId(path, runtime);
|
||||
|
||||
const res = await fetch(`${ctx.deploymentUrl}${path}`, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify([1337]),
|
||||
headers: {
|
||||
'Content-Type': 'text/plain;charset=UTF-8',
|
||||
'Next-Action': actionId,
|
||||
},
|
||||
});
|
||||
|
||||
expect(res.status).toEqual(200);
|
||||
const body = await res.text();
|
||||
expect(body).toContain('1338');
|
||||
expect(res.headers.get('x-matched-path')).toBe(path + '.action');
|
||||
if (runtime === 'node') {
|
||||
expect(res.headers.get('x-vercel-cache')).toBe('MISS');
|
||||
} else {
|
||||
expect(res.headers.get('x-edge-runtime')).toBe('1');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe('server component', () => {
|
||||
it('should bypass the static cache for a server action', async () => {
|
||||
const path = `${basePath}/rsc/static`;
|
||||
const actionId = findActionId(path, runtime);
|
||||
|
||||
const res = await fetch(
|
||||
`${ctx.deploymentUrl}${path}`,
|
||||
generateFormDataPayload(actionId)
|
||||
);
|
||||
|
||||
expect(res.status).toEqual(200);
|
||||
expect(res.headers.get('x-matched-path')).toBe(path + '.action');
|
||||
expect(res.headers.get('content-type')).toBe('text/x-component');
|
||||
if (runtime === 'node') {
|
||||
expect(res.headers.get('x-vercel-cache')).toBe('MISS');
|
||||
} else {
|
||||
expect(res.headers.get('x-edge-runtime')).toBe('1');
|
||||
}
|
||||
});
|
||||
|
||||
it('should bypass the static cache for a server action on a page with dynamic params', async () => {
|
||||
const path = `${basePath}/rsc/static/[dynamic-static]`;
|
||||
const actionId = findActionId(path, runtime);
|
||||
|
||||
const res = await fetch(
|
||||
`${ctx.deploymentUrl}${path}`,
|
||||
generateFormDataPayload(actionId)
|
||||
);
|
||||
|
||||
expect(res.status).toEqual(200);
|
||||
expect(res.headers.get('x-matched-path')).toBe(path + '.action');
|
||||
expect(res.headers.get('content-type')).toBe('text/x-component');
|
||||
if (runtime === 'node') {
|
||||
expect(res.headers.get('x-vercel-cache')).toBe('MISS');
|
||||
} else {
|
||||
expect(res.headers.get('x-edge-runtime')).toBe('1');
|
||||
}
|
||||
});
|
||||
|
||||
it('should properly invoke the action on a dynamic page', async () => {
|
||||
const path = `${basePath}/rsc/dynamic`;
|
||||
const actionId = findActionId(path, runtime);
|
||||
|
||||
const res = await fetch(
|
||||
`${ctx.deploymentUrl}${path}`,
|
||||
generateFormDataPayload(actionId)
|
||||
);
|
||||
|
||||
expect(res.status).toEqual(200);
|
||||
expect(res.headers.get('x-matched-path')).toBe(path + '.action');
|
||||
expect(res.headers.get('content-type')).toBe('text/x-component');
|
||||
if (runtime === 'node') {
|
||||
expect(res.headers.get('x-vercel-cache')).toBe('MISS');
|
||||
} else {
|
||||
expect(res.headers.get('x-edge-runtime')).toBe('1');
|
||||
}
|
||||
});
|
||||
|
||||
describe('generateStaticParams', () => {
|
||||
it('should bypass the static cache for a server action when pre-generated', async () => {
|
||||
const path = `${basePath}/rsc/static/generate-static-params/pre-generated`;
|
||||
const dynamicPath = `${basePath}/rsc/static/generate-static-params/[slug]`;
|
||||
const actionId = findActionId(dynamicPath, runtime);
|
||||
|
||||
const res = await fetch(
|
||||
`${ctx.deploymentUrl}${path}`,
|
||||
generateFormDataPayload(actionId)
|
||||
);
|
||||
|
||||
expect(res.status).toEqual(200);
|
||||
expect(res.headers.get('x-matched-path')).toBe(
|
||||
dynamicPath + '.action'
|
||||
);
|
||||
expect(res.headers.get('content-type')).toBe('text/x-component');
|
||||
if (runtime === 'node') {
|
||||
expect(res.headers.get('x-vercel-cache')).toBe('MISS');
|
||||
} else {
|
||||
expect(res.headers.get('x-edge-runtime')).toBe('1');
|
||||
}
|
||||
});
|
||||
|
||||
it('should bypass the static cache for a server action when not pre-generated', async () => {
|
||||
const page = `${basePath}/rsc/static/generate-static-params/[slug]`;
|
||||
const actionId = findActionId(page, runtime);
|
||||
|
||||
const res = await fetch(
|
||||
`${ctx.deploymentUrl}/${basePath}/rsc/static/generate-static-params/not-pre-generated`,
|
||||
generateFormDataPayload(actionId)
|
||||
);
|
||||
|
||||
expect(res.status).toEqual(200);
|
||||
expect(res.headers.get('x-matched-path')).toBe(page + '.action');
|
||||
expect(res.headers.get('content-type')).toBe('text/x-component');
|
||||
if (runtime === 'node') {
|
||||
expect(res.headers.get('x-vercel-cache')).toBe('MISS');
|
||||
} else {
|
||||
expect(res.headers.get('x-edge-runtime')).toBe('1');
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should not match to an action output when the RSC header is present', async () => {
|
||||
const canonicalPath = `${basePath}/client/dynamic/1`;
|
||||
const pagePath = `${basePath}/client/dynamic/[id]`;
|
||||
const actionId = findActionId(pagePath, runtime);
|
||||
|
||||
const res = await fetch(`${ctx.deploymentUrl}${canonicalPath}`, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify([1337]),
|
||||
headers: {
|
||||
'Content-Type': 'text/plain;charset=UTF-8',
|
||||
'Next-Action': actionId,
|
||||
'Next-Response': 'rsc',
|
||||
RSC: '1',
|
||||
},
|
||||
});
|
||||
|
||||
expect(res.status).toEqual(200);
|
||||
expect(res.headers.get('x-matched-path')).toBe(pagePath + '.rsc');
|
||||
expect(res.headers.get('content-type')).toBe('text/x-component');
|
||||
const body = await res.text();
|
||||
expect(body).toContain(JSON.stringify(['id', '1', 'd']));
|
||||
expect(body).not.toContain(JSON.stringify(['id', '1.action', 'd']));
|
||||
});
|
||||
});
|
||||
|
||||
describe('pages', () => {
|
||||
it('should not attempt to rewrite the action path for a server action (POST)', async () => {
|
||||
const res = await fetch(`${ctx.deploymentUrl}/api/test`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type':
|
||||
'multipart/form-data; boundary=----WebKitFormBoundaryHcVuFa30AN0QV3uZ',
|
||||
},
|
||||
});
|
||||
|
||||
expect(res.status).toEqual(200);
|
||||
expect(res.headers.get('x-matched-path')).toBe('/api/test');
|
||||
expect(res.headers.get('x-vercel-cache')).toBe('MISS');
|
||||
const body = await res.json();
|
||||
expect(body).toEqual({ message: 'Hello from Next.js!' });
|
||||
});
|
||||
|
||||
it('should not attempt to rewrite the action path for a server action (GET)', async () => {
|
||||
const res = await fetch(`${ctx.deploymentUrl}/api/test`);
|
||||
|
||||
expect(res.status).toEqual(200);
|
||||
expect(res.headers.get('x-matched-path')).toBe('/api/test');
|
||||
const body = await res.json();
|
||||
expect(body).toEqual({ message: 'Hello from Next.js!' });
|
||||
});
|
||||
});
|
||||
});
|
||||
1
packages/next/test/fixtures/00-app-dir-actions-experimental-streaming/next.config.js
vendored
Normal file
1
packages/next/test/fixtures/00-app-dir-actions-experimental-streaming/next.config.js
vendored
Normal file
@@ -0,0 +1 @@
|
||||
module.exports = {};
|
||||
9
packages/next/test/fixtures/00-app-dir-actions-experimental-streaming/package.json
vendored
Normal file
9
packages/next/test/fixtures/00-app-dir-actions-experimental-streaming/package.json
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"dependencies": {
|
||||
"next": "canary"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "next build && cp .next/server/server-reference-manifest.json public/"
|
||||
},
|
||||
"ignoreNextjsUpdates": true
|
||||
}
|
||||
3
packages/next/test/fixtures/00-app-dir-actions-experimental-streaming/pages/api/test.js
vendored
Normal file
3
packages/next/test/fixtures/00-app-dir-actions-experimental-streaming/pages/api/test.js
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
export default function handler(req, res) {
|
||||
res.status(200).json({ message: 'Hello from Next.js!' });
|
||||
}
|
||||
0
packages/next/test/fixtures/00-app-dir-actions-experimental-streaming/public/.keep
vendored
Normal file
0
packages/next/test/fixtures/00-app-dir-actions-experimental-streaming/public/.keep
vendored
Normal file
14
packages/next/test/fixtures/00-app-dir-actions-experimental-streaming/vercel.json
vendored
Normal file
14
packages/next/test/fixtures/00-app-dir-actions-experimental-streaming/vercel.json
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"builds": [
|
||||
{
|
||||
"src": "package.json",
|
||||
"use": "@vercel/next"
|
||||
}
|
||||
],
|
||||
"build": {
|
||||
"env": {
|
||||
"NEXT_EXPERIMENTAL_STREAMING_ACTIONS": "1"
|
||||
}
|
||||
},
|
||||
"probes": []
|
||||
}
|
||||
6
packages/next/test/fixtures/00-app-dir-actions/app/actions.js
vendored
Normal file
6
packages/next/test/fixtures/00-app-dir-actions/app/actions.js
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
'use server';
|
||||
|
||||
export async function increment(value) {
|
||||
await new Promise(resolve => setTimeout(resolve, 500));
|
||||
return value + 1;
|
||||
}
|
||||
19
packages/next/test/fixtures/00-app-dir-actions/app/client/dynamic/[id]/page.js
vendored
Normal file
19
packages/next/test/fixtures/00-app-dir-actions/app/client/dynamic/[id]/page.js
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
'use client';
|
||||
|
||||
import { useState } from 'react';
|
||||
import { increment } from '../../../actions';
|
||||
|
||||
export default function Page() {
|
||||
const [count, setCount] = useState(0);
|
||||
async function updateCount() {
|
||||
const newCount = await increment(count);
|
||||
setCount(newCount);
|
||||
}
|
||||
|
||||
return (
|
||||
<form action={updateCount}>
|
||||
<div id="count">{count}</div>
|
||||
<button>Submit</button>
|
||||
</form>
|
||||
);
|
||||
}
|
||||
5
packages/next/test/fixtures/00-app-dir-actions/app/client/dynamic/layout.js
vendored
Normal file
5
packages/next/test/fixtures/00-app-dir-actions/app/client/dynamic/layout.js
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
export const dynamic = 'force-dynamic';
|
||||
|
||||
export default function Layout({ children }) {
|
||||
return children;
|
||||
}
|
||||
19
packages/next/test/fixtures/00-app-dir-actions/app/client/static/[dynamic-static]/page.js
vendored
Normal file
19
packages/next/test/fixtures/00-app-dir-actions/app/client/static/[dynamic-static]/page.js
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
'use client';
|
||||
|
||||
import { useState } from 'react';
|
||||
import { increment } from '../../../actions';
|
||||
|
||||
export default function Page() {
|
||||
const [count, setCount] = useState(0);
|
||||
async function updateCount() {
|
||||
const newCount = await increment(count);
|
||||
setCount(newCount);
|
||||
}
|
||||
|
||||
return (
|
||||
<form action={updateCount}>
|
||||
<div id="count">{count}</div>
|
||||
<button>Submit</button>
|
||||
</form>
|
||||
);
|
||||
}
|
||||
5
packages/next/test/fixtures/00-app-dir-actions/app/client/static/layout.js
vendored
Normal file
5
packages/next/test/fixtures/00-app-dir-actions/app/client/static/layout.js
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
export const dynamic = 'force-static';
|
||||
|
||||
export default function Layout({ children }) {
|
||||
return children;
|
||||
}
|
||||
19
packages/next/test/fixtures/00-app-dir-actions/app/client/static/page.js
vendored
Normal file
19
packages/next/test/fixtures/00-app-dir-actions/app/client/static/page.js
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
'use client';
|
||||
|
||||
import { useState } from 'react';
|
||||
import { increment } from '../../actions';
|
||||
|
||||
export default function Page() {
|
||||
const [count, setCount] = useState(0);
|
||||
async function updateCount() {
|
||||
const newCount = await increment(count);
|
||||
setCount(newCount);
|
||||
}
|
||||
|
||||
return (
|
||||
<form action={updateCount}>
|
||||
<div id="count">{count}</div>
|
||||
<button>Submit</button>
|
||||
</form>
|
||||
);
|
||||
}
|
||||
10
packages/next/test/fixtures/00-app-dir-actions/app/layout.js
vendored
Normal file
10
packages/next/test/fixtures/00-app-dir-actions/app/layout.js
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
export default function Root({ children }) {
|
||||
return (
|
||||
<html>
|
||||
<head>
|
||||
<title>Hello World</title>
|
||||
</head>
|
||||
<body>{children}</body>
|
||||
</html>
|
||||
);
|
||||
}
|
||||
45
packages/next/test/fixtures/00-app-dir-actions/app/other/page.js
vendored
Normal file
45
packages/next/test/fixtures/00-app-dir-actions/app/other/page.js
vendored
Normal file
@@ -0,0 +1,45 @@
|
||||
'use client';
|
||||
|
||||
// @ts-ignore
|
||||
import { useCallback, useState } from 'react';
|
||||
|
||||
function request(method) {
|
||||
return fetch('/api/test', {
|
||||
method,
|
||||
headers: {
|
||||
'content-type': 'multipart/form-data;.*',
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export default function Home() {
|
||||
const [result, setResult] = useState('Press submit');
|
||||
const onClick = useCallback(async method => {
|
||||
const res = await request(method);
|
||||
const text = await res.text();
|
||||
|
||||
setResult(res.ok ? `${method} ${text}` : 'Error: ' + res.status);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="flex flex-col items-center justify-center h-screen">
|
||||
<div className="flex flex-row space-x-2 items-center justify-center">
|
||||
<button
|
||||
className="border border-white rounded-sm p-4 mb-4"
|
||||
onClick={() => onClick('GET')}
|
||||
>
|
||||
Submit GET
|
||||
</button>
|
||||
<button
|
||||
className="border border-white rounded-sm p-4 mb-4"
|
||||
onClick={() => onClick('POST')}
|
||||
>
|
||||
Submit POST
|
||||
</button>
|
||||
</div>
|
||||
<div className="text-white">{result}</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
5
packages/next/test/fixtures/00-app-dir-actions/app/rsc/dynamic/layout.js
vendored
Normal file
5
packages/next/test/fixtures/00-app-dir-actions/app/rsc/dynamic/layout.js
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
export const dynamic = 'force-dynamic';
|
||||
|
||||
export default function Layout({ children }) {
|
||||
return children;
|
||||
}
|
||||
15
packages/next/test/fixtures/00-app-dir-actions/app/rsc/dynamic/page.js
vendored
Normal file
15
packages/next/test/fixtures/00-app-dir-actions/app/rsc/dynamic/page.js
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
import { revalidatePath } from 'next/cache';
|
||||
|
||||
export default async function Page() {
|
||||
async function serverAction() {
|
||||
'use server';
|
||||
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||
revalidatePath('/dynamic');
|
||||
}
|
||||
|
||||
return (
|
||||
<form action={serverAction}>
|
||||
<button>Submit</button>
|
||||
</form>
|
||||
);
|
||||
}
|
||||
15
packages/next/test/fixtures/00-app-dir-actions/app/rsc/static/[dynamic-static]/page.js
vendored
Normal file
15
packages/next/test/fixtures/00-app-dir-actions/app/rsc/static/[dynamic-static]/page.js
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
import { revalidatePath } from 'next/cache';
|
||||
|
||||
export default async function Page() {
|
||||
async function serverAction() {
|
||||
'use server';
|
||||
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||
revalidatePath('/dynamic');
|
||||
}
|
||||
|
||||
return (
|
||||
<form action={serverAction}>
|
||||
<button>Submit</button>
|
||||
</form>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
import { revalidatePath } from 'next/cache';
|
||||
|
||||
export default async function Page() {
|
||||
async function serverAction() {
|
||||
'use server';
|
||||
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||
revalidatePath('/dynamic');
|
||||
}
|
||||
|
||||
return (
|
||||
<form action={serverAction}>
|
||||
<button>Submit</button>
|
||||
</form>
|
||||
);
|
||||
}
|
||||
|
||||
export function generateStaticParams() {
|
||||
return [{ slug: 'pre-generated' }];
|
||||
}
|
||||
5
packages/next/test/fixtures/00-app-dir-actions/app/rsc/static/layout.js
vendored
Normal file
5
packages/next/test/fixtures/00-app-dir-actions/app/rsc/static/layout.js
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
export const dynamic = 'force-static';
|
||||
|
||||
export default function Layout({ children }) {
|
||||
return children;
|
||||
}
|
||||
15
packages/next/test/fixtures/00-app-dir-actions/app/rsc/static/page.js
vendored
Normal file
15
packages/next/test/fixtures/00-app-dir-actions/app/rsc/static/page.js
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
import { revalidatePath } from 'next/cache'
|
||||
|
||||
export default async function Page() {
|
||||
async function serverAction() {
|
||||
'use server';
|
||||
await new Promise((resolve) => setTimeout(resolve, 1000));
|
||||
revalidatePath('/dynamic');
|
||||
}
|
||||
|
||||
return (
|
||||
<form action={serverAction}>
|
||||
<button>Submit</button>
|
||||
</form>
|
||||
);
|
||||
}
|
||||
237
packages/next/test/fixtures/00-app-dir-actions/index.test.js
vendored
Normal file
237
packages/next/test/fixtures/00-app-dir-actions/index.test.js
vendored
Normal file
@@ -0,0 +1,237 @@
|
||||
/* eslint-env jest */
|
||||
const path = require('path');
|
||||
const { deployAndTest } = require('../../utils');
|
||||
const fetch = require('../../../../../test/lib/deployment/fetch-retry');
|
||||
|
||||
const ctx = {};
|
||||
|
||||
function findActionId(page) {
|
||||
page = `app${page}/page`; // add /app prefix and /page suffix
|
||||
|
||||
for (const [actionId, details] of Object.entries(ctx.actionManifest.node)) {
|
||||
if (details.workers[page]) {
|
||||
return actionId;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function generateFormDataPayload(actionId) {
|
||||
return {
|
||||
method: 'POST',
|
||||
body: `------WebKitFormBoundaryHcVuFa30AN0QV3uZ\r\nContent-Disposition: form-data; name=\"1_$ACTION_ID_${actionId}\"\r\n\r\n\r\n------WebKitFormBoundaryHcVuFa30AN0QV3uZ\r\nContent-Disposition: form-data; name=\"0\"\r\n\r\n[\"$K1\"]\r\n------WebKitFormBoundaryHcVuFa30AN0QV3uZ--\r\n`,
|
||||
headers: {
|
||||
'Content-Type':
|
||||
'multipart/form-data; boundary=----WebKitFormBoundaryHcVuFa30AN0QV3uZ',
|
||||
'Next-Action': actionId,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
describe(`${__dirname.split(path.sep).pop()}`, () => {
|
||||
beforeAll(async () => {
|
||||
const info = await deployAndTest(__dirname);
|
||||
|
||||
const actionManifest = await fetch(
|
||||
`${info.deploymentUrl}/server-reference-manifest.json`
|
||||
).then(res => res.json());
|
||||
|
||||
ctx.actionManifest = actionManifest;
|
||||
|
||||
Object.assign(ctx, info);
|
||||
});
|
||||
|
||||
describe('client component', () => {
|
||||
it('should bypass the static cache for a server action', async () => {
|
||||
const path = '/client/static';
|
||||
const actionId = findActionId(path);
|
||||
|
||||
const res = await fetch(`${ctx.deploymentUrl}${path}`, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify([1337]),
|
||||
headers: {
|
||||
'Content-Type': 'text/plain;charset=UTF-8',
|
||||
'Next-Action': actionId,
|
||||
},
|
||||
});
|
||||
|
||||
expect(res.status).toEqual(200);
|
||||
const body = await res.text();
|
||||
expect(body).toContain('1338');
|
||||
expect(res.headers.get('x-matched-path')).toBe(path);
|
||||
expect(res.headers.get('x-vercel-cache')).toBe('BYPASS');
|
||||
});
|
||||
|
||||
it('should bypass the static cache for a server action on a page with dynamic params', async () => {
|
||||
const path = '/client/static/[dynamic-static]';
|
||||
const actionId = findActionId(path);
|
||||
|
||||
const res = await fetch(`${ctx.deploymentUrl}${path}`, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify([1337]),
|
||||
headers: {
|
||||
'Content-Type': 'text/plain;charset=UTF-8',
|
||||
'Next-Action': actionId,
|
||||
},
|
||||
});
|
||||
|
||||
expect(res.status).toEqual(200);
|
||||
const body = await res.text();
|
||||
expect(body).toContain('1338');
|
||||
expect(res.headers.get('x-matched-path')).toBe(path);
|
||||
expect(res.headers.get('x-vercel-cache')).toBe('BYPASS');
|
||||
});
|
||||
|
||||
it('should bypass the static cache for a multipart request (no action header)', async () => {
|
||||
const path = '/client/static';
|
||||
const actionId = findActionId(path);
|
||||
|
||||
const res = await fetch(`${ctx.deploymentUrl}${path}`, {
|
||||
method: 'POST',
|
||||
body: `------WebKitFormBoundaryHcVuFa30AN0QV3uZ\r\nContent-Disposition: form-data; name=\"1_$ACTION_ID_${actionId}\"\r\n\r\n\r\n------WebKitFormBoundaryHcVuFa30AN0QV3uZ\r\nContent-Disposition: form-data; name=\"0\"\r\n\r\n[\"$K1\"]\r\n------WebKitFormBoundaryHcVuFa30AN0QV3uZ--\r\n`,
|
||||
headers: {
|
||||
'Content-Type':
|
||||
'multipart/form-data; boundary=----WebKitFormBoundaryHcVuFa30AN0QV3uZ',
|
||||
},
|
||||
});
|
||||
|
||||
expect(res.status).toEqual(200);
|
||||
expect(res.headers.get('content-type')).toBe('text/html; charset=utf-8');
|
||||
expect(res.headers.get('x-vercel-cache')).toBe('BYPASS');
|
||||
expect(res.headers.get('x-matched-path')).toBe(path);
|
||||
});
|
||||
|
||||
it('should properly invoke the action on a dynamic page', async () => {
|
||||
const path = '/client/dynamic/[id]';
|
||||
const actionId = findActionId(path);
|
||||
|
||||
const res = await fetch(`${ctx.deploymentUrl}${path}`, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify([1337]),
|
||||
headers: {
|
||||
'Content-Type': 'text/plain;charset=UTF-8',
|
||||
'Next-Action': actionId,
|
||||
},
|
||||
});
|
||||
|
||||
expect(res.status).toEqual(200);
|
||||
const body = await res.text();
|
||||
expect(body).toContain('1338');
|
||||
expect(res.headers.get('x-matched-path')).toBe(path);
|
||||
// This isn't a "BYPASS" because the action wasn't part of a static prerender
|
||||
expect(res.headers.get('x-vercel-cache')).toBe('MISS');
|
||||
});
|
||||
});
|
||||
|
||||
describe('server component', () => {
|
||||
it('should bypass the static cache for a server action', async () => {
|
||||
const path = '/rsc/static';
|
||||
const actionId = findActionId(path);
|
||||
|
||||
const res = await fetch(
|
||||
`${ctx.deploymentUrl}${path}`,
|
||||
generateFormDataPayload(actionId)
|
||||
);
|
||||
|
||||
expect(res.status).toEqual(200);
|
||||
expect(res.headers.get('x-matched-path')).toBe(path);
|
||||
expect(res.headers.get('content-type')).toBe('text/x-component');
|
||||
expect(res.headers.get('x-vercel-cache')).toBe('BYPASS');
|
||||
});
|
||||
|
||||
it('should bypass the static cache for a server action on a page with dynamic params', async () => {
|
||||
const path = '/rsc/static/[dynamic-static]';
|
||||
const actionId = findActionId(path);
|
||||
|
||||
const res = await fetch(
|
||||
`${ctx.deploymentUrl}${path}`,
|
||||
generateFormDataPayload(actionId)
|
||||
);
|
||||
|
||||
expect(res.status).toEqual(200);
|
||||
expect(res.headers.get('x-matched-path')).toBe(path);
|
||||
expect(res.headers.get('content-type')).toBe('text/x-component');
|
||||
expect(res.headers.get('x-vercel-cache')).toBe('BYPASS');
|
||||
});
|
||||
|
||||
it('should properly invoke the action on a dynamic page', async () => {
|
||||
const path = '/rsc/dynamic';
|
||||
const actionId = findActionId(path);
|
||||
|
||||
const res = await fetch(
|
||||
`${ctx.deploymentUrl}${path}`,
|
||||
generateFormDataPayload(actionId)
|
||||
);
|
||||
|
||||
expect(res.status).toEqual(200);
|
||||
expect(res.headers.get('x-matched-path')).toBe(path);
|
||||
expect(res.headers.get('content-type')).toBe('text/x-component');
|
||||
// This isn't a "BYPASS" because the action wasn't part of a static prerender
|
||||
expect(res.headers.get('x-vercel-cache')).toBe('MISS');
|
||||
});
|
||||
|
||||
describe('generateStaticParams', () => {
|
||||
it('should bypass the static cache for a server action when pre-generated', async () => {
|
||||
const path = '/rsc/static/generate-static-params/pre-generated';
|
||||
const actionId = findActionId(
|
||||
'/rsc/static/generate-static-params/[slug]'
|
||||
);
|
||||
|
||||
const res = await fetch(
|
||||
`${ctx.deploymentUrl}${path}`,
|
||||
generateFormDataPayload(actionId)
|
||||
);
|
||||
|
||||
expect(res.status).toEqual(200);
|
||||
expect(res.headers.get('x-matched-path')).toBe(
|
||||
'/rsc/static/generate-static-params/pre-generated'
|
||||
);
|
||||
expect(res.headers.get('content-type')).toBe('text/x-component');
|
||||
expect(res.headers.get('x-vercel-cache')).toBe('BYPASS');
|
||||
});
|
||||
|
||||
it('should bypass the static cache for a server action when not pre-generated', async () => {
|
||||
const page = '/rsc/static/generate-static-params/[slug]';
|
||||
const actionId = findActionId(page);
|
||||
|
||||
const res = await fetch(
|
||||
`${ctx.deploymentUrl}/rsc/static/generate-static-params/not-pre-generated`,
|
||||
generateFormDataPayload(actionId)
|
||||
);
|
||||
|
||||
expect(res.status).toEqual(200);
|
||||
expect(res.headers.get('x-matched-path')).toBe(page);
|
||||
expect(res.headers.get('content-type')).toBe('text/x-component');
|
||||
// This isn't a "BYPASS" because the action wasn't part of a static prerender
|
||||
expect(res.headers.get('x-vercel-cache')).toBe('BYPASS');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('pages', () => {
|
||||
it('should not attempt to rewrite the action path for a server action (POST)', async () => {
|
||||
const res = await fetch(`${ctx.deploymentUrl}/api/test`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type':
|
||||
'multipart/form-data; boundary=----WebKitFormBoundaryHcVuFa30AN0QV3uZ',
|
||||
},
|
||||
});
|
||||
|
||||
expect(res.status).toEqual(200);
|
||||
expect(res.headers.get('x-matched-path')).toBe('/api/test');
|
||||
expect(res.headers.get('x-vercel-cache')).toBe('MISS');
|
||||
const body = await res.json();
|
||||
expect(body).toEqual({ message: 'Hello from Next.js!' });
|
||||
});
|
||||
|
||||
it('should not attempt to rewrite the action path for a server action (GET)', async () => {
|
||||
const res = await fetch(`${ctx.deploymentUrl}/api/test`);
|
||||
|
||||
expect(res.status).toEqual(200);
|
||||
expect(res.headers.get('x-matched-path')).toBe('/api/test');
|
||||
const body = await res.json();
|
||||
expect(body).toEqual({ message: 'Hello from Next.js!' });
|
||||
});
|
||||
});
|
||||
});
|
||||
1
packages/next/test/fixtures/00-app-dir-actions/next.config.js
vendored
Normal file
1
packages/next/test/fixtures/00-app-dir-actions/next.config.js
vendored
Normal file
@@ -0,0 +1 @@
|
||||
module.exports = {};
|
||||
9
packages/next/test/fixtures/00-app-dir-actions/package.json
vendored
Normal file
9
packages/next/test/fixtures/00-app-dir-actions/package.json
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"dependencies": {
|
||||
"next": "canary"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "next build && cp .next/server/server-reference-manifest.json public/"
|
||||
},
|
||||
"ignoreNextjsUpdates": true
|
||||
}
|
||||
3
packages/next/test/fixtures/00-app-dir-actions/pages/api/test.js
vendored
Normal file
3
packages/next/test/fixtures/00-app-dir-actions/pages/api/test.js
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
export default function handler(req, res) {
|
||||
res.status(200).json({ message: 'Hello from Next.js!' });
|
||||
}
|
||||
0
packages/next/test/fixtures/00-app-dir-actions/public/.keep
vendored
Normal file
0
packages/next/test/fixtures/00-app-dir-actions/public/.keep
vendored
Normal file
9
packages/next/test/fixtures/00-app-dir-actions/vercel.json
vendored
Normal file
9
packages/next/test/fixtures/00-app-dir-actions/vercel.json
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"builds": [
|
||||
{
|
||||
"src": "package.json",
|
||||
"use": "@vercel/next"
|
||||
}
|
||||
],
|
||||
"probes": []
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user