Compare commits

...

45 Commits

Author SHA1 Message Date
Steven
9178f14c40 Publish Stable
- @vercel/python@2.0.2
2021-05-24 16:46:49 -04:00
Nathan Rajlich
9a790f5352 [cli] Set tokenName query param for Git provider login methods (#6277)
We're not currently setting the `tokenName` when logging in via a
Git provider, so the name becomes the default "Website" one which
is confusing / incorrect.
2021-05-24 13:38:55 -07:00
Steven
33d8be7f8f [cli] Fix vc logout retry (#6276)
* [cli] Fix `vc logout` retry

* Fix logout message
2021-05-24 16:26:01 -04:00
JJ Kasper
82aa86b786 Apply dependabot changes (#6275)
* Bump elliptic in /packages/cli/test/dev/fixtures/02-angular-node

Bumps [elliptic](https://github.com/indutny/elliptic) from 6.5.2 to 6.5.4.
- [Release notes](https://github.com/indutny/elliptic/releases)
- [Commits](https://github.com/indutny/elliptic/compare/v6.5.2...v6.5.4)

Signed-off-by: dependabot[bot] <support@github.com>

* Bump node-fetch from 2.6.0 to 2.6.1 in /api

Bumps [node-fetch](https://github.com/bitinn/node-fetch) from 2.6.0 to 2.6.1.
- [Release notes](https://github.com/bitinn/node-fetch/releases)
- [Changelog](https://github.com/node-fetch/node-fetch/blob/master/docs/CHANGELOG.md)
- [Commits](https://github.com/bitinn/node-fetch/compare/v2.6.0...v2.6.1)

Signed-off-by: dependabot[bot] <support@github.com>

* Bump ssri from 6.0.1 to 6.0.2 in /examples/blitzjs

Bumps [ssri](https://github.com/npm/ssri) from 6.0.1 to 6.0.2.
- [Release notes](https://github.com/npm/ssri/releases)
- [Changelog](https://github.com/npm/ssri/blob/v6.0.2/CHANGELOG.md)
- [Commits](https://github.com/npm/ssri/compare/v6.0.1...v6.0.2)

Signed-off-by: dependabot[bot] <support@github.com>

* Bump hosted-git-info from 2.8.8 to 2.8.9 in /examples/angular

Bumps [hosted-git-info](https://github.com/npm/hosted-git-info) from 2.8.8 to 2.8.9.
- [Release notes](https://github.com/npm/hosted-git-info/releases)
- [Changelog](https://github.com/npm/hosted-git-info/blob/v2.8.9/CHANGELOG.md)
- [Commits](https://github.com/npm/hosted-git-info/compare/v2.8.8...v2.8.9)

Signed-off-by: dependabot[bot] <support@github.com>

* Bump hosted-git-info from 2.8.8 to 2.8.9 in /examples/blitzjs

Bumps [hosted-git-info](https://github.com/npm/hosted-git-info) from 2.8.8 to 2.8.9.
- [Release notes](https://github.com/npm/hosted-git-info/releases)
- [Changelog](https://github.com/npm/hosted-git-info/blob/v2.8.9/CHANGELOG.md)
- [Commits](https://github.com/npm/hosted-git-info/compare/v2.8.8...v2.8.9)

Signed-off-by: dependabot[bot] <support@github.com>

* Bump ssri in /packages/cli/test/dev/fixtures/02-angular-node

Bumps [ssri](https://github.com/npm/ssri) from 6.0.1 to 6.0.2.
- [Release notes](https://github.com/npm/ssri/releases)
- [Changelog](https://github.com/npm/ssri/blob/v6.0.2/CHANGELOG.md)
- [Commits](https://github.com/npm/ssri/compare/v6.0.1...v6.0.2)

Signed-off-by: dependabot[bot] <support@github.com>

* Bump ssri in /packages/cli/test/dev/fixtures/04-create-react-app

Bumps [ssri](https://github.com/npm/ssri) from 6.0.1 to 6.0.2.
- [Release notes](https://github.com/npm/ssri/releases)
- [Changelog](https://github.com/npm/ssri/blob/v6.0.2/CHANGELOG.md)
- [Commits](https://github.com/npm/ssri/compare/v6.0.1...v6.0.2)

Signed-off-by: dependabot[bot] <support@github.com>

* Bump ssri in /packages/cli/test/dev/fixtures/03-aurelia

Bumps [ssri](https://github.com/npm/ssri) from 6.0.1 to 6.0.2.
- [Release notes](https://github.com/npm/ssri/releases)
- [Changelog](https://github.com/npm/ssri/blob/v6.0.2/CHANGELOG.md)
- [Commits](https://github.com/npm/ssri/compare/v6.0.1...v6.0.2)

Signed-off-by: dependabot[bot] <support@github.com>

* Bump hosted-git-info in /packages/cli/test/dev/fixtures/02-angular-node

Bumps [hosted-git-info](https://github.com/npm/hosted-git-info) from 2.8.8 to 2.8.9.
- [Release notes](https://github.com/npm/hosted-git-info/releases)
- [Changelog](https://github.com/npm/hosted-git-info/blob/v2.8.9/CHANGELOG.md)
- [Commits](https://github.com/npm/hosted-git-info/compare/v2.8.8...v2.8.9)

Signed-off-by: dependabot[bot] <support@github.com>

* Bump url-parse in /packages/cli/test/dev/fixtures/04-create-react-app

Bumps [url-parse](https://github.com/unshiftio/url-parse) from 1.4.7 to 1.5.1.
- [Release notes](https://github.com/unshiftio/url-parse/releases)
- [Commits](https://github.com/unshiftio/url-parse/compare/1.4.7...1.5.1)

Signed-off-by: dependabot[bot] <support@github.com>

* Bump url-parse in /packages/cli/test/dev/fixtures/03-aurelia

Bumps [url-parse](https://github.com/unshiftio/url-parse) from 1.4.7 to 1.5.1.
- [Release notes](https://github.com/unshiftio/url-parse/releases)
- [Commits](https://github.com/unshiftio/url-parse/compare/1.4.7...1.5.1)

Signed-off-by: dependabot[bot] <support@github.com>

* Bump handlebars in /packages/cli/test/dev/fixtures/02-angular-node

Bumps [handlebars](https://github.com/wycats/handlebars.js) from 4.7.6 to 4.7.7.
- [Release notes](https://github.com/wycats/handlebars.js/releases)
- [Changelog](https://github.com/handlebars-lang/handlebars.js/blob/master/release-notes.md)
- [Commits](https://github.com/wycats/handlebars.js/compare/v4.7.6...v4.7.7)

Signed-off-by: dependabot[bot] <support@github.com>

* Bump lodash in /packages/cli/test/dev/fixtures/02-angular-node

Bumps [lodash](https://github.com/lodash/lodash) from 4.17.15 to 4.17.21.
- [Release notes](https://github.com/lodash/lodash/releases)
- [Commits](https://github.com/lodash/lodash/compare/4.17.15...4.17.21)

Signed-off-by: dependabot[bot] <support@github.com>

* Bump hosted-git-info in /packages/cli/test/dev/fixtures/03-aurelia

Bumps [hosted-git-info](https://github.com/npm/hosted-git-info) from 2.8.8 to 2.8.9.
- [Release notes](https://github.com/npm/hosted-git-info/releases)
- [Changelog](https://github.com/npm/hosted-git-info/blob/v2.8.9/CHANGELOG.md)
- [Commits](https://github.com/npm/hosted-git-info/compare/v2.8.8...v2.8.9)

Signed-off-by: dependabot[bot] <support@github.com>

* Bump hosted-git-info

Bumps [hosted-git-info](https://github.com/npm/hosted-git-info) from 2.8.8 to 2.8.9.
- [Release notes](https://github.com/npm/hosted-git-info/releases)
- [Changelog](https://github.com/npm/hosted-git-info/blob/v2.8.9/CHANGELOG.md)
- [Commits](https://github.com/npm/hosted-git-info/compare/v2.8.8...v2.8.9)

Signed-off-by: dependabot[bot] <support@github.com>

* Bump lodash in /packages/cli/test/dev/fixtures/03-aurelia

Bumps [lodash](https://github.com/lodash/lodash) from 4.17.15 to 4.17.21.
- [Release notes](https://github.com/lodash/lodash/releases)
- [Commits](https://github.com/lodash/lodash/compare/4.17.15...4.17.21)

Signed-off-by: dependabot[bot] <support@github.com>

* Bump url-parse in /packages/cli/test/dev/fixtures/02-angular-node

Bumps [url-parse](https://github.com/unshiftio/url-parse) from 1.4.7 to 1.5.1.
- [Release notes](https://github.com/unshiftio/url-parse/releases)
- [Commits](https://github.com/unshiftio/url-parse/compare/1.4.7...1.5.1)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Steven <steven@ceriously.com>
2021-05-24 15:49:31 -04:00
Steven
6288d5ef43 Publish Canary
- vercel@22.0.2-canary.6
 - @vercel/client@10.0.1-canary.5
 - @vercel/node@1.10.1-canary.2
 - @vercel/python@2.0.2-canary.0
2021-05-24 13:37:57 -04:00
Steven
00a97b7ae2 [node] Bump nft to 0.12.2 (#6274) 2021-05-24 13:34:28 -04:00
Steven
a19fb3aa0f [python] Fix installRequirement to use explicit version (#6272)
* [python] Fix installRequirement to use explicit version

* Remove `--upgrade`
2021-05-24 11:41:57 -04:00
Nathan Rajlich
93fae76f9c [cli] Simplify emoji module (#6260) 2021-05-21 13:53:13 -07:00
Nathan Rajlich
e5131d5557 [cli] Update NowConfig references to VercelConfig (#6256)
The `NowConfig` interface is deprecated.
2021-05-21 12:21:38 -07:00
Nathan Rajlich
930063638c [cli] Refactor vc logs command (#6259) 2021-05-21 11:36:58 -07:00
Nathan Rajlich
7b509ae672 [cli] Refactor vc switch command (#6257)
Refactors the `vc switch` command to more closely match how the team picker works on the Vercel dashboard:

 * Converts to TypeScript
 * Adds separator between user scope and team scopes
 * Sorts teams by name
 * No longer places the current scope at the top of the list (but the current scope is still selected by default)
2021-05-20 11:19:38 -07:00
Nathan Rajlich
711da37771 [cli] Refactor vc inspect command (#6258) 2021-05-20 10:30:33 -07:00
Steven
5e50b96994 Publish Canary
- vercel@22.0.2-canary.5
 - @vercel/node@1.10.1-canary.1
2021-05-13 16:40:36 -04:00
Steven
35d2f5950f [node] Bump nft to 0.12.0 (#6229) 2021-05-13 16:22:58 -04:00
Steven
37f969416c Publish Canary
- @vercel/build-utils@2.10.3-canary.4
 - vercel@22.0.2-canary.4
 - @vercel/client@10.0.1-canary.4
 - @vercel/node@1.10.1-canary.0
2021-05-10 14:01:41 -04:00
Steven
75630e0982 [node] Bump nft to 0.11.2 (#6211)
* [node] Bump nft to 0.11.2

* Fix tests since node 10.x is discontinued
2021-05-10 14:01:02 -04:00
dependabot[bot]
038c228af4 Bump hosted-git-info from 2.8.8 to 2.8.9 (#6200)
Bumps [hosted-git-info](https://github.com/npm/hosted-git-info) from 2.8.8 to 2.8.9.
- [Release notes](https://github.com/npm/hosted-git-info/releases)
- [Changelog](https://github.com/npm/hosted-git-info/blob/v2.8.9/CHANGELOG.md)
- [Commits](https://github.com/npm/hosted-git-info/compare/v2.8.8...v2.8.9)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-05-07 16:48:18 -04:00
dependabot[bot]
a17aaebba3 Bump lodash from 4.17.19 to 4.17.21 (#6195)
Bumps [lodash](https://github.com/lodash/lodash) from 4.17.19 to 4.17.21.
- [Release notes](https://github.com/lodash/lodash/releases)
- [Commits](https://github.com/lodash/lodash/compare/4.17.19...4.17.21)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-05-07 16:40:35 -04:00
dependabot[bot]
c39ba31b4b Bump handlebars from 4.7.6 to 4.7.7 (#6196)
Bumps [handlebars](https://github.com/wycats/handlebars.js) from 4.7.6 to 4.7.7.
- [Release notes](https://github.com/wycats/handlebars.js/releases)
- [Changelog](https://github.com/handlebars-lang/handlebars.js/blob/master/release-notes.md)
- [Commits](https://github.com/wycats/handlebars.js/compare/v4.7.6...v4.7.7)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-05-07 16:31:07 -04:00
Nathan Rajlich
91414d6f38 [build-utils] Remove 15-yarn-ignore-engines test fixture (#6193)
It's failing during runtime, like here: https://github.com/vercel/vercel/runs/2520732314

With an error like so:

```
[GET] / 2021-05-06T20:02:55.411Z	undefined	ERROR	The gRPC
binary module was not installed. This may be fixed by running "npm rebuild"
Original error: Cannot find module
'/var/task/node_modules/grpc/src/node/extension_binary/node-v83-linux-x64-glibc/grpc_node.node'
Require stack:
- /var/task/node_modules/grpc/src/grpc_extension.js
- /var/task/node_modules/grpc/src/client_interceptors.js
- /var/task/node_modules/grpc/src/client.js
- /var/task/node_modules/grpc/index.js
- /var/task/node_modules/google-gax/build/src/grpc.js
- /var/task/node_modules/google-gax/build/src/index.js
-
/var/task/node_modules/@google-cloud/scheduler/src/v1beta1/cloud_scheduler_client.js
- /var/task/node_modules/@google-cloud/scheduler/src/v1beta1/index.js
- /var/task/node_modules/@google-cloud/scheduler/src/index.js
- /var/task/index.js
- /var/task/___vc_launcher.js
- /var/runtime/UserFunction.js
- /var/runtime/index.js 2021-05-06T20:02:55.411Z	undefined	ERROR
Did you forget to add it to "dependencies" in `package.json`? RequestId:
2cdba37e-4bf4-4a2f-b443-4ebcc99fe308 Error: Runtime exited with error: exit
status 1 Runtime.ExitError
```

Because grpc is failing with NFT.

But also, the test is no longer necessary because we no longer support Node 8 which was what the original test intended https://github.com/vercel/now-builders/pull/463
2021-05-06 22:43:22 +00:00
Nathan Rajlich
00bb723db3 Publish Canary
- @vercel/build-utils@2.10.3-canary.3
 - vercel@22.0.2-canary.3
 - @vercel/client@10.0.1-canary.3
 - @vercel/frameworks@0.3.3-canary.3
2021-05-05 13:09:43 -07:00
Nathan Rajlich
65a97720b3 [cli] Refactor vc login command (#6185)
Refactors the CLI `vc login` command with the following features:

* Adds GitHub, GitLab and Bitbucket as login options
* Uses a "list" input to select which login method to use (same list as `vc init`)
* Support connecting SAML Profile to Vercel user during login
2021-05-05 13:08:26 -07:00
Steven
88eaf6efab [examples] Update pusher example to use deploy button (#6183) 2021-05-04 11:55:40 -04:00
Sam Ko
abf159d5e0 Update @ionic/angular devCommand (#6158)
Co-authored-by: Andy <AndyBitz@users.noreply.github.com>
2021-05-04 16:48:14 +02:00
luc
a23879b507 Publish Canary
- @vercel/build-utils@2.10.3-canary.2
 - vercel@22.0.2-canary.2
 - @vercel/client@10.0.1-canary.2
 - @vercel/frameworks@0.3.3-canary.2
2021-05-04 09:33:09 +02:00
Luc Leray
a713b9170b [frameworks] Add placeholder to frameworks' dev command (#6168)
* adjust type

* add placeholders

* adjust unit test
2021-05-03 17:26:27 +02:00
dependabot[bot]
8fd57459a1 Bump ssri from 6.0.1 to 6.0.2 (#6132)
Bumps [ssri](https://github.com/npm/ssri) from 6.0.1 to 6.0.2.
- [Release notes](https://github.com/npm/ssri/releases)
- [Changelog](https://github.com/npm/ssri/blob/v6.0.2/CHANGELOG.md)
- [Commits](https://github.com/npm/ssri/compare/v6.0.1...v6.0.2)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-05-01 20:29:19 -04:00
Markoz Peña
9a3dc8ed5f [dependencies] Bump husky from v3 to v6 (#6157)
Co-authored-by: Steven <steven@ceriously.com>
2021-05-01 20:28:34 -04:00
dependabot[bot]
c46dd8c556 Bump ssri from 6.0.1 to 6.0.2 in /examples/angular (#6131)
Bumps [ssri](https://github.com/npm/ssri) from 6.0.1 to 6.0.2.
- [Release notes](https://github.com/npm/ssri/releases)
- [Changelog](https://github.com/npm/ssri/blob/v6.0.2/CHANGELOG.md)
- [Commits](https://github.com/npm/ssri/compare/v6.0.1...v6.0.2)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-04-30 12:07:03 -04:00
JJ Kasper
e28edc5c93 Publish Canary
- @vercel/build-utils@2.10.3-canary.1
 - vercel@22.0.2-canary.1
 - @vercel/client@10.0.1-canary.1
 - @vercel/frameworks@0.3.3-canary.1
 - @vercel/routing-utils@1.11.1-canary.1
2021-04-29 11:03:01 -05:00
JJ Kasper
3fc4f64824 Replace regex lexer with minimal regex for named groups (#6171) 2021-04-29 11:01:39 -05:00
Steven
90e51287e9 [build-utils] Fix unit tests for Node.js version check (#6155)
Fixes unit tests since we throw now for Node.js 10 since April 20th.

https://vercel.com/changelog/node-js-10-is-being-deprecated
2021-04-26 18:54:56 +00:00
JJ Kasper
6d8b10802c Publish Canary
- @vercel/build-utils@2.10.3-canary.0
 - vercel@22.0.2-canary.0
 - @vercel/client@10.0.1-canary.0
 - @vercel/frameworks@0.3.3-canary.0
 - @vercel/routing-utils@1.11.1-canary.0
2021-04-23 14:48:09 -05:00
JJ Kasper
1dd2ffd895 Ensure segments from only has are replaced (#6142)
This ensures that when segments are coming from only `has` items we still replace them correctly as they currently don't get replaced if the `source` doesn't have any segments as well. 

### Related Issues

x-ref: https://vercel.slack.com/archives/CLDDX2Y0G/p1619061783470000

### 📋 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

- [ ] This PR has a concise title and thorough description useful to a reviewer
- [ ] Issue from task tracker has a link to this PR
2021-04-23 18:20:49 +00:00
Steven
e4d422a2d7 Publish Stable
- vercel@22.0.1
2021-04-22 15:07:42 -04:00
Steven
bd55ad8212 Publish Canary
- vercel@22.0.1-canary.0
2021-04-22 14:50:59 -04:00
Nathan Rajlich
6591dae152 [cli] Update default auth config comment to say "Vercel" (#6140) 2021-04-22 14:40:11 -04:00
Steven
a490faa496 [cli] Fix SAML usage with --token (#6139) 2021-04-22 14:39:51 -04:00
Steven
8fe16f1b0a [cli] Fix vercel login command (#6138)
* Revert file symbol usage for login

* Add missing remove() to test
2021-04-22 13:35:07 -04:00
Steven
bac19b66af Publish Stable
- @vercel/build-utils@2.10.2
 - vercel@22.0.0
 - @vercel/client@10.0.0
 - @vercel/frameworks@0.3.2
 - @vercel/node@1.10.0
 - @vercel/routing-utils@1.11.0
2021-04-21 16:57:38 -04:00
Steven
b58090a181 [examples] Update deploy URL to use main (#6133)
CH-21338
2021-04-21 16:37:16 -04:00
Steven
8c1b707f38 Publish Canary
- @vercel/build-utils@2.10.2-canary.6
 - vercel@21.3.4-canary.13
 - @vercel/client@9.0.9-canary.7
 - @vercel/frameworks@0.3.2-canary.6
2021-04-21 15:18:08 -04:00
Steven
4b4caccb3d [cli] (Major) Remove deprecated vc domains verify command (#6130)
The command `vc domains verify` was deprecated in #4959 so we can remove it for the next major version.
2021-04-21 19:07:39 +00:00
Steven
102dc04221 Update CI badge (#6116) 2021-04-15 10:50:25 -04:00
Nathan Rajlich
8d96a57117 Update references of default branch to "main" (#6114) 2021-04-15 10:11:13 -04:00
134 changed files with 1447 additions and 1294 deletions

View File

@@ -6,7 +6,7 @@ This directory is a brief example of a [Name](site-link) site that can be deploy
Deploy your own [Name] project with Vercel.
[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/import/project?template=https://github.com/vercel/vercel/tree/master/example-directory)
[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/import/project?template=https://github.com/vercel/vercel/tree/main/example-directory)
### How We Created This Example

View File

@@ -3,7 +3,7 @@ on:
push:
branches:
- '**'
- '!master'
- '!main'
jobs:
cancel:

View File

@@ -3,7 +3,7 @@ name: Publish
on:
push:
branches:
- master
- main
tags:
- '!*'

View File

@@ -3,7 +3,7 @@ name: CLI
on:
push:
branches:
- master
- main
tags:
- '!*'
pull_request:
@@ -29,9 +29,9 @@ jobs:
with:
fetch-depth: 100
- run: git --version
- run: git fetch origin master --depth=100
- run: git fetch origin main --depth=100
- run: git fetch origin ${{ github.ref }} --depth=100
- run: git diff origin/master...HEAD --name-only
- run: git diff origin/main...HEAD --name-only
- run: yarn install --network-timeout 1000000
- run: yarn run build
- run: yarn test-integration-cli --clean false

View File

@@ -3,7 +3,7 @@ name: Dev
on:
push:
branches:
- master
- main
tags:
- '!*'
pull_request:
@@ -29,9 +29,9 @@ jobs:
with:
fetch-depth: 100
- run: git --version
- run: git fetch origin master --depth=100
- run: git fetch origin main --depth=100
- run: git fetch origin ${{ github.ref }} --depth=100
- run: git diff origin/master...HEAD --name-only
- run: git diff origin/main...HEAD --name-only
- name: Install Hugo
if: matrix.os == 'macos-latest'
run: curl -L -O https://github.com/gohugoio/hugo/releases/download/v0.56.0/hugo_0.56.0_macOS-64bit.tar.gz && tar -xzf hugo_0.56.0_macOS-64bit.tar.gz && mv ./hugo packages/cli/test/dev/fixtures/08-hugo/

View File

@@ -3,7 +3,7 @@ name: E2E
on:
push:
branches:
- master
- main
tags:
- '!*'
pull_request:
@@ -24,9 +24,9 @@ jobs:
with:
fetch-depth: 100
- run: git --version
- run: git fetch origin master --depth=100
- run: git fetch origin main --depth=100
- run: git fetch origin ${{ github.ref }} --depth=100
- run: git diff origin/master...HEAD --name-only
- run: git diff origin/main...HEAD --name-only
- run: yarn install --network-timeout 1000000
- run: yarn run build
- run: yarn test-integration-once --clean false

View File

@@ -3,7 +3,7 @@ name: Unit
on:
push:
branches:
- master
- main
tags:
- '!*'
pull_request:
@@ -29,9 +29,9 @@ jobs:
with:
fetch-depth: 100
- run: git --version
- run: git fetch origin master --depth=100
- run: git fetch origin main --depth=100
- run: git fetch origin ${{ github.ref }} --depth=100
- run: git diff origin/master...HEAD --name-only
- run: git diff origin/main...HEAD --name-only
- run: yarn install --network-timeout 1000000
- run: yarn run build
- run: yarn run lint

1
.husky/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
_

4
.husky/pre-commit Executable file
View File

@@ -0,0 +1,4 @@
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"
yarn pre-commit

View File

@@ -6,7 +6,7 @@
<p align="center">Develop. Preview. Ship.</p>
</p>
[![CI Status](https://badgen.net/github/checks/vercel/vercel?label=CI)](https://github.com/vercel/vercel/actions?workflow=CI)
[![CI Status](https://github.com/vercel/vercel/actions/workflows/test-unit.yml/badge.svg)](https://github.com/vercel/vercel/actions/workflows/test-unit.yml)
[![Join the community on GitHub Discussions](https://badgen.net/badge/join%20the%20discussion/on%20github/black?icon=github)](https://github.com/vercel/vercel/discussions)
## Usage

View File

@@ -48,8 +48,8 @@ export default withApiHandler(async function (
const example = segment.slice(0, -ext.length);
await extract('https://github.com/vercel/vercel/archive/master.zip', TMP_DIR);
const directory = `${TMP_DIR}/vercel-master/examples/${example}`;
await extract('https://github.com/vercel/vercel/archive/main.zip', TMP_DIR);
const directory = `${TMP_DIR}/vercel-main/examples/${example}`;
if (!isDirectory(directory)) {
return notFound(res, `Example '${example}' was not found.`);

View File

@@ -8,8 +8,8 @@ export default withApiHandler(async function (
req: VercelRequest,
res: VercelResponse
) {
await extract('https://github.com/vercel/vercel/archive/master.zip', '/tmp');
const exampleList = summary('/tmp/vercel-master/examples');
await extract('https://github.com/vercel/vercel/archive/main.zip', '/tmp');
const exampleList = summary('/tmp/vercel-main/examples');
const existingExamples = Array.from(exampleList).map(key => ({
name: key,

View File

@@ -10,7 +10,7 @@
"dependencies": {
"@sentry/node": "5.11.1",
"got": "10.2.1",
"node-fetch": "2.6.0",
"node-fetch": "2.6.1",
"parse-github-url": "1.0.2",
"tar-fs": "2.0.0",
"unzip-stream": "0.3.0"

View File

@@ -362,10 +362,10 @@ ms@^2.1.1:
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009"
integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==
node-fetch@2.6.0:
version "2.6.0"
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.0.tgz#e633456386d4aa55863f676a7ab0daa8fdecb0fd"
integrity sha512-8dG4H5ujfvFiqDmVu9fQ5bOHUC15JMjMY/Zumv26oOvvVJjM67KF8koCWIabKQ1GJIa9r2mMZscBq/TbdOcmNA==
node-fetch@2.6.1:
version "2.6.1"
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.1.tgz#045bd323631f76ed2e2b55573394416b639a0052"
integrity sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==
normalize-url@^4.1.0:
version "4.5.0"

14
examples/README.md vendored
View File

@@ -44,13 +44,13 @@ We are continuously improving our examples based on best practices and feedback
For example, the previous `nodejs` example showed a static frontend with a Node.js API. This is illustrated in the `svelte` example. Below is a table that lists some of the most popular previous examples and the equivalent replacement:
| Previous Example | New Example |
| ----------------- | ------------------------------------------------------------------------------------------ |
| **monorepo** | [gatsby-functions](https://github.com/vercel/vercel/tree/master/examples/gatsby) |
| **nodejs** | [svelte-functions](https://github.com/vercel/vercel/tree/master/examples/svelte) |
| **nextjs-static** | [nextjs](https://github.com/vercel/vercel/tree/master/examples/nextjs) |
| **vanilla-go** | [create-react-app](https://github.com/vercel/vercel/tree/master/examples/create-react-app) |
| **typescript** | [gatsby-functions](https://github.com/vercel/vercel/tree/master/examples/gatsby) |
| Previous Example | New Example |
| ----------------- | ---------------------------------------------------------------------------------------- |
| **monorepo** | [gatsby-functions](https://github.com/vercel/vercel/tree/main/examples/gatsby) |
| **nodejs** | [svelte-functions](https://github.com/vercel/vercel/tree/main/examples/svelte) |
| **nextjs-static** | [nextjs](https://github.com/vercel/vercel/tree/main/examples/nextjs) |
| **vanilla-go** | [create-react-app](https://github.com/vercel/vercel/tree/main/examples/create-react-app) |
| **typescript** | [gatsby-functions](https://github.com/vercel/vercel/tree/main/examples/gatsby) |
## Migrating and Upgrading

View File

@@ -6,7 +6,7 @@ This directory is a brief example of an [AMP](https://amp.dev/) site that can be
Deploy your own AMP project with Vercel.
[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/import/project?template=https://github.com/vercel/vercel/tree/master/examples/amp)
[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/import/project?template=https://github.com/vercel/vercel/tree/main/examples/amp)
_Live Example: https://amp.now-examples.now.sh_

View File

@@ -8,7 +8,7 @@ This directory is a brief example of an [Angular](https://angular.io/) app that
Deploy your own Angular project with Vercel.
[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/import/project?template=https://github.com/vercel/vercel/tree/master/examples/angular)
[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/import/project?template=https://github.com/vercel/vercel/tree/main/examples/angular)
_Live Example: https://angular.now-examples.now.sh_

View File

@@ -3073,9 +3073,9 @@ hmac-drbg@^1.0.1:
minimalistic-crypto-utils "^1.0.1"
hosted-git-info@^2.1.4, hosted-git-info@^2.6.0, hosted-git-info@^2.7.1:
version "2.8.8"
resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.8.tgz#7539bd4bc1e0e0a895815a2e0262420b12858488"
integrity sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg==
version "2.8.9"
resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.9.tgz#dffc0bf9a21c02209090f2aa69429e1414daf3f9"
integrity sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==
hpack.js@^2.1.6:
version "2.1.6"
@@ -6204,9 +6204,9 @@ sshpk@^1.7.0:
tweetnacl "~0.14.0"
ssri@^6.0.0, ssri@^6.0.1:
version "6.0.1"
resolved "https://registry.yarnpkg.com/ssri/-/ssri-6.0.1.tgz#2a3c41b28dd45b62b63676ecb74001265ae9edd8"
integrity sha512-3Wge10hNcT1Kur4PDFwEieXSCMCJs/7WvSACcrMYrNp+b8kDL1/0wJch5Ni2WrtwEa2IO8OsVfeKIciKCDx/QA==
version "6.0.2"
resolved "https://registry.yarnpkg.com/ssri/-/ssri-6.0.2.tgz#157939134f20464e7301ddba3e90ffa8f7728ac5"
integrity sha512-cepbSq/neFK7xB6A50KHN0xHDotYzq58wWCa5LeWqnPrHG8GzfEjO/4O8kpmcGW+oaxkvhEJCWgbgNk4/ZV93Q==
dependencies:
figgy-pudding "^3.5.1"

View File

@@ -8,7 +8,7 @@ This directory is a brief example of a [Blitz.js](https://blitzjs.com/) project
Deploy your own Blitz.js project with Vercel by viewing the [documentation on deploying to Vercel](https://blitzjs.com/docs/deploy-vercel)
[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/import/project?template=https://github.com/vercel/vercel/tree/master/examples/blitzjs)
[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/import/project?template=https://github.com/vercel/vercel/tree/main/examples/blitzjs)
### How We Created This Example

View File

@@ -4681,9 +4681,9 @@ hmac-drbg@^1.0.1:
minimalistic-crypto-utils "^1.0.1"
hosted-git-info@^2.1.4:
version "2.8.8"
resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.8.tgz#7539bd4bc1e0e0a895815a2e0262420b12858488"
integrity sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg==
version "2.8.9"
resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.9.tgz#dffc0bf9a21c02209090f2aa69429e1414daf3f9"
integrity sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==
hsl-regex@^1.0.0:
version "1.0.0"
@@ -8195,9 +8195,9 @@ sprintf-js@~1.0.2:
integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=
ssri@^6.0.1:
version "6.0.1"
resolved "https://registry.yarnpkg.com/ssri/-/ssri-6.0.1.tgz#2a3c41b28dd45b62b63676ecb74001265ae9edd8"
integrity sha512-3Wge10hNcT1Kur4PDFwEieXSCMCJs/7WvSACcrMYrNp+b8kDL1/0wJch5Ni2WrtwEa2IO8OsVfeKIciKCDx/QA==
version "6.0.2"
resolved "https://registry.yarnpkg.com/ssri/-/ssri-6.0.2.tgz#157939134f20464e7301ddba3e90ffa8f7728ac5"
integrity sha512-cepbSq/neFK7xB6A50KHN0xHDotYzq58wWCa5LeWqnPrHG8GzfEjO/4O8kpmcGW+oaxkvhEJCWgbgNk4/ZV93Q==
dependencies:
figgy-pudding "^3.5.1"

View File

@@ -6,7 +6,7 @@ This directory is a brief example of a [Brunch](https://brunch.io/) site that ca
Deploy your own Brunch project with Vercel.
[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/import/project?template=https://github.com/vercel/vercel/tree/master/examples/brunch)
[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/import/project?template=https://github.com/vercel/vercel/tree/main/examples/brunch)
_Live Example: https://brunch.now-examples.now.sh_

View File

@@ -8,7 +8,7 @@ This directory is a brief example of a [React](https://reactjs.org/) app with [S
Deploy your own React project, along with Serverless Functions, with Vercel.
[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/import/project?template=https://github.com/vercel/vercel/tree/master/examples/create-react-app-functions)
[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/import/project?template=https://github.com/vercel/vercel/tree/main/examples/create-react-app-functions)
_Live Example: https://create-react-app.now-examples.now.sh/_

View File

@@ -28,7 +28,7 @@ function App() {
</h2>
<p>
<a
href="https://github.com/vercel/vercel/tree/master/examples/create-react-app"
href="https://github.com/vercel/vercel/tree/main/examples/create-react-app"
target="_blank"
rel="noreferrer noopener"
>

View File

@@ -6,7 +6,7 @@ This directory is a brief example of using a Custom Build script that can be dep
Deploy your own Custom Built project with Vercel.
[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/import/project?template=https://github.com/vercel/vercel/tree/master/examples/custom-build)
[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/import/project?template=https://github.com/vercel/vercel/tree/main/examples/custom-build)
_Live Example: https://custom-build.now-examples.now.sh_

View File

@@ -6,7 +6,7 @@ This directory is a brief example of a [Docusaurus](https://v2.docusaurus.io) si
Deploy your own Docusaurus project with Vercel.
[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/import/project?template=https://github.com/vercel/vercel/tree/master/docusaurus-2)
[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/import/project?template=https://github.com/vercel/vercel/tree/main/docusaurus-2)
### How We Created This Example

View File

@@ -8,7 +8,7 @@ This directory is a brief example of a [Docusaurus](https://docusaurus.io/) site
Deploy your own Docusaurus project with Vercel.
[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/import/project?template=https://github.com/vercel/vercel/tree/master/examples/docusaurus)
[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/import/project?template=https://github.com/vercel/vercel/tree/main/examples/docusaurus)
_Live Example: https://docusaurus.now-examples.now.sh_

View File

@@ -6,7 +6,7 @@ This directory is a brief example of a [Dojo](https://dojo.io) site that can be
Deploy your own Dojo project with Vercel.
[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/import/project?template=https://github.com/vercel/vercel/tree/master/dojo)
[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/import/project?template=https://github.com/vercel/vercel/tree/main/dojo)
### How We Created This Example

View File

@@ -8,7 +8,7 @@ This directory is a brief example of a [Eleventy](https://www.11ty.io/) site tha
Deploy your own Eleventy project with Vercel.
[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/import/project?template=https://github.com/vercel/vercel/tree/master/examples/eleventy)
[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/import/project?template=https://github.com/vercel/vercel/tree/main/examples/eleventy)
_Live Example: https://eleventy.now-examples.now.sh_

View File

@@ -8,7 +8,7 @@ This directory is a brief example of an [Ember](https://emberjs.com/) app that c
Deploy your own Ember project with Vercel.
[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/import/project?template=https://github.com/vercel/vercel/tree/master/examples/ember)
[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/import/project?template=https://github.com/vercel/vercel/tree/main/examples/ember)
_Live Example: https://ember.now-examples.now.sh_

View File

@@ -8,7 +8,7 @@ This directory is a brief example of a [Gatsby](https://www.gatsbyjs.org/) app w
Deploy your own Gatsby project, along with Serverless Functions, with Vercel.
[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/import/project?template=https://github.com/vercel/vercel/tree/master/examples/gatsby)
[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/import/project?template=https://github.com/vercel/vercel/tree/main/examples/gatsby)
_Live Example: https://gatsby.now-examples.now.sh_

View File

@@ -31,7 +31,7 @@ function Index() {
</h2>
<p>
<a
href="https://github.com/vercel/vercel/tree/master/examples/gatsby"
href="https://github.com/vercel/vercel/tree/main/examples/gatsby"
target="_blank"
rel="noreferrer noopener"
>

View File

@@ -8,7 +8,7 @@ This directory is a brief example of a [Gridsome](https://gridsome.org/) app tha
Deploy your own Gridsome project with Vercel.
[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/import/project?template=https://github.com/vercel/vercel/tree/master/examples/gridsome)
[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/import/project?template=https://github.com/vercel/vercel/tree/main/examples/gridsome)
_Live Example: https://gridsome.now-examples.now.sh_

View File

@@ -8,7 +8,7 @@ This directory is a brief example of a [Hexo](https://hexo.io/) site that can be
Deploy your own Hexo project with Vercel.
[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/import/project?template=https://github.com/vercel/vercel/tree/master/examples/hexo)
[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/import/project?template=https://github.com/vercel/vercel/tree/main/examples/hexo)
_Live Example: https://hexo.now-examples.now.sh_

View File

@@ -6,7 +6,7 @@ This directory is a brief example of a [Hugo](https://gohugo.io/) app that can b
Deploy your own Hugo project with Vercel.
[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/import/project?template=https://github.com/vercel/vercel/tree/master/examples/hugo)
[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/import/project?template=https://github.com/vercel/vercel/tree/main/examples/hugo)
_Live Example: https://hugo.now-examples.now.sh_

View File

@@ -6,7 +6,7 @@ This directory is a brief example of an [Ionic Angular](https://ionicframework.c
Deploy your own Ionic Angular project with Vercel.
[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/import/project?template=https://github.com/vercel/vercel/tree/master/examples/ionic-angular)
[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/import/project?template=https://github.com/vercel/vercel/tree/main/examples/ionic-angular)
_Live Example: https://ionic-angular.now-examples.now.sh_

View File

@@ -6,7 +6,7 @@ This directory is a brief example of an [Ionic React](https://ionicframework.com
Deploy your own Ionic React project with Vercel.
[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/import/project?template=https://github.com/vercel/vercel/tree/master/examples/ionic-react)
[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/import/project?template=https://github.com/vercel/vercel/tree/main/examples/ionic-react)
_Live Example: https://ionic-react.now-examples.now.sh_

View File

@@ -6,7 +6,7 @@ This directory is a brief example of a [Jekyll](https://jekyllrb.com/) site that
Deploy your own Jekyll project with Vercel.
[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/import/project?template=https://github.com/vercel/vercel/tree/master/examples/jekyll)
[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/import/project?template=https://github.com/vercel/vercel/tree/main/examples/jekyll)
_Live Example: https://jekyll.now-examples.now.sh_

View File

@@ -6,7 +6,7 @@ This directory is a brief example of a [Middleman](https://middlemanapp.com/) si
Deploy your own Middleman project with Vercel.
[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/import/project?template=https://github.com/vercel/vercel/tree/master/examples/middleman)
[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/import/project?template=https://github.com/vercel/vercel/tree/main/examples/middleman)
_Live Example: https://middleman.now-examples.now.sh_

View File

@@ -6,7 +6,7 @@ This directory is a brief example of a [Nuxt.js](https://nuxtjs.org) app that ca
Deploy your own Nuxt.js project with Vercel.
[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/import/project?template=https://github.com/vercel/vercel/tree/master/examples/nuxtjs)
[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/import/project?template=https://github.com/vercel/vercel/tree/main/examples/nuxtjs)
_Live Example: https://nuxtjs.now-examples.now.sh_

View File

@@ -8,7 +8,7 @@ This directory is a brief example of a [Polymer](https://www.polymer-project.org
Deploy your own Polymer project with Vercel.
[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/import/project?template=https://github.com/vercel/vercel/tree/master/examples/polymer)
[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/import/project?template=https://github.com/vercel/vercel/tree/main/examples/polymer)
_Live Example: https://polymer.now-examples.now.sh_

View File

@@ -8,7 +8,7 @@ This directory is a brief example of a [Preact](https://preactjs.com/) app that
Deploy your own Preact project with Vercel.
[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/import/project?template=https://github.com/vercel/vercel/tree/master/examples/preact)
[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/import/project?template=https://github.com/vercel/vercel/tree/main/examples/preact)
_Live Example: https://preact.now-examples.now.sh_

View File

@@ -8,7 +8,7 @@ This directory is a brief example of a [RedwoodJS](https://redwoodjs.com) app wi
Deploy your own RedwoodJS project, along with Serverless Functions, with Vercel.
[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/import/project?template=https://github.com/vercel/vercel/tree/master/examples/redwoodjs)
[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/import/project?template=https://github.com/vercel/vercel/tree/main/examples/redwoodjs)
_Live Example: https://redwoodjs.now-examples.now.sh_

View File

@@ -8,7 +8,7 @@ This directory is a brief example of a [Saber](https://saber.land) site that can
Deploy your own Saber project with Vercel.
[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/import/project?template=https://github.com/vercel/vercel/tree/master/examples/saber)
[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/import/project?template=https://github.com/vercel/vercel/tree/main/examples/saber)
_Live Example: https://saber.now-examples.now.sh_

View File

@@ -6,7 +6,7 @@ This directory is a brief example of a [Sapper](https://sapper.svelte.dev/) app
Deploy your own Sapper project with Vercel.
[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/import/project?template=https://github.com/vercel/vercel/tree/master/examples/sapper)
[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/import/project?template=https://github.com/vercel/vercel/tree/main/examples/sapper)
_Live Example: https://sapper.now-examples.now.sh_

View File

@@ -8,7 +8,7 @@ This directory is a brief example of a [Scully](https://scully.io) site that can
Deploy your own Scully project with Vercel.
[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/import/project?template=https://github.com/vercel/vercel/tree/master/examples/scully)
[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/import/project?template=https://github.com/vercel/vercel/tree/main/examples/scully)
_Live Example: https://scully.now-examples.now.sh_

View File

@@ -6,7 +6,7 @@ This directory is a brief example of a [Stencil](https://stenciljs.com/) app tha
Deploy your own Stencil project with Vercel.
[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/import/project?template=https://github.com/vercel/vercel/tree/master/examples/stencil)
[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/import/project?template=https://github.com/vercel/vercel/tree/main/examples/stencil)
_Live Example: https://stencil.now-examples.now.sh_

View File

@@ -6,7 +6,7 @@ This directory is a brief example of a [Svelte](https://svelte.dev/) app with [S
Deploy your own Svelte project, along with Serverless Functions, with Vercel.
[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/import/project?template=https://github.com/vercel/vercel/tree/master/examples/svelte)
[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/import/project?template=https://github.com/vercel/vercel/tree/main/examples/svelte)
_Live Example: https://svelte.now-examples.now.sh_

View File

@@ -20,7 +20,7 @@
</h2>
<p>
<a
href="https://github.com/vercel/vercel/tree/master/examples/svelte"
href="https://github.com/vercel/vercel/tree/main/examples/svelte"
target="_blank"
rel="noreferrer noopener">
This project

View File

@@ -8,7 +8,7 @@ This directory is a brief example of a [UmiJS](https://umijs.org/) app that can
Deploy your own UmiJS project with Vercel.
[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/import/project?template=https://github.com/vercel/vercel/tree/master/examples/umijs)
[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/import/project?template=https://github.com/vercel/vercel/tree/main/examples/umijs)
_Live Example: https://umijs.now-examples.now.sh_

View File

@@ -1,19 +1,9 @@
# Realtime Pusher Whiteboard
# Realtime Pusher Example
This directory is a realtime serverless whiteboard powered by Pusher. The frontend is static vanilla HTML, CSS, and JavaScript and the backend is a single Node.js function.
This directory is a realtime serverless whiteboard powered by Pusher. The frontend is static vanilla HTML, CSS, and JavaScript and the backend is a single Node.js function both deployed with Vercel and zero configuration.
## Creating This Example
## Deploy Your Own
To get started with a realtime whiteboard on Vercel, you can use [Vercel CLI](https://vercel.com/download) to initialize the project:
Deploy your own Pusher project with Vercel.
```shell
$ vercel init vanilla-pusher-functions
```
### Deploying From Your Terminal
You can deploy your new project with a single command from your terminal using [Vercel CLI](https://vercel.com/download):
```shell
$ vercel
```
[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/git/external?repository-url=https%3A%2F%2Fgithub.com%2Fvercel%2Fvercel%2Ftree%2Fmain%2Fexamples%2Fvanilla-pusher-functions&env=APP_ID,KEY,SECRET,CLUSTER&envDescription=Pusher%20Channels&envLink=https%3A%2F%2Fpusher.com%2Fchannels)

View File

@@ -1,9 +0,0 @@
{
"version": 2,
"env": {
"APP_ID": "@pusher-whiteboard-app-id",
"KEY": "@pusher-whiteboard-key",
"SECRET": "@pusher-whiteboard-secret",
"CLUSTER": "us2"
}
}

View File

@@ -8,7 +8,7 @@ This directory is a brief example of a [Vue.js](https://vuejs.org/) app that can
Deploy your own Vue.js project with Vercel.
[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/import/project?template=https://github.com/vercel/vercel/tree/master/examples/vue)
[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/import/project?template=https://github.com/vercel/vercel/tree/main/examples/vue)
_Live Example: https://vue.now-examples.now.sh_

View File

@@ -5,7 +5,7 @@
"command": {
"publish": {
"npmClient": "npm",
"allowBranch": ["master"],
"allowBranch": ["main"],
"registry": "https://registry.npmjs.org/"
}
},

View File

@@ -24,7 +24,7 @@
"eslint": "6.2.2",
"eslint-config-prettier": "6.1.0",
"eslint-plugin-jest": "23.8.2",
"husky": "3.0.4",
"husky": "6.0.0",
"json5": "2.1.1",
"lint-staged": "9.2.5",
"node-fetch": "2.6.1",
@@ -40,11 +40,13 @@
"changelog": "node utils/changelog.js",
"build": "node utils/run.js build all",
"vercel-build": "mkdir -p public && echo '<a href=\"https://vercel.com/import\">Import</a>' > public/output.html",
"pre-commit": "lint-staged",
"test-unit": "node utils/run.js test-unit",
"test-integration-cli": "node utils/run.js test-integration-cli",
"test-integration-once": "node utils/run.js test-integration-once",
"test-integration-dev": "node utils/run.js test-integration-dev",
"lint": "eslint . --ext .ts,.js"
"lint": "eslint . --ext .ts,.js",
"prepare": "husky install"
},
"lint-staged": {
"./{*,{api,packages,test,utils}/**/*}.{js,ts}": [
@@ -57,11 +59,6 @@
"git add"
]
},
"husky": {
"hooks": {
"pre-commit": "lint-staged"
}
},
"prettier": {
"trailingComma": "es5",
"singleQuote": true,

View File

@@ -1,6 +1,6 @@
{
"name": "@vercel/build-utils",
"version": "2.10.2-canary.5",
"version": "2.10.3-canary.4",
"license": "MIT",
"main": "./dist/index.js",
"types": "./dist/index.d.js",
@@ -29,7 +29,7 @@
"@types/node-fetch": "^2.1.6",
"@types/semver": "6.0.0",
"@types/yazl": "^2.4.1",
"@vercel/frameworks": "0.3.2-canary.5",
"@vercel/frameworks": "0.3.3-canary.3",
"@vercel/ncc": "0.24.0",
"aggregate-error": "3.0.1",
"async-retry": "1.2.3",

View File

@@ -1,9 +0,0 @@
const scheduler = require('@google-cloud/scheduler');
module.exports = (_, res) => {
if (scheduler) {
res.end('found:RANDOMNESS_PLACEHOLDER');
} else {
res.end('nope:RANDOMNESS_PLACEHOLDER');
}
};

View File

@@ -1,10 +0,0 @@
{
"version": 2,
"builds": [
{
"src": "index.js",
"use": "@vercel/node"
}
],
"probes": [{ "path": "/", "mustContain": "found:RANDOMNESS_PLACEHOLDER" }]
}

View File

@@ -1,8 +0,0 @@
{
"name": "15-yarn-ignore-engines",
"version": "1.0.0",
"main": "index.js",
"dependencies": {
"@google-cloud/scheduler": "0.3.0"
}
}

View File

@@ -97,10 +97,6 @@ it('should create zip files with symlinks properly', async () => {
});
it('should only match supported node versions, otherwise throw an error', async () => {
expect(await getSupportedNodeVersion('10.x', false)).toHaveProperty(
'major',
10
);
expect(await getSupportedNodeVersion('12.x', false)).toHaveProperty(
'major',
12
@@ -121,10 +117,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('10.x', true)).toHaveProperty(
'major',
10
);
expect(await getSupportedNodeVersion('12.x', true)).toHaveProperty(
'major',
12
@@ -154,20 +146,20 @@ 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('10.0.0')).toHaveProperty('major', 10);
expect(await getSupportedNodeVersion('10.x')).toHaveProperty('major', 10);
expect(await getSupportedNodeVersion('12.0.0')).toHaveProperty('major', 12);
expect(await getSupportedNodeVersion('12.x')).toHaveProperty('major', 12);
expect(await getSupportedNodeVersion('>=10')).toHaveProperty('major', 14);
expect(await getSupportedNodeVersion('>=10.3.0')).toHaveProperty('major', 14);
expect(await getSupportedNodeVersion('8.5.0 - 10.5.0')).toHaveProperty(
expect(await getSupportedNodeVersion('11.5.0 - 12.5.0')).toHaveProperty(
'major',
10
12
);
expect(await getSupportedNodeVersion('>=9.5.0 <=10.5.0')).toHaveProperty(
expect(await getSupportedNodeVersion('>=9.5.0 <=12.5.0')).toHaveProperty(
'major',
10
12
);
expect(await getSupportedNodeVersion('~10.5.0')).toHaveProperty('major', 10);
expect(await getSupportedNodeVersion('^10.5.0')).toHaveProperty('major', 10);
expect(await getSupportedNodeVersion('~12.5.0')).toHaveProperty('major', 12);
expect(await getSupportedNodeVersion('^12.5.0')).toHaveProperty('major', 12);
});
it('should ignore node version in vercel dev getNodeVersion()', async () => {

View File

@@ -1 +0,0 @@
declare module 'inquirer';

View File

@@ -0,0 +1,5 @@
declare module 'jsonlines' {
import { Transform } from 'stream';
function parse(): Transform;
}

View File

@@ -1,6 +1,6 @@
{
"name": "vercel",
"version": "21.3.4-canary.12",
"version": "22.0.2-canary.6",
"preferGlobal": true,
"license": "Apache-2.0",
"description": "The command-line interface for Vercel",
@@ -61,10 +61,10 @@
"node": ">= 12"
},
"dependencies": {
"@vercel/build-utils": "2.10.2-canary.5",
"@vercel/build-utils": "2.10.3-canary.4",
"@vercel/go": "1.2.2",
"@vercel/node": "1.9.2-canary.1",
"@vercel/python": "2.0.1",
"@vercel/node": "1.10.1-canary.2",
"@vercel/python": "2.0.2",
"@vercel/ruby": "1.2.6",
"update-notifier": "4.1.0"
},
@@ -82,6 +82,7 @@
"@types/fs-extra": "5.0.5",
"@types/glob": "7.1.1",
"@types/http-proxy": "1.16.2",
"@types/inquirer": "7.3.1",
"@types/load-json-file": "2.0.7",
"@types/mime-types": "2.1.0",
"@types/minimatch": "3.0.3",
@@ -99,7 +100,7 @@
"@types/universal-analytics": "0.4.2",
"@types/which": "1.3.2",
"@types/write-json-file": "2.2.1",
"@vercel/frameworks": "0.3.2-canary.5",
"@vercel/frameworks": "0.3.3-canary.3",
"@vercel/ncc": "0.24.0",
"@zeit/fun": "0.11.2",
"@zeit/source-map-support": "0.6.2",

View File

@@ -19,7 +19,7 @@ import link from '../../util/output/link';
import { User } from '../../types';
import { getCommandName } from '../../util/pkg-name';
import toHost from '../../util/to-host';
import { NowConfig } from '../../util/dev/types';
import { VercelConfig } from '../../util/dev/types';
type Options = {
'--debug': boolean;
@@ -421,7 +421,7 @@ function handleCreateAliasError<T>(
return error;
}
function getTargetsForAlias(args: string[], { alias }: NowConfig) {
function getTargetsForAlias(args: string[], { alias }: VercelConfig) {
if (args.length) {
return [args[args.length - 1]]
.map(target => (target.indexOf('.') !== -1 ? toHost(target) : target))

View File

@@ -13,7 +13,6 @@ import inspect from './inspect';
import ls from './ls';
import rm from './rm';
import move from './move';
import verify from './verify';
import { getPkgName } from '../../util/pkg-name';
const help = () => {
@@ -81,7 +80,6 @@ const COMMAND_CONFIG = {
move: ['move'],
rm: ['rm', 'remove'],
transferIn: ['transfer-in'],
verify: ['verify'],
};
export default async function main(client: Client) {
@@ -119,8 +117,6 @@ export default async function main(client: Client) {
return rm(client, argv, args);
case 'transferIn':
return transferIn(client, argv, args);
case 'verify':
return verify(client, argv, args);
default:
return ls(client, argv, args);
}

View File

@@ -1,31 +0,0 @@
import Client from '../../util/client';
import { NowBuildError } from '@vercel/build-utils';
import { getCommandName } from '../../util/pkg-name';
export default async function verify(
{ output }: Client,
_opts: {},
args: string[]
) {
const [domainName] = args;
if (!domainName) {
output.error(
`${getCommandName(`domains verify <domain>`)} expects one argument`
);
return 1;
}
const error = new NowBuildError({
code: 'domains_verify_command_deprecated',
message: `It's not necessary to verify Domains anymore. Instead, you can run ${getCommandName(
`domains inspect ${domainName}`
)} to see what you need to do in order to configure it properly.`,
link: 'https://vercel.link/domain-verification-via-cli',
});
output.prettyError(error);
return 0;
}

View File

@@ -111,7 +111,6 @@ async function chooseFromDropdown(message: string, exampleList: string[]) {
return listInput({
message,
separator: false,
choices,
});
}

View File

@@ -3,12 +3,14 @@ import getArgs from '../util/get-args';
import buildsList from '../util/output/builds';
import routesList from '../util/output/routes';
import indent from '../util/output/indent';
import Now from '../util';
import logo from '../util/output/logo';
import elapsed from '../util/output/elapsed.ts';
import elapsed from '../util/output/elapsed';
import { handleError } from '../util/error';
import getScope from '../util/get-scope.ts';
import { getPkgName, getCommandName } from '../util/pkg-name.ts';
import getScope from '../util/get-scope';
import { getPkgName, getCommandName } from '../util/pkg-name';
import Client from '../util/client';
import { getDeployment } from '../util/get-deployment';
import { Deployment } from '@vercel/client';
const help = () => {
console.log(`
@@ -33,15 +35,15 @@ const help = () => {
${chalk.gray('')} Get information about a deployment by its unique URL
${chalk.cyan(`$ ${getPkgName()} inspect my-deployment-ji2fjij2.now.sh`)}
${chalk.cyan(`$ ${getPkgName()} inspect my-deployment-ji2fjij2.vercel.app`)}
${chalk.gray('-')} Get information about the deployment an alias points to
${chalk.cyan(`$ ${getPkgName()} inspect my-deployment.now.sh`)}
${chalk.cyan(`$ ${getPkgName()} inspect my-deployment.vercel.app`)}
`);
};
export default async function main(client) {
export default async function main(client: Client) {
let deployment;
let argv;
@@ -57,14 +59,7 @@ export default async function main(client) {
return 2;
}
const {
apiUrl,
output,
authConfig: { token },
config,
} = client;
const debugEnabled = argv['--debug'];
const { print, log, error } = output;
const { print, log, error } = client.output;
// extract the first parameter
const [, deploymentIdOrHost] = argv._;
@@ -75,9 +70,7 @@ export default async function main(client) {
return 1;
}
const { currentTeam } = config;
let contextName = null;
let contextName: string | null = null;
try {
({ contextName } = await getScope(client));
@@ -90,22 +83,14 @@ export default async function main(client) {
throw err;
}
const now = new Now({
apiUrl,
token,
debug: debugEnabled,
currentTeam,
output,
});
// resolve the deployment, since we might have been given an alias
const depFetchStart = Date.now();
output.spinner(
client.output.spinner(
`Fetching deployment "${deploymentIdOrHost}" in ${chalk.bold(contextName)}`
);
try {
deployment = await now.findDeployment(deploymentIdOrHost);
deployment = await getDeployment(client, deploymentIdOrHost);
} catch (err) {
if (err.status === 404) {
error(
@@ -127,11 +112,11 @@ export default async function main(client) {
throw err;
}
const { id, name, url, created, routes, readyState } = deployment;
const { id, name, url, createdAt, routes, readyState } = deployment;
const { builds } =
deployment.version === 2
? await now.fetch(`/v1/now/deployments/${id}/builds`)
? await client.fetch(`/v1/now/deployments/${id}/builds`)
: { builds: [] };
log(
@@ -146,10 +131,10 @@ export default async function main(client) {
print(` ${chalk.cyan('name')}\t${name}\n`);
print(` ${chalk.cyan('readyState')}\t${stateString(readyState)}\n`);
print(` ${chalk.cyan('url')}\t\t${url}\n`);
if (created) {
if (createdAt) {
print(
` ${chalk.cyan('createdAt')}\t${new Date(created)} ${elapsed(
Date.now() - created,
` ${chalk.cyan('createdAt')}\t${new Date(createdAt)} ${elapsed(
Date.now() - createdAt,
true
)}\n`
);
@@ -157,7 +142,7 @@ export default async function main(client) {
print('\n\n');
if (builds.length > 0) {
const times = {};
const times: { [id: string]: string | null } = {};
for (const build of builds) {
const { id, createdAt, readyStateAt } = build;
@@ -179,7 +164,7 @@ export default async function main(client) {
}
// renders the state string
function stateString(s) {
function stateString(s: Deployment['readyState']) {
switch (s) {
case 'INITIALIZING':
return chalk.yellow(s);

View File

@@ -1,11 +1,10 @@
import inquirer from 'inquirer';
import { validate as validateEmail } from 'email-validator';
import chalk from 'chalk';
import hp from '../util/humanize-path';
import getArgs from '../util/get-args';
import error from '../util/output/error';
import handleError from '../util/handle-error';
import logo from '../util/output/logo';
import prompt from '../util/login/prompt';
import doSsoLogin from '../util/login/sso';
import doEmailLogin from '../util/login/email';
import { prependEmoji, emoji } from '../util/emoji';
@@ -13,6 +12,7 @@ import { getCommandName, getPkgName } from '../util/pkg-name';
import getGlobalPathConfig from '../util/config/global-path';
import { writeToAuthConfigFile, writeToConfigFile } from '../util/config/files';
import Client from '../util/client';
import { LoginParams } from '../util/login/types';
const help = () => {
console.log(`
@@ -44,35 +44,6 @@ const help = () => {
`);
};
const readInput = async () => {
let input;
while (!input) {
try {
const { val } = await inquirer.prompt({
type: 'input',
name: 'val',
message: 'Enter your email or team slug:',
});
input = val;
} catch (err) {
console.log(); // \n
if (err.isTtyError) {
throw new Error(
error(
`Interactive mode not supported please run ${getCommandName(
`login you@domain.com`
)}`
)
);
}
}
}
return input;
};
export default async function login(client: Client): Promise<number> {
let argv;
const { apiUrl, output } = client;
@@ -94,21 +65,21 @@ export default async function login(client: Client): Promise<number> {
return 2;
}
const input = argv._[1] || (await readInput());
// TODO: add proper validation
const isValidSlug = true;
const input = argv._[1];
let result: number | string = 1;
const params: LoginParams = { output, apiUrl };
if (validateEmail(input)) {
result = await doEmailLogin(input, { output, apiUrl });
} else if (isValidSlug) {
result = await doSsoLogin(input, { output, apiUrl });
if (input) {
// Email or Team slug was provided via command line
if (validateEmail(input)) {
result = await doEmailLogin(input, params);
} else {
result = await doSsoLogin(input, params);
}
} else {
output.error(`Invalid input: "${input}"`);
output.log(`Please enter a valid email address or team slug`);
return 2;
// Interactive mode
result = await prompt(params);
}
// The login function failed, so it returned an exit code

View File

@@ -1,5 +1,4 @@
import chalk from 'chalk';
import fetch from 'node-fetch';
import logo from '../util/output/logo';
// @ts-ignore
import { handleError } from '../util/error';
@@ -48,10 +47,9 @@ export default async function main(client: Client): Promise<number> {
return 2;
}
const { authConfig, config, apiUrl, output } = client;
const { token } = authConfig;
const { authConfig, config, output } = client;
if (!token) {
if (!authConfig.token) {
output.note(
`Not currently logged in, so ${getCommandName('logout')} did nothing`
);
@@ -59,6 +57,20 @@ export default async function main(client: Client): Promise<number> {
}
output.spinner('Logging out…', 200);
let exitCode = 0;
try {
await client.fetch(`/v3/user/tokens/current`, {
method: 'DELETE',
});
} catch (err) {
if (err.status === 403) {
output.debug('Token is invalid so it cannot be revoked');
} else if (err.status !== 200) {
output.debug(err?.message ?? '');
exitCode = 1;
}
}
delete config.currentTeam;
@@ -75,26 +87,15 @@ export default async function main(client: Client): Promise<number> {
writeToAuthConfigFile(authConfig);
output.debug('Configuration has been deleted');
} catch (err) {
output.error(`Couldn't remove config while logging out`);
return 1;
output.debug(err?.message ?? '');
exitCode = 1;
}
const res = await fetch(`${apiUrl}/v3/user/tokens/current`, {
method: 'DELETE',
headers: {
Authorization: `Bearer ${token}`,
},
});
if (res.status === 403) {
output.debug('Token is invalid so it cannot be revoked');
} else if (res.status !== 200) {
const err = await res.json();
output.error('Failed to revoke token');
output.debug(err ? err.message : '');
return 1;
if (exitCode === 0) {
output.log('Logged out!');
} else {
output.error(`Failed during logout`);
}
output.log('Logged out!');
return 0;
return exitCode;
}

View File

@@ -1,13 +1,13 @@
import chalk from 'chalk';
import Now from '../util';
import logo from '../util/output/logo';
import elapsed from '../util/output/elapsed.ts';
import elapsed from '../util/output/elapsed';
import { maybeURL, normalizeURL } from '../util/url';
import printEvents from '../util/events';
import getScope from '../util/get-scope.ts';
import { getPkgName } from '../util/pkg-name.ts';
import getArgs from '../util/get-args.ts';
import handleError from '../util/handle-error.ts';
import printEvents, { DeploymentEvent } from '../util/events';
import getScope from '../util/get-scope';
import { getPkgName } from '../util/pkg-name';
import getArgs from '../util/get-args';
import Client from '../util/client';
import { getDeployment } from '../util/get-deployment';
const help = () => {
console.log(`
@@ -53,35 +53,25 @@ const help = () => {
`);
};
export default async function main(client) {
let argv;
let deploymentIdOrURL;
let debug;
export default async function main(client: Client) {
let head;
let limit;
let follow;
let outputMode;
let since;
let until;
let deploymentIdOrURL;
try {
argv = getArgs(client.argv.slice(2), {
'--since': String,
'--until': String,
'--output': String,
'--limit': Number,
'--head': Boolean,
'--follow': Boolean,
'-f': '--follow',
'-o': '--output',
'-n': '--limit',
});
} catch (error) {
handleError(error);
return 1;
}
const argv = getArgs(client.argv.slice(2), {
'--since': String,
'--until': String,
'--output': String,
'--limit': Number,
'--head': Boolean,
'--follow': Boolean,
'-f': '--follow',
'-o': '--output',
'-n': '--limit',
});
argv._ = argv._.slice(1);
deploymentIdOrURL = argv._[0];
@@ -91,12 +81,7 @@ export default async function main(client) {
return 2;
}
const {
authConfig: { token },
apiUrl,
output,
config,
} = client;
const { output } = client;
try {
since = argv['--since'] ? toTimestamp(argv['--since']) : 0;
@@ -124,40 +109,24 @@ export default async function main(client) {
deploymentIdOrURL = normalizedURL;
}
debug = argv['--debug'];
head = argv['--head'];
limit = argv['--limit'] || 100;
follow = argv['--follow'];
if (follow) until = 0;
outputMode = argv['--output'] in logPrinters ? argv['--output'] : 'short';
const logPrinter = getLogPrinter(argv['--output'], 'short');
const { currentTeam } = config;
const now = new Now({ apiUrl, token, debug, currentTeam, output });
let contextName = null;
const { contextName } = await getScope(client);
try {
({ contextName } = await getScope(client));
} catch (err) {
if (err.code === 'NOT_AUTHORIZED' || err.code === 'TEAM_DELETED') {
output.error(err.message);
return 1;
}
throw err;
}
let deployment;
const id = deploymentIdOrURL;
const depFetchStart = Date.now();
output.spinner(`Fetching deployment "${id}" in ${chalk.bold(contextName)}`);
let deployment;
try {
deployment = await now.findDeployment(id);
deployment = await getDeployment(client, id);
} catch (err) {
output.stopSpinner();
now.close();
if (err.status === 404) {
output.error(
@@ -183,31 +152,28 @@ export default async function main(client) {
)} ${elapsed(Date.now() - depFetchStart)}`
);
let direction = head ? 'forward' : 'backward';
if (since && !until) direction = 'forward';
const findOpts1 = {
direction,
limit,
since,
until,
}; // no follow
const storage = [];
const storeEvent = event => storage.push(event);
const storage: DeploymentEvent[] = [];
await printEvents(now, deployment.uid || deployment.id, currentTeam, {
let direction = head ? ('forward' as const) : ('backward' as const);
if (since && !until) direction = 'forward';
await printEvents(client, deployment.id, {
mode: 'logs',
onEvent: storeEvent,
onEvent: event => storage.push(event),
quiet: false,
debug,
findOpts: findOpts1,
output,
findOpts: {
direction,
limit,
since,
until,
},
});
const printedEventIds = new Set();
const printEvent = event => {
const printedEventIds = new Set<string>();
const printEvent = (event: DeploymentEvent) => {
if (printedEventIds.has(event.id)) return 0;
printedEventIds.add(event.id);
return logPrinters[outputMode](event);
return logPrinter(event);
};
storage.sort(compareEvents).forEach(printEvent);
@@ -216,26 +182,22 @@ export default async function main(client) {
// NOTE: the API ignores `since` on follow mode.
// (but not sure if it's always true on legacy deployments)
const since2 = lastEvent ? lastEvent.date : Date.now();
const findOpts2 = {
direction: 'forward',
since: since2,
follow: true,
};
await printEvents(now, deployment.uid || deployment.id, currentTeam, {
await printEvents(client, deployment.id, {
mode: 'logs',
onEvent: printEvent,
quiet: false,
debug,
findOpts: findOpts2,
output,
findOpts: {
direction: 'forward',
since: since2,
follow: true,
},
});
}
now.close();
return 0;
}
function compareEvents(d1, d2) {
function compareEvents(d1: DeploymentEvent, d2: DeploymentEvent) {
const c1 = d1.date || d1.created;
const c2 = d2.date || d2.created;
if (c1 !== c2) return c1 - c2;
@@ -246,10 +208,10 @@ function compareEvents(d1, d2) {
return d1.created - d2.created; // if date are equal and no serial
}
function printLogShort(log) {
function printLogShort(log: any) {
if (!log.created) return; // keepalive
let data;
let data: string;
const obj = log.object;
if (log.type === 'request') {
data =
@@ -315,7 +277,7 @@ function printLogShort(log) {
return 0;
}
function printLogRaw(log) {
function printLogRaw(log: any) {
if (!log.created) return; // keepalive
if (log.object) {
@@ -340,7 +302,27 @@ const logPrinters = {
raw: printLogRaw,
};
function toTimestamp(datestr) {
type OutputMode = keyof typeof logPrinters;
const isLogPrinter = (v: any): v is OutputMode => {
return v && v in logPrinters;
};
const getLogPrinter = (mode: string | undefined, def: OutputMode) => {
if (mode) {
if (isLogPrinter(mode)) {
return logPrinters[mode];
}
throw new TypeError(
`Invalid output mode "${mode}". Must be one of: ${Object.keys(
logPrinters
).join(', ')}`
);
}
return logPrinters[def];
};
function toTimestamp(datestr: string) {
const t = Date.parse(datestr);
if (isNaN(t)) {
throw new TypeError('Invalid date string');

View File

@@ -1,14 +1,15 @@
import chalk from 'chalk';
import error from '../util/output/error';
import NowTeams from '../util/teams';
import logo from '../util/output/logo';
import list from './teams/list';
import add from './teams/add';
import change from './teams/switch';
import invite from './teams/invite';
import { getPkgName } from '../util/pkg-name.ts';
import getArgs from '../util/get-args.ts';
import handleError from '../util/handle-error.ts';
import error from '../../util/output/error';
import NowTeams from '../../util/teams';
import logo from '../../util/output/logo';
import list from './list';
import add from './add';
import change from './switch';
import invite from './invite';
import { getPkgName } from '../../util/pkg-name';
import getArgs from '../../util/get-args';
import handleError from '../../util/handle-error';
import Client from '../../util/client';
const help = () => {
console.log(`
@@ -65,7 +66,7 @@ let debug;
let apiUrl;
let subcommand;
const main = async client => {
export default async (client: Client) => {
try {
argv = getArgs(client.argv.slice(2), {
'--since': String,
@@ -113,7 +114,7 @@ const main = async client => {
}
case 'switch':
case 'change': {
exitCode = await change(client, argv);
exitCode = await change(client, argv._[0]);
break;
}
case 'add':
@@ -139,13 +140,3 @@ const main = async client => {
teams.close();
return exitCode || 0;
};
export default async client => {
try {
return await main(client);
} catch (err) {
console.error(err);
handleError(err);
return 1;
}
};

View File

@@ -1,190 +0,0 @@
// Packages
import chalk from 'chalk';
// Utilities
import listInput from '../../util/input/list';
import success from '../../util/output/success';
import info from '../../util/output/info';
import error from '../../util/output/error';
import param from '../../util/output/param.ts';
import { writeToConfigFile } from '../../util/config/files';
import getUser from '../../util/get-user.ts';
import NowTeams from '../../util/teams';
const updateCurrentTeam = (config, newTeam) => {
if (newTeam) {
config.currentTeam = newTeam.id;
} else {
delete config.currentTeam;
}
writeToConfigFile(config);
};
export default async function change(client, argv) {
const {
apiUrl,
authConfig: { token },
debug,
config,
output,
} = client;
output.spinner('Fetching teams');
// We're loading the teams here without `currentTeam`, so that
// people can use `vercel switch` in the case that their
// current team was deleted.
const teams = new NowTeams({ apiUrl, token, debug, output });
const list = (await teams.ls()).teams;
let { currentTeam } = config;
const accountIsCurrent = !currentTeam;
output.spinner('Fetching user information');
let user;
try {
user = await getUser(client);
} catch (err) {
if (err.code === 'NOT_AUTHORIZED' || err.code === 'TEAM_DELETED') {
output.error(err.message);
return 1;
}
throw err;
}
if (accountIsCurrent) {
currentTeam = {
slug: user.username || user.email,
};
} else {
currentTeam = list.find(team => team.id === currentTeam);
if (!currentTeam) {
output.error(`You are not a part of the current team anymore`);
return 1;
}
}
if (argv._.length !== 0) {
const desiredSlug = argv._[0];
const newTeam = list.find(team => team.slug === desiredSlug);
if (newTeam) {
updateCurrentTeam(config, newTeam);
console.log(
success(
`The team ${chalk.bold(newTeam.name)} (${
newTeam.slug
}) is now active!`
)
);
return 0;
}
if (desiredSlug === user.username) {
output.spinner('Saving');
updateCurrentTeam(config);
output.stopSpinner();
console.log(
success(`Your account (${chalk.bold(desiredSlug)}) is now active!`)
);
return 0;
}
console.error(
error(`Could not find membership for team ${param(desiredSlug)}`)
);
return 1;
}
const choices = list.map(({ id, slug, name }) => {
name = `${slug} (${name})`;
if (id === currentTeam.id) {
name += ` ${chalk.bold('(current)')}`;
}
return {
name,
value: slug,
short: slug,
};
});
const suffix = accountIsCurrent ? ` ${chalk.bold('(current)')}` : '';
const userEntryName = user.username
? `${user.username} (${user.email})${suffix}`
: user.email;
choices.unshift({
name: userEntryName,
value: user.email,
short: user.username,
});
// Let's bring the current team to the beginning of the list
if (!accountIsCurrent) {
const index = choices.findIndex(
choice => choice.value === currentTeam.slug
);
const choice = choices.splice(index, 1)[0];
choices.unshift(choice);
}
output.stopSpinner();
let message;
if (currentTeam) {
message = `Switch to:`;
}
const choice = await listInput({
message,
choices,
separator: false,
eraseFinalAnswer: true,
});
// Abort
if (!choice) {
console.log(info('No changes made'));
return 0;
}
const newTeam = list.find(item => item.slug === choice);
// Switch to account
if (!newTeam) {
if (currentTeam.slug === user.username || currentTeam.slug === user.email) {
console.log(info('No changes made'));
return 0;
}
output.spinner('Saving');
updateCurrentTeam(config);
output.stopSpinner();
console.log(success(`Your account (${chalk.bold(choice)}) is now active!`));
return 0;
}
if (newTeam.slug === currentTeam.slug) {
console.log(info('No changes made'));
return 0;
}
output.spinner('Saving');
updateCurrentTeam(config, newTeam);
output.stopSpinner();
console.log(
success(
`The team ${chalk.bold(newTeam.name)} (${newTeam.slug}) is now active!`
)
);
return 0;
}

View File

@@ -0,0 +1,123 @@
// Packages
import chalk from 'chalk';
// Utilities
import Client from '../../util/client';
import listInput from '../../util/input/list';
import getUser from '../../util/get-user';
import getTeams from '../../util/get-teams';
import { Team, GlobalConfig } from '../../types';
import { writeToConfigFile } from '../../util/config/files';
const updateCurrentTeam = (config: GlobalConfig, team?: Team) => {
if (team) {
config.currentTeam = team.id;
} else {
delete config.currentTeam;
}
writeToConfigFile(config);
};
export default async function main(client: Client, desiredSlug?: string) {
const { config, output } = client;
const personalScopeSelected = !config.currentTeam;
output.spinner('Fetching teams information');
const [user, teams] = await Promise.all([getUser(client), getTeams(client)]);
const currentTeam = personalScopeSelected
? undefined
: teams.find(team => team.id === config.currentTeam);
if (!personalScopeSelected && !currentTeam) {
output.error(`You are not a part of the current team anymore.`);
return 1;
}
if (!desiredSlug) {
const teamChoices = teams
.slice(0)
.sort((a, b) => {
return a.name.toLowerCase().localeCompare(b.name.toLowerCase());
})
.map(({ id, slug, name }) => {
let title = `${name} (${slug})`;
const selected = id === currentTeam?.id;
if (selected) {
title += ` ${chalk.bold('(current)')}`;
}
return {
name: title,
value: slug,
short: slug,
selected,
};
});
// Add the User scope entry at the top
const suffix = personalScopeSelected ? ` ${chalk.bold('(current)')}` : '';
const choices = [
{ separator: 'Personal Account' },
{
name: `${user.email} (${user.username})${suffix}`,
value: user.email,
short: user.username,
selected: personalScopeSelected,
},
{ separator: 'Teams' },
...teamChoices,
];
output.stopSpinner();
desiredSlug = await listInput({
message: 'Switch to:',
choices,
eraseFinalAnswer: true,
});
}
// Abort
if (!desiredSlug) {
output.log('No changes made.');
return 0;
}
if (desiredSlug === user.username || desiredSlug === user.email) {
// Switch to user's personal account
if (personalScopeSelected) {
output.log('No changes made');
return 0;
}
updateCurrentTeam(config);
output.success(`Your account (${chalk.bold(desiredSlug)}) is now active!`);
return 0;
}
// Switch to selected team
const newTeam = teams.find(team => team.slug === desiredSlug);
if (!newTeam) {
output.error(
`You do not have permission to access scope ${chalk.bold(desiredSlug)}.`
);
return 1;
}
if (newTeam.slug === currentTeam?.slug) {
output.log('No changes made');
return 0;
}
updateCurrentTeam(config, newTeam);
output.success(
`The team ${chalk.bold(newTeam.name)} (${newTeam.slug}) is now active!`
);
return 0;
}

View File

@@ -484,7 +484,7 @@ const main = async () => {
return 1;
}
client.authConfig = { token };
client.authConfig = { token, skipWrite: true };
// Don't use team from config if `--token` was set
if (client.config && client.config.currentTeam) {
@@ -587,10 +587,10 @@ const main = async () => {
const eventCategory = 'Exit Code';
try {
const start = new Date();
const start = Date.now();
const full = require(`./commands/${targetCommand}`).default;
exitCode = await full(client);
const end = new Date() - start;
const end = Date.now() - start;
if (shouldCollectMetrics) {
const category = 'Command Invocation';

View File

@@ -1,12 +1,9 @@
import { fileNameSymbol } from '@vercel/client';
export interface AuthConfig {
[fileNameSymbol]?: string;
token: string;
skipWrite?: boolean;
}
export interface GlobalConfig {
[fileNameSymbol]?: string;
currentTeam?: string;
collectMetrics?: boolean;
api?: string;

View File

@@ -3,7 +3,7 @@ import chalk from 'chalk';
import Client from '../client';
import { Output } from '../output';
import { User } from '../../types';
import { NowConfig } from '../dev/types';
import { VercelConfig } from '../dev/types';
import getDeploymentsByAppName from '../deploy/get-deployments-by-appname';
import getDeploymentByIdOrHost from '../deploy/get-deployment-by-id-or-host';
@@ -35,7 +35,7 @@ export async function getDeploymentForAlias(
localConfigPath: string | undefined,
user: User,
contextName: string,
localConfig: NowConfig
localConfig: VercelConfig
) {
output.spinner(`Fetching deployment to alias in ${chalk.bold(contextName)}`);

View File

@@ -1,14 +1,14 @@
import { URLSearchParams } from 'url';
import { EventEmitter } from 'events';
import { parse as parseUrl } from 'url';
import fetch, { RequestInit } from 'node-fetch';
import fetch, { RequestInit, Response } from 'node-fetch';
import retry, { RetryFunction, Options as RetryOptions } from 'async-retry';
import { Output } from './output/create-output';
import responseError from './response-error';
import ua from './ua';
import printIndications from './print-indications';
import { AuthConfig, GlobalConfig } from '../types';
import { NowConfig } from './dev/types';
import { VercelConfig } from './dev/types';
import doSsoLogin from './login/sso';
import { writeToAuthConfigFile } from './config/files';
@@ -28,7 +28,7 @@ export interface ClientOptions {
authConfig: AuthConfig;
output: Output;
config: GlobalConfig;
localConfig: NowConfig;
localConfig: VercelConfig;
}
export default class Client extends EventEmitter {
@@ -37,7 +37,7 @@ export default class Client extends EventEmitter {
authConfig: AuthConfig;
output: Output;
config: GlobalConfig;
localConfig: NowConfig;
localConfig: VercelConfig;
constructor(opts: ClientOptions) {
super();
@@ -103,10 +103,14 @@ export default class Client extends EventEmitter {
);
}
fetch(url: string, opts: { json: false }): Promise<Response>;
fetch<T>(url: string, opts?: FetchOptions): Promise<T>;
async fetch<T>(url: string, opts: FetchOptions = {}): Promise<T> {
return this.retry(async bail => {
const res = await this._fetch(url, opts);
printIndications(res);
if (!res.ok) {
const error = await responseError(res);
@@ -140,8 +144,6 @@ export default class Client extends EventEmitter {
return null;
}
printIndications(res);
return res.headers.get('content-type').includes('application/json')
? res.json()
: res;

View File

@@ -8,7 +8,7 @@ import getLocalPathConfig from './local-path';
import { NowError } from '../now-error';
import error from '../output/error';
import highlight from '../output/highlight';
import { NowConfig } from '../dev/types';
import { VercelConfig } from '../dev/types';
import { AuthConfig, GlobalConfig } from '../../types';
const VERCEL_DIR = getGlobalPathConfig();
@@ -16,19 +16,15 @@ const CONFIG_FILE_PATH = join(VERCEL_DIR, 'config.json');
const AUTH_CONFIG_FILE_PATH = join(VERCEL_DIR, 'auth.json');
// reads "global config" file atomically
export const readConfigFile = (fileName = CONFIG_FILE_PATH): GlobalConfig => {
const config = loadJSON.sync(fileName);
config[fileNameSymbol] = fileName;
export const readConfigFile = (): GlobalConfig => {
const config = loadJSON.sync(CONFIG_FILE_PATH);
return config;
};
// writes whatever's in `stuff` to "global config" file, atomically
export const writeToConfigFile = (stuff: GlobalConfig): void => {
const fileName = stuff[fileNameSymbol];
if (!fileName) return;
try {
return writeJSON.sync(fileName, stuff, { indent: 2 });
return writeJSON.sync(CONFIG_FILE_PATH, stuff, { indent: 2 });
} catch (err) {
if (err.code === 'EPERM') {
console.error(
@@ -55,21 +51,17 @@ export const writeToConfigFile = (stuff: GlobalConfig): void => {
};
// reads "auth config" file atomically
export const readAuthConfigFile = (
fileName = AUTH_CONFIG_FILE_PATH
): AuthConfig => {
const config = loadJSON.sync(fileName);
config[fileNameSymbol] = fileName;
export const readAuthConfigFile = (): AuthConfig => {
const config = loadJSON.sync(AUTH_CONFIG_FILE_PATH);
return config;
};
// writes whatever's in `stuff` to "auth config" file, atomically
export const writeToAuthConfigFile = (stuff: AuthConfig) => {
const fileName = stuff[fileNameSymbol];
if (!fileName) return;
export const writeToAuthConfigFile = (authConfig: AuthConfig) => {
if (authConfig.skipWrite) {
return;
}
try {
return writeJSON.sync(fileName, stuff, {
return writeJSON.sync(AUTH_CONFIG_FILE_PATH, authConfig, {
indent: 2,
mode: 0o600,
});
@@ -108,8 +100,8 @@ export function getAuthConfigFilePath() {
export function readLocalConfig(
prefix: string = process.cwd()
): NowConfig | null {
let config: NowConfig | null = null;
): VercelConfig | null {
let config: VercelConfig | null = null;
let target = '';
try {

View File

@@ -66,7 +66,7 @@ export const getDefaultAuthConfig = async existing => {
const config = {
_:
'This is your Now credentials file. DO NOT SHARE! More: https://vercel.com/docs/configuration#global',
'This is your Vercel credentials file. DO NOT SHARE! More: https://vercel.com/docs/configuration#global',
};
if (existing) {

View File

@@ -1,7 +1,7 @@
import { join } from 'path';
import { CantParseJSONFile } from '../errors-ts';
import readJSONFile from '../read-json-file';
import { NowConfig } from '../dev/types';
import { VercelConfig } from '../dev/types';
import getLocalConfigPath from './local-path';
export default async function readConfig(dir: string) {
@@ -13,7 +13,7 @@ export default async function readConfig(dir: string) {
}
if (result) {
return result as NowConfig;
return result as VercelConfig;
}
return null;

View File

@@ -9,7 +9,7 @@ import {
import { Output } from '../output';
// @ts-ignore
import Now from '../../util';
import { NowConfig } from '../dev/types';
import { VercelConfig } from '../dev/types';
import { Org } from '../../types';
import ua from '../ua';
import { linkFolderToProject } from '../projects/link';
@@ -43,7 +43,7 @@ export default async function processDeployment({
uploadStamp: () => string;
deployStamp: () => string;
quiet: boolean;
nowConfig?: NowConfig;
nowConfig?: VercelConfig;
force?: boolean;
withCache?: boolean;
org: Org;

View File

@@ -27,7 +27,7 @@ import { LambdaSizeExceededError } from '../errors-ts';
import DevServer from './server';
import { getBuilder } from './builder-cache';
import {
NowConfig,
VercelConfig,
BuildMatch,
BuildResult,
BuilderInputs,
@@ -96,7 +96,7 @@ async function createBuildProcess(
}
export async function executeBuild(
nowConfig: NowConfig,
nowConfig: VercelConfig,
devServer: DevServer,
files: BuilderInputs,
match: BuildMatch,
@@ -383,7 +383,7 @@ export async function executeBuild(
}
export async function getBuildMatches(
nowConfig: NowConfig,
nowConfig: VercelConfig,
cwd: string,
output: Output,
devServer: DevServer,

View File

@@ -4,7 +4,7 @@ import PCRE from 'pcre-to-regexp';
import isURL from './is-url';
import DevServer from './server';
import { NowConfig, HttpHeadersConfig, RouteResult } from './types';
import { VercelConfig, HttpHeadersConfig, RouteResult } from './types';
import { isHandler, Route, HandleValue } from '@vercel/routing-utils';
export function resolveRouteParameters(
@@ -50,7 +50,7 @@ export async function devRouter(
reqMethod?: string,
routes?: Route[],
devServer?: DevServer,
nowConfig?: NowConfig,
nowConfig?: VercelConfig,
previousHeaders?: HttpHeadersConfig,
missRoutes?: Route[],
phase?: HandleValue | null

View File

@@ -49,7 +49,7 @@ import sleep from '../sleep';
import { Output } from '../output';
import { relative } from '../path-helpers';
import { getDistTag } from '../get-dist-tag';
import getNowConfigPath from '../config/local-path';
import getVercelConfigPath from '../config/local-path';
import { MissingDotenvVarsError } from '../errors-ts';
import cliPkg from '../pkg';
import { getVercelDirectory } from '../projects/link';
@@ -73,7 +73,7 @@ import errorTemplate502 from './templates/error_502';
import redirectTemplate from './templates/redirect';
import {
NowConfig,
VercelConfig,
DevServerOptions,
BuildMatch,
BuildResult,
@@ -145,7 +145,7 @@ export default class DevServer {
private devServerPids: Set<number>;
private projectSettings?: ProjectSettings;
private getNowConfigPromise: Promise<NowConfig> | null;
private getVercelConfigPromise: Promise<VercelConfig> | null;
private blockingBuildsPromise: Promise<void> | null;
private updateBuildersPromise: Promise<void> | null;
private updateBuildersTimeout: NodeJS.Timeout | undefined;
@@ -181,7 +181,7 @@ export default class DevServer {
this.inProgressBuilds = new Map();
this.devCacheDir = join(getVercelDirectory(cwd), 'cache');
this.getNowConfigPromise = null;
this.getVercelConfigPromise = null;
this.blockingBuildsPromise = null;
this.updateBuildersPromise = null;
this.startPromise = null;
@@ -244,7 +244,7 @@ export default class DevServer {
}
}
const nowConfig = await this.getNowConfig();
const nowConfig = await this.getVercelConfig();
// Update the build matches in case an entrypoint was created or deleted
await this.updateBuildMatches(nowConfig);
@@ -375,7 +375,7 @@ export default class DevServer {
}
async updateBuildMatches(
nowConfig: NowConfig,
nowConfig: VercelConfig,
isInitial = false
): Promise<void> {
const fileList = this.resolveBuildFiles(this.files);
@@ -460,7 +460,7 @@ export default class DevServer {
}
async invalidateBuildMatches(
nowConfig: NowConfig,
nowConfig: VercelConfig,
updatedBuilders: string[]
): Promise<void> {
if (updatedBuilders.length === 0) {
@@ -516,25 +516,25 @@ export default class DevServer {
return {};
}
clearNowConfigPromise = () => {
this.getNowConfigPromise = null;
clearVercelConfigPromise = () => {
this.getVercelConfigPromise = null;
};
getNowConfig(): Promise<NowConfig> {
if (this.getNowConfigPromise) {
return this.getNowConfigPromise;
getVercelConfig(): Promise<VercelConfig> {
if (this.getVercelConfigPromise) {
return this.getVercelConfigPromise;
}
this.getNowConfigPromise = this._getNowConfig();
this.getVercelConfigPromise = this._getVercelConfig();
// Clean up the promise once it has resolved
const clear = this.clearNowConfigPromise;
this.getNowConfigPromise.finally(clear);
const clear = this.clearVercelConfigPromise;
this.getVercelConfigPromise.finally(clear);
return this.getNowConfigPromise;
return this.getVercelConfigPromise;
}
async _getNowConfig(): Promise<NowConfig> {
const configPath = getNowConfigPath(this.cwd);
async _getVercelConfig(): Promise<VercelConfig> {
const configPath = getVercelConfigPath(this.cwd);
const [
pkg = null,
@@ -543,10 +543,10 @@ export default class DevServer {
config = { version: 2, [fileNameSymbol]: 'vercel.json' },
] = await Promise.all([
this.readJsonFile<PackageJson>('package.json'),
this.readJsonFile<NowConfig>(configPath),
this.readJsonFile<VercelConfig>(configPath),
]);
await this.validateNowConfig(config);
await this.validateVercelConfig(config);
const { error: routeError, routes: maybeRoutes } = getTransformedRoutes({
nowConfig: config,
});
@@ -634,7 +634,7 @@ export default class DevServer {
config.builds.sort(sortBuilders);
}
await this.validateNowConfig(config);
await this.validateVercelConfig(config);
this.caseSensitive = hasNewRoutingProperties(config);
this.apiDir = detectApiDirectory(config.builds || []);
@@ -710,8 +710,8 @@ export default class DevServer {
}
async tryValidateOrExit(
config: NowConfig,
validate: (c: NowConfig) => string | null
config: VercelConfig,
validate: (c: VercelConfig) => string | null
): Promise<void> {
const message = validate(config);
@@ -721,7 +721,7 @@ export default class DevServer {
}
}
async validateNowConfig(config: NowConfig): Promise<void> {
async validateVercelConfig(config: VercelConfig): Promise<void> {
if (config.version === 1) {
this.output.error('Cannot run `version: 1` projects.');
await this.exit(1);
@@ -855,7 +855,7 @@ export default class DevServer {
.replace('[::]', 'localhost')
.replace('127.0.0.1', 'localhost');
const nowConfig = await this.getNowConfig();
const nowConfig = await this.getVercelConfig();
const devCommandPromise = this.runDevCommand();
const files = await getFiles(this.cwd, { output: this.output });
@@ -982,7 +982,7 @@ export default class DevServer {
if (devProcess) {
ops.push(
new Promise((resolve, reject) => {
new Promise<void>((resolve, reject) => {
devProcess.once('exit', () => resolve());
try {
process.kill(devProcess.pid);
@@ -1201,7 +1201,7 @@ export default class DevServer {
match: BuildMatch,
requestPath: string | null,
req: http.IncomingMessage | null,
nowConfig: NowConfig,
nowConfig: VercelConfig,
previousBuildResult?: BuildResult,
filesChanged?: string[],
filesRemoved?: string[]
@@ -1290,7 +1290,7 @@ export default class DevServer {
this.output.debug(`${chalk.bold(method)} ${req.url}`);
try {
const nowConfig = await this.getNowConfig();
const nowConfig = await this.getVercelConfig();
await this.serveProjectAsNowV2(req, res, nowRequestId, nowConfig);
} catch (err) {
console.error(err);
@@ -1339,7 +1339,7 @@ export default class DevServer {
req: http.IncomingMessage,
res: http.ServerResponse,
nowRequestId: string,
nowConfig: NowConfig,
nowConfig: VercelConfig,
routes: Route[] | undefined = nowConfig.routes,
callLevel: number = 0
) => {
@@ -1992,7 +1992,7 @@ export default class DevServer {
return true;
}
async hasFilesystem(dest: string, nowConfig: NowConfig): Promise<boolean> {
async hasFilesystem(dest: string, nowConfig: VercelConfig): Promise<boolean> {
if (
await findBuildMatch(
this.buildMatches,
@@ -2181,7 +2181,7 @@ async function findBuildMatch(
files: BuilderInputs,
requestPath: string,
devServer: DevServer,
nowConfig: NowConfig,
nowConfig: VercelConfig,
isFilesystem = false
): Promise<BuildMatch | null> {
requestPath = requestPath.replace(/^\//, '');
@@ -2219,7 +2219,7 @@ async function shouldServe(
files: BuilderInputs,
requestPath: string,
devServer: DevServer,
nowConfig: NowConfig,
nowConfig: VercelConfig,
isFilesystem = false
): Promise<boolean> {
const {
@@ -2284,7 +2284,7 @@ async function findMatchingRoute(
match: BuildMatch,
requestPath: string,
devServer: DevServer,
nowConfig: NowConfig
nowConfig: VercelConfig
): Promise<RouteResult | void> {
const reqUrl = `/${requestPath}`;
for (const buildResult of match.buildResults.values()) {
@@ -2305,7 +2305,7 @@ async function findMatchingRoute(
function findAsset(
match: BuildMatch,
requestPath: string,
nowConfig: NowConfig
nowConfig: VercelConfig
): { asset: BuilderOutput; assetKey: string } | void {
if (!match.buildOutput) {
return;
@@ -2397,7 +2397,7 @@ function filterFrontendBuilds(build: Builder) {
return !frontendRuntimeSet.has(name || '');
}
function hasNewRoutingProperties(nowConfig: NowConfig) {
function hasNewRoutingProperties(nowConfig: VercelConfig) {
return (
typeof nowConfig.cleanUrls !== undefined ||
typeof nowConfig.headers !== undefined ||

View File

@@ -14,12 +14,12 @@ import {
Lambda,
PackageJson,
} from '@vercel/build-utils';
import { NowConfig } from '@vercel/client';
import { VercelConfig } from '@vercel/client';
import { HandleValue, Route } from '@vercel/routing-utils';
import { Output } from '../output';
import { ProjectEnvVariable, ProjectSettings } from '../../types';
export { NowConfig };
export { VercelConfig };
export interface DevServerOptions {
output: Output;

View File

@@ -7,7 +7,7 @@ import {
rewritesSchema,
trailingSlashSchema,
} from '@vercel/routing-utils';
import { NowConfig } from './types';
import { VercelConfig } from './types';
import {
functionsSchema,
buildsSchema,
@@ -36,7 +36,7 @@ const vercelConfigSchema = {
const ajv = new Ajv();
const validate = ajv.compile(vercelConfigSchema);
export function validateConfig(config: NowConfig): NowBuildError | null {
export function validateConfig(config: VercelConfig): NowBuildError | null {
if (!validate(config)) {
if (validate.errors && validate.errors[0]) {
const error = validate.errors[0];

View File

@@ -1,28 +1,16 @@
export type EmojiLabel =
| 'notice'
| 'tip'
| 'warning'
| 'link'
| 'inspect'
| 'success';
export const emojiLabels = {
notice: '📝',
tip: '💡',
warning: '❗️',
link: '🔗',
inspect: '🔍',
success: '✅',
} as const;
export function emoji(label: EmojiLabel): string | undefined {
switch (label) {
case 'notice':
return '📝';
case 'tip':
return '💡';
case 'warning':
return '❗️';
case 'link':
return '🔗';
case 'inspect':
return '🔍';
case 'success':
return '✅';
default:
return undefined;
}
export type EmojiLabel = keyof typeof emojiLabels;
export function emoji(label: EmojiLabel) {
return emojiLabels[label];
}
export function prependEmoji(message: string, emoji?: string): string {

View File

@@ -2,52 +2,41 @@
import { URLSearchParams } from 'url';
// Packages
import retry from 'async-retry';
import jsonlines from 'jsonlines';
import { eraseLines } from 'ansi-escapes';
import jsonlines from 'jsonlines';
import retry from 'async-retry';
import Client from './client';
import { getDeployment } from './get-deployment';
export interface FindOpts {
direction: 'forward' | 'backward';
limit?: number;
since?: number;
until?: number;
follow?: boolean;
}
export interface PrintEventsOptions {
mode: string;
onEvent: (event: DeploymentEvent) => void;
quiet?: boolean;
findOpts: FindOpts;
}
export interface DeploymentEvent {
id: string;
created: number;
date?: number;
serial?: string;
}
async function printEvents(
now,
deploymentIdOrURL,
currentTeam = null,
{
mode,
onOpen = () => {},
onEvent,
quiet,
debugEnabled,
findOpts,
output,
} = {}
client: Client,
deploymentIdOrURL: string,
{ mode, onEvent, quiet, findOpts }: PrintEventsOptions
) {
const { log, debug } = output;
let onOpenCalled = false;
function callOnOpenOnce() {
if (onOpenCalled) return;
onOpenCalled = true;
onOpen();
}
const query = new URLSearchParams({
direction: findOpts.direction,
limit: findOpts.limit,
since: findOpts.since,
until: findOpts.until,
follow: findOpts.follow ? '1' : '',
format: 'lines',
});
let eventsUrl = `/v1/now/deployments/${deploymentIdOrURL}/events?${query}`;
let pollUrl = `/v3/now/deployments/${deploymentIdOrURL}`;
if (currentTeam) {
eventsUrl += `&teamId=${currentTeam.id}`;
pollUrl += `?teamId=${currentTeam.id}`;
}
debug(`Events ${eventsUrl}`);
const { log, debug } = client.output;
// we keep track of how much we log in case we
// drop the connection and have to start over
@@ -59,29 +48,34 @@ async function printEvents(
debug('Retrying events');
}
const eventsRes = await now._fetch(eventsUrl);
const query = new URLSearchParams({
direction: findOpts.direction,
follow: findOpts.follow ? '1' : '',
format: 'lines',
});
if (findOpts.limit) query.set('limit', String(findOpts.limit));
if (findOpts.since) query.set('since', String(findOpts.since));
if (findOpts.until) query.set('until', String(findOpts.until));
const eventsUrl = `/v1/now/deployments/${deploymentIdOrURL}/events?${query}`;
const eventsRes = await client.fetch(eventsUrl, { json: false });
if (eventsRes.ok) {
const readable = eventsRes.readable
? await eventsRes.readable()
: eventsRes.body;
const readable = eventsRes.body;
// handle the event stream and make the promise get rejected
// if errors occur so we can retry
return new Promise((resolve, reject) => {
return new Promise<void>((resolve, reject) => {
const stream = readable.pipe(jsonlines.parse());
let poller;
let poller: ReturnType<typeof setTimeout>;
if (mode === 'deploy') {
poller = (function startPoller() {
return setTimeout(async () => {
try {
const pollRes = await now._fetch(pollUrl);
if (!pollRes.ok)
throw new Error(`Response ${pollRes.status}`);
const json = await pollRes.json();
if (json.state === 'READY') {
const json = await getDeployment(client, deploymentIdOrURL);
if (json.readyState === 'READY') {
stream.end();
finish();
return;
@@ -96,10 +90,9 @@ async function printEvents(
}
let finishCalled = false;
function finish(error) {
function finish(error?: Error) {
if (finishCalled) return;
finishCalled = true;
callOnOpenOnce();
clearTimeout(poller);
if (error) {
reject(error);
@@ -110,26 +103,24 @@ async function printEvents(
let latestLogDate = 0;
const onData = data => {
const { event } = data;
if (event === 'state' && data.payload.value === 'READY') {
const onData = (data: any) => {
const { event, payload, date } = data;
if (event === 'state' && payload.value === 'READY') {
if (mode === 'deploy') {
stream.end();
finish();
}
} else {
latestLogDate = Math.max(latestLogDate, data.date);
const linesPrinted = onEvent(data, callOnOpenOnce);
o += linesPrinted || 0;
latestLogDate = Math.max(latestLogDate, date);
onEvent(data);
}
};
let onErrorCalled = false;
const onError = err => {
const onError = (err: Error) => {
if (finishCalled || onErrorCalled) return;
onErrorCalled = true;
o++;
callOnOpenOnce();
const errorMessage = `Deployment event stream error: ${err.message}`;
if (!findOpts.follow) {
@@ -140,7 +131,6 @@ async function printEvents(
debug(errorMessage);
clearTimeout(poller);
stream.destroy(err);
readable.destroy(err);
const retryFindOpts = {
...findOpts,
@@ -149,12 +139,10 @@ async function printEvents(
setTimeout(() => {
// retry without maximum amount nor clear past logs etc
printEvents(now, deploymentIdOrURL, currentTeam, {
printEvents(client, deploymentIdOrURL, {
mode,
onOpen,
onEvent,
quiet,
debugEnabled,
findOpts: retryFindOpts,
}).then(resolve, reject);
}, 2000);
@@ -166,7 +154,6 @@ async function printEvents(
readable.on('error', onError);
});
}
callOnOpenOnce();
const err = new Error(`Deployment events status ${eventsRes.status}`);
if (eventsRes.status < 500) {

View File

@@ -9,15 +9,15 @@ import {
import humanizePath from './humanize-path';
import readJSONFile from './read-json-file';
import readPackage from './read-package';
import { NowConfig } from './dev/types';
import { VercelConfig } from './dev/types';
import { Output } from './output';
let config: NowConfig;
let config: VercelConfig;
export default async function getConfig(
output: Output,
configFile?: string
): Promise<NowConfig | Error> {
): Promise<VercelConfig | Error> {
// If config was already read, just return it
if (config) {
return config;
@@ -44,7 +44,7 @@ export default async function getConfig(
return localConfig;
}
if (localConfig !== null) {
config = localConfig as NowConfig;
config = localConfig as VercelConfig;
config[fileNameSymbol] = configFile;
return config;
}
@@ -68,13 +68,13 @@ export default async function getConfig(
}
if (vercelConfig !== null) {
output.debug(`Found config in file "${vercelFilePath}"`);
config = vercelConfig as NowConfig;
config = vercelConfig as VercelConfig;
config[fileNameSymbol] = 'vercel.json';
return config;
}
if (nowConfig !== null) {
output.debug(`Found config in file "${nowFilePath}"`);
config = nowConfig as NowConfig;
config = nowConfig as VercelConfig;
config[fileNameSymbol] = 'now.json';
return config;
}
@@ -87,7 +87,7 @@ export default async function getConfig(
}
if (pkgConfig) {
output.debug(`Found config in package ${pkgFilePath}`);
config = pkgConfig as NowConfig;
config = pkgConfig as VercelConfig;
config[fileNameSymbol] = 'package.json';
return config;
}

View File

@@ -0,0 +1,27 @@
import { stringify } from 'querystring';
import { Deployment } from '@vercel/client';
import Client from './client';
export async function getDeployment(
client: Client,
hostOrId: string
): Promise<Deployment> {
let url = `/v13/deployments`;
if (hostOrId.includes('.')) {
let host = hostOrId.replace(/^https:\/\//i, '');
if (host.slice(-1) === '/') {
host = host.slice(0, -1);
}
url += `/get?${stringify({
url: host,
})}`;
} else {
url += `/${encodeURIComponent(hostOrId)}`;
}
const deployment = await client.fetch<Deployment>(url);
return deployment;
}

View File

@@ -1,8 +1,6 @@
import Client from './client';
import { APIError, InvalidToken } from './errors-ts';
import { Team } from '../types';
// @ts-ignore
import NowTeams from './teams.js';
import { APIError, InvalidToken } from './errors-ts';
let teams: Team[] | undefined;
@@ -10,15 +8,11 @@ export default async function getTeams(client: Client): Promise<Team[]> {
if (teams) return teams;
try {
// we're using NowTeams because `client.fetch` hangs on windows
const teamClient = new NowTeams({
apiUrl: client.apiUrl,
token: client.authConfig.token,
debug: client.output.isDebugEnabled(),
const body = await client.fetch<{ teams: Team[] }>('/v1/teams', {
useCurrentTeam: false,
});
teams = (await teamClient.ls()).teams;
return teams || [];
teams = body.teams || [];
return teams;
} catch (error) {
if (error instanceof APIError && error.status === 403) {
throw new InvalidToken();

View File

@@ -1,82 +0,0 @@
import inquirer from 'inquirer';
import stripAnsi from 'strip-ansi';
import eraseLines from '../output/erase-lines';
function getLength(string) {
let biggestLength = 0;
string.split('\n').map(str => {
str = stripAnsi(str);
if (str.length > biggestLength) {
biggestLength = str.length;
}
return undefined;
});
return biggestLength;
}
export default async function({
message = 'the question',
// eslint-disable-line no-unused-vars
choices = [
{
name: 'something\ndescription\ndetails\netc',
value: 'something unique',
short: 'generally the first line of `name`',
},
],
pageSize = 15, // Show 15 lines without scrolling (~4 credit cards)
separator = true, // Puts a blank separator between each choice
abort = 'end', // Wether the `abort` option will be at the `start` or the `end`,
eraseFinalAnswer = false, // If true, the line with the final answee that inquirer prints will be erased before returning
}) {
require('./patch-inquirer-legacy');
let biggestLength = 0;
choices = choices.map(choice => {
if (choice.name) {
const length = getLength(choice.name);
if (length > biggestLength) {
biggestLength = length;
}
return choice;
}
throw new Error('Invalid choice');
});
if (separator === true) {
choices = choices.reduce(
(prev, curr) => prev.concat(new inquirer.Separator(' '), curr),
[]
);
}
const abortSeparator = new inquirer.Separator('─'.repeat(biggestLength));
const _abort = {
name: 'Abort',
value: undefined,
};
if (abort === 'start') {
const blankSep = choices.shift();
choices.unshift(abortSeparator);
choices.unshift(_abort);
choices.unshift(blankSep);
} else {
choices.push(abortSeparator);
choices.push(_abort);
}
const nonce = Date.now();
const answer = await inquirer.prompt({
name: nonce,
type: 'list',
message,
choices,
pageSize,
});
if (eraseFinalAnswer === true) {
process.stdout.write(eraseLines(2));
}
return answer[nonce];
}

View File

@@ -0,0 +1,123 @@
import inquirer from 'inquirer';
import stripAnsi from 'strip-ansi';
import eraseLines from '../output/erase-lines';
interface ListEntry {
name: string;
value: string;
short: string;
selected?: boolean;
}
interface ListSeparator {
separator: string;
}
type ListChoice = ListEntry | ListSeparator | typeof inquirer.Separator;
interface ListOptions {
message: string;
choices: ListChoice[];
pageSize?: number;
separator?: boolean;
abort?: 'start' | 'end';
eraseFinalAnswer?: boolean;
}
function getLength(input: string): number {
let biggestLength = 0;
for (const line of input.split('\n')) {
const str = stripAnsi(line);
if (str.length > biggestLength) {
biggestLength = str.length;
}
}
return biggestLength;
}
export default async function list({
message = 'the question',
// eslint-disable-line no-unused-vars
choices: _choices = [
{
name: 'something\ndescription\ndetails\netc',
value: 'something unique',
short: 'generally the first line of `name`',
},
],
pageSize = 15, // Show 15 lines without scrolling (~4 credit cards)
separator = false, // Puts a blank separator between each choice
abort = 'end', // Whether the `abort` option will be at the `start` or the `end`,
eraseFinalAnswer = false, // If true, the line with the final answer that inquirer prints will be erased before returning
}: ListOptions): Promise<string> {
require('./patch-inquirer-legacy');
let biggestLength = 0;
let selected: string | undefined;
// First calculate the biggest length
for (const choice of _choices) {
if ('name' in choice) {
const length = getLength(choice.name);
if (length > biggestLength) {
biggestLength = length;
}
}
}
const choices = _choices.map(choice => {
if (choice instanceof inquirer.Separator) {
return choice;
}
if ('separator' in choice) {
const prefix = `── ${choice.separator} `;
const suffix = '─'.repeat(biggestLength - getLength(prefix));
return new inquirer.Separator(`${prefix}${suffix}`);
}
if ('short' in choice) {
if (choice.selected) {
if (selected) throw new Error('Only one choice may be selected');
selected = choice.short;
}
return choice;
}
throw new Error('Invalid choice');
});
if (separator) {
for (let i = 0; i < choices.length; i += 2) {
choices.splice(i, 0, new inquirer.Separator(' '));
}
}
const abortSeparator = new inquirer.Separator('─'.repeat(biggestLength));
const _abort = {
name: 'Abort',
value: '',
short: '',
};
if (abort === 'start') {
choices.unshift(_abort, abortSeparator);
} else {
choices.push(abortSeparator, _abort);
}
const answer = await inquirer.prompt({
name: 'value',
type: 'list',
default: selected,
message,
choices,
pageSize,
});
if (eraseFinalAnswer === true) {
process.stdout.write(eraseLines(2));
}
return answer.value;
}

View File

@@ -2,7 +2,7 @@ import { join, basename } from 'path';
import chalk from 'chalk';
import { remove } from 'fs-extra';
import { ProjectLinkResult, ProjectSettings } from '../../types';
import { NowConfig } from '../dev/types';
import { VercelConfig } from '../dev/types';
import {
getLinkedProject,
linkFolderToProject,
@@ -134,7 +134,7 @@ export default async function setupAndLink(
return { status: 'error', exitCode: 1 };
}
let localConfig: NowConfig = {};
let localConfig: VercelConfig = {};
if (client.localConfig && !(client.localConfig instanceof Error)) {
localConfig = client.localConfig;
}

View File

@@ -0,0 +1,13 @@
import { URL } from 'url';
import { LoginParams } from './types';
import doOauthLogin from './oauth';
export default function doBitbucketLogin(params: LoginParams) {
const url = new URL(
'/api/registration/bitbucket/connect',
// Can't use `apiUrl` here because this URL sets a
// cookie that the OAuth callback URL depends on
'https://vercel.com'
);
return doOauthLogin(url, 'Bitbucket', params);
}

View File

@@ -1,70 +1,18 @@
import ms from 'ms';
import { stringify as stringifyQuery } from 'querystring';
import fetch from 'node-fetch';
import sleep from '../sleep';
import ua from '../ua';
import error from '../output/error';
import highlight from '../output/highlight';
import eraseLines from '../output/erase-lines';
import verify from './verify';
import executeLogin from './login';
import { LoginParams } from './types';
async function verify(
email: string,
verificationToken: string,
{ apiUrl, output }: LoginParams
): Promise<string> {
const query = {
email,
token: verificationToken,
};
output.debug('GET /now/registration/verify');
let res;
try {
res = await fetch(
`${apiUrl}/now/registration/verify?${stringifyQuery(query)}`,
{
headers: { 'User-Agent': ua },
}
);
} catch (err) {
output.debug(`error fetching /now/registration/verify: ${err.stack}`);
throw new Error(
error(
`An unexpected error occurred while trying to verify your login: ${err.message}`
)
);
}
output.debug('parsing response from GET /now/registration/verify');
let body;
try {
body = await res.json();
} catch (err) {
output.debug(
`error parsing the response from /now/registration/verify: ${err.stack}`
);
throw new Error(
error(
`An unexpected error occurred while trying to verify your login: ${err.message}`
)
);
}
return body.token;
}
export default async function doEmailLogin(
email: string,
{ apiUrl, output }: LoginParams
params: LoginParams
): Promise<number | string> {
let securityCode;
let verificationToken;
const { apiUrl, output } = params;
output.spinner('Sending you an email');
@@ -91,22 +39,18 @@ export default async function doEmailLogin(
output.spinner('Waiting for your confirmation');
let token = '';
while (!token) {
try {
await sleep(ms('1s'));
token = await verify(email, verificationToken, { apiUrl, output });
token = await verify(email, verificationToken, params);
} catch (err) {
if (/invalid json response body/.test(err.message)) {
// /now/registraton is currently returning plain text in that case
// we just wait for the user to click on the link
} else {
if (err.message !== 'Confirmation incomplete') {
output.error(err.message);
return 1;
}
}
}
output.success('Email confirmed');
output.success(`Email authentication complete for ${email}`);
return token;
}

View File

@@ -0,0 +1,13 @@
import { URL } from 'url';
import { LoginParams } from './types';
import doOauthLogin from './oauth';
export default function doGithubLogin(params: LoginParams) {
const url = new URL(
'/api/registration/login-with-github',
// Can't use `apiUrl` here because this URL sets a
// cookie that the OAuth callback URL depends on
'https://vercel.com'
);
return doOauthLogin(url, 'GitHub', params);
}

View File

@@ -0,0 +1,10 @@
import { URL } from 'url';
import { LoginParams } from './types';
import doOauthLogin from './oauth';
export default function doGitlabLogin(params: LoginParams) {
// Can't use `apiUrl` here because this URL sets a
// cookie that the OAuth callback URL depends on
const url = new URL('/api/registration/gitlab/connect', 'https://vercel.com');
return doOauthLogin(url, 'GitLab', params);
}

View File

@@ -0,0 +1,117 @@
import http from 'http';
import open from 'open';
import { URL } from 'url';
import listen from 'async-listen';
import { hostname } from 'os';
import { LoginParams } from './types';
import prompt from './prompt';
import verify from './verify';
import { getTitleName } from '../pkg-name';
import highlight from '../output/highlight';
export default async function doOauthLogin(
url: URL,
provider: string,
params: LoginParams
): Promise<number | string> {
const { output } = params;
output.spinner(
`Please complete the ${provider} authentication in your web browser`
);
const server = http.createServer();
const address = await listen(server, 0, '127.0.0.1');
const { port } = new URL(address);
url.searchParams.append('mode', 'login');
url.searchParams.append('next', `http://localhost:${port}`);
// Append token name param
const hyphens = new RegExp('-', 'g');
const host = hostname().replace(hyphens, ' ').replace('.local', '');
const tokenName = `${getTitleName()} CLI on ${host} via ${provider}`;
url.searchParams.append('tokenName', tokenName);
try {
const [query] = await Promise.all([
new Promise<URL['searchParams']>((resolve, reject) => {
server.once('request', (req, res) => {
const query = new URL(req.url || '/', 'http://localhost')
.searchParams;
resolve(query);
// Redirect the user's web browser back to
// the Vercel CLI login notification page
const location = new URL(
'https://vercel.com/notifications/cli-login-'
);
const loginError = query.get('loginError');
const ssoEmail = query.get('ssoEmail');
if (loginError) {
location.pathname += 'failed';
location.searchParams.set('loginError', loginError);
} else if (ssoEmail) {
location.pathname += 'incomplete';
location.searchParams.set('ssoEmail', ssoEmail);
const teamName = query.get('teamName');
const ssoType = query.get('ssoType');
if (teamName) {
location.searchParams.set('teamName', teamName);
}
if (ssoType) {
location.searchParams.set('ssoType', ssoType);
}
} else {
location.pathname += 'success';
const email = query.get('email');
if (email) {
location.searchParams.set('email', email);
}
}
res.statusCode = 302;
res.setHeader('location', location.href);
res.end();
});
server.once('error', reject);
}),
open(url.href),
]);
const loginError = query.get('loginError');
if (loginError) {
const err = JSON.parse(loginError);
output.prettyError(err);
return 1;
}
// If an `ssoUserId` was returned, then the SAML Profile is not yet connected
// to a Team member. Prompt the user to log in to a Vercel account now, which
// will complete the connection to the SAML Profile.
const ssoUserId = query.get('ssoUserId');
if (ssoUserId) {
output.log(
'Please log in to your Vercel account to complete SAML connection.'
);
return prompt({ ...params, ssoUserId });
}
const email = query.get('email');
const verificationToken = query.get('token');
if (!email || !verificationToken) {
output.error(
'Verification token was not provided. Please contact support.'
);
return 1;
}
output.spinner('Verifying authentication token');
const token = await verify(email, verificationToken, params);
output.success(
`${provider} authentication complete for ${highlight(email)}`
);
return token;
} finally {
server.close();
}
}

View File

@@ -0,0 +1,77 @@
import inquirer from 'inquirer';
import error from '../output/error';
import listInput from '../input/list';
import { getCommandName } from '../pkg-name';
import { LoginParams } from './types';
import doSsoLogin from './sso';
import doEmailLogin from './email';
import doGithubLogin from './github';
import doGitlabLogin from './gitlab';
import doBitbucketLogin from './bitbucket';
export default async function prompt(params: LoginParams) {
let result: number | string = 1;
const choices = [
{ name: 'Continue with GitHub', value: 'github', short: 'github' },
{ name: 'Continue with GitLab', value: 'gitlab', short: 'gitlab' },
{ name: 'Continue with Bitbucket', value: 'bitbucket', short: 'bitbucket' },
{ name: 'Continue with Email', value: 'email', short: 'email' },
{ name: 'Continue with SAML Single Sign-On', value: 'saml', short: 'saml' },
];
if (params.ssoUserId) {
// Remove SAML login option if we're connecting SAML Profile
choices.pop();
}
const choice = await listInput({
message: 'Log in to Vercel',
choices,
});
if (choice === 'github') {
result = await doGithubLogin(params);
} else if (choice === 'gitlab') {
result = await doGitlabLogin(params);
} else if (choice === 'bitbucket') {
result = await doBitbucketLogin(params);
} else if (choice === 'email') {
const email = await readInput('Enter your email address');
result = await doEmailLogin(email, params);
} else if (choice === 'saml') {
const slug = await readInput('Enter your Team slug');
result = await doSsoLogin(slug, params);
}
return result;
}
async function readInput(message: string) {
let input;
while (!input) {
try {
const { val } = await inquirer.prompt({
type: 'input',
name: 'val',
message,
});
input = val;
} catch (err) {
console.log(); // \n
if (err.isTtyError) {
throw new Error(
error(
`Interactive mode not supported please run ${getCommandName(
`login you@domain.com`
)}`
)
);
}
}
}
return input;
}

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