Compare commits

...

50 Commits

Author SHA1 Message Date
Steven
40f38948a0 Publish Stable
- @vercel/build-utils@5.5.5
 - vercel@28.4.11
 - @vercel/client@12.2.13
 - @vercel/frameworks@1.1.8
 - @vercel/fs-detectors@3.4.5
 - @vercel/go@2.2.13
 - @vercel/hydrogen@0.0.26
 - @vercel/next@3.2.5
 - @vercel/node@2.5.25
 - @vercel/python@3.1.22
 - @vercel/redwood@1.0.31
 - @vercel/remix@1.0.32
 - @vercel/ruby@1.3.39
 - @vercel/static-build@1.0.32
2022-10-16 14:11:02 -04:00
Sean Massa
87eba56063 [node] update typescript detection message (#8727)
When deploying a project that uses typescript, but typescript is not a dependency, the default typescript is used. The message that's logged when this happens says:

> Using TypeScript 4.3.4 (no local tsconfig.json)

which is not necessarily true. You can have a `tsconfig.json` file with no typescript dependency.

This message leads to bad debugging paths.

This PR updates the message to be more specific. Now the message will say:

> Using TypeScript 4.3.4 (no local "typescript" package detected)
2022-10-14 21:31:13 +00:00
JJ Kasper
d0a5676c26 [next] Fix index rsc route handling (#8729)
### Related Issues

This ensures we probably route the `/` rsc route properly and adds a regression test along with enabling some now patched tests that were skipped. 

Fixes: [slack thread](https://vercel.slack.com/archives/C043ANYDB24/p1665746921485109)

### 📋 Checklist

<!--
  Please keep your PR as a Draft until the checklist is complete
-->

#### Tests

- [ ] The code changed/added as part of this PR has been covered with tests
- [ ] All tests pass locally with `yarn test-unit`

#### Code Review

- [ ] This PR has a concise title and thorough description useful to a reviewer
- [ ] Issue from task tracker has a link to this PR
2022-10-14 21:18:32 +00:00
Siarhei
da9fa997ed [tests] Update the checkout action to V3 in publish.yml (#8721) 2022-10-11 19:57:55 -04:00
Peter van der Zee
3d79a9d4d4 [build-utils] Cleanup getNodeVersion (#8720)
- `meta.isDev` is unconditionally read after the first check (even though it could technically still be `null`, ignoring typescript)
- `meta.isDev` is explicitly asserted to be falsy, since the first `if` will return early if it's truthy, so the later checks are all redundant
- added `?.` for the `packageJson?.engines?.node` read

The rest is auto formatting (and if my local eslint warnings are correct then I'm just gonna close this PR...)
2022-10-11 23:47:52 +00:00
Sean Massa
ae13c5ee92 Publish Stable
- vercel@28.4.10
 - @vercel/frameworks@1.1.7
 - @vercel/fs-detectors@3.4.4
 - @vercel/node@2.5.24
 - @vercel/static-build@1.0.31
2022-10-11 12:43:42 -05:00
Jacob Ford
ad654139df [frameworks] Detect Hydrogen TypeScript config (#8711)
Co-authored-by: Sean Massa <EndangeredMassa@gmail.com>
2022-10-11 12:04:53 -05:00
Jacob Ford
74f8414e12 [frameworks] Detect additional valid Sanity v3 configs (#8710)
Co-authored-by: Steven <steven@ceriously.com>
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
Co-authored-by: Sean Massa <EndangeredMassa@gmail.com>
2022-10-11 11:02:27 -05:00
Kiko Beats
65c2860e14 [node] upgrade edge-runtime to v1.1.0-beta.38 (#8715)
It fixes an implementation detail issue users are facing when you try to redirect
2022-10-11 13:02:34 +00:00
JJ Kasper
3b5b397b35 Publish Stable
- vercel@28.4.9
 - @vercel/fs-detectors@3.4.3
 - @vercel/next@3.2.4
 - @vercel/node@2.5.23
2022-10-10 17:52:11 -07:00
JJ Kasper
051e061176 [next] Fix optional catch-all data route with middleware (#8713)
### Related Issues

This ensures we properly normalize the optional catch-all data route correctly with middleware as currently the route is expecting a repeated slash in the regex `(?:/(?` which it should just be `(?:(?`. This also adds a regression test for this specific case. 

Fixes: [slack thread](https://vercel.slack.com/archives/C045FKE5P51/p1665153317613089)

### 📋 Checklist

<!--
  Please keep your PR as a Draft until the checklist is complete
-->

#### Tests

- [ ] The code changed/added as part of this PR has been covered with tests
- [ ] All tests pass locally with `yarn test-unit`

#### Code Review

- [ ] This PR has a concise title and thorough description useful to a reviewer
- [ ] Issue from task tracker has a link to this PR
2022-10-10 23:50:20 +00:00
chloetedder
30d46321cc [fs-detectors] Add dark/light turborepo logo (#8694)
### 📋 Checklist

<!--
  Please keep your PR as a Draft until the checklist is complete
-->

#### Tests

- [ ] The code changed/added as part of this PR has been covered with
tests
- [ ] All tests pass locally with `yarn test-unit`

#### Code Review

- [ ] This PR has a concise title and thorough description useful to a
reviewer
- [ ] Issue from task tracker has a link to this PR
2022-10-07 12:22:41 -05:00
Christian Rackerseder
c2563535ea [node] Allow VercelApiHandler to also return a Promise (#8690)
### Related Issues

Otherwise you are not able to write a serverless function that returns a
Promise when using the ESLint rule
[@typescript-eslint/no-misused-promises](https://typescript-eslint.io/rules/no-misused-promises/)

Following function

```typescript
const handler: VercelApiHandler = async () => {};
```

will report a `Promise-returning function provided to variable where a
void return was expected`

### 📋 Checklist

<!--
  Please keep your PR as a Draft until the checklist is complete
-->

#### Tests

- [x] The code changed/added as part of this PR has been covered with
tests
- [x] All tests pass locally with `yarn test-unit`

#### Code Review

- [x] This PR has a concise title and thorough description useful to a
reviewer
- [ ] Issue from task tracker has a link to this PR
2022-10-07 12:59:12 -04:00
Steven
5f2bed4f24 [tests] Enable corepack for monorepo (#8691)
This will ensure yarn 1 is used by contributors
2022-10-06 18:07:22 +00:00
chloetedder
cfb7946f4b Publish Stable
- vercel@28.4.8
 - @vercel/fs-detectors@3.4.2
2022-10-06 09:51:35 -05:00
chloetedder
05c5b3a80d [fs-detectors] Add setting placeholders for monorepos (#8688)
### Related Issues

Add in placeholder settings for monorepos

### 📋 Checklist

<!--
  Please keep your PR as a Draft until the checklist is complete
-->

#### Tests

- [ ] The code changed/added as part of this PR has been covered with tests
- [ ] All tests pass locally with `yarn test-unit`

#### Code Review

- [ ] This PR has a concise title and thorough description useful to a reviewer
- [ ] Issue from task tracker has a link to this PR
2022-10-05 21:34:00 +00:00
Nathan Rajlich
45bd855250 Publish Stable
- @vercel/build-utils@5.5.4
 - vercel@28.4.7
 - @vercel/client@12.2.12
 - @vercel/go@2.2.12
 - @vercel/hydrogen@0.0.25
 - @vercel/next@3.2.3
 - @vercel/node@2.5.22
 - @vercel/python@3.1.21
 - @vercel/redwood@1.0.30
 - @vercel/remix@1.0.31
 - @vercel/ruby@1.3.38
 - @vercel/static-build@1.0.30
2022-10-05 12:41:42 -07:00
Nathan Rajlich
49de8ad9a0 [node] Update edge-runtime to v1.1.0-beta.37 (#8687)
Fixes error:

```
ENOENT: no such file or directory, open 'querystring'
```

Unfortunately this issue would only manifest when installed externally. I.e. our tests didn't catch this since the `querystring` module is presumably present in the monorepo.
2022-10-05 19:33:53 +00:00
JJ Kasper
a1ea56fd67 [next] Update max route src check for generated route (#8689)
### Related Issues

This reduces the max length we check for when generating routes to ensure we stay under the 4096 limit after normalizing. 

x-ref: https://github.com/vercel/customer-issues/issues/779

### 📋 Checklist

<!--
  Please keep your PR as a Draft until the checklist is complete
-->

#### Tests

- [ ] The code changed/added as part of this PR has been covered with tests
- [ ] All tests pass locally with `yarn test-unit`

#### Code Review

- [ ] This PR has a concise title and thorough description useful to a reviewer
- [ ] Issue from task tracker has a link to this PR
2022-10-05 19:18:41 +00:00
JJ Kasper
e88addc9ed [next] Fix legacy pages/404 case (#8682)
### Related Issues

This ensures we handle the case were a lambda isn't present for `pages/404.js` with `getStaticProps` which can occur in older Next.js versions e.g. `v9.5.5`. This also adds a regression test for this specific version to ensure it is working as expected. 

x-ref: https://github.com/vercel/vercel/pull/8663
Fixes: [slack thread](https://vercel.slack.com/archives/C03DQ3QFV7C/p1664945825621409)

### 📋 Checklist

<!--
  Please keep your PR as a Draft until the checklist is complete
-->

#### Tests

- [ ] The code changed/added as part of this PR has been covered with tests
- [ ] All tests pass locally with `yarn test-unit`

#### Code Review

- [ ] This PR has a concise title and thorough description useful to a reviewer
- [ ] Issue from task tracker has a link to this PR
2022-10-05 14:21:10 +00:00
Peter van der Zee
5d50013f93 [build-utils] Allow file-ref sema to be controlled through env flag (#8681)
My IDE tells me `process` is unknown but mentions something about package.json so that may just be a superficial issue. I guess CI/CD will tell me soon enough.

This adds an env flag to override the file ref sema's so we can experiment with setting a higher sema.

One potential problem I'm seeing is that this is a generic sema for all the things that use this class. Not sure if that's going to work out as intended but in that case we'll have to find a different way :)
2022-10-05 13:21:27 +00:00
Lee Robinson
44e1eb3983 Update CLI README (#8675)
:another-one:
2022-10-04 04:48:10 +00:00
Lee Robinson
f8af013349 Update README (#8674)
Small changes 😄
2022-10-04 04:13:41 +00:00
Steven
972cc495ec [tests] Replace cancel-workflow-action with native cancel-in-progress (#8671)
This removes the `styfle/cancel-workflow-action` in favor of native GitHub Actions `cancel-in-progress`.

The cancel key is workflow+branch but we don't want to cancel on the `main` branch.

https://docs.github.com/en/actions/using-jobs/using-concurrency
2022-10-03 23:49:28 +00:00
Steven
1c580da3d8 [cli] Fix vc build to error early when runtime is discontinued (#8669)
This moves an existing error from the build container to `vercel build`.

Its rare, but [Vercel Runtimes](https://vercel.com/docs/runtimes) might target a discontinued [AWS Lambda Runtime](https://docs.aws.amazon.com/lambda/latest/dg/lambda-runtimes.html) so we should fail fast when we know this has happened in `vercel build`.

A test has been added to demonstrate the failure using an old PHP version.
2022-10-03 22:07:02 +00:00
Steven
244554ab1b [tests] Remove nodejs12.x tests (#8667)
Now that `nodejs12.x` has passed the sunset date, new deployments will fail so we need to update a few tests.

https://vercel.com/changelog/node-js-12-is-being-deprecated
2022-10-03 20:59:05 +00:00
Steven
053c185481 Publish Stable
- vercel@28.4.6
 - @vercel/client@12.2.11
 - @vercel/next@3.2.2
2022-10-03 10:07:07 -04:00
JJ Kasper
8805b586ea [next] Allow revalidating ISR 404 path itself (#8663)
### Related Issues

Fixes: https://vercel.slack.com/archives/C03S8ED1DKM/p1664521958768189

### 📋 Checklist

<!--
  Please keep your PR as a Draft until the checklist is complete
-->

#### Tests

- [ ] The code changed/added as part of this PR has been covered with tests
- [ ] All tests pass locally with `yarn test-unit`

#### Code Review

- [ ] This PR has a concise title and thorough description useful to a reviewer
- [ ] Issue from task tracker has a link to this PR
2022-10-02 13:09:24 +00:00
Chris Barber
681070ffa0 [tests] Adding test for next builder OS path separator for serverless file refs (#8661)
Here's the test for https://github.com/vercel/vercel/pull/8657.

#### Tests

- [x] The code changed/added as part of this PR has been covered with tests
- [x] All tests pass locally with `yarn test-unit`

#### Code Review

- [ ] This PR has a concise title and thorough description useful to a reviewer
- [ ] Issue from task tracker has a link to this PR
2022-09-30 18:29:39 +00:00
Chris Barber
362b17d60a [next] Use OS path separator to match serverless file references (#8657)
When running `vc build` for a Next.js app, the Next builder will execute the server build which performs several steps. One of the steps is to trace each serverless function for any referenced files, then the raw list of files is scrubbed and filtered. The filtering uses OS specific file path comparisons to see if a file is of interest. Since it's comparing OS specific paths, we need to use OS specific path separators.

During testing on Windows, the traced serverless functions file list was always empty.

### 📋 Checklist

<!--
  Please keep your PR as a Draft until the checklist is complete
-->

#### Tests

- [ ] The code changed/added as part of this PR has been covered with tests
- [x] All tests pass locally with `yarn test-unit`

#### Code Review

- [ ] This PR has a concise title and thorough description useful to a reviewer
- [ ] Issue from task tracker has a link to this PR
2022-09-30 15:25:33 +00:00
JJ Kasper
c7c9b1a791 [next] Update RSC header in has routes (#8651)
### Related Issues

x-ref: https://github.com/vercel/next.js/pull/40979

### 📋 Checklist

<!--
  Please keep your PR as a Draft until the checklist is complete
-->

#### Tests

- [ ] The code changed/added as part of this PR has been covered with
tests
- [ ] All tests pass locally with `yarn test-unit`

#### Code Review

- [ ] This PR has a concise title and thorough description useful to a
reviewer
- [ ] Issue from task tracker has a link to this PR
2022-09-28 13:24:01 -07:00
Nathan Rajlich
c42f309463 [cli] Print upload progress in increments of 25% when non-TTY (#8650)
When running `vc deploy` in a non-TTY context (i.e. CI), limit the number of progress updates to 25% increments (for a total of 5).

```
Uploading [--------------------] (0.0B/71.9MB)
Uploading [=====---------------] (18.0MB/71.9MB)
Uploading [==========----------] (36.0MB/71.9MB)
Uploading [===============-----] (54.0MB/71.9MB)
Uploading [====================] (71.9MB/71.9MB)
```

This avoids spamming the user logs with many progress updates.
2022-09-28 19:33:33 +00:00
Sean Massa
a0ead28369 [tests] replace spinner messages with normal output during tests (#8634)
Convert spinner output to simple prints during test runs. This makes it easier to write tests against the output of commands.
2022-09-28 17:52:40 +00:00
chloetedder
8814fc1515 Publish Stable
- @vercel/build-utils@5.5.3
 - vercel@28.4.5
 - @vercel/client@12.2.10
 - @vercel/fs-detectors@3.4.1
 - @vercel/go@2.2.11
 - @vercel/hydrogen@0.0.24
 - @vercel/next@3.2.1
 - @vercel/node@2.5.21
 - @vercel/python@3.1.20
 - @vercel/redwood@1.0.29
 - @vercel/remix@1.0.30
 - @vercel/ruby@1.3.37
 - @vercel/static-build@1.0.29
2022-09-28 10:14:27 -05:00
chloetedder
0d044b4eac [fs-detectors] Use json5 parser for Rush to have valid json parsing (#8645)
### Related Issues

Parse `rush.json` files with `json5` because it is very common for these
to have comments in them

[Template for people to clone for
Rush](https://rushjs.io/pages/configs/rush_json/) which has comments in
it as a default which most people will clone
 
Docs of Rush showing to not use `JSON.parse`

https://rushjs.io/pages/help/faq/#why-do-rushs-json-config-files-contain--comments-that-github-shows-in-red

Added in tests with block comments and single line comments

### 📋 Checklist

<!--
  Please keep your PR as a Draft until the checklist is complete
-->

#### Tests

- [ ] The code changed/added as part of this PR has been covered with
tests
- [ ] All tests pass locally with `yarn test-unit`

#### Code Review

- [ ] This PR has a concise title and thorough description useful to a
reviewer
- [ ] Issue from task tracker has a link to this PR

Co-authored-by: Sean Massa <EndangeredMassa@gmail.com>
2022-09-28 06:38:25 -05:00
Steven
f6bd1aa8c0 [tests] Remove console.log() from test (#8647)
Remove `console.log()` from test
2022-09-27 23:38:49 +00:00
Steven
8cd84ec066 Publish Stable
- @vercel/build-utils@5.5.2
 - vercel@28.4.4
 - @vercel/client@12.2.9
 - @vercel/go@2.2.10
 - @vercel/hydrogen@0.0.23
 - @vercel/next@3.2.0
 - @vercel/node@2.5.20
 - @vercel/python@3.1.19
 - @vercel/redwood@1.0.28
 - @vercel/remix@1.0.29
 - @vercel/ruby@1.3.36
 - @vercel/static-build@1.0.28
2022-09-27 16:35:00 -04:00
Steven
a8df231e4c [build-utils] Fix npm version detection for --legacy-peer-deps (#8646)
There was a case where the npm version wasn't decided base on Node.js version but instead based on the lockfile.
This PR fixes the case when a newer npm version is detected base on the lockfile.

- Follow up to #8598
- Follow up to #8550
2022-09-27 16:33:24 -04:00
Gal Schlezinger
f674842bed [next] read regions from middleware-manifest output (#8629)
### Related Issues

- Needs https://github.com/vercel/next.js/pull/40881 for this to be
useful
- Resolves EC-238

### 📋 Checklist

<!--
  Please keep your PR as a Draft until the checklist is complete
-->

#### Tests

- [ ] The code changed/added as part of this PR has been covered with
tests
- [ ] All tests pass locally with `yarn test-unit`

#### Code Review

- [ ] This PR has a concise title and thorough description useful to a
reviewer
- [ ] Issue from task tracker has a link to this PR

Co-authored-by: Steven <steven@ceriously.com>
Co-authored-by: JJ Kasper <jj@jjsweb.site>
2022-09-27 10:46:02 -07:00
Steven
bf5cfa9a41 Publish Stable
- @vercel/build-utils@5.5.1
 - vercel@28.4.3
 - @vercel/client@12.2.8
 - @vercel/go@2.2.9
 - @vercel/hydrogen@0.0.22
 - @vercel/next@3.1.30
 - @vercel/node@2.5.19
 - @vercel/python@3.1.18
 - @vercel/redwood@1.0.27
 - @vercel/remix@1.0.28
 - @vercel/ruby@1.3.35
 - @vercel/static-build@1.0.27
2022-09-27 09:39:04 -04:00
JJ Kasper
12121b7a71 [next] Fix build time 404 route with trailingSlash: true (#8627)
This ensures we properly handle trailing slashes in the `notFound: true` build time routes. 

Fixes: [slack thread](https://vercel.slack.com/archives/C03S8ED1DKM/p1663691719703509)
2022-09-27 09:21:49 -04:00
Steven
baa56aed2c [tests] Fix timeout for actions/setup-node (#8639)
Try fixing the timeout again. For example: https://github.com/vercel/vercel/actions/runs/3130757219/jobs/5081381465

- Follow up to #8613 
- Related to https://github.com/actions/cache/issues/810 
- Related to https://github.com/Azure/azure-sdk-for-js/issues/22321
2022-09-26 18:33:43 -04:00
Steven
6f767367e4 [build-utils] Adjust nodejs12.x discontinueDate to Monday (#8638)
The previous date was on a Saturday so lets move it to the following Monday to ensure support tickets are quickly answered.
2022-09-26 22:22:32 +00:00
Sean Massa
0e4124f94c [cli] fix vc bisect off by one (#8399)
Co-authored-by: Nathan Rajlich <n@n8.io>
2022-09-26 14:47:15 -05:00
Sean Massa
30503d0a3f [go] remove unused, breaking watchlist from Go builder (#8633) 2022-09-26 14:31:36 -05:00
Steven
6c9164f67d [cli] Refactor doBuild to return void (#8626)
This PR refactor `doBuild()` to return `void`.

This will prevent accidental bugs like #8623 where an exit code number was returned instead of throwing on error.
2022-09-24 01:25:59 +00:00
Steven
906b7a8f2c [cli] Fix invalid vercel.json config error serialization (#8623)
Follow up to #8622 since we should be throwing errors so the error is correctly serialized to `builds.json`.
2022-09-23 22:08:53 +00:00
Steven
43499b13d8 [cli] Add vercel.json validation to vercel build (#8622)
We were doing this validation in `vercel dev` but not `vercel build`.

This PR adds `vercel.json` validation to `vercel build` too.

Note I am calling this a patch because invalid `vercel.json` was already failing when passed to the API so this allows a nice error message earlier in the process.
2022-09-23 20:33:28 +00:00
Sean Massa
7d6e56670f [cli][dev] Add strict mode to vc dev edge function handlers (#8616)
Add strict mode to `vc dev` edge function handlers. This is behind a flag in production, but that flag has been at 100% for a while. So, it seems safe to include it here unconditionally.

Also remove legal comments.

These changes bring `vc dev` edge function support closer to production.
2022-09-23 14:23:03 +00:00
Sean Massa
dba337f148 [cli][dev] extract edge/serverless handler logic into separate files (#8615) 2022-09-22 16:34:32 -05:00
138 changed files with 2598 additions and 983 deletions

View File

@@ -13,6 +13,7 @@ To get started, execute the following:
```
git clone https://github.com/vercel/vercel
cd vercel
corepack enable
yarn install
yarn bootstrap
yarn build

View File

@@ -1,17 +0,0 @@
name: Cancel
on:
push:
branches:
- '**'
- '!main'
jobs:
cancel:
name: 'Cancel Previous Runs'
runs-on: ubuntu-latest
timeout-minutes: 2
steps:
- uses: styfle/cancel-workflow-action@0.9.1
with:
workflow_id: test.yml, test-integration-cli.yml, test-unit.yml
access_token: ${{ github.token }}

View File

@@ -18,7 +18,9 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v1
uses: actions/checkout@v3
with:
fetch-depth: 0
- name: Check Release
id: check-release
run: |
@@ -37,8 +39,7 @@ jobs:
- name: Setup Node
if: ${{ steps.check-release.outputs.IS_RELEASE == 'true' }}
uses: actions/setup-node@v3
env:
SEGMENT_DOWNLOAD_TIMEOUT_MIN: 5 # https://github.com/actions/cache/issues/810
timeout-minutes: 5 # See https://github.com/actions/cache/issues/810
with:
node-version: 14
cache: 'yarn'

View File

@@ -13,6 +13,10 @@ env:
TURBO_TEAM: 'vercel'
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: ${{ github.ref != 'refs/heads/main' }}
jobs:
test:
name: CLI
@@ -31,8 +35,7 @@ jobs:
with:
go-version: '1.13.15'
- uses: actions/setup-node@v3
env:
SEGMENT_DOWNLOAD_TIMEOUT_MIN: 5 # https://github.com/actions/cache/issues/810
timeout-minutes: 5 # See https://github.com/actions/cache/issues/810
with:
node-version: ${{ matrix.node }}
cache: 'yarn'

View File

@@ -13,6 +13,10 @@ env:
TURBO_TEAM: 'vercel'
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: ${{ github.ref != 'refs/heads/main' }}
jobs:
test:
name: Unit
@@ -31,8 +35,7 @@ jobs:
with:
fetch-depth: 2
- uses: actions/setup-node@v3
env:
SEGMENT_DOWNLOAD_TIMEOUT_MIN: 5 # https://github.com/actions/cache/issues/810
timeout-minutes: 5 # See https://github.com/actions/cache/issues/810
with:
node-version: ${{ matrix.node }}
cache: 'yarn'

View File

@@ -14,6 +14,10 @@ env:
TURBO_TEAM: 'vercel'
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: ${{ github.ref != 'refs/heads/main' }}
jobs:
setup:
name: Find Changes
@@ -29,8 +33,7 @@ jobs:
with:
go-version: '1.13.15'
- uses: actions/setup-node@v3
env:
SEGMENT_DOWNLOAD_TIMEOUT_MIN: 5 # https://github.com/actions/cache/issues/810
timeout-minutes: 5 # See https://github.com/actions/cache/issues/810
with:
node-version: ${{ env.NODE_VERSION }}
cache: 'yarn'
@@ -67,8 +70,7 @@ jobs:
with:
go-version: '1.13.15'
- uses: actions/setup-node@v3
env:
SEGMENT_DOWNLOAD_TIMEOUT_MIN: 5 # https://github.com/actions/cache/issues/810
timeout-minutes: 5 # See https://github.com/actions/cache/issues/810
with:
node-version: ${{ env.NODE_VERSION }}
cache: 'yarn'

View File

@@ -380,8 +380,8 @@ This is a [class](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Refere
This is an abstract enumeration type that is implemented by one of the following possible `String` values:
- `nodejs16.x`
- `nodejs14.x`
- `nodejs12.x`
- `go1.x`
- `java11`
- `python3.9`

View File

@@ -19,11 +19,9 @@
## Vercel
Vercel is a platform for **static sites and frontend frameworks**, built to integrate with your headless content, commerce, or database.
Vercel is the platform for frontend developers, providing the speed and reliability innovators need to create at the moment of inspiration.
We provide a **frictionless developer experience** to take care of the hard things: deploy instantly, scale automatically, and serve personalized content around the globe.
We make it easy for frontend teams to **develop, preview, and ship** delightful user experiences, where performance is the default.
We enable teams to iterate quickly and develop, preview, and ship delightful user experiences. Vercel has zero-configuration support for 35+ frontend frameworks and integrates with your headless content, commerce, or database of choice.
## Deploy

View File

@@ -18,6 +18,12 @@ async function main() {
{ recursive: true, force: true }
);
await fs.cp(
join(repoRoot, 'packages', 'fs-detectors', 'logos'),
join(pubDir, 'monorepo-logos'),
{ recursive: true, force: true }
);
const examples = await getExampleList();
const pathListAll = join(pubDir, 'list-all.json');
await fs.writeFile(pathListAll, JSON.stringify(examples));

View File

@@ -3,6 +3,7 @@
"version": "0.0.0",
"private": true,
"license": "Apache-2.0",
"packageManager": "yarn@1.22.19",
"workspaces": {
"packages": [
"packages/*"

View File

@@ -1,6 +1,6 @@
{
"name": "@vercel/build-utils",
"version": "5.5.0",
"version": "5.5.5",
"license": "MIT",
"main": "./dist/index.js",
"types": "./dist/index.d.js",

View File

@@ -61,14 +61,14 @@ export function getPrettyError(obj: {
}
return new NowBuildError({
code: 'DEV_VALIDATE_CONFIG',
code: 'INVALID_VERCEL_CONFIG',
message: message,
link: prop ? `${docsUrl}#project/${prop.toLowerCase()}` : docsUrl,
action: 'View Documentation',
});
} catch (e) {
return new NowBuildError({
code: 'DEV_VALIDATE_CONFIG',
code: 'INVALID_VERCEL_CONFIG',
message: `Failed to validate configuration.`,
link: docsUrl,
action: 'View Documentation',

View File

@@ -5,7 +5,13 @@ import path from 'path';
import Sema from 'async-sema';
import { FileBase } from './types';
const semaToPreventEMFILE = new Sema(20);
const DEFAULT_SEMA = 20;
const semaToPreventEMFILE = new Sema(
parseInt(
process.env.VERCEL_INTERNAL_FILE_FS_REF_SEMA || String(DEFAULT_SEMA),
10
) || DEFAULT_SEMA
);
interface FileFsRefOptions {
mode?: number;

View File

@@ -12,7 +12,13 @@ interface FileRefOptions {
mutable?: boolean;
}
const semaToDownloadFromS3 = new Sema(5);
const DEFAULT_SEMA = 5;
const semaToDownloadFromS3 = new Sema(
parseInt(
process.env.VERCEL_INTERNAL_FILE_REF_SEMA || String(DEFAULT_SEMA),
10
) || DEFAULT_SEMA
);
class BailableError extends Error {
public bail: boolean;

View File

@@ -10,7 +10,7 @@ const allOptions = [
major: 12,
range: '12.x',
runtime: 'nodejs12.x',
discontinueDate: new Date('2022-10-01'),
discontinueDate: new Date('2022-10-03'),
},
{
major: 10,

View File

@@ -251,33 +251,24 @@ export async function getNodeVersion(
meta: Meta = {}
): Promise<NodeVersion> {
const latest = getLatestNodeVersion();
if (meta && meta.isDev) {
if (meta.isDev) {
// Use the system-installed version of `node` in PATH for `vercel dev`
return { ...latest, runtime: 'nodejs' };
}
const { packageJson } = await scanParentDirs(destPath, true);
let { nodeVersion } = config;
let isAuto = true;
if (packageJson && packageJson.engines && packageJson.engines.node) {
if (packageJson?.engines?.node) {
const { node } = packageJson.engines;
if (
nodeVersion &&
validRange(node) &&
!intersects(nodeVersion, node) &&
!meta.isDev
) {
if (nodeVersion && validRange(node) && !intersects(nodeVersion, node)) {
console.warn(
`Warning: Due to "engines": { "node": "${node}" } in your \`package.json\` file, the Node.js Version defined in your Project Settings ("${nodeVersion}") will not apply. Learn More: http://vercel.link/node-version`
);
} else if (coerce(node)?.raw === node && !meta.isDev) {
} else if (coerce(node)?.raw === node) {
console.warn(
`Warning: Detected "engines": { "node": "${node}" } in your \`package.json\` with major.minor.patch, but only major Node.js Version can be selected. Learn More: http://vercel.link/node-version`
);
} else if (
validRange(node) &&
intersects(`${latest.major + 1}.x`, node) &&
!meta.isDev
) {
} else if (validRange(node) && intersects(`${latest.major + 1}.x`, node)) {
console.warn(
`Warning: Detected "engines": { "node": "${node}" } in your \`package.json\` that will automatically upgrade when a new major Node.js Version is released. Learn More: http://vercel.link/node-version`
);
@@ -461,7 +452,8 @@ export async function runNpmInstall(
let commandArgs: string[];
const isPotentiallyBrokenNpm =
cliType === 'npm' &&
nodeVersion?.major === 16 &&
(nodeVersion?.major === 16 ||
opts.env.PATH?.includes('/node16/bin-npm7')) &&
!args.includes('--legacy-peer-deps') &&
spawnOpts?.env?.ENABLE_EXPERIMENTAL_COREPACK !== '1';

View File

@@ -0,0 +1,2 @@
node_modules
.vercel

View File

@@ -0,0 +1,64 @@
{
"requires": true,
"lockfileVersion": 1,
"dependencies": {
"js-tokens": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
"integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="
},
"loose-envify": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
"integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
"requires": {
"js-tokens": "^3.0.0 || ^4.0.0"
}
},
"object-assign": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
"integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg=="
},
"prop-types": {
"version": "15.8.1",
"resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
"integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==",
"requires": {
"loose-envify": "^1.4.0",
"object-assign": "^4.1.1",
"react-is": "^16.13.1"
}
},
"react": {
"version": "16.8.0",
"resolved": "https://registry.npmjs.org/react/-/react-16.8.0.tgz",
"integrity": "sha512-g+nikW2D48kqgWSPwNo0NH9tIGG3DsQFlrtrQ1kj6W77z5ahyIHG0w8kPpz4Sdj6gyLnz0lEd/xsjOoGge2MYQ==",
"requires": {
"loose-envify": "^1.1.0",
"object-assign": "^4.1.1",
"prop-types": "^15.6.2",
"scheduler": "^0.13.0"
}
},
"react-is": {
"version": "16.13.1",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="
},
"scheduler": {
"version": "0.13.6",
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.13.6.tgz",
"integrity": "sha512-IWnObHt413ucAYKsD9J1QShUKkbKLQQHdxRyw73sw4FN26iWr3DY/H34xGPe4nmL1DwXyWmSWmMrA9TfQbE/XQ==",
"requires": {
"loose-envify": "^1.1.0",
"object-assign": "^4.1.1"
}
},
"swr": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/swr/-/swr-1.3.0.tgz",
"integrity": "sha512-dkghQrOl2ORX9HYrMDtPa7LTVHJjCTeZoB1dqTbnnEDlSvN8JEKpYIYurDfvbQFUUS8Cg8PceFVZNkW0KNNYPw=="
}
}
}

View File

@@ -0,0 +1,11 @@
{
"private": true,
"scripts": {
"build": "mkdir -p public && echo 'legacy peer deps' > public/index.txt"
},
"packageManager": "npm@6.14.17",
"dependencies": {
"swr": "1.3.0",
"react": "16.8.0"
}
}

View File

@@ -0,0 +1,3 @@
{
"probes": [{ "path": "/", "mustContain": "legacy peer deps" }]
}

View File

@@ -34,7 +34,7 @@ it('should not include peer dependencies when missing VERCEL_NPM_LEGACY_PEER_DEP
const fixture = path.join(__dirname, 'fixtures', '20-npm-7');
const meta: Meta = {};
const spawnOpts = getTestSpawnOpts({});
const nodeVersion = { major: 16 } as any;
const nodeVersion = getNodeVersion(16);
await runNpmInstall(fixture, [], spawnOpts, meta, nodeVersion);
expect(spawnMock.mock.calls.length).toBe(1);
const args = spawnMock.mock.calls[0];
@@ -71,10 +71,35 @@ it('should include peer dependencies when VERCEL_NPM_LEGACY_PEER_DEPS=1 on node1
});
});
it('should not include peer dependencies when VERCEL_NPM_LEGACY_PEER_DEPS=1 on node14', async () => {
it('should include peer dependencies when VERCEL_NPM_LEGACY_PEER_DEPS=1 on node14 and npm7+', async () => {
const fixture = path.join(__dirname, 'fixtures', '20-npm-7');
const meta: Meta = {};
const spawnOpts = getTestSpawnOpts({ VERCEL_NPM_LEGACY_PEER_DEPS: '1' });
const nodeVersion = getNodeVersion(14);
await runNpmInstall(fixture, [], spawnOpts, meta, nodeVersion);
expect(spawnMock.mock.calls.length).toBe(1);
const args = spawnMock.mock.calls[0];
expect(args[0]).toEqual('npm');
expect(args[1]).toEqual([
'install',
'--no-audit',
'--unsafe-perm',
'--legacy-peer-deps',
]);
expect(args[2]).toEqual({
cwd: fixture,
prettyCommand: 'npm install',
stdio: 'inherit',
env: expect.any(Object),
});
});
it('should not include peer dependencies when VERCEL_NPM_LEGACY_PEER_DEPS=1 on node14 and npm6', async () => {
const fixture = path.join(__dirname, 'fixtures', '14-npm-6-legacy-peer-deps');
const meta: Meta = {};
const spawnOpts = getTestSpawnOpts({ VERCEL_NPM_LEGACY_PEER_DEPS: '1' });
const nodeVersion = getNodeVersion(14);
await runNpmInstall(fixture, [], spawnOpts, meta, nodeVersion);
expect(spawnMock.mock.calls.length).toBe(1);

View File

@@ -216,10 +216,6 @@ it('should download symlinks even with incorrect file', async () => {
});
it('should only match supported node versions, otherwise throw an error', async () => {
expect(await getSupportedNodeVersion('12.x', false)).toHaveProperty(
'major',
12
);
expect(await getSupportedNodeVersion('14.x', false)).toHaveProperty(
'major',
14
@@ -240,10 +236,6 @@ it('should only match supported node versions, otherwise throw an error', async
await expectBuilderError(getSupportedNodeVersion('foo', true), autoMessage);
await expectBuilderError(getSupportedNodeVersion('=> 10', true), autoMessage);
expect(await getSupportedNodeVersion('12.x', true)).toHaveProperty(
'major',
12
);
expect(await getSupportedNodeVersion('14.x', true)).toHaveProperty(
'major',
14
@@ -273,21 +265,21 @@ it('should only match supported node versions, otherwise throw an error', async
it('should match all semver ranges', async () => {
// See https://docs.npmjs.com/files/package.json#engines
expect(await getSupportedNodeVersion('12.0.0')).toHaveProperty('major', 12);
expect(await getSupportedNodeVersion('12.x')).toHaveProperty('major', 12);
expect(await getSupportedNodeVersion('14.0.0')).toHaveProperty('major', 14);
expect(await getSupportedNodeVersion('14.x')).toHaveProperty('major', 14);
expect(await getSupportedNodeVersion('>=10')).toHaveProperty('major', 16);
expect(await getSupportedNodeVersion('>=10.3.0')).toHaveProperty('major', 16);
expect(await getSupportedNodeVersion('11.5.0 - 12.5.0')).toHaveProperty(
expect(await getSupportedNodeVersion('16.5.0 - 16.9.0')).toHaveProperty(
'major',
12
16
);
expect(await getSupportedNodeVersion('>=9.5.0 <=12.5.0')).toHaveProperty(
expect(await getSupportedNodeVersion('>=9.5.0 <=14.5.0')).toHaveProperty(
'major',
12
14
);
expect(await getSupportedNodeVersion('~12.5.0')).toHaveProperty('major', 12);
expect(await getSupportedNodeVersion('^12.5.0')).toHaveProperty('major', 12);
expect(await getSupportedNodeVersion('12.5.0 - 14.5.0')).toHaveProperty(
expect(await getSupportedNodeVersion('~14.5.0')).toHaveProperty('major', 14);
expect(await getSupportedNodeVersion('^14.5.0')).toHaveProperty('major', 14);
expect(await getSupportedNodeVersion('14.5.0 - 14.20.0')).toHaveProperty(
'major',
14
);
@@ -434,8 +426,8 @@ it('should warn for deprecated versions, soon to be discontinued', async () => {
expect(warningMessages).toStrictEqual([
'Error: Node.js version 10.x has reached End-of-Life. Deployments created on or after 2021-04-20 will fail to build. Please set "engines": { "node": "16.x" } in your `package.json` file to use Node.js 16.',
'Error: Node.js version 10.x has reached End-of-Life. Deployments created on or after 2021-04-20 will fail to build. Please set Node.js Version to 16.x in your Project Settings to use Node.js 16.',
'Error: Node.js version 12.x has reached End-of-Life. Deployments created on or after 2022-10-01 will fail to build. Please set "engines": { "node": "16.x" } in your `package.json` file to use Node.js 16.',
'Error: Node.js version 12.x has reached End-of-Life. Deployments created on or after 2022-10-01 will fail to build. Please set Node.js Version to 16.x in your Project Settings to use Node.js 16.',
'Error: Node.js version 12.x has reached End-of-Life. Deployments created on or after 2022-10-03 will fail to build. Please set "engines": { "node": "16.x" } in your `package.json` file to use Node.js 16.',
'Error: Node.js version 12.x has reached End-of-Life. Deployments created on or after 2022-10-03 will fail to build. Please set Node.js Version to 16.x in your Project Settings to use Node.js 16.',
]);
global.Date.now = realDateNow;

View File

@@ -10,11 +10,9 @@
## Usage
Vercel is a platform for **static sites and frontend frameworks**, built to integrate with your headless content, commerce, or database.
Vercel is the platform for frontend developers, providing the speed and reliability innovators need to create at the moment of inspiration.
We provide a **frictionless developer experience** to take care of the hard things: deploy instantly, scale automatically, and serve personalized content around the globe.
We make it easy for frontend teams to **develop, preview, and ship** delightful user experiences, where performance is the default.
We enable teams to iterate quickly and develop, preview, and ship delightful user experiences. Vercel has zero-configuration support for 35+ frontend frameworks and integrates with your headless content, commerce, or database of choice.
To install the latest version of Vercel CLI, run this command:

View File

@@ -1,6 +1,6 @@
{
"name": "vercel",
"version": "28.4.2",
"version": "28.4.11",
"preferGlobal": true,
"license": "Apache-2.0",
"description": "The command-line interface for Vercel",
@@ -41,16 +41,16 @@
"node": ">= 14"
},
"dependencies": {
"@vercel/build-utils": "5.5.0",
"@vercel/go": "2.2.8",
"@vercel/hydrogen": "0.0.21",
"@vercel/next": "3.1.29",
"@vercel/node": "2.5.18",
"@vercel/python": "3.1.17",
"@vercel/redwood": "1.0.26",
"@vercel/remix": "1.0.27",
"@vercel/ruby": "1.3.34",
"@vercel/static-build": "1.0.26",
"@vercel/build-utils": "5.5.5",
"@vercel/go": "2.2.13",
"@vercel/hydrogen": "0.0.26",
"@vercel/next": "3.2.5",
"@vercel/node": "2.5.25",
"@vercel/python": "3.1.22",
"@vercel/redwood": "1.0.31",
"@vercel/remix": "1.0.32",
"@vercel/ruby": "1.3.39",
"@vercel/static-build": "1.0.32",
"update-notifier": "5.1.0"
},
"devDependencies": {
@@ -95,9 +95,9 @@
"@types/which": "1.3.2",
"@types/write-json-file": "2.2.1",
"@types/yauzl-promise": "2.1.0",
"@vercel/client": "12.2.7",
"@vercel/frameworks": "1.1.6",
"@vercel/fs-detectors": "3.4.0",
"@vercel/client": "12.2.13",
"@vercel/frameworks": "1.1.8",
"@vercel/fs-detectors": "3.4.5",
"@vercel/fun": "1.0.4",
"@vercel/ncc": "0.24.0",
"@zeit/source-map-support": "0.6.2",

View File

@@ -2,7 +2,6 @@ import open from 'open';
import boxen from 'boxen';
import execa from 'execa';
import plural from 'pluralize';
import inquirer from 'inquirer';
import { resolve } from 'path';
import chalk, { Chalk } from 'chalk';
import { URLSearchParams, parse } from 'url';
@@ -150,7 +149,9 @@ export default async function main(client: Client): Promise<number> {
if (badDeployment) {
if (badDeployment instanceof Error) {
badDeployment.message += ` "${bad}"`;
badDeployment.message += ` when requesting bad deployment "${normalizeURL(
bad
)}"`;
output.prettyError(badDeployment);
return 1;
}
@@ -165,7 +166,9 @@ export default async function main(client: Client): Promise<number> {
if (goodDeployment) {
if (goodDeployment instanceof Error) {
goodDeployment.message += ` "${good}"`;
goodDeployment.message += ` when requesting good deployment "${normalizeURL(
good
)}"`;
output.prettyError(goodDeployment);
return 1;
}
@@ -226,7 +229,8 @@ export default async function main(client: Client): Promise<number> {
// If we have the "good" deployment in this chunk, then we're done
for (let i = 0; i < newDeployments.length; i++) {
if (newDeployments[i].url === good) {
newDeployments = newDeployments.slice(0, i + 1);
// grab all deployments up until the good one
newDeployments = newDeployments.slice(0, i);
next = undefined;
break;
}
@@ -316,7 +320,7 @@ export default async function main(client: Client): Promise<number> {
if (openEnabled) {
await open(testUrl);
}
const answer = await inquirer.prompt({
const answer = await client.prompt({
type: 'expand',
name: 'action',
message: 'Select an action:',

View File

@@ -3,6 +3,7 @@ import chalk from 'chalk';
import dotenv from 'dotenv';
import { join, normalize, relative, resolve } from 'path';
import {
getDiscontinuedNodeVersions,
normalizePath,
Files,
FileFsRef,
@@ -25,6 +26,7 @@ import {
MergeRoutesProps,
Route,
} from '@vercel/routing-utils';
import { fileNameSymbol } from '@vercel/client';
import type { VercelConfig } from '@vercel/client';
import pull from './pull';
@@ -54,6 +56,7 @@ import { importBuilders } from '../util/build/import-builders';
import { initCorepack, cleanupCorepack } from '../util/build/corepack';
import { sortBuilders } from '../util/build/sort-builders';
import { toEnumerableError } from '../util/error';
import { validateConfig } from '../util/validate-config';
type BuildResult = BuildResultV2 | BuildResultV3;
@@ -232,7 +235,8 @@ export default async function main(client: Client): Promise<number> {
process.env.VERCEL = '1';
process.env.NOW_BUILDER = '1';
return await doBuild(client, project, buildsJson, cwd, outputDir);
await doBuild(client, project, buildsJson, cwd, outputDir);
return 0;
} catch (err: any) {
output.prettyError(err);
@@ -265,23 +269,36 @@ async function doBuild(
buildsJson: BuildsManifest,
cwd: string,
outputDir: string
): Promise<number> {
): Promise<void> {
const { output } = client;
const workPath = join(cwd, project.settings.rootDirectory || '.');
// Load `package.json` and `vercel.json` files
const [pkg, vercelConfig] = await Promise.all([
const [pkg, vercelConfig, nowConfig] = await Promise.all([
readJSONFile<PackageJson>(join(workPath, 'package.json')),
readJSONFile<VercelConfig>(join(workPath, 'vercel.json')).then(
config => config || readJSONFile<VercelConfig>(join(workPath, 'now.json'))
),
readJSONFile<VercelConfig>(join(workPath, 'vercel.json')),
readJSONFile<VercelConfig>(join(workPath, 'now.json')),
]);
if (pkg instanceof CantParseJSONFile) throw pkg;
if (vercelConfig instanceof CantParseJSONFile) throw vercelConfig;
if (nowConfig instanceof CantParseJSONFile) throw nowConfig;
if (vercelConfig) {
vercelConfig[fileNameSymbol] = 'vercel.json';
} else if (nowConfig) {
nowConfig[fileNameSymbol] = 'now.json';
}
const localConfig = vercelConfig || nowConfig || {};
const validateError = validateConfig(localConfig);
if (validateError) {
throw validateError;
}
const projectSettings = {
...project.settings,
...pickOverrides(vercelConfig || {}),
...pickOverrides(localConfig),
};
// Get a list of source files
@@ -289,12 +306,12 @@ async function doBuild(
normalizePath(relative(workPath, f))
);
const routesResult = getTransformedRoutes(vercelConfig || {});
const routesResult = getTransformedRoutes(localConfig);
if (routesResult.error) {
throw routesResult.error;
}
if (vercelConfig?.builds && vercelConfig.functions) {
if (localConfig.builds && localConfig.functions) {
throw new NowBuildError({
code: 'bad_request',
message:
@@ -303,7 +320,7 @@ async function doBuild(
});
}
let builds = vercelConfig?.builds || [];
let builds = localConfig.builds || [];
let zeroConfigRoutes: Route[] = [];
let isZeroConfig = false;
@@ -318,7 +335,7 @@ async function doBuild(
// Detect the Vercel Builders that will need to be invoked
const detectedBuilders = await detectBuilders(files, pkg, {
...vercelConfig,
...localConfig,
projectSettings,
ignoreBuildScript: true,
featHandleMiss: true,
@@ -395,13 +412,10 @@ async function doBuild(
})
);
buildsJson.builds = Array.from(buildsJsonBuilds.values());
const buildsJsonPath = join(outputDir, 'builds.json');
const writeBuildsJsonPromise = fs.writeJSON(buildsJsonPath, buildsJson, {
await fs.writeJSON(join(outputDir, 'builds.json'), buildsJson, {
spaces: 2,
});
ops.push(writeBuildsJsonPromise);
// The `meta` config property is re-used for each Builder
// invocation so that Builders can share state between
// subsequent entrypoint builds.
@@ -454,6 +468,25 @@ async function doBuild(
);
const buildResult = await builder.build(buildOptions);
if (
buildResult &&
'output' in buildResult &&
'runtime' in buildResult.output &&
'type' in buildResult.output &&
buildResult.output.type === 'Lambda'
) {
const lambdaRuntime = buildResult.output.runtime;
if (
getDiscontinuedNodeVersions().some(o => o.runtime === lambdaRuntime)
) {
throw new NowBuildError({
code: 'NODEJS_DISCONTINUED_VERSION',
message: `The Runtime "${build.use}" is using "${lambdaRuntime}", which is discontinued. Please upgrade your Runtime to a more recent version or consult the author for more details.`,
link: 'https://github.com/vercel/vercel/blob/main/DEVELOPING_A_RUNTIME.md#lambdaruntime',
});
}
}
// Store the build result to generate the final `config.json` after
// all builds have completed
buildResults.set(build, buildResult);
@@ -466,7 +499,7 @@ async function doBuild(
build,
builder,
builderPkg,
vercelConfig
localConfig
).then(
override => {
if (override) overrides.push(override);
@@ -475,26 +508,11 @@ async function doBuild(
)
);
} catch (err: any) {
output.prettyError(err);
const writeConfigJsonPromise = fs.writeJSON(
join(outputDir, 'config.json'),
{ version: 3 },
{ spaces: 2 }
);
await Promise.all([writeBuildsJsonPromise, writeConfigJsonPromise]);
const buildJsonBuild = buildsJsonBuilds.get(build);
if (buildJsonBuild) {
buildJsonBuild.error = toEnumerableError(err);
await fs.writeJSON(buildsJsonPath, buildsJson, {
spaces: 2,
});
}
return 1;
throw err;
}
}
@@ -555,150 +573,7 @@ async function doBuild(
builds: builderRoutes,
});
const images = vercelConfig?.images
if (images) {
if (typeof images !== 'object') {
throw new Error(
`vercel.json "images" should be an object received ${typeof images}.`
);
}
if (!Array.isArray(images.domains)) {
throw new Error(
`vercel.json "images.domains" should be an Array received ${typeof images.domains}.`
);
}
if (images.domains.length > 50) {
throw new Error(
`vercel.json "images.domains" exceeds length of 50 received length (${images.domains.length}).`
);
}
const invalidImageDomains = images.domains.filter(
(d: unknown) => typeof d !== 'string'
);
if (invalidImageDomains.length > 0) {
throw new Error(
`vercel.json "images.domains" should be an Array of strings received invalid values (${invalidImageDomains.join(
', '
)}).`
);
}
if (images.remotePatterns) {
if (!Array.isArray(images.remotePatterns)) {
throw new Error(
`vercel.json "images.remotePatterns" should be an Array received ${typeof images.remotePatterns}.`
);
}
if (images.remotePatterns.length > 50) {
throw new Error(
`vercel.json "images.remotePatterns" exceeds length of 50, received length (${images.remotePatterns.length}).`
);
}
const validProps = new Set(['protocol', 'hostname', 'pathname', 'port']);
const requiredProps = ['hostname'];
const invalidPatterns = images.remotePatterns.filter(
(d: unknown) =>
!d ||
typeof d !== 'object' ||
Object.entries(d).some(
([k, v]) => !validProps.has(k) || typeof v !== 'string'
) ||
requiredProps.some(k => !(k in d))
);
if (invalidPatterns.length > 0) {
throw new Error(
`vercel.json "images.remotePatterns" received invalid values:\n${invalidPatterns
.map(item => JSON.stringify(item))
.join(
'\n'
)}\n\nremotePatterns value must follow format { protocol: 'https', hostname: 'example.com', port: '', pathname: '/imgs/**' }.`
);
}
}
if (!Array.isArray(images.sizes)) {
throw new Error(
`vercel.json "images.sizes" should be an Array received ${typeof images.sizes}.`
);
}
if (images.sizes.length < 1 || images.sizes.length > 50) {
throw new Error(
`vercel.json "images.sizes" should be an Array of length between 1 to 50 received length (${images.sizes.length}).`
);
}
const invalidImageSizes = images.sizes.filter((d: unknown) => {
return typeof d !== 'number' || d < 1 || d > 10000;
});
if (invalidImageSizes.length > 0) {
throw new Error(
`vercel.json "images.sizes" should be an Array of numbers that are between 1 and 10000, received invalid values (${invalidImageSizes.join(
', '
)}).`
);
}
if (images.minimumCacheTTL) {
if (
!Number.isInteger(images.minimumCacheTTL) ||
images.minimumCacheTTL < 0
) {
throw new Error(
`vercel.json "images.minimumCacheTTL" should be an integer 0 or more received (${images.minimumCacheTTL}).`
);
}
}
if (images.formats) {
if (!Array.isArray(images.formats)) {
throw new Error(
`vercel.json "images.formats" should be an Array received ${typeof images.formats}.`
);
}
if (images.formats.length < 1 || images.formats.length > 2) {
throw new Error(
`vercel.json "images.formats" must be length 1 or 2, received length (${images.formats.length}).`
);
}
const invalid = images.formats.filter(f => {
return f !== 'image/avif' && f !== 'image/webp';
});
if (invalid.length > 0) {
throw new Error(
`vercel.json "images.formats" should be an Array of mime type strings, received invalid values (${invalid.join(
', '
)}).`
);
}
}
if (
typeof images.dangerouslyAllowSVG !== 'undefined' &&
typeof images.dangerouslyAllowSVG !== 'boolean'
) {
throw new Error(
`vercel.json "images.dangerouslyAllowSVG" should be a boolean received (${images.dangerouslyAllowSVG}).`
);
}
if (
typeof images.contentSecurityPolicy !== 'undefined' &&
typeof images.contentSecurityPolicy !== 'string'
) {
throw new Error(
`vercel.json "images.contentSecurityPolicy" should be a string received ${images.contentSecurityPolicy}`
);
}
}
const mergedImages = mergeImages(images, buildResults.values());
const mergedImages = mergeImages(localConfig.images, buildResults.values());
const mergedWildcard = mergeWildcard(buildResults.values());
const mergedOverrides: Record<string, PathOverride> =
overrides.length > 0 ? Object.assign({}, ...overrides) : undefined;
@@ -724,8 +599,6 @@ async function doBuild(
emoji('success')
)}\n`
);
return 0;
}
function expandBuild(files: string[], build: Builder): Builder[] {

View File

@@ -23,6 +23,7 @@ import type {
} from '../types';
import { sharedPromise } from './promise';
import { APIError } from './errors-ts';
import { normalizeError } from './is-error';
const isSAMLError = (v: any): v is SAMLError => {
return v && v.saml;
@@ -146,10 +147,15 @@ export default class Client extends EventEmitter implements Stdio {
const error = await responseError(res);
if (isSAMLError(error)) {
// A SAML error means the token is expired, or is not
// designated for the requested team, so the user needs
// to re-authenticate
await this.reauthenticate(error);
try {
// A SAML error means the token is expired, or is not
// designated for the requested team, so the user needs
// to re-authenticate
await this.reauthenticate(error);
} catch (reauthError) {
// there's no sense in retrying
return bail(normalizeError(reauthError));
}
} else if (res.status >= 400 && res.status < 500) {
// Any other 4xx should bail without retrying
return bail(error);
@@ -186,7 +192,7 @@ export default class Client extends EventEmitter implements Stdio {
`Failed to re-authenticate for ${bold(error.scope)} scope`
);
}
process.exit(1);
throw error;
}
this.authConfig.token = result.token;

View File

@@ -115,29 +115,39 @@ export default async function processDeployment({
.reduce((a: number, b: number) => a + b, 0);
const totalSizeHuman = bytes.format(missingSize, { decimalPlaces: 1 });
uploads.forEach((e: any) =>
e.on('progress', () => {
const uploadedBytes = uploads.reduce((acc: number, e: any) => {
return acc + e.bytesUploaded;
}, 0);
// When stderr is not a TTY then we only want to
// print upload progress in 25% increments
let nextStep = 0;
const stepSize = now._client.stderr.isTTY ? 0 : 0.25;
const bar = progress(uploadedBytes, missingSize);
if (!bar || uploadedBytes === missingSize) {
output.spinner(deployingSpinnerVal, 0);
} else {
const uploadedHuman = bytes.format(uploadedBytes, {
decimalPlaces: 1,
fixedDecimals: true,
});
const updateProgress = () => {
const uploadedBytes = uploads.reduce((acc: number, e: any) => {
return acc + e.bytesUploaded;
}, 0);
const bar = progress(uploadedBytes, missingSize);
if (!bar) {
output.spinner(deployingSpinnerVal, 0);
} else {
const uploadedHuman = bytes.format(uploadedBytes, {
decimalPlaces: 1,
fixedDecimals: true,
});
const percent = uploadedBytes / missingSize;
if (percent >= nextStep) {
output.spinner(
`Uploading ${chalk.reset(
`[${bar}] (${uploadedHuman}/${totalSizeHuman})`
)}`,
0
);
nextStep += stepSize;
}
})
);
}
};
uploads.forEach((e: any) => e.on('progress', updateProgress));
updateProgress();
}
if (event.type === 'file-uploaded') {

View File

@@ -57,7 +57,7 @@ import { MissingDotenvVarsError } from '../errors-ts';
import cliPkg from '../pkg';
import { getVercelDirectory } from '../projects/link';
import { staticFiles as getFiles } from '../get-files';
import { validateConfig } from './validate';
import { validateConfig } from '../validate-config';
import { devRouter, getRoutesTypes } from './router';
import getMimeType from './mime-type';
import { executeBuild, getBuildMatches, shutdownBuilder } from './builder';

View File

@@ -4,6 +4,8 @@ import wait, { StopSpinner } from './wait';
import type { WritableTTY } from '../../types';
import { errorToString } from '../is-error';
const IS_TEST = process.env.NODE_ENV === 'test';
export interface OutputOptions {
debug?: boolean;
}
@@ -108,12 +110,15 @@ export class Output {
};
spinner = (message: string, delay: number = 300): void => {
this.spinnerMessage = message;
if (this.debugEnabled) {
this.debug(`Spinner invoked (${message}) with a ${delay}ms delay`);
return;
}
if (this.stream.isTTY) {
if (IS_TEST || !this.stream.isTTY) {
this.print(`${message}\n`);
} else {
this.spinnerMessage = message;
if (this._spinner) {
this._spinner.text = message;
} else {
@@ -125,8 +130,6 @@ export class Output {
delay
);
}
} else {
this.print(`${message}\n`);
}
};

View File

@@ -7,7 +7,7 @@ import {
rewritesSchema,
trailingSlashSchema,
} from '@vercel/routing-utils';
import { VercelConfig } from './types';
import { VercelConfig } from './dev/types';
import {
functionsSchema,
buildsSchema,
@@ -16,6 +16,83 @@ import {
} from '@vercel/build-utils';
import { fileNameSymbol } from '@vercel/client';
const imagesSchema = {
type: 'object',
additionalProperties: false,
required: ['sizes'],
properties: {
contentSecurityPolicy: {
type: 'string',
minLength: 1,
maxLength: 256,
},
dangerouslyAllowSVG: {
type: 'boolean',
},
domains: {
type: 'array',
minItems: 0,
maxItems: 50,
items: {
type: 'string',
minLength: 1,
maxLength: 256,
},
},
formats: {
type: 'array',
minItems: 1,
maxItems: 4,
items: {
enum: ['image/avif', 'image/webp', 'image/jpeg', 'image/png'],
},
},
minimumCacheTTL: {
type: 'integer',
minimum: 1,
maximum: 315360000,
},
remotePatterns: {
type: 'array',
minItems: 0,
maxItems: 50,
items: {
type: 'object',
additionalProperties: false,
required: ['hostname'],
properties: {
protocol: {
enum: ['http', 'https'],
},
hostname: {
type: 'string',
minLength: 1,
maxLength: 256,
},
port: {
type: 'string',
minLength: 1,
maxLength: 5,
},
pathname: {
type: 'string',
minLength: 1,
maxLength: 256,
},
},
},
},
sizes: {
type: 'array',
minItems: 1,
maxItems: 50,
items: {
type: 'number',
},
},
},
};
const vercelConfigSchema = {
type: 'object',
// These are not all possibilities because `vc dev`
@@ -30,6 +107,7 @@ const vercelConfigSchema = {
rewrites: rewritesSchema,
trailingSlash: trailingSlashSchema,
functions: functionsSchema,
images: imagesSchema,
},
};

View File

@@ -0,0 +1,2 @@
.vercel/builders
.vercel/output

View File

@@ -0,0 +1,7 @@
{
"orgId": ".",
"projectId": ".",
"settings": {
"framework": null
}
}

View File

@@ -0,0 +1 @@
<?php echo 'This version of vercel-php uses the nodejs12.x Lambda Runtime'; ?>

View File

@@ -0,0 +1,8 @@
{
"$schema": "https://openapi.vercel.sh/vercel.json",
"functions": {
"api/index.php": {
"runtime": "vercel-php@0.1.0"
}
}
}

View File

@@ -3,6 +3,6 @@
"sizes": [256, 384, 600, 1000],
"domains": [],
"minimumCacheTTL": 60,
"formats": ["image/webp", "image/avif"]
"formats": ["image/avif", "image/webp"]
}
}

View File

@@ -0,0 +1,7 @@
{
"orgId": ".",
"projectId": ".",
"settings": {
"framework": null
}
}

View File

@@ -0,0 +1 @@
<h1>Vercel</h1>

View File

@@ -0,0 +1,16 @@
{
"rewrites": [
{
"source": "/one",
"destination": "/api/one"
},
{
"source": "/two",
"destination": "/api/two"
},
{
"src": "/three",
"dest": "/api/three"
}
]
}

View File

@@ -326,7 +326,7 @@ module.exports = async function prepare(session, binaryPath, tmpFixturesDir) {
'vercel.json': JSON.stringify({
functions: {
'api/**/*.php': {
runtime: 'vercel-php@0.1.0',
runtime: 'vercel-php@0.5.2',
},
},
}),

View File

@@ -3,20 +3,26 @@ import chance from 'chance';
import { Deployment } from '@vercel/client';
import { client } from './client';
import { Build, User } from '../../src/types';
import type { Request, Response } from 'express';
let deployments = new Map<string, Deployment>();
let deploymentBuilds = new Map<Deployment, Build[]>();
let alreadySetupDeplomentEndpoints = false;
type State = Deployment['readyState'];
export function useDeployment({
creator,
state = 'READY',
createdAt,
}: {
creator: Pick<User, 'id' | 'email' | 'name' | 'username'>;
state?: State;
createdAt?: number;
}) {
const createdAt = Date.now();
setupDeploymentEndpoints();
createdAt = createdAt || Date.now();
const url = new URL(chance().url());
const name = chance().name();
const id = `dpl_${chance().guid()}`;
@@ -99,6 +105,15 @@ export function useDeploymentMissingProjectSettings() {
beforeEach(() => {
deployments = new Map();
deploymentBuilds = new Map();
alreadySetupDeplomentEndpoints = false;
});
function setupDeploymentEndpoints() {
if (alreadySetupDeplomentEndpoints) {
return;
}
alreadySetupDeplomentEndpoints = true;
client.scenario.get('/:version/deployments/:id', (req, res) => {
const { id } = req.params;
@@ -136,8 +151,21 @@ beforeEach(() => {
res.json({ builds });
});
client.scenario.get('/:version/now/deployments', (req, res) => {
const deploymentsList = Array.from(deployments.values());
res.json({ deployments: deploymentsList });
});
});
function handleGetDeployments(req: Request, res: Response) {
const currentDeployments = Array.from(deployments.values()).sort(
(a: Deployment, b: Deployment) => {
// sort in reverse chronological order
return b.createdAt - a.createdAt;
}
);
res.json({
pagination: {
count: currentDeployments.length,
},
deployments: currentDeployments,
});
}
client.scenario.get('/:version/now/deployments', handleGetDeployments);
client.scenario.get('/:version/deployments', handleGetDeployments);
}

View File

@@ -0,0 +1,55 @@
import { client } from '../../mocks/client';
import { useUser } from '../../mocks/user';
import bisect from '../../../src/commands/bisect';
import { useDeployment } from '../../mocks/deployment';
describe('bisect', () => {
it('should find the bad deployment', async () => {
const user = useUser();
const now = Date.now();
const deployment1 = useDeployment({ creator: user, createdAt: now });
const deployment2 = useDeployment({
creator: user,
createdAt: now + 10000,
});
const deployment3 = useDeployment({
creator: user,
createdAt: now + 20000,
});
// also create an extra deployment before the known good deployment
// to make sure the bisect pool doesn't include it
useDeployment({
creator: user,
createdAt: now - 30000,
});
const bisectPromise = bisect(client);
await expect(client.stderr).toOutput('Specify a URL where the bug occurs:');
client.stdin.write(`https://${deployment3.url}\n`);
await expect(client.stderr).toOutput(
'Specify a URL where the bug does not occur:'
);
client.stdin.write(`https://${deployment1.url}\n`);
await expect(client.stderr).toOutput(
'Specify the URL subpath where the bug occurs:'
);
client.stdin.write('/docs\n');
await expect(client.stderr).toOutput('Bisecting');
await expect(client.stderr).toOutput(
`Deployment URL: https://${deployment2.url}`
);
client.stdin.write('b\n');
await expect(client.stderr).toOutput(
`The first bad deployment is: https://${deployment2.url}`
);
await expect(bisectPromise).resolves.toEqual(0);
});
});

View File

@@ -750,11 +750,71 @@ describe('build', () => {
const errorBuilds = builds.builds.filter((b: any) => 'error' in b);
expect(errorBuilds).toHaveLength(1);
expect(errorBuilds[0].error.name).toEqual('Error');
expect(errorBuilds[0].error.message).toMatch(`TS1005`);
expect(errorBuilds[0].error.message).toMatch(`',' expected.`);
expect(errorBuilds[0].error.hideStackTrace).toEqual(true);
expect(errorBuilds[0].error.code).toEqual('NODE_TYPESCRIPT_ERROR');
expect(errorBuilds[0].error).toEqual({
name: 'Error',
message: expect.stringContaining('TS1005'),
stack: expect.stringContaining('api/typescript.ts'),
hideStackTrace: true,
code: 'NODE_TYPESCRIPT_ERROR',
});
// top level "error" also contains the same error
expect(builds.error).toEqual({
name: 'Error',
message: expect.stringContaining('TS1005'),
stack: expect.stringContaining('api/typescript.ts'),
hideStackTrace: true,
code: 'NODE_TYPESCRIPT_ERROR',
});
// `config.json` contains `version`
const configJson = await fs.readJSON(join(output, 'config.json'));
expect(configJson.version).toBe(3);
} finally {
process.chdir(originalCwd);
delete process.env.__VERCEL_BUILD_RUNNING;
}
});
it('should error when "functions" has runtime that emits discontinued "nodejs12.x"', async () => {
if (process.platform === 'win32') {
console.log('Skipping test on Windows');
return;
}
const cwd = fixture('discontinued-nodejs12.x');
const output = join(cwd, '.vercel/output');
try {
process.chdir(cwd);
const exitCode = await build(client);
expect(exitCode).toEqual(1);
// Error gets printed to the terminal
await expect(client.stderr).toOutput(
'The Runtime "vercel-php@0.1.0" is using "nodejs12.x", which is discontinued. Please upgrade your Runtime to a more recent version or consult the author for more details.'
);
// `builds.json` contains "error" build
const builds = await fs.readJSON(join(output, 'builds.json'));
const errorBuilds = builds.builds.filter((b: any) => 'error' in b);
expect(errorBuilds).toHaveLength(1);
expect(errorBuilds[0].error).toEqual({
name: 'Error',
message: expect.stringContaining('Please upgrade your Runtime'),
stack: expect.stringContaining('Please upgrade your Runtime'),
hideStackTrace: true,
code: 'NODEJS_DISCONTINUED_VERSION',
link: 'https://github.com/vercel/vercel/blob/main/DEVELOPING_A_RUNTIME.md#lambdaruntime',
});
// top level "error" also contains the same error
expect(builds.error).toEqual({
name: 'Error',
message: expect.stringContaining('Please upgrade your Runtime'),
stack: expect.stringContaining('Please upgrade your Runtime'),
hideStackTrace: true,
code: 'NODEJS_DISCONTINUED_VERSION',
link: 'https://github.com/vercel/vercel/blob/main/DEVELOPING_A_RUNTIME.md#lambdaruntime',
});
// `config.json` contains `version`
const configJson = await fs.readJSON(join(output, 'config.json'));
@@ -920,7 +980,7 @@ describe('build', () => {
delete process.env.__VERCEL_BUILD_RUNNING;
}
});
it('should apply "images" configuration from `vercel.json`', async () => {
const cwd = fixture('images');
const output = join(cwd, '.vercel/output');
@@ -936,7 +996,7 @@ describe('build', () => {
sizes: [256, 384, 600, 1000],
domains: [],
minimumCacheTTL: 60,
formats: ['image/webp', 'image/avif'],
formats: ['image/avif', 'image/webp'],
},
});
} finally {
@@ -945,6 +1005,38 @@ describe('build', () => {
}
});
it('should fail with invalid "rewrites" configuration from `vercel.json`', async () => {
const cwd = fixture('invalid-rewrites');
const output = join(cwd, '.vercel/output');
try {
process.chdir(cwd);
const exitCode = await build(client);
expect(exitCode).toEqual(1);
await expect(client.stderr).toOutput(
'Error: Invalid vercel.json - `rewrites[2]` should NOT have additional property `src`. Did you mean `source`?' +
'\n' +
'View Documentation: https://vercel.com/docs/configuration#project/rewrites'
);
const builds = await fs.readJSON(join(output, 'builds.json'));
expect(builds.builds).toBeUndefined();
expect(builds.error).toEqual({
name: 'Error',
message:
'Invalid vercel.json - `rewrites[2]` should NOT have additional property `src`. Did you mean `source`?',
stack: expect.stringContaining('at validateConfig'),
hideStackTrace: true,
code: 'INVALID_VERCEL_CONFIG',
link: 'https://vercel.com/docs/configuration#project/rewrites',
action: 'View Documentation',
});
const configJson = await fs.readJSON(join(output, 'config.json'));
expect(configJson.version).toBe(3);
} finally {
process.chdir(originalCwd);
delete process.env.__VERCEL_BUILD_RUNNING;
}
});
describe('should find packages with different main/module/browser keys', function () {
let output: string;

View File

@@ -1,4 +1,7 @@
import bytes from 'bytes';
import fs from 'fs-extra';
import { join } from 'path';
import { randomBytes } from 'crypto';
import { fileNameSymbol } from '@vercel/client';
import { client } from '../../mocks/client';
import deploy from '../../../src/commands/deploy';
@@ -199,4 +202,119 @@ describe('deploy', () => {
process.chdir(originalCwd);
}
});
it('should upload missing files', async () => {
const cwd = setupFixture('commands/deploy/archive');
const originalCwd = process.cwd();
// Add random 1mb file
await fs.writeFile(join(cwd, 'data'), randomBytes(bytes('1mb')));
try {
process.chdir(cwd);
const user = useUser();
useTeams('team_dummy');
useProject({
...defaultProject,
name: 'archive',
id: 'archive',
});
let body: any;
let fileUploaded = false;
client.scenario.post(`/v13/deployments`, (req, res) => {
if (fileUploaded) {
body = req.body;
res.json({
creator: {
uid: user.id,
username: user.username,
},
id: 'dpl_archive_test',
});
} else {
const sha = req.body.files[0].sha;
res.status(400).json({
error: {
code: 'missing_files',
message: 'Missing files',
missing: [sha],
},
});
}
});
client.scenario.post('/v2/files', (req, res) => {
// Wait for file to be finished uploading
req.on('data', () => {
// Noop
});
req.on('end', () => {
fileUploaded = true;
res.end();
});
});
client.scenario.get(`/v13/deployments/dpl_archive_test`, (req, res) => {
res.json({
creator: {
uid: user.id,
username: user.username,
},
id: 'dpl_archive_test',
readyState: 'READY',
aliasAssigned: true,
alias: [],
});
});
client.scenario.get(
`/v10/now/deployments/dpl_archive_test`,
(req, res) => {
res.json({
creator: {
uid: user.id,
username: user.username,
},
id: 'dpl_archive_test',
readyState: 'READY',
aliasAssigned: true,
alias: [],
});
}
);
// When stderr is not a TTY we expect 5 progress lines to be printed
client.stderr.isTTY = false;
client.setArgv('deploy', '--archive=tgz');
const uploadingLines: string[] = [];
client.stderr.on('data', data => {
if (data.startsWith('Uploading [')) {
uploadingLines.push(data);
}
});
client.stderr.resume();
const exitCode = await deploy(client);
expect(exitCode).toEqual(0);
expect(body?.files?.length).toEqual(1);
expect(body?.files?.[0].file).toEqual('.vercel/source.tgz');
expect(uploadingLines.length).toEqual(5);
expect(
uploadingLines[0].startsWith('Uploading [--------------------]')
).toEqual(true);
expect(
uploadingLines[1].startsWith('Uploading [=====---------------]')
).toEqual(true);
expect(
uploadingLines[2].startsWith('Uploading [==========----------]')
).toEqual(true);
expect(
uploadingLines[3].startsWith('Uploading [===============-----]')
).toEqual(true);
expect(
uploadingLines[4].startsWith('Uploading [====================]')
).toEqual(true);
} finally {
process.chdir(originalCwd);
}
});
});

View File

@@ -38,11 +38,11 @@ describe('list', () => {
await list(client);
const output = await readOutputStream(client, 4);
const output = await readOutputStream(client, 6);
const { org } = pluckIdentifiersFromDeploymentList(output.split('\n')[0]);
const header: string[] = parseSpacedTableRow(output.split('\n')[3]);
const data: string[] = parseSpacedTableRow(output.split('\n')[4]);
const { org } = pluckIdentifiersFromDeploymentList(output.split('\n')[2]);
const header: string[] = parseSpacedTableRow(output.split('\n')[5]);
const data: string[] = parseSpacedTableRow(output.split('\n')[6]);
data.shift();
expect(org).toEqual(team[0].slug);
@@ -81,11 +81,11 @@ describe('list', () => {
client.setArgv('-S', user.username);
await list(client);
const output = await readOutputStream(client, 4);
const output = await readOutputStream(client, 6);
const { org } = pluckIdentifiersFromDeploymentList(output.split('\n')[0]);
const header: string[] = parseSpacedTableRow(output.split('\n')[3]);
const data: string[] = parseSpacedTableRow(output.split('\n')[4]);
const { org } = pluckIdentifiersFromDeploymentList(output.split('\n')[2]);
const header: string[] = parseSpacedTableRow(output.split('\n')[5]);
const data: string[] = parseSpacedTableRow(output.split('\n')[6]);
data.shift();
expect(org).toEqual(user.username);
@@ -116,11 +116,11 @@ describe('list', () => {
client.setArgv(deployment.name);
await list(client);
const output = await readOutputStream(client, 4);
const output = await readOutputStream(client, 6);
const { org } = pluckIdentifiersFromDeploymentList(output.split('\n')[0]);
const header: string[] = parseSpacedTableRow(output.split('\n')[3]);
const data: string[] = parseSpacedTableRow(output.split('\n')[4]);
const { org } = pluckIdentifiersFromDeploymentList(output.split('\n')[2]);
const header: string[] = parseSpacedTableRow(output.split('\n')[5]);
const data: string[] = parseSpacedTableRow(output.split('\n')[6]);
data.shift();
expect(org).toEqual(teamSlug || team[0].slug);

View File

@@ -22,10 +22,10 @@ describe('project', () => {
client.setArgv('project', 'ls');
await projects(client);
const output = await readOutputStream(client, 2);
const { org } = pluckIdentifiersFromDeploymentList(output.split('\n')[0]);
const header: string[] = parseSpacedTableRow(output.split('\n')[2]);
const data: string[] = parseSpacedTableRow(output.split('\n')[3]);
const output = await readOutputStream(client, 3);
const { org } = pluckIdentifiersFromDeploymentList(output.split('\n')[1]);
const header: string[] = parseSpacedTableRow(output.split('\n')[3]);
const data: string[] = parseSpacedTableRow(output.split('\n')[4]);
data.pop();
expect(org).toEqual(user.username);
@@ -47,10 +47,10 @@ describe('project', () => {
client.setArgv('project', 'ls');
await projects(client);
const output = await readOutputStream(client, 2);
const { org } = pluckIdentifiersFromDeploymentList(output.split('\n')[0]);
const header: string[] = parseSpacedTableRow(output.split('\n')[2]);
const data: string[] = parseSpacedTableRow(output.split('\n')[3]);
const output = await readOutputStream(client, 3);
const { org } = pluckIdentifiersFromDeploymentList(output.split('\n')[1]);
const header: string[] = parseSpacedTableRow(output.split('\n')[3]);
const data: string[] = parseSpacedTableRow(output.split('\n')[4]);
data.pop();
expect(org).toEqual(user.username);

View File

@@ -1,4 +1,4 @@
import { validateConfig } from '../../../../src/util/dev/validate';
import { validateConfig } from '../../../../src/util/validate-config';
describe('validateConfig', () => {
it('should not error with empty config', async () => {

View File

@@ -1,6 +1,6 @@
{
"name": "@vercel/client",
"version": "12.2.7",
"version": "12.2.13",
"main": "dist/index.js",
"typings": "dist/index.d.ts",
"homepage": "https://vercel.com",
@@ -43,7 +43,7 @@
]
},
"dependencies": {
"@vercel/build-utils": "5.5.0",
"@vercel/build-utils": "5.5.5",
"@vercel/routing-utils": "2.0.2",
"@zeit/fetch": "5.2.0",
"async-retry": "1.2.3",

View File

@@ -1,4 +1,5 @@
import { Agent } from 'https';
import http from 'http';
import https from 'https';
import { Readable } from 'stream';
import { EventEmitter } from 'events';
import retry from 'async-retry';
@@ -78,7 +79,9 @@ export async function* upload(
debug('Building an upload list...');
const semaphore = new Sema(50, { capacity: 50 });
const agent = new Agent({ keepAlive: true });
const agent = apiUrl?.startsWith('https://')
? new https.Agent({ keepAlive: true })
: new http.Agent({ keepAlive: true });
shas.forEach((sha, index) => {
const uploadProgress = uploads[index];

View File

@@ -1,6 +1,6 @@
{
"name": "@vercel/frameworks",
"version": "1.1.6",
"version": "1.1.8",
"main": "./dist/frameworks.js",
"types": "./dist/frameworks.d.ts",
"files": [

View File

@@ -1792,9 +1792,15 @@ export const frameworks = [
{
path: 'sanity.config.js',
},
{
path: 'sanity.config.jsx',
},
{
path: 'sanity.config.ts',
},
{
path: 'sanity.config.tsx',
},
],
},
settings: {
@@ -1834,10 +1840,13 @@ export const frameworks = [
website: 'https://hydrogen.shopify.dev',
useRuntime: { src: 'package.json', use: '@vercel/hydrogen' },
detectors: {
every: [
some: [
{
path: 'hydrogen.config.js',
},
{
path: 'hydrogen.config.ts',
},
],
},
settings: {

View File

@@ -0,0 +1,8 @@
<svg width="100" height="100" viewBox="0 0 100 100" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M49.8779 58.813L37.2099 39.0114L37.1908 55.6489L15.3588 18.8931H0V80.9427H15.187L15.2366 44.2328L36.8435 79.2977L49.8779 58.813Z" fill="white"/>
<path d="M37.2137 34.8435H52.3817L52.416 34.6679V18.8931H37.229L37.2099 34.6679L37.2137 34.8435Z" fill="white"/>
<path d="M75.8244 52.0458C72.1908 51.9769 68.9079 54.2048 67.6298 57.6069C69.7576 54.4249 73.809 53.1463 77.3779 54.5305C79.0267 55.2023 81.1947 56.2863 82.9122 55.7061C81.2787 53.4137 78.6393 52.0506 75.8244 52.0458Z" fill="#96D8E9"/>
<path d="M93.0343 59.5114C93.0343 57.6527 92.0229 57.2214 89.9237 56.458C88.3664 55.916 86.5992 55.3053 85.313 53.6259C85.0611 53.2939 84.8282 52.9198 84.5802 52.5229C84.0244 51.4948 83.2707 50.5868 82.3626 49.8511C81.1374 48.9504 79.5305 48.5114 77.4466 48.5114C73.265 48.5144 69.4978 51.0384 67.9046 54.9046C69.9187 52.1099 73.2659 50.5956 76.6947 50.928C80.1235 51.2605 83.1175 53.3895 84.5572 56.5191C85.2766 57.706 86.6952 58.2728 88.0343 57.9084C90.084 57.4695 89.9427 59.4084 93.0382 60.0649L93.0343 59.5114Z" fill="#48C4E5"/>
<path d="M99.7939 59.1145V59.0725C99.7481 46.7672 89.7595 36.2405 77.4466 36.2405C69.9264 36.2352 62.9091 40.0172 58.7786 46.3015L58.6985 46.1641L52.3779 36.2405L37.2137 36.2404L51.6221 58.8053L37.5611 80.9427H52.2901L58.6985 71.0992L65.2214 80.9427H79.9542L66.5954 59.9198C66.3737 59.5102 66.2533 59.0535 66.2443 58.5878C66.2433 55.6185 67.4224 52.7705 69.522 50.6708C71.6216 48.5712 74.4696 47.3921 77.4389 47.3931C83.6298 47.3931 84.6641 51.0954 86 52.8359C88.6374 56.271 93.9046 54.7672 93.9046 59.2481C93.9203 60.2981 94.4949 61.2599 95.4121 61.7713C96.3292 62.2827 97.4495 62.266 98.351 61.7274C99.2525 61.1889 99.7981 60.2103 99.7824 59.1603V59.1145H99.7939Z" fill="white"/>
<path d="M99.8092 62.2824C99.9589 63.3289 99.8107 64.3964 99.3817 65.3626C98.542 67.3053 97.1412 66.6565 97.1412 66.6565C97.1412 66.6565 95.9351 66.0687 96.6679 64.9427C97.4809 63.687 99.0878 63.8321 99.8092 62.2824Z" fill="#022F56"/>
</svg>

After

Width:  |  Height:  |  Size: 2.0 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 7.2 KiB

View File

@@ -0,0 +1,35 @@
<svg width="100" height="125" viewBox="0 0 100 125" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M40 0.0374222C25.8531 -0.46154 11.7078 4.02912 0 13.5094L7.80471 18C17.0735 11.0146 28.7797 8.02081 40 8.51977V0.0374222Z" fill="url(#paint0_linear_8_11)"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M82 19.1224C72.2699 8.83673 59.6211 2.46939 46 1V9.32653C57.6764 11.2857 67.8927 16.6735 76.1626 25L82 19.1224Z" fill="url(#paint1_linear_8_11)"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M100 60C99.5253 47 94.7753 34 86.701 23L81 29C87.6503 38.5001 91.451 49 91.9256 60H100Z" fill="url(#paint2_linear_8_11)"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M86.701 102C94.7753 91.2973 99.5253 78.6487 100 66H91.9256C91.451 76.7027 87.6503 86.9189 81 96.1621L86.701 102Z" fill="url(#paint3_linear_8_11)"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M46 124C59.135 122.531 72.2699 116.163 82 105.878L76.1626 100C67.8927 108.816 57.1886 114.204 46 115.673V124Z" fill="url(#paint4_linear_8_11)"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M0 111.491C11.7078 120.971 25.8531 125.461 40 124.963V116.48C28.7797 116.979 17.561 113.985 7.80471 107L0 111.491Z" fill="url(#paint5_linear_8_11)"/>
<path d="M19.9222 63C19.9222 65.6382 20.4182 68.0713 21.4084 70.299C22.3987 72.5267 23.7403 74.4614 25.4299 76.103C27.1196 77.7446 29.0732 79.0343 31.2875 79.9724C33.5605 80.8518 35.9495 81.2915 38.4563 81.2915C40.963 81.2915 43.3235 80.8518 45.5377 79.9724C47.8108 79.0343 49.793 77.7446 51.4826 76.103C53.2311 74.4614 54.6014 72.5267 55.5916 70.299C56.5819 68.0713 57.0778 65.6382 57.0778 63C57.0778 60.3618 56.5819 57.9288 55.5916 55.701C54.6014 53.4733 53.2311 51.5386 51.4826 49.897C49.793 48.2554 47.8108 46.995 45.5377 46.1156C43.3235 45.1775 40.963 44.7085 38.4563 44.7085C35.9495 44.7085 33.5605 45.1775 31.2875 46.1156C29.0732 46.995 27.1196 48.2554 25.4299 49.897C23.7403 51.5386 22.3987 53.4733 21.4084 55.701C20.4182 57.9288 19.9222 60.3618 19.9222 63ZM2 63C2 58.0754 2.90283 53.5025 4.71018 49.2814C6.51753 45.0016 9.02263 41.2789 12.2288 38.1131C15.4349 34.9472 19.2513 32.4849 23.6814 30.7261C28.1687 28.9087 33.0947 28 38.4563 28C43.7606 28 48.6565 28.9087 53.1437 30.7261C57.631 32.4849 61.4777 34.9472 64.6838 38.1131C67.9471 41.2789 70.4825 45.0016 72.2899 49.2814C74.0972 53.5025 75 58.0754 75 63C75 67.9246 74.0972 72.5267 72.2899 76.8065C70.4825 81.0276 67.9471 84.7211 64.6838 87.8869C61.4777 91.0528 57.631 93.5443 53.1437 95.3618C48.6565 97.1206 43.7606 98 38.4563 98C33.0947 98 28.1687 97.1206 23.6814 95.3618C19.2513 93.5443 15.4349 91.0528 12.2288 87.8869C9.02263 84.7211 6.51753 81.0276 4.71018 76.8065C2.90283 72.5267 2 67.9246 2 63Z" fill="white"/>
<defs>
<linearGradient id="paint0_linear_8_11" x1="49.5117" y1="-6.94291e-08" x2="49.5117" y2="125.813" gradientUnits="userSpaceOnUse">
<stop stop-color="#1E90FF"/>
<stop offset="1" stop-color="#FF1E56"/>
</linearGradient>
<linearGradient id="paint1_linear_8_11" x1="50.135" y1="0.473494" x2="50.135" y2="123.975" gradientUnits="userSpaceOnUse">
<stop stop-color="#1E90FF"/>
<stop offset="1" stop-color="#FF1E56"/>
</linearGradient>
<linearGradient id="paint2_linear_8_11" x1="51.7888" y1="-0.0373877" x2="51.7888" y2="126.037" gradientUnits="userSpaceOnUse">
<stop stop-color="#1E90FF"/>
<stop offset="1" stop-color="#FF1E56"/>
</linearGradient>
<linearGradient id="paint3_linear_8_11" x1="51.7888" y1="1.74712" x2="51.7888" y2="124.415" gradientUnits="userSpaceOnUse">
<stop stop-color="#1E90FF"/>
<stop offset="1" stop-color="#FF1E56"/>
</linearGradient>
<linearGradient id="paint4_linear_8_11" x1="50.135" y1="1.02464" x2="50.135" y2="124.526" gradientUnits="userSpaceOnUse">
<stop stop-color="#1E90FF"/>
<stop offset="1" stop-color="#FF1E56"/>
</linearGradient>
<linearGradient id="paint5_linear_8_11" x1="49.5117" y1="-0.812955" x2="49.5117" y2="125" gradientUnits="userSpaceOnUse">
<stop stop-color="#1E90FF"/>
<stop offset="1" stop-color="#FF1E56"/>
</linearGradient>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 3.9 KiB

View File

@@ -0,0 +1,35 @@
<svg width="100" height="125" viewBox="0 0 100 125" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M40 0.0374222C25.8531 -0.46154 11.7078 4.02912 0 13.5094L7.80471 18C17.0735 11.0146 28.7797 8.02081 40 8.51977V0.0374222Z" fill="url(#paint0_linear_8_11)"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M82 19.1224C72.2699 8.83673 59.6211 2.46939 46 1V9.32653C57.6764 11.2857 67.8927 16.6735 76.1626 25L82 19.1224Z" fill="url(#paint1_linear_8_11)"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M100 60C99.5253 47 94.7753 34 86.701 23L81 29C87.6503 38.5001 91.451 49 91.9256 60H100Z" fill="url(#paint2_linear_8_11)"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M86.701 102C94.7753 91.2973 99.5253 78.6487 100 66H91.9256C91.451 76.7027 87.6503 86.9189 81 96.1621L86.701 102Z" fill="url(#paint3_linear_8_11)"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M46 124C59.135 122.531 72.2699 116.163 82 105.878L76.1626 100C67.8927 108.816 57.1886 114.204 46 115.673V124Z" fill="url(#paint4_linear_8_11)"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M0 111.491C11.7078 120.971 25.8531 125.461 40 124.963V116.48C28.7797 116.979 17.561 113.985 7.80471 107L0 111.491Z" fill="url(#paint5_linear_8_11)"/>
<path d="M19.9222 63C19.9222 65.6382 20.4182 68.0713 21.4084 70.299C22.3987 72.5267 23.7403 74.4614 25.4299 76.103C27.1196 77.7446 29.0732 79.0343 31.2875 79.9724C33.5605 80.8518 35.9495 81.2915 38.4563 81.2915C40.963 81.2915 43.3235 80.8518 45.5377 79.9724C47.8108 79.0343 49.793 77.7446 51.4826 76.103C53.2311 74.4614 54.6014 72.5267 55.5916 70.299C56.5819 68.0713 57.0778 65.6382 57.0778 63C57.0778 60.3618 56.5819 57.9288 55.5916 55.701C54.6014 53.4733 53.2311 51.5386 51.4826 49.897C49.793 48.2554 47.8108 46.995 45.5377 46.1156C43.3235 45.1775 40.963 44.7085 38.4563 44.7085C35.9495 44.7085 33.5605 45.1775 31.2875 46.1156C29.0732 46.995 27.1196 48.2554 25.4299 49.897C23.7403 51.5386 22.3987 53.4733 21.4084 55.701C20.4182 57.9288 19.9222 60.3618 19.9222 63ZM2 63C2 58.0754 2.90283 53.5025 4.71018 49.2814C6.51753 45.0016 9.02263 41.2789 12.2288 38.1131C15.4349 34.9472 19.2513 32.4849 23.6814 30.7261C28.1687 28.9087 33.0947 28 38.4563 28C43.7606 28 48.6565 28.9087 53.1437 30.7261C57.631 32.4849 61.4777 34.9472 64.6838 38.1131C67.9471 41.2789 70.4825 45.0016 72.2899 49.2814C74.0972 53.5025 75 58.0754 75 63C75 67.9246 74.0972 72.5267 72.2899 76.8065C70.4825 81.0276 67.9471 84.7211 64.6838 87.8869C61.4777 91.0528 57.631 93.5443 53.1437 95.3618C48.6565 97.1206 43.7606 98 38.4563 98C33.0947 98 28.1687 97.1206 23.6814 95.3618C19.2513 93.5443 15.4349 91.0528 12.2288 87.8869C9.02263 84.7211 6.51753 81.0276 4.71018 76.8065C2.90283 72.5267 2 67.9246 2 63Z" fill="black"/>
<defs>
<linearGradient id="paint0_linear_8_11" x1="49.5117" y1="-6.94291e-08" x2="49.5117" y2="125.813" gradientUnits="userSpaceOnUse">
<stop stop-color="#1E90FF"/>
<stop offset="1" stop-color="#FF1E56"/>
</linearGradient>
<linearGradient id="paint1_linear_8_11" x1="50.135" y1="0.473494" x2="50.135" y2="123.975" gradientUnits="userSpaceOnUse">
<stop stop-color="#1E90FF"/>
<stop offset="1" stop-color="#FF1E56"/>
</linearGradient>
<linearGradient id="paint2_linear_8_11" x1="51.7888" y1="-0.0373877" x2="51.7888" y2="126.037" gradientUnits="userSpaceOnUse">
<stop stop-color="#1E90FF"/>
<stop offset="1" stop-color="#FF1E56"/>
</linearGradient>
<linearGradient id="paint3_linear_8_11" x1="51.7888" y1="1.74712" x2="51.7888" y2="124.415" gradientUnits="userSpaceOnUse">
<stop stop-color="#1E90FF"/>
<stop offset="1" stop-color="#FF1E56"/>
</linearGradient>
<linearGradient id="paint4_linear_8_11" x1="50.135" y1="1.02464" x2="50.135" y2="124.526" gradientUnits="userSpaceOnUse">
<stop stop-color="#1E90FF"/>
<stop offset="1" stop-color="#FF1E56"/>
</linearGradient>
<linearGradient id="paint5_linear_8_11" x1="49.5117" y1="-0.812955" x2="49.5117" y2="125" gradientUnits="userSpaceOnUse">
<stop stop-color="#1E90FF"/>
<stop offset="1" stop-color="#FF1E56"/>
</linearGradient>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 3.9 KiB

View File

@@ -1,6 +1,6 @@
{
"name": "@vercel/fs-detectors",
"version": "3.4.0",
"version": "3.4.5",
"description": "Vercel filesystem detectors",
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
@@ -19,10 +19,11 @@
"test-unit": "yarn test"
},
"dependencies": {
"@vercel/frameworks": "1.1.6",
"@vercel/frameworks": "1.1.8",
"@vercel/routing-utils": "2.0.2",
"glob": "8.0.3",
"js-yaml": "4.1.0",
"json5": "2.2.1",
"minimatch": "3.0.4",
"semver": "6.1.1"
},

View File

@@ -14,11 +14,14 @@ import type { Framework } from '@vercel/frameworks';
*
*/
export const monorepoManagers: Array<
Omit<Framework, 'description' | 'logo' | 'settings' | 'getOutputDirName'>
Omit<Framework, 'description' | 'getOutputDirName'>
> = [
{
name: 'Turborepo',
slug: 'turbo',
logo: 'https://api-frameworks.vercel.sh/monorepo-logos/turborepo.svg',
darkModeLogo:
'https://api-frameworks.vercel.sh/monorepo-logos/turborepo-dark.svg',
detectors: {
some: [
{
@@ -30,10 +33,26 @@ export const monorepoManagers: Array<
},
],
},
settings: {
buildCommand: {
placeholder: 'Turborepo default',
value: null,
},
outputDirectory: {
value: null,
},
installCommand: {
value: null,
},
devCommand: {
value: null,
},
},
},
{
name: 'Nx',
slug: 'nx',
logo: 'https://api-frameworks.vercel.sh/monorepo-logos/nx.svg',
detectors: {
every: [
{
@@ -41,10 +60,26 @@ export const monorepoManagers: Array<
},
],
},
settings: {
buildCommand: {
placeholder: 'Nx default',
value: null,
},
outputDirectory: {
placeholder: 'Nx default',
},
installCommand: {
value: null,
},
devCommand: {
value: null,
},
},
},
{
name: 'Rush',
slug: 'rush',
logo: 'https://api-frameworks.vercel.sh/monorepo-logos/rush.svg',
detectors: {
every: [
{
@@ -52,6 +87,21 @@ export const monorepoManagers: Array<
},
],
},
settings: {
buildCommand: {
placeholder: 'Rush default',
value: null,
},
outputDirectory: {
placeholder: 'Rush default',
},
installCommand: {
placeholder: 'Rush default',
},
devCommand: {
value: null,
},
},
},
];

View File

@@ -1,6 +1,7 @@
import _path from 'path';
import yaml from 'js-yaml';
import glob from 'glob';
import json5 from 'json5';
import { DetectorFilesystem } from '../detectors/filesystem';
import { Workspace } from './get-workspaces';
import { getGlobFs } from './get-glob-fs';
@@ -144,7 +145,7 @@ async function getRushWorkspacePackagePaths({
}: GetPackagePathOptions): Promise<string[]> {
const rushWorkspaceAsBuffer = await fs.readFile('rush.json');
const { projects = [] } = JSON.parse(
const { projects = [] } = json5.parse(
rushWorkspaceAsBuffer.toString()
) as RushWorkspaces;

View File

@@ -2,7 +2,6 @@
"$schema": "https://developer.microsoft.com/json-schemas/rush/v5/rush.schema.json",
"rushVersion": "5.76.1",
"pnpmVersion": "6.7.1",
"pnpmOptions": {
@@ -22,6 +21,7 @@
"postRushBuild": []
},
// comment
"variants": [],
"projects": [
{

View File

@@ -12,7 +12,9 @@
"nodeSupportedVersionRange": ">=12.13.0 <13.0.0 || >=14.15.0 <15.0.0 || >=16.13.0 <17.0.0",
"gitPolicy": {},
/*
this is a comment
*/
"repository": {},
"eventHooks": {
"preRushInstall": [],

View File

@@ -46,7 +46,6 @@ interface Analyzed {
found?: boolean;
packageName: string;
functionName: string;
watch: string[];
}
interface PortInfo {
@@ -498,18 +497,8 @@ export async function build({
environment: {},
});
const watch = parsedAnalyzed.watch;
let watchSub: string[] = [];
// if `entrypoint` located in subdirectory
// we will need to concat it with return watch array
if (entrypointArr.length > 1) {
entrypointArr.pop();
watchSub = parsedAnalyzed.watch.map(file => join(...entrypointArr, file));
}
return {
output: lambda,
watch: watch.concat(watchSub),
};
} catch (error) {
debug('Go Builder Error: ' + error);

View File

@@ -1,6 +1,6 @@
{
"name": "@vercel/go",
"version": "2.2.8",
"version": "2.2.13",
"license": "MIT",
"main": "./dist/index",
"homepage": "https://vercel.com/docs/runtimes#official-runtimes/go",
@@ -35,7 +35,7 @@
"@types/jest": "28.1.6",
"@types/node-fetch": "^2.3.0",
"@types/tar": "^4.0.0",
"@vercel/build-utils": "5.5.0",
"@vercel/build-utils": "5.5.5",
"@vercel/ncc": "0.24.0",
"async-retry": "1.3.1",
"execa": "^1.0.0",

View File

@@ -0,0 +1,10 @@
package handler
import (
"fmt"
"net/http"
)
func Handler2(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "from one.go")
}

View File

@@ -0,0 +1,10 @@
package handler
import (
"fmt"
"net/http"
)
func Handler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "from two.go")
}

View File

@@ -0,0 +1,3 @@
module handler
go 1.16

View File

@@ -0,0 +1,12 @@
{
"probes": [
{
"path": "/api/one",
"mustContain": "from one.go"
},
{
"path": "/api/two",
"mustContain": "from two.go"
}
]
}

View File

@@ -2,7 +2,6 @@ package main
import (
"encoding/json"
"flag"
"fmt"
"go/ast"
"go/parser"
@@ -104,84 +103,6 @@ func main() {
if err != nil {
log.Fatal(err)
}
se := string(rf)
var files []string
var relatedFiles []string
// Add entrypoint to watchlist
relFileName, err := filepath.Rel(filepath.Dir(fileName), fileName)
if err != nil {
log.Fatal(err)
}
relatedFiles = append(relatedFiles, relFileName)
// looking for all go files that have export func
// using in entrypoint
err = filepath.Walk(filepath.Dir(fileName), visit(&files))
if err != nil {
log.Fatal(err)
}
// looking related packages
var modPath string
flag.StringVar(&modPath, "modpath", "", "module path")
flag.Parse()
if len(modPath) > 1 {
err = filepath.Walk(modPath, visit(&files))
if err != nil {
log.Fatal(err)
}
}
for _, file := range files {
absFileName, _ := filepath.Abs(fileName)
absFile, _ := filepath.Abs(file)
// if it isn't entrypoint
if absFileName != absFile {
// find all export structs and functions
pf := parse(file)
var exportedDecl []string
ast.Inspect(pf, func(n ast.Node) bool {
switch t := n.(type) {
case *ast.FuncDecl:
if t.Name.IsExported() {
exportedDecl = append(exportedDecl, t.Name.Name)
}
// find variable declarations
case *ast.TypeSpec:
// which are public
if t.Name.IsExported() {
switch t.Type.(type) {
// and are interfaces
case *ast.StructType:
exportedDecl = append(exportedDecl, t.Name.Name)
}
}
}
return true
})
for _, ed := range exportedDecl {
if strings.Contains(se, ed) {
// find relative path of related file
var basePath string
if modPath == "" {
basePath = filepath.Dir(fileName)
} else {
basePath = modPath
}
rel, err := filepath.Rel(basePath, file)
if err != nil {
log.Fatal(err)
}
relatedFiles = append(relatedFiles, rel)
}
}
}
}
parsed := parse(fileName)
offset := parsed.Pos()
@@ -207,7 +128,6 @@ func main() {
analyzed := analyze{
PackageName: parsed.Name.Name,
FuncName: fn.Name.Name,
Watch: unique(relatedFiles),
}
analyzedJSON, _ := json.Marshal(analyzed)
fmt.Print(string(analyzedJSON))
@@ -229,7 +149,6 @@ func main() {
analyzed := analyze{
PackageName: parsed.Name.Name,
FuncName: fn.Name.Name,
Watch: unique(relatedFiles),
}
analyzedJSON, _ := json.Marshal(analyzed)
fmt.Print(string(analyzedJSON))

View File

@@ -1,6 +1,6 @@
{
"name": "@vercel/hydrogen",
"version": "0.0.21",
"version": "0.0.26",
"license": "MIT",
"main": "./dist/index.js",
"homepage": "https://vercel.com/docs",
@@ -21,7 +21,7 @@
"devDependencies": {
"@types/jest": "27.5.1",
"@types/node": "*",
"@vercel/build-utils": "5.5.0",
"@vercel/build-utils": "5.5.5",
"typescript": "4.6.4"
}
}

View File

@@ -1,6 +1,6 @@
{
"name": "@vercel/next",
"version": "3.1.29",
"version": "3.2.5",
"license": "MIT",
"main": "./dist/index",
"homepage": "https://vercel.com/docs/runtimes#official-runtimes/next-js",
@@ -44,7 +44,7 @@
"@types/semver": "6.0.0",
"@types/text-table": "0.2.1",
"@types/webpack-sources": "3.2.0",
"@vercel/build-utils": "5.5.0",
"@vercel/build-utils": "5.5.5",
"@vercel/nft": "0.22.1",
"@vercel/routing-utils": "2.0.2",
"async-sema": "3.0.1",

View File

@@ -633,7 +633,7 @@ export async function serverBuild({
const curPagesDir = isAppPath && appDir ? appDir : pagesDir;
const pageDir = path.dirname(path.join(curPagesDir, originalPagePath));
const normalizedBaseDir = `${baseDir}${
baseDir.endsWith('/') ? '' : '/'
baseDir.endsWith(path.sep) ? '' : path.sep
}`;
files.forEach((file: string) => {
const absolutePath = path.join(pageDir, file);
@@ -970,7 +970,9 @@ export async function serverBuild({
// routes since only the status is being modified and we don't want
// to exceed the routes limit
const starterRouteSrc = `^${
entryDirectory !== '.' ? path.posix.join('/', entryDirectory, '()') : '()'
entryDirectory !== '.'
? `${path.posix.join('/', entryDirectory)}()`
: '()'
}`;
let currentRouteSrc = starterRouteSrc;
@@ -997,7 +999,7 @@ export async function serverBuild({
const isLastRoute = i === prerenderManifest.notFoundRoutes.length - 1;
if (prerenderManifest.staticRoutes[route]?.initialRevalidate === false) {
if (currentRouteSrc.length + route.length + 1 >= 4096) {
if (currentRouteSrc.length + route.length + 1 >= 4000) {
pushRoute(currentRouteSrc);
currentRouteSrc = starterRouteSrc;
}
@@ -1007,7 +1009,7 @@ export async function serverBuild({
currentRouteSrc.length - 1
)}${
currentRouteSrc[currentRouteSrc.length - 2] === '(' ? '' : '|'
}${route})`;
}${route}/?)`;
if (isLastRoute) {
pushRoute(currentRouteSrc);
@@ -1134,7 +1136,7 @@ export async function serverBuild({
if (appPathRoutesManifest) {
// create .rsc variant for app lambdas and edge functions
// to match prerenders so we can route the same when the
// __flight__ header is present
// __rsc__ header is present
const edgeFunctions = middleware.edgeFunctions;
for (let route of Object.values(appPathRoutesManifest)) {
@@ -1142,7 +1144,8 @@ export async function serverBuild({
if (lambdas[route]) {
lambdas[`${route}.rsc`] = lambdas[route];
} else if (edgeFunctions[route]) {
}
if (edgeFunctions[route]) {
edgeFunctions[`${route}.rsc`] = edgeFunctions[route];
}
}
@@ -1343,6 +1346,12 @@ export async function serverBuild({
.join('|')})?[/]?404/?`,
status: 404,
continue: true,
missing: [
{
type: 'header',
key: 'x-prerender-revalidate',
},
],
},
]
: [
@@ -1350,6 +1359,12 @@ export async function serverBuild({
src: path.posix.join('/', entryDirectory, '404/?'),
status: 404,
continue: true,
missing: [
{
type: 'header',
key: 'x-prerender-revalidate',
},
],
},
]),
@@ -1388,12 +1403,23 @@ export async function serverBuild({
...(appDir
? [
{
src: `^${path.posix.join('/', entryDirectory, '/')}`,
has: [
{
type: 'header',
key: '__rsc__',
},
],
dest: path.posix.join('/', entryDirectory, '/index.rsc'),
check: true,
},
{
src: `^${path.posix.join('/', entryDirectory, '/(.*)$')}`,
has: [
{
type: 'header',
key: '__flight__',
key: '__rsc__',
},
],
dest: path.posix.join('/', entryDirectory, '/$1.rsc'),
@@ -1497,18 +1523,31 @@ export async function serverBuild({
dynamicRoutes
.map(route => {
route = Object.assign({}, route);
let normalizedSrc = route.src;
if (routesManifest.basePath) {
normalizedSrc = normalizedSrc.replace(
new RegExp(
`\\^${escapeStringRegexp(routesManifest.basePath)}`
),
'^'
);
}
route.src = path.posix.join(
'^/',
entryDirectory,
'_next/data/',
escapedBuildId,
route.src.replace(/(^\^|\$$)/g, '') + '.json$'
normalizedSrc
.replace(/\^\(\?:\/\(\?</, '(?:(?<')
.replace(/(^\^|\$$)/g, '') + '.json$'
);
const { pathname, search } = new URL(
route.dest || '/',
'http://n'
);
const parsedDestination = new URL(route.dest || '/', 'http://n');
let pathname = parsedDestination.pathname;
const search = parsedDestination.search;
let isPrerender = !!prerenders[path.join('./', pathname)];
if (routesManifest.i18n) {
@@ -1525,9 +1564,17 @@ export async function serverBuild({
}
if (isPrerender) {
route.dest = `/_next/data/${buildId}${pathname}.json${
search || ''
}`;
if (routesManifest.basePath) {
pathname = pathname.replace(
new RegExp(
`^${escapeStringRegexp(routesManifest.basePath)}`
),
''
);
}
route.dest = `${
routesManifest.basePath || ''
}/_next/data/${buildId}${pathname}.json${search || ''}`;
}
return route;
})
@@ -1600,6 +1647,18 @@ export async function serverBuild({
continue: true,
important: true,
},
...(appDir
? [
{
src: path.posix.join('/', entryDirectory, '/(.*).rsc$'),
headers: {
'content-type': 'application/octet-stream',
},
continue: true,
important: true,
},
]
: []),
// TODO: remove below workaround when `/` is allowed to be output
// different than `/index`

View File

@@ -1698,7 +1698,6 @@ export const onPrerenderRoute =
const {
appDir,
pagesDir,
hasPages404,
static404Page,
entryDirectory,
prerenderManifest,
@@ -1896,11 +1895,12 @@ export const onPrerenderRoute =
});
}
// If revalidate isn't enabled we force the /404 route to be static
// to match next start behavior otherwise getStaticProps would be
// recalled for each 404 URL path since Prerender is cached based
// on the URL path
if (!canUsePreviewMode || (hasPages404 && routeKey === '/404')) {
// if preview mode/On-Demand ISR can't be leveraged
// we can output pure static outputs instead of prerenders
if (
!canUsePreviewMode ||
(routeKey === '/404' && !lambdas[outputPathPage])
) {
htmlFsRef.contentType = htmlContentType;
prerenders[outputPathPage] = htmlFsRef;
prerenders[outputPathData] = jsonFsRef;
@@ -2202,6 +2202,7 @@ interface BaseEdgeFunctionInfo {
page: string;
wasm?: { filePath: string; name: string }[];
assets?: { filePath: string; name: string }[];
regions?: 'auto' | string[] | 'all' | 'default';
}
interface EdgeFunctionInfoV1 extends BaseEdgeFunctionInfo {
@@ -2341,6 +2342,7 @@ export async function getMiddlewareBundle({
...wasmFiles,
...assetFiles,
},
regions: edgeFunction.regions,
entrypoint: 'index.js',
envVarsInUse: edgeFunction.env,
assets: (edgeFunction.assets ?? []).map(({ name }) => {

View File

@@ -0,0 +1,10 @@
export default function Root({ children }) {
return (
<html>
<head>
<title>test app</title>
</head>
<body>{children}</body>
</html>
);
}

View File

@@ -1,8 +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 () => {
await deployAndTest(__dirname);
const info = await deployAndTest(__dirname);
Object.assign(ctx, info);
});
});

View File

@@ -10,6 +10,49 @@
"path": "/edge",
"status": 200,
"mustContain": "edge"
},
{
"path": "/edge",
"status": 200,
"headers": {
"__rsc__": "1"
},
"mustContain": "M1:{",
"mustNotContain": "<html"
},
{
"path": "/edge",
"status": 200,
"headers": {
"__rsc__": "1"
},
"responseHeaders": {
"content-type": "application/octet-stream"
}
},
{
"path": "/",
"status": 200,
"mustContain": "page"
},
{
"path": "/",
"status": 200,
"headers": {
"__rsc__": "1"
},
"mustContain": "M1:{",
"mustNotContain": "<html"
},
{
"path": "/",
"status": 200,
"headers": {
"__rsc__": "1"
},
"responseHeaders": {
"content-type": "application/octet-stream"
}
}
]
}

View File

@@ -1,4 +1,4 @@
'client';
'use client';
import { useState, useEffect } from 'react';

View File

@@ -1,4 +1,4 @@
'client';
'use client';
import { useState, useEffect } from 'react';

View File

@@ -1,4 +1,4 @@
'client';
'use client';
export default function LazyComponent() {
return (

View File

@@ -1,11 +1,11 @@
'client';
'use client';
import { useState, lazy } from 'react';
const Lazy = lazy(() => import('./lazy.js'));
export function ClientComponent() {
let [state] = useState('client');
let [state] = useState('use client');
return (
<>
<Lazy />

View File

@@ -1,4 +1,4 @@
'client';
'use client';
export default function ShouldNotServeClientDotJs(props) {
return (

View File

@@ -15,23 +15,31 @@
"path": "/dashboard",
"status": 200,
"headers": {
"__flight__": "1"
"__rsc__": "1"
},
"mustContain": "M1:{",
"mustNotContain": "<html"
},
{
"path": "/dashboard",
"status": 200,
"headers": {
"__rsc__": "1"
},
"responseHeaders": {
"content-type": "application/octet-stream"
}
},
{
"path": "/dashboard/another",
"status": 200,
"mustContain": "hello from newroot/dashboard/another"
},
// TODO: uncomment after this is fixed upstream
// x-ref: https://vercel.slack.com/archives/C035J346QQL/p1663820032810519?thread_ts=1663775935.504379&cid=C035J346QQL
// {
// "path": "/dashboard/deployments/123",
// "status": 200,
// "mustContain": "hello from app/dashboard/deployments/[id]. ID is: <!-- -->123"
// },
{
"path": "/dashboard/deployments/123",
"status": 200,
"mustContain": "hello from app/dashboard/deployments/[id]. ID is: <!-- -->123"
},
{
"path": "/",
"status": 200,
@@ -42,16 +50,24 @@
"status": 200,
"mustContain": "hello from pages/blog/[slug]"
},
// TODO: uncomment after this is fixed upstream
// {
// "path": "/dynamic/category-1/id-1",
// "status": 200,
// "mustContain": "{&quot;category&quot;:&quot;category-1&quot;,&quot;id&quot;:&quot;id-1&quot;}"
// },
{
"path": "/dynamic/category-1/id-1",
"status": 200,
"mustContain": "{&quot;category&quot;:&quot;category-1&quot;,&quot;id&quot;:&quot;id-1&quot;}"
},
{
"path": "/dashboard/changelog",
"status": 200,
"mustContain": "hello from app/dashboard/changelog"
},
{
"path": "/",
"status": 200,
"headers": {
"__rsc__": "1"
},
"mustContain": "M1:{",
"mustNotContain": "<html"
}
]
}

View 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);
});
});

View File

@@ -0,0 +1,7 @@
{
"dependencies": {
"next": "9.5.5",
"react": "17.0.2",
"react-dom": "17.0.2"
}
}

View File

@@ -0,0 +1,11 @@
export default function Page() {
return <p>custom 404</p>;
}
export function getStaticProps() {
return {
props: {
is404: true,
},
};
}

View File

@@ -0,0 +1,3 @@
export default function handler(req, res) {
return res.json({ hello: 'world' });
}

View File

@@ -0,0 +1,3 @@
export default function Page() {
return <p>index page</p>;
}

View File

@@ -0,0 +1,14 @@
{
"probes": [
{
"path": "/",
"status": 200,
"mustContain": "index page"
},
{
"path": "/non-existent",
"status": 404,
"mustContain": "custom 404"
}
]
}

View File

@@ -0,0 +1,127 @@
const path = require('path');
const cheerio = require('cheerio');
const { deployAndTest, check } = require('../../utils');
const fetch = require('../../../../../test/lib/deployment/fetch-retry');
describe(`${__dirname.split(path.sep).pop()}`, () => {
let ctx = {};
it('should deploy and pass probe checks', async () => {
const info = await deployAndTest(__dirname);
Object.assign(ctx, info);
});
it('should revalidate content correctly for middleware rewrite', async () => {
const propsFromHtml = async () => {
let res = await fetch(
`${ctx.deploymentUrl}/docs/rewrite-to-another-site`
);
let $ = cheerio.load(await res.text());
let props = JSON.parse($('#props').text());
return props;
};
let props = await propsFromHtml();
expect(isNaN(props.now)).toBe(false);
const { pageProps: data } = await fetch(
`${ctx.deploymentUrl}/docs/_next/data/testing-build-id/rewrite-to-another-site.json`
).then(res => res.json());
expect(isNaN(data.now)).toBe(false);
const revalidateRes = await fetch(
`${ctx.deploymentUrl}/docs/api/revalidate?urlPath=/docs/_sites/test-revalidate`
);
expect(revalidateRes.status).toBe(200);
expect(await revalidateRes.json()).toEqual({ revalidated: true });
await check(async () => {
const newProps = await propsFromHtml();
console.log({ props, newProps });
if (isNaN(newProps.now)) {
throw new Error();
}
return newProps.now !== props.now
? 'success'
: JSON.stringify({
newProps,
props,
});
}, 'success');
await check(async () => {
const { pageProps: newData } = await fetch(
`${ctx.deploymentUrl}/docs/_next/data/testing-build-id/rewrite-to-another-site.json`
).then(res => res.json());
console.log({ newData, data });
if (isNaN(newData.now)) {
throw new Error();
}
return newData.now !== data.now
? 'success'
: JSON.stringify({
newData,
data,
});
}, 'success');
});
it('should revalidate content correctly for optional catch-all route', async () => {
const propsFromHtml = async () => {
let res = await fetch(`${ctx.deploymentUrl}/docs/financial`);
let $ = cheerio.load(await res.text());
let props = JSON.parse($('#props').text());
return props;
};
let props = await propsFromHtml();
expect(isNaN(props.now)).toBe(false);
const { pageProps: data } = await fetch(
`${ctx.deploymentUrl}/docs/_next/data/testing-build-id/financial.json?slug=financial`
).then(res => res.json());
expect(isNaN(data.now)).toBe(false);
const revalidateRes = await fetch(
`${ctx.deploymentUrl}/docs/api/revalidate?urlPath=/docs/financial`
);
expect(revalidateRes.status).toBe(200);
expect(await revalidateRes.json()).toEqual({ revalidated: true });
await check(async () => {
const newProps = await propsFromHtml();
console.log({ props, newProps });
if (isNaN(newProps.now)) {
throw new Error();
}
return newProps.now !== props.now
? 'success'
: JSON.stringify({
newProps,
props,
});
}, 'success');
await check(async () => {
const { pageProps: newData } = await fetch(
`${ctx.deploymentUrl}/docs/_next/data/testing-build-id/financial.json?slug=financial`
).then(res => res.json());
console.log({ newData, data });
if (isNaN(newData.now)) {
throw new Error();
}
return newData.now !== data.now
? 'success'
: JSON.stringify({
newData,
data,
});
}, 'success');
});
});

View File

@@ -0,0 +1,253 @@
import { NextResponse } from 'next/server';
const ALLOWED = ['allowed'];
export const config = {
matcher: [
'/dynamic/:path*',
'/_sites/:path*',
'/:teamId/:slug',
'/:path*',
'/',
],
};
export function middleware(request) {
const url = request.nextUrl;
const pathname = url.pathname;
if (process.env.FOO) {
console.log(`Includes env variable ${process.env.FOO}`);
}
if (url.pathname === '/redirect-me') {
url.pathname = '/from-middleware';
return NextResponse.redirect(url, 307);
}
if (url.pathname === '/next') {
return NextResponse.next();
}
if (url.pathname === '/version') {
return NextResponse.json({
enumerable: Object.keys(self).includes('VercelRuntime'),
version: self.VercelRuntime.version,
});
}
if (url.pathname === '/globals') {
const globalThisKeys = Object.keys(globalThis);
const globalKeys = globalThisKeys.reduce((acc, globalName) => {
const key = globalName.toString();
if (global[key]) acc.push(key);
return acc;
}, []);
const res = NextResponse.next();
res.headers.set(
'data',
JSON.stringify({ globals: globalKeys, globalThis: globalThisKeys })
);
return res;
}
if (url.pathname === '/log') {
console.log('hi there');
return;
}
if (url.pathname === '/somewhere') {
url.pathname = '/from-middleware';
return NextResponse.redirect(url);
}
if (url.pathname === '/logs') {
console.clear();
for (let i = 0; i < 3; i++) console.count();
console.count('test');
console.count('test');
console.dir({ hello: 'world' });
console.log('hello');
console.log('world');
return;
}
if (url.pathname === '/greetings') {
const data = { message: 'hello world!' };
const res = NextResponse.next();
res.headers.set('x-example', 'edge');
res.headers.set('data', JSON.stringify(data));
return res;
}
if (url.pathname === '/rewrite-me-to-about') {
url.pathname = '/about';
url.searchParams.set('middleware', 'foo');
return NextResponse.rewrite(url);
}
if (url.pathname === '/rewrite-to-site') {
url.pathname = '/_sites/subdomain-1';
console.log('rewriting to', url.pathname, url.href);
return NextResponse.rewrite(url);
}
if (url.pathname === '/rewrite-to-another-site') {
url.pathname = '/_sites/test-revalidate';
console.log('rewriting to', url.pathname, url.href);
return NextResponse.rewrite(url);
}
if (url.pathname === '/redirect-me-to-about') {
url.pathname = '/about';
url.searchParams.set('middleware', 'foo');
return Response.redirect(url);
}
if (url.pathname === '/rewrite-absolute') {
return NextResponse.rewrite('https://example.vercel.sh/foo?foo=bar');
}
if (url.pathname === '/rewrite-relative') {
url.pathname = '/foo';
url.searchParams.set('foo', 'bar');
return NextResponse.rewrite(url);
}
if (url.pathname === '/redirect-absolute') {
return Response.redirect('https://vercel.com');
}
if (url.pathname === '/redirect-301') {
url.pathname = '/greetings';
return NextResponse.redirect(url, 301);
}
if (url.pathname === '/reflect') {
const res = NextResponse.next();
res.headers.set(
'data',
JSON.stringify({
geo: request.geo,
headers: Object.fromEntries(request.headers),
ip: request.ip,
method: request.method,
nextUrl: {
hash: request.nextUrl.hash,
hostname: request.nextUrl.hostname,
pathname: request.nextUrl.pathname,
port: request.nextUrl.port,
protocol: request.nextUrl.protocol,
search: request.nextUrl.search,
},
url: request.url,
})
);
return res;
}
if (url.pathname === '/stream-response') {
const { readable, writable } = new TransformStream();
const waitUntil = (async () => {
const enc = new TextEncoder();
const writer = writable.getWriter();
writer.write(enc.encode('this is a streamed '));
writer.write(enc.encode('response '));
return writer.close();
})();
return {
waitUntil,
response: NextResponse.next(),
};
}
if (url.pathname === '/throw-error') {
const error = new Error('oh no!');
console.log('This is not worker.js');
console.error(error);
return new Promise((_, reject) => reject(error));
}
if (url.pathname === '/throw-error-internal') {
function myFunctionName() {
throw new Error('Oh no!');
}
function anotherFunction() {
return myFunctionName();
}
try {
anotherFunction();
} catch (err) {
console.error(err);
}
return new Promise((_, reject) => reject(new Error('oh no!')));
}
if (url.pathname === '/unhandledrejection') {
Promise.reject(new TypeError('captured unhandledrejection error.'));
return NextResponse.next();
}
if (pathname.startsWith('/query-params')) {
if (pathname.endsWith('/clear')) {
const strategy =
url.searchParams.get('strategy') === 'rewrite' ? 'rewrite' : 'redirect';
for (const key of [...url.searchParams.keys()]) {
if (!ALLOWED.includes(key)) {
url.searchParams.delete(key);
}
}
const newPath = url.pathname.replace(/\/clear$/, '');
url.pathname = newPath;
if (strategy === 'redirect') {
return NextResponse.redirect(url);
} else {
return NextResponse.rewrite(url);
}
}
const obj = Object.fromEntries([...url.searchParams.entries()]);
const res = NextResponse.next();
res.headers.set('data', JSON.stringify(obj));
return res;
}
if (pathname.startsWith('/home')) {
if (!request.cookies.get('bucket')) {
const bucket = Math.random() >= 0.5 ? 'a' : 'b';
url.pathname = `/home/${bucket}`;
const response = NextResponse.rewrite(url);
response.cookies.set('bucket', bucket);
return response;
}
url.pathname = `/home/${request.cookies.get('bucket')}`;
return NextResponse.rewrite(url);
}
if (pathname.startsWith('/fetch-subrequest')) {
const destinationUrl =
url.searchParams.get('url') || 'https://example.vercel.sh';
return fetch(destinationUrl, { headers: request.headers });
}
if (url.pathname === '/dynamic/greet') {
const res = NextResponse.next();
res.headers.set(
'data',
JSON.stringify({
message: url.searchParams.get('greeting') || 'Hi friend',
})
);
return res;
}
}

View File

@@ -0,0 +1,39 @@
module.exports = {
generateBuildId() {
return 'testing-build-id';
},
basePath: '/docs',
redirects() {
return [
{
source: '/redirect-me',
destination: '/from-next-config',
permanent: false,
},
];
},
rewrites() {
return {
beforeFiles: [
{
source: '/rewrite-before-files',
destination: '/somewhere',
},
],
afterFiles: [
{
source: '/after-file-rewrite',
destination: '/about',
},
{
source: '/after-file-rewrite-auto-static',
destination: '/home/a',
},
{
source: '/after-file-rewrite-auto-static-dynamic',
destination: '/dynamic/first',
},
],
};
},
};

View File

@@ -0,0 +1,11 @@
{
"scripts": {
"dev": "next dev",
"build": "next build"
},
"dependencies": {
"next": "canary",
"react": "latest",
"react-dom": "latest"
}
}

View File

@@ -0,0 +1,111 @@
import Link from 'next/link';
export default function Page(props) {
if (!props.params.slug) {
return (
<div>
<h1>Demo</h1>
<ul>
<li>
<Link href="/home">
<a>A/B Testing</a>
</Link>
</li>
<li>
<Link href="/rewrite-me-to-about">
<a>Rewrite to existing page</a>
</Link>
</li>
<li>
<Link href="/redirect-me-to-about">
<a>Redirect to existing page</a>
</Link>
</li>
<li>
<Link href="/rewrite">
<a>Rewrite to external site</a>
</Link>
</li>
<li>
<Link href="/redirect">
<a>Redirect to external site</a>
</Link>
</li>
<li>
<Link href="/greetings">
<a>Respond with JSON</a>
</Link>
</li>
<li>
<Link href="/stream-response">
<a>Respond with Stream</a>
</Link>
</li>
<li>
<Link href="/dynamic/greet?greeting=hola">
<a>Dynamic Nested Middleware</a>
</Link>
</li>
<li>
<Link href="/eval">
<a>do a eval</a>
</Link>
</li>
<li>
<Link href="/logs">
<a>print some logs</a>
</Link>
</li>
<li>
<Link href="/fetch">
<a>perform a fetch</a>
</Link>
</li>
<li>
<Link href="/throw-error">
<a>throw an error</a>
</Link>
</li>
<li>
<Link href="/throw-error-internal">
<a>throw a controller error</a>
</Link>
</li>
<li>
<Link href="/timeout">
<a>simulate timeout</a>
</Link>
</li>
</ul>
</div>
);
}
return (
<>
<p id="page">[[..slug]]</p>
<p id="props">{JSON.stringify(props)}</p>
</>
);
}
export function getStaticPaths() {
return {
paths: ['/somewhere'],
fallback: 'blocking',
};
}
export function getStaticProps({ params }) {
if (params.slug?.[0] === 'somewhere' && params.slug.length === 1) {
return {
notFound: true,
};
}
return {
props: {
params,
now: Date.now(),
},
};
}

View File

@@ -0,0 +1,3 @@
export default function Page() {
return <p>/[teamId]/[slug]</p>;
}

View File

@@ -0,0 +1,31 @@
export default function Page(props) {
return (
<>
<p>/_sites/[site]</p>
<p id="props">{JSON.stringify(props)}</p>
</>
);
}
export function getStaticProps({ params }) {
return {
props: {
params,
now: Date.now(),
},
};
}
export function getStaticPaths() {
return {
paths: [
{
params: { site: 'subdomain-1' },
},
{
params: { site: 'subdomain-2' },
},
],
fallback: 'blocking',
};
}

View File

@@ -0,0 +1,17 @@
export default function Main({ message, middleware }) {
return (
<div>
<h1 className="title">About Page</h1>
<p className={message}>{message}</p>
<p className="middleware">{middleware}</p>
</div>
);
}
export const getServerSideProps = ({ query }) => ({
props: {
middleware: query.middleware || '',
message: query.message || '',
page: 'about',
},
});

View File

@@ -0,0 +1,4 @@
export default async function handler(req, res) {
await res.revalidate(req.query.urlPath);
res.json({ revalidated: true });
}

View File

@@ -0,0 +1,3 @@
export default function Index() {
return <p className="title">Dynamic route</p>;
}

View File

@@ -0,0 +1,3 @@
export default function Index() {
return <p className="title">static route</p>;
}

View File

@@ -0,0 +1,16 @@
export default function Main({ message, middleware }) {
return (
<div>
<h1 className="title">About Page</h1>
<p className={message}>{message}</p>
<p className="middleware">{middleware}</p>
</div>
);
}
export const getServerSideProps = ({ query }) => ({
props: {
middleware: query.middleware || '',
message: query.message || '',
},
});

View File

@@ -0,0 +1,3 @@
export default function Home() {
return <h1>Home A</h1>;
}

View File

@@ -0,0 +1,3 @@
export default function Home() {
return <h1>Home B</h1>;
}

Some files were not shown because too many files have changed in this diff Show More