Compare commits

...

65 Commits

Author SHA1 Message Date
Sean Massa
6700630feb Publish Stable
- @vercel/build-utils@5.3.2
 - vercel@28.1.4
 - @vercel/client@12.2.1
 - @vercel/go@2.2.2
 - @vercel/hydrogen@0.0.15
 - @vercel/next@3.1.21
 - @vercel/node@2.5.10
 - @vercel/python@3.1.11
 - @vercel/redwood@1.0.19
 - @vercel/remix@1.0.20
 - @vercel/ruby@1.3.28
 - @vercel/static-build@1.0.19
2022-08-24 09:09:02 -05:00
Sean Massa
34cd8b4144 [cli] revert git connect inside vc link (#8450) 2022-08-24 09:07:04 -05:00
Sean Massa
ad0ed6d852 Publish Stable
- vercel@28.1.3
 - @vercel/node@2.5.9
2022-08-23 16:58:19 -05:00
Gal Schlezinger
0bad09b47a [node] fix WebAssembly bindings evaluating to undefined (#8417)
This was broken since https://github.com/vercel/vercel/pull/8242, which accidentally pushed all WebAssembly modules to be nested under `global.wasmBindings` instead of being directly under the global scope.

### Related Issues

- This was broken since https://github.com/vercel/vercel/pull/8242

### 📋 Checklist

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

#### Tests

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

#### Code Review

- [ ] This PR has a concise title and thorough description useful to a reviewer
- [ ] Issue from task tracker has a link to this PR
2022-08-23 21:56:13 +00:00
Steven
5120689bf2 [tests] Fix update-next script (#8452)
Follow up to #8447 since exec returns a buffer, not a string.
2022-08-23 17:28:38 -04:00
Steven
5a39fd9242 [tests] Automatically update Next.js example periodically (#8447)
Automatically update Next.js example every 4 hours using `create-next-app`
2022-08-23 17:19:55 +00:00
Sean Massa
352cd00ef0 Publish Stable
- vercel@28.1.2
 - @vercel/go@2.2.1
2022-08-23 10:15:26 -05:00
Sean Massa
abfe817f86 [go] ignore bad go handlers and rename functions with more precision (#8422)
Co-authored-by: Steven <steven@ceriously.com>
2022-08-23 10:14:21 -05:00
Sean Massa
ebb5e2b208 Publish Stable
- vercel@28.1.1
 - @vercel/next@3.1.20
 - @vercel/python@3.1.10
2022-08-22 15:33:41 -05:00
JJ Kasper
e34858d082 [next] Add handling for app-paths-manifest (#8098)
This adds handling for the `app` outputs and adds initial tests to ensure it is working as expected. 

### Related Issues

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

### 📋 Checklist

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

#### Tests

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

#### Code Review

- [ ] This PR has a concise title and thorough description useful to a reviewer
- [ ] Issue from task tracker has a link to this PR
2022-08-22 14:58:21 -05:00
Ikko Ashimine
f03c947f91 [python] fix typo in install.ts comment (#8442)
overriden -> overridden
2022-08-22 11:58:27 -04:00
JJ Kasper
0d13fe7e34 [next] Ensure non-static pages/500 is handled (#8438)
Related Issues
Previously we were only checking for a non-static version of pages/404 although this can also be the case for pages/500 so this ensures we match that handling and add a test case to prevent regression.
Fixes: slack thread
📋 Checklist

Tests

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

Code Review

 This PR has a concise title and thorough description useful to a reviewer
 Issue from task tracker has a link to this PR
2022-08-22 10:13:16 -05:00
JJ Kasper
4afec9d373 [next] Update middleware data routes to fix caching (#8431)
Related Issues
This fixes caches for data routes with middleware due to search params not matching between the non-data variant and the data variant. Additional tests have been added to ensure this is working as expected.
Fixes: vercel/next.js#39595
📋 Checklist

Tests

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

Code Review

 This PR has a concise title and thorough description useful to a reviewer
 Issue from task tracker has a link to this PR
2022-08-22 09:52:24 -05:00
Sean Massa
09c85f63d2 Publish Stable
- vercel@28.1.0
 - @vercel/go@2.2.0
 - @vercel/next@3.1.19
2022-08-16 15:05:36 -05:00
Matthew Stanciu
9963965e9a [cli] Fix vc link not respecting --yes (#8412)
As of 28.0.0, `vc link` stopped respecting `--yes` and would wait for prompt if it found a Git repository to connect. This PR passes `autoConfirm` through to the Git prompt, so that `vc link` continues to respect `--yes`.

Here's what `vc link` looks like after this PR if a user has a local Git repository:

<img width="513" alt="Screen Shot 2022-08-16 at 10 05 56 AM" src="https://user-images.githubusercontent.com/14811170/184899917-c3dc0603-370d-4c86-afb8-19758fbc051a.png">

Not sure if we want to remove the log messages to be consistent with the rest of `vc link`?

### 📋 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
2022-08-16 19:16:54 +00:00
Matthew Stanciu
f3ed279007 [cli] Change mentions of "Aborted" to "Canceled" (#8411)
@leerob flagged that the language "Aborted" throughout the CLI should be changed to be more inclusive. This PR updates this throughout.

I didn't go as far as to update type names because I wanted to avoid potentially unintended consequences, but happy to also do that if we want.

### 📋 Checklist

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

#### Tests

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

#### Code Review

- [ ] This PR has a concise title and thorough description useful to a reviewer
- [ ] Issue from task tracker has a link to this PR
2022-08-16 18:47:52 +00:00
Sean Massa
4fe489edad Revert "Revert "[go] add lambda wrapper support to the go runtime"" (#8407)
Co-authored-by: Craig Andrews <craig.andrews@vercel.com>
Co-authored-by: Craig Andrews <44933829+craigandrews@users.noreply.github.com>
2022-08-16 11:54:44 -05:00
Matthew Stanciu
e2911aac0b [cli] Small vc ls wording changes (#8405)
I ran `vc ls` for a project and I spotted a few small details that I felt compelled to make better.

### 📋 Checklist

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

#### Tests

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

#### Code Review

- [ ] This PR has a concise title and thorough description useful to a reviewer
- [ ] Issue from task tracker has a link to this PR
2022-08-16 14:47:41 +00:00
JJ Kasper
f3cbc5d746 [next] Fix un-necessary edge routes being added (#8408)
Currently we are adding routes for edge function un-necessarily as static edge functions don't need routes added and dynamic edge functions already have their routes included in dynamicRoutes in the routes-manifest.
Related Issues
Fixes: slack thread
📋 Checklist

Tests

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

Code Review

 This PR has a concise title and thorough description useful to a reviewer
 Issue from task tracker has a link to this PR
2022-08-16 15:14:49 +01:00
Sean Massa
40df88b483 Revert "add test for main.go scenario"
This reverts commit 75c4f45b73.
2022-08-16 03:03:11 -05:00
Sean Massa
75c4f45b73 add test for main.go scenario 2022-08-16 03:01:12 -05:00
Sean Massa
05a236f944 Publish Stable
- vercel@28.0.2
 - @vercel/ruby@1.3.27
2022-08-16 02:49:19 -05:00
Sean Massa
4b7383f521 [cli] serialize getDeployment calls in vercel bisect (#8403) 2022-08-16 02:45:48 -05:00
Sean Massa
c263c31e48 [cli][tests] remove dupe inspectorUrl (#8400) 2022-08-15 22:14:29 -05:00
Sean Massa
c80530f9b1 [cli] fix: undefined variable (#8401)
The variable `chunks` doesn't exist in this context. Updated the error message to reference the `lines` count instead.
2022-08-15 22:43:04 +00:00
Sean Massa
16fd4396ef [tests] skip ruby test for now (#8404) 2022-08-15 17:30:02 -05:00
Nathan Rajlich
4e7138f400 [docs] Remove "watch" from DEVELOPING_A_RUNTIME.md (#8391)
Remove "watch" from `DEVELOPING_A_RUNTIME.md`

`vercel dev` no longer utilizes the `watch` field, so let's not document it here.
2022-08-13 15:57:25 -07:00
Steven
5273279cf1 Publish Stable
- vercel@28.0.1
 - @vercel/ruby@1.3.26
2022-08-12 22:19:42 -04:00
Steven
37290039d6 [ruby] Fix bundle install (#8392)
This PR is a follow up to #8388. The tests were not running as expected because only the "vendored" test used `2.7.x` which was never failing `bundle install` to begin with. I had to add `2.7.x` to the non-vendored test to get `bundle install` to fail. The fix is to rename `2.7.x` to `2.7.0` before running `bundle install`.
2022-08-12 21:59:50 -04:00
Steven
b52d01f809 Publish Stable
- @vercel/build-utils@5.3.1
 - vercel@28.0.0
 - @vercel/client@12.2.0
 - @vercel/go@2.1.1
 - @vercel/hydrogen@0.0.14
 - @vercel/next@3.1.18
 - @vercel/node@2.5.8
 - @vercel/python@3.1.9
 - @vercel/redwood@1.0.18
 - @vercel/remix@1.0.19
 - @vercel/ruby@1.3.25
 - @vercel/static-build@1.0.18
2022-08-12 16:31:35 -04:00
Matthew Stanciu
ffefaf82a1 [cli] Fix vc env pull delta changes bug (#8382)
#8170 shipped with a small bug: double quotes aren't stripped when pulling existing environment variables, but double quotes are stripped when reading `.env`, so if an environment variable uploaded to Vercel contained double quotes, it would always show as "Changed". This PR fixes the bug.
2022-08-12 15:56:47 -04:00
Steven
6d8dbfc7d6 [ruby] Handle exit code 18 gracefully (#8388)
This reverts #8381 and instead supports the "2.7.x" syntax.

- Related to https://github.com/vercel/customer-issues/issues/684
2022-08-12 15:56:13 -04:00
Steven
551cd7f688 [ruby] Fix ruby version error message (#8381)
This error message was incorrectly telling user to use the `2.7.x` value even though it will fail `bundle install` with the following error:

```
Your Ruby patchlevel is 0, but your Gemfile specified -1
Command failed with exit code 18 (EXDEV)
```

The exact version, `2.7.0`, is necessary.
2022-08-12 11:31:36 -04:00
Chris Barber
2dfb6b45cd [cli] MAJOR: Remove update command (#8369)
The `vc update` command is essentially a noop command that doesn't actually update the CLI. This command is not listed in the `--help` output, nor the CLI documentation. It should be safe to remove in the next major release.
2022-08-12 11:24:31 -04:00
Steven
65ae2a289e Revert "[go] add lambda wrapper support to the go runtime" (#8385)
Revert "[go] add lambda wrapper support to the go runtime (#8350)"

This reverts commit 4c230c8436.
2022-08-12 10:51:43 -04:00
Damien Simonin Feugas
72ea3532b1 chore: does not add /index when already present (#8373) 2022-08-12 10:10:05 +02:00
Sean Massa
78fac00823 [cli] improve vc build --help language (#8380)
Tweak the `--yes` help language for the `build` command to be more consistent with other flag descriptions.

---

Related to: https://github.com/vercel/vercel/pull/8330
2022-08-12 02:16:07 +00:00
Nathan Rajlich
9e255afa37 [cli] Support more granular file upload progress bar (#8371)
### Before

Previously, the file upload progress bar would only get updated upon completion of uploading an individual file. This would cause the the progress bar to appear "chunky" when uploading large files. This problem is exasperated with #8356, since there is only a single (potentially large) file that gets uploaded, which would cause the progress bar to not render at all, making the deployment process seem "stuck".

https://user-images.githubusercontent.com/71256/184241009-ba6d1e63-a0fe-462f-804b-e58f56ec310c.mov

### After

Now, the progress bar gets updated incrementally as individual files get uploaded which makes the progress bar updates smoother and play nicely with large files. This is done by "chunking" the individual file into slices and creating a Readable stream from those chunks.

https://user-images.githubusercontent.com/71256/184241050-51832996-d42b-46fd-9a71-d8a1f87a026c.mov
2022-08-12 01:18:58 +00:00
Matthew Stanciu
e4be68270f [cli] MAJOR: vc ls visual changes (#8151)
- Username field does not show if all deployment usernames in the page match the context name
- The `age` field shows first in the table
- A new `duration` field shows the time it took to deploy
- A new `--prod` flag filters for production deployments
- Table headers are colored cyan

### Before

<img width="1000" alt="Screen Shot 2022-07-13 at 6 29 15 PM" src="https://user-images.githubusercontent.com/14811170/178867816-5d252c94-86c0-46f4-82bf-a33341686aa6.png">

<img width="1081" alt="Screen Shot 2022-07-13 at 6 31 15 PM" src="https://user-images.githubusercontent.com/14811170/178867934-4fa570a1-e2ba-4d1f-8a93-b88b095c1dca.png">

### After

<img width="1002" alt="Screen Shot 2022-07-26 at 2 23 27 PM" src="https://user-images.githubusercontent.com/14811170/181114658-b1cc0f8d-18a7-4996-8635-a7868af50a19.png">

<img width="1077" alt="Screen Shot 2022-07-26 at 2 23 21 PM" src="https://user-images.githubusercontent.com/14811170/181114646-f614a235-f962-468e-a8e0-ca65f1a94b69.png">

### 📋 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
2022-08-12 00:15:17 +00:00
Matthew Stanciu
9c636dc1ba [cli] MAJOR: Remove vc billing command (#8377) 2022-08-11 16:29:49 -07:00
Matthew Stanciu
c98c9996bf [cli] MAJOR: Connect a Git provider repository in vc link (#8290)
#8100 added a new `vc git` command, which allows users to connect a Git provider repository, enabling them to set up a full Git workflow for their Vercel projects without having to leave the CLI.

This PR takes this functionality a step further by including it as part of the `vc link` flow. This way, users can set up a Vercel project and add a Git provider repository all in one step.

This PR is blocked by a PR to `front` which adds an option to the Git Settings page for a Project to re-enable the prompt if the user opted out (in review).

### 📋 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
2022-08-11 22:49:36 +00:00
Matthew Stanciu
0fcf172a10 [cli] vc project ls visual updates (#8157)
Related to #8102 & #8151. This PR updates the look of `vc project ls`:

- Header names are cyan
- A new "Latest Production URL" section

### Before

<img width="583" alt="Screen Shot 2022-08-11 at 10 46 03 AM" src="https://user-images.githubusercontent.com/14811170/184199554-5b298f2c-7d62-4200-a580-ddb16765c20e.png">

### After

<img width="816" alt="Screen Shot 2022-08-11 at 10 45 54 AM" src="https://user-images.githubusercontent.com/14811170/184199502-34295b2f-be7f-4289-b426-372c58f37eb6.png">

### 📋 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
2022-08-11 22:08:27 +00:00
Matthew Stanciu
99e5c4a6db [cli] Accept URL argument in vc git connect (#8351)
* Add functional but bad code

* Add docs

* Re-add thing just in case

* Fix some things

* Add a few tests

* Add test for multiple

* Add another test

* Small type fix

* Update packages/cli/src/commands/git/index.ts

Co-authored-by: Steven <steven@ceriously.com>

* Update packages/cli/src/commands/git/index.ts

Co-authored-by: Steven <steven@ceriously.com>

* Update packages/cli/src/commands/git/index.ts

Co-authored-by: Steven <steven@ceriously.com>

* Update packages/cli/test/unit/commands/git.test.ts

Co-authored-by: Steven <steven@ceriously.com>

* Update packages/cli/src/commands/git/index.ts

Co-authored-by: Sean Massa <EndangeredMassa@gmail.com>

* Fix typo

Co-authored-by: Sean Massa <EndangeredMassa@gmail.com>

* Update packages/cli/src/commands/git/connect.ts

Co-authored-by: Steven <steven@ceriously.com>

* Update packages/cli/src/commands/git/connect.ts

Co-authored-by: Steven <steven@ceriously.com>

* Update packages/cli/src/commands/git/connect.ts

Co-authored-by: Steven <steven@ceriously.com>

* Update packages/cli/src/commands/git/connect.ts

Co-authored-by: Steven <steven@ceriously.com>

* Update packages/cli/src/commands/git/connect.ts

Co-authored-by: Steven <steven@ceriously.com>

* return 0

* only return boolean in `promptConnectArg()`

* Remove err.meta check

* `parseRepoUrl()`: parse url without a scheme

* Remove todo

* Fix typo

* Small wording change

* Print each line instead of building buffer

* Make consistent quotes vs template literals

* Remove `multiple` variable

* Remove else

* Accept objects & rename some variables

* Move `connect-git-provider` to `git` folder

* Don't pass in `output`

* Fix small thing

* Add another test

Co-authored-by: Steven <steven@ceriously.com>
Co-authored-by: Sean Massa <EndangeredMassa@gmail.com>
Co-authored-by: Chris Barber <chris.barber@vercel.com>
2022-08-11 14:28:58 -07:00
Nathan Rajlich
b8269b0111 [build-utils] Remove as cast in glob() (#8376)
This is just a small TypeScript cleanup to remove an `as Stats` cast for the "statsCache".

Also drops the `FsFiles` interface in favor of just using the built-in `Record` utility type.
2022-08-11 21:08:53 +00:00
Sean Massa
decac0fe3f Revert "[remix] add image optimization configuration" (#8375)
Reverts vercel/vercel#8217

The previous PR had unresolved comments, but the kodiak bot automerged it. Let's revert for now, then we can continue to iterate on code in a new PR.
2022-08-11 19:20:20 +00:00
Mosaad
591d1686d0 [remix] add image optimization configuration (#8217)
### Feature
Makes it possible to use [Vercel's image optimization](https://vercel.com/docs/build-output-api/v3#build-output-configuration/supported-properties/images).

### Demo
This example is built locally with this patch:
https://github.com/theMosaad/remix-vercel-image
2022-08-11 18:30:21 +00:00
Matthew Stanciu
5e1d5c921c [client] Fix archive type on VercelClientOptions (#8367)
Fix `archive` type on `VercelClientOptions`
2022-08-10 13:44:20 -07:00
Matthew Stanciu
603b1256c6 [client] Send single source.tgz archive when creating deployment (#8356)
Right now, deployments via Vercel CLI are created by uploading every source file individually. This PR creates a tarball of the user's source files and sends the packed tarball instead. This speeds up deployment times, especially for large projects, and also bypasses the 15,000 file upload limit.

Currently, it requires a `--archive=tgz` flag. Perhaps we can enable this by default for prebuilt, but that may be for a separate PR.

### 📋 Checklist

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

#### Tests

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

#### Code Review

- [ ] This PR has a concise title and thorough description useful to a reviewer
- [ ] Issue from task tracker has a link to this PR
2022-08-10 19:35:25 +00:00
Nathan Rajlich
6957c72828 [client] Remove extraneous file read during deployment upload (#8366)
The `file` object already contains the contents as a Buffer, so there's no need to read the files again.

Extracted from #8356.
2022-08-10 12:09:04 -07:00
Steven
9be3650cb7 [tests] Use yarn --frozen-lockfile (#8363)
This PR updates CI to use `yarn install --frozen-lockfile` and also updates some github actions to v3.
2022-08-10 15:55:38 +00:00
Steven
6e1ee7a7d6 [cli] Fix dev query string merging (#8183)
Since `vercel dev` performs query string merging with `vercel.json` routes and the requested url, there is sometimes a slight difference between the request that the browser sends and the request that the upstream dev server receives.

Most servers treat the query string key with "empty" value the same as undefined value. Meaning, these two URLs should be equivalent because each has empty values:

- `http://example.com/src/App.vue?vue&type=style&index=0&lang.css`
- `http://example.com/src/App.vue?vue=&type=style&index=0&lang.css=`

However, `vite dev` handles these two URLs differently because of [string comparisons](2289d04af5/packages/plugin-vue/src/handleHotUpdate.ts (L98-L99)) instead of URL parsing, which causes requests from `vercel dev` to fail. Thus, this PR changes from Node.js native URL parsing to a custom implementation in order to handle this corner case and preserve the difference between `?a=` and `?a`.

Ideally this would be [fixed in vite](https://github.com/vitejs/vite/pull/9589), however we might run into other dev servers that also fail in the same way in the future.

- Fixes https://github.com/vercel/vercel/issues/7283
- Closes https://github.com/vitejs/vite/pull/9589
- Related to https://github.com/nodejs/node/issues/9500
2022-08-10 11:34:29 -04:00
Damien Simonin Feugas
767ce2cff1 [node] adds /index to / middleware matchers (#8349) 2022-08-10 15:17:15 +02:00
Craig Andrews
bb1d0ce1b7 chore(docs) Add details of how to enable lambda runtime wrappers to custom runtimes (#8360)
### 📋 Checklist

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

#### Tests

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

#### Code Review

- [ ] This PR has a concise title and thorough description useful to a reviewer
- [ ] Issue from task tracker has a link to this PR
2022-08-09 21:54:25 +00:00
Sean Massa
31f79c7de1 Publish Stable
- vercel@27.4.0
 - @vercel/go@2.1.0
 - @vercel/next@3.1.17
2022-08-09 16:11:57 -05:00
Craig Andrews
4c230c8436 [go] add lambda wrapper support to the go runtime (#8350)
Co-authored-by: Sean Massa <EndangeredMassa@gmail.com>
2022-08-09 16:08:20 -05:00
Lee Robinson
7941f5a104 [examples] Update Astro template for 1.0. (#8357)
To prepare for [Astro 1.0](https://github.com/withastro/astro/releases/tag/astro%401.0.0-rc.1), this PR updates the template (it has [been deployed](https://astro-template.vercel.app/)).
2022-08-09 18:09:38 +00:00
JJ Kasper
5b931afbf3 [next] Ensure $$ in ISR page is handled correctly (#8359)
* Ensure $$ in ISR page is handled correctly

* update test

* Remove duplicate tests

* add comment

* tweak
2022-08-09 11:13:15 -05:00
Chris Barber
15080364b8 [cli] Standardize on --yes instead of --confirm (#8330)
Allow for `--yes` flag consistently throughout CLI commands.

Also adds a deprecation warning for usage of `--confirm`,
since `--yes` is now preferred.
2022-08-08 18:05:22 -07:00
Steven
47e3381c6d Publish Stable
- @vercel/build-utils@5.3.0
 - vercel@27.3.8
 - @vercel/client@12.1.11
 - @vercel/go@2.0.16
 - @vercel/hydrogen@0.0.13
 - @vercel/next@3.1.16
 - @vercel/node@2.5.7
 - @vercel/python@3.1.8
 - @vercel/redwood@1.0.17
 - @vercel/remix@1.0.18
 - @vercel/ruby@1.3.24
 - @vercel/static-build@1.0.17
2022-08-08 19:11:12 -04:00
Sean Massa
33aefdc029 [go] fix function name conflict (#8299)
Co-authored-by: Steven <steven@ceriously.com>
2022-08-08 17:07:02 -05:00
Steven
30fe76a0cf [build-utils] Push back nodejs12.x sunset date (#8355)
This PR pushes back the `nodejs12.x` discontinue date to give more projects a chance to upgrade
2022-08-08 20:46:04 +00:00
Thomas Knickman
97ef88dc28 chore(docs): use relative links for README (#8354)
This allows README links to work locally (within your IDE) without kicking to a browser - and maintains the current functionality elsewhere.
2022-08-08 20:04:09 +00:00
Nathan Rajlich
f679098d7a [cli] Fix "retrieve" typo (#8352) 2022-08-08 19:22:00 +00:00
Steven
2b57e12ad3 [build-utils] Refactor framework prefixed env vars into shared function (#8166)
This consolidates the logic to get the framework-specific prefixed System Environment Variables into a single shared function so each builder can reuse the same function.

- Related to #7009 
- Related to #8306

In the future, this feature could be added to any other missing builders as well as `vc dev` but we'll save that for a new PR.
2022-08-05 00:51:52 +00:00
Nathan Rajlich
c4e94ad03f [cli] Always set BROWSER=none env var for frontend dev server in vc dev (#8326)
Previously `vc dev` would only set this env var for "create-react-app" Framework preset, but other frameworks do the "auto-open browser window" behavior as well, and respect this env var (Docusaurus, specifically).

So always set the `BROWSER=none` env var regardless of which framework preset is selected. Also simplifies the code since this was the only place `frameworkSlug` property was being used.
2022-08-04 21:49:14 +00:00
255 changed files with 5196 additions and 2702 deletions

25
.github/workflows/cron-update-next.yml vendored Normal file
View File

@@ -0,0 +1,25 @@
name: Cron Update Next
on:
# Run every 4 hours https://crontab.guru/every-4-hours
schedule:
- cron: '0 */4 * * *'
jobs:
create-pull-request:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
# 0 means fetch all commits so we can commit and push in the script below
with:
fetch-depth: 0
- name: Create Pull Request
uses: actions/github-script@v6
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
# See https://github.com/actions/github-script#run-a-separate-file-with-an-async-function
with:
script: |
const script = require('./utils/update-next.js')
await script({ github, context })

View File

@@ -26,12 +26,12 @@ jobs:
fi
- name: Setup Go
if: ${{ steps.check-release.outputs.IS_RELEASE == 'true' }}
uses: actions/setup-go@v2
uses: actions/setup-go@v3
with:
go-version: '1.13.15'
- name: Setup Node
if: ${{ steps.check-release.outputs.IS_RELEASE == 'true' }}
uses: actions/setup-node@v2
uses: actions/setup-node@v3
with:
node-version: 14
- name: Install

View File

@@ -25,20 +25,20 @@ jobs:
echo "TURBO_REMOTE_ONLY=true" >> $GITHUB_ENV
echo "TURBO_TEAM=vercel" >> $GITHUB_ENV
echo "TURBO_TOKEN=${{ secrets.TURBO_TOKEN }}" >> $GITHUB_ENV
- uses: actions/setup-go@v2
- uses: actions/setup-go@v3
with:
go-version: '1.13.15'
- uses: actions/setup-node@v2
- uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node }}
- uses: actions/checkout@v2
- uses: actions/checkout@v3
with:
fetch-depth: 100
- run: git --version
- run: git fetch origin main --depth=100
- run: git fetch origin ${{ github.ref }} --depth=100
- run: git diff origin/main...HEAD --name-only
- run: yarn install --network-timeout 1000000
- run: yarn install --network-timeout 1000000 --frozen-lockfile
- run: yarn run build
- run: yarn test-integration-cli
env:

View File

@@ -25,20 +25,20 @@ jobs:
echo "TURBO_REMOTE_ONLY=true" >> $GITHUB_ENV
echo "TURBO_TEAM=vercel" >> $GITHUB_ENV
echo "TURBO_TOKEN=${{ secrets.TURBO_TOKEN }}" >> $GITHUB_ENV
- uses: actions/setup-go@v2
- uses: actions/setup-go@v3
with:
go-version: '1.13.15'
- uses: actions/setup-node@v2
- uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node }}
- uses: actions/checkout@v2
- uses: actions/checkout@v3
with:
fetch-depth: 100
- run: git --version
- run: git fetch origin main --depth=100
- run: git fetch origin ${{ github.ref }} --depth=100
- run: git diff origin/main...HEAD --name-only
- run: yarn install --network-timeout 1000000
- run: yarn install --network-timeout 1000000 --frozen-lockfile
- run: yarn run build
- run: yarn run lint
if: matrix.os == 'ubuntu-latest' && matrix.node == 14 # only run lint once

View File

@@ -19,14 +19,14 @@ jobs:
tests: ${{ steps['set-tests'].outputs['tests'] }}
dplUrl: ${{ steps.waitForTarball.outputs.url }}
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
- run: git --version
- run: git fetch origin main
- uses: actions/setup-node@v2
- uses: actions/setup-node@v3
with:
node-version: ${{ env.NODE_VERSION }}
cache: 'yarn'
- run: yarn install --network-timeout 1000000
- run: yarn install --network-timeout 1000000 --frozen-lockfile
- id: set-tests
run: |
TESTS_ARRAY=$(node utils/chunk-tests.js $SCRIPT_NAME)
@@ -58,13 +58,13 @@ jobs:
echo "TURBO_REMOTE_ONLY=true" >> $GITHUB_ENV
echo "TURBO_TEAM=vercel" >> $GITHUB_ENV
echo "TURBO_TOKEN=${{ secrets.TURBO_TOKEN }}" >> $GITHUB_ENV
- uses: actions/checkout@v2
- uses: actions/checkout@v3
with:
fetch-depth: 2
- uses: actions/setup-go@v2
- uses: actions/setup-go@v3
with:
go-version: '1.13.15'
- uses: actions/setup-node@v2
- uses: actions/setup-node@v3
with:
node-version: ${{ env.NODE_VERSION }}
cache: 'yarn'

View File

@@ -63,9 +63,6 @@ export async function build(options: BuildOptions) {
const lambda = createLambda(/* … */);
return {
output: lambda,
watch: [
// Dependent files to trigger a rebuild in `vercel dev` go here…
],
routes: [
// If your Runtime needs to define additional routing, define it here…
],
@@ -115,7 +112,8 @@ export async function shouldServe(options: ShouldServeOptions) {
}
```
If this function is not defined, Vercel CLI will use the [default implementation](https://github.com/vercel/vercel/blob/52994bfe26c5f4f179bdb49783ee57ce19334631/packages/now-build-utils/src/should-serve.ts).
If this function is not defined, Vercel CLI will use the [default
implementation](https://github.com/vercel/vercel/blob/52994bfe26c5f4f179bdb49783ee57ce19334631/packages/now-build-utils/src/should-serve.ts).
### `startDevServer()`
@@ -189,7 +187,8 @@ If you need to share state between those steps, use the filesystem.
### Directory and Cache Lifecycle
When a new build is created, we pre-populate the `workPath` supplied to `analyze` with the results of the `prepareCache` step of the previous build.
When a new build is created, we pre-populate the `workPath` supplied to `analyze` with the results of the `prepareCache` step of the
previous build.
The `analyze` step can modify that directory, and it will not be re-created when it's supplied to `build` and `prepareCache`.
@@ -197,6 +196,77 @@ The `analyze` step can modify that directory, and it will not be re-created when
The env and secrets specified by the user as `build.env` are passed to the Runtime process. This means you can access user env via `process.env` in Node.js.
### Supporting Large Environment
We provide the ability to support more than 4KB of environment (up to 64KB) by way of
a Lambda runtime wrapper that is added to every Lambda function we create. These are
supported by many of the existing Lambda runtimes, but custom runtimes may require
additional work.
The following Lambda runtime families have built-in support for the runtime wrapper:
- `nodejs`
- `python` (>= 3.8)
- `ruby`
- `java11`
- `java8.al2` (not `java8`)
- `dotnetcore`
If a custom runtime is based on one of these Lambda runtimes, large environment
support will be available without further configuration. Custom runtimes based on
other Lambda runtimes, including those that provide the runtime via `provided` and
`provided.al2`, must implement runtime wrapper support and indicate it via the
`supportsWrapper` flag when calling [`createLambda`](#createlambda()).
To add support for runtime wrappers to a custom runtime, first check the value of the
`AWS_LAMBDA_EXEC_WRAPPER` environment variable in the bootstrap script. Its value is
the path to the wrapper executable.
The wrapper must be passed the path to the runtime as well as any parameters that the
runtime requires. This is most easily done in a small `bootstrap` script.
In this simple `bash` example, the runtime is called directly if
`AWS_LAMBDA_EXEC_WRAPPER` has no value, otherwise the wrapper is called with the
runtime command as parameters.
```bash
#!/bin/bash
exec $AWS_LAMBDA_EXEC_WRAPPER path/to/runtime param1 param2
```
If the `bootstrap` file is not a launcher script, but the entrypoint of the runtime
itself, replace the bootstrap process with the wrapper. Pass the path and parameters
of the executing file, ensuring the `AWS_LAMBDA_EXEC_WRAPPER` environment variable is
set to blank.
This `bash` example uses `exec` to replace the running bootstrap process with the
wrapper, passing its own path and parameters.
```bash
#!/bin/bash
if [[ -n $AWS_LAMBDA_EXEC_WRAPPER ]]
__WRAPPER=$AWS_LAMBDA_EXEC_WRAPPER
AWS_LAMBDA_EXEC_WRAPPER=""
exec $__WRAPPER "$0" "${@}"
fi
# start the actual runtime functionality
```
Note that unsetting the variable may not have the desired effect due to the way
Lambda spawns runtime processes. It is better to explicitly set it to blank.
The best way to replace the existing bootstrap process is with the
[`execve`](https://www.man7.org/linux/man-pages/man2/execve.2.html) syscall.
This is achieved by using `exec` in `bash` to replace the running process with the wrapper,
maintaining the same PID and environment.
Once support for runtime wrappers is included, ensure `supportsWrapper` is set to
`true` in the call to [`createLambda`](#createlambda()). This will inform the build
process to enable large environment support for this runtime.
### Utilities as peerDependencies
When you publish your Runtime to npm, make sure to not specify `@vercel/build-utils` (as seen below in the API definitions) as a dependency, but rather as part of `peerDependencies`.
@@ -304,6 +374,7 @@ This is a [class](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Refere
- `handler: String` path to handler file and (optionally) a function name it exports
- `runtime: LambdaRuntime` the name of the lambda runtime
- `environment: Object` key-value map of handler-related (aside of those passed by user) environment variables
- `supportsWrapper: Boolean` set to true to indicate that Lambda runtime wrappers are supported by this runtime
### `LambdaRuntime`

View File

@@ -35,6 +35,6 @@ For details on how to use Vercel, check out our [documentation](https://vercel.c
## Contributing
- [Code of Conduct](https://github.com/vercel/vercel/blob/main/.github/CODE_OF_CONDUCT.md)
- [Contributing Guidelines](https://github.com/vercel/vercel/blob/main/.github/CONTRIBUTING.md)
- [MIT License](https://github.com/vercel/vercel/blob/main/LICENSE)
- [Code of Conduct](./.github/CODE_OF_CONDUCT.md)
- [Contributing Guidelines](./.github/CONTRIBUTING.md)
- [MIT License](./LICENSE)

View File

@@ -18,3 +18,4 @@ pnpm-debug.log*
# macOS-specific files
.DS_Store
.vercel

View File

@@ -1 +0,0 @@
README.md

View File

@@ -1,10 +1,16 @@
# Welcome to [Astro](https://astro.build)
# Astro
[![Open in StackBlitz](https://developer.stackblitz.com/img/open_in_stackblitz.svg)](https://stackblitz.com/github/withastro/astro/tree/latest/examples/starter)
This directory is a brief example of an [Astro](https://astro.build/) site that can be deployed to Vercel with zero configuration.
> 🧑‍🚀 **Seasoned astronaut?** Delete this file. Have fun!
## Deploy Your Own
## 🚀 Project Structure
Deploy your own Astro project with Vercel.
[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?repository-url=https://github.com/vercel/vercel/tree/main/examples/astro&template=astro)
_Live Example: https://astro-template.vercel.app_
## Project Structure
Inside of your Astro project, you'll see the following folders and files:
@@ -26,17 +32,15 @@ There's nothing special about `src/components/`, but that's where we like to put
Any static assets, like images, can be placed in the `public/` directory.
## 🧞 Commands
## Commands
All commands are run from the root of the project, from a terminal:
| Command | Action |
| :---------------- | :------------------------------------------- |
| `npm install` | Installs dependencies |
| `npm run dev` | Starts local dev server at `localhost:3000` |
| `npm run build` | Build your production site to `./dist/` |
| `npm run preview` | Preview your build locally, before deploying |
## 👀 Want to learn more?
Feel free to check [our documentation](https://github.com/withastro/astro) or jump into our [Discord server](https://astro.build/chat).
| Command | Action |
| :--------------------- | :------------------------------------------------- |
| `npm install` | Installs dependencies |
| `npm run dev` | Starts local dev server at `localhost:3000` |
| `npm run build` | Build your production site to `./dist/` |
| `npm run preview` | Preview your build locally, before deploying |
| `npm run astro ...` | Run CLI commands like `astro add`, `astro preview` |
| `npm run astro --help` | Get help using the Astro CLI |

View File

@@ -1,14 +1,13 @@
{
"name": "@example/basics",
"version": "0.0.1",
"private": true,
"scripts": {
"dev": "astro dev",
"start": "astro dev",
"build": "astro build",
"preview": "astro preview"
"preview": "astro preview",
"astro": "astro"
},
"devDependencies": {
"astro": "^1.0.0-beta.20"
"astro": "^1.0.0-rc.8"
}
}

View File

@@ -0,0 +1,76 @@
---
export interface Props {
title: string;
body: string;
href: string;
}
const { href, title, body } = Astro.props as Props;
---
<li class="link-card">
<a href={href}>
<h2>
{title}
<span>&rarr;</span>
</h2>
<p>
{body}
</p>
</a>
</li>
<style>
:root {
--link-gradient: linear-gradient(45deg, #4f39fa, #da62c4 30%, var(--color-border) 60%);
}
.link-card {
list-style: none;
display: flex;
padding: 0.15rem;
background-image: var(--link-gradient);
background-size: 400%;
border-radius: 0.5rem;
background-position: 100%;
transition: background-position 0.6s cubic-bezier(0.22, 1, 0.36, 1);
}
.link-card > a {
width: 100%;
text-decoration: none;
line-height: 1.4;
padding: 1em 1.3em;
border-radius: 0.35rem;
color: var(--text-color);
background-color: white;
opacity: 0.8;
}
h2 {
margin: 0;
transition: color 0.6s cubic-bezier(0.22, 1, 0.36, 1);
}
p {
margin-top: 0.75rem;
margin-bottom: 0;
}
h2 span {
display: inline-block;
transition: transform 0.3s cubic-bezier(0.22, 1, 0.36, 1);
}
.link-card:is(:hover, :focus-within) {
background-position: 0;
}
.link-card:is(:hover, :focus-within) h2 {
color: #4f39fa;
}
.link-card:is(:hover, :focus-within) h2 span {
will-change: transform;
transform: translateX(2px);
}
</style>

View File

@@ -1,55 +0,0 @@
---
export interface Props {
title: string;
}
const { title } = Astro.props as Props;
---
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width">
<link rel="icon" type="image/x-icon" href="/favicon.ico" />
<title>{title}</title>
</head>
<body>
<slot />
</body>
</html>
<style>
:root {
--font-size-base: clamp(1rem, 0.34vw + 0.91rem, 1.19rem);
--font-size-lg: clamp(1.2rem, 0.7vw + 1.2rem, 1.5rem);
--font-size-xl: clamp(2.44rem, 2.38vw + 1.85rem, 3.75rem);
--color-text: hsl(12, 5%, 4%);
--color-bg: hsl(10, 21%, 95%);
}
html {
font-family: system-ui, sans-serif;
font-size: var(--font-size-base);
color: var(--color-text);
background-color: var(--color-bg);
}
body {
margin: 0;
}
:global(h1) {
font-size: var(--font-size-xl);
}
:global(h2) {
font-size: var(--font-size-lg);
}
:global(code) {
font-family: Menlo, Monaco, Lucida Console, Liberation Mono, DejaVu Sans Mono,
Bitstream Vera Sans Mono, Courier New, monospace;
}
</style>

1
examples/astro/src/env.d.ts vendored Normal file
View File

@@ -0,0 +1 @@
/// <reference types="astro/client" />

View File

@@ -0,0 +1,56 @@
---
export interface Props {
title: string;
}
const { title } = Astro.props as Props;
---
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width" />
<link rel="icon" type="image/x-icon" href="/favicon.ico" />
<meta name="generator" content={Astro.generator} />
<title>{title}</title>
</head>
<body>
<slot />
</body>
</html>
<style>
:root {
--font-size-base: clamp(1rem, 0.34vw + 0.91rem, 1.19rem);
--font-size-lg: clamp(1.2rem, 0.7vw + 1.2rem, 1.5rem);
--font-size-xl: clamp(2.44rem, 2.38vw + 1.85rem, 3.75rem);
--color-text: hsl(12, 5%, 4%);
--color-bg: hsl(10, 21%, 95%);
--color-border: hsl(17, 24%, 90%);
}
html {
font-family: system-ui, sans-serif;
font-size: var(--font-size-base);
color: var(--color-text);
background-color: var(--color-bg);
}
body {
margin: 0;
}
:global(h1) {
font-size: var(--font-size-xl);
}
:global(h2) {
font-size: var(--font-size-lg);
}
:global(code) {
font-family: Menlo, Monaco, Lucida Console, Liberation Mono, DejaVu Sans Mono,
Bitstream Vera Sans Mono, Courier New, monospace;
}
</style>

View File

@@ -1,81 +1,52 @@
---
import Layout from '../components/Layout.astro';
import Layout from '../layouts/Layout.astro';
import Card from '../components/Card.astro';
---
<Layout title="Welcome to Astro.">
<main>
<h1>Welcome to <span class="text-gradient">Astro</span></h1>
<p class="instructions"><strong>Your first mission:</strong> tweak this message to try our hot module reloading. Check the <code>src/pages</code> directory!</p>
<p class="instructions">
Check out the <code>src/pages</code> directory to get started.<br />
<strong>Code Challenge:</strong> Tweak the "Welcome to Astro" message above.
</p>
<ul role="list" class="link-card-grid">
<li class="link-card">
<a href="https://astro.build/integrations/">
<h2>Integrations <span>&rarr;</span></h2>
<p>Add component frameworks, Tailwind, Partytown, and more!</p>
</a>
</li>
<li class="link-card">
<a href="https://astro.build/themes/">
<h2>Themes <span>&rarr;</span></h2>
<p>Explore a galaxy of community-built starters.</p>
</a>
</li>
<li class="link-card">
<a href="https://docs.astro.build/">
<h2>Docs <span>&rarr;</span></h2>
<p>Learn our complete feature set and explore the API.</p>
</a>
</li>
<li class="link-card">
<a href="https://astro.build/chat/">
<h2>Chat <span>&rarr;</span></h2>
<p>
Ask, contribute, and have fun on our community Discord
<svg
class="heart"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 512 512"
width="16"
height="16"
fill="currentColor"
>
<title>heart</title>
<path d="M256 448l-30.164-27.211C118.718 322.442 48 258.61 48 179.095 48 114.221 97.918 64 162.4 64c36.399 0 70.717 16.742 93.6 43.947C278.882 80.742 313.199 64 349.6 64 414.082 64 464 114.221 464 179.095c0 79.516-70.719 143.348-177.836 241.694L256 448z" />
</svg>
</p>
</a>
</li>
<Card
href="https://docs.astro.build/"
title="Documentation"
body="Learn how Astro works and explore the official API docs."
/>
<Card
href="https://astro.build/integrations/"
title="Integrations"
body="Supercharge your project with new frameworks and libraries."
/>
<Card
href="https://astro.build/themes/"
title="Themes"
body="Explore a galaxy of community-built starter themes."
/>
<Card
href="https://astro.build/chat/"
title="Chat"
body="Come say hi to our amazing Discord community. ❤️"
/>
</ul>
</main>
</Layout>
<style>
:root {
--color-border: hsl(17, 24%, 90%);
--astro-gradient: linear-gradient(0deg,#4F39FA, #DA62C4);
--link-gradient: linear-gradient(45deg, #4F39FA, #DA62C4 30%, var(--color-border) 60%);
--night-sky-gradient: linear-gradient(0deg, #392362 -33%, #431f69 10%, #30216b 50%, #1f1638 100%);
--astro-gradient: linear-gradient(0deg, #4f39fa, #da62c4);
}
h2 {
margin: 0;
transition: color 0.6s cubic-bezier(0.22, 1, 0.36, 1);
}
h2 span {
display: inline-block;
transition: transform 0.3s cubic-bezier(0.22, 1, 0.36, 1);
}
code {
font-size: 0.875em;
border: 0.1em solid var(--color-border);
border-radius: 4px;
padding: 0.15em 0.25em;
h1 {
margin: 2rem 0;
}
main {
margin: auto;
padding: 1em;
padding: 1em;
max-width: 60ch;
}
@@ -83,7 +54,7 @@ import Layout from '../components/Layout.astro';
font-weight: 900;
background-image: var(--astro-gradient);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
-webkit-text-fill-color: transparent;
background-size: 100% 200%;
background-position-y: 100%;
border-radius: 0.4rem;
@@ -91,7 +62,8 @@ import Layout from '../components/Layout.astro';
}
@keyframes pulse {
0%, 100% {
0%,
100% {
background-position-y: 0%;
}
50% {
@@ -100,75 +72,25 @@ import Layout from '../components/Layout.astro';
}
.instructions {
line-height: 1.8;
margin-bottom: 2rem;
background-image: var(--night-sky-gradient);
padding: 1.5rem;
line-height: 1.6;
margin: 1rem 0;
background: #4f39fa;
padding: 1rem;
border-radius: 0.4rem;
color: var(--color-bg);
}
.instructions code {
font-size: 0.875em;
border: 0.1em solid var(--color-border);
border-radius: 4px;
padding: 0.15em 0.25em;
}
.link-card-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(24ch, 1fr));
gap: 1rem;
padding: 0;
}
.link-card {
list-style: none;
display: flex;
padding: 0.15rem;
background-image: var(--link-gradient);
background-size: 400%;
border-radius: 0.5rem;
background-position: 100%;
transition: background-position 0.6s cubic-bezier(0.22, 1, 0.36, 1);
}
.link-card > a {
width: 100%;
text-decoration: none;
line-height: 1.4;
padding: 1em 1.3em;
border-radius: 0.35rem;
color: var(--text-color);
background-color: white;
opacity: 0.8;
}
.link-card:is(:hover, :focus-within) {
background-position: 0;
}
.link-card:is(:hover, :focus-within) h2 {
color: #4F39FA;
}
.link-card:is(:hover, :focus-within) h2 span {
transform: translateX(2px);
}
.heart {
display: inline-block;
color: #DA62C4;
animation: heartbeat 3s ease-in-out infinite;
}
@keyframes heartbeat {
0%,
50%,
100% {
transform: scale(1);
}
5% {
transform: scale(1.125);
}
10% {
transform: scale(1.05);
}
15% {
transform: scale(1.25);
}
}
</style>

View File

@@ -1,15 +0,0 @@
{
"compilerOptions": {
// Enable top-level await, and other modern ESM features.
"target": "ESNext",
"module": "ESNext",
// Enable node-style module resolution, for things like npm package imports.
"moduleResolution": "node",
// Enable JSON imports.
"resolveJsonModule": true,
// Enable stricter transpilation for better output.
"isolatedModules": true,
// Add type definitions for our Vite runtime.
"types": ["vite/client"]
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -99,6 +99,7 @@
"selector": "MemberExpression > Identifier[name='substr']"
}
],
"no-dupe-keys": 2,
"require-atomic-updates": 0,
"@typescript-eslint/ban-ts-comment": 0,
"@typescript-eslint/camelcase": 0,

View File

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

View File

@@ -8,17 +8,13 @@ import FileFsRef from '../file-fs-ref';
export type GlobOptions = vanillaGlob_.IOptions;
interface FsFiles {
[filePath: string]: FileFsRef;
}
const vanillaGlob = promisify(vanillaGlob_);
export default async function glob(
pattern: string,
opts: GlobOptions | string,
mountpoint?: string
): Promise<FsFiles> {
): Promise<Record<string, FileFsRef>> {
let options: GlobOptions;
if (typeof opts === 'string') {
options = { cwd: opts };
@@ -36,10 +32,11 @@ export default async function glob(
throw new Error(`basePath/cwd must be an absolute path (${options.cwd})`);
}
const results: FsFiles = {};
const results: Record<string, FileFsRef> = {};
const statCache: Record<string, Stats> = {};
options.symlinks = {};
options.statCache = {};
options.statCache = statCache;
options.stat = true;
options.dot = true;
@@ -47,7 +44,7 @@ export default async function glob(
for (const relativePath of files) {
const fsPath = normalizePath(path.join(options.cwd, relativePath));
let stat: Stats = options.statCache[fsPath] as Stats;
let stat = statCache[fsPath];
assert(
stat,
`statCache does not contain value for ${relativePath} (resolved to ${fsPath})`

View File

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

View File

@@ -0,0 +1,32 @@
type Envs = { [key: string]: string | undefined };
/**
* Get the framework-specific prefixed System Environment Variables.
* See https://vercel.com/docs/concepts/projects/environment-variables#system-environment-variables
* @param envPrefix - Prefix, typically from `@vercel/frameworks`
* @param envs - Environment Variables, typically from `process.env`
*/
export function getPrefixedEnvVars({
envPrefix,
envs,
}: {
envPrefix: string | undefined;
envs: Envs;
}): Envs {
const vercelSystemEnvPrefix = 'VERCEL_';
const newEnvs: Envs = {};
if (envPrefix && envs.VERCEL_URL) {
Object.keys(envs)
.filter(key => key.startsWith(vercelSystemEnvPrefix))
.forEach(key => {
const newKey = `${envPrefix}${key}`;
if (!(newKey in envs)) {
newEnvs[newKey] = envs[key];
}
});
// Tell turbo to exclude all Vercel System Env Vars
// See https://github.com/vercel/turborepo/pull/1622
newEnvs.TURBO_CI_VENDOR_ENV_KEY = `${envPrefix}${vercelSystemEnvPrefix}`;
}
return newEnvs;
}

View File

@@ -40,6 +40,7 @@ import streamToBuffer from './fs/stream-to-buffer';
import debug from './debug';
import getIgnoreFilter from './get-ignore-filter';
import { getPlatformEnv } from './get-platform-env';
import { getPrefixedEnvVars } from './get-prefixed-env-vars';
export {
FileBlob,
@@ -76,6 +77,7 @@ export {
getDiscontinuedNodeVersions,
getSpawnOptions,
getPlatformEnv,
getPrefixedEnvVars,
streamToBuffer,
debug,
isSymbolicLink,

View File

@@ -0,0 +1,87 @@
import { getPrefixedEnvVars } from '../src';
describe('Test `getPrefixedEnvVars()`', () => {
const cases: Array<{
name: string;
args: Parameters<typeof getPrefixedEnvVars>[0];
want: ReturnType<typeof getPrefixedEnvVars>;
}> = [
{
name: 'should work with NEXT_PUBLIC_',
args: {
envPrefix: 'NEXT_PUBLIC_',
envs: {
VERCEL: '1',
VERCEL_URL: 'example.vercel.sh',
USER_ENV_VAR_NOT_VERCEL: 'example.com',
FOO: 'bar',
},
},
want: {
NEXT_PUBLIC_VERCEL_URL: 'example.vercel.sh',
TURBO_CI_VENDOR_ENV_KEY: 'NEXT_PUBLIC_VERCEL_',
},
},
{
name: 'should work with GATSBY_',
args: {
envPrefix: 'GATSBY_',
envs: {
USER_ENV_VAR_NOT_VERCEL: 'example.com',
FOO: 'bar',
VERCEL_URL: 'example.vercel.sh',
VERCEL_ENV: 'production',
VERCEL_REGION: 'iad1',
VERCEL_GIT_COMMIT_AUTHOR_LOGIN: 'rauchg',
},
},
want: {
GATSBY_VERCEL_URL: 'example.vercel.sh',
GATSBY_VERCEL_ENV: 'production',
GATSBY_VERCEL_REGION: 'iad1',
GATSBY_VERCEL_GIT_COMMIT_AUTHOR_LOGIN: 'rauchg',
TURBO_CI_VENDOR_ENV_KEY: 'GATSBY_VERCEL_',
},
},
{
name: 'should not return anything if no system env vars detected',
args: {
envPrefix: 'GATSBY_',
envs: {
USER_ENV_VAR_NOT_VERCEL: 'example.com',
FOO: 'bar',
BLARG_VERCEL_THING: 'fake',
},
},
want: {},
},
{
name: 'should not return anything if envPrefix is empty string',
args: {
envPrefix: '',
envs: {
VERCEL: '1',
VERCEL_URL: 'example.vercel.sh',
},
},
want: {},
},
{
name: 'should not return anything if envPrefix is undefined',
args: {
envPrefix: undefined,
envs: {
VERCEL: '1',
VERCEL_URL: 'example.vercel.sh',
},
},
want: {},
},
];
for (const { name, args, want } of cases) {
it(name, () => {
expect(getPrefixedEnvVars(args)).toEqual(want);
});
}
});

View File

@@ -394,7 +394,7 @@ it('should get latest node version', async () => {
it('should throw for discontinued versions', async () => {
// Mock a future date so that Node 8 and 10 become discontinued
const realDateNow = Date.now.bind(global.Date);
global.Date.now = () => new Date('2022-09-01').getTime();
global.Date.now = () => new Date('2022-10-15').getTime();
expect(getSupportedNodeVersion('8.10.x', false)).rejects.toThrow();
expect(getSupportedNodeVersion('8.10.x', true)).rejects.toThrow();
@@ -436,8 +436,8 @@ it('should warn for deprecated versions, soon to be discontinued', async () => {
expect(warningMessages).toStrictEqual([
'Error: Node.js version 10.x has reached End-of-Life. Deployments created on or after 2021-04-20 will fail to build. Please set "engines": { "node": "16.x" } in your `package.json` file to use Node.js 16.',
'Error: Node.js version 10.x has reached End-of-Life. Deployments created on or after 2021-04-20 will fail to build. Please set Node.js Version to 16.x in your Project Settings to use Node.js 16.',
'Error: Node.js version 12.x has reached End-of-Life. Deployments created on or after 2022-08-09 will fail to build. Please set "engines": { "node": "16.x" } in your `package.json` file to use Node.js 16.',
'Error: Node.js version 12.x has reached End-of-Life. Deployments created on or after 2022-08-09 will fail to build. Please set Node.js Version to 16.x in your Project Settings to use Node.js 16.',
'Error: Node.js version 12.x has reached End-of-Life. Deployments created on or after 2022-10-01 will fail to build. Please set "engines": { "node": "16.x" } in your `package.json` file to use Node.js 16.',
'Error: Node.js version 12.x has reached End-of-Life. Deployments created on or after 2022-10-01 will fail to build. Please set Node.js Version to 16.x in your Project Settings to use Node.js 16.',
]);
global.Date.now = realDateNow;

View File

@@ -1,6 +1,6 @@
{
"name": "vercel",
"version": "27.3.7",
"version": "28.1.4",
"preferGlobal": true,
"license": "Apache-2.0",
"description": "The command-line interface for Vercel",
@@ -41,16 +41,16 @@
"node": ">= 14"
},
"dependencies": {
"@vercel/build-utils": "5.2.0",
"@vercel/go": "2.0.15",
"@vercel/hydrogen": "0.0.12",
"@vercel/next": "3.1.15",
"@vercel/node": "2.5.6",
"@vercel/python": "3.1.7",
"@vercel/redwood": "1.0.16",
"@vercel/remix": "1.0.17",
"@vercel/ruby": "1.3.23",
"@vercel/static-build": "1.0.16",
"@vercel/build-utils": "5.3.2",
"@vercel/go": "2.2.2",
"@vercel/hydrogen": "0.0.15",
"@vercel/next": "3.1.21",
"@vercel/node": "2.5.10",
"@vercel/python": "3.1.11",
"@vercel/redwood": "1.0.19",
"@vercel/remix": "1.0.20",
"@vercel/ruby": "1.3.28",
"@vercel/static-build": "1.0.19",
"update-notifier": "5.1.0"
},
"devDependencies": {
@@ -96,7 +96,7 @@
"@types/which": "1.3.2",
"@types/write-json-file": "2.2.1",
"@types/yauzl-promise": "2.1.0",
"@vercel/client": "12.1.10",
"@vercel/client": "12.2.1",
"@vercel/frameworks": "1.1.3",
"@vercel/fs-detectors": "2.0.5",
"@vercel/fun": "1.0.4",
@@ -118,7 +118,6 @@
"chokidar": "3.3.1",
"codecov": "3.8.2",
"cpy": "7.2.0",
"credit-card": "3.0.1",
"date-fns": "1.29.0",
"debug": "3.1.0",
"dot": "1.1.3",

View File

@@ -37,6 +37,7 @@ const help = () => {
)} Login token
-S, --scope Set a custom scope
-N, --next Show next page of results
-y, --yes Skip the confirmation prompt when removing an alias
${chalk.dim('Examples:')}

View File

@@ -59,7 +59,7 @@ export default async function rm(
const removeStamp = stamp();
if (!opts['--yes'] && !(await confirmAliasRemove(client, alias))) {
output.log('Aborted');
output.log('Canceled');
return 0;
}

View File

@@ -220,7 +220,7 @@ function handleSetupDomainError<T>(
}
if (error instanceof ERRORS.UserAborted) {
output.error(`User aborted`);
output.error(`User canceled.`);
return 1;
}

View File

@@ -1,170 +0,0 @@
import ansiEscapes from 'ansi-escapes';
import chalk from 'chalk';
import ccValidator from 'credit-card';
import textInput from '../../util/input/text';
import cardBrands from '../../util/billing/card-brands';
import success from '../../util/output/success';
import wait from '../../util/output/wait';
import chars from '../../util/output/chars';
import error from '../../util/output/error';
const expDateMiddleware = data => data;
export default async function ({ creditCards, clear = false, contextName }) {
const state = {
error: undefined,
cardGroupLabel: `> ${chalk.bold(
`Enter your card details for ${chalk.bold(contextName)}`
)}`,
name: {
label: 'Full Name'.padEnd(12),
placeholder: 'John Appleseed',
validateValue: data => data.trim().length > 0,
},
cardNumber: {
label: 'Number'.padEnd(12),
mask: 'cc',
placeholder: '#### #### #### ####',
validateKeypress: (data, value) => /\d/.test(data) && value.length < 19,
validateValue: data => {
data = data.replace(/ /g, '');
const type = ccValidator.determineCardType(data);
if (!type) {
return false;
}
return ccValidator.isValidCardNumber(data, type);
},
},
ccv: {
label: 'CCV'.padEnd(12),
mask: 'ccv',
placeholder: '###',
validateValue: data => {
const brand = state.cardNumber.brand.toLowerCase();
return ccValidator.doesCvvMatchType(data, brand);
},
},
expDate: {
label: 'Exp. Date'.padEnd(12),
mask: 'expDate',
placeholder: 'mm / yyyy',
middleware: expDateMiddleware,
validateValue: data => !ccValidator.isExpired(...data.split(' / ')),
},
};
async function render() {
for (const key in state) {
if (!Object.hasOwnProperty.call(state, key)) {
continue;
}
const piece = state[key];
if (typeof piece === 'string') {
console.log(piece);
} else if (typeof piece === 'object') {
let result;
try {
/* eslint-disable no-await-in-loop */
result = await textInput({
label: `- ${piece.label}`,
initialValue: piece.initialValue || piece.value,
placeholder: piece.placeholder,
mask: piece.mask,
validateKeypress: piece.validateKeypress,
validateValue: piece.validateValue,
autoComplete: piece.autoComplete,
});
piece.value = result;
if (key === 'cardNumber') {
let brand = cardBrands[ccValidator.determineCardType(result)];
piece.brand = brand;
if (brand === 'American Express') {
state.ccv.placeholder = '#'.repeat(4);
} else {
state.ccv.placeholder = '#'.repeat(3);
}
brand = chalk.cyan(`[${brand}]`);
const masked = chalk.gray('#### '.repeat(3)) + result.split(' ')[3];
process.stdout.write(
`${chalk.cyan(chars.tick)} ${piece.label}${masked} ${brand}\n`
);
} else if (key === 'ccv') {
process.stdout.write(
`${chalk.cyan(chars.tick)} ${piece.label}${'*'.repeat(
result.length
)}\n`
);
} else if (key === 'expDate') {
let text = result.split(' / ');
text = text[0] + chalk.gray(' / ') + text[1];
process.stdout.write(
`${chalk.cyan(chars.tick)} ${piece.label}${text}\n`
);
} else {
process.stdout.write(
`${chalk.cyan(chars.tick)} ${piece.label}${result}\n`
);
}
} catch (err) {
if (err.message === 'USER_ABORT') {
process.exit(1);
} else {
console.error(error(err));
}
}
}
}
console.log(''); // New line
const stopSpinner = wait(process.stderr, 'Saving card');
try {
const res = await creditCards.add({
name: state.name.value,
cardNumber: state.cardNumber.value,
ccv: state.ccv.value,
expDate: state.expDate.value,
});
stopSpinner();
if (clear) {
const linesToClear = state.error ? 15 : 14;
process.stdout.write(ansiEscapes.eraseLines(linesToClear));
}
console.log(
success(
`${state.cardNumber.brand || state.cardNumber.card.brand} ending in ${
res.last4 || res.card.last4
} was added to ${chalk.bold(contextName)}`
)
);
} catch (err) {
stopSpinner();
const linesToClear = state.error ? 15 : 14;
process.stdout.write(ansiEscapes.eraseLines(linesToClear));
state.error = `${chalk.red('> Error!')} ${
err.message
} Please make sure the info is correct`;
await render();
}
}
try {
await render();
} catch (err) {
console.erorr(err);
}
}

View File

@@ -1,353 +0,0 @@
import chalk from 'chalk';
import ms from 'ms';
import plural from 'pluralize';
import { error } from '../../util/error';
import NowCreditCards from '../../util/credit-cards';
import indent from '../../util/output/indent';
import listInput from '../../util/input/list';
import success from '../../util/output/success';
import promptBool from '../../util/input/prompt-bool';
import info from '../../util/output/info';
import logo from '../../util/output/logo';
import addBilling from './add';
import exit from '../../util/exit';
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';
const help = () => {
console.log(`
${chalk.bold(`${logo} ${getPkgName()} billing`)} [options] <command>
${chalk.dim('Options:')}
ls Show all of your credit cards
add Add a new credit card
rm [id] Remove a credit card
set-default [id] Make a credit card your default one
${chalk.dim('Options:')}
-h, --help Output usage information
-A ${chalk.bold.underline('FILE')}, --local-config=${chalk.bold.underline(
'FILE'
)} Path to the local ${'`vercel.json`'} file
-Q ${chalk.bold.underline('DIR')}, --global-config=${chalk.bold.underline(
'DIR'
)} Path to the global ${'`.vercel`'} directory
-d, --debug Debug mode [off]
-t ${chalk.bold.underline('TOKEN')}, --token=${chalk.bold.underline(
'TOKEN'
)} Login token
-S, --scope Set a custom scope
${chalk.dim('Examples:')}
${chalk.gray('')} Add a new credit card (interactively)
${chalk.cyan(`$ ${getPkgName()} billing add`)}
`);
};
let argv;
let subcommand;
export default async client => {
try {
argv = getArgs(client.argv.slice(2), {});
} catch (error) {
handleError(error);
return 1;
}
argv._ = argv._.slice(1);
subcommand = argv._[0];
if (argv['--help'] || !subcommand) {
help();
return 2;
}
const {
output,
config: { currentTeam },
} = client;
const start = new Date();
const creditCards = new NowCreditCards({
client,
currentTeam,
});
let contextName = null;
try {
({ contextName } = await getScope(client));
} catch (err) {
if (err.code === 'NOT_AUTHORIZED' || err.code === 'TEAM_DELETED') {
output.error(err.message);
return 1;
}
throw err;
}
const args = argv._.slice(1);
switch (subcommand) {
case 'ls':
case 'list': {
let cards;
try {
cards = await creditCards.ls();
} catch (err) {
console.error(error(err.message));
return 1;
}
const text = cards.sources
.map(source => {
const _default =
source.id === cards.defaultSource
? ` ${chalk.bold('(default)')}`
: '';
const id = `${chalk.gray('-')} ${chalk.cyan(
`ID: ${source.id}`
)}${_default}`;
const number = `${chalk.gray('#### ').repeat(3)}${
source.last4 || source.card.last4
}`;
return [
id,
indent(source.name || source.owner.name, 2),
indent(`${source.brand || source.card.brand} ${number}`, 2),
].join('\n');
})
.join('\n\n');
const elapsed = ms(new Date() - start);
console.log(
`> ${plural(
'card',
cards.sources.length,
true
)} found under ${chalk.bold(contextName)} ${chalk.gray(`[${elapsed}]`)}`
);
if (text) {
console.log(`\n${text}\n`);
}
break;
}
case 'set-default': {
if (args.length > 1) {
console.error(error('Invalid number of arguments'));
return 1;
}
const start = new Date();
let cards;
try {
cards = await creditCards.ls();
} catch (err) {
console.error(error(err.message));
return 1;
}
if (cards.sources.length === 0) {
console.error(error('You have no credit cards to choose from'));
return 0;
}
let cardId = args[0];
if (cardId === undefined) {
const elapsed = ms(new Date() - start);
const message = `Selecting a new default payment card for ${chalk.bold(
contextName
)} ${chalk.gray(`[${elapsed}]`)}`;
const choices = buildInquirerChoices(cards);
cardId = await listInput(client, {
message,
choices,
separator: true,
abort: 'end',
});
}
// Check if the provided cardId (in case the user
// typed `vercel billing set-default <some-id>`) is valid
if (cardId) {
const label = `Are you sure that you to set this card as the default?`;
const confirmation = await promptBool(label, {
...client,
trailing: '\n',
});
if (!confirmation) {
console.log(info('Aborted'));
break;
}
const start = new Date();
await creditCards.setDefault(cardId);
const card = cards.sources.find(card => card.id === cardId);
const elapsed = ms(new Date() - start);
console.log(
success(
`${card.brand || card.card.brand} ending in ${
card.last4 || card.card.last4
} is now the default ${chalk.gray(`[${elapsed}]`)}`
)
);
} else {
console.log('No changes made');
}
break;
}
case 'rm':
case 'remove': {
if (args.length > 1) {
console.error(error('Invalid number of arguments'));
return 1;
}
const start = new Date();
let cards;
try {
cards = await creditCards.ls();
} catch (err) {
console.error(error(err.message));
return 1;
}
if (cards.sources.length === 0) {
console.error(
error(
`You have no credit cards to choose from to delete under ${chalk.bold(
contextName
)}`
)
);
return 0;
}
let cardId = args[0];
if (cardId === undefined) {
const elapsed = ms(new Date() - start);
const message = `Selecting a card to ${chalk.underline(
'remove'
)} under ${chalk.bold(contextName)} ${chalk.gray(`[${elapsed}]`)}`;
const choices = buildInquirerChoices(cards);
cardId = await listInput(client, {
message,
choices,
separator: true,
abort: 'start',
});
}
// Shoud check if the provided cardId (in case the user
// typed `vercel billing rm <some-id>`) is valid
if (cardId) {
const label = `Are you sure that you want to remove this card?`;
const confirmation = await promptBool(label, client);
if (!confirmation) {
console.log('Aborted');
break;
}
const start = new Date();
await creditCards.rm(cardId);
const deletedCard = cards.sources.find(card => card.id === cardId);
const remainingCards = cards.sources.filter(card => card.id !== cardId);
let text = `${deletedCard.brand || deletedCard.card.brand} ending in ${
deletedCard.last4 || deletedCard.card.last4
} was deleted`;
// ${chalk.gray(`[${elapsed}]`)}
if (cardId === cards.defaultSource) {
if (remainingCards.length === 0) {
// The user deleted the last card in their account
text += `\n${chalk.yellow('Warning!')} You have no default card`;
} else {
// We can't guess the current default card let's ask the API
const cards = await creditCards.ls();
const newDefaultCard = cards.sources.find(
card => card.id === cards.defaultCardId
);
text += `\n${
newDefaultCard.brand || newDefaultCard.card.brand
} ending in ${
newDefaultCard.last4 || newDefaultCard.card.last4
} in now default for ${chalk.bold(contextName)}`;
}
}
const elapsed = ms(new Date() - start);
text += ` ${chalk.gray(`[${elapsed}]`)}`;
console.log(success(text));
} else {
console.log('No changes made');
}
break;
}
case 'add': {
await addBilling({
creditCards,
contextName,
});
break;
}
default:
console.error(
error('Please specify a valid subcommand: ls | add | rm | set-default')
);
help();
return 1;
}
// This is required, otherwise we get those weird zlib errors
return exit(0);
};
// Builds a `choices` object that can be passesd to inquirer.prompt()
function buildInquirerChoices(cards) {
return cards.sources.map(source => {
const _default =
source.id === cards.defaultSource ? ` ${chalk.bold('(default)')}` : '';
const id = `${chalk.cyan(`ID: ${source.id}`)}${_default}`;
const number = `${chalk.gray('#### ').repeat(3)}${
source.last4 || source.card.last4
}`;
const str = [
id,
indent(source.name || source.owner.name, 2),
indent(`${source.brand || source.card.brand} ${number}`, 2),
].join('\n');
return {
name: str, // Will be displayed by Inquirer
value: source.id, // Will be used to identify the answer
short: source.id, // Will be displayed after the users answers
};
});
}

View File

@@ -116,8 +116,6 @@ export default async function main(client: Client): Promise<number> {
}
}
const badDeploymentPromise = getDeployment(client, bad).catch(err => err);
good = normalizeURL(good);
parsed = parse(good);
if (!parsed.hostname) {
@@ -138,8 +136,6 @@ export default async function main(client: Client): Promise<number> {
);
}
const goodDeploymentPromise = getDeployment(client, good).catch(err => err);
if (!subpath) {
subpath = await prompt(
client,
@@ -148,10 +144,9 @@ export default async function main(client: Client): Promise<number> {
}
output.spinner('Retrieving deployments…');
const [badDeployment, goodDeployment] = await Promise.all([
badDeploymentPromise,
goodDeploymentPromise,
]);
// `getDeployment` cannot be parallelized because it might prompt for login
const badDeployment = await getDeployment(client, bad).catch(err => err);
if (badDeployment) {
if (badDeployment instanceof Error) {
@@ -165,7 +160,8 @@ export default async function main(client: Client): Promise<number> {
return 1;
}
const { projectId } = badDeployment;
// `getDeployment` cannot be parallelized because it might prompt for login
const goodDeployment = await getDeployment(client, good).catch(err => err);
if (goodDeployment) {
if (goodDeployment instanceof Error) {
@@ -181,6 +177,8 @@ export default async function main(client: Client): Promise<number> {
return 1;
}
const { projectId } = badDeployment;
if (projectId !== goodDeployment.projectId) {
output.error(`Good and Bad deployments must be from the same Project`);
return 1;

View File

@@ -91,7 +91,7 @@ const help = () => {
--output [path] Directory where built assets should be written to
--prod Build a production deployment
-d, --debug Debug mode [off]
-y, --yes Skip the confirmation prompt
-y, --yes Skip the confirmation prompt about pulling environment variables and project settings when not found locally
${chalk.dim('Examples:')}
@@ -157,7 +157,7 @@ export default async function main(client: Client): Promise<number> {
client.output.print(
`No Project Settings found locally. Run ${cli.getCommandName(
'pull --yes'
)} to retreive them.`
)} to retrieve them.`
);
return 1;
}
@@ -171,7 +171,7 @@ export default async function main(client: Client): Promise<number> {
);
}
if (!confirmed) {
client.output.print(`Aborted. No Project Settings retrieved.\n`);
client.output.print(`Canceled. No Project Settings retrieved.\n`);
return 0;
}
const { argv: originalArgv } = client;

View File

@@ -9,7 +9,6 @@ export const help = () => `
${chalk.dim('Basic')}
billing Manages the account payment methods
deploy [path] Performs a deployment ${chalk.bold(
'(default)'
)}
@@ -73,7 +72,7 @@ export const help = () => `
-S, --scope Set a custom scope
--regions Set default regions to enable the deployment on
--prod Create a production deployment
-c, --confirm Confirm default options and skip questions
-y, --yes Skip questions when setting up new project using default scope and settings
${chalk.dim('Examples:')}

View File

@@ -3,7 +3,11 @@ import fs from 'fs-extra';
import bytes from 'bytes';
import chalk from 'chalk';
import { join, resolve, basename } from 'path';
import { fileNameSymbol, VercelConfig } from '@vercel/client';
import {
fileNameSymbol,
VALID_ARCHIVE_FORMATS,
VercelConfig,
} from '@vercel/client';
import code from '../../util/output/code';
import highlight from '../../util/output/highlight';
import { readLocalConfig } from '../../util/config/files';
@@ -66,10 +70,11 @@ import { getDeploymentChecks } from '../../util/deploy/get-deployment-checks';
import parseTarget from '../../util/deploy/parse-target';
import getPrebuiltJson from '../../util/deploy/get-prebuilt-json';
import { createGitMeta } from '../../util/create-git-meta';
import { isValidArchive } from '../../util/deploy/validate-archive-format';
import { parseEnv } from '../../util/parse-env';
import { errorToString, isErrnoException, isError } from '../../util/is-error';
export default async (client: Client) => {
export default async (client: Client): Promise<number> => {
const { output } = client;
let argv = null;
@@ -87,20 +92,28 @@ export default async (client: Client) => {
'--regions': String,
'--prebuilt': Boolean,
'--prod': Boolean,
'--confirm': Boolean,
'--archive': String,
'--yes': Boolean,
'-f': '--force',
'-p': '--public',
'-e': '--env',
'-b': '--build-env',
'-m': '--meta',
'-c': '--confirm',
'-y': '--yes',
// deprecated
'--name': String,
'-n': '--name',
'--no-clipboard': Boolean,
'--target': String,
'--confirm': Boolean,
'-c': '--confirm',
});
if ('--confirm' in argv) {
output.warn('`--confirm` is deprecated, please use `--yes` instead');
argv['--yes'] = argv['--confirm'];
}
} catch (error) {
handleError(error);
return 1;
@@ -173,7 +186,7 @@ export default async (client: Client) => {
}
const { path } = pathValidation;
const autoConfirm = argv['--confirm'];
const autoConfirm = argv['--yes'];
// deprecate --name
if (argv['--name']) {
@@ -254,6 +267,12 @@ export default async (client: Client) => {
}
}
const archive = argv['--archive'];
if (typeof archive === 'string' && !isValidArchive(archive)) {
output.error(`Format must be one of: ${VALID_ARCHIVE_FORMATS.join(', ')}`);
return 1;
}
// retrieve `project` and `org` from .vercel
const link = await getLinkedProject(client, path);
@@ -277,7 +296,7 @@ export default async (client: Client) => {
));
if (!shouldStartSetup) {
output.print(`Aborted. Project not set up.\n`);
output.print(`Canceled. Project not set up.\n`);
return 0;
}
@@ -538,7 +557,8 @@ export default async (client: Client) => {
createArgs,
org,
!project,
path
path,
archive
);
if (deployment.code === 'missing_project_settings') {
@@ -911,4 +931,6 @@ const printDeploymentStatus = async (
) + newline;
output.print(message + link);
}
return 0;
};

View File

@@ -17,7 +17,7 @@ import { OUTPUT_DIR } from '../../util/build/write-build-result';
type Options = {
'--listen': string;
'--confirm': boolean;
'--yes': boolean;
};
export default async function dev(
@@ -38,7 +38,7 @@ export default async function dev(
if (link.status === 'not_linked' && !process.env.__VERCEL_SKIP_DEV_CMD) {
link = await setupAndLink(client, cwd, {
autoConfirm: opts['--confirm'],
autoConfirm: opts['--yes'],
successEmoji: 'link',
setupMsg: 'Set up and develop',
});
@@ -54,14 +54,13 @@ export default async function dev(
client.output.error(
`Command ${getCommandName(
'dev'
)} requires confirmation. Use option ${param('--confirm')} to confirm.`
)} requires confirmation. Use option ${param('--yes')} to confirm.`
);
}
return link.exitCode;
}
let devCommand: string | undefined;
let frameworkSlug: string | undefined;
let projectSettings: ProjectSettings | undefined;
let projectEnvs: ProjectEnvVariable[] = [];
let systemEnvValues: string[] = [];
@@ -77,10 +76,6 @@ export default async function dev(
const framework = frameworks.find(f => f.slug === project.framework);
if (framework) {
if (framework.slug) {
frameworkSlug = framework.slug;
}
const defaults = framework.settings.devCommand.value;
if (defaults) {
devCommand = defaults;
@@ -120,7 +115,6 @@ export default async function dev(
const devServer = new DevServer(cwd, {
output,
devCommand,
frameworkSlug,
projectSettings,
projectEnvs,
systemEnvValues,

View File

@@ -33,7 +33,7 @@ const help = () => {
-d, --debug Debug mode [off]
-l, --listen [uri] Specify a URI endpoint on which to listen [0.0.0.0:3000]
-t, --token [token] Specify an Authorization Token
--confirm Skip questions and use defaults when setting up a new project
-y, --yes Skip questions when setting up new project using default scope and settings
${chalk.dim('Examples:')}
@@ -74,14 +74,22 @@ export default async function main(client: Client) {
argv = getArgs(client.argv.slice(2), {
'--listen': String,
'-l': '--listen',
'--confirm': Boolean,
'--yes': Boolean,
'-y': '--yes',
// Deprecated
'--port': Number,
'-p': '--port',
'--confirm': Boolean,
'-c': '--confirm',
});
args = getSubcommand(argv._.slice(1), COMMAND_CONFIG).args;
if ('--confirm' in argv) {
output.warn('`--confirm` is deprecated, please use `--yes` instead');
argv['--yes'] = argv['--confirm'];
}
if ('--port' in argv) {
output.warn('`--port` is deprecated, please use `--listen` instead');
argv['--listen'] = String(argv['--port']);

View File

@@ -37,7 +37,7 @@ export default async function add(
const { domain, data: argData } = parsedParams;
const data = await getDNSData(client, argData);
if (!data) {
output.log(`Aborted`);
output.log(`Canceled`);
return 1;
}

View File

@@ -46,7 +46,7 @@ export default async function rm(
);
if (!yes) {
output.error(`User aborted.`);
output.error(`User canceled.`);
return 0;
}

View File

@@ -116,9 +116,7 @@ export default async function buy(
if (buyResult instanceof ERRORS.SourceNotFound) {
output.error(
`Could not purchase domain. Please add a payment method using ${getCommandName(
`billing add`
)}.`
`Could not purchase domain. Please add a payment method using the dashboard.`
);
return 1;
}

View File

@@ -45,6 +45,7 @@ const help = () => {
)} Login token
-S, --scope Set a custom scope
-N, --next Show next page of results
-y, --yes Skip the confirmation prompt when removing a domain
${chalk.dim('Examples:')}
@@ -92,6 +93,7 @@ export default async function main(client: Client) {
'--force': Boolean,
'--next': Number,
'-N': '--next',
'-y': '--yes',
});
} catch (error) {
handleError(error);

View File

@@ -68,7 +68,7 @@ export default async function move(
client
))
) {
output.log('Aborted');
output.log('Canceled');
return 0;
}
}
@@ -87,7 +87,7 @@ export default async function move(
client
))
) {
output.log('Aborted');
output.log('Canceled');
return 0;
}
}

View File

@@ -86,7 +86,7 @@ export default async function rm(
client
))
) {
output.log('Aborted');
output.log('Canceled');
return 0;
}
@@ -224,7 +224,7 @@ async function removeDomain(
!skipConfirmation &&
!(await promptBool(`Remove conflicts associated with domain?`, client))
) {
output.log('Aborted');
output.log('Canceled');
return 0;
}

View File

@@ -111,9 +111,7 @@ export default async function transferIn(
if (transferInResult instanceof ERRORS.SourceNotFound) {
output.error(
`Could not purchase domain. Please add a payment method using ${getCommandName(
`billing add`
)}.`
`Could not purchase domain. Please add a payment method using the dashboard.`
);
return 1;
}

View File

@@ -42,6 +42,7 @@ const help = () => {
-t ${chalk.bold.underline('TOKEN')}, --token=${chalk.bold.underline(
'TOKEN'
)} Login token
-y, --yes Skip the confirmation prompt when overwriting env file on pull or removing an env variable
${chalk.dim('Examples:')}

View File

@@ -84,7 +84,7 @@ export default async function pull(
false
))
) {
output.log('Aborted');
output.log('Canceled');
return 0;
}
@@ -117,7 +117,12 @@ export default async function pull(
if (exists) {
oldEnv = await createEnvObject(fullPath, output);
if (oldEnv) {
deltaString = buildDeltaString(oldEnv, records);
// Removes any double quotes from `records`, if they exist
// We need this because double quotes are stripped from the local .env file,
// but `records` is already in the form of a JSON object that doesn't filter
// double quotes.
const newEnv = JSON.parse(JSON.stringify(records).replace(/\\"/g, ''));
deltaString = buildDeltaString(oldEnv, newEnv);
}
}

View File

@@ -1,5 +1,4 @@
import chalk from 'chalk';
import inquirer from 'inquirer';
import { Project } from '../../types';
import { Output } from '../../util/output';
import confirm from '../../util/input/confirm';
@@ -45,7 +44,7 @@ export default async function rm(
let [envName, envTarget, envGitBranch] = args;
while (!envName) {
const { inputName } = await inquirer.prompt({
const { inputName } = await client.prompt({
type: 'input',
name: 'inputName',
message: `Whats the name of the variable?`,
@@ -87,7 +86,7 @@ export default async function rm(
}
while (envs.length > 1) {
const { id } = await inquirer.prompt({
const { id } = await client.prompt({
name: 'id',
type: 'list',
message: `Remove ${envName} from which Environments?`,
@@ -112,7 +111,7 @@ export default async function rm(
false
))
) {
output.log('Aborted');
output.log('Canceled');
return 0;
}

View File

@@ -1,21 +1,54 @@
import { Dictionary } from '@vercel/client';
import chalk from 'chalk';
import { join } from 'path';
import { Org, Project } from '../../types';
import { Org, Project, ProjectLinkData } from '../../types';
import Client from '../../util/client';
import { parseGitConfig, pluckRemoteUrls } from '../../util/create-git-meta';
import confirm from '../../util/input/confirm';
import list, { ListChoice } from '../../util/input/list';
import { Output } from '../../util/output';
import link from '../../util/output/link';
import { getCommandName } from '../../util/pkg-name';
import {
connectGitProvider,
disconnectGitProvider,
formatProvider,
RepoInfo,
parseRepoUrl,
} from '../../util/projects/connect-git-provider';
printRemoteUrls,
} from '../../util/git/connect-git-provider';
import validatePaths from '../../util/validate-paths';
interface GitRepoCheckParams {
client: Client;
confirm: boolean;
gitProviderLink?: ProjectLinkData;
org: Org;
gitOrg: string;
project: Project;
provider: string;
repo: string;
repoPath: string;
}
interface ConnectArgParams {
client: Client;
org: Org;
project: Project;
confirm: boolean;
repoInfo: RepoInfo;
}
interface ConnectGitArgParams extends ConnectArgParams {
gitConfig: Dictionary<any>;
}
interface PromptConnectArgParams {
client: Client;
yes: boolean;
repoInfo: RepoInfo;
remoteUrls: Dictionary<string>;
}
export default async function connect(
client: Client,
argv: any,
@@ -24,9 +57,10 @@ export default async function connect(
org: Org | undefined
) {
const { output } = client;
const confirm = Boolean(argv['--confirm']);
const confirm = Boolean(argv['--yes']);
const repoArg = argv._[1];
if (args.length !== 0) {
if (args.length > 1) {
output.error(
`Invalid number of arguments. Usage: ${chalk.cyan(
`${getCommandName('project connect')}`
@@ -36,7 +70,7 @@ export default async function connect(
}
if (!project || !org) {
output.error(
`Can't find \`org\` or \`project\`. Make sure your current directory is linked to a Vercel projet by running ${getCommandName(
`Can't find \`org\` or \`project\`. Make sure your current directory is linked to a Vercel project by running ${getCommandName(
'link'
)}.`
);
@@ -57,9 +91,38 @@ export default async function connect(
// get project from .git
const gitConfigPath = join(path, '.git/config');
const gitConfig = await parseGitConfig(gitConfigPath, output);
if (repoArg) {
// parse repo arg
const parsedUrlArg = parseRepoUrl(repoArg);
if (!parsedUrlArg) {
output.error(
`Failed to parse URL "${repoArg}". Please ensure the URL is valid.`
);
return 1;
}
if (gitConfig) {
return await connectArgWithLocalGit({
client,
org,
project,
confirm,
gitConfig,
repoInfo: parsedUrlArg,
});
}
return await connectArg({
client,
confirm,
org,
project,
repoInfo: repoArg,
});
}
if (!gitConfig) {
output.error(
`No local git repo found. Run ${chalk.cyan(
`No local Git repository found. Run ${chalk.cyan(
'`git clone <url>`'
)} to clone a remote Git repository first.`
);
@@ -78,7 +141,7 @@ export default async function connect(
let remoteUrl: string;
if (Object.keys(remoteUrls).length > 1) {
output.log(`Found multiple remote URLs.`);
output.log('Found multiple remote URLs.');
remoteUrl = await selectRemoteUrl(client, remoteUrls);
} else {
// If only one is found, get it — usually "origin"
@@ -86,14 +149,14 @@ export default async function connect(
}
if (remoteUrl === '') {
output.log('Aborted.');
output.log('Canceled');
return 0;
}
output.log(`Connecting Git remote: ${link(remoteUrl)}`);
const parsedUrl = parseRepoUrl(remoteUrl);
if (!parsedUrl) {
const repoInfo = parseRepoUrl(remoteUrl);
if (!repoInfo) {
output.error(
`Failed to parse Git repo data from the following remote URL: ${link(
remoteUrl
@@ -101,10 +164,176 @@ export default async function connect(
);
return 1;
}
const { provider, org: gitOrg, repo } = parsedUrl;
const { provider, org: gitOrg, repo } = repoInfo;
const repoPath = `${gitOrg}/${repo}`;
let connectedRepoPath;
const checkAndConnect = await checkExistsAndConnect({
client,
confirm,
org,
project,
gitProviderLink,
provider,
repoPath,
gitOrg,
repo,
});
if (typeof checkAndConnect === 'number') {
return checkAndConnect;
}
output.log(
`Connected ${formatProvider(provider)} repository ${chalk.cyan(repoPath)}!`
);
return 0;
}
async function connectArg({
client,
confirm,
org,
project,
repoInfo,
}: ConnectArgParams) {
const { url: repoUrl } = repoInfo;
client.output.log(`Connecting Git remote: ${link(repoUrl)}`);
const parsedRepoArg = parseRepoUrl(repoUrl);
if (!parsedRepoArg) {
client.output.error(
`Failed to parse URL "${repoUrl}". Please ensure the URL is valid.`
);
return 1;
}
const { provider, org: gitOrg, repo } = parsedRepoArg;
const repoPath = `${gitOrg}/${repo}`;
const connect = await checkExistsAndConnect({
client,
confirm,
org,
project,
gitProviderLink: project.link,
provider,
repoPath,
gitOrg,
repo,
});
if (typeof connect === 'number') {
return connect;
}
client.output.log(
`Connected ${formatProvider(provider)} repository ${chalk.cyan(repoPath)}!`
);
return 0;
}
async function connectArgWithLocalGit({
client,
org,
project,
confirm,
gitConfig,
repoInfo,
}: ConnectGitArgParams) {
const remoteUrls = pluckRemoteUrls(gitConfig);
if (remoteUrls) {
const shouldConnect = await promptConnectArg({
client,
yes: confirm,
repoInfo,
remoteUrls,
});
if (!shouldConnect) {
return 1;
}
if (shouldConnect) {
const { provider, org: gitOrg, repo, url: repoUrl } = repoInfo;
const repoPath = `${gitOrg}/${repo}`;
client.output.log(`Connecting Git remote: ${link(repoUrl)}`);
const connect = await checkExistsAndConnect({
client,
confirm,
org,
project,
gitProviderLink: project.link,
provider,
repoPath,
gitOrg,
repo,
});
if (typeof connect === 'number') {
return connect;
}
client.output.log(
`Connected ${formatProvider(provider)} repository ${chalk.cyan(
repoPath
)}!`
);
}
return 0;
}
return await connectArg({ client, confirm, org, project, repoInfo });
}
async function promptConnectArg({
client,
yes,
repoInfo: repoInfoFromArg,
remoteUrls,
}: PromptConnectArgParams) {
if (Object.keys(remoteUrls).length > 1) {
client.output.log(
'Found multiple Git repositories in your local Git config:'
);
printRemoteUrls(client.output, remoteUrls);
} else {
const url = Object.values(remoteUrls)[0];
const repoInfoFromGitConfig = parseRepoUrl(url);
if (!repoInfoFromGitConfig) {
client.output.error(
`Failed to parse URL "${url}". Please ensure the URL is valid.`
);
return false;
}
if (
JSON.stringify(repoInfoFromGitConfig) === JSON.stringify(repoInfoFromArg)
) {
return true;
}
client.output.log(
`Found a repository in your local Git Config: ${chalk.cyan(
Object.values(remoteUrls)[0]
)}`
);
}
let shouldConnect = yes;
if (!shouldConnect) {
const { url: repoUrlFromArg } = repoInfoFromArg;
shouldConnect = await confirm(
client,
`Do you still want to connect ${link(repoUrlFromArg)}?`,
false
);
if (!shouldConnect) {
client.output.log('Canceled. Repo not connected.');
}
}
return shouldConnect;
}
async function checkExistsAndConnect({
client,
confirm,
org,
project,
gitProviderLink,
provider,
repoPath,
gitOrg,
repo,
}: GitRepoCheckParams) {
if (!gitProviderLink) {
const connect = await connectGitProvider(
client,
@@ -120,14 +349,14 @@ export default async function connect(
const connectedProvider = gitProviderLink.type;
const connectedOrg = gitProviderLink.org;
const connectedRepo = gitProviderLink.repo;
connectedRepoPath = `${connectedOrg}/${connectedRepo}`;
const connectedRepoPath = `${connectedOrg}/${connectedRepo}`;
const isSameRepo =
connectedProvider === provider &&
connectedOrg === gitOrg &&
connectedRepo === repo;
if (isSameRepo) {
output.log(
client.output.log(
`${chalk.cyan(connectedRepoPath)} is already connected to your project.`
);
return 1;
@@ -135,8 +364,8 @@ export default async function connect(
const shouldReplaceRepo = await confirmRepoConnect(
client,
output,
confirm,
connectedProvider,
connectedRepoPath
);
if (!shouldReplaceRepo) {
@@ -155,31 +384,27 @@ export default async function connect(
return connect;
}
}
output.log(
`Connected ${formatProvider(provider)} repository ${chalk.cyan(repoPath)}!`
);
return 0;
}
async function confirmRepoConnect(
client: Client,
output: Output,
yes: boolean,
connectedProvider: string,
connectedRepoPath: string
) {
let shouldReplaceProject = yes;
if (!shouldReplaceProject) {
shouldReplaceProject = await confirm(
client,
`Looks like you already have a repository connected: ${chalk.cyan(
`Looks like you already have a ${formatProvider(
connectedProvider
)} repository connected: ${chalk.cyan(
connectedRepoPath
)}. Do you want to replace it?`,
true
);
if (!shouldReplaceProject) {
output.log(`Aborted. Repo not connected.`);
client.output.log('Canceled. Repo not connected.');
}
}
return shouldReplaceProject;
@@ -187,7 +412,7 @@ async function confirmRepoConnect(
async function selectRemoteUrl(
client: Client,
remoteUrls: { [key: string]: string }
remoteUrls: Dictionary<string>
): Promise<string> {
let choices: ListChoice[] = [];
for (const [urlKey, urlValue] of Object.entries(remoteUrls)) {

View File

@@ -3,7 +3,7 @@ import { Org, Project } from '../../types';
import Client from '../../util/client';
import confirm from '../../util/input/confirm';
import { getCommandName } from '../../util/pkg-name';
import { disconnectGitProvider } from '../../util/projects/connect-git-provider';
import { disconnectGitProvider } from '../../util/git/connect-git-provider';
export default async function disconnect(
client: Client,
@@ -43,7 +43,7 @@ export default async function disconnect(
await disconnectGitProvider(client, org, project.id);
output.log(`Disconnected ${chalk.cyan(`${linkOrg}/${repo}`)}.`);
} else {
output.log('Aborted.');
output.log('Canceled');
}
} else {
output.error(

View File

@@ -16,21 +16,30 @@ const help = () => {
${chalk.dim('Commands:')}
connect Connect your Git config "origin" remote as a Git provider to your project
disconnect Disconnect the Git provider repository from your project
connect [url] Connect your Vercel Project to your Git repository or provide the remote URL to your Git repository
disconnect Disconnect the Git provider repository from your project
${chalk.dim('Options:')}
-h, --help Output usage information
-h, --help Output usage information
-t ${chalk.bold.underline('TOKEN')}, --token=${chalk.bold.underline(
'TOKEN'
)} Login token
)} Login token
-y, --yes Skip questions when setting up new project using default scope and settings
${chalk.dim('Examples:')}
${chalk.gray('')} Connect a Git provider repository
${chalk.gray(
''
)} Connect your Vercel Project to your Git repository defined in your local .git config
${chalk.cyan(`$ ${getPkgName()} git connect`)}
${chalk.gray('')} Connect your Vercel Project to a Git repository using the remote URL
${chalk.cyan(
`$ ${getPkgName()} git connect https://github.com/user/repo.git`
)}
${chalk.gray('')} Disconnect the Git provider repository
@@ -49,7 +58,12 @@ export default async function main(client: Client) {
try {
argv = getArgs(client.argv.slice(2), {
'--confirm': Boolean,
'--yes': Boolean,
'-y': '--yes',
// deprecated
'-c': '--yes',
'--confirm': '--yes',
});
} catch (error) {
handleError(error);
@@ -64,7 +78,7 @@ export default async function main(client: Client) {
argv._ = argv._.slice(1);
subcommand = argv._[0];
const args = argv._.slice(1);
const confirm = Boolean(argv['--confirm']);
const confirm = Boolean(argv['--yes']);
const { output } = client;
let paths = [process.cwd()];

View File

@@ -1,10 +1,8 @@
export default new Map([
['alias', 'alias'],
['aliases', 'alias'],
['billing', 'billing'],
['bisect', 'bisect'],
['build', 'build'],
['cc', 'billing'],
['cert', 'certs'],
['certs', 'certs'],
['deploy', 'deploy'],
@@ -36,6 +34,5 @@ export default new Map([
['switch', 'teams'],
['team', 'teams'],
['teams', 'teams'],
['update', 'update'],
['whoami', 'whoami'],
]);

View File

@@ -53,7 +53,7 @@ export default async function init(
);
if (!chosen) {
output.log('Aborted');
output.log('Canceled');
return 0;
}

View File

@@ -27,7 +27,7 @@ const help = () => {
-p ${chalk.bold.underline('NAME')}, --project=${chalk.bold.underline(
'NAME'
)} Project name
--confirm Confirm default options and skip questions
-y, --yes Skip questions when setting up new project using default scope and settings
${chalk.dim('Examples:')}
@@ -39,7 +39,7 @@ const help = () => {
''
)} Link current directory with default options and skip questions
${chalk.cyan(`$ ${getPkgName()} link --confirm`)}
${chalk.cyan(`$ ${getPkgName()} link --yes`)}
${chalk.gray('')} Link a specific directory to a Vercel Project
@@ -49,9 +49,14 @@ const help = () => {
export default async function main(client: Client) {
const argv = getArgs(client.argv.slice(2), {
'--confirm': Boolean,
'--yes': Boolean,
'-y': '--yes',
'--project': String,
'-p': '--project',
// deprecated
'--confirm': Boolean,
'-c': '--confirm',
});
if (argv['--help']) {
@@ -59,10 +64,15 @@ export default async function main(client: Client) {
return 2;
}
if ('--confirm' in argv) {
client.output.warn('`--confirm` is deprecated, please use `--yes` instead');
argv['--yes'] = argv['--confirm'];
}
const cwd = argv._[1] || process.cwd();
const link = await setupAndLink(client, cwd, {
forceDelete: true,
autoConfirm: argv['--confirm'],
autoConfirm: argv['--yes'],
projectName: argv['--project'],
successEmoji: 'success',
setupMsg: 'Set up',
@@ -73,7 +83,7 @@ export default async function main(client: Client) {
client.output.error(
`Command ${getCommandName(
'link'
)} requires confirmation. Use option ${param('--confirm')} to confirm.`
)} requires confirmation. Use option ${param('--yes')} to confirm.`
);
}
return link.exitCode;

View File

@@ -1,10 +1,10 @@
import chalk from 'chalk';
import ms from 'ms';
import table from 'text-table';
import title from 'title';
import Now from '../util';
import getArgs from '../util/get-args';
import { handleError } from '../util/error';
import cmd from '../util/output/cmd';
import logo from '../util/output/logo';
import elapsed from '../util/output/elapsed';
import strlen from '../util/strlen';
@@ -14,12 +14,13 @@ import { isValidName } from '../util/is-valid-name';
import getCommandFlags from '../util/get-command-flags';
import { getPkgName, getCommandName } from '../util/pkg-name';
import Client from '../util/client';
import { Deployment } from '../types';
import { Deployment } from '@vercel/client';
import validatePaths from '../util/validate-paths';
import { getLinkedProject } from '../util/projects/link';
import { ensureLink } from '../util/ensure-link';
import getScope from '../util/get-scope';
import { isAPIError } from '../util/errors-ts';
import { isErrnoException } from '../util/is-error';
const help = () => {
console.log(`
@@ -35,7 +36,7 @@ const help = () => {
'DIR'
)} Path to the global ${'`.vercel`'} directory
-d, --debug Debug mode [off]
--confirm Skip the confirmation prompt
-y, --yes Skip questions when setting up new project using default scope and settings
-t ${chalk.bold.underline('TOKEN')}, --token=${chalk.bold.underline(
'TOKEN'
)} Login token
@@ -43,6 +44,7 @@ const help = () => {
-m, --meta Filter deployments by metadata (e.g.: ${chalk.dim(
'`-m KEY=value`'
)}). Can appear many times.
--prod Filter for production URLs
-N, --next Show next page of results
${chalk.dim('Examples:')}
@@ -78,7 +80,13 @@ export default async function main(client: Client) {
'-m': '--meta',
'--next': Number,
'-N': '--next',
'--prod': Boolean,
'--yes': Boolean,
'-y': '--yes',
// deprecated
'--confirm': Boolean,
'-c': '--confirm',
});
} catch (err) {
handleError(err);
@@ -87,6 +95,11 @@ export default async function main(client: Client) {
const { output, config } = client;
if ('--confirm' in argv) {
output.warn('`--confirm` is deprecated, please use `--yes` instead');
argv['--yes'] = argv['--confirm'];
}
const { print, log, error, note, debug, spinner } = output;
if (argv._.length > 2) {
@@ -99,10 +112,10 @@ export default async function main(client: Client) {
return 2;
}
const yes = argv['--confirm'] || false;
const yes = !!argv['--yes'];
const prod = argv['--prod'] || false;
const meta = parseMeta(argv['--meta']);
const { includeScheme } = config;
let paths = [process.cwd()];
const pathValidation = await validatePaths(client, paths);
@@ -136,11 +149,25 @@ export default async function main(client: Client) {
if (typeof linkedProject === 'number') {
return linkedProject;
}
link.org = linkedProject.org;
link.project = linkedProject.project;
org = linkedProject.org;
project = linkedProject.project;
app = project.name;
}
let { contextName, team } = await getScope(client);
let contextName;
let team;
try {
({ contextName, team } = await getScope(client));
} catch (err: unknown) {
if (
isErrnoException(err) &&
(err.code === 'NOT_AUTHORIZED' || err.code === 'TEAM_DELETED')
) {
error(err.message);
return 1;
}
}
// If user passed in a custom scope, update the current team & context name
if (argv['--scope']) {
@@ -198,11 +225,15 @@ export default async function main(client: Client) {
debug('Fetching deployments');
const response = await now.list(app, {
version: 6,
meta,
nextTimestamp,
});
const response = await now.list(
app,
{
version: 6,
meta,
nextTimestamp,
},
prod
);
let {
deployments,
@@ -212,6 +243,14 @@ export default async function main(client: Client) {
pagination: { count: number; next: number };
} = response;
let showUsername = false;
for (const deployment of deployments) {
const username = deployment.creator?.username;
if (username !== contextName) {
showUsername = true;
}
}
if (app && !deployments.length) {
debug(
'No deployments: attempting to find deployment that matches supplied app name'
@@ -247,37 +286,34 @@ export default async function main(client: Client) {
}
log(
`Deployments under ${chalk.bold(contextName)} ${elapsed(
Date.now() - start
)}`
`${prod ? `Production deployments` : `Deployments`} for ${chalk.bold(
app
)} under ${chalk.bold(contextName)} ${elapsed(Date.now() - start)}`
);
// information to help the user find other deployments or instances
if (app == null) {
log(
`To list more deployments for a project run ${cmd(
`${getCommandName('ls [project]')}`
)}`
);
}
log(
`To list deployments for a project, run ${getCommandName('ls [project]')}.`
);
print('\n');
const headers = ['Age', 'Deployment', 'Status', 'Duration'];
if (showUsername) headers.push('Username');
client.output.print(
`${table(
[
['project', 'latest deployment', 'state', 'age', 'username'].map(
header => chalk.dim(header)
),
headers.map(header => chalk.bold(chalk.cyan(header))),
...deployments
.sort(sortRecent())
.map(dep => [
[
getProjectName(dep),
chalk.bold((includeScheme ? 'https://' : '') + dep.url),
stateString(dep.state),
chalk.gray(ms(Date.now() - dep.createdAt)),
dep.creator.username,
`https://${dep.url}`,
stateString(dep.state || ''),
chalk.gray(getDeploymentDuration(dep)),
showUsername ? chalk.gray(dep.creator?.username) : '',
],
])
// flatten since the previous step returns a nested
@@ -290,8 +326,8 @@ export default async function main(client: Client) {
),
],
{
align: ['l', 'l', 'r', 'l', 'l'],
hsep: ' '.repeat(4),
align: ['l', 'l', 'l', 'l', 'l'],
hsep: ' '.repeat(5),
stringLength: strlen,
}
).replace(/^/gm, ' ')}\n\n`
@@ -300,34 +336,43 @@ export default async function main(client: Client) {
if (pagination && pagination.count === 20) {
const flags = getCommandFlags(argv, ['_', '--next']);
log(
`To display the next page run ${getCommandName(
`To display the next page, run ${getCommandName(
`ls${app ? ' ' + app : ''}${flags} --next ${pagination.next}`
)}`
);
}
}
function getProjectName(d: Deployment) {
// We group both file and files into a single project
if (d.name === 'file') {
return 'files';
export function getDeploymentDuration(dep: Deployment): string {
if (!dep || !dep.ready || !dep.buildingAt) {
return '?';
}
return d.name;
const duration = ms(dep.ready - dep.buildingAt);
if (duration === '0ms') {
return '--';
}
return duration;
}
// renders the state string
export function stateString(s: string) {
const CIRCLE = '● ';
// make `s` title case
const sTitle = title(s);
switch (s) {
case 'INITIALIZING':
return chalk.yellow(s);
case 'BUILDING':
case 'DEPLOYING':
case 'ANALYZING':
return chalk.yellow(CIRCLE) + sTitle;
case 'ERROR':
return chalk.red(s);
return chalk.red(CIRCLE) + sTitle;
case 'READY':
return s;
return chalk.green(CIRCLE) + sTitle;
case 'QUEUED':
return chalk.white(CIRCLE) + sTitle;
case 'CANCELED':
return chalk.gray(sTitle);
default:
return chalk.gray('UNKNOWN');
}

View File

@@ -1,6 +1,7 @@
import chalk from 'chalk';
import ms from 'ms';
import table from 'text-table';
import { Project } from '../../types';
import Client from '../../util/client';
import getCommandFlags from '../../util/get-command-flags';
import { getCommandName } from '../../util/pkg-name';
@@ -34,10 +35,10 @@ export default async function list(
}
const {
projects: list,
projects: projectList,
pagination,
}: {
projects: [{ name: string; updatedAt: number }];
projects: Project[];
pagination: { count: number; next: number };
} = await client.fetch(projectsUrl, {
method: 'GET',
@@ -48,39 +49,48 @@ export default async function list(
const elapsed = ms(Date.now() - start);
output.log(
`${list.length > 0 ? 'Projects' : 'No projects'} found under ${chalk.bold(
contextName
)} ${chalk.gray(`[${elapsed}]`)}`
`${
projectList.length > 0 ? 'Projects' : 'No projects'
} found under ${chalk.bold(contextName)} ${chalk.gray(`[${elapsed}]`)}`
);
if (list.length > 0) {
const cur = Date.now();
const header = [['', 'name', 'updated'].map(title => chalk.dim(title))];
const out = table(
header.concat(
list.map(secret => [
'',
chalk.bold(secret.name),
chalk.gray(`${ms(cur - secret.updatedAt)} ago`),
])
),
if (projectList.length > 0) {
const tablePrint = table(
[
['Project Name', 'Latest Production URL', 'Updated'].map(header =>
chalk.bold(chalk.cyan(header))
),
...projectList
.map(project => [
[
chalk.bold(project.name),
getLatestProdUrl(project),
chalk.gray(ms(Date.now() - project.updatedAt)),
],
])
.flat(),
],
{
align: ['l', 'l', 'l'],
hsep: ' '.repeat(2),
hsep: ' '.repeat(3),
stringLength: strlen,
}
);
if (out) {
output.print(`\n${out}\n\n`);
}
).replace(/^/gm, ' ');
output.print(`\n${tablePrint}\n\n`);
if (pagination && pagination.count === 20) {
const flags = getCommandFlags(argv, ['_', '--next', '-N', '-d', '-y']);
const nextCmd = `project ls${flags} --next ${pagination.next}`;
output.log(`To display the next page run ${getCommandName(nextCmd)}`);
output.log(`To display the next page, run ${getCommandName(nextCmd)}`);
}
}
return 0;
}
function getLatestProdUrl(project: Project): string {
const alias =
project.alias?.filter(al => al.deployment)?.[0]?.domain ||
project.alias?.[0]?.domain;
if (alias) return 'https://' + alias;
return '--';
}

View File

@@ -38,7 +38,7 @@ const help = () => {
)} Path to the global ${'`.vercel`'} directory
-d, --debug Debug mode [off]
--environment [environment] Deployment environment [development]
-y, --yes Skip the confirmation prompt
-y, --yes Skip questions when setting up new project using default scope and settings
${chalk.dim('Examples:')}

View File

@@ -226,7 +226,7 @@ export default async function main(client: Client) {
).toLowerCase();
if (confirmation !== 'y' && confirmation !== 'yes') {
output.log('Aborted');
output.log('Canceled');
return 1;
}
}

View File

@@ -229,7 +229,7 @@ async function run({ output, contextName, currentTeam, client }) {
argv.yes ||
(await readConfirmation(client, output, theSecret, contextName));
if (!yes) {
output.print(`Aborted. Secret not deleted.\n`);
output.print(`Canceled. Secret not deleted.\n`);
return 0;
}
} else {

View File

@@ -59,7 +59,7 @@ export default async function add(client: Client): Promise<number> {
});
} catch (err: unknown) {
if (isError(err) && err.message === 'USER_ABORT') {
output.log('Aborted');
output.log('Canceled');
return 0;
}

View File

@@ -1,64 +0,0 @@
import chalk from 'chalk';
import cmd from '../util/output/cmd';
import logo from '../util/output/logo';
import handleError from '../util/handle-error';
import getArgs from '../util/get-args';
import Client from '../util/client';
import getUpdateCommand from '../util/get-update-command';
import { getPkgName, getTitleName } from '../util/pkg-name';
const help = () => {
console.log(`
${chalk.bold(`${logo} ${getPkgName()} update`)} [options]
${chalk.dim('Options:')}
-h, --help Output usage information
-d, --debug Debug mode [off]
-c ${chalk.bold.underline('NAME')}, --channel=${chalk.bold.underline(
'NAME'
)} Specify which release channel to install [stable]
-r ${chalk.bold.underline('VERSION')}, --release=${chalk.bold.underline(
'VERSION'
)} Specfic version to install (overrides \`--channel\`)
-y, --yes Skip the confirmation prompt
${chalk.dim('Examples:')}
${chalk.gray('')} Update ${getTitleName()} CLI to the latest "canary" version
${chalk.cyan(`$ ${getPkgName()} update --channel=canary`)}
`);
};
export default async function main(client: Client): Promise<number> {
let argv;
const { output } = client;
try {
argv = getArgs(client.argv.slice(2), {
'--channel': String,
'-c': '--channel',
'--release': String,
'-V': '--release',
'--yes': Boolean,
'-y': '--yes',
});
} catch (err) {
handleError(err);
return 1;
}
if (argv['--help']) {
help();
return 2;
}
output.log(
`Please run ${cmd(
await getUpdateCommand()
)} to update ${getTitleName()} CLI`
);
return 0;
}

View File

@@ -323,14 +323,7 @@ const main = async () => {
client.argv.push('-h');
}
const subcommandsWithoutToken = [
'login',
'logout',
'help',
'init',
'update',
'build',
];
const subcommandsWithoutToken = ['login', 'logout', 'help', 'init', 'build'];
// Prompt for login if there is no current token
if (
@@ -505,9 +498,6 @@ const main = async () => {
case 'alias':
func = require('./commands/alias').default;
break;
case 'billing':
func = require('./commands/billing').default;
break;
case 'bisect':
func = require('./commands/bisect').default;
break;
@@ -571,9 +561,6 @@ const main = async () => {
case 'teams':
func = require('./commands/teams').default;
break;
case 'update':
func = require('./commands/update').default;
break;
case 'whoami':
func = require('./commands/whoami').default;
break;

View File

@@ -30,7 +30,6 @@ export interface GlobalConfig {
'// Note'?: string;
'// Docs'?: string;
currentTeam?: string;
includeScheme?: string;
collectMetrics?: boolean;
api?: string;
@@ -142,6 +141,7 @@ export type Deployment = {
meta: {
[key: string]: any;
};
alias?: string[];
};
export type Alias = {
@@ -211,6 +211,7 @@ export interface ProjectAliasTarget {
configuredBy?: null | 'CNAME' | 'A';
configuredChangedAt?: null | number;
configuredChangeAttempts?: [number, number];
deployment?: Deployment | undefined;
}
export interface Secret {

View File

@@ -1,8 +0,0 @@
{
"VISA": "Visa",
"MASTERCARD": "MasterCard",
"AMERICANEXPRESS": "American Express",
"DINERSCLUB": "Diners Club",
"DISCOVER": "Discover",
"JCB": "JCB"
}

View File

@@ -6,7 +6,7 @@ import mapCertError from '../certs/map-cert-error';
import { Org } from '../../types';
import Now, { CreateOptions } from '..';
import Client from '../client';
import { DeploymentError } from '../../../../client/dist';
import { ArchiveFormat, DeploymentError } from '@vercel/client';
export default async function createDeploy(
client: Client,
@@ -16,10 +16,18 @@ export default async function createDeploy(
createArgs: CreateOptions,
org: Org,
isSettingUpProject: boolean,
cwd?: string
cwd?: string,
archive?: ArchiveFormat
): Promise<any | DeploymentError> {
try {
return await now.create(paths, createArgs, org, isSettingUpProject, cwd);
return await now.create(
paths,
createArgs,
org,
isSettingUpProject,
archive,
cwd
);
} catch (err: unknown) {
if (ERRORS_TS.isAPIError(err)) {
if (err.code === 'rate_limited') {

View File

@@ -2,12 +2,12 @@ import bytes from 'bytes';
import Progress from 'progress';
import chalk from 'chalk';
import {
ArchiveFormat,
createDeployment,
DeploymentOptions,
VercelClientOptions,
} from '@vercel/client';
import { Output } from '../output';
// @ts-ignore
import Now from '../../util';
import { Org } from '../../types';
import ua from '../ua';
@@ -32,6 +32,7 @@ export default async function processDeployment({
cwd,
projectName,
isSettingUpProject,
archive,
skipAutoDetectionConfirmation,
...args
}: {
@@ -48,6 +49,7 @@ export default async function processDeployment({
prebuilt: boolean;
projectName: string;
isSettingUpProject: boolean;
archive?: ArchiveFormat;
skipAutoDetectionConfirmation?: boolean;
cwd?: string;
rootDirectory?: string;
@@ -87,14 +89,13 @@ export default async function processDeployment({
prebuilt,
rootDirectory,
skipAutoDetectionConfirmation,
archive,
};
output.spinner(
isSettingUpProject
? 'Setting up project'
: `Deploying ${chalk.bold(`${org.slug}/${projectName}`)}`,
0
);
const deployingSpinnerVal = isSettingUpProject
? 'Setting up project'
: `Deploying ${chalk.bold(`${org.slug}/${projectName}`)}`;
output.spinner(deployingSpinnerVal, 0);
// collect indications to show the user once
// the deployment is done
@@ -107,12 +108,11 @@ export default async function processDeployment({
}
if (event.type === 'file-count') {
debug(
`Total files ${event.payload.total.size}, ${event.payload.missing.length} changed`
);
const { total, missing, uploads } = event.payload;
debug(`Total files ${total.size}, ${missing.length} changed`);
const missingSize = event.payload.missing
.map((sha: string) => event.payload.total.get(sha).data.length)
const missingSize = missing
.map((sha: string) => total.get(sha).data.length)
.reduce((a: number, b: number) => a + b, 0);
output.stopSpinner();
@@ -123,6 +123,26 @@ export default async function processDeployment({
total: missingSize,
clear: true,
});
bar.tick(0);
uploads.forEach((e: any) =>
e.on('progress', () => {
if (!bar) return;
const totalBytesUploaded = uploads.reduce((acc: number, e: any) => {
return acc + e.bytesUploaded;
}, 0);
// set the current progress bar value
bar.curr = totalBytesUploaded;
// trigger rendering
bar.tick(0);
if (bar.complete) {
output.spinner(deployingSpinnerVal, 0);
}
})
);
}
if (event.type === 'file-uploaded') {
@@ -131,10 +151,6 @@ export default async function processDeployment({
event.payload.file.data.length
)})`
);
if (bar) {
bar.tick(event.payload.file.data.length);
}
}
if (event.type === 'created') {

View File

@@ -0,0 +1,7 @@
import { ArchiveFormat, VALID_ARCHIVE_FORMATS } from '@vercel/client';
const validArchiveFormats = new Set<string>(VALID_ARCHIVE_FORMATS);
export function isValidArchive(archive: string): archive is ArchiveFormat {
return validArchiveFormats.has(archive);
}

View File

@@ -0,0 +1,59 @@
/**
* This function is necessary to account for the difference between
* `?a=` and `?a` because native `url.parse(str, true)` can't tell.
* @param querystring - The querystring to parse, also known as the "search" string.
*/
export function parseQueryString(
querystring?: string
): Record<string, string[]> {
const query: Record<string, string[]> = Object.create(null);
if (!querystring || !querystring.startsWith('?') || querystring === '?') {
return query;
}
const params = querystring.slice(1).split('&');
for (let param of params) {
let [key, value] = param.split('=');
if (key !== undefined) {
key = decodeURIComponent(key);
}
if (value !== undefined) {
value = decodeURIComponent(value);
}
let existing = query[key];
if (!existing) {
existing = [];
query[key] = existing;
}
existing.push(value);
}
return query;
}
/**
* This function is necessary to account for the difference between
* `?a=` and `?a` because native `url.format({ query })` can't tell.
* @param query - The query object to stringify.
*/
export function formatQueryString(
query: Record<string, string[]> | undefined
): string | undefined {
if (!query) {
return undefined;
}
let s = '';
let prefix = '?';
for (let [key, values] of Object.entries(query)) {
for (let value of values) {
s += prefix;
s += encodeURIComponent(key);
if (value !== undefined) {
s += '=';
s += encodeURIComponent(value);
}
prefix = '&';
}
}
return s || undefined;
}

View File

@@ -6,6 +6,7 @@ import DevServer from './server';
import { VercelConfig, HttpHeadersConfig, RouteResult } from './types';
import { isHandler, Route, HandleValue } from '@vercel/routing-utils';
import { parseQueryString } from './parse-query-string';
export function resolveRouteParameters(
str: string,
@@ -56,7 +57,8 @@ export async function devRouter(
phase?: HandleValue | null
): Promise<RouteResult> {
let result: RouteResult | undefined;
let { query, pathname: reqPathname = '/' } = url.parse(reqUrl, true);
let { pathname: reqPathname = '/', search: reqSearch } = url.parse(reqUrl);
const reqQuery = parseQueryString(reqSearch);
const combinedHeaders: HttpHeadersConfig = { ...previousHeaders };
let status: number | undefined;
let isContinue = false;
@@ -174,7 +176,7 @@ export async function devRouter(
isDestUrl,
status: routeConfig.status || status,
headers: combinedHeaders,
uri_args: query,
query: reqQuery,
matched_route: routeConfig,
matched_route_idx: idx,
phase,
@@ -184,17 +186,19 @@ export async function devRouter(
if (!destPath.startsWith('/')) {
destPath = `/${destPath}`;
}
const destParsed = url.parse(destPath, true);
Object.assign(destParsed.query, query);
const { pathname: destPathname = '/', search: destSearch } =
url.parse(destPath);
const destQuery = parseQueryString(destSearch);
Object.assign(destQuery, reqQuery);
result = {
found: true,
dest: destParsed.pathname || '/',
dest: destPathname,
continue: isContinue,
userDest: Boolean(routeConfig.dest),
isDestUrl,
status: routeConfig.status || status,
headers: combinedHeaders,
uri_args: destParsed.query,
query: destQuery,
matched_route: routeConfig,
matched_route_idx: idx,
phase,
@@ -212,7 +216,7 @@ export async function devRouter(
continue: isContinue,
status,
isDestUrl: false,
uri_args: query,
query: reqQuery,
headers: combinedHeaders,
phase,
};

View File

@@ -94,6 +94,7 @@ import { ProjectEnvVariable, ProjectSettings } from '../../types';
import exposeSystemEnvs from './expose-system-envs';
import { treeKill } from '../tree-kill';
import { nodeHeadersToFetchHeaders } from './headers';
import { formatQueryString, parseQueryString } from './parse-query-string';
import {
errorToString,
isErrnoException,
@@ -131,7 +132,6 @@ export default class DevServer {
public output: Output;
public proxy: httpProxy;
public envConfigs: EnvConfigs;
public frameworkSlug?: string;
public files: BuilderInputs;
public address: string;
public devCacheDir: string;
@@ -175,7 +175,6 @@ export default class DevServer {
this.address = '';
this.devCommand = options.devCommand;
this.projectSettings = options.projectSettings;
this.frameworkSlug = options.frameworkSlug;
this.caseSensitive = false;
this.apiDir = null;
this.apiExtensions = new Set();
@@ -1398,9 +1397,11 @@ export default class DevServer {
const getReqUrl = (rr: RouteResult): string | undefined => {
if (rr.dest) {
if (rr.uri_args) {
const destParsed = url.parse(rr.dest, true);
Object.assign(destParsed.query, rr.uri_args);
if (rr.query) {
const destParsed = url.parse(rr.dest);
const destQuery = parseQueryString(destParsed.search);
Object.assign(destQuery, rr.query);
destParsed.search = formatQueryString(destQuery);
return url.format(destParsed);
}
return rr.dest;
@@ -1535,9 +1536,8 @@ export default class DevServer {
// Retain orginal pathname, but override query parameters from the rewrite
const beforeRewriteUrl = req.url || '/';
const rewriteUrlParsed = url.parse(beforeRewriteUrl, true);
delete rewriteUrlParsed.search;
rewriteUrlParsed.query = url.parse(rewritePath, true).query;
const rewriteUrlParsed = url.parse(beforeRewriteUrl);
rewriteUrlParsed.search = url.parse(rewritePath).search;
req.url = url.format(rewriteUrlParsed);
debug(
`Rewrote incoming HTTP URL from "${beforeRewriteUrl}" to "${req.url}"`
@@ -1596,9 +1596,10 @@ export default class DevServer {
if (routeResult.isDestUrl) {
// Mix the `routes` result dest query params into the req path
const destParsed = url.parse(routeResult.dest, true);
delete destParsed.search;
Object.assign(destParsed.query, routeResult.uri_args);
const destParsed = url.parse(routeResult.dest);
const destQuery = parseQueryString(destParsed.search);
Object.assign(destQuery, routeResult.query);
destParsed.search = formatQueryString(destQuery);
const destUrl = url.format(destParsed);
debug(`ProxyPass: ${destUrl}`);
@@ -1739,7 +1740,7 @@ export default class DevServer {
throw new Error('Expected Route Result but none was found.');
}
const { dest, headers, uri_args } = routeResult;
const { dest, query, headers } = routeResult;
// Set any headers defined in the matched `route` config
for (const [name, value] of Object.entries(headers)) {
@@ -1775,10 +1776,11 @@ export default class DevServer {
}
this.setResponseHeaders(res, requestId);
const origUrl = url.parse(req.url || '/', true);
delete origUrl.search;
const origUrl = url.parse(req.url || '/');
const origQuery = parseQueryString(origUrl.search);
origUrl.pathname = dest;
Object.assign(origUrl.query, uri_args);
Object.assign(origQuery, query);
origUrl.search = formatQueryString(origQuery);
req.url = url.format(origUrl);
return proxyPass(req, res, upstream, this, requestId, false);
}
@@ -1800,10 +1802,11 @@ export default class DevServer {
Array.isArray(buildResult.routes) &&
buildResult.routes.length > 0
) {
const origUrl = url.parse(req.url || '/', true);
delete origUrl.search;
const origUrl = url.parse(req.url || '/');
const origQuery = parseQueryString(origUrl.search);
origUrl.pathname = dest;
Object.assign(origUrl.query, uri_args);
Object.assign(origQuery, query);
origUrl.search = formatQueryString(origQuery);
const newUrl = url.format(origUrl);
debug(
`Checking build result's ${buildResult.routes.length} \`routes\` to match ${newUrl}`
@@ -1899,11 +1902,13 @@ export default class DevServer {
);
// Mix in the routing based query parameters
const parsed = url.parse(req.url || '/', true);
Object.assign(parsed.query, uri_args);
const origUrl = url.parse(req.url || '/');
const origQuery = parseQueryString(origUrl.search);
Object.assign(origQuery, query);
origUrl.search = formatQueryString(origQuery);
req.url = url.format({
pathname: parsed.pathname,
query: parsed.query,
pathname: origUrl.pathname,
search: origUrl.search,
});
// Add the Vercel platform proxy request headers
@@ -2019,11 +2024,13 @@ export default class DevServer {
requestId = generateRequestId(this.podId, true);
// Mix the `routes` result dest query params into the req path
const parsed = url.parse(req.url || '/', true);
Object.assign(parsed.query, uri_args);
const origUrl = url.parse(req.url || '/');
const origQuery = parseQueryString(origUrl.search);
Object.assign(origQuery, query);
origUrl.search = formatQueryString(origQuery);
const path = url.format({
pathname: parsed.pathname,
query: parsed.query,
pathname: origUrl.pathname,
search: origUrl.search,
});
const body = await rawBody(req);
@@ -2210,7 +2217,10 @@ export default class DevServer {
// Because of child process 'pipe' below, isTTY will be false.
// Most frameworks use `chalk`/`supports-color` so we enable it anyway.
FORCE_COLOR: process.stdout.isTTY ? '1' : '0',
...(this.frameworkSlug === 'create-react-app' ? { BROWSER: 'none' } : {}),
// Prevent framework dev servers from automatically opening a web
// browser window, since it will not be the port that `vc dev`
// is listening on and thus will be missing Vercel features.
BROWSER: 'none',
...process.env,
...this.envConfigs.allEnv,
PORT: `${port}`,

View File

@@ -24,7 +24,6 @@ export { VercelConfig };
export interface DevServerOptions {
output: Output;
devCommand?: string;
frameworkSlug?: string;
projectSettings?: ProjectSettings;
systemEnvValues?: string[];
projectEnvs?: ProjectEnvVariable[];
@@ -146,8 +145,8 @@ export interface RouteResult {
status?: number;
// "headers": <object of the added response header values>
headers: HttpHeadersConfig;
// "uri_args": <object (key=value) list of new uri args to be passed along to dest >
uri_args?: { [key: string]: any };
// "query": <object (key=values) of new uri args to be passed along to dest>
query?: Record<string, string[]>;
// "matched_route": <object of the route spec that matched>
matched_route?: Route;
// "matched_route_idx": <integer of the index of the route matched>

View File

@@ -9,6 +9,7 @@ type LinkResult = {
org: Org;
project: Project;
};
export async function ensureLink(
commandName: string,
client: Client,

View File

@@ -152,9 +152,7 @@ export class SourceNotFound extends NowError<'SOURCE_NOT_FOUND', {}> {
super({
code: 'SOURCE_NOT_FOUND',
meta: {},
message: `Not able to purchase. Please add a payment method using ${getCommandName(
`billing add`
)}.`,
message: `Not able to purchase. Please add a payment method using the dashboard.`,
});
}
}
@@ -428,7 +426,7 @@ export class UserAborted extends NowError<'USER_ABORTED', {}> {
super({
code: 'USER_ABORTED',
meta: {},
message: `The user aborted the operation.`,
message: `The user canceled the operation.`,
});
}
}

View File

@@ -4,13 +4,22 @@ import { Org } from '../../types';
import chalk from 'chalk';
import link from '../output/link';
import { isAPIError } from '../errors-ts';
import { Output } from '../output';
import { Dictionary } from '@vercel/client';
export interface RepoInfo {
url: string;
provider: string;
org: string;
repo: string;
}
export async function disconnectGitProvider(
client: Client,
org: Org,
projectId: string
) {
const fetchUrl = `/v4/projects/${projectId}/link?${stringify({
const fetchUrl = `/v9/projects/${projectId}/link?${stringify({
teamId: org.type === 'team' ? org.id : undefined,
})}`;
return client.fetch(fetchUrl, {
@@ -28,7 +37,7 @@ export async function connectGitProvider(
type: string,
repo: string
) {
const fetchUrl = `/v4/projects/${projectId}/link?${stringify({
const fetchUrl = `/v9/projects/${projectId}/link?${stringify({
teamId: org.type === 'team' ? org.id : undefined,
})}`;
try {
@@ -43,22 +52,21 @@ export async function connectGitProvider(
}),
});
} catch (err: unknown) {
if (isAPIError(err)) {
if (
err.meta?.action === 'Install GitHub App' ||
err.code === 'repo_not_found'
) {
client.output.error(
`Failed to link ${chalk.cyan(
repo
)}. Make sure there aren't any typos and that you have access to the repository if it's private.`
);
} else if (err.action === 'Add a Login Connection') {
client.output.error(
err.message.replace(repo, chalk.cyan(repo)) +
`\nVisit ${link(err.link)} for more information.`
);
}
const apiError = isAPIError(err);
if (
apiError &&
(err.action === 'Install GitHub App' || err.code === 'repo_not_found')
) {
client.output.error(
`Failed to connect ${chalk.cyan(
repo
)} to project. Make sure there aren't any typos and that you have access to the repository if it's private.`
);
} else if (apiError && err.action === 'Add a Login Connection') {
client.output.error(
err.message.replace(repo, chalk.cyan(repo)) +
`\nVisit ${link(err.link)} for more information.`
);
} else {
client.output.error(
`Failed to connect the ${formatProvider(
@@ -83,15 +91,13 @@ export function formatProvider(type: string): string {
}
}
export function parseRepoUrl(originUrl: string): {
provider: string;
org: string;
repo: string;
} | null {
export function parseRepoUrl(originUrl: string): RepoInfo | null {
const isSSH = originUrl.startsWith('git@');
// Matches all characters between (// or @) and (.com or .org)
// eslint-disable-next-line prefer-named-capture-group
const provider = /(?<=(\/\/|@)).*(?=(\.com|\.org))/.exec(originUrl);
const provider =
/(?<=(\/\/|@)).*(?=(\.com|\.org))/.exec(originUrl)?.[0] ||
originUrl.replace('www.', '').split('.')[0];
if (!provider) {
return null;
}
@@ -104,8 +110,8 @@ export function parseRepoUrl(originUrl: string): {
repo = originUrl.split('/')[1]?.replace('.git', '');
} else {
// Assume https:// or git://
org = originUrl.split('/')[3];
repo = originUrl.split('/')[4]?.replace('.git', '');
org = originUrl.replace('//', '').split('/')[1];
repo = originUrl.replace('//', '').split('/')[2]?.replace('.git', '');
}
if (!org || !repo) {
@@ -113,8 +119,17 @@ export function parseRepoUrl(originUrl: string): {
}
return {
provider: provider[0],
url: originUrl,
provider,
org,
repo,
};
}
export function printRemoteUrls(
output: Output,
remoteUrls: Dictionary<string>
) {
for (const [name, url] of Object.entries(remoteUrls)) {
output.print(`${name}: ${chalk.cyan(url)}\n`);
}
}

View File

@@ -39,7 +39,7 @@ export default function handleError(error: unknown, { debug = false } = {}) {
} else if (status === 500) {
console.error(errorOutput('Unexpected server error. Please retry.'));
} else if (code === 'USER_ABORT') {
info('Aborted');
info('Canceled');
} else {
console.error(
errorOutput(`Unexpected error. Please try again later. (${message})`)

View File

@@ -17,7 +17,7 @@ import printIndications from './print-indications';
import { GitMetadata, Org } from '../types';
import { VercelConfig } from './dev/types';
import Client, { FetchOptions, isJSONObject } from './client';
import { Dictionary } from '@vercel/client';
import { ArchiveFormat, Dictionary } from '@vercel/client';
export interface NowOptions {
client: Client;
@@ -131,6 +131,7 @@ export default class Now extends EventEmitter {
}: CreateOptions,
org: Org,
isSettingUpProject: boolean,
archive?: ArchiveFormat,
cwd?: string
) {
let hashes: any = {};
@@ -168,6 +169,7 @@ export default class Now extends EventEmitter {
org,
projectName: name,
isSettingUpProject,
archive,
skipAutoDetectionConfirmation,
cwd,
prebuilt,
@@ -330,7 +332,8 @@ export default class Now extends EventEmitter {
async list(
app?: string,
{ version = 4, meta = {}, nextTimestamp }: ListOptions = {}
{ version = 4, meta = {}, nextTimestamp }: ListOptions = {},
prod?: boolean
) {
const fetchRetry = async (url: string, options: FetchOptions = {}) => {
return this.retry(
@@ -393,6 +396,9 @@ export default class Now extends EventEmitter {
if (nextTimestamp) {
query.set('until', String(nextTimestamp));
}
if (prod) {
query.set('target', 'production');
}
const response = await fetchRetry(`/v${version}/now/deployments?${query}`);
return response;

View File

@@ -21,7 +21,7 @@ interface ListOptions {
choices: ListChoice[];
pageSize?: number;
separator?: boolean;
abort?: 'start' | 'end';
cancel?: 'start' | 'end';
eraseFinalAnswer?: boolean;
}
@@ -50,7 +50,7 @@ export default async function list(
],
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`,
cancel = 'end', // Whether the `cancel` 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> {
@@ -97,17 +97,17 @@ export default async function list(
}
}
const abortSeparator = new inquirer.Separator('─'.repeat(biggestLength));
const _abort = {
name: 'Abort',
const cancelSeparator = new inquirer.Separator('─'.repeat(biggestLength));
const _cancel = {
name: 'Cancel',
value: '',
short: '',
};
if (abort === 'start') {
choices.unshift(_abort, abortSeparator);
if (cancel === 'start') {
choices.unshift(_cancel, cancelSeparator);
} else {
choices.push(abortSeparator, _abort);
choices.push(cancelSeparator, _cancel);
}
const answer = await client.prompt({

View File

@@ -87,7 +87,7 @@ export default async function setupAndLink(
));
if (!shouldStartSetup) {
output.print(`Aborted. Project not set up.\n`);
output.print(`Canceled. Project not set up.\n`);
return { status: 'not_linked', org: null, project: null };
}
@@ -241,6 +241,7 @@ export default async function setupAndLink(
}
const project = await createProject(client, newProjectName);
await updateProject(client, project.id, settings);
Object.assign(project, settings);

View File

@@ -7,7 +7,7 @@ export default async function createProject(
) {
const project = await client.fetch<Project>('/v1/projects', {
method: 'POST',
body: JSON.stringify({ name: projectName }),
body: { name: projectName },
});
return project;
}

View File

@@ -1,5 +1,5 @@
import Client from '../client';
import { ProjectSettings } from '../../types';
import type { JSONObject, ProjectSettings } from '../../types';
interface ProjectSettingsResponse extends ProjectSettings {
id: string;
@@ -13,11 +13,14 @@ export default async function updateProject(
prjNameOrId: string,
settings: ProjectSettings
) {
// `ProjectSettings` is technically compatible with JSONObject
const body = settings as JSONObject;
const res = await client.fetch<ProjectSettingsResponse>(
`/v2/projects/${encodeURIComponent(prjNameOrId)}`,
{
method: 'PATCH',
body: JSON.stringify(settings),
body,
}
);
return res;

View File

@@ -1,3 +1,5 @@
import stripAnsi from 'strip-ansi';
export default function strlen(str: string) {
return str.replace(/\u001b[^m]*m/g, '').length;
return stripAnsi(str).length;
}

View File

@@ -94,7 +94,7 @@ export default async function validatePaths(
);
if (!shouldDeployHomeDirectory) {
output.print(`Aborted\n`);
output.print(`Canceled\n`);
return { valid: false, exitCode: 0 };
}
}

View File

@@ -0,0 +1,13 @@
import mod from '../increment.wasm?module';
export const config = { runtime: 'experimental-edge' };
const init$ = WebAssembly.instantiate(mod);
/** @param {Request} req */
export default async req => {
const givenNumber = Number(new URL(req.url).searchParams.get('number') || 0);
const { exports } = await init$;
const added = exports.add_one(givenNumber);
return new Response(`${givenNumber} + 1 = ${added}`);
};

Binary file not shown.

View File

@@ -0,0 +1,7 @@
node_modules
.DS_Store
dist
dist-ssr
*.local
.vercel
!public

View File

@@ -0,0 +1,13 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" href="/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite App</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.js"></script>
</body>
</html>

View File

@@ -0,0 +1,16 @@
{
"private": true,
"scripts": {
"dev": "vite --port $PORT",
"build": "vite build",
"serve": "vite preview"
},
"dependencies": {
"vue": "3.2.37"
},
"devDependencies": {
"@vitejs/plugin-vue": "1.10.2",
"@vue/compiler-sfc": "3.2.37",
"vite": "2.9.14"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

View File

@@ -0,0 +1,22 @@
<template>
<img alt="Vue logo" src="./assets/logo.png" />
<HelloWorld msg="Hello Vue 3 + Vite" />
</template>
<script setup>
import HelloWorld from './components/HelloWorld.vue'
// This starter template is using Vue 3 experimental <script setup> SFCs
// Check out https://github.com/vuejs/rfcs/blob/script-setup-2/active-rfcs/0000-script-setup.md
</script>
<style>
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
</style>

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

View File

@@ -0,0 +1,33 @@
<template>
<h1>{{ msg }}</h1>
<p>
<a href="https://vitejs.dev/guide/features.html" target="_blank">
Vite Documentation
</a>
|
<a href="https://v3.vuejs.org/" target="_blank">Vue 3 Documentation</a>
</p>
<button type="button" @click="state.count++">count is: {{ state.count }}</button>
<p>
Edit
<code>components/HelloWorld.vue</code> to test hot module replacement.
</p>
</template>
<script setup>
import { defineProps, reactive } from 'vue'
defineProps({
msg: String
})
const state = reactive({ count: 0 })
</script>
<style scoped>
a {
color: #42b983;
}
</style>

View File

@@ -0,0 +1,4 @@
import { createApp } from 'vue';
import App from './App.vue';
createApp(App).mount('#app');

View File

@@ -0,0 +1,7 @@
import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';
// https://vitejs.dev/config/
export default defineConfig({
plugins: [vue()],
});

View File

@@ -0,0 +1,359 @@
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
# yarn lockfile v1
"@babel/parser@^7.16.4":
version "7.18.11"
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.18.11.tgz#68bb07ab3d380affa9a3f96728df07969645d2d9"
integrity sha512-9JKn5vN+hDt0Hdqn1PiJ2guflwP+B6Ga8qbDuoF0PzzVhrzsKIJo8yGqVk6CmMHiMei9w1C1Bp9IMJSIK+HPIQ==
"@esbuild/linux-loong64@0.14.54":
version "0.14.54"
resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.14.54.tgz#de2a4be678bd4d0d1ffbb86e6de779cde5999028"
integrity sha512-bZBrLAIX1kpWelV0XemxBZllyRmM6vgFQQG2GdNb+r3Fkp0FOh1NJSvekXDs7jq70k4euu1cryLMfU+mTXlEpw==
"@vitejs/plugin-vue@1.10.2":
version "1.10.2"
resolved "https://registry.yarnpkg.com/@vitejs/plugin-vue/-/plugin-vue-1.10.2.tgz#d718479e2789d8a94b63e00f23f1898ba239253a"
integrity sha512-/QJ0Z9qfhAFtKRY+r57ziY4BSbGUTGsPRMpB/Ron3QPwBZM4OZAZHdTa4a8PafCwU5DTatXG8TMDoP8z+oDqJw==
"@vue/compiler-core@3.2.37":
version "3.2.37"
resolved "https://registry.yarnpkg.com/@vue/compiler-core/-/compiler-core-3.2.37.tgz#b3c42e04c0e0f2c496ff1784e543fbefe91e215a"
integrity sha512-81KhEjo7YAOh0vQJoSmAD68wLfYqJvoiD4ulyedzF+OEk/bk6/hx3fTNVfuzugIIaTrOx4PGx6pAiBRe5e9Zmg==
dependencies:
"@babel/parser" "^7.16.4"
"@vue/shared" "3.2.37"
estree-walker "^2.0.2"
source-map "^0.6.1"
"@vue/compiler-dom@3.2.37":
version "3.2.37"
resolved "https://registry.yarnpkg.com/@vue/compiler-dom/-/compiler-dom-3.2.37.tgz#10d2427a789e7c707c872da9d678c82a0c6582b5"
integrity sha512-yxJLH167fucHKxaqXpYk7x8z7mMEnXOw3G2q62FTkmsvNxu4FQSu5+3UMb+L7fjKa26DEzhrmCxAgFLLIzVfqQ==
dependencies:
"@vue/compiler-core" "3.2.37"
"@vue/shared" "3.2.37"
"@vue/compiler-sfc@3.2.37":
version "3.2.37"
resolved "https://registry.yarnpkg.com/@vue/compiler-sfc/-/compiler-sfc-3.2.37.tgz#3103af3da2f40286edcd85ea495dcb35bc7f5ff4"
integrity sha512-+7i/2+9LYlpqDv+KTtWhOZH+pa8/HnX/905MdVmAcI/mPQOBwkHHIzrsEsucyOIZQYMkXUiTkmZq5am/NyXKkg==
dependencies:
"@babel/parser" "^7.16.4"
"@vue/compiler-core" "3.2.37"
"@vue/compiler-dom" "3.2.37"
"@vue/compiler-ssr" "3.2.37"
"@vue/reactivity-transform" "3.2.37"
"@vue/shared" "3.2.37"
estree-walker "^2.0.2"
magic-string "^0.25.7"
postcss "^8.1.10"
source-map "^0.6.1"
"@vue/compiler-ssr@3.2.37":
version "3.2.37"
resolved "https://registry.yarnpkg.com/@vue/compiler-ssr/-/compiler-ssr-3.2.37.tgz#4899d19f3a5fafd61524a9d1aee8eb0505313cff"
integrity sha512-7mQJD7HdXxQjktmsWp/J67lThEIcxLemz1Vb5I6rYJHR5vI+lON3nPGOH3ubmbvYGt8xEUaAr1j7/tIFWiEOqw==
dependencies:
"@vue/compiler-dom" "3.2.37"
"@vue/shared" "3.2.37"
"@vue/reactivity-transform@3.2.37":
version "3.2.37"
resolved "https://registry.yarnpkg.com/@vue/reactivity-transform/-/reactivity-transform-3.2.37.tgz#0caa47c4344df4ae59f5a05dde2a8758829f8eca"
integrity sha512-IWopkKEb+8qpu/1eMKVeXrK0NLw9HicGviJzhJDEyfxTR9e1WtpnnbYkJWurX6WwoFP0sz10xQg8yL8lgskAZg==
dependencies:
"@babel/parser" "^7.16.4"
"@vue/compiler-core" "3.2.37"
"@vue/shared" "3.2.37"
estree-walker "^2.0.2"
magic-string "^0.25.7"
"@vue/reactivity@3.2.37":
version "3.2.37"
resolved "https://registry.yarnpkg.com/@vue/reactivity/-/reactivity-3.2.37.tgz#5bc3847ac58828e2b78526e08219e0a1089f8848"
integrity sha512-/7WRafBOshOc6m3F7plwzPeCu/RCVv9uMpOwa/5PiY1Zz+WLVRWiy0MYKwmg19KBdGtFWsmZ4cD+LOdVPcs52A==
dependencies:
"@vue/shared" "3.2.37"
"@vue/runtime-core@3.2.37":
version "3.2.37"
resolved "https://registry.yarnpkg.com/@vue/runtime-core/-/runtime-core-3.2.37.tgz#7ba7c54bb56e5d70edfc2f05766e1ca8519966e3"
integrity sha512-JPcd9kFyEdXLl/i0ClS7lwgcs0QpUAWj+SKX2ZC3ANKi1U4DOtiEr6cRqFXsPwY5u1L9fAjkinIdB8Rz3FoYNQ==
dependencies:
"@vue/reactivity" "3.2.37"
"@vue/shared" "3.2.37"
"@vue/runtime-dom@3.2.37":
version "3.2.37"
resolved "https://registry.yarnpkg.com/@vue/runtime-dom/-/runtime-dom-3.2.37.tgz#002bdc8228fa63949317756fb1e92cdd3f9f4bbd"
integrity sha512-HimKdh9BepShW6YozwRKAYjYQWg9mQn63RGEiSswMbW+ssIht1MILYlVGkAGGQbkhSh31PCdoUcfiu4apXJoPw==
dependencies:
"@vue/runtime-core" "3.2.37"
"@vue/shared" "3.2.37"
csstype "^2.6.8"
"@vue/server-renderer@3.2.37":
version "3.2.37"
resolved "https://registry.yarnpkg.com/@vue/server-renderer/-/server-renderer-3.2.37.tgz#840a29c8dcc29bddd9b5f5ffa22b95c0e72afdfc"
integrity sha512-kLITEJvaYgZQ2h47hIzPh2K3jG8c1zCVbp/o/bzQOyvzaKiCquKS7AaioPI28GNxIsE/zSx+EwWYsNxDCX95MA==
dependencies:
"@vue/compiler-ssr" "3.2.37"
"@vue/shared" "3.2.37"
"@vue/shared@3.2.37":
version "3.2.37"
resolved "https://registry.yarnpkg.com/@vue/shared/-/shared-3.2.37.tgz#8e6adc3f2759af52f0e85863dfb0b711ecc5c702"
integrity sha512-4rSJemR2NQIo9Klm1vabqWjD8rs/ZaJSzMxkMNeJS6lHiUjjUeYFbooN19NgFjztubEKh3WlZUeOLVdbbUWHsw==
csstype@^2.6.8:
version "2.6.17"
resolved "https://registry.yarnpkg.com/csstype/-/csstype-2.6.17.tgz#4cf30eb87e1d1a005d8b6510f95292413f6a1c0e"
integrity sha512-u1wmTI1jJGzCJzWndZo8mk4wnPTZd1eOIYTYvuEyOQGfmDl3TrabCCfKnOC86FZwW/9djqTl933UF/cS425i9A==
esbuild-android-64@0.14.54:
version "0.14.54"
resolved "https://registry.yarnpkg.com/esbuild-android-64/-/esbuild-android-64-0.14.54.tgz#505f41832884313bbaffb27704b8bcaa2d8616be"
integrity sha512-Tz2++Aqqz0rJ7kYBfz+iqyE3QMycD4vk7LBRyWaAVFgFtQ/O8EJOnVmTOiDWYZ/uYzB4kvP+bqejYdVKzE5lAQ==
esbuild-android-arm64@0.14.54:
version "0.14.54"
resolved "https://registry.yarnpkg.com/esbuild-android-arm64/-/esbuild-android-arm64-0.14.54.tgz#8ce69d7caba49646e009968fe5754a21a9871771"
integrity sha512-F9E+/QDi9sSkLaClO8SOV6etqPd+5DgJje1F9lOWoNncDdOBL2YF59IhsWATSt0TLZbYCf3pNlTHvVV5VfHdvg==
esbuild-darwin-64@0.14.54:
version "0.14.54"
resolved "https://registry.yarnpkg.com/esbuild-darwin-64/-/esbuild-darwin-64-0.14.54.tgz#24ba67b9a8cb890a3c08d9018f887cc221cdda25"
integrity sha512-jtdKWV3nBviOd5v4hOpkVmpxsBy90CGzebpbO9beiqUYVMBtSc0AL9zGftFuBon7PNDcdvNCEuQqw2x0wP9yug==
esbuild-darwin-arm64@0.14.54:
version "0.14.54"
resolved "https://registry.yarnpkg.com/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.14.54.tgz#3f7cdb78888ee05e488d250a2bdaab1fa671bf73"
integrity sha512-OPafJHD2oUPyvJMrsCvDGkRrVCar5aVyHfWGQzY1dWnzErjrDuSETxwA2HSsyg2jORLY8yBfzc1MIpUkXlctmw==
esbuild-freebsd-64@0.14.54:
version "0.14.54"
resolved "https://registry.yarnpkg.com/esbuild-freebsd-64/-/esbuild-freebsd-64-0.14.54.tgz#09250f997a56ed4650f3e1979c905ffc40bbe94d"
integrity sha512-OKwd4gmwHqOTp4mOGZKe/XUlbDJ4Q9TjX0hMPIDBUWWu/kwhBAudJdBoxnjNf9ocIB6GN6CPowYpR/hRCbSYAg==
esbuild-freebsd-arm64@0.14.54:
version "0.14.54"
resolved "https://registry.yarnpkg.com/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.14.54.tgz#bafb46ed04fc5f97cbdb016d86947a79579f8e48"
integrity sha512-sFwueGr7OvIFiQT6WeG0jRLjkjdqWWSrfbVwZp8iMP+8UHEHRBvlaxL6IuKNDwAozNUmbb8nIMXa7oAOARGs1Q==
esbuild-linux-32@0.14.54:
version "0.14.54"
resolved "https://registry.yarnpkg.com/esbuild-linux-32/-/esbuild-linux-32-0.14.54.tgz#e2a8c4a8efdc355405325033fcebeb941f781fe5"
integrity sha512-1ZuY+JDI//WmklKlBgJnglpUL1owm2OX+8E1syCD6UAxcMM/XoWd76OHSjl/0MR0LisSAXDqgjT3uJqT67O3qw==
esbuild-linux-64@0.14.54:
version "0.14.54"
resolved "https://registry.yarnpkg.com/esbuild-linux-64/-/esbuild-linux-64-0.14.54.tgz#de5fdba1c95666cf72369f52b40b03be71226652"
integrity sha512-EgjAgH5HwTbtNsTqQOXWApBaPVdDn7XcK+/PtJwZLT1UmpLoznPd8c5CxqsH2dQK3j05YsB3L17T8vE7cp4cCg==
esbuild-linux-arm64@0.14.54:
version "0.14.54"
resolved "https://registry.yarnpkg.com/esbuild-linux-arm64/-/esbuild-linux-arm64-0.14.54.tgz#dae4cd42ae9787468b6a5c158da4c84e83b0ce8b"
integrity sha512-WL71L+0Rwv+Gv/HTmxTEmpv0UgmxYa5ftZILVi2QmZBgX3q7+tDeOQNqGtdXSdsL8TQi1vIaVFHUPDe0O0kdig==
esbuild-linux-arm@0.14.54:
version "0.14.54"
resolved "https://registry.yarnpkg.com/esbuild-linux-arm/-/esbuild-linux-arm-0.14.54.tgz#a2c1dff6d0f21dbe8fc6998a122675533ddfcd59"
integrity sha512-qqz/SjemQhVMTnvcLGoLOdFpCYbz4v4fUo+TfsWG+1aOu70/80RV6bgNpR2JCrppV2moUQkww+6bWxXRL9YMGw==
esbuild-linux-mips64le@0.14.54:
version "0.14.54"
resolved "https://registry.yarnpkg.com/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.14.54.tgz#d9918e9e4cb972f8d6dae8e8655bf9ee131eda34"
integrity sha512-qTHGQB8D1etd0u1+sB6p0ikLKRVuCWhYQhAHRPkO+OF3I/iSlTKNNS0Lh2Oc0g0UFGguaFZZiPJdJey3AGpAlw==
esbuild-linux-ppc64le@0.14.54:
version "0.14.54"
resolved "https://registry.yarnpkg.com/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.14.54.tgz#3f9a0f6d41073fb1a640680845c7de52995f137e"
integrity sha512-j3OMlzHiqwZBDPRCDFKcx595XVfOfOnv68Ax3U4UKZ3MTYQB5Yz3X1mn5GnodEVYzhtZgxEBidLWeIs8FDSfrQ==
esbuild-linux-riscv64@0.14.54:
version "0.14.54"
resolved "https://registry.yarnpkg.com/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.14.54.tgz#618853c028178a61837bc799d2013d4695e451c8"
integrity sha512-y7Vt7Wl9dkOGZjxQZnDAqqn+XOqFD7IMWiewY5SPlNlzMX39ocPQlOaoxvT4FllA5viyV26/QzHtvTjVNOxHZg==
esbuild-linux-s390x@0.14.54:
version "0.14.54"
resolved "https://registry.yarnpkg.com/esbuild-linux-s390x/-/esbuild-linux-s390x-0.14.54.tgz#d1885c4c5a76bbb5a0fe182e2c8c60eb9e29f2a6"
integrity sha512-zaHpW9dziAsi7lRcyV4r8dhfG1qBidQWUXweUjnw+lliChJqQr+6XD71K41oEIC3Mx1KStovEmlzm+MkGZHnHA==
esbuild-netbsd-64@0.14.54:
version "0.14.54"
resolved "https://registry.yarnpkg.com/esbuild-netbsd-64/-/esbuild-netbsd-64-0.14.54.tgz#69ae917a2ff241b7df1dbf22baf04bd330349e81"
integrity sha512-PR01lmIMnfJTgeU9VJTDY9ZerDWVFIUzAtJuDHwwceppW7cQWjBBqP48NdeRtoP04/AtO9a7w3viI+PIDr6d+w==
esbuild-openbsd-64@0.14.54:
version "0.14.54"
resolved "https://registry.yarnpkg.com/esbuild-openbsd-64/-/esbuild-openbsd-64-0.14.54.tgz#db4c8495287a350a6790de22edea247a57c5d47b"
integrity sha512-Qyk7ikT2o7Wu76UsvvDS5q0amJvmRzDyVlL0qf5VLsLchjCa1+IAvd8kTBgUxD7VBUUVgItLkk609ZHUc1oCaw==
esbuild-sunos-64@0.14.54:
version "0.14.54"
resolved "https://registry.yarnpkg.com/esbuild-sunos-64/-/esbuild-sunos-64-0.14.54.tgz#54287ee3da73d3844b721c21bc80c1dc7e1bf7da"
integrity sha512-28GZ24KmMSeKi5ueWzMcco6EBHStL3B6ubM7M51RmPwXQGLe0teBGJocmWhgwccA1GeFXqxzILIxXpHbl9Q/Kw==
esbuild-windows-32@0.14.54:
version "0.14.54"
resolved "https://registry.yarnpkg.com/esbuild-windows-32/-/esbuild-windows-32-0.14.54.tgz#f8aaf9a5667630b40f0fb3aa37bf01bbd340ce31"
integrity sha512-T+rdZW19ql9MjS7pixmZYVObd9G7kcaZo+sETqNH4RCkuuYSuv9AGHUVnPoP9hhuE1WM1ZimHz1CIBHBboLU7w==
esbuild-windows-64@0.14.54:
version "0.14.54"
resolved "https://registry.yarnpkg.com/esbuild-windows-64/-/esbuild-windows-64-0.14.54.tgz#bf54b51bd3e9b0f1886ffdb224a4176031ea0af4"
integrity sha512-AoHTRBUuYwXtZhjXZbA1pGfTo8cJo3vZIcWGLiUcTNgHpJJMC1rVA44ZereBHMJtotyN71S8Qw0npiCIkW96cQ==
esbuild-windows-arm64@0.14.54:
version "0.14.54"
resolved "https://registry.yarnpkg.com/esbuild-windows-arm64/-/esbuild-windows-arm64-0.14.54.tgz#937d15675a15e4b0e4fafdbaa3a01a776a2be982"
integrity sha512-M0kuUvXhot1zOISQGXwWn6YtS+Y/1RT9WrVIOywZnJHo3jCDyewAc79aKNQWFCQm+xNHVTq9h8dZKvygoXQQRg==
esbuild@^0.14.27:
version "0.14.54"
resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.14.54.tgz#8b44dcf2b0f1a66fc22459943dccf477535e9aa2"
integrity sha512-Cy9llcy8DvET5uznocPyqL3BFRrFXSVqbgpMJ9Wz8oVjZlh/zUSNbPRbov0VX7VxN2JH1Oa0uNxZ7eLRb62pJA==
optionalDependencies:
"@esbuild/linux-loong64" "0.14.54"
esbuild-android-64 "0.14.54"
esbuild-android-arm64 "0.14.54"
esbuild-darwin-64 "0.14.54"
esbuild-darwin-arm64 "0.14.54"
esbuild-freebsd-64 "0.14.54"
esbuild-freebsd-arm64 "0.14.54"
esbuild-linux-32 "0.14.54"
esbuild-linux-64 "0.14.54"
esbuild-linux-arm "0.14.54"
esbuild-linux-arm64 "0.14.54"
esbuild-linux-mips64le "0.14.54"
esbuild-linux-ppc64le "0.14.54"
esbuild-linux-riscv64 "0.14.54"
esbuild-linux-s390x "0.14.54"
esbuild-netbsd-64 "0.14.54"
esbuild-openbsd-64 "0.14.54"
esbuild-sunos-64 "0.14.54"
esbuild-windows-32 "0.14.54"
esbuild-windows-64 "0.14.54"
esbuild-windows-arm64 "0.14.54"
estree-walker@^2.0.2:
version "2.0.2"
resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-2.0.2.tgz#52f010178c2a4c117a7757cfe942adb7d2da4cac"
integrity sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==
fsevents@~2.3.2:
version "2.3.2"
resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a"
integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==
function-bind@^1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d"
integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==
has@^1.0.3:
version "1.0.3"
resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796"
integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==
dependencies:
function-bind "^1.1.1"
is-core-module@^2.9.0:
version "2.10.0"
resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.10.0.tgz#9012ede0a91c69587e647514e1d5277019e728ed"
integrity sha512-Erxj2n/LDAZ7H8WNJXd9tw38GYM3dv8rk8Zcs+jJuxYTW7sozH+SS8NtrSjVL1/vpLvWi1hxy96IzjJ3EHTJJg==
dependencies:
has "^1.0.3"
magic-string@^0.25.7:
version "0.25.7"
resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.25.7.tgz#3f497d6fd34c669c6798dcb821f2ef31f5445051"
integrity sha512-4CrMT5DOHTDk4HYDlzmwu4FVCcIYI8gauveasrdCu2IKIFOJ3f0v/8MDGJCDL9oD2ppz/Av1b0Nj345H9M+XIA==
dependencies:
sourcemap-codec "^1.4.4"
nanoid@^3.3.4:
version "3.3.4"
resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.4.tgz#730b67e3cd09e2deacf03c027c81c9d9dbc5e8ab"
integrity sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==
path-parse@^1.0.7:
version "1.0.7"
resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735"
integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==
picocolors@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c"
integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==
postcss@^8.1.10, postcss@^8.4.13:
version "8.4.16"
resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.16.tgz#33a1d675fac39941f5f445db0de4db2b6e01d43c"
integrity sha512-ipHE1XBvKzm5xI7hiHCZJCSugxvsdq2mPnsq5+UF+VHCjiBvtDrlxJfMBToWaP9D5XlgNmcFGqoHmUn0EYEaRQ==
dependencies:
nanoid "^3.3.4"
picocolors "^1.0.0"
source-map-js "^1.0.2"
resolve@^1.22.0:
version "1.22.1"
resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.1.tgz#27cb2ebb53f91abb49470a928bba7558066ac177"
integrity sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==
dependencies:
is-core-module "^2.9.0"
path-parse "^1.0.7"
supports-preserve-symlinks-flag "^1.0.0"
rollup@^2.59.0:
version "2.77.2"
resolved "https://registry.yarnpkg.com/rollup/-/rollup-2.77.2.tgz#6b6075c55f9cc2040a5912e6e062151e42e2c4e3"
integrity sha512-m/4YzYgLcpMQbxX3NmAqDvwLATZzxt8bIegO78FZLl+lAgKJBd1DRAOeEiZcKOIOPjxE6ewHWHNgGEalFXuz1g==
optionalDependencies:
fsevents "~2.3.2"
source-map-js@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.0.2.tgz#adbc361d9c62df380125e7f161f71c826f1e490c"
integrity sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==
source-map@^0.6.1:
version "0.6.1"
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263"
integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==
sourcemap-codec@^1.4.4:
version "1.4.8"
resolved "https://registry.yarnpkg.com/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz#ea804bd94857402e6992d05a38ef1ae35a9ab4c4"
integrity sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==
supports-preserve-symlinks-flag@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09"
integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==
vite@2.9.14:
version "2.9.14"
resolved "https://registry.yarnpkg.com/vite/-/vite-2.9.14.tgz#c438324c6594afd1050df3777da981dee988bb1b"
integrity sha512-P/UCjSpSMcE54r4mPak55hWAZPlyfS369svib/gpmz8/01L822lMPOJ/RYW6tLCe1RPvMvOsJ17erf55bKp4Hw==
dependencies:
esbuild "^0.14.27"
postcss "^8.4.13"
resolve "^1.22.0"
rollup "^2.59.0"
optionalDependencies:
fsevents "~2.3.2"
vue@3.2.37:
version "3.2.37"
resolved "https://registry.yarnpkg.com/vue/-/vue-3.2.37.tgz#da220ccb618d78579d25b06c7c21498ca4e5452e"
integrity sha512-bOKEZxrm8Eh+fveCqS1/NkG/n6aMidsI6hahas7pa0w/l7jkbssJVsRhVDs07IdDq7h9KHswZOgItnwJAgtVtQ==
dependencies:
"@vue/compiler-dom" "3.2.37"
"@vue/compiler-sfc" "3.2.37"
"@vue/runtime-dom" "3.2.37"
"@vue/server-renderer" "3.2.37"
"@vue/shared" "3.2.37"

View File

@@ -53,6 +53,33 @@ test('[vercel dev] should support edge functions', async () => {
}
});
test('[vercel dev] edge functions support WebAssembly files', async () => {
const dir = fixture('edge-function');
const { dev, port, readyResolver } = await testFixture(dir, {
env: {
ENV_VAR_IN_EDGE: '1',
},
});
try {
await readyResolver;
for (const { number, result } of [
{ number: 1, result: 2 },
{ number: 2, result: 3 },
{ number: 12, result: 13 },
]) {
let res = await fetch(
`http://localhost:${port}/api/webassembly?number=${number}`
);
validateResponseHeaders(res);
await expect(res.text()).resolves.toEqual(`${number} + 1 = ${result}`);
}
} finally {
await dev.kill('SIGTERM');
}
});
test(
'[vercel dev] edge functions respond properly the same as production',
testFixtureStdio('edge-function', async (testPath: any) => {
@@ -1045,3 +1072,20 @@ test('[vercel dev] validate rewrites', async () => {
/Invalid vercel\.json - `rewrites\[0\].destination` should be string/m
);
});
test(
'[vercel dev] should correctly proxy to vite dev',
testFixtureStdio(
'vite-dev',
async (testPath: any) => {
const url = '/src/App.vue?vue&type=style&index=0&lang.css';
// The first request should return the HTML template
await testPath(200, url, /<template>/gm);
// The second request should return the HMR JS
await testPath(200, url, /__vite__createHotContext/gm);
// Home page should always return HTML
await testPath(200, '/', /<title>Vite App<\/title>/gm);
},
{ skipDeploy: true }
)
);

View File

@@ -305,7 +305,7 @@ function testFixtureStdio(
? ['--scope', process.env.VERCEL_TEAM_ID]
: []),
'link',
'--confirm',
'--yes',
],
{ cwd, stdio: 'pipe', reject: false }
);

View File

@@ -0,0 +1 @@
!.vercel

View File

@@ -0,0 +1,4 @@
{
"orgId": "team_dummy",
"projectId": "archive"
}

View File

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

View File

@@ -0,0 +1,3 @@
body {
background-color: red;
}

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