mirror of
https://github.com/LukeHagar/vercel.git
synced 2025-12-24 03:39:11 +00:00
Compare commits
47 Commits
@vercel/ru
...
@vercel/ga
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fcea36bf04 | ||
|
|
93f5a4438b | ||
|
|
72265aa9a1 | ||
|
|
6ee5eb137b | ||
|
|
c4f1c2f5ed | ||
|
|
f35a77c292 | ||
|
|
4bf3c237ee | ||
|
|
62c991f25e | ||
|
|
6ea2db4ae9 | ||
|
|
1943b1ecc0 | ||
|
|
92f5b6e0c9 | ||
|
|
ed6ce1149a | ||
|
|
fc3611fb80 | ||
|
|
ed33c2b27c | ||
|
|
a7a5bf1a12 | ||
|
|
cc687b3880 | ||
|
|
053ec92d5f | ||
|
|
4838dc336a | ||
|
|
eae45f4019 | ||
|
|
02feb564a7 | ||
|
|
e174a06673 | ||
|
|
de034943af | ||
|
|
b3862271a5 | ||
|
|
aaceeef604 | ||
|
|
ad107ecf79 | ||
|
|
79ef5c3724 | ||
|
|
02ff265074 | ||
|
|
ae89b8b8be | ||
|
|
4ccdcde463 | ||
|
|
22d3ee160b | ||
|
|
6d97e1673e | ||
|
|
522565f6e5 | ||
|
|
07bf81ab10 | ||
|
|
35024a4e3a | ||
|
|
c1df9bca19 | ||
|
|
4c1cdd1f0f | ||
|
|
b5cdc82a1c | ||
|
|
c7851404b3 | ||
|
|
e54da8a2e5 | ||
|
|
a066bedf95 | ||
|
|
09b23e53ba | ||
|
|
b793a67588 | ||
|
|
31dd354b3a | ||
|
|
529ff3b2d7 | ||
|
|
e71d5638ee | ||
|
|
8c16e765ee | ||
|
|
a008c9c7fe |
@@ -43,5 +43,8 @@ packages/static-build/test/cache-fixtures
|
||||
# redwood
|
||||
packages/redwood/test/fixtures
|
||||
|
||||
# remix
|
||||
packages/remix/test/fixtures
|
||||
|
||||
# gatsby-plugin-vercel-analytics
|
||||
packages/gatsby-plugin-vercel-analytics
|
||||
|
||||
@@ -4,8 +4,7 @@
|
||||
"version": "0.0.0",
|
||||
"description": "API for the vercel/vercel repo",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
},
|
||||
"scripts": {},
|
||||
"dependencies": {
|
||||
"@sentry/node": "5.11.1",
|
||||
"got": "10.2.1",
|
||||
@@ -17,7 +16,7 @@
|
||||
"devDependencies": {
|
||||
"@types/node": "16.18.11",
|
||||
"@types/node-fetch": "2.5.4",
|
||||
"@vercel/node": "workspace:2.8.6",
|
||||
"@vercel/node": "*",
|
||||
"typescript": "4.3.4"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,5 +9,8 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"gridsome": "0.7.23"
|
||||
},
|
||||
"engines": {
|
||||
"node": "<17"
|
||||
}
|
||||
}
|
||||
|
||||
517
examples/nextjs/package-lock.json
generated
517
examples/nextjs/package-lock.json
generated
@@ -8,10 +8,10 @@
|
||||
"name": "nextjs",
|
||||
"version": "0.1.0",
|
||||
"dependencies": {
|
||||
"@next/font": "13.1.2",
|
||||
"eslint": "8.31.0",
|
||||
"eslint-config-next": "13.1.2",
|
||||
"next": "13.1.2",
|
||||
"@next/font": "13.1.4",
|
||||
"eslint": "8.32.0",
|
||||
"eslint-config-next": "13.1.4",
|
||||
"next": "13.1.4",
|
||||
"react": "18.2.0",
|
||||
"react-dom": "18.2.0"
|
||||
}
|
||||
@@ -80,27 +80,27 @@
|
||||
"integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA=="
|
||||
},
|
||||
"node_modules/@next/env": {
|
||||
"version": "13.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@next/env/-/env-13.1.2.tgz",
|
||||
"integrity": "sha512-PpT4UZIX66VMTqXt4HKEJ+/PwbS+tWmmhZlazaws1a+dbUA5pPdjntQ46Jvj616i3ZKN9doS9LHx3y50RLjAWg=="
|
||||
"version": "13.1.4",
|
||||
"resolved": "https://registry.npmjs.org/@next/env/-/env-13.1.4.tgz",
|
||||
"integrity": "sha512-x7ydhMpi9/xX7yVK+Fw33OuwwQWVZUFRxenK3z89fmPzQZyUk35Ynb+b7JkrhfRhDIFFvvqpzVSXeseSlBAw7A=="
|
||||
},
|
||||
"node_modules/@next/eslint-plugin-next": {
|
||||
"version": "13.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-13.1.2.tgz",
|
||||
"integrity": "sha512-WGaNVvIYphdriesP6r7jq/8l7u38tzotnVQuxc1RYKLqYYApSsrebti3OCPoT3Gx0pw2smPIFHH98RzcsgW5GQ==",
|
||||
"version": "13.1.4",
|
||||
"resolved": "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-13.1.4.tgz",
|
||||
"integrity": "sha512-a/T30+7Q1scom5t3L+wEBkYzCa+bhT/3DTxzxlNy4Xckw2InzcckQGeIi/larDgh5r2fSSJswhYAZEcKtuJiig==",
|
||||
"dependencies": {
|
||||
"glob": "7.1.7"
|
||||
}
|
||||
},
|
||||
"node_modules/@next/font": {
|
||||
"version": "13.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@next/font/-/font-13.1.2.tgz",
|
||||
"integrity": "sha512-NXGXGFGiOKEnvBIHq9cdFTKbHO2/4B3Zd9K27M7j1DioIQVar7oVRqZMYs0h3XMVEZLwjjkdAtqRPCzzd3RtXg=="
|
||||
"version": "13.1.4",
|
||||
"resolved": "https://registry.npmjs.org/@next/font/-/font-13.1.4.tgz",
|
||||
"integrity": "sha512-NfqQPAIFJhssdaZEsP0MADZKN+tc040jOkNKVlDZHtVjJWBJSgbe8UHg3w0S6YuQr7PHc0ACbFeVCBQeG4MEvg=="
|
||||
},
|
||||
"node_modules/@next/swc-android-arm-eabi": {
|
||||
"version": "13.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-android-arm-eabi/-/swc-android-arm-eabi-13.1.2.tgz",
|
||||
"integrity": "sha512-7mRz1owoGsbfIcdOJA3kk7KEwPZ+OvVT1z9DkR/yru4QdVLF69h/1SHy0vlUNQMxDRllabhxCfkoZCB34GOGAg==",
|
||||
"version": "13.1.4",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-android-arm-eabi/-/swc-android-arm-eabi-13.1.4.tgz",
|
||||
"integrity": "sha512-5PAchzFst3In6Ml+9APvBj89H29lcPXcUqEYBVv09fWK/V4IuViKc2qOqM9pyPyw7KsqaZPmuqaG595E6jdZLA==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
@@ -113,9 +113,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@next/swc-android-arm64": {
|
||||
"version": "13.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-android-arm64/-/swc-android-arm64-13.1.2.tgz",
|
||||
"integrity": "sha512-mgjZ2eJSayovQm1LcE54BLSI4jjnnnLtq5GY5g+DdPuUiCT644gKtjZ/w2BQvuIecCqqBO+Ph9yzo/wUTq7NLg==",
|
||||
"version": "13.1.4",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-android-arm64/-/swc-android-arm64-13.1.4.tgz",
|
||||
"integrity": "sha512-LCLjjRhsQ5fR9ExzR2fqxuyJe/D4Ct/YkdonVfJfqOfkEpFwUTQDOVo5GrQec4LZDk3zY+o6vZYjXbB0nD9VLA==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -128,9 +128,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@next/swc-darwin-arm64": {
|
||||
"version": "13.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-13.1.2.tgz",
|
||||
"integrity": "sha512-RikoQqy109r2222UJlyGs4dZw2BibkfPqpeFdW5JEGv+L2PStlHID8DwyVYbmHfQ0VIBGvbf/NAUtFakAWlhwg==",
|
||||
"version": "13.1.4",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-13.1.4.tgz",
|
||||
"integrity": "sha512-LSc/tF1FQ1y1SwKiCdGg8IIl7+Csk6nuLcLIyQXs24UNYjXg5+7vUQXqE8y66v/Dq8qFDC9rM61QhpM9ZDftbg==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -143,9 +143,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@next/swc-darwin-x64": {
|
||||
"version": "13.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-13.1.2.tgz",
|
||||
"integrity": "sha512-JbDZjaTvL8gyPC5TAH6OnD4jmXPkyUxRYPvu08ZmhT/XAFBb/Cso0BdXyDax/BPCG70mimP9d3hXNKNq+A0VtQ==",
|
||||
"version": "13.1.4",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-13.1.4.tgz",
|
||||
"integrity": "sha512-WoApDo8xfafrNc9+Mz5MwGFKUwbDHsGqLleTGZ8upegwVqDyHsYzqJQudf+loqhV58oGTOqP1eWaHn2J7dijXA==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -158,9 +158,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@next/swc-freebsd-x64": {
|
||||
"version": "13.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-freebsd-x64/-/swc-freebsd-x64-13.1.2.tgz",
|
||||
"integrity": "sha512-ax4j8VrdFQ/xc3W7Om0u1vnDxVApQHKsChBbAMynCrnycZmpbqK4MZu4ZkycT+mx2eccCiqZROpbzDbEdPosEw==",
|
||||
"version": "13.1.4",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-freebsd-x64/-/swc-freebsd-x64-13.1.4.tgz",
|
||||
"integrity": "sha512-fqNyeT8G4guN8AHPIoBRhGY2GJg89FyWpuwX4o0Y3vUy/84IGZpNst3paCzaYkQSqQE/AuCpkB7hKxkN7ittXw==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -173,9 +173,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@next/swc-linux-arm-gnueabihf": {
|
||||
"version": "13.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-linux-arm-gnueabihf/-/swc-linux-arm-gnueabihf-13.1.2.tgz",
|
||||
"integrity": "sha512-NcRHTesnCxnUvSJa637PQJffBBkmqi5XS/xVWGY7dI6nyJ+pC96Oj7kd+mcjnFUQI5lHKbg39qBWKtOzbezc4w==",
|
||||
"version": "13.1.4",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-linux-arm-gnueabihf/-/swc-linux-arm-gnueabihf-13.1.4.tgz",
|
||||
"integrity": "sha512-MEfm8OC1YR9/tYHUzlQsxcSmiuf8XdO7bqh5VtG4pilScjc5I5t+tQgIDgoDGePfh5W99W23hb3s6oCFrt99rw==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
@@ -188,9 +188,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@next/swc-linux-arm64-gnu": {
|
||||
"version": "13.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-13.1.2.tgz",
|
||||
"integrity": "sha512-AxJdjocLtPrsBY4P2COSBIc3crT5bpjgGenNuINoensOlXhBkYM0aRDYZdydwXOhG+kN2ngUvfgitop9pa204w==",
|
||||
"version": "13.1.4",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-13.1.4.tgz",
|
||||
"integrity": "sha512-2wgth/KsuODzW/E7jsRoWdhKmE5oZzXcBPvf9RW+ZpBNvYQkEDlzfLA7n8DtxTU8I4oMas0mdEPdCWXrSNnVZw==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -203,9 +203,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@next/swc-linux-arm64-musl": {
|
||||
"version": "13.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-13.1.2.tgz",
|
||||
"integrity": "sha512-JmNimDkcCRq7P5zpkdqeaSZ69qKDntEPtyIaMNWqy5M0WUJxGim0Fs6Qzxayiyvuuh9Guxks4woQ/j/ZvX/c8Q==",
|
||||
"version": "13.1.4",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-13.1.4.tgz",
|
||||
"integrity": "sha512-GdWhCRljsT7rNEElEsdu4RRppd+XaQOX1IJslsh/+HU6LsJGUE8tXpa68yJjCsHZHifkbdZNeCr5SYdsN6CbAA==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -218,9 +218,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@next/swc-linux-x64-gnu": {
|
||||
"version": "13.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-13.1.2.tgz",
|
||||
"integrity": "sha512-TsLsjZwUlgmvI42neTuIoD6K9RlXCUzqPtvIClgXxVO0um0DiZwK+M+0zX/uVXhMVphfPY2c5YeR1zFSIONY4A==",
|
||||
"version": "13.1.4",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-13.1.4.tgz",
|
||||
"integrity": "sha512-Rsk/ojwYqMskN2eo5hUSVe7UuMV/aSjmrmJ0BCFGFPfBY9sPgmYj/oXlDDN0y5lJD9acPuiBjknLWgnOnx5JIA==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -233,9 +233,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@next/swc-linux-x64-musl": {
|
||||
"version": "13.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-13.1.2.tgz",
|
||||
"integrity": "sha512-eSkyXgCXydEFPTkcncQOGepafedPte6JT/OofB9uvruucrrMVBagCASOuPxodWEMrlfEKSXVnExMKIlfmQMD7A==",
|
||||
"version": "13.1.4",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-13.1.4.tgz",
|
||||
"integrity": "sha512-gKSVPozedA2gpA+vggYnAqpDuzWFed2oxFeXxHw0aW2ALdAZswAinn1ZwXEQ5fHnVguxjZhH0+2nBxpMdF8p5Q==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -248,9 +248,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@next/swc-win32-arm64-msvc": {
|
||||
"version": "13.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-13.1.2.tgz",
|
||||
"integrity": "sha512-DmXFaRTgt2KrV9dmRLifDJE+cYiutHVFIw5/C9BtnwXH39uf3YbPxeD98vNrtqqqZVVLXY/1ySaSIwzYnqeY9g==",
|
||||
"version": "13.1.4",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-13.1.4.tgz",
|
||||
"integrity": "sha512-+kAXIIVb7Q4LCKmi7dn9qVlG1XUf3Chgj5Rwl0rAP4WBV2TnJIgsOEC24G1Mm3jjif+qXm7SJS9YZ9Yg3Y8sSQ==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -263,9 +263,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@next/swc-win32-ia32-msvc": {
|
||||
"version": "13.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-13.1.2.tgz",
|
||||
"integrity": "sha512-3+nBkuFs/wT+lmRVQNH5SyDT7I4vUlNPntosEaEP63FuYQdPLaxz0GvcR66MdFSFh2fsvazpe4wciOwVS4FItQ==",
|
||||
"version": "13.1.4",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-13.1.4.tgz",
|
||||
"integrity": "sha512-EsfzAFBVaw1zg1FzlLMgRaTX/DKY+EnAvJ6mCIJMGeSOPIj4Oy6xF2yEQ3VaRkwFpAafHJH6JNB/CGrdKFCMXw==",
|
||||
"cpu": [
|
||||
"ia32"
|
||||
],
|
||||
@@ -278,9 +278,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@next/swc-win32-x64-msvc": {
|
||||
"version": "13.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-13.1.2.tgz",
|
||||
"integrity": "sha512-avsyveEvcvH42PvKjR4Pb8JlLttuGURr2H3ZhS2b85pHOiZ7yjH3rMUoGnNzuLMApyxYaCvd4MedPrLhnNhkog==",
|
||||
"version": "13.1.4",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-13.1.4.tgz",
|
||||
"integrity": "sha512-bygNjmnq+F9NqJXh7OfhJgqu6LGU29GNKQYVyZkxY/h5K0WWUvAE/VL+TdyMwbvQr9KByx5XLwORwetLxXCo4g==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -362,13 +362,13 @@
|
||||
"integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ=="
|
||||
},
|
||||
"node_modules/@typescript-eslint/parser": {
|
||||
"version": "5.48.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.48.1.tgz",
|
||||
"integrity": "sha512-4yg+FJR/V1M9Xoq56SF9Iygqm+r5LMXvheo6DQ7/yUWynQ4YfCRnsKuRgqH4EQ5Ya76rVwlEpw4Xu+TgWQUcdA==",
|
||||
"version": "5.48.2",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.48.2.tgz",
|
||||
"integrity": "sha512-38zMsKsG2sIuM5Oi/olurGwYJXzmtdsHhn5mI/pQogP+BjYVkK5iRazCQ8RGS0V+YLk282uWElN70zAAUmaYHw==",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/scope-manager": "5.48.1",
|
||||
"@typescript-eslint/types": "5.48.1",
|
||||
"@typescript-eslint/typescript-estree": "5.48.1",
|
||||
"@typescript-eslint/scope-manager": "5.48.2",
|
||||
"@typescript-eslint/types": "5.48.2",
|
||||
"@typescript-eslint/typescript-estree": "5.48.2",
|
||||
"debug": "^4.3.4"
|
||||
},
|
||||
"engines": {
|
||||
@@ -388,12 +388,12 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/scope-manager": {
|
||||
"version": "5.48.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.48.1.tgz",
|
||||
"integrity": "sha512-S035ueRrbxRMKvSTv9vJKIWgr86BD8s3RqoRZmsSh/s8HhIs90g6UlK8ZabUSjUZQkhVxt7nmZ63VJ9dcZhtDQ==",
|
||||
"version": "5.48.2",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.48.2.tgz",
|
||||
"integrity": "sha512-zEUFfonQid5KRDKoI3O+uP1GnrFd4tIHlvs+sTJXiWuypUWMuDaottkJuR612wQfOkjYbsaskSIURV9xo4f+Fw==",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/types": "5.48.1",
|
||||
"@typescript-eslint/visitor-keys": "5.48.1"
|
||||
"@typescript-eslint/types": "5.48.2",
|
||||
"@typescript-eslint/visitor-keys": "5.48.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
|
||||
@@ -404,9 +404,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/types": {
|
||||
"version": "5.48.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.48.1.tgz",
|
||||
"integrity": "sha512-xHyDLU6MSuEEdIlzrrAerCGS3T7AA/L8Hggd0RCYBi0w3JMvGYxlLlXHeg50JI9Tfg5MrtsfuNxbS/3zF1/ATg==",
|
||||
"version": "5.48.2",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.48.2.tgz",
|
||||
"integrity": "sha512-hE7dA77xxu7ByBc6KCzikgfRyBCTst6dZQpwaTy25iMYOnbNljDT4hjhrGEJJ0QoMjrfqrx+j1l1B9/LtKeuqA==",
|
||||
"engines": {
|
||||
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
|
||||
},
|
||||
@@ -416,12 +416,12 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/typescript-estree": {
|
||||
"version": "5.48.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.48.1.tgz",
|
||||
"integrity": "sha512-Hut+Osk5FYr+sgFh8J/FHjqX6HFcDzTlWLrFqGoK5kVUN3VBHF/QzZmAsIXCQ8T/W9nQNBTqalxi1P3LSqWnRA==",
|
||||
"version": "5.48.2",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.48.2.tgz",
|
||||
"integrity": "sha512-bibvD3z6ilnoVxUBFEgkO0k0aFvUc4Cttt0dAreEr+nrAHhWzkO83PEVVuieK3DqcgL6VAK5dkzK8XUVja5Zcg==",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/types": "5.48.1",
|
||||
"@typescript-eslint/visitor-keys": "5.48.1",
|
||||
"@typescript-eslint/types": "5.48.2",
|
||||
"@typescript-eslint/visitor-keys": "5.48.2",
|
||||
"debug": "^4.3.4",
|
||||
"globby": "^11.1.0",
|
||||
"is-glob": "^4.0.3",
|
||||
@@ -442,11 +442,11 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/visitor-keys": {
|
||||
"version": "5.48.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.48.1.tgz",
|
||||
"integrity": "sha512-Ns0XBwmfuX7ZknznfXozgnydyR8F6ev/KEGePP4i74uL3ArsKbEhJ7raeKr1JSa997DBDwol/4a0Y+At82c9dA==",
|
||||
"version": "5.48.2",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.48.2.tgz",
|
||||
"integrity": "sha512-z9njZLSkwmjFWUelGEwEbdf4NwKvfHxvGC0OcGN1Hp/XNDIcJ7D5DpPNPv6x6/mFvc1tQHsaWmpD/a4gOvvCJQ==",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/types": "5.48.1",
|
||||
"@typescript-eslint/types": "5.48.2",
|
||||
"eslint-visitor-keys": "^3.3.0"
|
||||
},
|
||||
"engines": {
|
||||
@@ -676,9 +676,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/caniuse-lite": {
|
||||
"version": "1.0.30001443",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001443.tgz",
|
||||
"integrity": "sha512-jUo8svymO8+Mkj3qbUbVjR8zv8LUGpGkUM/jKvc9SO2BvjCI980dp9fQbf/dyLs6RascPzgR4nhAKFA4OHeSaA==",
|
||||
"version": "1.0.30001446",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001446.tgz",
|
||||
"integrity": "sha512-fEoga4PrImGcwUUGEol/PoFCSBnSkA9drgdkxXkJLsUBOnJ8rs3zDv6ApqYXGQFOyMPsjh79naWhF4DAxbF8rw==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "opencollective",
|
||||
@@ -906,18 +906,19 @@
|
||||
}
|
||||
},
|
||||
"node_modules/es-get-iterator": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/es-get-iterator/-/es-get-iterator-1.1.2.tgz",
|
||||
"integrity": "sha512-+DTO8GYwbMCwbywjimwZMHp8AuYXOS2JZFWoi2AlPOS3ebnII9w/NLpNZtA7A0YLaVDw+O7KFCeoIV7OPvM7hQ==",
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/es-get-iterator/-/es-get-iterator-1.1.3.tgz",
|
||||
"integrity": "sha512-sPZmqHBe6JIiTfN5q2pEi//TwxmAFHwj/XEuYjTuse78i8KxaqMTTzxPoFKuzRpDpTJ+0NAbpfenkmH2rePtuw==",
|
||||
"dependencies": {
|
||||
"call-bind": "^1.0.2",
|
||||
"get-intrinsic": "^1.1.0",
|
||||
"has-symbols": "^1.0.1",
|
||||
"is-arguments": "^1.1.0",
|
||||
"get-intrinsic": "^1.1.3",
|
||||
"has-symbols": "^1.0.3",
|
||||
"is-arguments": "^1.1.1",
|
||||
"is-map": "^2.0.2",
|
||||
"is-set": "^2.0.2",
|
||||
"is-string": "^1.0.5",
|
||||
"isarray": "^2.0.5"
|
||||
"is-string": "^1.0.7",
|
||||
"isarray": "^2.0.5",
|
||||
"stop-iteration-iterator": "^1.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
@@ -972,9 +973,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/eslint": {
|
||||
"version": "8.31.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint/-/eslint-8.31.0.tgz",
|
||||
"integrity": "sha512-0tQQEVdmPZ1UtUKXjX7EMm9BlgJ08G90IhWh0PKDCb3ZLsgAOHI8fYSIzYVZej92zsgq+ft0FGsxhJ3xo2tbuA==",
|
||||
"version": "8.32.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint/-/eslint-8.32.0.tgz",
|
||||
"integrity": "sha512-nETVXpnthqKPFyuY2FNjz/bEd6nbosRgKbkgS/y1C7LJop96gYHWpiguLecMHQ2XCPxn77DS0P+68WzG6vkZSQ==",
|
||||
"dependencies": {
|
||||
"@eslint/eslintrc": "^1.4.1",
|
||||
"@humanwhocodes/config-array": "^0.11.8",
|
||||
@@ -1027,11 +1028,11 @@
|
||||
}
|
||||
},
|
||||
"node_modules/eslint-config-next": {
|
||||
"version": "13.1.2",
|
||||
"resolved": "https://registry.npmjs.org/eslint-config-next/-/eslint-config-next-13.1.2.tgz",
|
||||
"integrity": "sha512-zdRAQOr8v69ZwJRtBrGqAqm160ONqKxU/pV1FB1KlgfyqveGsLZmlQ7l31otwtw763901J7xdiTVkj2y3YxXZA==",
|
||||
"version": "13.1.4",
|
||||
"resolved": "https://registry.npmjs.org/eslint-config-next/-/eslint-config-next-13.1.4.tgz",
|
||||
"integrity": "sha512-r7n9V4/kkiDDVFfBwI3tviGUV/jUzGI0lY3JefxceYaU18gdk2kMgNPyhHobowu1+yHZpZi8iEzRtzeTrtGRLg==",
|
||||
"dependencies": {
|
||||
"@next/eslint-plugin-next": "13.1.2",
|
||||
"@next/eslint-plugin-next": "13.1.4",
|
||||
"@rushstack/eslint-patch": "^1.1.3",
|
||||
"@typescript-eslint/parser": "^5.42.0",
|
||||
"eslint-import-resolver-node": "^0.3.6",
|
||||
@@ -1147,13 +1148,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/eslint-plugin-import": {
|
||||
"version": "2.27.4",
|
||||
"resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.27.4.tgz",
|
||||
"integrity": "sha512-Z1jVt1EGKia1X9CnBCkpAOhWy8FgQ7OmJ/IblEkT82yrFU/xJaxwujaTzLWqigewwynRQ9mmHfX9MtAfhxm0sA==",
|
||||
"version": "2.27.5",
|
||||
"resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.27.5.tgz",
|
||||
"integrity": "sha512-LmEt3GVofgiGuiE+ORpnvP+kAm3h6MLZJ4Q5HCyHADofsb4VzXFsRiWj3c0OFiV+3DWFh0qg3v9gcPlfc3zRow==",
|
||||
"dependencies": {
|
||||
"array-includes": "^3.1.6",
|
||||
"array.prototype.flat": "^1.3.1",
|
||||
"array.prototype.flatmap": "^1.3.0",
|
||||
"array.prototype.flatmap": "^1.3.1",
|
||||
"debug": "^3.2.7",
|
||||
"doctrine": "^2.1.0",
|
||||
"eslint-import-resolver-node": "^0.3.7",
|
||||
@@ -1239,9 +1240,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/eslint-plugin-react": {
|
||||
"version": "7.32.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.32.0.tgz",
|
||||
"integrity": "sha512-vSBi1+SrPiLZCGvxpiZIa28fMEUaMjXtCplrvxcIxGzmFiYdsXQDwInEjuv5/i/2CTTxbkS87tE8lsQ0Qxinbw==",
|
||||
"version": "7.32.1",
|
||||
"resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.32.1.tgz",
|
||||
"integrity": "sha512-vOjdgyd0ZHBXNsmvU+785xY8Bfe57EFbTYYk8XrROzWpr9QBvpjITvAXt9xqcE6+8cjR/g1+mfumPToxsl1www==",
|
||||
"dependencies": {
|
||||
"array-includes": "^3.1.6",
|
||||
"array.prototype.flatmap": "^1.3.1",
|
||||
@@ -1558,9 +1559,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/get-intrinsic": {
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.3.tgz",
|
||||
"integrity": "sha512-QJVz1Tj7MS099PevUG5jvnt9tSkXN8K14dxQlikJuPt4uD9hHAHjLyLBiLR5zELelBdD9QNRAXZzsJx0WaDL9A==",
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.0.tgz",
|
||||
"integrity": "sha512-L049y6nFOuom5wGyRc3/gdTLO94dySVKRACj1RmJZBQXlbTMhtNIgkWkUHq+jYmZvKf14EW1EoJnnjbmoHij0Q==",
|
||||
"dependencies": {
|
||||
"function-bind": "^1.1.1",
|
||||
"has": "^1.0.3",
|
||||
@@ -2138,9 +2139,9 @@
|
||||
"integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="
|
||||
},
|
||||
"node_modules/js-sdsl": {
|
||||
"version": "4.2.0",
|
||||
"resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.2.0.tgz",
|
||||
"integrity": "sha512-dyBIzQBDkCqCu+0upx25Y2jGdbTGxE9fshMsCdK0ViOongpV+n5tXRcZY9v7CaVQ79AGS9KA1KHtojxiM7aXSQ==",
|
||||
"version": "4.3.0",
|
||||
"resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.3.0.tgz",
|
||||
"integrity": "sha512-mifzlm2+5nZ+lEcLJMoBK0/IH/bDg8XnJfd/Wq6IP+xoCjLZsTOnV2QpxlVbX9bMnkl5PdEjNtBJ9Cj1NjifhQ==",
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/js-sdsl"
|
||||
@@ -2322,11 +2323,11 @@
|
||||
"integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw=="
|
||||
},
|
||||
"node_modules/next": {
|
||||
"version": "13.1.2",
|
||||
"resolved": "https://registry.npmjs.org/next/-/next-13.1.2.tgz",
|
||||
"integrity": "sha512-Rdnnb2YH///w78FEOR/IQ6TXga+qpth4OqFSem48ng1PYYKr6XBsIk1XVaRcIGM3o6iiHnun0nJvkJHDf+ICyQ==",
|
||||
"version": "13.1.4",
|
||||
"resolved": "https://registry.npmjs.org/next/-/next-13.1.4.tgz",
|
||||
"integrity": "sha512-g0oBUU+tcOPKbXTVdsDO2adc6wd/ggqauHHysPQJxuIKqZ+fwICGJht0C5D5V0A/77eQDF5EFwNdAHkFvBDsog==",
|
||||
"dependencies": {
|
||||
"@next/env": "13.1.2",
|
||||
"@next/env": "13.1.4",
|
||||
"@swc/helpers": "0.4.14",
|
||||
"caniuse-lite": "^1.0.30001406",
|
||||
"postcss": "8.4.14",
|
||||
@@ -2339,19 +2340,19 @@
|
||||
"node": ">=14.6.0"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@next/swc-android-arm-eabi": "13.1.2",
|
||||
"@next/swc-android-arm64": "13.1.2",
|
||||
"@next/swc-darwin-arm64": "13.1.2",
|
||||
"@next/swc-darwin-x64": "13.1.2",
|
||||
"@next/swc-freebsd-x64": "13.1.2",
|
||||
"@next/swc-linux-arm-gnueabihf": "13.1.2",
|
||||
"@next/swc-linux-arm64-gnu": "13.1.2",
|
||||
"@next/swc-linux-arm64-musl": "13.1.2",
|
||||
"@next/swc-linux-x64-gnu": "13.1.2",
|
||||
"@next/swc-linux-x64-musl": "13.1.2",
|
||||
"@next/swc-win32-arm64-msvc": "13.1.2",
|
||||
"@next/swc-win32-ia32-msvc": "13.1.2",
|
||||
"@next/swc-win32-x64-msvc": "13.1.2"
|
||||
"@next/swc-android-arm-eabi": "13.1.4",
|
||||
"@next/swc-android-arm64": "13.1.4",
|
||||
"@next/swc-darwin-arm64": "13.1.4",
|
||||
"@next/swc-darwin-x64": "13.1.4",
|
||||
"@next/swc-freebsd-x64": "13.1.4",
|
||||
"@next/swc-linux-arm-gnueabihf": "13.1.4",
|
||||
"@next/swc-linux-arm64-gnu": "13.1.4",
|
||||
"@next/swc-linux-arm64-musl": "13.1.4",
|
||||
"@next/swc-linux-x64-gnu": "13.1.4",
|
||||
"@next/swc-linux-x64-musl": "13.1.4",
|
||||
"@next/swc-win32-arm64-msvc": "13.1.4",
|
||||
"@next/swc-win32-ia32-msvc": "13.1.4",
|
||||
"@next/swc-win32-x64-msvc": "13.1.4"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"fibers": ">= 3.1.0",
|
||||
@@ -2659,9 +2660,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/punycode": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.2.0.tgz",
|
||||
"integrity": "sha512-LN6QV1IJ9ZhxWTNdktaPClrNfp8xdSAYS0Zk2ddX7XsXZAxckMHPCBcHRo0cTcEIgYPRiGEkmji3Idkh2yFtYw==",
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz",
|
||||
"integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==",
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
@@ -2897,6 +2898,17 @@
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/stop-iteration-iterator": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.0.0.tgz",
|
||||
"integrity": "sha512-iCGQj+0l0HOdZ2AEeBADlsRC+vsnDsZsbdSiH1yNSjcfKM7fdpCMfqAL/dwF5BLiw/XhRft/Wax6zQbhq2BcjQ==",
|
||||
"dependencies": {
|
||||
"internal-slot": "^1.0.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/string.prototype.matchall": {
|
||||
"version": "4.0.8",
|
||||
"resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.8.tgz",
|
||||
@@ -3306,99 +3318,99 @@
|
||||
"integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA=="
|
||||
},
|
||||
"@next/env": {
|
||||
"version": "13.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@next/env/-/env-13.1.2.tgz",
|
||||
"integrity": "sha512-PpT4UZIX66VMTqXt4HKEJ+/PwbS+tWmmhZlazaws1a+dbUA5pPdjntQ46Jvj616i3ZKN9doS9LHx3y50RLjAWg=="
|
||||
"version": "13.1.4",
|
||||
"resolved": "https://registry.npmjs.org/@next/env/-/env-13.1.4.tgz",
|
||||
"integrity": "sha512-x7ydhMpi9/xX7yVK+Fw33OuwwQWVZUFRxenK3z89fmPzQZyUk35Ynb+b7JkrhfRhDIFFvvqpzVSXeseSlBAw7A=="
|
||||
},
|
||||
"@next/eslint-plugin-next": {
|
||||
"version": "13.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-13.1.2.tgz",
|
||||
"integrity": "sha512-WGaNVvIYphdriesP6r7jq/8l7u38tzotnVQuxc1RYKLqYYApSsrebti3OCPoT3Gx0pw2smPIFHH98RzcsgW5GQ==",
|
||||
"version": "13.1.4",
|
||||
"resolved": "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-13.1.4.tgz",
|
||||
"integrity": "sha512-a/T30+7Q1scom5t3L+wEBkYzCa+bhT/3DTxzxlNy4Xckw2InzcckQGeIi/larDgh5r2fSSJswhYAZEcKtuJiig==",
|
||||
"requires": {
|
||||
"glob": "7.1.7"
|
||||
}
|
||||
},
|
||||
"@next/font": {
|
||||
"version": "13.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@next/font/-/font-13.1.2.tgz",
|
||||
"integrity": "sha512-NXGXGFGiOKEnvBIHq9cdFTKbHO2/4B3Zd9K27M7j1DioIQVar7oVRqZMYs0h3XMVEZLwjjkdAtqRPCzzd3RtXg=="
|
||||
"version": "13.1.4",
|
||||
"resolved": "https://registry.npmjs.org/@next/font/-/font-13.1.4.tgz",
|
||||
"integrity": "sha512-NfqQPAIFJhssdaZEsP0MADZKN+tc040jOkNKVlDZHtVjJWBJSgbe8UHg3w0S6YuQr7PHc0ACbFeVCBQeG4MEvg=="
|
||||
},
|
||||
"@next/swc-android-arm-eabi": {
|
||||
"version": "13.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-android-arm-eabi/-/swc-android-arm-eabi-13.1.2.tgz",
|
||||
"integrity": "sha512-7mRz1owoGsbfIcdOJA3kk7KEwPZ+OvVT1z9DkR/yru4QdVLF69h/1SHy0vlUNQMxDRllabhxCfkoZCB34GOGAg==",
|
||||
"version": "13.1.4",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-android-arm-eabi/-/swc-android-arm-eabi-13.1.4.tgz",
|
||||
"integrity": "sha512-5PAchzFst3In6Ml+9APvBj89H29lcPXcUqEYBVv09fWK/V4IuViKc2qOqM9pyPyw7KsqaZPmuqaG595E6jdZLA==",
|
||||
"optional": true
|
||||
},
|
||||
"@next/swc-android-arm64": {
|
||||
"version": "13.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-android-arm64/-/swc-android-arm64-13.1.2.tgz",
|
||||
"integrity": "sha512-mgjZ2eJSayovQm1LcE54BLSI4jjnnnLtq5GY5g+DdPuUiCT644gKtjZ/w2BQvuIecCqqBO+Ph9yzo/wUTq7NLg==",
|
||||
"version": "13.1.4",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-android-arm64/-/swc-android-arm64-13.1.4.tgz",
|
||||
"integrity": "sha512-LCLjjRhsQ5fR9ExzR2fqxuyJe/D4Ct/YkdonVfJfqOfkEpFwUTQDOVo5GrQec4LZDk3zY+o6vZYjXbB0nD9VLA==",
|
||||
"optional": true
|
||||
},
|
||||
"@next/swc-darwin-arm64": {
|
||||
"version": "13.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-13.1.2.tgz",
|
||||
"integrity": "sha512-RikoQqy109r2222UJlyGs4dZw2BibkfPqpeFdW5JEGv+L2PStlHID8DwyVYbmHfQ0VIBGvbf/NAUtFakAWlhwg==",
|
||||
"version": "13.1.4",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-13.1.4.tgz",
|
||||
"integrity": "sha512-LSc/tF1FQ1y1SwKiCdGg8IIl7+Csk6nuLcLIyQXs24UNYjXg5+7vUQXqE8y66v/Dq8qFDC9rM61QhpM9ZDftbg==",
|
||||
"optional": true
|
||||
},
|
||||
"@next/swc-darwin-x64": {
|
||||
"version": "13.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-13.1.2.tgz",
|
||||
"integrity": "sha512-JbDZjaTvL8gyPC5TAH6OnD4jmXPkyUxRYPvu08ZmhT/XAFBb/Cso0BdXyDax/BPCG70mimP9d3hXNKNq+A0VtQ==",
|
||||
"version": "13.1.4",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-13.1.4.tgz",
|
||||
"integrity": "sha512-WoApDo8xfafrNc9+Mz5MwGFKUwbDHsGqLleTGZ8upegwVqDyHsYzqJQudf+loqhV58oGTOqP1eWaHn2J7dijXA==",
|
||||
"optional": true
|
||||
},
|
||||
"@next/swc-freebsd-x64": {
|
||||
"version": "13.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-freebsd-x64/-/swc-freebsd-x64-13.1.2.tgz",
|
||||
"integrity": "sha512-ax4j8VrdFQ/xc3W7Om0u1vnDxVApQHKsChBbAMynCrnycZmpbqK4MZu4ZkycT+mx2eccCiqZROpbzDbEdPosEw==",
|
||||
"version": "13.1.4",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-freebsd-x64/-/swc-freebsd-x64-13.1.4.tgz",
|
||||
"integrity": "sha512-fqNyeT8G4guN8AHPIoBRhGY2GJg89FyWpuwX4o0Y3vUy/84IGZpNst3paCzaYkQSqQE/AuCpkB7hKxkN7ittXw==",
|
||||
"optional": true
|
||||
},
|
||||
"@next/swc-linux-arm-gnueabihf": {
|
||||
"version": "13.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-linux-arm-gnueabihf/-/swc-linux-arm-gnueabihf-13.1.2.tgz",
|
||||
"integrity": "sha512-NcRHTesnCxnUvSJa637PQJffBBkmqi5XS/xVWGY7dI6nyJ+pC96Oj7kd+mcjnFUQI5lHKbg39qBWKtOzbezc4w==",
|
||||
"version": "13.1.4",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-linux-arm-gnueabihf/-/swc-linux-arm-gnueabihf-13.1.4.tgz",
|
||||
"integrity": "sha512-MEfm8OC1YR9/tYHUzlQsxcSmiuf8XdO7bqh5VtG4pilScjc5I5t+tQgIDgoDGePfh5W99W23hb3s6oCFrt99rw==",
|
||||
"optional": true
|
||||
},
|
||||
"@next/swc-linux-arm64-gnu": {
|
||||
"version": "13.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-13.1.2.tgz",
|
||||
"integrity": "sha512-AxJdjocLtPrsBY4P2COSBIc3crT5bpjgGenNuINoensOlXhBkYM0aRDYZdydwXOhG+kN2ngUvfgitop9pa204w==",
|
||||
"version": "13.1.4",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-13.1.4.tgz",
|
||||
"integrity": "sha512-2wgth/KsuODzW/E7jsRoWdhKmE5oZzXcBPvf9RW+ZpBNvYQkEDlzfLA7n8DtxTU8I4oMas0mdEPdCWXrSNnVZw==",
|
||||
"optional": true
|
||||
},
|
||||
"@next/swc-linux-arm64-musl": {
|
||||
"version": "13.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-13.1.2.tgz",
|
||||
"integrity": "sha512-JmNimDkcCRq7P5zpkdqeaSZ69qKDntEPtyIaMNWqy5M0WUJxGim0Fs6Qzxayiyvuuh9Guxks4woQ/j/ZvX/c8Q==",
|
||||
"version": "13.1.4",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-13.1.4.tgz",
|
||||
"integrity": "sha512-GdWhCRljsT7rNEElEsdu4RRppd+XaQOX1IJslsh/+HU6LsJGUE8tXpa68yJjCsHZHifkbdZNeCr5SYdsN6CbAA==",
|
||||
"optional": true
|
||||
},
|
||||
"@next/swc-linux-x64-gnu": {
|
||||
"version": "13.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-13.1.2.tgz",
|
||||
"integrity": "sha512-TsLsjZwUlgmvI42neTuIoD6K9RlXCUzqPtvIClgXxVO0um0DiZwK+M+0zX/uVXhMVphfPY2c5YeR1zFSIONY4A==",
|
||||
"version": "13.1.4",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-13.1.4.tgz",
|
||||
"integrity": "sha512-Rsk/ojwYqMskN2eo5hUSVe7UuMV/aSjmrmJ0BCFGFPfBY9sPgmYj/oXlDDN0y5lJD9acPuiBjknLWgnOnx5JIA==",
|
||||
"optional": true
|
||||
},
|
||||
"@next/swc-linux-x64-musl": {
|
||||
"version": "13.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-13.1.2.tgz",
|
||||
"integrity": "sha512-eSkyXgCXydEFPTkcncQOGepafedPte6JT/OofB9uvruucrrMVBagCASOuPxodWEMrlfEKSXVnExMKIlfmQMD7A==",
|
||||
"version": "13.1.4",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-13.1.4.tgz",
|
||||
"integrity": "sha512-gKSVPozedA2gpA+vggYnAqpDuzWFed2oxFeXxHw0aW2ALdAZswAinn1ZwXEQ5fHnVguxjZhH0+2nBxpMdF8p5Q==",
|
||||
"optional": true
|
||||
},
|
||||
"@next/swc-win32-arm64-msvc": {
|
||||
"version": "13.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-13.1.2.tgz",
|
||||
"integrity": "sha512-DmXFaRTgt2KrV9dmRLifDJE+cYiutHVFIw5/C9BtnwXH39uf3YbPxeD98vNrtqqqZVVLXY/1ySaSIwzYnqeY9g==",
|
||||
"version": "13.1.4",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-13.1.4.tgz",
|
||||
"integrity": "sha512-+kAXIIVb7Q4LCKmi7dn9qVlG1XUf3Chgj5Rwl0rAP4WBV2TnJIgsOEC24G1Mm3jjif+qXm7SJS9YZ9Yg3Y8sSQ==",
|
||||
"optional": true
|
||||
},
|
||||
"@next/swc-win32-ia32-msvc": {
|
||||
"version": "13.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-13.1.2.tgz",
|
||||
"integrity": "sha512-3+nBkuFs/wT+lmRVQNH5SyDT7I4vUlNPntosEaEP63FuYQdPLaxz0GvcR66MdFSFh2fsvazpe4wciOwVS4FItQ==",
|
||||
"version": "13.1.4",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-13.1.4.tgz",
|
||||
"integrity": "sha512-EsfzAFBVaw1zg1FzlLMgRaTX/DKY+EnAvJ6mCIJMGeSOPIj4Oy6xF2yEQ3VaRkwFpAafHJH6JNB/CGrdKFCMXw==",
|
||||
"optional": true
|
||||
},
|
||||
"@next/swc-win32-x64-msvc": {
|
||||
"version": "13.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-13.1.2.tgz",
|
||||
"integrity": "sha512-avsyveEvcvH42PvKjR4Pb8JlLttuGURr2H3ZhS2b85pHOiZ7yjH3rMUoGnNzuLMApyxYaCvd4MedPrLhnNhkog==",
|
||||
"version": "13.1.4",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-13.1.4.tgz",
|
||||
"integrity": "sha512-bygNjmnq+F9NqJXh7OfhJgqu6LGU29GNKQYVyZkxY/h5K0WWUvAE/VL+TdyMwbvQr9KByx5XLwORwetLxXCo4g==",
|
||||
"optional": true
|
||||
},
|
||||
"@nodelib/fs.scandir": {
|
||||
@@ -3456,37 +3468,37 @@
|
||||
"integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ=="
|
||||
},
|
||||
"@typescript-eslint/parser": {
|
||||
"version": "5.48.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.48.1.tgz",
|
||||
"integrity": "sha512-4yg+FJR/V1M9Xoq56SF9Iygqm+r5LMXvheo6DQ7/yUWynQ4YfCRnsKuRgqH4EQ5Ya76rVwlEpw4Xu+TgWQUcdA==",
|
||||
"version": "5.48.2",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.48.2.tgz",
|
||||
"integrity": "sha512-38zMsKsG2sIuM5Oi/olurGwYJXzmtdsHhn5mI/pQogP+BjYVkK5iRazCQ8RGS0V+YLk282uWElN70zAAUmaYHw==",
|
||||
"requires": {
|
||||
"@typescript-eslint/scope-manager": "5.48.1",
|
||||
"@typescript-eslint/types": "5.48.1",
|
||||
"@typescript-eslint/typescript-estree": "5.48.1",
|
||||
"@typescript-eslint/scope-manager": "5.48.2",
|
||||
"@typescript-eslint/types": "5.48.2",
|
||||
"@typescript-eslint/typescript-estree": "5.48.2",
|
||||
"debug": "^4.3.4"
|
||||
}
|
||||
},
|
||||
"@typescript-eslint/scope-manager": {
|
||||
"version": "5.48.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.48.1.tgz",
|
||||
"integrity": "sha512-S035ueRrbxRMKvSTv9vJKIWgr86BD8s3RqoRZmsSh/s8HhIs90g6UlK8ZabUSjUZQkhVxt7nmZ63VJ9dcZhtDQ==",
|
||||
"version": "5.48.2",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.48.2.tgz",
|
||||
"integrity": "sha512-zEUFfonQid5KRDKoI3O+uP1GnrFd4tIHlvs+sTJXiWuypUWMuDaottkJuR612wQfOkjYbsaskSIURV9xo4f+Fw==",
|
||||
"requires": {
|
||||
"@typescript-eslint/types": "5.48.1",
|
||||
"@typescript-eslint/visitor-keys": "5.48.1"
|
||||
"@typescript-eslint/types": "5.48.2",
|
||||
"@typescript-eslint/visitor-keys": "5.48.2"
|
||||
}
|
||||
},
|
||||
"@typescript-eslint/types": {
|
||||
"version": "5.48.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.48.1.tgz",
|
||||
"integrity": "sha512-xHyDLU6MSuEEdIlzrrAerCGS3T7AA/L8Hggd0RCYBi0w3JMvGYxlLlXHeg50JI9Tfg5MrtsfuNxbS/3zF1/ATg=="
|
||||
"version": "5.48.2",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.48.2.tgz",
|
||||
"integrity": "sha512-hE7dA77xxu7ByBc6KCzikgfRyBCTst6dZQpwaTy25iMYOnbNljDT4hjhrGEJJ0QoMjrfqrx+j1l1B9/LtKeuqA=="
|
||||
},
|
||||
"@typescript-eslint/typescript-estree": {
|
||||
"version": "5.48.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.48.1.tgz",
|
||||
"integrity": "sha512-Hut+Osk5FYr+sgFh8J/FHjqX6HFcDzTlWLrFqGoK5kVUN3VBHF/QzZmAsIXCQ8T/W9nQNBTqalxi1P3LSqWnRA==",
|
||||
"version": "5.48.2",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.48.2.tgz",
|
||||
"integrity": "sha512-bibvD3z6ilnoVxUBFEgkO0k0aFvUc4Cttt0dAreEr+nrAHhWzkO83PEVVuieK3DqcgL6VAK5dkzK8XUVja5Zcg==",
|
||||
"requires": {
|
||||
"@typescript-eslint/types": "5.48.1",
|
||||
"@typescript-eslint/visitor-keys": "5.48.1",
|
||||
"@typescript-eslint/types": "5.48.2",
|
||||
"@typescript-eslint/visitor-keys": "5.48.2",
|
||||
"debug": "^4.3.4",
|
||||
"globby": "^11.1.0",
|
||||
"is-glob": "^4.0.3",
|
||||
@@ -3495,11 +3507,11 @@
|
||||
}
|
||||
},
|
||||
"@typescript-eslint/visitor-keys": {
|
||||
"version": "5.48.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.48.1.tgz",
|
||||
"integrity": "sha512-Ns0XBwmfuX7ZknznfXozgnydyR8F6ev/KEGePP4i74uL3ArsKbEhJ7raeKr1JSa997DBDwol/4a0Y+At82c9dA==",
|
||||
"version": "5.48.2",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.48.2.tgz",
|
||||
"integrity": "sha512-z9njZLSkwmjFWUelGEwEbdf4NwKvfHxvGC0OcGN1Hp/XNDIcJ7D5DpPNPv6x6/mFvc1tQHsaWmpD/a4gOvvCJQ==",
|
||||
"requires": {
|
||||
"@typescript-eslint/types": "5.48.1",
|
||||
"@typescript-eslint/types": "5.48.2",
|
||||
"eslint-visitor-keys": "^3.3.0"
|
||||
}
|
||||
},
|
||||
@@ -3662,9 +3674,9 @@
|
||||
"integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ=="
|
||||
},
|
||||
"caniuse-lite": {
|
||||
"version": "1.0.30001443",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001443.tgz",
|
||||
"integrity": "sha512-jUo8svymO8+Mkj3qbUbVjR8zv8LUGpGkUM/jKvc9SO2BvjCI980dp9fQbf/dyLs6RascPzgR4nhAKFA4OHeSaA=="
|
||||
"version": "1.0.30001446",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001446.tgz",
|
||||
"integrity": "sha512-fEoga4PrImGcwUUGEol/PoFCSBnSkA9drgdkxXkJLsUBOnJ8rs3zDv6ApqYXGQFOyMPsjh79naWhF4DAxbF8rw=="
|
||||
},
|
||||
"chalk": {
|
||||
"version": "4.1.2",
|
||||
@@ -3835,18 +3847,19 @@
|
||||
}
|
||||
},
|
||||
"es-get-iterator": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/es-get-iterator/-/es-get-iterator-1.1.2.tgz",
|
||||
"integrity": "sha512-+DTO8GYwbMCwbywjimwZMHp8AuYXOS2JZFWoi2AlPOS3ebnII9w/NLpNZtA7A0YLaVDw+O7KFCeoIV7OPvM7hQ==",
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/es-get-iterator/-/es-get-iterator-1.1.3.tgz",
|
||||
"integrity": "sha512-sPZmqHBe6JIiTfN5q2pEi//TwxmAFHwj/XEuYjTuse78i8KxaqMTTzxPoFKuzRpDpTJ+0NAbpfenkmH2rePtuw==",
|
||||
"requires": {
|
||||
"call-bind": "^1.0.2",
|
||||
"get-intrinsic": "^1.1.0",
|
||||
"has-symbols": "^1.0.1",
|
||||
"is-arguments": "^1.1.0",
|
||||
"get-intrinsic": "^1.1.3",
|
||||
"has-symbols": "^1.0.3",
|
||||
"is-arguments": "^1.1.1",
|
||||
"is-map": "^2.0.2",
|
||||
"is-set": "^2.0.2",
|
||||
"is-string": "^1.0.5",
|
||||
"isarray": "^2.0.5"
|
||||
"is-string": "^1.0.7",
|
||||
"isarray": "^2.0.5",
|
||||
"stop-iteration-iterator": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"es-set-tostringtag": {
|
||||
@@ -3883,9 +3896,9 @@
|
||||
"integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA=="
|
||||
},
|
||||
"eslint": {
|
||||
"version": "8.31.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint/-/eslint-8.31.0.tgz",
|
||||
"integrity": "sha512-0tQQEVdmPZ1UtUKXjX7EMm9BlgJ08G90IhWh0PKDCb3ZLsgAOHI8fYSIzYVZej92zsgq+ft0FGsxhJ3xo2tbuA==",
|
||||
"version": "8.32.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint/-/eslint-8.32.0.tgz",
|
||||
"integrity": "sha512-nETVXpnthqKPFyuY2FNjz/bEd6nbosRgKbkgS/y1C7LJop96gYHWpiguLecMHQ2XCPxn77DS0P+68WzG6vkZSQ==",
|
||||
"requires": {
|
||||
"@eslint/eslintrc": "^1.4.1",
|
||||
"@humanwhocodes/config-array": "^0.11.8",
|
||||
@@ -3929,11 +3942,11 @@
|
||||
}
|
||||
},
|
||||
"eslint-config-next": {
|
||||
"version": "13.1.2",
|
||||
"resolved": "https://registry.npmjs.org/eslint-config-next/-/eslint-config-next-13.1.2.tgz",
|
||||
"integrity": "sha512-zdRAQOr8v69ZwJRtBrGqAqm160ONqKxU/pV1FB1KlgfyqveGsLZmlQ7l31otwtw763901J7xdiTVkj2y3YxXZA==",
|
||||
"version": "13.1.4",
|
||||
"resolved": "https://registry.npmjs.org/eslint-config-next/-/eslint-config-next-13.1.4.tgz",
|
||||
"integrity": "sha512-r7n9V4/kkiDDVFfBwI3tviGUV/jUzGI0lY3JefxceYaU18gdk2kMgNPyhHobowu1+yHZpZi8iEzRtzeTrtGRLg==",
|
||||
"requires": {
|
||||
"@next/eslint-plugin-next": "13.1.2",
|
||||
"@next/eslint-plugin-next": "13.1.4",
|
||||
"@rushstack/eslint-patch": "^1.1.3",
|
||||
"@typescript-eslint/parser": "^5.42.0",
|
||||
"eslint-import-resolver-node": "^0.3.6",
|
||||
@@ -4016,13 +4029,13 @@
|
||||
}
|
||||
},
|
||||
"eslint-plugin-import": {
|
||||
"version": "2.27.4",
|
||||
"resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.27.4.tgz",
|
||||
"integrity": "sha512-Z1jVt1EGKia1X9CnBCkpAOhWy8FgQ7OmJ/IblEkT82yrFU/xJaxwujaTzLWqigewwynRQ9mmHfX9MtAfhxm0sA==",
|
||||
"version": "2.27.5",
|
||||
"resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.27.5.tgz",
|
||||
"integrity": "sha512-LmEt3GVofgiGuiE+ORpnvP+kAm3h6MLZJ4Q5HCyHADofsb4VzXFsRiWj3c0OFiV+3DWFh0qg3v9gcPlfc3zRow==",
|
||||
"requires": {
|
||||
"array-includes": "^3.1.6",
|
||||
"array.prototype.flat": "^1.3.1",
|
||||
"array.prototype.flatmap": "^1.3.0",
|
||||
"array.prototype.flatmap": "^1.3.1",
|
||||
"debug": "^3.2.7",
|
||||
"doctrine": "^2.1.0",
|
||||
"eslint-import-resolver-node": "^0.3.7",
|
||||
@@ -4091,9 +4104,9 @@
|
||||
}
|
||||
},
|
||||
"eslint-plugin-react": {
|
||||
"version": "7.32.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.32.0.tgz",
|
||||
"integrity": "sha512-vSBi1+SrPiLZCGvxpiZIa28fMEUaMjXtCplrvxcIxGzmFiYdsXQDwInEjuv5/i/2CTTxbkS87tE8lsQ0Qxinbw==",
|
||||
"version": "7.32.1",
|
||||
"resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.32.1.tgz",
|
||||
"integrity": "sha512-vOjdgyd0ZHBXNsmvU+785xY8Bfe57EFbTYYk8XrROzWpr9QBvpjITvAXt9xqcE6+8cjR/g1+mfumPToxsl1www==",
|
||||
"requires": {
|
||||
"array-includes": "^3.1.6",
|
||||
"array.prototype.flatmap": "^1.3.1",
|
||||
@@ -4327,9 +4340,9 @@
|
||||
"integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ=="
|
||||
},
|
||||
"get-intrinsic": {
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.3.tgz",
|
||||
"integrity": "sha512-QJVz1Tj7MS099PevUG5jvnt9tSkXN8K14dxQlikJuPt4uD9hHAHjLyLBiLR5zELelBdD9QNRAXZzsJx0WaDL9A==",
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.0.tgz",
|
||||
"integrity": "sha512-L049y6nFOuom5wGyRc3/gdTLO94dySVKRACj1RmJZBQXlbTMhtNIgkWkUHq+jYmZvKf14EW1EoJnnjbmoHij0Q==",
|
||||
"requires": {
|
||||
"function-bind": "^1.1.1",
|
||||
"has": "^1.0.3",
|
||||
@@ -4709,9 +4722,9 @@
|
||||
"integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="
|
||||
},
|
||||
"js-sdsl": {
|
||||
"version": "4.2.0",
|
||||
"resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.2.0.tgz",
|
||||
"integrity": "sha512-dyBIzQBDkCqCu+0upx25Y2jGdbTGxE9fshMsCdK0ViOongpV+n5tXRcZY9v7CaVQ79AGS9KA1KHtojxiM7aXSQ=="
|
||||
"version": "4.3.0",
|
||||
"resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.3.0.tgz",
|
||||
"integrity": "sha512-mifzlm2+5nZ+lEcLJMoBK0/IH/bDg8XnJfd/Wq6IP+xoCjLZsTOnV2QpxlVbX9bMnkl5PdEjNtBJ9Cj1NjifhQ=="
|
||||
},
|
||||
"js-tokens": {
|
||||
"version": "4.0.0",
|
||||
@@ -4847,24 +4860,24 @@
|
||||
"integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw=="
|
||||
},
|
||||
"next": {
|
||||
"version": "13.1.2",
|
||||
"resolved": "https://registry.npmjs.org/next/-/next-13.1.2.tgz",
|
||||
"integrity": "sha512-Rdnnb2YH///w78FEOR/IQ6TXga+qpth4OqFSem48ng1PYYKr6XBsIk1XVaRcIGM3o6iiHnun0nJvkJHDf+ICyQ==",
|
||||
"version": "13.1.4",
|
||||
"resolved": "https://registry.npmjs.org/next/-/next-13.1.4.tgz",
|
||||
"integrity": "sha512-g0oBUU+tcOPKbXTVdsDO2adc6wd/ggqauHHysPQJxuIKqZ+fwICGJht0C5D5V0A/77eQDF5EFwNdAHkFvBDsog==",
|
||||
"requires": {
|
||||
"@next/env": "13.1.2",
|
||||
"@next/swc-android-arm-eabi": "13.1.2",
|
||||
"@next/swc-android-arm64": "13.1.2",
|
||||
"@next/swc-darwin-arm64": "13.1.2",
|
||||
"@next/swc-darwin-x64": "13.1.2",
|
||||
"@next/swc-freebsd-x64": "13.1.2",
|
||||
"@next/swc-linux-arm-gnueabihf": "13.1.2",
|
||||
"@next/swc-linux-arm64-gnu": "13.1.2",
|
||||
"@next/swc-linux-arm64-musl": "13.1.2",
|
||||
"@next/swc-linux-x64-gnu": "13.1.2",
|
||||
"@next/swc-linux-x64-musl": "13.1.2",
|
||||
"@next/swc-win32-arm64-msvc": "13.1.2",
|
||||
"@next/swc-win32-ia32-msvc": "13.1.2",
|
||||
"@next/swc-win32-x64-msvc": "13.1.2",
|
||||
"@next/env": "13.1.4",
|
||||
"@next/swc-android-arm-eabi": "13.1.4",
|
||||
"@next/swc-android-arm64": "13.1.4",
|
||||
"@next/swc-darwin-arm64": "13.1.4",
|
||||
"@next/swc-darwin-x64": "13.1.4",
|
||||
"@next/swc-freebsd-x64": "13.1.4",
|
||||
"@next/swc-linux-arm-gnueabihf": "13.1.4",
|
||||
"@next/swc-linux-arm64-gnu": "13.1.4",
|
||||
"@next/swc-linux-arm64-musl": "13.1.4",
|
||||
"@next/swc-linux-x64-gnu": "13.1.4",
|
||||
"@next/swc-linux-x64-musl": "13.1.4",
|
||||
"@next/swc-win32-arm64-msvc": "13.1.4",
|
||||
"@next/swc-win32-ia32-msvc": "13.1.4",
|
||||
"@next/swc-win32-x64-msvc": "13.1.4",
|
||||
"@swc/helpers": "0.4.14",
|
||||
"caniuse-lite": "^1.0.30001406",
|
||||
"postcss": "8.4.14",
|
||||
@@ -5061,9 +5074,9 @@
|
||||
}
|
||||
},
|
||||
"punycode": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.2.0.tgz",
|
||||
"integrity": "sha512-LN6QV1IJ9ZhxWTNdktaPClrNfp8xdSAYS0Zk2ddX7XsXZAxckMHPCBcHRo0cTcEIgYPRiGEkmji3Idkh2yFtYw=="
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz",
|
||||
"integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA=="
|
||||
},
|
||||
"queue-microtask": {
|
||||
"version": "1.2.3",
|
||||
@@ -5207,6 +5220,14 @@
|
||||
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz",
|
||||
"integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw=="
|
||||
},
|
||||
"stop-iteration-iterator": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.0.0.tgz",
|
||||
"integrity": "sha512-iCGQj+0l0HOdZ2AEeBADlsRC+vsnDsZsbdSiH1yNSjcfKM7fdpCMfqAL/dwF5BLiw/XhRft/Wax6zQbhq2BcjQ==",
|
||||
"requires": {
|
||||
"internal-slot": "^1.0.4"
|
||||
}
|
||||
},
|
||||
"string.prototype.matchall": {
|
||||
"version": "4.0.8",
|
||||
"resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.8.tgz",
|
||||
|
||||
@@ -9,10 +9,10 @@
|
||||
"lint": "next lint"
|
||||
},
|
||||
"dependencies": {
|
||||
"@next/font": "13.1.2",
|
||||
"eslint": "8.31.0",
|
||||
"eslint-config-next": "13.1.2",
|
||||
"next": "13.1.2",
|
||||
"@next/font": "13.1.4",
|
||||
"eslint": "8.32.0",
|
||||
"eslint-config-next": "13.1.4",
|
||||
"next": "13.1.4",
|
||||
"react": "18.2.0",
|
||||
"react-dom": "18.2.0"
|
||||
}
|
||||
|
||||
@@ -11,9 +11,8 @@
|
||||
"@types/node": "14.18.33",
|
||||
"@typescript-eslint/eslint-plugin": "5.21.0",
|
||||
"@typescript-eslint/parser": "5.21.0",
|
||||
"@vercel/build-utils": "workspace:5.7.5",
|
||||
"@vercel/build-utils": "*",
|
||||
"@vercel/ncc": "0.24.0",
|
||||
"@vercel/next": "workspace:3.3.9",
|
||||
"async-retry": "1.2.3",
|
||||
"buffer-replace": "1.0.0",
|
||||
"create-svelte": "2.0.1",
|
||||
@@ -37,6 +36,7 @@
|
||||
},
|
||||
"scripts": {
|
||||
"lerna": "lerna",
|
||||
"version": "pnpm install && git add pnpm-lock.yaml",
|
||||
"bootstrap": "lerna bootstrap",
|
||||
"publish-stable": "echo 'Run `pnpm changelog` for instructions'",
|
||||
"publish-canary": "git checkout main && git pull && lerna version prerelease --preid canary --message \"Publish Canary\" --exact",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vercel/build-utils",
|
||||
"version": "5.7.6",
|
||||
"version": "5.9.0",
|
||||
"license": "MIT",
|
||||
"main": "./dist/index.js",
|
||||
"types": "./dist/index.d.js",
|
||||
|
||||
@@ -2,7 +2,7 @@ import path from 'path';
|
||||
import debug from '../debug';
|
||||
import FileFsRef from '../file-fs-ref';
|
||||
import { File, Files, Meta } from '../types';
|
||||
import { remove, mkdirp, readlink, symlink } from 'fs-extra';
|
||||
import { remove, mkdirp, readlink, symlink, chmod } from 'fs-extra';
|
||||
import streamToBuffer from './stream-to-buffer';
|
||||
|
||||
export interface DownloadedFiles {
|
||||
@@ -51,6 +51,12 @@ export async function downloadFile(
|
||||
): Promise<FileFsRef> {
|
||||
const { mode } = file;
|
||||
|
||||
if (isDirectory(mode)) {
|
||||
await mkdirp(fsPath);
|
||||
await chmod(fsPath, mode);
|
||||
return FileFsRef.fromFsPath({ mode, fsPath });
|
||||
}
|
||||
|
||||
// If the source is a symlink, try to create it instead of copying the file.
|
||||
// Note: creating symlinks on Windows requires admin priviliges or symlinks
|
||||
// enabled in the group policy. We may want to improve the error message.
|
||||
|
||||
@@ -6,7 +6,9 @@ import { lstat, Stats } from 'fs-extra';
|
||||
import { normalizePath } from './normalize-path';
|
||||
import FileFsRef from '../file-fs-ref';
|
||||
|
||||
export type GlobOptions = vanillaGlob_.IOptions;
|
||||
export interface GlobOptions extends vanillaGlob_.IOptions {
|
||||
includeDirectories?: boolean;
|
||||
}
|
||||
|
||||
const vanillaGlob = promisify(vanillaGlob_);
|
||||
|
||||
@@ -15,12 +17,7 @@ export default async function glob(
|
||||
opts: GlobOptions | string,
|
||||
mountpoint?: string
|
||||
): Promise<Record<string, FileFsRef>> {
|
||||
let options: GlobOptions;
|
||||
if (typeof opts === 'string') {
|
||||
options = { cwd: opts };
|
||||
} else {
|
||||
options = opts;
|
||||
}
|
||||
const options = typeof opts === 'string' ? { cwd: opts } : opts;
|
||||
|
||||
if (!options.cwd) {
|
||||
throw new Error(
|
||||
@@ -34,13 +31,18 @@ export default async function glob(
|
||||
|
||||
const results: Record<string, FileFsRef> = {};
|
||||
const statCache: Record<string, Stats> = {};
|
||||
const symlinks: Record<string, boolean | undefined> = {};
|
||||
|
||||
options.symlinks = {};
|
||||
options.statCache = statCache;
|
||||
options.stat = true;
|
||||
options.dot = true;
|
||||
const files = await vanillaGlob(pattern, {
|
||||
...options,
|
||||
symlinks,
|
||||
statCache,
|
||||
stat: true,
|
||||
dot: true,
|
||||
});
|
||||
|
||||
const files = await vanillaGlob(pattern, options);
|
||||
const dirs = new Set<string>();
|
||||
const dirsWithEntries = new Set<string>();
|
||||
|
||||
for (const relativePath of files) {
|
||||
const fsPath = normalizePath(path.join(options.cwd, relativePath));
|
||||
@@ -49,12 +51,20 @@ export default async function glob(
|
||||
stat,
|
||||
`statCache does not contain value for ${relativePath} (resolved to ${fsPath})`
|
||||
);
|
||||
const isSymlink = options.symlinks![fsPath];
|
||||
if (isSymlink || stat.isFile()) {
|
||||
const isSymlink = symlinks[fsPath];
|
||||
if (isSymlink || stat.isFile() || stat.isDirectory()) {
|
||||
if (isSymlink) {
|
||||
stat = await lstat(fsPath);
|
||||
}
|
||||
|
||||
// Some bookkeeping to track which directories already have entries within
|
||||
const dirname = path.dirname(relativePath);
|
||||
dirsWithEntries.add(dirname);
|
||||
if (stat.isDirectory()) {
|
||||
dirs.add(relativePath);
|
||||
continue;
|
||||
}
|
||||
|
||||
let finalPath = relativePath;
|
||||
if (mountpoint) {
|
||||
finalPath = path.join(mountpoint, finalPath);
|
||||
@@ -64,5 +74,22 @@ export default async function glob(
|
||||
}
|
||||
}
|
||||
|
||||
// Add empty directory entries
|
||||
if (options.includeDirectories) {
|
||||
for (const relativePath of dirs) {
|
||||
if (dirsWithEntries.has(relativePath)) continue;
|
||||
|
||||
let finalPath = relativePath;
|
||||
if (mountpoint) {
|
||||
finalPath = path.join(mountpoint, finalPath);
|
||||
}
|
||||
|
||||
const fsPath = normalizePath(path.join(options.cwd, relativePath));
|
||||
const stat = statCache[fsPath];
|
||||
|
||||
results[finalPath] = new FileFsRef({ mode: stat.mode, fsPath });
|
||||
}
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ import download, {
|
||||
downloadFile,
|
||||
DownloadedFiles,
|
||||
isSymbolicLink,
|
||||
isDirectory,
|
||||
} from './fs/download';
|
||||
import getWriteableDirectory from './fs/get-writable-directory';
|
||||
import glob, { GlobOptions } from './fs/glob';
|
||||
@@ -82,6 +83,7 @@ export {
|
||||
streamToBuffer,
|
||||
debug,
|
||||
isSymbolicLink,
|
||||
isDirectory,
|
||||
getLambdaOptionsFromFunction,
|
||||
scanParentDirs,
|
||||
getIgnoreFilter,
|
||||
|
||||
195
packages/build-utils/test/unit.download.test.ts
vendored
Normal file
195
packages/build-utils/test/unit.download.test.ts
vendored
Normal file
@@ -0,0 +1,195 @@
|
||||
import path from 'path';
|
||||
import fs, { readlink } from 'fs-extra';
|
||||
import { strict as assert, strictEqual } from 'assert';
|
||||
import { download, glob, FileBlob } from '../src';
|
||||
|
||||
describe('download()', () => {
|
||||
let warningMessages: string[];
|
||||
const originalConsoleWarn = console.warn;
|
||||
beforeEach(() => {
|
||||
warningMessages = [];
|
||||
console.warn = m => {
|
||||
warningMessages.push(m);
|
||||
};
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
console.warn = originalConsoleWarn;
|
||||
});
|
||||
|
||||
it('should re-create FileFsRef symlinks properly', async () => {
|
||||
if (process.platform === 'win32') {
|
||||
console.log('Skipping test on windows');
|
||||
return;
|
||||
}
|
||||
const files = await glob('**', path.join(__dirname, 'symlinks'));
|
||||
assert.equal(Object.keys(files).length, 4);
|
||||
|
||||
const outDir = path.join(__dirname, 'symlinks-out');
|
||||
await fs.remove(outDir);
|
||||
|
||||
const files2 = await download(files, outDir);
|
||||
assert.equal(Object.keys(files2).length, 4);
|
||||
|
||||
const [linkStat, linkDirStat, aStat] = await Promise.all([
|
||||
fs.lstat(path.join(outDir, 'link.txt')),
|
||||
fs.lstat(path.join(outDir, 'link-dir')),
|
||||
fs.lstat(path.join(outDir, 'a.txt')),
|
||||
]);
|
||||
assert(linkStat.isSymbolicLink());
|
||||
assert(linkDirStat.isSymbolicLink());
|
||||
assert(aStat.isFile());
|
||||
|
||||
const [linkDirContents, linkTextContents] = await Promise.all([
|
||||
readlink(path.join(outDir, 'link-dir')),
|
||||
readlink(path.join(outDir, 'link.txt')),
|
||||
]);
|
||||
|
||||
strictEqual(linkDirContents, 'dir');
|
||||
strictEqual(linkTextContents, './a.txt');
|
||||
});
|
||||
|
||||
it('should re-create FileBlob symlinks properly', async () => {
|
||||
if (process.platform === 'win32') {
|
||||
console.log('Skipping test on windows');
|
||||
return;
|
||||
}
|
||||
|
||||
const files = {
|
||||
'a.txt': new FileBlob({
|
||||
mode: 33188,
|
||||
contentType: undefined,
|
||||
data: 'a text',
|
||||
}),
|
||||
'dir/b.txt': new FileBlob({
|
||||
mode: 33188,
|
||||
contentType: undefined,
|
||||
data: 'b text',
|
||||
}),
|
||||
'link-dir': new FileBlob({
|
||||
mode: 41453,
|
||||
contentType: undefined,
|
||||
data: 'dir',
|
||||
}),
|
||||
'link.txt': new FileBlob({
|
||||
mode: 41453,
|
||||
contentType: undefined,
|
||||
data: 'a.txt',
|
||||
}),
|
||||
};
|
||||
|
||||
strictEqual(Object.keys(files).length, 4);
|
||||
|
||||
const outDir = path.join(__dirname, 'symlinks-out');
|
||||
await fs.remove(outDir);
|
||||
|
||||
const files2 = await download(files, outDir);
|
||||
strictEqual(Object.keys(files2).length, 4);
|
||||
|
||||
const [linkStat, linkDirStat, aStat, dirStat] = await Promise.all([
|
||||
fs.lstat(path.join(outDir, 'link.txt')),
|
||||
fs.lstat(path.join(outDir, 'link-dir')),
|
||||
fs.lstat(path.join(outDir, 'a.txt')),
|
||||
fs.lstat(path.join(outDir, 'dir')),
|
||||
]);
|
||||
|
||||
assert(linkStat.isSymbolicLink());
|
||||
assert(linkDirStat.isSymbolicLink());
|
||||
assert(aStat.isFile());
|
||||
assert(dirStat.isDirectory());
|
||||
|
||||
const [linkDirContents, linkTextContents] = await Promise.all([
|
||||
readlink(path.join(outDir, 'link-dir')),
|
||||
readlink(path.join(outDir, 'link.txt')),
|
||||
]);
|
||||
|
||||
strictEqual(linkDirContents, 'dir');
|
||||
strictEqual(linkTextContents, 'a.txt');
|
||||
});
|
||||
|
||||
it('should download symlinks even with incorrect file', async () => {
|
||||
if (process.platform === 'win32') {
|
||||
console.log('Skipping test on windows');
|
||||
return;
|
||||
}
|
||||
const files = {
|
||||
'dir/file.txt': new FileBlob({
|
||||
mode: 33188,
|
||||
contentType: undefined,
|
||||
data: 'file text',
|
||||
}),
|
||||
linkdir: new FileBlob({
|
||||
mode: 41453,
|
||||
contentType: undefined,
|
||||
data: 'dir',
|
||||
}),
|
||||
'linkdir/file.txt': new FileBlob({
|
||||
mode: 33188,
|
||||
contentType: undefined,
|
||||
data: 'this file should be discarded',
|
||||
}),
|
||||
};
|
||||
|
||||
const outDir = path.join(__dirname, 'symlinks-out');
|
||||
await fs.remove(outDir);
|
||||
await fs.mkdirp(outDir);
|
||||
|
||||
await download(files, outDir);
|
||||
|
||||
const [dir, file, linkdir] = await Promise.all([
|
||||
fs.lstat(path.join(outDir, 'dir')),
|
||||
fs.lstat(path.join(outDir, 'dir/file.txt')),
|
||||
fs.lstat(path.join(outDir, 'linkdir')),
|
||||
]);
|
||||
expect(dir.isFile()).toBe(false);
|
||||
expect(dir.isSymbolicLink()).toBe(false);
|
||||
|
||||
expect(file.isFile()).toBe(true);
|
||||
expect(file.isSymbolicLink()).toBe(false);
|
||||
|
||||
expect(linkdir.isSymbolicLink()).toBe(true);
|
||||
|
||||
expect(warningMessages).toEqual([
|
||||
'Warning: file "linkdir/file.txt" is within a symlinked directory "linkdir" and will be ignored',
|
||||
]);
|
||||
});
|
||||
|
||||
it('should create empty directory entries', async () => {
|
||||
const outDir = path.join(__dirname, 'symlinks-out');
|
||||
await fs.remove(outDir);
|
||||
const files = {
|
||||
'empty-dir': new FileBlob({
|
||||
mode: 16877, // drwxr-xr-x
|
||||
contentType: undefined,
|
||||
data: '',
|
||||
}),
|
||||
dir: new FileBlob({
|
||||
mode: 16877,
|
||||
contentType: undefined,
|
||||
data: '',
|
||||
}),
|
||||
'dir/subdir': new FileBlob({
|
||||
mode: 16877,
|
||||
contentType: undefined,
|
||||
data: '',
|
||||
}),
|
||||
'another/subdir': new FileBlob({
|
||||
mode: 16895, // drwxrwxrwx
|
||||
contentType: undefined,
|
||||
data: '',
|
||||
}),
|
||||
};
|
||||
|
||||
await download(files, outDir);
|
||||
|
||||
for (const [p, f] of Object.entries(files)) {
|
||||
const stat = await fs.lstat(path.join(outDir, p));
|
||||
expect(stat.isDirectory()).toEqual(true);
|
||||
|
||||
if (process.platform !== 'win32') {
|
||||
// Don't test Windows since it doesn't support the same permissions
|
||||
expect(stat.mode).toEqual(f.mode);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
65
packages/build-utils/test/unit.glob.test.ts
vendored
Normal file
65
packages/build-utils/test/unit.glob.test.ts
vendored
Normal file
@@ -0,0 +1,65 @@
|
||||
import fs from 'fs-extra';
|
||||
import { join } from 'path';
|
||||
import { tmpdir } from 'os';
|
||||
import { glob, isDirectory } from '../src';
|
||||
|
||||
describe('glob()', () => {
|
||||
it('should not return entries for empty directories by default', async () => {
|
||||
const dir = await fs.mkdtemp(join(tmpdir(), 'build-utils-test'));
|
||||
try {
|
||||
await Promise.all([
|
||||
fs.writeFile(join(dir, 'root.txt'), 'file at the root'),
|
||||
fs.mkdirp(join(dir, 'empty-dir')),
|
||||
fs
|
||||
.mkdirp(join(dir, 'dir-with-file'))
|
||||
.then(() =>
|
||||
fs.writeFile(join(dir, 'dir-with-file/data.json'), '{"a":"b"}')
|
||||
),
|
||||
fs.mkdirp(join(dir, 'another/subdir')),
|
||||
]);
|
||||
const files = await glob('**', dir);
|
||||
const fileNames = Object.keys(files).sort();
|
||||
expect(fileNames).toHaveLength(2);
|
||||
expect(fileNames).toEqual(['dir-with-file/data.json', 'root.txt']);
|
||||
expect(isDirectory(files['dir-with-file/data.json'].mode)).toEqual(false);
|
||||
expect(isDirectory(files['root.txt'].mode)).toEqual(false);
|
||||
expect(files['dir-with-file']).toBeUndefined();
|
||||
expect(files['another/subdir']).toBeUndefined();
|
||||
expect(files['empty-dir']).toBeUndefined();
|
||||
} finally {
|
||||
await fs.remove(dir);
|
||||
}
|
||||
});
|
||||
|
||||
it('should return entries for empty directories with `includeDirectories: true`', async () => {
|
||||
const dir = await fs.mkdtemp(join(tmpdir(), 'build-utils-test'));
|
||||
try {
|
||||
await Promise.all([
|
||||
fs.writeFile(join(dir, 'root.txt'), 'file at the root'),
|
||||
fs.mkdirp(join(dir, 'empty-dir')),
|
||||
fs
|
||||
.mkdirp(join(dir, 'dir-with-file'))
|
||||
.then(() =>
|
||||
fs.writeFile(join(dir, 'dir-with-file/data.json'), '{"a":"b"}')
|
||||
),
|
||||
fs.mkdirp(join(dir, 'another/subdir')),
|
||||
]);
|
||||
const files = await glob('**', { cwd: dir, includeDirectories: true });
|
||||
const fileNames = Object.keys(files).sort();
|
||||
expect(fileNames).toHaveLength(4);
|
||||
expect(fileNames).toEqual([
|
||||
'another/subdir',
|
||||
'dir-with-file/data.json',
|
||||
'empty-dir',
|
||||
'root.txt',
|
||||
]);
|
||||
expect(isDirectory(files['another/subdir'].mode)).toEqual(true);
|
||||
expect(isDirectory(files['empty-dir'].mode)).toEqual(true);
|
||||
expect(isDirectory(files['dir-with-file/data.json'].mode)).toEqual(false);
|
||||
expect(isDirectory(files['root.txt'].mode)).toEqual(false);
|
||||
expect(files['dir-with-file']).toBeUndefined();
|
||||
} finally {
|
||||
await fs.remove(dir);
|
||||
}
|
||||
});
|
||||
});
|
||||
144
packages/build-utils/test/unit.test.ts
vendored
144
packages/build-utils/test/unit.test.ts
vendored
@@ -1,18 +1,15 @@
|
||||
import ms from 'ms';
|
||||
import path from 'path';
|
||||
import fs, { readlink } from 'fs-extra';
|
||||
import { strict as assert, strictEqual } from 'assert';
|
||||
import fs from 'fs-extra';
|
||||
import { strict as assert } from 'assert';
|
||||
import { getSupportedNodeVersion } from '../src/fs/node-version';
|
||||
import download from '../src/fs/download';
|
||||
import {
|
||||
glob,
|
||||
getNodeVersion,
|
||||
getLatestNodeVersion,
|
||||
getDiscontinuedNodeVersions,
|
||||
runNpmInstall,
|
||||
runPackageJsonScript,
|
||||
scanParentDirs,
|
||||
FileBlob,
|
||||
Prerender,
|
||||
} from '../src';
|
||||
|
||||
@@ -49,143 +46,6 @@ afterEach(() => {
|
||||
console.warn = originalConsoleWarn;
|
||||
});
|
||||
|
||||
it('should re-create FileFsRef symlinks properly', async () => {
|
||||
if (process.platform === 'win32') {
|
||||
console.log('Skipping test on windows');
|
||||
return;
|
||||
}
|
||||
const files = await glob('**', path.join(__dirname, 'symlinks'));
|
||||
assert.equal(Object.keys(files).length, 4);
|
||||
|
||||
const outDir = path.join(__dirname, 'symlinks-out');
|
||||
await fs.remove(outDir);
|
||||
|
||||
const files2 = await download(files, outDir);
|
||||
assert.equal(Object.keys(files2).length, 4);
|
||||
|
||||
const [linkStat, linkDirStat, aStat] = await Promise.all([
|
||||
fs.lstat(path.join(outDir, 'link.txt')),
|
||||
fs.lstat(path.join(outDir, 'link-dir')),
|
||||
fs.lstat(path.join(outDir, 'a.txt')),
|
||||
]);
|
||||
assert(linkStat.isSymbolicLink());
|
||||
assert(linkDirStat.isSymbolicLink());
|
||||
assert(aStat.isFile());
|
||||
|
||||
const [linkDirContents, linkTextContents] = await Promise.all([
|
||||
readlink(path.join(outDir, 'link-dir')),
|
||||
readlink(path.join(outDir, 'link.txt')),
|
||||
]);
|
||||
|
||||
strictEqual(linkDirContents, 'dir');
|
||||
strictEqual(linkTextContents, './a.txt');
|
||||
});
|
||||
|
||||
it('should re-create FileBlob symlinks properly', async () => {
|
||||
if (process.platform === 'win32') {
|
||||
console.log('Skipping test on windows');
|
||||
return;
|
||||
}
|
||||
|
||||
const files = {
|
||||
'a.txt': new FileBlob({
|
||||
mode: 33188,
|
||||
contentType: undefined,
|
||||
data: 'a text',
|
||||
}),
|
||||
'dir/b.txt': new FileBlob({
|
||||
mode: 33188,
|
||||
contentType: undefined,
|
||||
data: 'b text',
|
||||
}),
|
||||
'link-dir': new FileBlob({
|
||||
mode: 41453,
|
||||
contentType: undefined,
|
||||
data: 'dir',
|
||||
}),
|
||||
'link.txt': new FileBlob({
|
||||
mode: 41453,
|
||||
contentType: undefined,
|
||||
data: 'a.txt',
|
||||
}),
|
||||
};
|
||||
|
||||
strictEqual(Object.keys(files).length, 4);
|
||||
|
||||
const outDir = path.join(__dirname, 'symlinks-out');
|
||||
await fs.remove(outDir);
|
||||
|
||||
const files2 = await download(files, outDir);
|
||||
strictEqual(Object.keys(files2).length, 4);
|
||||
|
||||
const [linkStat, linkDirStat, aStat, dirStat] = await Promise.all([
|
||||
fs.lstat(path.join(outDir, 'link.txt')),
|
||||
fs.lstat(path.join(outDir, 'link-dir')),
|
||||
fs.lstat(path.join(outDir, 'a.txt')),
|
||||
fs.lstat(path.join(outDir, 'dir')),
|
||||
]);
|
||||
|
||||
assert(linkStat.isSymbolicLink());
|
||||
assert(linkDirStat.isSymbolicLink());
|
||||
assert(aStat.isFile());
|
||||
assert(dirStat.isDirectory());
|
||||
|
||||
const [linkDirContents, linkTextContents] = await Promise.all([
|
||||
readlink(path.join(outDir, 'link-dir')),
|
||||
readlink(path.join(outDir, 'link.txt')),
|
||||
]);
|
||||
|
||||
strictEqual(linkDirContents, 'dir');
|
||||
strictEqual(linkTextContents, 'a.txt');
|
||||
});
|
||||
|
||||
it('should download symlinks even with incorrect file', async () => {
|
||||
if (process.platform === 'win32') {
|
||||
console.log('Skipping test on windows');
|
||||
return;
|
||||
}
|
||||
const files = {
|
||||
'dir/file.txt': new FileBlob({
|
||||
mode: 33188,
|
||||
contentType: undefined,
|
||||
data: 'file text',
|
||||
}),
|
||||
linkdir: new FileBlob({
|
||||
mode: 41453,
|
||||
contentType: undefined,
|
||||
data: 'dir',
|
||||
}),
|
||||
'linkdir/file.txt': new FileBlob({
|
||||
mode: 33188,
|
||||
contentType: undefined,
|
||||
data: 'this file should be discarded',
|
||||
}),
|
||||
};
|
||||
|
||||
const outDir = path.join(__dirname, 'symlinks-out');
|
||||
await fs.remove(outDir);
|
||||
await fs.mkdirp(outDir);
|
||||
|
||||
await download(files, outDir);
|
||||
|
||||
const [dir, file, linkdir] = await Promise.all([
|
||||
fs.lstat(path.join(outDir, 'dir')),
|
||||
fs.lstat(path.join(outDir, 'dir/file.txt')),
|
||||
fs.lstat(path.join(outDir, 'linkdir')),
|
||||
]);
|
||||
expect(dir.isFile()).toBe(false);
|
||||
expect(dir.isSymbolicLink()).toBe(false);
|
||||
|
||||
expect(file.isFile()).toBe(true);
|
||||
expect(file.isSymbolicLink()).toBe(false);
|
||||
|
||||
expect(linkdir.isSymbolicLink()).toBe(true);
|
||||
|
||||
expect(warningMessages).toEqual([
|
||||
'Warning: file "linkdir/file.txt" is within a symlinked directory "linkdir" and will be ignored',
|
||||
]);
|
||||
});
|
||||
|
||||
it('should only match supported node versions, otherwise throw an error', async () => {
|
||||
expect(await getSupportedNodeVersion('14.x', false)).toHaveProperty(
|
||||
'major',
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "vercel",
|
||||
"version": "28.12.0",
|
||||
"version": "28.13.1",
|
||||
"preferGlobal": true,
|
||||
"license": "Apache-2.0",
|
||||
"description": "The command-line interface for Vercel",
|
||||
@@ -41,16 +41,16 @@
|
||||
"node": ">= 14"
|
||||
},
|
||||
"dependencies": {
|
||||
"@vercel/build-utils": "workspace:5.7.6",
|
||||
"@vercel/go": "workspace:2.2.25",
|
||||
"@vercel/hydrogen": "workspace:0.0.39",
|
||||
"@vercel/next": "workspace:3.3.10",
|
||||
"@vercel/node": "workspace:2.8.7",
|
||||
"@vercel/python": "workspace:3.1.35",
|
||||
"@vercel/redwood": "workspace:1.0.46",
|
||||
"@vercel/remix": "workspace:1.2.0",
|
||||
"@vercel/ruby": "workspace:1.3.51",
|
||||
"@vercel/static-build": "workspace:1.1.2"
|
||||
"@vercel/build-utils": "5.9.0",
|
||||
"@vercel/go": "2.2.30",
|
||||
"@vercel/hydrogen": "0.0.44",
|
||||
"@vercel/next": "3.3.18",
|
||||
"@vercel/node": "2.8.15",
|
||||
"@vercel/python": "3.1.40",
|
||||
"@vercel/redwood": "1.0.51",
|
||||
"@vercel/remix": "1.2.7",
|
||||
"@vercel/ruby": "1.3.56",
|
||||
"@vercel/static-build": "1.2.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@alex_neo/jest-expect-message": "1.0.5",
|
||||
@@ -93,13 +93,13 @@
|
||||
"@types/which": "1.3.2",
|
||||
"@types/write-json-file": "2.2.1",
|
||||
"@types/yauzl-promise": "2.1.0",
|
||||
"@vercel/client": "workspace:12.2.27",
|
||||
"@vercel/error-utils": "workspace:1.0.4",
|
||||
"@vercel/frameworks": "workspace:1.2.0",
|
||||
"@vercel/fs-detectors": "workspace:3.7.0",
|
||||
"@vercel/client": "12.3.2",
|
||||
"@vercel/error-utils": "1.0.8",
|
||||
"@vercel/frameworks": "1.2.4",
|
||||
"@vercel/fs-detectors": "3.7.5",
|
||||
"@vercel/fun": "1.0.4",
|
||||
"@vercel/ncc": "0.24.0",
|
||||
"@vercel/routing-utils": "workspace:2.1.4",
|
||||
"@vercel/routing-utils": "2.1.8",
|
||||
"@zeit/source-map-support": "0.6.2",
|
||||
"ajv": "6.12.2",
|
||||
"alpha-sort": "2.0.1",
|
||||
|
||||
@@ -51,7 +51,7 @@ export default async function ls(
|
||||
...paginationOptions
|
||||
);
|
||||
output.log(`aliases found under ${chalk.bold(contextName)} ${lsStamp()}`);
|
||||
output.log(printAliasTable(aliases));
|
||||
client.stdout.write(printAliasTable(aliases));
|
||||
|
||||
if (pagination && pagination.count === 20) {
|
||||
const flags = getCommandFlags(opts, ['_', '--next']);
|
||||
|
||||
@@ -55,7 +55,7 @@ async function ls(
|
||||
);
|
||||
|
||||
if (certs.length > 0) {
|
||||
output.log(formatCertsTable(certs));
|
||||
client.stdout.write(formatCertsTable(certs));
|
||||
}
|
||||
|
||||
if (pagination && pagination.count === 20) {
|
||||
|
||||
@@ -70,7 +70,7 @@ export default async function ls(
|
||||
records.length > 0 ? 'Records' : 'No records'
|
||||
} found under ${chalk.bold(contextName)} ${chalk.gray(lsStamp())}`
|
||||
);
|
||||
output.log(getDNSRecordsTable([{ domainName, records }]));
|
||||
client.stdout.write(getDNSRecordsTable([{ domainName, records }]));
|
||||
|
||||
if (pagination && pagination.count === 20) {
|
||||
const flags = getCommandFlags(opts, ['_', '--next']);
|
||||
|
||||
@@ -30,7 +30,7 @@ import {
|
||||
import pipe from 'promisepipe';
|
||||
import { unzip } from './unzip';
|
||||
import { VERCEL_DIR } from '../projects/link';
|
||||
import { VercelConfig } from '@vercel/client';
|
||||
import { fileNameSymbol, VercelConfig } from '@vercel/client';
|
||||
|
||||
const { normalize } = posix;
|
||||
export const OUTPUT_DIR = join(VERCEL_DIR, 'output');
|
||||
@@ -56,6 +56,7 @@ export async function writeBuildResult(
|
||||
return writeBuildResultV2(
|
||||
outputDir,
|
||||
buildResult as BuildResultV2,
|
||||
build,
|
||||
vercelConfig
|
||||
);
|
||||
} else if (version === 3) {
|
||||
@@ -107,6 +108,7 @@ function stripDuplicateSlashes(path: string): string {
|
||||
async function writeBuildResultV2(
|
||||
outputDir: string,
|
||||
buildResult: BuildResultV2,
|
||||
build: Builder,
|
||||
vercelConfig: VercelConfig | null
|
||||
) {
|
||||
if ('buildOutputPath' in buildResult) {
|
||||
@@ -114,6 +116,18 @@ async function writeBuildResultV2(
|
||||
return;
|
||||
}
|
||||
|
||||
// Some very old `@now` scoped Builders return `output` at the top-level.
|
||||
// These Builders are no longer supported.
|
||||
if (!buildResult.output) {
|
||||
const configFile = vercelConfig?.[fileNameSymbol];
|
||||
const updateMessage = build.use.startsWith('@now/')
|
||||
? ` Please update from "@now" to "@vercel" in your \`${configFile}\` file.`
|
||||
: '';
|
||||
throw new Error(
|
||||
`The build result from "${build.use}" is missing the "output" property.${updateMessage}`
|
||||
);
|
||||
}
|
||||
|
||||
const lambdas = new Map<Lambda, string>();
|
||||
const overrides: Record<string, PathOverride> = {};
|
||||
for (const [path, output] of Object.entries(buildResult.output)) {
|
||||
|
||||
@@ -1,16 +1,5 @@
|
||||
import { join } from 'path';
|
||||
import ms from 'ms';
|
||||
import fs, { mkdirp } from 'fs-extra';
|
||||
|
||||
const {
|
||||
exec,
|
||||
fetch,
|
||||
fixture,
|
||||
sleep,
|
||||
testFixture,
|
||||
testFixtureStdio,
|
||||
validateResponseHeaders,
|
||||
} = require('./utils.js');
|
||||
import { isIP } from 'net';
|
||||
const { exec, fixture, testFixture, testFixtureStdio } = require('./utils.js');
|
||||
|
||||
test('[vercel dev] validate redirects', async () => {
|
||||
const directory = fixture('invalid-redirects');
|
||||
@@ -124,260 +113,112 @@ test(
|
||||
);
|
||||
|
||||
test(
|
||||
'[vercel dev] test cleanUrls serve correct content',
|
||||
testFixtureStdio('test-clean-urls', async (testPath: any) => {
|
||||
await testPath(200, '/', 'Index Page');
|
||||
await testPath(200, '/about', 'About Page');
|
||||
await testPath(200, '/sub', 'Sub Index Page');
|
||||
await testPath(200, '/sub/another', 'Sub Another Page');
|
||||
await testPath(200, '/style.css', 'body { color: green }');
|
||||
await testPath(308, '/index.html', 'Redirecting to / (308)', {
|
||||
Location: '/',
|
||||
});
|
||||
await testPath(308, '/about.html', 'Redirecting to /about (308)', {
|
||||
Location: '/about',
|
||||
});
|
||||
await testPath(308, '/sub/index.html', 'Redirecting to /sub (308)', {
|
||||
Location: '/sub',
|
||||
'[vercel dev] Use `@vercel/python` with Flask requirements.txt',
|
||||
testFixtureStdio('python-flask', async (testPath: any) => {
|
||||
const name = 'Alice';
|
||||
const year = new Date().getFullYear();
|
||||
await testPath(200, `/api/user?name=${name}`, new RegExp(`Hello ${name}`));
|
||||
await testPath(200, `/api/date`, new RegExp(`Current date is ${year}`));
|
||||
await testPath(200, `/api/date.py`, new RegExp(`Current date is ${year}`));
|
||||
await testPath(200, `/api/headers`, (body: any, res: any) => {
|
||||
// @ts-ignore
|
||||
const { host } = new URL(res.url);
|
||||
expect(body).toBe(host);
|
||||
});
|
||||
})
|
||||
);
|
||||
|
||||
test(
|
||||
'[vercel dev] Use custom runtime from the "functions" property',
|
||||
testFixtureStdio('custom-runtime', async (testPath: any) => {
|
||||
await testPath(200, `/api/user`, /Hello, from Bash!/m);
|
||||
await testPath(200, `/api/user.sh`, /Hello, from Bash!/m);
|
||||
})
|
||||
);
|
||||
|
||||
test(
|
||||
'[vercel dev] Should work with nested `tsconfig.json` files',
|
||||
testFixtureStdio('nested-tsconfig', async (testPath: any) => {
|
||||
await testPath(200, `/`, /Nested tsconfig.json test page/);
|
||||
await testPath(200, `/api`, 'Nested `tsconfig.json` API endpoint');
|
||||
})
|
||||
);
|
||||
|
||||
test(
|
||||
'[vercel dev] Should force `tsc` option "module: commonjs" for `startDevServer()`',
|
||||
testFixtureStdio('force-module-commonjs', async (testPath: any) => {
|
||||
await testPath(200, `/`, /Force "module: commonjs" test page/);
|
||||
await testPath(
|
||||
308,
|
||||
'/sub/another.html',
|
||||
'Redirecting to /sub/another (308)',
|
||||
{ Location: '/sub/another' }
|
||||
200,
|
||||
`/api`,
|
||||
'Force "module: commonjs" JavaScript with ES Modules API endpoint'
|
||||
);
|
||||
await testPath(
|
||||
200,
|
||||
`/api/ts`,
|
||||
'Force "module: commonjs" TypeScript API endpoint'
|
||||
);
|
||||
})
|
||||
);
|
||||
|
||||
test(
|
||||
'[vercel dev] test cleanUrls serve correct content when using `outputDirectory`',
|
||||
testFixtureStdio(
|
||||
'test-clean-urls-with-output-directory',
|
||||
async (testPath: any) => {
|
||||
await testPath(200, '/', 'Index Page');
|
||||
await testPath(200, '/about', 'About Page');
|
||||
await testPath(200, '/sub', 'Sub Index Page');
|
||||
await testPath(200, '/sub/another', 'Sub Another Page');
|
||||
await testPath(200, '/style.css', 'body { color: green }');
|
||||
await testPath(308, '/index.html', 'Redirecting to / (308)', {
|
||||
Location: '/',
|
||||
});
|
||||
await testPath(308, '/about.html', 'Redirecting to /about (308)', {
|
||||
Location: '/about',
|
||||
});
|
||||
await testPath(308, '/sub/index.html', 'Redirecting to /sub (308)', {
|
||||
Location: '/sub',
|
||||
});
|
||||
await testPath(
|
||||
308,
|
||||
'/sub/another.html',
|
||||
'Redirecting to /sub/another (308)',
|
||||
{ Location: '/sub/another' }
|
||||
);
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
test(
|
||||
'[vercel dev] should serve custom 404 when `cleanUrls: true`',
|
||||
testFixtureStdio('test-clean-urls-custom-404', async (testPath: any) => {
|
||||
await testPath(200, '/', 'This is the home page');
|
||||
await testPath(200, '/about', 'The about page');
|
||||
await testPath(200, '/contact/me', 'Contact Me Subdirectory');
|
||||
await testPath(404, '/nothing', 'Custom 404 Page');
|
||||
await testPath(404, '/nothing/', 'Custom 404 Page');
|
||||
'[vercel dev] should prioritize index.html over other file named index.*',
|
||||
testFixtureStdio('index-html-priority', async (testPath: any) => {
|
||||
await testPath(200, '/', 'This is index.html');
|
||||
await testPath(200, '/index.css', 'This is index.css');
|
||||
})
|
||||
);
|
||||
|
||||
test(
|
||||
'[vercel dev] test cleanUrls and trailingSlash serve correct content',
|
||||
testFixtureStdio('test-clean-urls-trailing-slash', async (testPath: any) => {
|
||||
await testPath(200, '/', 'Index Page');
|
||||
await testPath(200, '/about/', 'About Page');
|
||||
await testPath(200, '/sub/', 'Sub Index Page');
|
||||
await testPath(200, '/sub/another/', 'Sub Another Page');
|
||||
await testPath(200, '/style.css', 'body { color: green }');
|
||||
//TODO: fix this test so that location is `/` instead of `//`
|
||||
//await testPath(308, '/index.html', 'Redirecting to / (308)', { Location: '/' });
|
||||
await testPath(308, '/about.html', 'Redirecting to /about/ (308)', {
|
||||
Location: '/about/',
|
||||
});
|
||||
await testPath(308, '/sub/index.html', 'Redirecting to /sub/ (308)', {
|
||||
Location: '/sub/',
|
||||
});
|
||||
'[vercel dev] Should support `*.go` API serverless functions',
|
||||
testFixtureStdio('go', async (testPath: any) => {
|
||||
await testPath(200, `/api`, 'This is the index page');
|
||||
await testPath(200, `/api/index`, 'This is the index page');
|
||||
await testPath(200, `/api/index.go`, 'This is the index page');
|
||||
await testPath(200, `/api/another`, 'This is another page');
|
||||
await testPath(200, '/api/another.go', 'This is another page');
|
||||
await testPath(200, `/api/foo`, 'Req Path: /api/foo');
|
||||
await testPath(200, `/api/bar`, 'Req Path: /api/bar');
|
||||
})
|
||||
);
|
||||
|
||||
test(
|
||||
'[vercel dev] Should set the `ts-node` "target" to match Node.js version',
|
||||
testFixtureStdio('node-ts-node-target', async (testPath: any) => {
|
||||
await testPath(200, `/api/subclass`, '{"ok":true}');
|
||||
await testPath(
|
||||
308,
|
||||
'/sub/another.html',
|
||||
'Redirecting to /sub/another/ (308)',
|
||||
{
|
||||
Location: '/sub/another/',
|
||||
}
|
||||
200,
|
||||
`/api/array`,
|
||||
'{"months":[1,2,3,4,5,6,7,8,9,10,11,12]}'
|
||||
);
|
||||
})
|
||||
);
|
||||
|
||||
test(
|
||||
'[vercel dev] test cors headers work with OPTIONS',
|
||||
testFixtureStdio('test-cors-routes', async (testPath: any) => {
|
||||
const headers = {
|
||||
'Access-Control-Allow-Origin': '*',
|
||||
'Access-Control-Allow-Headers':
|
||||
'Content-Type, Authorization, Accept, Content-Length, Origin, User-Agent',
|
||||
'Access-Control-Allow-Methods':
|
||||
'GET, POST, OPTIONS, HEAD, PATCH, PUT, DELETE',
|
||||
};
|
||||
await testPath(200, '/', 'status api', headers, { method: 'GET' });
|
||||
await testPath(200, '/', 'status api', headers, { method: 'POST' });
|
||||
await testPath(200, '/api/status.js', 'status api', headers, {
|
||||
method: 'GET',
|
||||
});
|
||||
await testPath(200, '/api/status.js', 'status api', headers, {
|
||||
method: 'POST',
|
||||
});
|
||||
await testPath(204, '/', '', headers, { method: 'OPTIONS' });
|
||||
await testPath(204, '/api/status.js', '', headers, { method: 'OPTIONS' });
|
||||
})
|
||||
);
|
||||
await testPath(200, `/api/dump`, (body: any, res: any, isDev: any) => {
|
||||
// @ts-ignore
|
||||
const { host } = new URL(res.url);
|
||||
const { env, headers } = JSON.parse(body);
|
||||
|
||||
test(
|
||||
'[vercel dev] test trailingSlash true serve correct content',
|
||||
testFixtureStdio('test-trailing-slash', async (testPath: any) => {
|
||||
await testPath(200, '/', 'Index Page');
|
||||
await testPath(200, '/index.html', 'Index Page');
|
||||
await testPath(200, '/about.html', 'About Page');
|
||||
await testPath(200, '/sub/', 'Sub Index Page');
|
||||
await testPath(200, '/sub/index.html', 'Sub Index Page');
|
||||
await testPath(200, '/sub/another.html', 'Sub Another Page');
|
||||
await testPath(200, '/style.css', 'body { color: green }');
|
||||
await testPath(308, '/about.html/', 'Redirecting to /about.html (308)', {
|
||||
Location: '/about.html',
|
||||
});
|
||||
await testPath(308, '/style.css/', 'Redirecting to /style.css (308)', {
|
||||
Location: '/style.css',
|
||||
});
|
||||
await testPath(308, '/sub', 'Redirecting to /sub/ (308)', {
|
||||
Location: '/sub/',
|
||||
});
|
||||
})
|
||||
);
|
||||
// Test that the API endpoint receives the Vercel proxy request headers
|
||||
expect(headers['x-forwarded-host']).toBe(host);
|
||||
expect(headers['x-vercel-deployment-url']).toBe(host);
|
||||
expect(isIP(headers['x-real-ip'])).toBeTruthy();
|
||||
expect(isIP(headers['x-forwarded-for'])).toBeTruthy();
|
||||
expect(isIP(headers['x-vercel-forwarded-for'])).toBeTruthy();
|
||||
|
||||
test(
|
||||
'[vercel dev] should serve custom 404 when `trailingSlash: true`',
|
||||
testFixtureStdio('test-trailing-slash-custom-404', async (testPath: any) => {
|
||||
await testPath(200, '/', 'This is the home page');
|
||||
await testPath(200, '/about.html', 'The about page');
|
||||
await testPath(200, '/contact/', 'Contact Subdirectory');
|
||||
await testPath(404, '/nothing/', 'Custom 404 Page');
|
||||
})
|
||||
);
|
||||
|
||||
test(
|
||||
'[vercel dev] test trailingSlash false serve correct content',
|
||||
testFixtureStdio('test-trailing-slash-false', async (testPath: any) => {
|
||||
await testPath(200, '/', 'Index Page');
|
||||
await testPath(200, '/index.html', 'Index Page');
|
||||
await testPath(200, '/about.html', 'About Page');
|
||||
await testPath(200, '/sub', 'Sub Index Page');
|
||||
await testPath(200, '/sub/index.html', 'Sub Index Page');
|
||||
await testPath(200, '/sub/another.html', 'Sub Another Page');
|
||||
await testPath(200, '/style.css', 'body { color: green }');
|
||||
await testPath(308, '/about.html/', 'Redirecting to /about.html (308)', {
|
||||
Location: '/about.html',
|
||||
});
|
||||
await testPath(308, '/sub/', 'Redirecting to /sub (308)', {
|
||||
Location: '/sub',
|
||||
});
|
||||
await testPath(
|
||||
308,
|
||||
'/sub/another.html/',
|
||||
'Redirecting to /sub/another.html (308)',
|
||||
{
|
||||
Location: '/sub/another.html',
|
||||
// Test that the API endpoint has the Vercel platform env vars defined.
|
||||
expect(env.NOW_REGION).toMatch(/^[a-z]{3}\d$/);
|
||||
if (isDev) {
|
||||
// Only dev is tested because in production these are opt-in.
|
||||
expect(env.VERCEL_URL).toBe(host);
|
||||
expect(env.VERCEL_REGION).toBe('dev1');
|
||||
}
|
||||
);
|
||||
});
|
||||
})
|
||||
);
|
||||
|
||||
test(
|
||||
'[vercel dev] throw when invalid builder routes detected',
|
||||
testFixtureStdio(
|
||||
'invalid-builder-routes',
|
||||
async (testPath: any) => {
|
||||
await testPath(
|
||||
500,
|
||||
'/',
|
||||
/Route at index 0 has invalid `src` regular expression/m
|
||||
);
|
||||
},
|
||||
{ skipDeploy: true }
|
||||
)
|
||||
);
|
||||
|
||||
test(
|
||||
'[vercel dev] support legacy `@now` scope runtimes',
|
||||
testFixtureStdio('legacy-now-runtime', async (testPath: any) => {
|
||||
await testPath(200, '/', /A simple deployment with the Vercel API!/m);
|
||||
})
|
||||
);
|
||||
|
||||
test(
|
||||
'[vercel dev] 00-list-directory',
|
||||
testFixtureStdio(
|
||||
'00-list-directory',
|
||||
async (testPath: any) => {
|
||||
await testPath(200, '/', /Files within/m);
|
||||
await testPath(200, '/', /test[0-3]\.txt/m);
|
||||
await testPath(200, '/', /\.well-known/m);
|
||||
await testPath(200, '/.well-known/keybase.txt', 'proof goes here');
|
||||
},
|
||||
{ projectSettings: { directoryListing: true } }
|
||||
)
|
||||
);
|
||||
|
||||
test(
|
||||
'[vercel dev] 01-node',
|
||||
testFixtureStdio('01-node', async (testPath: any) => {
|
||||
await testPath(200, '/', /A simple deployment with the Vercel API!/m);
|
||||
})
|
||||
);
|
||||
|
||||
test(
|
||||
'[vercel dev] add a `api/fn.ts` when `api` does not exist at startup`',
|
||||
testFixtureStdio('no-api', async (_testPath: any, port: any) => {
|
||||
const directory = fixture('no-api');
|
||||
const apiDir = join(directory, 'api');
|
||||
|
||||
try {
|
||||
{
|
||||
const response = await fetch(`http://localhost:${port}/api/new-file`);
|
||||
validateResponseHeaders(response);
|
||||
expect(response.status).toBe(404);
|
||||
}
|
||||
|
||||
const fileContents = `
|
||||
export const config = {
|
||||
runtime: 'edge'
|
||||
}
|
||||
|
||||
export default async function edge(request, event) {
|
||||
return new Response('from new file');
|
||||
}
|
||||
`;
|
||||
|
||||
await mkdirp(apiDir);
|
||||
await fs.writeFile(join(apiDir, 'new-file.js'), fileContents);
|
||||
|
||||
// Wait until file events have been processed
|
||||
await sleep(ms('1s'));
|
||||
|
||||
{
|
||||
const response = await fetch(`http://localhost:${port}/api/new-file`);
|
||||
validateResponseHeaders(response);
|
||||
const body = await response.text();
|
||||
expect(body.trim()).toBe('from new file');
|
||||
}
|
||||
} finally {
|
||||
await fs.remove(apiDir);
|
||||
}
|
||||
'[vercel dev] Do not fail if `src` is missing',
|
||||
testFixtureStdio('missing-src-property', async (testPath: any) => {
|
||||
await testPath(200, '/', /hello:index.txt/m);
|
||||
await testPath(404, '/i-do-not-exist');
|
||||
})
|
||||
);
|
||||
|
||||
@@ -1,196 +1,15 @@
|
||||
import ms from 'ms';
|
||||
import fs from 'fs-extra';
|
||||
import { isIP } from 'net';
|
||||
import { join } from 'path';
|
||||
import { Response } from 'node-fetch';
|
||||
|
||||
const {
|
||||
fetch,
|
||||
sleep,
|
||||
fixture,
|
||||
testFixture,
|
||||
testFixtureStdio,
|
||||
validateResponseHeaders,
|
||||
} = require('./utils.js');
|
||||
|
||||
test(
|
||||
'[vercel dev] temporary directory listing',
|
||||
testFixtureStdio(
|
||||
'temporary-directory-listing',
|
||||
async (_testPath: any, port: any) => {
|
||||
const directory = fixture('temporary-directory-listing');
|
||||
await fs.unlink(join(directory, 'index.txt')).catch(() => null);
|
||||
|
||||
await sleep(ms('20s'));
|
||||
|
||||
const firstResponse = await fetch(`http://localhost:${port}`);
|
||||
validateResponseHeaders(firstResponse);
|
||||
const body = await firstResponse.text();
|
||||
console.log(body);
|
||||
expect(firstResponse.status).toBe(404);
|
||||
|
||||
await fs.writeFile(join(directory, 'index.txt'), 'hello');
|
||||
|
||||
for (let i = 0; i < 20; i++) {
|
||||
const response = await fetch(`http://localhost:${port}`);
|
||||
validateResponseHeaders(response);
|
||||
|
||||
if (response.status === 200) {
|
||||
const body = await response.text();
|
||||
expect(body).toBe('hello');
|
||||
}
|
||||
|
||||
await sleep(ms('1s'));
|
||||
}
|
||||
},
|
||||
{ skipDeploy: true }
|
||||
)
|
||||
);
|
||||
|
||||
test('[vercel dev] add a `package.json` to trigger `@vercel/static-build`', async () => {
|
||||
const directory = fixture('trigger-static-build');
|
||||
|
||||
await fs.unlink(join(directory, 'package.json')).catch(() => null);
|
||||
|
||||
await fs.unlink(join(directory, 'public', 'index.txt')).catch(() => null);
|
||||
|
||||
await fs.rmdir(join(directory, 'public')).catch(() => null);
|
||||
|
||||
const tester = testFixtureStdio(
|
||||
'trigger-static-build',
|
||||
async (_testPath: any, port: any) => {
|
||||
{
|
||||
const response = await fetch(`http://localhost:${port}`);
|
||||
validateResponseHeaders(response);
|
||||
const body = await response.text();
|
||||
expect(body.trim()).toBe('hello:index.txt');
|
||||
}
|
||||
|
||||
const rnd = Math.random().toString();
|
||||
const pkg = {
|
||||
private: true,
|
||||
scripts: { build: `mkdir -p public && echo ${rnd} > public/index.txt` },
|
||||
};
|
||||
|
||||
await fs.writeFile(join(directory, 'package.json'), JSON.stringify(pkg));
|
||||
|
||||
// Wait until file events have been processed
|
||||
await sleep(ms('2s'));
|
||||
|
||||
{
|
||||
const response = await fetch(`http://localhost:${port}`);
|
||||
validateResponseHeaders(response);
|
||||
const body = await response.text();
|
||||
expect(body.trim()).toBe(rnd);
|
||||
}
|
||||
},
|
||||
{ skipDeploy: true }
|
||||
);
|
||||
|
||||
await tester();
|
||||
});
|
||||
|
||||
test('[vercel dev] no build matches warning', async () => {
|
||||
const directory = fixture('no-build-matches');
|
||||
const { dev } = await testFixture(directory, {
|
||||
stdio: ['ignore', 'pipe', 'pipe'],
|
||||
});
|
||||
|
||||
try {
|
||||
// start `vercel dev` detached in child_process
|
||||
dev.unref();
|
||||
|
||||
dev.stderr.setEncoding('utf8');
|
||||
await new Promise<void>(resolve => {
|
||||
dev.stderr.on('data', (str: string) => {
|
||||
if (str.includes('did not match any source files')) {
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
});
|
||||
} finally {
|
||||
await dev.kill();
|
||||
}
|
||||
});
|
||||
|
||||
test(
|
||||
'[vercel dev] do not recursivly check the path',
|
||||
testFixtureStdio('handle-filesystem-missing', async (testPath: any) => {
|
||||
await testPath(200, '/', /hello/m);
|
||||
await testPath(404, '/favicon.txt');
|
||||
})
|
||||
);
|
||||
|
||||
test('[vercel dev] render warning for empty cwd dir', async () => {
|
||||
const directory = fixture('empty');
|
||||
const { dev, port } = await testFixture(directory, {
|
||||
stdio: ['ignore', 'pipe', 'pipe'],
|
||||
});
|
||||
|
||||
try {
|
||||
dev.unref();
|
||||
|
||||
// Monitor `stderr` for the warning
|
||||
dev.stderr.setEncoding('utf8');
|
||||
const msg = 'There are no files inside your deployment.';
|
||||
await new Promise<void>(resolve => {
|
||||
dev.stderr.on('data', (str: string) => {
|
||||
if (str.includes(msg)) {
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Issue a request to ensure a 404 response
|
||||
await sleep(ms('3s'));
|
||||
const response = await fetch(`http://localhost:${port}`);
|
||||
validateResponseHeaders(response);
|
||||
expect(response.status).toBe(404);
|
||||
} finally {
|
||||
await dev.kill();
|
||||
}
|
||||
});
|
||||
|
||||
test('[vercel dev] do not rebuild for changes in the output directory', async () => {
|
||||
const directory = fixture('output-is-source');
|
||||
|
||||
const { dev, port } = await testFixture(directory, {
|
||||
stdio: ['ignore', 'pipe', 'pipe'],
|
||||
});
|
||||
|
||||
try {
|
||||
dev.unref();
|
||||
|
||||
let stderr: any = [];
|
||||
const start = Date.now();
|
||||
|
||||
dev.stderr.on('data', (str: any) => stderr.push(str));
|
||||
|
||||
while (stderr.join('').includes('Ready') === false) {
|
||||
await sleep(ms('3s'));
|
||||
|
||||
if (Date.now() - start > ms('30s')) {
|
||||
console.log('stderr:', stderr.join(''));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
const resp1 = await fetch(`http://localhost:${port}`);
|
||||
const text1 = await resp1.text();
|
||||
expect(text1.trim()).toBe('hello first');
|
||||
|
||||
await fs.writeFile(join(directory, 'public', 'index.html'), 'hello second');
|
||||
|
||||
await sleep(ms('3s'));
|
||||
|
||||
const resp2 = await fetch(`http://localhost:${port}`);
|
||||
const text2 = await resp2.text();
|
||||
expect(text2.trim()).toBe('hello second');
|
||||
} finally {
|
||||
await dev.kill();
|
||||
}
|
||||
});
|
||||
|
||||
test(
|
||||
'[vercel dev] 25-nextjs-src-dir',
|
||||
testFixtureStdio('25-nextjs-src-dir', async (testPath: any) => {
|
||||
@@ -324,117 +143,6 @@ test(
|
||||
})
|
||||
);
|
||||
|
||||
test(
|
||||
'[vercel dev] Use `@vercel/python` with Flask requirements.txt',
|
||||
testFixtureStdio('python-flask', async (testPath: any) => {
|
||||
const name = 'Alice';
|
||||
const year = new Date().getFullYear();
|
||||
await testPath(200, `/api/user?name=${name}`, new RegExp(`Hello ${name}`));
|
||||
await testPath(200, `/api/date`, new RegExp(`Current date is ${year}`));
|
||||
await testPath(200, `/api/date.py`, new RegExp(`Current date is ${year}`));
|
||||
await testPath(200, `/api/headers`, (body: any, res: any) => {
|
||||
// @ts-ignore
|
||||
const { host } = new URL(res.url);
|
||||
expect(body).toBe(host);
|
||||
});
|
||||
})
|
||||
);
|
||||
|
||||
test(
|
||||
'[vercel dev] Use custom runtime from the "functions" property',
|
||||
testFixtureStdio('custom-runtime', async (testPath: any) => {
|
||||
await testPath(200, `/api/user`, /Hello, from Bash!/m);
|
||||
await testPath(200, `/api/user.sh`, /Hello, from Bash!/m);
|
||||
})
|
||||
);
|
||||
|
||||
test(
|
||||
'[vercel dev] Should work with nested `tsconfig.json` files',
|
||||
testFixtureStdio('nested-tsconfig', async (testPath: any) => {
|
||||
await testPath(200, `/`, /Nested tsconfig.json test page/);
|
||||
await testPath(200, `/api`, 'Nested `tsconfig.json` API endpoint');
|
||||
})
|
||||
);
|
||||
|
||||
test(
|
||||
'[vercel dev] Should force `tsc` option "module: commonjs" for `startDevServer()`',
|
||||
testFixtureStdio('force-module-commonjs', async (testPath: any) => {
|
||||
await testPath(200, `/`, /Force "module: commonjs" test page/);
|
||||
await testPath(
|
||||
200,
|
||||
`/api`,
|
||||
'Force "module: commonjs" JavaScript with ES Modules API endpoint'
|
||||
);
|
||||
await testPath(
|
||||
200,
|
||||
`/api/ts`,
|
||||
'Force "module: commonjs" TypeScript API endpoint'
|
||||
);
|
||||
})
|
||||
);
|
||||
|
||||
test(
|
||||
'[vercel dev] should prioritize index.html over other file named index.*',
|
||||
testFixtureStdio('index-html-priority', async (testPath: any) => {
|
||||
await testPath(200, '/', 'This is index.html');
|
||||
await testPath(200, '/index.css', 'This is index.css');
|
||||
})
|
||||
);
|
||||
|
||||
test(
|
||||
'[vercel dev] Should support `*.go` API serverless functions',
|
||||
testFixtureStdio('go', async (testPath: any) => {
|
||||
await testPath(200, `/api`, 'This is the index page');
|
||||
await testPath(200, `/api/index`, 'This is the index page');
|
||||
await testPath(200, `/api/index.go`, 'This is the index page');
|
||||
await testPath(200, `/api/another`, 'This is another page');
|
||||
await testPath(200, '/api/another.go', 'This is another page');
|
||||
await testPath(200, `/api/foo`, 'Req Path: /api/foo');
|
||||
await testPath(200, `/api/bar`, 'Req Path: /api/bar');
|
||||
})
|
||||
);
|
||||
|
||||
test(
|
||||
'[vercel dev] Should set the `ts-node` "target" to match Node.js version',
|
||||
testFixtureStdio('node-ts-node-target', async (testPath: any) => {
|
||||
await testPath(200, `/api/subclass`, '{"ok":true}');
|
||||
await testPath(
|
||||
200,
|
||||
`/api/array`,
|
||||
'{"months":[1,2,3,4,5,6,7,8,9,10,11,12]}'
|
||||
);
|
||||
|
||||
await testPath(200, `/api/dump`, (body: any, res: any, isDev: any) => {
|
||||
// @ts-ignore
|
||||
const { host } = new URL(res.url);
|
||||
const { env, headers } = JSON.parse(body);
|
||||
|
||||
// Test that the API endpoint receives the Vercel proxy request headers
|
||||
expect(headers['x-forwarded-host']).toBe(host);
|
||||
expect(headers['x-vercel-deployment-url']).toBe(host);
|
||||
expect(isIP(headers['x-real-ip'])).toBeTruthy();
|
||||
expect(isIP(headers['x-forwarded-for'])).toBeTruthy();
|
||||
expect(isIP(headers['x-vercel-forwarded-for'])).toBeTruthy();
|
||||
|
||||
// Test that the API endpoint has the Vercel platform env vars defined.
|
||||
expect(env.NOW_REGION).toMatch(/^[a-z]{3}\d$/);
|
||||
if (isDev) {
|
||||
// Only dev is tested because in production these are opt-in.
|
||||
expect(env.VERCEL_URL).toBe(host);
|
||||
expect(env.VERCEL_REGION).toBe('dev1');
|
||||
}
|
||||
});
|
||||
})
|
||||
);
|
||||
|
||||
test(
|
||||
'[vercel dev] Do not fail if `src` is missing',
|
||||
testFixtureStdio('missing-src-property', async (testPath: any) => {
|
||||
await testPath(200, '/', /hello:index.txt/m);
|
||||
await testPath(404, '/i-do-not-exist');
|
||||
})
|
||||
);
|
||||
|
||||
test(
|
||||
'[vercel dev] Middleware that returns a 200 response',
|
||||
testFixtureStdio('middleware-response', async (testPath: any) => {
|
||||
|
||||
449
packages/cli/test/dev/integration-5.test.ts
Normal file
449
packages/cli/test/dev/integration-5.test.ts
Normal file
@@ -0,0 +1,449 @@
|
||||
import { join } from 'path';
|
||||
import ms from 'ms';
|
||||
import fs, { mkdirp } from 'fs-extra';
|
||||
|
||||
const {
|
||||
fetch,
|
||||
fixture,
|
||||
sleep,
|
||||
testFixture,
|
||||
testFixtureStdio,
|
||||
validateResponseHeaders,
|
||||
} = require('./utils.js');
|
||||
|
||||
test(
|
||||
'[vercel dev] temporary directory listing',
|
||||
testFixtureStdio(
|
||||
'temporary-directory-listing',
|
||||
async (_testPath: any, port: any) => {
|
||||
const directory = fixture('temporary-directory-listing');
|
||||
await fs.unlink(join(directory, 'index.txt')).catch(() => null);
|
||||
|
||||
await sleep(ms('20s'));
|
||||
|
||||
const firstResponse = await fetch(`http://localhost:${port}`);
|
||||
validateResponseHeaders(firstResponse);
|
||||
const body = await firstResponse.text();
|
||||
console.log(body);
|
||||
expect(firstResponse.status).toBe(404);
|
||||
|
||||
await fs.writeFile(join(directory, 'index.txt'), 'hello');
|
||||
|
||||
for (let i = 0; i < 20; i++) {
|
||||
const response = await fetch(`http://localhost:${port}`);
|
||||
validateResponseHeaders(response);
|
||||
|
||||
if (response.status === 200) {
|
||||
const body = await response.text();
|
||||
expect(body).toBe('hello');
|
||||
}
|
||||
|
||||
await sleep(ms('1s'));
|
||||
}
|
||||
},
|
||||
{ skipDeploy: true }
|
||||
)
|
||||
);
|
||||
|
||||
test('[vercel dev] add a `package.json` to trigger `@vercel/static-build`', async () => {
|
||||
const directory = fixture('trigger-static-build');
|
||||
|
||||
await fs.unlink(join(directory, 'package.json')).catch(() => null);
|
||||
|
||||
await fs.unlink(join(directory, 'public', 'index.txt')).catch(() => null);
|
||||
|
||||
await fs.rmdir(join(directory, 'public')).catch(() => null);
|
||||
|
||||
const tester = testFixtureStdio(
|
||||
'trigger-static-build',
|
||||
async (_testPath: any, port: any) => {
|
||||
{
|
||||
const response = await fetch(`http://localhost:${port}`);
|
||||
validateResponseHeaders(response);
|
||||
const body = await response.text();
|
||||
expect(body.trim()).toBe('hello:index.txt');
|
||||
}
|
||||
|
||||
const rnd = Math.random().toString();
|
||||
const pkg = {
|
||||
private: true,
|
||||
scripts: { build: `mkdir -p public && echo ${rnd} > public/index.txt` },
|
||||
};
|
||||
|
||||
await fs.writeFile(join(directory, 'package.json'), JSON.stringify(pkg));
|
||||
|
||||
// Wait until file events have been processed
|
||||
await sleep(ms('2s'));
|
||||
|
||||
{
|
||||
const response = await fetch(`http://localhost:${port}`);
|
||||
validateResponseHeaders(response);
|
||||
const body = await response.text();
|
||||
expect(body.trim()).toBe(rnd);
|
||||
}
|
||||
},
|
||||
{ skipDeploy: true }
|
||||
);
|
||||
|
||||
await tester();
|
||||
});
|
||||
|
||||
test('[vercel dev] no build matches warning', async () => {
|
||||
const directory = fixture('no-build-matches');
|
||||
const { dev } = await testFixture(directory, {
|
||||
stdio: ['ignore', 'pipe', 'pipe'],
|
||||
});
|
||||
|
||||
try {
|
||||
// start `vercel dev` detached in child_process
|
||||
dev.unref();
|
||||
|
||||
dev.stderr.setEncoding('utf8');
|
||||
await new Promise<void>(resolve => {
|
||||
dev.stderr.on('data', (str: string) => {
|
||||
if (str.includes('did not match any source files')) {
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
});
|
||||
} finally {
|
||||
await dev.kill();
|
||||
}
|
||||
});
|
||||
|
||||
test(
|
||||
'[vercel dev] do not recursivly check the path',
|
||||
testFixtureStdio('handle-filesystem-missing', async (testPath: any) => {
|
||||
await testPath(200, '/', /hello/m);
|
||||
await testPath(404, '/favicon.txt');
|
||||
})
|
||||
);
|
||||
|
||||
test('[vercel dev] render warning for empty cwd dir', async () => {
|
||||
const directory = fixture('empty');
|
||||
const { dev, port } = await testFixture(directory, {
|
||||
stdio: ['ignore', 'pipe', 'pipe'],
|
||||
});
|
||||
|
||||
try {
|
||||
dev.unref();
|
||||
|
||||
// Monitor `stderr` for the warning
|
||||
dev.stderr.setEncoding('utf8');
|
||||
const msg = 'There are no files inside your deployment.';
|
||||
await new Promise<void>(resolve => {
|
||||
dev.stderr.on('data', (str: string) => {
|
||||
if (str.includes(msg)) {
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Issue a request to ensure a 404 response
|
||||
await sleep(ms('3s'));
|
||||
const response = await fetch(`http://localhost:${port}`);
|
||||
validateResponseHeaders(response);
|
||||
expect(response.status).toBe(404);
|
||||
} finally {
|
||||
await dev.kill();
|
||||
}
|
||||
});
|
||||
|
||||
test('[vercel dev] do not rebuild for changes in the output directory', async () => {
|
||||
const directory = fixture('output-is-source');
|
||||
|
||||
const { dev, port } = await testFixture(directory, {
|
||||
stdio: ['ignore', 'pipe', 'pipe'],
|
||||
});
|
||||
|
||||
try {
|
||||
dev.unref();
|
||||
|
||||
let stderr: any = [];
|
||||
const start = Date.now();
|
||||
|
||||
dev.stderr.on('data', (str: any) => stderr.push(str));
|
||||
|
||||
while (stderr.join('').includes('Ready') === false) {
|
||||
await sleep(ms('3s'));
|
||||
|
||||
if (Date.now() - start > ms('30s')) {
|
||||
console.log('stderr:', stderr.join(''));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
const resp1 = await fetch(`http://localhost:${port}`);
|
||||
const text1 = await resp1.text();
|
||||
expect(text1.trim()).toBe('hello first');
|
||||
|
||||
await fs.writeFile(join(directory, 'public', 'index.html'), 'hello second');
|
||||
|
||||
await sleep(ms('3s'));
|
||||
|
||||
const resp2 = await fetch(`http://localhost:${port}`);
|
||||
const text2 = await resp2.text();
|
||||
expect(text2.trim()).toBe('hello second');
|
||||
} finally {
|
||||
await dev.kill();
|
||||
}
|
||||
});
|
||||
|
||||
test(
|
||||
'[vercel dev] test cleanUrls serve correct content',
|
||||
testFixtureStdio('test-clean-urls', async (testPath: any) => {
|
||||
await testPath(200, '/', 'Index Page');
|
||||
await testPath(200, '/about', 'About Page');
|
||||
await testPath(200, '/sub', 'Sub Index Page');
|
||||
await testPath(200, '/sub/another', 'Sub Another Page');
|
||||
await testPath(200, '/style.css', 'body { color: green }');
|
||||
await testPath(308, '/index.html', 'Redirecting to / (308)', {
|
||||
Location: '/',
|
||||
});
|
||||
await testPath(308, '/about.html', 'Redirecting to /about (308)', {
|
||||
Location: '/about',
|
||||
});
|
||||
await testPath(308, '/sub/index.html', 'Redirecting to /sub (308)', {
|
||||
Location: '/sub',
|
||||
});
|
||||
await testPath(
|
||||
308,
|
||||
'/sub/another.html',
|
||||
'Redirecting to /sub/another (308)',
|
||||
{ Location: '/sub/another' }
|
||||
);
|
||||
})
|
||||
);
|
||||
|
||||
test(
|
||||
'[vercel dev] test cleanUrls serve correct content when using `outputDirectory`',
|
||||
testFixtureStdio(
|
||||
'test-clean-urls-with-output-directory',
|
||||
async (testPath: any) => {
|
||||
await testPath(200, '/', 'Index Page');
|
||||
await testPath(200, '/about', 'About Page');
|
||||
await testPath(200, '/sub', 'Sub Index Page');
|
||||
await testPath(200, '/sub/another', 'Sub Another Page');
|
||||
await testPath(200, '/style.css', 'body { color: green }');
|
||||
await testPath(308, '/index.html', 'Redirecting to / (308)', {
|
||||
Location: '/',
|
||||
});
|
||||
await testPath(308, '/about.html', 'Redirecting to /about (308)', {
|
||||
Location: '/about',
|
||||
});
|
||||
await testPath(308, '/sub/index.html', 'Redirecting to /sub (308)', {
|
||||
Location: '/sub',
|
||||
});
|
||||
await testPath(
|
||||
308,
|
||||
'/sub/another.html',
|
||||
'Redirecting to /sub/another (308)',
|
||||
{ Location: '/sub/another' }
|
||||
);
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
test(
|
||||
'[vercel dev] should serve custom 404 when `cleanUrls: true`',
|
||||
testFixtureStdio('test-clean-urls-custom-404', async (testPath: any) => {
|
||||
await testPath(200, '/', 'This is the home page');
|
||||
await testPath(200, '/about', 'The about page');
|
||||
await testPath(200, '/contact/me', 'Contact Me Subdirectory');
|
||||
await testPath(404, '/nothing', 'Custom 404 Page');
|
||||
await testPath(404, '/nothing/', 'Custom 404 Page');
|
||||
})
|
||||
);
|
||||
|
||||
test(
|
||||
'[vercel dev] test cleanUrls and trailingSlash serve correct content',
|
||||
testFixtureStdio('test-clean-urls-trailing-slash', async (testPath: any) => {
|
||||
await testPath(200, '/', 'Index Page');
|
||||
await testPath(200, '/about/', 'About Page');
|
||||
await testPath(200, '/sub/', 'Sub Index Page');
|
||||
await testPath(200, '/sub/another/', 'Sub Another Page');
|
||||
await testPath(200, '/style.css', 'body { color: green }');
|
||||
//TODO: fix this test so that location is `/` instead of `//`
|
||||
//await testPath(308, '/index.html', 'Redirecting to / (308)', { Location: '/' });
|
||||
await testPath(308, '/about.html', 'Redirecting to /about/ (308)', {
|
||||
Location: '/about/',
|
||||
});
|
||||
await testPath(308, '/sub/index.html', 'Redirecting to /sub/ (308)', {
|
||||
Location: '/sub/',
|
||||
});
|
||||
await testPath(
|
||||
308,
|
||||
'/sub/another.html',
|
||||
'Redirecting to /sub/another/ (308)',
|
||||
{
|
||||
Location: '/sub/another/',
|
||||
}
|
||||
);
|
||||
})
|
||||
);
|
||||
|
||||
test(
|
||||
'[vercel dev] test cors headers work with OPTIONS',
|
||||
testFixtureStdio('test-cors-routes', async (testPath: any) => {
|
||||
const headers = {
|
||||
'Access-Control-Allow-Origin': '*',
|
||||
'Access-Control-Allow-Headers':
|
||||
'Content-Type, Authorization, Accept, Content-Length, Origin, User-Agent',
|
||||
'Access-Control-Allow-Methods':
|
||||
'GET, POST, OPTIONS, HEAD, PATCH, PUT, DELETE',
|
||||
};
|
||||
await testPath(200, '/', 'status api', headers, { method: 'GET' });
|
||||
await testPath(200, '/', 'status api', headers, { method: 'POST' });
|
||||
await testPath(200, '/api/status.js', 'status api', headers, {
|
||||
method: 'GET',
|
||||
});
|
||||
await testPath(200, '/api/status.js', 'status api', headers, {
|
||||
method: 'POST',
|
||||
});
|
||||
await testPath(204, '/', '', headers, { method: 'OPTIONS' });
|
||||
await testPath(204, '/api/status.js', '', headers, { method: 'OPTIONS' });
|
||||
})
|
||||
);
|
||||
|
||||
test(
|
||||
'[vercel dev] test trailingSlash true serve correct content',
|
||||
testFixtureStdio('test-trailing-slash', async (testPath: any) => {
|
||||
await testPath(200, '/', 'Index Page');
|
||||
await testPath(200, '/index.html', 'Index Page');
|
||||
await testPath(200, '/about.html', 'About Page');
|
||||
await testPath(200, '/sub/', 'Sub Index Page');
|
||||
await testPath(200, '/sub/index.html', 'Sub Index Page');
|
||||
await testPath(200, '/sub/another.html', 'Sub Another Page');
|
||||
await testPath(200, '/style.css', 'body { color: green }');
|
||||
await testPath(308, '/about.html/', 'Redirecting to /about.html (308)', {
|
||||
Location: '/about.html',
|
||||
});
|
||||
await testPath(308, '/style.css/', 'Redirecting to /style.css (308)', {
|
||||
Location: '/style.css',
|
||||
});
|
||||
await testPath(308, '/sub', 'Redirecting to /sub/ (308)', {
|
||||
Location: '/sub/',
|
||||
});
|
||||
})
|
||||
);
|
||||
|
||||
test(
|
||||
'[vercel dev] should serve custom 404 when `trailingSlash: true`',
|
||||
testFixtureStdio('test-trailing-slash-custom-404', async (testPath: any) => {
|
||||
await testPath(200, '/', 'This is the home page');
|
||||
await testPath(200, '/about.html', 'The about page');
|
||||
await testPath(200, '/contact/', 'Contact Subdirectory');
|
||||
await testPath(404, '/nothing/', 'Custom 404 Page');
|
||||
})
|
||||
);
|
||||
|
||||
test(
|
||||
'[vercel dev] test trailingSlash false serve correct content',
|
||||
testFixtureStdio('test-trailing-slash-false', async (testPath: any) => {
|
||||
await testPath(200, '/', 'Index Page');
|
||||
await testPath(200, '/index.html', 'Index Page');
|
||||
await testPath(200, '/about.html', 'About Page');
|
||||
await testPath(200, '/sub', 'Sub Index Page');
|
||||
await testPath(200, '/sub/index.html', 'Sub Index Page');
|
||||
await testPath(200, '/sub/another.html', 'Sub Another Page');
|
||||
await testPath(200, '/style.css', 'body { color: green }');
|
||||
await testPath(308, '/about.html/', 'Redirecting to /about.html (308)', {
|
||||
Location: '/about.html',
|
||||
});
|
||||
await testPath(308, '/sub/', 'Redirecting to /sub (308)', {
|
||||
Location: '/sub',
|
||||
});
|
||||
await testPath(
|
||||
308,
|
||||
'/sub/another.html/',
|
||||
'Redirecting to /sub/another.html (308)',
|
||||
{
|
||||
Location: '/sub/another.html',
|
||||
}
|
||||
);
|
||||
})
|
||||
);
|
||||
|
||||
test(
|
||||
'[vercel dev] throw when invalid builder routes detected',
|
||||
testFixtureStdio(
|
||||
'invalid-builder-routes',
|
||||
async (testPath: any) => {
|
||||
await testPath(
|
||||
500,
|
||||
'/',
|
||||
/Route at index 0 has invalid `src` regular expression/m
|
||||
);
|
||||
},
|
||||
{ skipDeploy: true }
|
||||
)
|
||||
);
|
||||
|
||||
test(
|
||||
'[vercel dev] support legacy `@now` scope runtimes',
|
||||
testFixtureStdio('legacy-now-runtime', async (testPath: any) => {
|
||||
await testPath(200, '/', /A simple deployment with the Vercel API!/m);
|
||||
})
|
||||
);
|
||||
|
||||
test(
|
||||
'[vercel dev] 00-list-directory',
|
||||
testFixtureStdio(
|
||||
'00-list-directory',
|
||||
async (testPath: any) => {
|
||||
await testPath(200, '/', /Files within/m);
|
||||
await testPath(200, '/', /test[0-3]\.txt/m);
|
||||
await testPath(200, '/', /\.well-known/m);
|
||||
await testPath(200, '/.well-known/keybase.txt', 'proof goes here');
|
||||
},
|
||||
{ projectSettings: { directoryListing: true } }
|
||||
)
|
||||
);
|
||||
|
||||
test(
|
||||
'[vercel dev] 01-node',
|
||||
testFixtureStdio('01-node', async (testPath: any) => {
|
||||
await testPath(200, '/', /A simple deployment with the Vercel API!/m);
|
||||
})
|
||||
);
|
||||
|
||||
test(
|
||||
'[vercel dev] add a `api/fn.ts` when `api` does not exist at startup`',
|
||||
testFixtureStdio('no-api', async (_testPath: any, port: any) => {
|
||||
const directory = fixture('no-api');
|
||||
const apiDir = join(directory, 'api');
|
||||
|
||||
try {
|
||||
{
|
||||
const response = await fetch(`http://localhost:${port}/api/new-file`);
|
||||
validateResponseHeaders(response);
|
||||
expect(response.status).toBe(404);
|
||||
}
|
||||
|
||||
const fileContents = `
|
||||
export const config = {
|
||||
runtime: 'edge'
|
||||
}
|
||||
|
||||
export default async function edge(request, event) {
|
||||
return new Response('from new file');
|
||||
}
|
||||
`;
|
||||
|
||||
await mkdirp(apiDir);
|
||||
await fs.writeFile(join(apiDir, 'new-file.js'), fileContents);
|
||||
|
||||
// Wait until file events have been processed
|
||||
await sleep(ms('1s'));
|
||||
|
||||
{
|
||||
const response = await fetch(`http://localhost:${port}/api/new-file`);
|
||||
validateResponseHeaders(response);
|
||||
const body = await response.text();
|
||||
expect(body.trim()).toBe('from new file');
|
||||
}
|
||||
} finally {
|
||||
await fs.remove(apiDir);
|
||||
}
|
||||
})
|
||||
);
|
||||
5
packages/cli/test/fixtures/package.json
vendored
Normal file
5
packages/cli/test/fixtures/package.json
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"private": true,
|
||||
"name": "cli-test-fixtures",
|
||||
"description": "We created package.json here to avoid reading the monorepo package.json during testing"
|
||||
}
|
||||
1
packages/cli/test/fixtures/unit/commands/build/now-node-server/.gitignore
vendored
Normal file
1
packages/cli/test/fixtures/unit/commands/build/now-node-server/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
.vercel/builders
|
||||
7
packages/cli/test/fixtures/unit/commands/build/now-node-server/.vercel/project.json
vendored
Normal file
7
packages/cli/test/fixtures/unit/commands/build/now-node-server/.vercel/project.json
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"orgId": ".",
|
||||
"projectId": ".",
|
||||
"settings": {
|
||||
"framework": null
|
||||
}
|
||||
}
|
||||
4
packages/cli/test/fixtures/unit/commands/build/now-node-server/server.js
vendored
Normal file
4
packages/cli/test/fixtures/unit/commands/build/now-node-server/server.js
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
const { createServer } = require('http');
|
||||
const handler = (_req, res) => res.end('hi');
|
||||
const server = createServer(handler);
|
||||
module.exports = server;
|
||||
4
packages/cli/test/fixtures/unit/commands/build/now-node-server/vercel.json
vendored
Normal file
4
packages/cli/test/fixtures/unit/commands/build/now-node-server/vercel.json
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"builds": [{ "src": "server.js", "use": "@now/node-server" }],
|
||||
"routes": [{ "src": "/(.*)", "dest": "/server.js" }]
|
||||
}
|
||||
@@ -10,7 +10,7 @@ describe('alias', () => {
|
||||
client.setArgv('alias', 'ls');
|
||||
const exitCodePromise = alias(client);
|
||||
await expect(exitCodePromise).resolves.toEqual(0);
|
||||
await expect(client.stderr).toOutput('dummy-19.app');
|
||||
await expect(client.stdout).toOutput('dummy-19.app');
|
||||
});
|
||||
|
||||
it('should list up to 2 aliases', async () => {
|
||||
@@ -19,6 +19,6 @@ describe('alias', () => {
|
||||
client.setArgv('alias', 'ls', '--limit', '2');
|
||||
const exitCodePromise = alias(client);
|
||||
await expect(exitCodePromise).resolves.toEqual(0);
|
||||
await expect(client.stderr).toOutput('dummy-1.app');
|
||||
await expect(client.stdout).toOutput('dummy-1.app');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -834,6 +834,42 @@ describe('build', () => {
|
||||
}
|
||||
});
|
||||
|
||||
/* Skipping because this legacy builder is causing something to break with cwd
|
||||
it('should error when builder returns result without "output" such as @now/node-server', async () => {
|
||||
const cwd = join(os.tmpdir(), 'now-node-server');
|
||||
const output = join(cwd, '.vercel/output');
|
||||
try {
|
||||
// Copy to a temp directory to avoid breaking other tests
|
||||
await fs.copy(fixture('now-node-server'), cwd);
|
||||
process.chdir(cwd);
|
||||
const exitCode = await build(client);
|
||||
expect(exitCode).toEqual(1);
|
||||
|
||||
// Error gets printed to the terminal
|
||||
const message =
|
||||
'The build result from "@now/node-server" is missing the "output" property. Please update from "@now" to "@vercel" in your `vercel.json` file.';
|
||||
await expect(client.stderr).toOutput(message);
|
||||
|
||||
const builds = await fs.readJSON(join(output, 'builds.json'));
|
||||
|
||||
// top level "error" also contains the same error
|
||||
expect(builds.error).toEqual({
|
||||
name: 'Error',
|
||||
message,
|
||||
stack: expect.stringContaining(message),
|
||||
});
|
||||
|
||||
// `config.json` contains `version`
|
||||
const configJson = await fs.readJSON(join(output, 'config.json'));
|
||||
expect(configJson.version).toBe(3);
|
||||
} finally {
|
||||
await fs.remove(cwd);
|
||||
process.chdir(originalCwd);
|
||||
delete process.env.__VERCEL_BUILD_RUNNING;
|
||||
}
|
||||
});
|
||||
*/
|
||||
|
||||
it('should allow for missing "build" script', async () => {
|
||||
const cwd = fixture('static-with-pkg');
|
||||
const output = join(cwd, '.vercel/output');
|
||||
|
||||
@@ -9,7 +9,7 @@ describe('certs', () => {
|
||||
useCert();
|
||||
client.setArgv('certs', 'ls');
|
||||
const exitCodePromise = certs(client);
|
||||
await expect(client.stderr).toOutput('dummy-19.cert');
|
||||
await expect(client.stdout).toOutput('dummy-19.cert');
|
||||
await expect(exitCodePromise).resolves.toEqual(0);
|
||||
});
|
||||
|
||||
@@ -18,7 +18,7 @@ describe('certs', () => {
|
||||
useCert();
|
||||
client.setArgv('certs', 'ls', '--limit', '2');
|
||||
const exitCodePromise = certs(client);
|
||||
await expect(client.stderr).toOutput('dummy-1.cert');
|
||||
await expect(client.stdout).toOutput('dummy-1.cert');
|
||||
await expect(exitCodePromise).resolves.toEqual(0);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vercel/client",
|
||||
"version": "12.2.27",
|
||||
"version": "12.3.2",
|
||||
"main": "dist/index.js",
|
||||
"typings": "dist/index.d.ts",
|
||||
"homepage": "https://vercel.com",
|
||||
@@ -23,7 +23,7 @@
|
||||
"node": ">= 14"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/async-retry": "1.4.1",
|
||||
"@types/async-retry": "1.4.5",
|
||||
"@types/fs-extra": "7.0.0",
|
||||
"@types/jest": "27.4.1",
|
||||
"@types/minimatch": "3.0.5",
|
||||
@@ -43,8 +43,8 @@
|
||||
]
|
||||
},
|
||||
"dependencies": {
|
||||
"@vercel/build-utils": "workspace:5.7.6",
|
||||
"@vercel/routing-utils": "workspace:2.1.4",
|
||||
"@vercel/build-utils": "5.9.0",
|
||||
"@vercel/routing-utils": "2.1.8",
|
||||
"@zeit/fetch": "5.2.0",
|
||||
"async-retry": "1.2.3",
|
||||
"async-sema": "3.0.0",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import sleep from 'sleep-promise';
|
||||
import ms from 'ms';
|
||||
import { fetch, getApiDeploymentsUrl } from './utils';
|
||||
import { getPollingDelay } from './utils/get-polling-delay';
|
||||
import {
|
||||
isDone,
|
||||
isReady,
|
||||
@@ -47,6 +47,7 @@ export async function* checkDeploymentStatus(
|
||||
// Build polling
|
||||
debug('Waiting for builds and the deployment to complete...');
|
||||
const finishedEvents = new Set();
|
||||
const startTime = Date.now();
|
||||
|
||||
while (true) {
|
||||
// Deployment polling
|
||||
@@ -155,6 +156,8 @@ export async function* checkDeploymentStatus(
|
||||
};
|
||||
}
|
||||
|
||||
await sleep(ms('1.5s'));
|
||||
const elapsed = Date.now() - startTime;
|
||||
const duration = getPollingDelay(elapsed);
|
||||
await sleep(duration);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -73,7 +73,7 @@ export default function buildCreateDeployment() {
|
||||
debug(`Provided 'path' is a single file`);
|
||||
}
|
||||
|
||||
let { fileList } = await buildFileTree(path, clientOptions, debug);
|
||||
const { fileList } = await buildFileTree(path, clientOptions, debug);
|
||||
|
||||
// This is a useful warning because it prevents people
|
||||
// from getting confused about a deployment that renders 404.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { DeploymentFile } from './utils/hashes';
|
||||
import { FilesMap } from './utils/hashes';
|
||||
import { generateQueryString } from './utils/query-string';
|
||||
import { isReady, isAliasAssigned } from './utils/ready-state';
|
||||
import { checkDeploymentStatus } from './check-deployment-status';
|
||||
@@ -16,7 +16,7 @@ import {
|
||||
} from './types';
|
||||
|
||||
async function* postDeployment(
|
||||
files: Map<string, DeploymentFile>,
|
||||
files: FilesMap,
|
||||
clientOptions: VercelClientOptions,
|
||||
deploymentOptions: DeploymentOptions
|
||||
): AsyncIterableIterator<{
|
||||
@@ -90,7 +90,7 @@ async function* postDeployment(
|
||||
}
|
||||
|
||||
function getDefaultName(
|
||||
files: Map<string, DeploymentFile>,
|
||||
files: FilesMap,
|
||||
clientOptions: VercelClientOptions
|
||||
): string {
|
||||
const debug = createDebug(clientOptions.debug);
|
||||
@@ -109,7 +109,7 @@ function getDefaultName(
|
||||
}
|
||||
|
||||
export async function* deploy(
|
||||
files: Map<string, DeploymentFile>,
|
||||
files: FilesMap,
|
||||
clientOptions: VercelClientOptions,
|
||||
deploymentOptions: DeploymentOptions
|
||||
): AsyncIterableIterator<{ type: string; payload: any }> {
|
||||
|
||||
@@ -5,7 +5,7 @@ import { EventEmitter } from 'events';
|
||||
import retry from 'async-retry';
|
||||
import { Sema } from 'async-sema';
|
||||
|
||||
import { DeploymentFile } from './utils/hashes';
|
||||
import { DeploymentFile, FilesMap } from './utils/hashes';
|
||||
import { fetch, API_FILES, createDebug } from './utils';
|
||||
import { DeploymentError } from './errors';
|
||||
import { deploy } from './deploy';
|
||||
@@ -29,7 +29,7 @@ const isClientNetworkError = (err: Error) => {
|
||||
};
|
||||
|
||||
export async function* upload(
|
||||
files: Map<string, DeploymentFile>,
|
||||
files: FilesMap,
|
||||
clientOptions: VercelClientOptions,
|
||||
deploymentOptions: DeploymentOptions
|
||||
): AsyncIterableIterator<any> {
|
||||
@@ -98,6 +98,10 @@ export async function* upload(
|
||||
await semaphore.acquire();
|
||||
|
||||
const { data } = file;
|
||||
if (typeof data === 'undefined') {
|
||||
// Directories don't need to be uploaded
|
||||
return;
|
||||
}
|
||||
|
||||
uploadProgress.bytesUploaded = 0;
|
||||
|
||||
|
||||
14
packages/client/src/utils/get-polling-delay.ts
Normal file
14
packages/client/src/utils/get-polling-delay.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
import ms from 'ms';
|
||||
|
||||
export function getPollingDelay(elapsed: number): number {
|
||||
if (elapsed <= ms('15s')) {
|
||||
return ms('1s');
|
||||
}
|
||||
if (elapsed <= ms('1m')) {
|
||||
return ms('5s');
|
||||
}
|
||||
if (elapsed <= ms('5m')) {
|
||||
return ms('15s');
|
||||
}
|
||||
return ms('30s');
|
||||
}
|
||||
@@ -4,10 +4,12 @@ import { Sema } from 'async-sema';
|
||||
|
||||
export interface DeploymentFile {
|
||||
names: string[];
|
||||
data: Buffer;
|
||||
data?: Buffer;
|
||||
mode: number;
|
||||
}
|
||||
|
||||
export type FilesMap = Map<string | undefined, DeploymentFile>;
|
||||
|
||||
/**
|
||||
* Computes a hash for the given buf.
|
||||
*
|
||||
@@ -23,14 +25,12 @@ export function hash(buf: Buffer): string {
|
||||
* @param map with hashed files
|
||||
* @return {object}
|
||||
*/
|
||||
export const mapToObject = (
|
||||
map: Map<string, DeploymentFile>
|
||||
): { [key: string]: DeploymentFile } => {
|
||||
export const mapToObject = (map: FilesMap): Record<string, DeploymentFile> => {
|
||||
const obj: { [key: string]: DeploymentFile } = {};
|
||||
for (const [key, value] of map) {
|
||||
if (typeof key === 'undefined') continue;
|
||||
obj[key] = value;
|
||||
}
|
||||
|
||||
return obj;
|
||||
};
|
||||
|
||||
@@ -43,8 +43,8 @@ export const mapToObject = (
|
||||
*/
|
||||
export async function hashes(
|
||||
files: string[],
|
||||
map = new Map<string, DeploymentFile>()
|
||||
): Promise<Map<string, DeploymentFile>> {
|
||||
map = new Map<string | undefined, DeploymentFile>()
|
||||
): Promise<FilesMap> {
|
||||
const semaphore = new Sema(100);
|
||||
|
||||
await Promise.all(
|
||||
@@ -54,15 +54,21 @@ export async function hashes(
|
||||
const stat = await fs.lstat(name);
|
||||
const mode = stat.mode;
|
||||
|
||||
let data: Buffer | null = null;
|
||||
if (stat.isSymbolicLink()) {
|
||||
const link = await fs.readlink(name);
|
||||
data = Buffer.from(link, 'utf8');
|
||||
} else {
|
||||
data = await fs.readFile(name);
|
||||
let data: Buffer | undefined;
|
||||
const isDirectory = stat.isDirectory();
|
||||
|
||||
let h: string | undefined;
|
||||
|
||||
if (!isDirectory) {
|
||||
if (stat.isSymbolicLink()) {
|
||||
const link = await fs.readlink(name);
|
||||
data = Buffer.from(link, 'utf8');
|
||||
} else {
|
||||
data = await fs.readFile(name);
|
||||
}
|
||||
h = hash(data);
|
||||
}
|
||||
|
||||
const h = hash(data);
|
||||
const entry = map.get(h);
|
||||
|
||||
if (entry) {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { DeploymentFile } from './hashes';
|
||||
import { FilesMap } from './hashes';
|
||||
import { FetchOptions } from '@zeit/fetch';
|
||||
import { nodeFetch, zeitFetch } from './fetch';
|
||||
import { join, sep, relative } from 'path';
|
||||
@@ -256,15 +256,15 @@ export const fetch = async (
|
||||
|
||||
export interface PreparedFile {
|
||||
file: string;
|
||||
sha: string;
|
||||
size: number;
|
||||
sha?: string;
|
||||
size?: number;
|
||||
mode: number;
|
||||
}
|
||||
|
||||
const isWin = process.platform.includes('win');
|
||||
|
||||
export const prepareFiles = (
|
||||
files: Map<string, DeploymentFile>,
|
||||
files: FilesMap,
|
||||
clientOptions: VercelClientOptions
|
||||
): PreparedFile[] => {
|
||||
const preparedFiles: PreparedFile[] = [];
|
||||
@@ -286,9 +286,9 @@ export const prepareFiles = (
|
||||
|
||||
preparedFiles.push({
|
||||
file: isWin ? fileName.replace(/\\/g, '/') : fileName,
|
||||
size: file.data.byteLength || file.data.length,
|
||||
size: file.data?.byteLength || file.data?.length,
|
||||
mode: file.mode,
|
||||
sha,
|
||||
sha: sha || undefined,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -87,6 +87,10 @@ export default function readdir(
|
||||
if (stats.isDirectory()) {
|
||||
readdir(filePath, ignores)
|
||||
.then(function (res) {
|
||||
if (res.length === 0) {
|
||||
// Empty directories get returned
|
||||
list.push(filePath);
|
||||
}
|
||||
list = list.concat(res);
|
||||
pending -= 1;
|
||||
if (!pending) {
|
||||
|
||||
41
packages/client/tests/unit.get-polling-delay.test.ts
Normal file
41
packages/client/tests/unit.get-polling-delay.test.ts
Normal file
@@ -0,0 +1,41 @@
|
||||
import { getPollingDelay } from '../src/utils/get-polling-delay';
|
||||
|
||||
describe('getPollingDelay()', () => {
|
||||
it('should return 1 second', async () => {
|
||||
expect(getPollingDelay(0)).toBe(1000);
|
||||
expect(getPollingDelay(1000)).toBe(1000);
|
||||
expect(getPollingDelay(3000)).toBe(1000);
|
||||
expect(getPollingDelay(5000)).toBe(1000);
|
||||
expect(getPollingDelay(8000)).toBe(1000);
|
||||
expect(getPollingDelay(9000)).toBe(1000);
|
||||
expect(getPollingDelay(10000)).toBe(1000);
|
||||
expect(getPollingDelay(13000)).toBe(1000);
|
||||
expect(getPollingDelay(15000)).toBe(1000);
|
||||
});
|
||||
|
||||
it('should return 5 second', async () => {
|
||||
expect(getPollingDelay(15001)).toBe(5000);
|
||||
expect(getPollingDelay(16000)).toBe(5000);
|
||||
expect(getPollingDelay(23000)).toBe(5000);
|
||||
expect(getPollingDelay(36000)).toBe(5000);
|
||||
expect(getPollingDelay(59000)).toBe(5000);
|
||||
expect(getPollingDelay(60000)).toBe(5000);
|
||||
});
|
||||
|
||||
it('should return 15 second', async () => {
|
||||
expect(getPollingDelay(60001)).toBe(15000);
|
||||
expect(getPollingDelay(80000)).toBe(15000);
|
||||
expect(getPollingDelay(100000)).toBe(15000);
|
||||
expect(getPollingDelay(200000)).toBe(15000);
|
||||
expect(getPollingDelay(250000)).toBe(15000);
|
||||
expect(getPollingDelay(300000)).toBe(15000);
|
||||
});
|
||||
|
||||
it('should return 30 second', async () => {
|
||||
expect(getPollingDelay(300001)).toBe(30000);
|
||||
expect(getPollingDelay(400000)).toBe(30000);
|
||||
expect(getPollingDelay(1400000)).toBe(30000);
|
||||
expect(getPollingDelay(9400000)).toBe(30000);
|
||||
expect(getPollingDelay(99400000)).toBe(30000);
|
||||
});
|
||||
});
|
||||
@@ -24,7 +24,11 @@ describe('buildFileTree()', () => {
|
||||
noop
|
||||
);
|
||||
|
||||
const expectedFileList = toAbsolutePaths(cwd, ['.nowignore', 'index.txt']);
|
||||
const expectedFileList = toAbsolutePaths(cwd, [
|
||||
'.nowignore',
|
||||
'folder',
|
||||
'index.txt',
|
||||
]);
|
||||
expect(normalizeWindowsPaths(expectedFileList).sort()).toEqual(
|
||||
normalizeWindowsPaths(fileList).sort()
|
||||
);
|
||||
@@ -41,9 +45,14 @@ describe('buildFileTree()', () => {
|
||||
|
||||
it('should include symlinked files and directories', async () => {
|
||||
const cwd = fixture('symlinks');
|
||||
|
||||
// Also add an empty directory to make sure it's included
|
||||
await fs.mkdirp(join(cwd, 'empty'));
|
||||
|
||||
const { fileList } = await buildFileTree(cwd, { isDirectory: true }, noop);
|
||||
|
||||
const expectedFileList = toAbsolutePaths(cwd, [
|
||||
'empty',
|
||||
'folder-link',
|
||||
'folder/text.txt',
|
||||
'index.txt',
|
||||
|
||||
@@ -12,7 +12,8 @@
|
||||
"noUnusedLocals": true,
|
||||
"noUnusedParameters": true,
|
||||
"strict": true,
|
||||
"target": "ES2020"
|
||||
"target": "ES2020",
|
||||
"skipLibCheck": true
|
||||
},
|
||||
"include": ["./src"]
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vercel/edge",
|
||||
"version": "0.2.2",
|
||||
"version": "0.2.6",
|
||||
"license": "MIT",
|
||||
"main": "dist/index.js",
|
||||
"module": "dist/index.mjs",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vercel/error-utils",
|
||||
"version": "1.0.4",
|
||||
"version": "1.0.8",
|
||||
"description": "A collection of error utilities for vercel/vercel",
|
||||
"main": "dist/index.js",
|
||||
"types": "dist/index.d.ts",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vercel/frameworks",
|
||||
"version": "1.2.0",
|
||||
"version": "1.2.4",
|
||||
"main": "./dist/frameworks.js",
|
||||
"types": "./dist/frameworks.d.ts",
|
||||
"files": [
|
||||
@@ -21,7 +21,7 @@
|
||||
"@types/js-yaml": "3.12.1",
|
||||
"@types/node": "14.18.33",
|
||||
"@types/node-fetch": "2.5.8",
|
||||
"@vercel/routing-utils": "workspace:2.1.4",
|
||||
"@vercel/routing-utils": "2.1.8",
|
||||
"ajv": "6.12.2",
|
||||
"typescript": "4.3.4"
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vercel/fs-detectors",
|
||||
"version": "3.7.0",
|
||||
"version": "3.7.5",
|
||||
"description": "Vercel filesystem detectors",
|
||||
"main": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts",
|
||||
@@ -19,9 +19,9 @@
|
||||
"test-unit": "pnpm test"
|
||||
},
|
||||
"dependencies": {
|
||||
"@vercel/error-utils": "workspace:1.0.4",
|
||||
"@vercel/frameworks": "workspace:1.2.0",
|
||||
"@vercel/routing-utils": "workspace:2.1.4",
|
||||
"@vercel/error-utils": "1.0.8",
|
||||
"@vercel/frameworks": "1.2.4",
|
||||
"@vercel/routing-utils": "2.1.8",
|
||||
"glob": "8.0.3",
|
||||
"js-yaml": "4.1.0",
|
||||
"json5": "2.2.2",
|
||||
@@ -35,7 +35,7 @@
|
||||
"@types/minimatch": "3.0.5",
|
||||
"@types/node": "14.18.33",
|
||||
"@types/semver": "7.3.10",
|
||||
"@vercel/build-utils": "workspace:5.7.6",
|
||||
"@vercel/build-utils": "5.9.0",
|
||||
"typescript": "4.3.4"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,2 +1,4 @@
|
||||
node_modules
|
||||
.DS_Store
|
||||
gatsby-browser.js
|
||||
web-vitals.js
|
||||
|
||||
@@ -4,10 +4,6 @@
|
||||
|
||||
⚠️ This repo was migrated from https://github.com/vercel/gatsby-plugin-vercel
|
||||
|
||||
It requires Node.js v12 to be built, so the latest, built version of `gatsby-plugin-vercel` has been committed to this repo _temporarily_ so that they can be included in the initial v1 publish of `@vercel/gatsby-plugin-vercel-analytics`.
|
||||
|
||||
At some point in the future, this plugin will be updated to Node.js v16 so that it can be included in the rest of this monorepo's build tooling.
|
||||
|
||||
---
|
||||
|
||||
This plugin sends [Core Web Vitals](https://web.dev/vitals/) to Vercel Analytics. This plugin is configured by default on Vercel. You **do not** need to install it manually. For more information, [read this post](https://vercel.com/blog/gatsby-analytics).
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
"use strict";
|
||||
|
||||
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
|
||||
|
||||
exports.__esModule = true;
|
||||
exports.onClientEntry = void 0;
|
||||
|
||||
var _extends2 = _interopRequireDefault(require("@babel/runtime/helpers/extends"));
|
||||
|
||||
var _webVitals = require("./web-vitals");
|
||||
|
||||
var onClientEntry = function onClientEntry(_, pluginOptions) {
|
||||
if (pluginOptions === void 0) {
|
||||
pluginOptions = {};
|
||||
}
|
||||
|
||||
var options = (0, _extends2.default)({
|
||||
debug: false
|
||||
}, pluginOptions, {
|
||||
analyticsId: process.env.GATSBY_VERCEL_ANALYTICS_ID
|
||||
});
|
||||
|
||||
if (!options.analyticsId) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (options.debug || process.env.NODE_ENV === "production") {
|
||||
(0, _webVitals.webVitals)({
|
||||
options: options
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
exports.onClientEntry = onClientEntry;
|
||||
@@ -1,13 +1,15 @@
|
||||
{
|
||||
"name": "@vercel/gatsby-plugin-vercel-analytics",
|
||||
"version": "1.0.2",
|
||||
"version": "1.0.7",
|
||||
"description": "Track Core Web Vitals in Gatsby projects with Vercel Analytics.",
|
||||
"main": "index.js",
|
||||
"files": [
|
||||
"gatsby-browser.js",
|
||||
"web-vitals.js"
|
||||
],
|
||||
"scripts": {},
|
||||
"scripts": {
|
||||
"build": "babel src --out-dir ."
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/runtime": "7.12.1",
|
||||
"web-vitals": "0.2.4"
|
||||
@@ -29,5 +31,9 @@
|
||||
"lcp",
|
||||
"ttfb"
|
||||
],
|
||||
"license": "MIT"
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"@babel/cli": "7.20.7",
|
||||
"@babel/core": "7.5.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,115 +0,0 @@
|
||||
"use strict";
|
||||
|
||||
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
|
||||
|
||||
exports.__esModule = true;
|
||||
exports.webVitals = webVitals;
|
||||
|
||||
var _regenerator = _interopRequireDefault(require("@babel/runtime/regenerator"));
|
||||
|
||||
var _asyncToGenerator2 = _interopRequireDefault(require("@babel/runtime/helpers/asyncToGenerator"));
|
||||
|
||||
var _webVitals2 = require("web-vitals");
|
||||
|
||||
var isRegistered = false;
|
||||
|
||||
function onError(err) {
|
||||
console.error("[gatsby-plugin-vercel]", err); // eslint-disable-line no-console
|
||||
}
|
||||
|
||||
function onDebug(label, payload) {
|
||||
console.log(label, payload); // eslint-disable-line no-console
|
||||
}
|
||||
|
||||
function sendToAnalytics(metric, options) {
|
||||
var _$exec, _document$querySelect;
|
||||
|
||||
// Scrape the intial component name from the DOM:
|
||||
var pageScript = [].slice.call((_$exec = /^\/component---(.+)\-(.+?)\-.{20}\.js$/.exec((_document$querySelect = document.querySelector("script[src^=\"/component---\"]")) === null || _document$querySelect === void 0 ? void 0 : _document$querySelect.getAttribute("src"))) !== null && _$exec !== void 0 ? _$exec : []).slice(1).join("-");
|
||||
var chunkMapping = self.___chunkMapping ? typeof self.___chunkMapping === "string" ? JSON.parse(self.___chunkMapping) : self.___chunkMapping : {}; // Verify page name is correct:
|
||||
|
||||
var pageName = "component---" + pageScript in chunkMapping ? pageScript : null;
|
||||
|
||||
if (options.debug && !pageName) {
|
||||
onDebug("[gatsby-plugin-vercel]", "Unable to detect Page Name, skipping reporting.");
|
||||
}
|
||||
|
||||
var body = {
|
||||
dsn: options.analyticsId,
|
||||
id: metric.id,
|
||||
page: pageName !== null && pageName !== void 0 ? pageName : "",
|
||||
href: location.href,
|
||||
event_name: metric.name,
|
||||
value: metric.value.toString(),
|
||||
speed: "connection" in navigator && navigator["connection"] && "effectiveType" in navigator["connection"] ? navigator["connection"]["effectiveType"] : ""
|
||||
};
|
||||
|
||||
if (options.debug) {
|
||||
onDebug(metric.name, JSON.stringify(body, null, 2));
|
||||
}
|
||||
|
||||
var blob = new Blob([new URLSearchParams(body).toString()], {
|
||||
// This content type is necessary for `sendBeacon`:
|
||||
type: "application/x-www-form-urlencoded"
|
||||
});
|
||||
var vitalsUrl = "https://vitals.vercel-analytics.com/v1/vitals";
|
||||
navigator.sendBeacon && navigator.sendBeacon(vitalsUrl, blob) || fetch(vitalsUrl, {
|
||||
body: blob,
|
||||
method: "POST",
|
||||
credentials: "omit",
|
||||
keepalive: true
|
||||
});
|
||||
}
|
||||
|
||||
function webVitals(_x) {
|
||||
return _webVitals.apply(this, arguments);
|
||||
}
|
||||
|
||||
function _webVitals() {
|
||||
_webVitals = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee(_ref) {
|
||||
var options;
|
||||
return _regenerator.default.wrap(function _callee$(_context) {
|
||||
while (1) {
|
||||
switch (_context.prev = _context.next) {
|
||||
case 0:
|
||||
options = _ref.options;
|
||||
|
||||
if (!isRegistered) {
|
||||
_context.next = 3;
|
||||
break;
|
||||
}
|
||||
|
||||
return _context.abrupt("return");
|
||||
|
||||
case 3:
|
||||
isRegistered = true;
|
||||
|
||||
try {
|
||||
(0, _webVitals2.getFID)(function (metric) {
|
||||
return sendToAnalytics(metric, options);
|
||||
});
|
||||
(0, _webVitals2.getTTFB)(function (metric) {
|
||||
return sendToAnalytics(metric, options);
|
||||
});
|
||||
(0, _webVitals2.getLCP)(function (metric) {
|
||||
return sendToAnalytics(metric, options);
|
||||
});
|
||||
(0, _webVitals2.getCLS)(function (metric) {
|
||||
return sendToAnalytics(metric, options);
|
||||
});
|
||||
(0, _webVitals2.getFCP)(function (metric) {
|
||||
return sendToAnalytics(metric, options);
|
||||
});
|
||||
} catch (err) {
|
||||
onError(err);
|
||||
}
|
||||
|
||||
case 5:
|
||||
case "end":
|
||||
return _context.stop();
|
||||
}
|
||||
}
|
||||
}, _callee);
|
||||
}));
|
||||
return _webVitals.apply(this, arguments);
|
||||
}
|
||||
4
packages/gatsby-plugin-vercel-builder/.gitignore
vendored
Normal file
4
packages/gatsby-plugin-vercel-builder/.gitignore
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
gatsby-node.*
|
||||
!gatsby-node.ts
|
||||
|
||||
dist
|
||||
20
packages/gatsby-plugin-vercel-builder/README.md
Normal file
20
packages/gatsby-plugin-vercel-builder/README.md
Normal file
@@ -0,0 +1,20 @@
|
||||
# @vercel/gatsby-plugin-vercel-builder
|
||||
|
||||
This plugin generates [Vercel Build Output API v3](https://vercel.com/docs/build-output-api/v3) for Gatsby v4+ projects.
|
||||
|
||||
The Vercel platform automatically injects this plugin for you if it can detect Gatsby v4+ in your project's `package.json` dependencies. If detected, you will see a log message in your project's [build logs](https://vercel.com/docs/concepts/deployments/logs#build-logs) as follows:
|
||||
|
||||
> Injecting Gatsby.js plugin "@vercel/gatsby-plugin-vercel-builder" to package.json
|
||||
|
||||
If auto-detection is not working, this plugin can also be installed and used manually:
|
||||
|
||||
1. `npm install @vercel/gatsby-plugin-vercel-builder`
|
||||
2. Add `'@vercel/gatsby-plugin-vercel-builder'` to your `gatsby-config.(t|j)s` file, such as:
|
||||
|
||||
```js
|
||||
module.exports = {
|
||||
plugins: ['@vercel/gatsby-plugin-vercel-builder'],
|
||||
};
|
||||
```
|
||||
|
||||
3. 🚀 Ship It 🎉
|
||||
28
packages/gatsby-plugin-vercel-builder/gatsby-node.ts
Normal file
28
packages/gatsby-plugin-vercel-builder/gatsby-node.ts
Normal file
@@ -0,0 +1,28 @@
|
||||
import path from 'path';
|
||||
|
||||
import type { GatsbyNode } from 'gatsby';
|
||||
|
||||
// this gets built separately, so import from "dist" instead of "src"
|
||||
import { generateVercelBuildOutputAPI3Output } from './dist';
|
||||
|
||||
export const pluginOptionsSchema: GatsbyNode['pluginOptionsSchema'] = ({
|
||||
Joi,
|
||||
}) => {
|
||||
return Joi.object({
|
||||
exportPath: Joi.string().optional(),
|
||||
});
|
||||
};
|
||||
|
||||
export const onPostBuild: GatsbyNode['onPostBuild'] = async (
|
||||
{ store },
|
||||
pluginOptions
|
||||
) => {
|
||||
// validated by `pluginOptionSchema`
|
||||
const exportPath = (pluginOptions?.exportPath ??
|
||||
path.join('.vercel', 'output', 'config.json')) as string;
|
||||
|
||||
await generateVercelBuildOutputAPI3Output({
|
||||
exportPath,
|
||||
gatsbyStoreState: store.getState(),
|
||||
});
|
||||
};
|
||||
33
packages/gatsby-plugin-vercel-builder/package.json
Normal file
33
packages/gatsby-plugin-vercel-builder/package.json
Normal file
@@ -0,0 +1,33 @@
|
||||
{
|
||||
"name": "@vercel/gatsby-plugin-vercel-builder",
|
||||
"version": "1.0.0",
|
||||
"main": "dist/index.js",
|
||||
"files": [
|
||||
"dist",
|
||||
"gatsby-node.ts",
|
||||
"gatsby-node.js",
|
||||
"gatsby-node.js.map"
|
||||
],
|
||||
"scripts": {
|
||||
"build": "pnpm build:src && pnpm build:gatsby",
|
||||
"build:gatsby": "tsc -p tsconfig.gatsby.json",
|
||||
"build:src": "tsc -p tsconfig.src.json"
|
||||
},
|
||||
"dependencies": {
|
||||
"@vercel/build-utils": "5.9.0",
|
||||
"@vercel/node": "2.8.15",
|
||||
"@vercel/routing-utils": "2.1.8",
|
||||
"ajv": "8.12.0",
|
||||
"esbuild": "0.16.17",
|
||||
"etag": "1.8.1",
|
||||
"fs-extra": "11.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/etag": "1.8.0",
|
||||
"@types/fs-extra": "11.0.1",
|
||||
"@types/node": "14.18.33",
|
||||
"@types/react": "18.0.26",
|
||||
"gatsby": "4.25.2",
|
||||
"typescript": "4.3.4"
|
||||
}
|
||||
}
|
||||
129
packages/gatsby-plugin-vercel-builder/src/handlers/build.ts
Normal file
129
packages/gatsby-plugin-vercel-builder/src/handlers/build.ts
Normal file
@@ -0,0 +1,129 @@
|
||||
import { join } from 'path';
|
||||
|
||||
import { getNodeVersion } from '@vercel/build-utils';
|
||||
import { build } from 'esbuild';
|
||||
import {
|
||||
copy,
|
||||
copyFile,
|
||||
pathExists,
|
||||
writeJson,
|
||||
writeFileSync,
|
||||
ensureFileSync,
|
||||
} from 'fs-extra';
|
||||
|
||||
import type {
|
||||
NodejsServerlessFunctionConfig,
|
||||
PrerenderFunctionConfig,
|
||||
} from './../types';
|
||||
|
||||
export const writeHandler = async ({
|
||||
outDir,
|
||||
handlerFile,
|
||||
}: {
|
||||
outDir: string;
|
||||
handlerFile: string;
|
||||
}) => {
|
||||
const { major } = await getNodeVersion(process.cwd());
|
||||
|
||||
try {
|
||||
return await build({
|
||||
entryPoints: [handlerFile],
|
||||
loader: { '.ts': 'ts' },
|
||||
outfile: join(outDir, './index.js'),
|
||||
format: 'cjs',
|
||||
target: `node${major}`,
|
||||
platform: 'node',
|
||||
bundle: true,
|
||||
minify: true,
|
||||
define: {
|
||||
'process.env.NODE_ENV': "'production'",
|
||||
},
|
||||
});
|
||||
} catch (e: any) {
|
||||
console.error('Failed to build lambda handler', e.message);
|
||||
}
|
||||
};
|
||||
|
||||
export const writeVCConfig = async ({
|
||||
functionDir,
|
||||
handler = 'index.js',
|
||||
}: {
|
||||
functionDir: string;
|
||||
handler?: string;
|
||||
}) => {
|
||||
const { runtime } = await getNodeVersion(process.cwd());
|
||||
|
||||
const config: NodejsServerlessFunctionConfig = {
|
||||
runtime,
|
||||
handler,
|
||||
launcherType: 'Nodejs',
|
||||
shouldAddHelpers: true,
|
||||
};
|
||||
|
||||
return writeJson(`${functionDir}/.vc-config.json`, config);
|
||||
};
|
||||
|
||||
export const writePrerenderConfig = (outputPath: string, group: number) => {
|
||||
const config: PrerenderFunctionConfig = {
|
||||
group,
|
||||
expiration: 600, // 10 minutes TODO: make this configurable?
|
||||
};
|
||||
ensureFileSync(outputPath);
|
||||
return writeFileSync(outputPath, JSON.stringify(config));
|
||||
};
|
||||
|
||||
export async function movePageData({ functionDir }: { functionDir: string }) {
|
||||
await copy(
|
||||
join('.vercel', 'output', 'static', 'page-data'),
|
||||
join(functionDir, 'page-data')
|
||||
);
|
||||
}
|
||||
|
||||
export async function copyFunctionLibs({
|
||||
functionDir,
|
||||
}: {
|
||||
functionDir: string;
|
||||
}) {
|
||||
/* Copies the required libs for Serverless Functions from .cache to the <name>.func folder */
|
||||
await Promise.allSettled(
|
||||
[
|
||||
{
|
||||
src: join('.cache', 'query-engine'),
|
||||
dest: join(functionDir, '.cache', 'query-engine'),
|
||||
},
|
||||
{
|
||||
src: join('.cache', 'page-ssr'),
|
||||
dest: join(functionDir, '.cache', 'page-ssr'),
|
||||
},
|
||||
// {
|
||||
// src: join(functionDir, '.cache', 'query-engine', 'assets'),
|
||||
// dest: join(functionDir, 'assets'),
|
||||
// },
|
||||
{
|
||||
src: join('.cache', 'data', 'datastore'),
|
||||
dest: join(functionDir, '.cache', 'data', 'datastore'),
|
||||
},
|
||||
{
|
||||
src: join('.cache', 'caches'),
|
||||
dest: join(functionDir, '.cache', 'caches'),
|
||||
},
|
||||
].map(({ src, dest }) => copy(src, dest))
|
||||
);
|
||||
}
|
||||
|
||||
export async function copyHTMLFiles({ functionDir }: { functionDir: string }) {
|
||||
/* If available, copies the 404.html and 500.html files to the <name>.func/lib folder */
|
||||
for (const htmlFile of ['404', '500']) {
|
||||
if (await pathExists(join('public', `${htmlFile}.html`))) {
|
||||
try {
|
||||
await copyFile(
|
||||
join('public', `${htmlFile}.html`),
|
||||
join(functionDir, `${htmlFile}.html`)
|
||||
);
|
||||
} catch (e: any) {
|
||||
console.error('Failed to copy HTML files', e.message);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
import os from 'os';
|
||||
import etag from 'etag';
|
||||
import { parse } from 'url';
|
||||
import { copySync, existsSync } from 'fs-extra';
|
||||
import { join, dirname, basename } from 'path';
|
||||
import { getGraphQLEngine, getPageSSRHelpers } from '../utils';
|
||||
import type { VercelRequest, VercelResponse } from '@vercel/node';
|
||||
|
||||
const TMP_DATA_PATH = join(os.tmpdir(), 'data/datastore');
|
||||
const CUR_DATA_PATH = join(__dirname, '.cache/data/datastore');
|
||||
|
||||
if (!existsSync(TMP_DATA_PATH)) {
|
||||
// Copies executable `data` files to the writable /tmp directory.
|
||||
copySync(CUR_DATA_PATH, TMP_DATA_PATH);
|
||||
}
|
||||
|
||||
export default async function handler(req: VercelRequest, res: VercelResponse) {
|
||||
const pathname = parse(req.url!).pathname || '/';
|
||||
let pageName = basename(dirname(pathname));
|
||||
if (pageName === 'index') {
|
||||
pageName = '/';
|
||||
}
|
||||
|
||||
const graphqlEngine = await getGraphQLEngine();
|
||||
const { getData, renderPageData } = await getPageSSRHelpers();
|
||||
|
||||
const data = await getData({
|
||||
pathName: pageName,
|
||||
graphqlEngine,
|
||||
req,
|
||||
});
|
||||
|
||||
const pageData = await renderPageData({ data });
|
||||
|
||||
if (data.serverDataHeaders) {
|
||||
for (const [name, value] of Object.entries(data.serverDataHeaders)) {
|
||||
res.setHeader(name, value);
|
||||
}
|
||||
}
|
||||
|
||||
res.setHeader('ETag', etag(JSON.stringify(pageData)));
|
||||
return res.json(pageData);
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
import os from 'os';
|
||||
import { parse } from 'url';
|
||||
import { copySync, existsSync } from 'fs-extra';
|
||||
import { join, dirname, basename } from 'path';
|
||||
import { getPageSSRHelpers, getGraphQLEngine } from '../utils';
|
||||
import type { VercelRequest, VercelResponse } from '@vercel/node';
|
||||
|
||||
const TMP_DATA_PATH = join(os.tmpdir(), 'data/datastore');
|
||||
const CUR_DATA_PATH = join(__dirname, '.cache/data/datastore');
|
||||
|
||||
if (!existsSync(TMP_DATA_PATH)) {
|
||||
// Copies executable `data` files to the writable /tmp directory.
|
||||
copySync(CUR_DATA_PATH, TMP_DATA_PATH);
|
||||
}
|
||||
|
||||
export default async function handler(req: VercelRequest, res: VercelResponse) {
|
||||
const pathname = parse(req.url!).pathname || '/';
|
||||
let pageName = basename(pathname);
|
||||
if (pageName === 'index.html') {
|
||||
pageName = basename(dirname(pathname));
|
||||
}
|
||||
if (!pageName) {
|
||||
pageName = '/';
|
||||
}
|
||||
|
||||
const graphqlEngine = await getGraphQLEngine();
|
||||
const { getData, renderHTML } = await getPageSSRHelpers();
|
||||
|
||||
const data = await getData({
|
||||
pathName: pageName,
|
||||
graphqlEngine,
|
||||
req,
|
||||
});
|
||||
|
||||
const results = await renderHTML({ data });
|
||||
|
||||
if (data.serverDataHeaders) {
|
||||
for (const [name, value] of Object.entries(data.serverDataHeaders)) {
|
||||
res.setHeader(name, value);
|
||||
}
|
||||
}
|
||||
|
||||
if (data.serverDataStatus) {
|
||||
res.statusCode = data.serverDataStatus;
|
||||
}
|
||||
|
||||
res.send(results);
|
||||
}
|
||||
20
packages/gatsby-plugin-vercel-builder/src/handlers/utils.ts
Normal file
20
packages/gatsby-plugin-vercel-builder/src/handlers/utils.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
import { join } from 'path';
|
||||
import os from 'os';
|
||||
|
||||
const TMP_DATA_PATH = join(os.tmpdir(), 'data/datastore');
|
||||
|
||||
export async function getGraphQLEngine() {
|
||||
const { GraphQLEngine } = (await import(
|
||||
join(__dirname, '.cache/query-engine/index.js')
|
||||
)) as typeof import('gatsby/dist/schema/graphql-engine/entry');
|
||||
|
||||
return new GraphQLEngine({ dbPath: TMP_DATA_PATH });
|
||||
}
|
||||
|
||||
export async function getPageSSRHelpers() {
|
||||
const { getData, renderPageData, renderHTML } = (await import(
|
||||
join(__dirname, '.cache/page-ssr/index.js')
|
||||
)) as typeof import('gatsby/dist/utils/page-ssr-module/entry');
|
||||
|
||||
return { getData, renderPageData, renderHTML };
|
||||
}
|
||||
134
packages/gatsby-plugin-vercel-builder/src/helpers/functions.ts
Normal file
134
packages/gatsby-plugin-vercel-builder/src/helpers/functions.ts
Normal file
@@ -0,0 +1,134 @@
|
||||
import { join } from 'path';
|
||||
|
||||
import { ensureDir } from 'fs-extra';
|
||||
|
||||
import { createSymlink } from '../utils/symlink';
|
||||
import {
|
||||
writeHandler,
|
||||
writeVCConfig,
|
||||
copyFunctionLibs,
|
||||
movePageData,
|
||||
copyHTMLFiles,
|
||||
writePrerenderConfig,
|
||||
} from '../handlers/build';
|
||||
import { GatsbyFunction } from '../schemas';
|
||||
import { Routes } from '../types';
|
||||
|
||||
export async function createServerlessFunctions({
|
||||
dsgRoutes,
|
||||
ssrRoutes,
|
||||
}: Routes) {
|
||||
/* Gatsby SSR/DSG on Vercel is enabled through Vercel Serverless Functions.
|
||||
This plugin creates one Serverless Function called `_ssr.func` that is used by SSR and DSG pages through symlinks.
|
||||
DSG is enabled through prerender functions.
|
||||
*/
|
||||
const functionName = '_ssr.func';
|
||||
const functionDir = join('.vercel', 'output', 'functions', functionName);
|
||||
const handlerFile = join(
|
||||
__dirname,
|
||||
'..',
|
||||
'handlers',
|
||||
'templates',
|
||||
'./ssr-handler.js'
|
||||
);
|
||||
|
||||
await ensureDir(functionDir);
|
||||
|
||||
await Promise.all([
|
||||
writeHandler({ outDir: functionDir, handlerFile }),
|
||||
copyFunctionLibs({ functionDir }),
|
||||
copyHTMLFiles({ functionDir }),
|
||||
writeVCConfig({ functionDir }),
|
||||
]);
|
||||
|
||||
await Promise.all([
|
||||
...ssrRoutes.map(async pathName => {
|
||||
const funcPath = join(pathName, 'index.html');
|
||||
return createSymlink(funcPath, functionName);
|
||||
}),
|
||||
...dsgRoutes.map(async (pathName, index) => {
|
||||
const funcPath = join(pathName, 'index.html');
|
||||
writePrerenderConfig(
|
||||
join(
|
||||
'.vercel',
|
||||
'output',
|
||||
'functions',
|
||||
`${funcPath}.prerender-config.json`
|
||||
),
|
||||
index + 1
|
||||
);
|
||||
return createSymlink(funcPath, functionName);
|
||||
}),
|
||||
]);
|
||||
}
|
||||
|
||||
export async function createPageDataFunctions({
|
||||
dsgRoutes,
|
||||
ssrRoutes,
|
||||
}: Routes) {
|
||||
/* Gatsby uses /page-data/<path>/page-data.json to fetch data. This plugin creates a
|
||||
`_page-data.func` function that dynamically generates this data if it's not available in `static/page-data`. */
|
||||
const functionName = '_page-data.func';
|
||||
const functionDir = join('.vercel', 'output', 'functions', functionName);
|
||||
const handlerFile = join(
|
||||
__dirname,
|
||||
'..',
|
||||
'handlers',
|
||||
'templates',
|
||||
'./page-data.js'
|
||||
);
|
||||
|
||||
await ensureDir(functionDir);
|
||||
|
||||
await Promise.all([
|
||||
writeHandler({ outDir: functionDir, handlerFile }),
|
||||
copyFunctionLibs({ functionDir }),
|
||||
movePageData({ functionDir }),
|
||||
writeVCConfig({ functionDir }),
|
||||
]);
|
||||
|
||||
await Promise.all([
|
||||
...ssrRoutes.map(async pathName => {
|
||||
if (!pathName || pathName === '/') {
|
||||
pathName = 'index';
|
||||
}
|
||||
const funcPath = join('page-data', pathName, 'page-data.json');
|
||||
return createSymlink(funcPath, functionName);
|
||||
}),
|
||||
...dsgRoutes.map(async (pathName, index) => {
|
||||
if (!pathName || pathName === '/') {
|
||||
pathName = 'index';
|
||||
}
|
||||
const funcPath = join('page-data', pathName, 'page-data.json');
|
||||
writePrerenderConfig(
|
||||
join(
|
||||
'.vercel',
|
||||
'output',
|
||||
'functions',
|
||||
`${funcPath}.prerender-config.json`
|
||||
),
|
||||
index + 1
|
||||
);
|
||||
return createSymlink(funcPath, functionName);
|
||||
}),
|
||||
]);
|
||||
}
|
||||
|
||||
export async function createAPIRoutes(functions: GatsbyFunction[]) {
|
||||
const apiDir = join('.vercel', 'output', 'functions', 'api');
|
||||
await ensureDir(apiDir);
|
||||
|
||||
await Promise.allSettled(
|
||||
functions.map(async (func: GatsbyFunction) => {
|
||||
const apiRouteDir = `${apiDir}/${func.functionRoute}.func`;
|
||||
const handlerFile = func.originalAbsoluteFilePath;
|
||||
|
||||
await ensureDir(apiRouteDir);
|
||||
|
||||
await Promise.all([
|
||||
writeHandler({ outDir: apiRouteDir, handlerFile }),
|
||||
writeVCConfig({ functionDir: apiRouteDir }),
|
||||
]);
|
||||
})
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
import { join } from 'path';
|
||||
import { copy, ensureDir } from 'fs-extra';
|
||||
|
||||
export async function createStaticDir() {
|
||||
const targetDir = join(process.cwd(), '.vercel', 'output', 'static');
|
||||
await ensureDir(targetDir);
|
||||
await copy(join(process.cwd(), 'public'), targetDir);
|
||||
}
|
||||
106
packages/gatsby-plugin-vercel-builder/src/index.ts
Normal file
106
packages/gatsby-plugin-vercel-builder/src/index.ts
Normal file
@@ -0,0 +1,106 @@
|
||||
import { join } from 'path';
|
||||
import { getTransformedRoutes } from '@vercel/routing-utils';
|
||||
import { pathExists, writeJson, remove } from 'fs-extra';
|
||||
import { validateGatsbyState } from './schemas';
|
||||
import {
|
||||
createServerlessFunctions,
|
||||
createPageDataFunctions,
|
||||
createAPIRoutes,
|
||||
} from './helpers/functions';
|
||||
import { createStaticDir } from './helpers/static';
|
||||
import type { Config, Routes } from './types';
|
||||
|
||||
export interface GenerateVercelBuildOutputAPI3OutputOptions {
|
||||
exportPath: string;
|
||||
gatsbyStoreState: {
|
||||
pages: Map<string, unknown>;
|
||||
redirects: unknown;
|
||||
functions: unknown;
|
||||
config: unknown;
|
||||
};
|
||||
[x: string]: unknown;
|
||||
}
|
||||
export async function generateVercelBuildOutputAPI3Output({
|
||||
exportPath,
|
||||
gatsbyStoreState,
|
||||
}: GenerateVercelBuildOutputAPI3OutputOptions) {
|
||||
const state = {
|
||||
pages: Array.from(gatsbyStoreState.pages.entries()), // must transform from a Map for validation
|
||||
redirects: gatsbyStoreState.redirects,
|
||||
functions: gatsbyStoreState.functions,
|
||||
config: gatsbyStoreState.config,
|
||||
};
|
||||
|
||||
if (validateGatsbyState(state)) {
|
||||
console.log('▲ Creating Vercel build output');
|
||||
await remove(join('.vercel', 'output'));
|
||||
|
||||
const { pages, redirects, functions, config: gatsbyConfig } = state;
|
||||
|
||||
const { ssrRoutes, dsgRoutes } = pages.reduce<Routes>(
|
||||
(acc, [, cur]) => {
|
||||
if (cur.mode === 'SSR') {
|
||||
acc.ssrRoutes.push(cur.path);
|
||||
} else if (cur.mode === 'DSG') {
|
||||
acc.dsgRoutes.push(cur.path);
|
||||
}
|
||||
|
||||
return acc;
|
||||
},
|
||||
{
|
||||
ssrRoutes: [],
|
||||
dsgRoutes: [],
|
||||
}
|
||||
);
|
||||
|
||||
await createStaticDir();
|
||||
|
||||
const createPromises: Promise<void>[] = [];
|
||||
|
||||
if (functions.length > 0) {
|
||||
createPromises.push(createAPIRoutes(functions));
|
||||
}
|
||||
|
||||
if (ssrRoutes.length > 0 || dsgRoutes.length > 0) {
|
||||
createPromises.push(createPageDataFunctions({ ssrRoutes, dsgRoutes }));
|
||||
createPromises.push(createServerlessFunctions({ ssrRoutes, dsgRoutes }));
|
||||
}
|
||||
|
||||
await Promise.all(createPromises);
|
||||
|
||||
const vercelConfigPath = `${process.cwd()}/vercel.config.js`;
|
||||
const vercelConfig: Config = (await pathExists(vercelConfigPath))
|
||||
? require(vercelConfigPath).default
|
||||
: {};
|
||||
|
||||
let trailingSlash: boolean | undefined = undefined;
|
||||
|
||||
if (gatsbyConfig.trailingSlash === 'always') {
|
||||
trailingSlash = true;
|
||||
} else if (gatsbyConfig.trailingSlash === 'never') {
|
||||
trailingSlash = false;
|
||||
}
|
||||
|
||||
const { routes } = getTransformedRoutes({
|
||||
...vercelConfig,
|
||||
trailingSlash,
|
||||
redirects: redirects.map(({ fromPath, toPath, isPermanent }) => ({
|
||||
source: fromPath,
|
||||
destination: toPath,
|
||||
permanent: isPermanent,
|
||||
})),
|
||||
});
|
||||
|
||||
const config: Config = {
|
||||
version: 3,
|
||||
routes: routes || undefined,
|
||||
};
|
||||
|
||||
await writeJson(exportPath, config);
|
||||
console.log('Vercel output has been generated');
|
||||
} else {
|
||||
throw new Error(
|
||||
'Gatsby state validation error. Please file an issue https://vercel.com/help#issues'
|
||||
);
|
||||
}
|
||||
}
|
||||
98
packages/gatsby-plugin-vercel-builder/src/schemas.ts
Normal file
98
packages/gatsby-plugin-vercel-builder/src/schemas.ts
Normal file
@@ -0,0 +1,98 @@
|
||||
import type {
|
||||
IGatsbyPage,
|
||||
IGatsbyFunction,
|
||||
IRedirect,
|
||||
IGatsbyConfig,
|
||||
} from 'gatsby/dist/redux/types';
|
||||
import Ajv, { JSONSchemaType } from 'ajv';
|
||||
|
||||
export type GatsbyPage = Pick<IGatsbyPage, 'mode' | 'path'>;
|
||||
const GatsbyPageSchema: JSONSchemaType<GatsbyPage> = {
|
||||
type: 'object',
|
||||
properties: {
|
||||
mode: {
|
||||
type: 'string',
|
||||
enum: ['SSG', 'DSG', 'SSR'],
|
||||
},
|
||||
path: {
|
||||
type: 'string',
|
||||
},
|
||||
},
|
||||
required: ['mode', 'path'],
|
||||
} as const;
|
||||
|
||||
export interface GatsbyState {
|
||||
pages: Array<[string, GatsbyPage]>;
|
||||
redirects: Array<GatsbyRedirect>;
|
||||
functions: Array<GatsbyFunction>;
|
||||
config: GatsbyConfig;
|
||||
}
|
||||
|
||||
export type GatsbyFunction = Pick<
|
||||
IGatsbyFunction,
|
||||
'functionRoute' | 'originalAbsoluteFilePath'
|
||||
>;
|
||||
const GatsbyFunctionSchema: JSONSchemaType<GatsbyFunction> = {
|
||||
type: 'object',
|
||||
properties: {
|
||||
functionRoute: { type: 'string' },
|
||||
originalAbsoluteFilePath: { type: 'string' },
|
||||
},
|
||||
required: ['functionRoute', 'originalAbsoluteFilePath'],
|
||||
} as const;
|
||||
|
||||
export type GatsbyRedirect = Pick<
|
||||
IRedirect,
|
||||
'fromPath' | 'toPath' | 'isPermanent'
|
||||
>;
|
||||
const GatsbyRedirectSchema: JSONSchemaType<GatsbyRedirect> = {
|
||||
type: 'object',
|
||||
properties: {
|
||||
fromPath: { type: 'string' },
|
||||
toPath: { type: 'string' },
|
||||
isPermanent: { type: 'boolean', nullable: true },
|
||||
},
|
||||
required: ['fromPath', 'toPath'],
|
||||
} as const;
|
||||
|
||||
export type GatsbyConfig = Pick<IGatsbyConfig, 'trailingSlash'>;
|
||||
|
||||
const GatsbyConfigSchema: JSONSchemaType<GatsbyConfig> = {
|
||||
type: 'object',
|
||||
properties: {
|
||||
trailingSlash: {
|
||||
type: 'string',
|
||||
enum: ['always', 'never', 'ignore', 'legacy'],
|
||||
nullable: true,
|
||||
},
|
||||
},
|
||||
} as const;
|
||||
|
||||
const GatsbyStateSchema: JSONSchemaType<GatsbyState> = {
|
||||
type: 'object',
|
||||
properties: {
|
||||
pages: {
|
||||
type: 'array',
|
||||
items: {
|
||||
type: 'array',
|
||||
minItems: 2,
|
||||
maxItems: 2,
|
||||
items: [{ type: 'string' }, GatsbyPageSchema],
|
||||
},
|
||||
},
|
||||
redirects: {
|
||||
type: 'array',
|
||||
items: GatsbyRedirectSchema,
|
||||
},
|
||||
functions: {
|
||||
type: 'array',
|
||||
items: GatsbyFunctionSchema,
|
||||
},
|
||||
config: GatsbyConfigSchema,
|
||||
},
|
||||
required: ['pages', 'redirects', 'functions', 'config'],
|
||||
additionalProperties: true,
|
||||
} as const;
|
||||
|
||||
export const ajv = new Ajv({ allErrors: true });
|
||||
export const validateGatsbyState = ajv.compile(GatsbyStateSchema);
|
||||
126
packages/gatsby-plugin-vercel-builder/src/types.d.ts
vendored
Normal file
126
packages/gatsby-plugin-vercel-builder/src/types.d.ts
vendored
Normal file
@@ -0,0 +1,126 @@
|
||||
import { GatsbyPage } from './schemas';
|
||||
|
||||
export type Config = {
|
||||
version: 3;
|
||||
routes?: Route[];
|
||||
images?: ImagesConfig;
|
||||
wildcard?: WildcardConfig;
|
||||
overrides?: OverrideConfig;
|
||||
cache?: string[];
|
||||
};
|
||||
|
||||
type Route = Source | Handler;
|
||||
|
||||
type Source = {
|
||||
src: string;
|
||||
dest?: string;
|
||||
headers?: Record<string, string>;
|
||||
methods?: string[];
|
||||
continue?: boolean;
|
||||
caseSensitive?: boolean;
|
||||
check?: boolean;
|
||||
status?: number;
|
||||
has?: Array<HostHasField | HeaderHasField | CookieHasField | QueryHasField>;
|
||||
missing?: Array<
|
||||
HostHasField | HeaderHasField | CookieHasField | QueryHasField
|
||||
>;
|
||||
locale?: Locale;
|
||||
middlewarePath?: string;
|
||||
};
|
||||
|
||||
type Locale = {
|
||||
redirect?: Record<string, string>;
|
||||
cookie?: string;
|
||||
};
|
||||
|
||||
type HostHasField = {
|
||||
type: 'host';
|
||||
value: string;
|
||||
};
|
||||
|
||||
type HeaderHasField = {
|
||||
type: 'header';
|
||||
key: string;
|
||||
value?: string;
|
||||
};
|
||||
|
||||
type CookieHasField = {
|
||||
type: 'cookie';
|
||||
key: string;
|
||||
value?: string;
|
||||
};
|
||||
|
||||
type QueryHasField = {
|
||||
type: 'query';
|
||||
key: string;
|
||||
value?: string;
|
||||
};
|
||||
|
||||
type HandleValue =
|
||||
| 'rewrite'
|
||||
| 'filesystem' // check matches after the filesystem misses
|
||||
| 'resource'
|
||||
| 'miss' // check matches after every filesystem miss
|
||||
| 'hit'
|
||||
| 'error'; // check matches after error (500, 404, etc.)
|
||||
|
||||
type Handler = {
|
||||
handle: HandleValue;
|
||||
src?: string;
|
||||
dest?: string;
|
||||
status?: number;
|
||||
};
|
||||
|
||||
type ImageFormat = 'image/avif' | 'image/webp';
|
||||
|
||||
type ImagesConfig = {
|
||||
sizes: number[];
|
||||
domains: string[];
|
||||
minimumCacheTTL?: number; // seconds
|
||||
formats?: ImageFormat[];
|
||||
dangerouslyAllowSVG?: boolean;
|
||||
contentSecurityPolicy?: string;
|
||||
};
|
||||
|
||||
type WildCard = {
|
||||
domain: string;
|
||||
value: string;
|
||||
};
|
||||
|
||||
type WildcardConfig = Array<WildCard>;
|
||||
|
||||
type Override = {
|
||||
path?: string;
|
||||
contentType?: string;
|
||||
};
|
||||
|
||||
type OverrideConfig = Record<string, Override>;
|
||||
|
||||
type ServerlessFunctionConfig = {
|
||||
handler: string;
|
||||
runtime: string;
|
||||
memory?: number;
|
||||
maxDuration?: number;
|
||||
environment?: Record<string, string>[];
|
||||
allowQuery?: string[];
|
||||
regions?: string[];
|
||||
};
|
||||
|
||||
export type NodejsServerlessFunctionConfig = ServerlessFunctionConfig & {
|
||||
launcherType: 'Nodejs';
|
||||
shouldAddHelpers?: boolean; // default: false
|
||||
shouldAddSourceMapSupport?: boolean; // default: false
|
||||
};
|
||||
|
||||
export type PrerenderFunctionConfig = {
|
||||
expiration: number | false;
|
||||
group?: number;
|
||||
bypassToken?: string;
|
||||
fallback?: string;
|
||||
allowQuery?: string[];
|
||||
};
|
||||
|
||||
export interface Routes {
|
||||
ssrRoutes: Array<GatsbyPage['path']>;
|
||||
dsgRoutes: Array<GatsbyPage['path']>;
|
||||
}
|
||||
20
packages/gatsby-plugin-vercel-builder/src/utils/symlink.ts
Normal file
20
packages/gatsby-plugin-vercel-builder/src/utils/symlink.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
import path, { join, normalize, sep } from 'path';
|
||||
|
||||
import { ensureDir, symlinkSync } from 'fs-extra';
|
||||
|
||||
const removeTrailingSlash = (str: string) => str.replace(/\/$/, '');
|
||||
|
||||
export const createSymlink = async (pathName: string, destName: string) => {
|
||||
const functionName = removeTrailingSlash(pathName).split(sep).pop();
|
||||
|
||||
const dirPath = removeTrailingSlash(
|
||||
join('.vercel', 'output', 'functions', normalize(join(pathName, '..')))
|
||||
);
|
||||
|
||||
await ensureDir(dirPath);
|
||||
|
||||
symlinkSync(
|
||||
path.relative(dirPath, join('.vercel', 'output', 'functions', destName)),
|
||||
path.join(dirPath, `${functionName}.func`)
|
||||
);
|
||||
};
|
||||
104
packages/gatsby-plugin-vercel-builder/tsconfig.base.json
Normal file
104
packages/gatsby-plugin-vercel-builder/tsconfig.base.json
Normal file
@@ -0,0 +1,104 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
/* Visit https://aka.ms/tsconfig to read more about this file */
|
||||
|
||||
/* Projects */
|
||||
// "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */
|
||||
// "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */
|
||||
// "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */
|
||||
// "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */
|
||||
// "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */
|
||||
// "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */
|
||||
|
||||
/* Language and Environment */
|
||||
"target": "es2020" /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */,
|
||||
// "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */
|
||||
// "jsx": "preserve", /* Specify what JSX code is generated. */
|
||||
// "experimentalDecorators": true, /* Enable experimental support for TC39 stage 2 draft decorators. */
|
||||
// "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */
|
||||
// "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */
|
||||
// "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */
|
||||
// "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */
|
||||
// "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */
|
||||
// "noLib": true, /* Disable including any library files, including the default lib.d.ts. */
|
||||
// "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */
|
||||
// "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */
|
||||
|
||||
/* Modules */
|
||||
"module": "commonjs" /* Specify what module code is generated. */,
|
||||
// "rootDir": "./", /* Specify the root folder within your source files. */
|
||||
"moduleResolution": "node" /* Specify how TypeScript looks up a file from a given module specifier. */,
|
||||
// "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */
|
||||
// "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */
|
||||
// "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */
|
||||
// "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */
|
||||
// "types": [], /* Specify type package names to be included without being referenced in a source file. */
|
||||
// "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
|
||||
// "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */
|
||||
// "resolveJsonModule": true, /* Enable importing .json files. */
|
||||
// "noResolve": true, /* Disallow 'import's, 'require's or '<reference>'s from expanding the number of files TypeScript should add to a project. */
|
||||
|
||||
/* JavaScript Support */
|
||||
// "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */
|
||||
// "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */
|
||||
// "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */
|
||||
|
||||
/* Emit */
|
||||
"declaration": true /* Generate .d.ts files from TypeScript and JavaScript files in your project. */,
|
||||
"declarationMap": true /* Create sourcemaps for d.ts files. */,
|
||||
// "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */
|
||||
"sourceMap": true /* Create source map files for emitted JavaScript files. */,
|
||||
// "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */
|
||||
// "outDir": "./", /* Specify an output folder for all emitted files. */
|
||||
// "removeComments": true, /* Disable emitting comments. */
|
||||
// "noEmit": true, /* Disable emitting files from a compilation. */
|
||||
// "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */
|
||||
// "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types. */
|
||||
// "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */
|
||||
// "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */
|
||||
// "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
|
||||
// "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */
|
||||
// "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */
|
||||
// "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */
|
||||
// "newLine": "crlf", /* Set the newline character for emitting files. */
|
||||
// "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */
|
||||
// "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */
|
||||
// "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */
|
||||
// "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */
|
||||
// "declarationDir": "./", /* Specify the output directory for generated declaration files. */
|
||||
// "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */
|
||||
|
||||
/* Interop Constraints */
|
||||
// "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */
|
||||
// "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */
|
||||
"esModuleInterop": true /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */,
|
||||
// "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */
|
||||
"forceConsistentCasingInFileNames": true /* Ensure that casing is correct in imports. */,
|
||||
|
||||
/* Type Checking */
|
||||
"strict": true /* Enable all strict type-checking options. */,
|
||||
// "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */
|
||||
// "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */
|
||||
// "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */
|
||||
// "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */
|
||||
// "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */
|
||||
// "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */
|
||||
// "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */
|
||||
// "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */
|
||||
// "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */
|
||||
// "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */
|
||||
// "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */
|
||||
// "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */
|
||||
// "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */
|
||||
// "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */
|
||||
// "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */
|
||||
// "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */
|
||||
// "allowUnusedLabels": true, /* Disable error reporting for unused labels. */
|
||||
// "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */
|
||||
|
||||
/* Completeness */
|
||||
// "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */
|
||||
"skipLibCheck": true /* Skip type checking all .d.ts files. */
|
||||
},
|
||||
"include": []
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"extends": "./tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"declaration": false,
|
||||
"declarationMap": false
|
||||
},
|
||||
"include": ["gatsby-node.ts"]
|
||||
}
|
||||
7
packages/gatsby-plugin-vercel-builder/tsconfig.src.json
Normal file
7
packages/gatsby-plugin-vercel-builder/tsconfig.src.json
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"extends": "./tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "./dist"
|
||||
},
|
||||
"include": ["src/**/*.ts"]
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vercel/go",
|
||||
"version": "2.2.25",
|
||||
"version": "2.2.30",
|
||||
"license": "MIT",
|
||||
"main": "./dist/index",
|
||||
"homepage": "https://vercel.com/docs/runtimes#official-runtimes/go",
|
||||
@@ -36,7 +36,7 @@
|
||||
"@types/node": "14.18.33",
|
||||
"@types/node-fetch": "^2.3.0",
|
||||
"@types/tar": "^4.0.0",
|
||||
"@vercel/build-utils": "workspace:5.7.6",
|
||||
"@vercel/build-utils": "5.9.0",
|
||||
"@vercel/ncc": "0.24.0",
|
||||
"async-retry": "1.3.1",
|
||||
"execa": "^1.0.0",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vercel/hydrogen",
|
||||
"version": "0.0.39",
|
||||
"version": "0.0.44",
|
||||
"license": "MIT",
|
||||
"main": "./dist/index.js",
|
||||
"homepage": "https://vercel.com/docs",
|
||||
@@ -21,8 +21,8 @@
|
||||
"devDependencies": {
|
||||
"@types/jest": "27.5.1",
|
||||
"@types/node": "14.18.33",
|
||||
"@vercel/build-utils": "workspace:5.7.6",
|
||||
"@vercel/static-config": "workspace:2.0.7",
|
||||
"@vercel/build-utils": "5.9.0",
|
||||
"@vercel/static-config": "2.0.11",
|
||||
"execa": "3.2.0",
|
||||
"fs-extra": "11.1.0",
|
||||
"ts-morph": "12.0.0",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vercel/next",
|
||||
"version": "3.3.10",
|
||||
"version": "3.3.18",
|
||||
"license": "MIT",
|
||||
"main": "./dist/index",
|
||||
"homepage": "https://vercel.com/docs/runtimes#official-runtimes/next-js",
|
||||
@@ -45,9 +45,9 @@
|
||||
"@types/semver": "6.0.0",
|
||||
"@types/text-table": "0.2.1",
|
||||
"@types/webpack-sources": "3.2.0",
|
||||
"@vercel/build-utils": "workspace:5.7.6",
|
||||
"@vercel/build-utils": "5.9.0",
|
||||
"@vercel/nft": "0.22.5",
|
||||
"@vercel/routing-utils": "workspace:2.1.4",
|
||||
"@vercel/routing-utils": "2.1.8",
|
||||
"async-sema": "3.0.1",
|
||||
"buffer-crc32": "0.2.13",
|
||||
"bytes": "3.1.2",
|
||||
|
||||
@@ -214,7 +214,7 @@ export const build: BuildV2 = async ({
|
||||
repoRootPath = entryPath;
|
||||
entryPath = path.join(entryPath, config.rootDirectory as string);
|
||||
}
|
||||
const outputDirectory = config.outputDirectory || '.next';
|
||||
const outputDirectory = path.join('./', config.outputDirectory || '.next');
|
||||
const dotNextStatic = path.join(entryPath, outputDirectory, 'static');
|
||||
// TODO: remove after testing used for simulating root directory monorepo
|
||||
// setting that can't be triggered with vercel.json
|
||||
@@ -2595,7 +2595,7 @@ export const prepareCache: PrepareCache = async ({
|
||||
debug('Preparing cache...');
|
||||
const entryDirectory = path.dirname(entrypoint);
|
||||
const entryPath = path.join(workPath, entryDirectory);
|
||||
const outputDirectory = config.outputDirectory || '.next';
|
||||
const outputDirectory = path.join('./', config.outputDirectory || '.next');
|
||||
|
||||
const nextVersionRange = await getNextVersionRange(entryPath);
|
||||
const isLegacy = nextVersionRange && isLegacyNext(nextVersionRange);
|
||||
|
||||
@@ -1060,9 +1060,9 @@ export async function serverBuild({
|
||||
{
|
||||
src: path.posix.join(
|
||||
'^/',
|
||||
entryDirectory,
|
||||
trailingSlash ? '/' : '',
|
||||
'$'
|
||||
entryDirectory !== '.'
|
||||
? `${entryDirectory}${trailingSlash ? '/$' : '$'}`
|
||||
: '$'
|
||||
),
|
||||
has: [
|
||||
{
|
||||
@@ -1417,7 +1417,7 @@ export async function serverBuild({
|
||||
src: `^${path.posix.join(
|
||||
'/',
|
||||
entryDirectory,
|
||||
'/((?!.+\\.rsc).+)$'
|
||||
'/((?!.+\\.rsc).+?)(?:/)?$'
|
||||
)}`,
|
||||
has: [
|
||||
{
|
||||
|
||||
12
packages/next/test/fixtures/00-absolute-output-directory/index.test.js
vendored
Normal file
12
packages/next/test/fixtures/00-absolute-output-directory/index.test.js
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
/* eslint-env jest */
|
||||
const path = require('path');
|
||||
const { deployAndTest } = require('../../utils');
|
||||
|
||||
const ctx = {};
|
||||
|
||||
describe(`${__dirname.split(path.sep).pop()}`, () => {
|
||||
it('should deploy and pass probe checks', async () => {
|
||||
const info = await deployAndTest(__dirname);
|
||||
Object.assign(ctx, info);
|
||||
});
|
||||
});
|
||||
12
packages/next/test/fixtures/00-absolute-output-directory/package.json
vendored
Normal file
12
packages/next/test/fixtures/00-absolute-output-directory/package.json
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"name": "test-root-dir",
|
||||
"version": "1.0.0",
|
||||
"main": "index.js",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@next/font": "13.1.2",
|
||||
"next": "13.1.2",
|
||||
"react": "18.2.0",
|
||||
"react-dom": "18.2.0"
|
||||
}
|
||||
}
|
||||
6
packages/next/test/fixtures/00-absolute-output-directory/packages/client/next.config.js
vendored
Normal file
6
packages/next/test/fixtures/00-absolute-output-directory/packages/client/next.config.js
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
/** @type {import('next').NextConfig} */
|
||||
const nextConfig = {
|
||||
reactStrictMode: true,
|
||||
};
|
||||
|
||||
module.exports = nextConfig;
|
||||
12
packages/next/test/fixtures/00-absolute-output-directory/packages/client/package.json
vendored
Normal file
12
packages/next/test/fixtures/00-absolute-output-directory/packages/client/package.json
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"name": "client",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "next dev",
|
||||
"build": "next build",
|
||||
"start": "next start",
|
||||
"lint": "next lint"
|
||||
},
|
||||
"dependencies": {}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
import { NextResponse } from 'next/server';
|
||||
|
||||
export default function handler(req) {
|
||||
return NextResponse.json({ hello: 'world', now: Date.now(), url: req.url });
|
||||
}
|
||||
|
||||
export const config = {
|
||||
runtime: 'edge',
|
||||
};
|
||||
5
packages/next/test/fixtures/00-absolute-output-directory/packages/client/pages/api/hello.js
vendored
Normal file
5
packages/next/test/fixtures/00-absolute-output-directory/packages/client/pages/api/hello.js
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
// Next.js API route support: https://nextjs.org/docs/api-routes/introduction
|
||||
|
||||
export default function handler(req, res) {
|
||||
res.status(200).json({ name: 'John Doe' });
|
||||
}
|
||||
3
packages/next/test/fixtures/00-absolute-output-directory/packages/client/pages/index.js
vendored
Normal file
3
packages/next/test/fixtures/00-absolute-output-directory/packages/client/pages/index.js
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
export default function Index() {
|
||||
return <p>Index page</p>;
|
||||
}
|
||||
20
packages/next/test/fixtures/00-absolute-output-directory/vercel.json
vendored
Normal file
20
packages/next/test/fixtures/00-absolute-output-directory/vercel.json
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"version": 2,
|
||||
"builds": [
|
||||
{
|
||||
"src": "package.json",
|
||||
"use": "@vercel/next",
|
||||
"config": {
|
||||
"buildCommand": "cd packages/client && yarn build",
|
||||
"outputDirectory": "/packages/client/.next"
|
||||
}
|
||||
}
|
||||
],
|
||||
"probes": [
|
||||
{
|
||||
"path": "/api/data/first",
|
||||
"status": 200,
|
||||
"mustContain": "\"hello\":\"world\""
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
export default function AnotherPage(props) {
|
||||
return (
|
||||
<>
|
||||
<p>hello from newroot/dashboard/another</p>
|
||||
</>
|
||||
);
|
||||
}
|
||||
10
packages/next/test/fixtures/00-app-dir-trailing-slash/app/(newroot)/layout.js
vendored
Normal file
10
packages/next/test/fixtures/00-app-dir-trailing-slash/app/(newroot)/layout.js
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
export default function Root({ children }) {
|
||||
return (
|
||||
<html className="this-is-another-document-html">
|
||||
<head>
|
||||
<title>{`hello world`}</title>
|
||||
</head>
|
||||
<body className="this-is-another-document-body">{children}</body>
|
||||
</html>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
export default function ChangelogPage(props) {
|
||||
return (
|
||||
<>
|
||||
<p>hello from app/dashboard/changelog</p>
|
||||
</>
|
||||
);
|
||||
}
|
||||
7
packages/next/test/fixtures/00-app-dir-trailing-slash/app/(rootonly)/dashboard/hello/page.js
vendored
Normal file
7
packages/next/test/fixtures/00-app-dir-trailing-slash/app/(rootonly)/dashboard/hello/page.js
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
export default function HelloPage(props) {
|
||||
return (
|
||||
<>
|
||||
<p>hello from app/dashboard/rootonly/hello</p>
|
||||
</>
|
||||
);
|
||||
}
|
||||
20
packages/next/test/fixtures/00-app-dir-trailing-slash/app/client-component-route/page.js
vendored
Normal file
20
packages/next/test/fixtures/00-app-dir-trailing-slash/app/client-component-route/page.js
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
'use client';
|
||||
|
||||
import { useState, useEffect } from 'react';
|
||||
|
||||
import style from './style.module.css';
|
||||
import './style.css';
|
||||
|
||||
export default function ClientComponentRoute() {
|
||||
const [count, setCount] = useState(0);
|
||||
useEffect(() => {
|
||||
setCount(1);
|
||||
}, [count]);
|
||||
return (
|
||||
<>
|
||||
<p className={style.red}>
|
||||
hello from app/client-component-route. <b>count: {count}</b>
|
||||
</p>
|
||||
</>
|
||||
);
|
||||
}
|
||||
3
packages/next/test/fixtures/00-app-dir-trailing-slash/app/client-component-route/style.css
vendored
Normal file
3
packages/next/test/fixtures/00-app-dir-trailing-slash/app/client-component-route/style.css
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
b {
|
||||
color: blue;
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
.red {
|
||||
color: red;
|
||||
}
|
||||
20
packages/next/test/fixtures/00-app-dir-trailing-slash/app/client-nested/layout.js
vendored
Normal file
20
packages/next/test/fixtures/00-app-dir-trailing-slash/app/client-nested/layout.js
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
'use client';
|
||||
|
||||
import { useState, useEffect } from 'react';
|
||||
|
||||
import styles from './style.module.css';
|
||||
import './style.css';
|
||||
|
||||
export default function ClientNestedLayout({ children }) {
|
||||
const [count, setCount] = useState(0);
|
||||
useEffect(() => {
|
||||
setCount(1);
|
||||
}, []);
|
||||
return (
|
||||
<>
|
||||
<h1 className={styles.red}>Client Nested. Count: {count}</h1>
|
||||
<button onClick={() => setCount(count + 1)}>{count}</button>
|
||||
{children}
|
||||
</>
|
||||
);
|
||||
}
|
||||
7
packages/next/test/fixtures/00-app-dir-trailing-slash/app/client-nested/page.js
vendored
Normal file
7
packages/next/test/fixtures/00-app-dir-trailing-slash/app/client-nested/page.js
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
export default function ClientPage() {
|
||||
return (
|
||||
<>
|
||||
<p>hello from app/client-nested</p>
|
||||
</>
|
||||
);
|
||||
}
|
||||
3
packages/next/test/fixtures/00-app-dir-trailing-slash/app/client-nested/style.css
vendored
Normal file
3
packages/next/test/fixtures/00-app-dir-trailing-slash/app/client-nested/style.css
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
button {
|
||||
color: red;
|
||||
}
|
||||
3
packages/next/test/fixtures/00-app-dir-trailing-slash/app/client-nested/style.module.css
vendored
Normal file
3
packages/next/test/fixtures/00-app-dir-trailing-slash/app/client-nested/style.module.css
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
.red {
|
||||
color: red;
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
export default function DeploymentsBreakdownPage(props) {
|
||||
return (
|
||||
<>
|
||||
<p>hello from app/dashboard/(custom)/deployments/breakdown</p>
|
||||
</>
|
||||
);
|
||||
}
|
||||
8
packages/next/test/fixtures/00-app-dir-trailing-slash/app/dashboard/(custom)/layout.js
vendored
Normal file
8
packages/next/test/fixtures/00-app-dir-trailing-slash/app/dashboard/(custom)/layout.js
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
export default function CustomDashboardRootLayout({ children }) {
|
||||
return (
|
||||
<>
|
||||
<h2>Custom dashboard</h2>
|
||||
{children}
|
||||
</>
|
||||
);
|
||||
}
|
||||
7
packages/next/test/fixtures/00-app-dir-trailing-slash/app/dashboard/deployments/[id]/page.js
vendored
Normal file
7
packages/next/test/fixtures/00-app-dir-trailing-slash/app/dashboard/deployments/[id]/page.js
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
export default function DeploymentsPage(props) {
|
||||
return (
|
||||
<>
|
||||
<p>hello from app/dashboard/deployments/[id]. ID is: {props.params.id}</p>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
export default function DeploymentsPage(props) {
|
||||
return (
|
||||
<>
|
||||
<p>
|
||||
hello from app/dashboard/deployments/[id]/settings. ID is:{' '}
|
||||
{props.params.id}
|
||||
</p>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
export default function Page() {
|
||||
return <p>catchall</p>;
|
||||
}
|
||||
7
packages/next/test/fixtures/00-app-dir-trailing-slash/app/dashboard/deployments/info/page.js
vendored
Normal file
7
packages/next/test/fixtures/00-app-dir-trailing-slash/app/dashboard/deployments/info/page.js
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
export default function DeploymentsInfoPage(props) {
|
||||
return (
|
||||
<>
|
||||
<p>hello from app/dashboard/deployments/info</p>
|
||||
</>
|
||||
);
|
||||
}
|
||||
8
packages/next/test/fixtures/00-app-dir-trailing-slash/app/dashboard/deployments/layout.js
vendored
Normal file
8
packages/next/test/fixtures/00-app-dir-trailing-slash/app/dashboard/deployments/layout.js
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
export default function DeploymentsLayout({ message, children }) {
|
||||
return (
|
||||
<>
|
||||
<h2>Deployments hello</h2>
|
||||
{children}
|
||||
</>
|
||||
);
|
||||
}
|
||||
7
packages/next/test/fixtures/00-app-dir-trailing-slash/app/dashboard/integrations/page.js
vendored
Normal file
7
packages/next/test/fixtures/00-app-dir-trailing-slash/app/dashboard/integrations/page.js
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
export default function IntegrationsPage(props) {
|
||||
return (
|
||||
<>
|
||||
<p>hello from app/dashboard/integrations</p>
|
||||
</>
|
||||
);
|
||||
}
|
||||
8
packages/next/test/fixtures/00-app-dir-trailing-slash/app/dashboard/layout.js
vendored
Normal file
8
packages/next/test/fixtures/00-app-dir-trailing-slash/app/dashboard/layout.js
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
export default function DashboardLayout(props) {
|
||||
return (
|
||||
<>
|
||||
<h1>Dashboard</h1>
|
||||
{props.children}
|
||||
</>
|
||||
);
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user