Compare commits

...

117 Commits

Author SHA1 Message Date
Nathan Rajlich
726d3924ae Publish Canary
- vercel@21.2.1-canary.0
 - @vercel/python@1.2.5-canary.0
2021-01-26 18:31:12 -08:00
Nathan Rajlich
d3e6c2d335 [python] Fix request URL path encoding for basic, WSGI and ASGI interfaces (#5739)
`@vercel/python` has been incorrectly "unquoting" the incoming request
URL for all Python interfaces (basic, WSGI, ASGI). The proper behavior is
to pass along the request URL to the serverless function handler unmodified.

This is proven by the additional test that is added in this PR, which actually
fires up the corresponding Python server via i.e. `python3 api/asgi.py`,
and executes the test probes defined in the
`test/fixtures/00-request-path/vercel.json` file. Adding this test ensures
that the request URL behavior of `@vercel/python` matches the behavior
as compared to executing via `python` directly.
2021-01-26 12:37:15 -08:00
Nathan Rajlich
f9ed84a5c9 [cli] Pass the output object to vercel switch command (#5756)
Fixes error: `Cannot read property 'spinner' of undefined`
2021-01-26 12:34:08 -08:00
Nathan Rajlich
88a815b5f0 Publish Stable
- vercel@21.2.0
2021-01-25 14:44:41 -08:00
Steven
9f49743ea4 Publish Stable
- @vercel/build-utils@2.8.0
2021-01-22 18:53:38 -05:00
Steven
0ac3ae19c0 Publish Canary
- @vercel/build-utils@2.7.1-canary.2
 - vercel@21.1.1-canary.3
 - @vercel/client@9.0.6-canary.2
2021-01-22 18:48:09 -05:00
Steven
e7920fd783 [build-utils] Fix warning message (#5741) 2021-01-22 18:47:33 -05:00
Steven
b4c13470df Publish Canary
- @vercel/build-utils@2.7.1-canary.1
 - vercel@21.1.1-canary.2
 - @vercel/client@9.0.6-canary.1
2021-01-22 11:59:09 -05:00
Steven
2a797b77b9 [build-utils] Add support for config.nodeVersion (#5729)
### 📋 Checklist

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

#### Tests

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

#### Code Review

- [x] This PR has a concise title and thorough description useful to a reviewer
- [x] Issue from task tracker has a link to this PR
2021-01-22 11:57:55 -05:00
Nathan Rajlich
85a34126df Publish Canary
- vercel@21.1.1-canary.1
2021-01-21 22:48:10 -08:00
Nathan Rajlich
92889c5376 [cli] Re-use a single Output instance throughout the codebase (#5722)
Previously every command implementation was invoking `createOutput()` individually, causing multiple instances of `Output` to be created, which was unnecessary, and causes issues with the shared `spinner` instance introduced by #5717.

Now, the `Output` instance created in `src/index.js` is passed down to the sub-command being executed and therefore re-used.
2021-01-21 19:57:08 -08:00
Nathan Rajlich
66a7fa30b8 [cli] Show login prompt for vercel dev (#5718)
`vercel dev` requires the user to be logged in ever since the project needs to be queried from the database.

**Before:**

<img width="682" alt="Screen Shot 2021-01-18 at 7 58 56 PM" src="https://user-images.githubusercontent.com/71256/104986545-14aac800-59c8-11eb-8302-836a5481ce5f.png">

**After:**

<img width="497" alt="Screen Shot 2021-01-18 at 7 59 01 PM" src="https://user-images.githubusercontent.com/71256/104986547-15dbf500-59c8-11eb-85f6-d0ad628c8e8d.png">
2021-01-21 19:56:35 -08:00
Nathan Rajlich
a7ba405503 [cli] Make Output class a singleton and have reusable Spinner (#5717)
This is a refactor of the `ora` Spinner usage to make the preferred usage be via the `output.spinner()` and `output.stopSpinner()` functions.

* The `Output` instance has a local spinner instance that can be updated via multiple calls to `output.spinner()`.
* `output.print()` and friends call `output.stopSpinner()` implicitly, so `output.stopSpinner()` doesn't need to be called unless it is desired to immediately remove the spinner before rendering the next text.
* Because there's meant to be a shared `spinner` for the `output` instance, it means that the same `output` instance needs to be used throughout the CLI lifecycle. Therefore, the `createOutput()` function now returns a singleton so that it's guaranteed to be the same instance.

https://user-images.githubusercontent.com/71256/105009979-0670a200-59f0-11eb-9853-232f92eae536.mov

https://user-images.githubusercontent.com/71256/105010034-17211800-59f0-11eb-918d-b15f457e9640.mov

In the "before" video above, note how there's a brief second where the spinner is cleared, but the deployment URL has not yet been printed. In the "after" video, this is fixed and the spinner is only cleared once the CLI is ready to render the deployment URL.
2021-01-19 11:38:29 -08:00
luc
84145245ba Publish Canary
- @vercel/build-utils@2.7.1-canary.0
 - vercel@21.1.1-canary.0
 - @vercel/client@9.0.6-canary.0
2021-01-19 09:29:20 +01:00
Luc Leray
90d2e8b63b [cli] Hide "Development" for secret env variables (#5710) 2021-01-19 09:27:42 +01:00
Nathan Rajlich
0d31fe8018 [cli] Fix cleanUrls: true when using outputDirectory (#5701)
This is essentially a refactor of the `@vercel/static` builder that
simplifies the code a lot and fixes a bug where `cleanUrls: true`
was not working correctly when using an `outputDirectory`.
2021-01-18 12:03:34 -08:00
Mark Glagola
37d747c241 [cli] Add auto renew prompt to domains buy (#5663)
Add auto renew prompt to `domains buy`

```diff
 > The domain "example.com" is available to buy under <username>!
 > Buy now for $20 (1yr)? [y|N] 
+ > Auto renew yearly for $20? [Y|n] 
```

### 📋 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
2021-01-15 19:13:37 +00:00
Nathan Rajlich
3f052d905f [build-utils] Remove now-next and now-static-build tests (#5695)
These directories are no longer in this repository, so the tests were failing.
2021-01-14 13:50:19 +00:00
Nathan Rajlich
0b60467d2f Publish Stable
- @vercel/build-utils@2.7.0
 - vercel@21.1.0
 - @vercel/client@9.0.5
 - @vercel/go@1.1.7
 - @vercel/node-bridge@1.3.2
 - @vercel/node@1.9.0
 - @vercel/python@1.2.4
 - @vercel/routing-utils@1.9.2
 - @vercel/ruby@1.2.5
2021-01-13 17:18:43 -08:00
Nathan Rajlich
4384a6104f Publish Canary
- vercel@21.0.2-canary.10
2021-01-13 11:42:54 -08:00
Nathan Rajlich
f40f95ff37 [cli] Respect the directoryListing Project setting in vc dev (#5690)
Implements the ["Directory Listing" Project setting](https://vercel.com/changelog/listing-the-content-of-directories-can-now-be-toggled) in `vercel dev`.

The Dev integration test file has been updated to allow specifying custom Project settings that will be `PATCH`'d to the project document before running `vc deploy` / `vc dev`.
2021-01-13 15:36:12 +00:00
Nathan Rajlich
90c05250b0 Publish Canary
- vercel@21.0.2-canary.9
 - @vercel/go@1.1.7-canary.1
 - @vercel/node@1.8.6-canary.5
2021-01-12 17:47:24 -08:00
Nathan Rajlich
030880fe74 [node][go] Fix order of exit code in startDevServer() error reason (#5688) 2021-01-12 17:02:55 -08:00
Nathan Rajlich
02bc88f33b [cli] Use cmd() formatting for "npm not installed" message (#5687)
* [cli] Use `cmd()` formatting for "npm not installed" message

* Fix build
2021-01-12 17:02:36 -08:00
Nathan Rajlich
38ff557cad [cli] Skip "00-list-directory" and "temporary directory listing" tests temporarily (#5689)
* [cli] Skip "now-dev-directory-listing" test temporarily

* Disable correct tests

* Fix `vc env` test
2021-01-12 17:01:56 -08:00
Nathan Rajlich
47c34842d5 Publish Canary
- vercel@21.0.2-canary.8
 - @vercel/go@1.1.7-canary.0
 - @vercel/node@1.8.6-canary.4
 - @vercel/python@1.2.4-canary.0
 - @vercel/ruby@1.2.5-canary.0
2021-01-12 14:51:30 -08:00
Nathan Rajlich
d21b215ad0 [cli] Add error link when startDevServer() throws, and make npm not installed formatting consistent (#5684) 2021-01-12 14:47:11 -08:00
Nathan Rajlich
5827737fd5 [node][go] Better startDevServer() error message (#5674)
* [go] Better `startDevServer()` error message

* [node] Better `startDevServer()` error message

Co-authored-by: Steven <steven@ceriously.com>
2021-01-11 15:55:58 -08:00
Steven
a9e078d410 [all] Rename init files to vc_ prefix (#5675)
These files show up in error logs, so its confusing to see the [legacy now terminology](https://vercel.com/blog/zeit-is-now-vercel).
2021-01-11 20:38:03 +00:00
Lee Robinson
b4d9b17fb8 Update to latest Create Next App example. (#5655) 2021-01-10 19:52:18 -06:00
Steven
02c55bf634 [cli] Remove legacy example from vc ls --help (#5651)
`vc ls --help` was still listing an example using `--all` which was removed with Now 1.0 in PR #5011.

This PR removes that legacy example.
2021-01-07 20:28:48 +00:00
Nathan Rajlich
08af15055f Publish Canary
- vercel@21.0.2-canary.7
2021-01-06 18:33:16 -08:00
Nathan Rajlich
0597aaa5e4 [cli] Print better message upon ENOENT error when invoking startDevServer() (#5643)
When `spawn()` emits an "ENOENT" error, meaning that the command was not
found, the error would only be visible if `--debug` was enabled for vc
dev.

Now we will always render any error from `startDevServer()`, as well as
special-case the "ENOENT" error informing the user to double check their
installation of the command.
2021-01-06 13:48:55 -08:00
Nathan Rajlich
176856a1ea Stop publishing legacy @now scoped packages (#5641)
These `@now` scoped packages have been published and deprecated with the following warning since May 2020:

```
DEPRECATED ⚠️  - "@now/node" is deprecated and will stop receiving updates on December 31, 2020. Please use "@vercel/node" instead.
```

Since it is now 2021, the time has come to stop publishing.

### Related Issues

[[ch3378]]
2021-01-06 01:59:25 +00:00
Nathan Rajlich
375a55ebed [next][redwood][static-build] Remove frontend runtimes (#5640) 2021-01-05 14:11:55 -08:00
JJ Kasper
7d33a05581 Publish Stable
- @vercel/next@2.7.8
2021-01-05 09:58:19 -06:00
JJ Kasper
0377c8b737 [next] Add tests for optional catch-all fallback true page params (#5247)
This adds tests for optional catch-all `fallback: true` pages to ensure the params are normalized correctly. These should be failing initially until the PR with the normalizing fix in Next.js is released to canary. 

x-ref: https://github.com/vercel/next.js/issues/17220
2021-01-05 04:59:16 +00:00
Nathan Rajlich
662d546388 Publish Canary
- @vercel/node-bridge@1.3.2-canary.1
2021-01-04 16:37:52 -08:00
Nathan Rajlich
8c514b5608 [node-bridge] Publish launcher.* files to npm (#5636) 2021-01-04 16:37:22 -08:00
Nathan Rajlich
a6ec53d9d3 Publish Canary
- vercel@21.0.2-canary.6
 - @vercel/node-bridge@1.3.2-canary.0
 - @vercel/node@1.8.6-canary.3
 - @vercel/static-build@0.18.1-canary.1
 - @vercel/redwood@0.2.1-canary.0
2021-01-04 16:27:14 -08:00
Nathan Rajlich
3ad5903f70 [node-bridge] Move launcher.ts to @vercel/node-bridge (#5634)
The `launcher.ts` file is more closely aligned to the node bridge package, as it's used by other runtimes as well.
2021-01-04 16:25:27 -08:00
Nathan Rajlich
3cf155e999 [tests] Retry fetch upon "ECONNREFUSED" (#5635)
CI failed due to this error code, which can be retried.
2021-01-04 16:06:28 -08:00
JJ Kasper
d9a298d97c Publish Canary
- @vercel/next@2.7.8-canary.0
2021-01-04 15:15:21 -06:00
JJ Kasper
487d3c8554 [next] Ensure 404 route is correct for GSP/GIP 404 (#5632)
This is a follow-up to https://github.com/vercel/vercel/pull/5618 ensuring the 404 route is pointing to the static 404 output correctly when `_app.gip` and getStaticProps in `/404.js` is used

### Related Issues

Fixes: https://github.com/vercel/next.js/issues/19849

#### 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
2021-01-04 20:53:29 +00:00
JJ Kasper
9d0bfd3656 Publish Stable
- @vercel/next@2.7.7
2021-01-02 12:24:18 -06:00
JJ Kasper
ec75333569 [next] Fix next test files lint (#5619) 2020-12-31 17:10:52 -06:00
JJ Kasper
51aab912a2 Publish Canary
- @vercel/next@2.7.7-canary.2
2020-12-31 16:01:02 -06:00
JJ Kasper
670b2653c0 [next] Ensure 404 is static with _app.GIP and 404 GSP (#5618) 2020-12-31 15:47:18 -06:00
JJ Kasper
f71686fdad Publish Canary
- vercel@21.0.2-canary.5
 - @vercel/next@2.7.7-canary.1
 - @vercel/node@1.8.6-canary.2
2020-12-29 18:26:39 -06:00
Joe Haddad
ec9c8ce150 [next] share cache between nft runs (#5614)
This should result in faster Lambda tracing, as it avoids duplicate work.
2020-12-29 19:04:47 -05:00
Steven
a2048fc6d3 [node][next] Bump nft to 0.9.5 (#5581)
This PR bumps nft from version 0.9.4 to [0.9.5](https://github.com/vercel/nft/releases/tag/0.9.5).
2020-12-28 12:23:03 -05:00
Steven
09ff9cda9f [docs] Fix error message when credentials are missing (#5590) 2020-12-21 12:12:32 -08:00
Nathan Rajlich
3a4d6f7848 Publish Canary
- vercel@21.0.2-canary.4
 - @vercel/node@1.8.6-canary.1
2020-12-21 10:11:14 -08:00
Andy
9a0d676c0d Remove "Add a new framework" CONTRIBUTING.md (#5583) 2020-12-18 12:34:50 -08:00
Nathan Rajlich
25cd7b9e6e [cli] Fix vc logs -o raw and add test (#5571)
There are events (i.e. with `type: "deployment-event"`) that do not
contain a `text` property, so check that it's a string before invoking
`.replace()` on it.

Fixes this stack trace:

```
Error! An unexpected error occurred in logs: TypeError: Cannot read property 'replace' of undefined
    at Object.printLogRaw [as raw] (/home/me/nextjs-site/node_modules/vercel/dist/index.js:203803:10)
    at printEvent (/home/me/nextjs-site/node_modules/vercel/dist/index.js:203688:35)
    at Array.forEach (<anonymous>)
    at main (/home/me/nextjs-site/node_modules/vercel/dist/index.js:203690:31)
    at processTicksAndRejections (internal/process/task_queues.js:93:5)
    at main (/home/me/nextjs-site/node_modules/vercel/dist/index.js:207173:16)
```
2020-12-16 16:16:36 -08:00
Steven
f926d5516c [examples] Update gatsby to use @vercel/node (#5567)
[`@now/node`](https://www.npmjs.com/package/@now/node) is deprecated in favor of [`@vercel/node`](https://www.npmjs.com/package/@vercel/node)
2020-12-14 18:29:10 -05:00
Nathan Rajlich
4603383850 [node] Add Vercel prefixed TypeScript types (#5568)
The `Now` prefixed types remain as aliases for backwards-compat purposes.

#### Tests

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

#### Code Review

- [x] This PR has a concise title and thorough description useful to a reviewer
2020-12-14 18:26:22 -05:00
Nathan Rajlich
c0c57889c8 Publish Canary
- @vercel/build-utils@2.6.1-canary.2
 - vercel@21.0.2-canary.3
 - @vercel/client@9.0.5-canary.2
 - @vercel/next@2.7.7-canary.0
 - @vercel/static-build@0.18.1-canary.0
2020-12-14 09:59:43 -08:00
Nathan Rajlich
85908a0524 [build-utils][static-build][next] Force YARN_NODE_LINKER="node-modules" env var when executing yarn (#5552)
### Related Issues

* https://vercel.com/knowledge/does-vercel-support-yarn-2
* https://github.com/vercel/vercel/discussions/4223
* https://github.com/vercel/vercel/discussions/4910
* https://github.com/vercel/vercel/issues/5136
* https://github.com/vercel/vercel/issues/5280

### 📋 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
2020-12-14 04:27:59 +00:00
Andy Bitz
503b9a2429 Publish Canary
- @vercel/build-utils@2.6.1-canary.1
 - vercel@21.0.2-canary.2
 - @vercel/client@9.0.5-canary.1
2020-12-11 19:02:19 +01:00
Andy
eac8f32ae7 [cli][build-utils] Change type to handle builds without a src property (#5553)
* [cli][build-utils] Change type to handle builds without a `src` property

* Add test

* Update unit tests

* Update test

* Undo test changes
2020-12-11 19:00:49 +01:00
dependabot[bot]
3f76fefde6 Bump ini from 1.3.5 to 1.3.7 (#5550)
Bumps [ini](https://github.com/isaacs/ini) from 1.3.5 to 1.3.7.
<details>
<summary>Commits</summary>
<ul>
<li><a href="c74c8af35f"><code>c74c8af</code></a> 1.3.7</li>
<li><a href="024b8b55ac"><code>024b8b5</code></a> update deps, add linting</li>
<li><a href="032fbaf5f0"><code>032fbaf</code></a> Use Object.create(null) to avoid default object property hazards</li>
<li><a href="2da90391ef"><code>2da9039</code></a> 1.3.6</li>
<li><a href="cfea636f53"><code>cfea636</code></a> better git push script, before publish instead of after</li>
<li><a href="56d2805e07"><code>56d2805</code></a> do not allow invalid hazardous string as section name</li>
<li>See full diff in <a href="https://github.com/isaacs/ini/compare/v1.3.5...v1.3.7">compare view</a></li>
</ul>
</details>
<details>
<summary>Maintainer changes</summary>
<p>This version was pushed to npm by <a href="https://www.npmjs.com/~isaacs">isaacs</a>, a new releaser for ini since your current version.</p>
</details>
<br />


[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=ini&package-manager=npm_and_yarn&previous-version=1.3.5&new-version=1.3.7)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
- `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
- `@dependabot use these labels` will set the current labels as the default for future PRs for this repo and language
- `@dependabot use these reviewers` will set the current reviewers as the default for future PRs for this repo and language
- `@dependabot use these assignees` will set the current assignees as the default for future PRs for this repo and language
- `@dependabot use this milestone` will set the current milestone as the default for future PRs for this repo and language

You can disable automated security fix PRs for this repo from the [Security Alerts page](https://github.com/vercel/vercel/network/alerts).

</details>
2020-12-10 22:09:11 +00:00
Steven
79ddc5746b Publish Canary
- @vercel/routing-utils@1.9.2-canary.4
2020-12-10 15:14:12 -05:00
Javi Velasco
9a14615b43 [routing-utils] More accurate schema (#5477)
This PR completes the routes schema to be more accurate for the provided type.
This will allow us to generate the type dynamically from the JSON Schema
2020-12-10 20:10:57 +00:00
JJ Kasper
6d6ccbdc25 [tests] Add retrying for GitHub actions connection error (#5533)
Follow-up to https://github.com/vercel/vercel/pull/5526 this also adds retrying for fetch connection issues in GitHub Actions to prevent false test failures.

x-ref: https://github.com/vercel/vercel/runs/1518604380
2020-12-09 21:11:01 +00:00
JJ Kasper
704424ec58 Publish Stable
- @vercel/next@2.7.6
2020-12-08 10:49:48 -06:00
JJ Kasper
c471127c69 Publish Canary
- @vercel/next@2.7.6-canary.1
2020-12-08 10:33:13 -06:00
JJ Kasper
c02dc9ac49 [next] Add public dir check and update tests (#5526)
* Add public dir check and update tests

* De-dupe test checks a bit
2020-12-08 10:25:55 -05:00
JJ Kasper
dd10e8cc77 Publish Canary
- @vercel/next@2.7.6-canary.0
 - @vercel/routing-utils@1.9.2-canary.3
2020-12-01 16:11:08 -06:00
Connor Davis
cf69c2398c [next] Add Important Headers to Avoid Users Overriding Cache Headers (#5452)
We want to ensure users cannot override Next.js' cache-control headers

### Related Issues

https://app.clubhouse.io/vercel/story/14921/add-important-headers-to-routes
https://app.clubhouse.io/vercel/story/15528/add-important-to-cache-control-headers-in-next-js

#### Tests

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

#### Code Review

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


Fixes: https://github.com/vercel/vercel/pull/5452
2020-12-01 22:08:50 +00:00
JJ Kasper
e48707571f Publish Stable
- @vercel/next@2.7.5
2020-11-30 11:06:59 -06:00
JJ Kasper
6051fe6f0c Publish Canary
- @vercel/next@2.7.5-canary.1
2020-11-30 11:00:43 -06:00
JJ Kasper
2b3ba8a14f [next] Make sure to use outputDirectory when loading prerenderManifest (#5487)
* Make sure to use outputDirectory when loading prerenderManifest

* Add test case
2020-11-30 10:57:56 -06:00
Steven
25bea3f83e Publish Canary
- vercel@21.0.2-canary.1
 - @vercel/next@2.7.5-canary.0
 - @vercel/node@1.8.6-canary.0
2020-11-24 15:24:52 -05:00
Steven
b3e1828ebe [node][next] Bump nft to 0.9.4 (#5455)
This PR bumps nft to [0.9.4](https://github.com/vercel/nft/releases/tag/0.9.4)
2020-11-24 14:32:46 -05:00
Andy Bitz
e0e2a8e87e Publish Canary
- @vercel/routing-utils@1.9.2-canary.2
2020-11-24 19:52:19 +01:00
Andy
37ec89796d [routing-utils] Handle null phase at the beginning (#5470)
* [routing-utils] Handle `null` phase at the beginning

* Add fixed list

* Add tests

* Change hit and miss order

* Put null at the start

* Undo order changes

Co-authored-by: Steven <steven@ceriously.com>
2020-11-24 19:51:06 +01:00
JJ Kasper
d3e2a4c4db Publish Stable
- @vercel/next@2.7.4
2020-11-24 11:14:03 -06:00
JJ Kasper
ece3645914 Publish Canary
- vercel@21.0.2-canary.0
 - @vercel/next@2.7.4-canary.0
2020-11-24 10:53:50 -06:00
JJ Kasper
fee386493b [next] Add handle: resource to ensure 404 page (#5454)
This adds `handle: resource` to ensure the 404 page is shown instead of a directory listing when there isn't an index route for an output directory. This also removes the `.well-known` route added in https://github.com/vercel/vercel/pull/5447 in favor of adding it in Next.js itself here https://github.com/vercel/next.js/pull/19364 and revalidate tests wait times are increased in this PR due to [this thread](https://vercel.slack.com/archives/C9MMK4674/p1605821738156200)

x-ref: https://github.com/vercel/next.js/pull/19364
Fixes: https://github.com/vercel/next.js/issues/16555
2020-11-24 16:38:43 +00:00
Luc Leray
d95ed184ab [tests] Add test for secrets decryption in vc env pull and vc dev (#5461)
* add test for env with decryptable secret

* Apply suggestions from code review

Co-authored-by: Nathan Rajlich <nathan@tootallnate.net>

Co-authored-by: Nathan Rajlich <nathan@tootallnate.net>
2020-11-23 10:38:29 +01:00
luc
aef8e6388e Publish Stable
- vercel@21.0.1
2020-11-23 00:48:10 +01:00
luc
ba43e88603 Publish Canary
- vercel@21.0.1-canary.0
2020-11-23 00:39:59 +01:00
Igor Klopov
e61f9740c4 [cli] Fix secrets decryption when running vc env pull and vc dev (#5460) 2020-11-23 00:39:17 +01:00
luc
c4b010fe8b Publish Stable
- vercel@21.0.0
2020-11-20 23:10:15 +01:00
luc
db18eb091f Publish Canary
- vercel@20.1.5-canary.6
2020-11-20 17:33:55 +01:00
Luc Leray
360e62d172 [cli] Download automatically exposed System Environment Variables when running vercel env pull (#5451)
* fix VERCEL_REGION and NOW_REGION

* remove VERCEL_REGION from exposeSystemEnvs

* refactor exposeSystemEnvs

* refactor getDecryptedEnvRecords

* consider project envs in exposeSystemEnvs

* simplify exposeSystemEnvs

* correctly set value of VERCEL_URL system envs

* make exposeSystemEnvs return all envs object

* parse url in server

* simplify

* refactor getDecryptedEnvRecords

* add comment

* remove unnecessary code

* add test

* fix test

* fix dev server unit tests

* always expose NOW_REGION

* fix dev test

* fix dev test

* only retrieve system env values when autoExposeSystemEnvs is true
2020-11-20 17:33:16 +01:00
JJ Kasper
8c3cd0332d Publish Stable
- @vercel/next@2.7.3
2020-11-19 14:47:12 -06:00
JJ Kasper
f5f276021e Publish Canary
- @vercel/next@2.7.3-canary.0
2020-11-19 14:30:56 -06:00
JJ Kasper
9fbec823f3 [next] Ensure public files are matched with i18n domains (#5447)
This makes sure we strip all locales during the `handle: 'miss'` phase to allow locale domains to match public files correctly. This also updates the trailing slash redirect handling to match the behavior for `.well-known` added in https://github.com/vercel/vercel/pull/5407

We aren't able to add tests for locale domains with the current test flow yet since they require domains be assigned although the changes in this PR was tested manually against http://i18n-support.vercel.app/ with `@ijjk/now-next@0.1.3-i18n`

x-ref: https://github.com/vercel/vercel/pull/5407
Fixes: https://github.com/vercel/next.js/issues/19324
2020-11-19 20:24:46 +00:00
luc
18c3dd3a63 Publish Canary
- vercel@20.1.5-canary.5
2020-11-19 16:01:27 +01:00
Ana Trajkovska
5a4a20b33f Remove code which removes auto-generated secrets (#5440) 2020-11-19 16:00:57 +01:00
luc
4489ed0c85 Publish Canary
- vercel@20.1.5-canary.4
2020-11-18 16:11:28 +01:00
Luc Leray
359f23daf1 [cli] Expose VERCEL_REGION when autoExposeSystemEnvs is true (#5437)
* auto expose VERCEL_REGION

* add test
2020-11-18 16:00:20 +01:00
luc
4ef92e85db Publish Canary
- vercel@20.1.5-canary.3
2020-11-18 00:27:15 +01:00
Naoyuki Kanezawa
659c4d6ccd [cli] Expose system envs when autoExposeSystemEnvs is enabled on vc dev (#5434)
Ref: https://app.clubhouse.io/vercel/story/15112

We added a property called `autoExposeSystemEnvs` to projects. If that property is `true`, we automatically expose system env variables such as `VERCEL=1`, `VERCEL_ENV=<production | preview>`, ... to the runtime and build time.

This PR makes sure we mirror this behavior when running `vc dev` locally.

### 📋 Checklist

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

#### Tests

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

#### Code Review

- [x] This PR has a concise title and thorough description useful to a reviewer
- [x] Issue from task tracker has a link to this PR
2020-11-17 23:21:40 +00:00
JJ Kasper
e93d477df8 Publish Stable
- @vercel/next@2.7.2
2020-11-17 14:49:04 -06:00
JJ Kasper
f64625655b Publish Canary
- @vercel/next@2.7.2-canary.0
2020-11-17 14:11:32 -06:00
JJ Kasper
25a8189997 [next] Correct non-dynamic index SSG data route output (#5435)
Co-authored-by: Joe Haddad <joe.haddad@zeit.co>
2020-11-17 15:09:56 -05:00
Andy Bitz
25c3e627cf Publish Canary
- @vercel/build-utils@2.6.1-canary.0
 - vercel@20.1.5-canary.2
 - @vercel/client@9.0.5-canary.0
2020-11-17 18:13:30 +01:00
Andy
1d6d8b530f [build-utils] Remove continue: true from 404 route (#5432)
* [build-utils] Remove `continue: true` from 404 route

* Update tests
2020-11-17 18:08:29 +01:00
Steven
e821cc0ae7 [cli] Add tests to compare dev/prd image optimization (#5428)
Add E2E test for to test image optimization against `vc dev` as well as a prod deployment.

### 📋 Checklist

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

#### Tests

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

#### Code Review

- [x] This PR has a concise title and thorough description useful to a reviewer
- [x] Issue from task tracker has a link to this PR (CH13104)
2020-11-16 20:20:27 +00:00
JJ Kasper
8ecbdc5d03 Publish Stable
- @vercel/next@2.7.1
2020-11-14 16:48:35 -06:00
JJ Kasper
895224985b Publish Canary
- @vercel/next@2.7.1-canary.0
2020-11-14 16:23:24 -06:00
JJ Kasper
0f42a63c03 [next] Ensure correct route order for i18n + custom-routes (#5421)
Co-authored-by: Steven <steven@ceriously.com>
2020-11-14 17:13:08 -05:00
dav-is
81e4c9e6fe Publish Canary
- @vercel/routing-utils@1.9.2-canary.1
2020-11-13 13:48:28 -05:00
Connor Davis
a0a29dc836 [routing-utils] Exclude /.well-known from trailing slash redirect (#5407)
`/.well-known` files shouldn't have trailing slashes added as these clients likely do not follow redirects

Specifically, `/.well-known/apple-developer-merchantid-domain-association` cannot have trailing slash.

### Related Issues

https://vercel.slack.com/archives/CLDDX2Y0G/p1605127589058800

#### Tests

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

#### Code Review

- [x] This PR has a concise title and thorough description useful to a reviewer
- [x] Issue from task tracker has a link to this PR
2020-11-12 22:38:01 +00:00
luc
c1f9d51d7a Publish Canary
- vercel@20.1.5-canary.1
2020-11-12 22:06:16 +01:00
Luc Leray
422f0558c1 [cli] Rename "Provided by system" to "Reference ..." (#5415)
* Revert "Revert "[cli] (major) Update `vercel env` (#5372)" (#5410)"

This reverts commit 2d24a75ca6.

* fetch system env values from dedicated endpoint

* rename "Provided by System"
2020-11-12 22:05:24 +01:00
luc
f064ae2908 Publish Canary
- vercel@20.1.5-canary.0
 - @vercel/routing-utils@1.9.2-canary.0
2020-11-12 21:07:28 +01:00
Luc Leray
58c3e636f0 [cli] (Major) Update vc env (#5413)
* Revert "Revert "[cli] (major) Update `vercel env` (#5372)" (#5410)"

This reverts commit 2d24a75ca6.

* fetch system env values from dedicated endpoint
2020-11-12 21:06:25 +01:00
Connor Davis
d5081367f3 [routing-utils] Add important headers to disallow users from overriding (#5409)
If a builder wants to set a header that isn't allowed to be overridden by users, it should use `important: true`

#### Tests

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

#### Code Review

- [x] This PR has a concise title and thorough description useful to a reviewer
- [x] Issue from task tracker has a link to this PR: CH-14921
2020-11-12 17:45:33 +00:00
luc
0ee88366ff Publish Stable
- vercel@20.1.4
2020-11-12 17:16:29 +01:00
luc
9ae42c9e92 Publish Canary
- vercel@20.1.4-canary.1
2020-11-12 16:59:28 +01:00
Luc Leray
62b8df4a8d [cli] Fix vc env rm with advanced env variables (#5411) 2020-11-12 16:58:41 +01:00
luc
73ec7f3018 Publish Canary
- vercel@20.1.4-canary.0
2020-11-12 15:31:54 +01:00
Luc Leray
2d24a75ca6 Revert "[cli] (major) Update vercel env (#5372)" (#5410)
* Revert "[cli] (major) Update `vercel env` (#5372)"

This reverts commit 9a57cc72dd.

* fix test

* do not change prompt UI
2020-11-12 15:30:47 +01:00
2036 changed files with 2039 additions and 323438 deletions

View File

@@ -94,12 +94,3 @@ Sometimes you want to test changes to a Builder against an existing project, may
4. Run `vercel *.tgz` to upload the tarball file and get a URL
5. Edit any existing `vercel.json` project and replace `use` with the URL
6. Run `vercel` or `vercel dev` to deploy with the experimental Builder
## Add a New Framework
You can add support for a new Framework by creating a Pull Request for this repository and following the steps below:
1. Add the Framework to the `@vercel/frameworks` package: The file is located in `./packages/frameworks/frameworks.json`. You can copy the structure of an existing one and adjust the required fields. Note that the `settings` property either contains a `value` or a `placeholder`. The `value` property is used when something is not configurable, the `placeholder` is used when something is configurable and can be changed with configuration. An example would be the Output Directory for Hugo, it's `public` by default but can be changed through its config file, so we use `placeholder` with an explanation of what can be used.
2. Add an example to the `./examples` directory: The name of the directory should equal the slug of the framework used in `@vercel/frameworks`. The `.github/EXAMPLE_README_TEMPLATE.md` file can be used to create a `README.md` file for the example.
3. Update the `@vercel/static-build` package: The file `./packages/now-static-build/src/frameworks.ts` has to be extended. You can add default routes that will always be applied to projects that use this Framework or specify some paths that will be cached to speed up the build process.
4. After your Pull Request has been merged and released, other users can select the example on the Vercel dashboard and deploy it.

View File

@@ -11,7 +11,7 @@ on:
jobs:
test:
name: Dev
timeout-minutes: 45
timeout-minutes: 60
strategy:
fail-fast: false
matrix:

View File

@@ -6,5 +6,5 @@ You're running Vercel CLI in a non-terminal context and there are no credentials
#### Possible Ways to Fix It
- Specify a value for the `--token` flag (this needs to be the token of the user account as which you'd like to act). You can either get the token from the `./vercel/auth.json` file located in your user directory or [from the dashboard](https://vercel.com/account/tokens).
- Ensure that both `~/vercel/auth.json` and `~/vercel/config.json` exist
- Specify a value for the `--token` flag (this needs to be the token of the user account as which you'd like to act). You can create a new token on your [Settings page](https://vercel.com/account/tokens).
- Run `vercel login` to sign in and generate a new token

View File

@@ -1,4 +1,4 @@
import { NowRequest, NowResponse } from '@now/node';
import { NowRequest, NowResponse } from '@vercel/node';
export default (_req: NowRequest, res: NowResponse) => {
const date = new Date().toString();

View File

@@ -12,7 +12,7 @@
"react-helmet": "^5.2.0"
},
"devDependencies": {
"@now/node": "^1.3.0"
"@vercel/node": "1.8.5"
},
"scripts": {
"dev": "gatsby develop",

View File

@@ -1107,15 +1107,6 @@
"@nodelib/fs.scandir" "2.1.3"
fastq "^1.6.0"
"@now/node@^1.3.0":
version "1.7.1"
resolved "https://registry.yarnpkg.com/@now/node/-/node-1.7.1.tgz#764a0c6bcb24967f8014c4f73ad238c292996fe3"
integrity sha512-+srVKopsVTPDR3u9eOjJryZroLTrPp8XEOuIDGBdfFcJuS7qpAomctSbfyA7WNyjC0ExtUxELqBg5sAedG5+2g==
dependencies:
"@types/node" "*"
ts-node "8.9.1"
typescript "3.9.3"
"@pieh/friendly-errors-webpack-plugin@1.7.0-chalk-2":
version "1.7.0-chalk-2"
resolved "https://registry.yarnpkg.com/@pieh/friendly-errors-webpack-plugin/-/friendly-errors-webpack-plugin-1.7.0-chalk-2.tgz#2e9da9d3ade9d18d013333eb408c457d04eabac0"
@@ -1409,6 +1400,15 @@
dependencies:
wonka "^4.0.14"
"@vercel/node@1.8.5":
version "1.8.5"
resolved "https://registry.yarnpkg.com/@vercel/node/-/node-1.8.5.tgz#2c8b9532f1bb25734a9964c52973386ed78022d4"
integrity sha512-1iw7FSR8Oau6vZB1MWfBnA5q2a/IqRHiSZSbt8lz0dyTF599q8pc5GcSv/TvmrYaEGzh3+N0S4cbmuMCqVlwJg==
dependencies:
"@types/node" "*"
ts-node "8.9.1"
typescript "3.9.3"
"@webassemblyjs/ast@1.9.0":
version "1.9.0"
resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.9.0.tgz#bd850604b4042459a5a41cd7d338cbed695ed964"

View File

@@ -14,6 +14,10 @@ Open [http://localhost:3000](http://localhost:3000) with your browser to see the
You can start editing the page by modifying `pages/index.js`. The page auto-updates as you edit the file.
[API routes](https://nextjs.org/docs/api-routes/introduction) can be accessed on [http://localhost:3000/api/hello](http://localhost:3000/api/hello). This endpoint can be edited in `pages/api/hello.js`.
The `pages/api` directory is mapped to `/api/*`. Files in this directory are treated as [API routes](https://nextjs.org/docs/api-routes/introduction) instead of React pages.
## Learn More
To learn more about Next.js, take a look at the following resources:

View File

@@ -8,7 +8,7 @@
"start": "next start"
},
"dependencies": {
"next": "10.0.0",
"next": "10.0.5",
"react": "17.0.1",
"react-dom": "17.0.1"
}

View File

@@ -76,7 +76,6 @@
align-items: center;
justify-content: center;
flex-wrap: wrap;
max-width: 800px;
margin-top: 3rem;
}

View File

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

View File

@@ -43,9 +43,8 @@ export function sortFiles(fileA: string, fileB: string) {
export function detectApiExtensions(builders: Builder[]): Set<string> {
return new Set<string>(
builders
.filter(
b =>
b.config && b.config.zeroConfig && b.src && b.src.startsWith('api/')
.filter((b): b is Builder & { src: string } =>
Boolean(b.config && b.config.zeroConfig && b.src?.startsWith('api/'))
)
.map(b => extname(b.src))
.filter(Boolean)
@@ -56,22 +55,28 @@ export function detectApiDirectory(builders: Builder[]): string | null {
// TODO: We eventually want to save the api directory to
// builder.config.apiDirectory so it is only detected once
const found = builders.some(
b => b.config && b.config.zeroConfig && b.src.startsWith('api/')
b => b.config && b.config.zeroConfig && b.src?.startsWith('api/')
);
return found ? 'api' : null;
}
// TODO: Replace this function with `config.outputDirectory`
function getPublicBuilder(builders: Builder[]): Builder | null {
const builder = builders.find(
builder =>
function getPublicBuilder(
builders: Builder[]
): (Builder & { src: string }) | null {
for (const builder of builders) {
if (
typeof builder.src === 'string' &&
isOfficialRuntime('static', builder.use) &&
/^.*\/\*\*\/\*$/.test(builder.src) &&
builder.config &&
builder.config.zeroConfig === true
);
) {
return builder as Builder & { src: string };
}
}
return builder || null;
return null;
}
export function detectOutputDirectory(builders: Builder[]): string | null {
// TODO: We eventually want to save the output directory to
@@ -361,7 +366,7 @@ function maybeGetApiBuilder(
return null;
}
const match = apiMatches.find(({ src }) => {
const match = apiMatches.find(({ src = '**' }) => {
return src === fileName || minimatch(fileName, src);
});
@@ -989,7 +994,6 @@ function getRouteResult(
rewriteRoutes.push({
src: '^/api(/.*)?$',
status: 404,
continue: true,
});
}
} else {

View File

@@ -165,8 +165,8 @@ export function getSpawnOptions(
export async function getNodeVersion(
destPath: string,
_nodeVersion?: string,
_config?: Config,
meta?: Meta
config: Config = {},
meta: Meta = {}
): Promise<NodeVersion> {
if (meta && meta.isDev) {
// Use the system-installed version of `node` in PATH for `vercel dev`
@@ -174,13 +174,22 @@ export async function getNodeVersion(
return { ...latest, runtime: 'nodejs' };
}
const { packageJson } = await scanParentDirs(destPath, true);
let range: string | undefined;
let { nodeVersion } = config;
let isAuto = true;
if (packageJson && packageJson.engines && packageJson.engines.node) {
range = packageJson.engines.node;
if (
nodeVersion &&
nodeVersion !== packageJson.engines.node &&
!meta.isDev
) {
console.warn(
'Warning: Due to `engines` existing in your `package.json` file, the Node.js Version defined in your Project Settings will not apply. Learn More: http://vercel.link/node-version'
);
}
nodeVersion = packageJson.engines.node;
isAuto = false;
}
return getSupportedNodeVersion(range, isAuto);
return getSupportedNodeVersion(nodeVersion, isAuto);
}
async function scanParentDirs(destPath: string, readPackageJson = false) {
@@ -292,6 +301,11 @@ export async function runNpmInstall(
opts.prettyCommand = 'yarn install';
command = 'yarn';
commandArgs = ['install', ...args];
// Yarn v2 PnP mode may be activated, so force "node-modules" linker style
if (!env.YARN_NODE_LINKER) {
env.YARN_NODE_LINKER = 'node-modules';
}
}
if (process.env.NPM_ONLY_PRODUCTION) {
@@ -388,10 +402,17 @@ export async function runPackageJsonScript(
prettyCommand,
});
} else {
// Yarn v2 PnP mode may be activated, so force "node-modules" linker style
const env: typeof process.env = { ...spawnOpts?.env };
if (!env.YARN_NODE_LINKER) {
env.YARN_NODE_LINKER = 'node-modules';
}
const prettyCommand = `yarn run ${scriptName}`;
console.log(`Running "${prettyCommand}"`);
await spawnAsync('yarn', ['run', scriptName], {
...spawnOpts,
env,
cwd: destPath,
prettyCommand,
});

View File

@@ -336,7 +336,7 @@ export interface NodeVersion {
export interface Builder {
use: string;
src: string;
src?: string;
config?: Config;
}

View File

@@ -0,0 +1,5 @@
const { camelCase } = require('camel-case');
module.exports = (req, res) => {
res.end(camelCase('camel-case module is working'));
};

View File

@@ -1,5 +0,0 @@
{
"version": 2,
"builds": [{ "src": "package.json", "use": "@vercel/static-build" }],
"probes": [{ "path": "/", "mustContain": "Svelte app" }]
}

View File

@@ -16,6 +16,7 @@
"svelte": "^3.0.0"
},
"dependencies": {
"camel-case": "^4.1.2",
"sirv-cli": "^0.4.4"
}
}

View File

@@ -0,0 +1,11 @@
{
"version": 2,
"builds": [
{ "src": "package.json", "use": "@vercel/static-build" },
{ "src": "api/index.js", "use": "@vercel/node" }
],
"probes": [
{ "path": "/", "mustContain": "Svelte app" },
{ "path": "/api", "mustContain": "camelCaseModuleIsWorking" }
]
}

View File

@@ -270,6 +270,16 @@ __metadata:
languageName: node
linkType: hard
"camel-case@npm:^4.1.2":
version: 4.1.2
resolution: "camel-case@npm:4.1.2"
dependencies:
pascal-case: ^3.1.2
tslib: ^2.0.3
checksum: 3/0b8dcfb424c9497e45984b88ef005c66bdf8e877e36365aedfc3cf73182684fde5a14cf2c526579c0351a5f27dc39a00f1edecc25d43606075fea948c504e37f
languageName: node
linkType: hard
"caseless@npm:~0.12.0":
version: 0.12.0
resolution: "caseless@npm:0.12.0"
@@ -850,6 +860,15 @@ fsevents@~2.1.2:
languageName: node
linkType: hard
"lower-case@npm:^2.0.2":
version: 2.0.2
resolution: "lower-case@npm:2.0.2"
dependencies:
tslib: ^2.0.3
checksum: 3/aabaca9cef65f7564a1005b625664527e4d169e363101e65773f8f6ff2fdcf09884a3bc02990cd7a62cf05f3538114af25ee7bef553f1ca3208c8a77ac75cbfa
languageName: node
linkType: hard
"magic-string@npm:^0.25.2":
version: 0.25.7
resolution: "magic-string@npm:0.25.7"
@@ -944,6 +963,16 @@ fsevents@~2.1.2:
languageName: node
linkType: hard
"no-case@npm:^3.0.4":
version: 3.0.4
resolution: "no-case@npm:3.0.4"
dependencies:
lower-case: ^2.0.2
tslib: ^2.0.3
checksum: 3/84db4909caec37504c6655f995a004067f8733be8cd8d849f1578661b60a1685e086325fa4e1a5e8ce94e7416c1d0f037e2a00f635a14457183de80ab4fc7612
languageName: node
linkType: hard
"node-gyp@npm:latest":
version: 6.1.0
resolution: "node-gyp@npm:6.1.0"
@@ -1057,6 +1086,16 @@ fsevents@~2.1.2:
languageName: node
linkType: hard
"pascal-case@npm:^3.1.2":
version: 3.1.2
resolution: "pascal-case@npm:3.1.2"
dependencies:
no-case: ^3.0.4
tslib: ^2.0.3
checksum: 3/31708cecab221482edc81e2bd9b9d8282d72d4f1443b31f39725aa23768c5e42d93c4c014f1bc90f7f074e2a70d5091e4892ea370e550affc9ccf1d33c900bcd
languageName: node
linkType: hard
"path-is-absolute@npm:^1.0.0":
version: 1.0.1
resolution: "path-is-absolute@npm:1.0.1"
@@ -1447,6 +1486,7 @@ fsevents@~2.1.2:
dependencies:
"@rollup/plugin-commonjs": ^12.0.0
"@rollup/plugin-node-resolve": ^8.0.0
camel-case: ^4.1.2
rollup: ^2.3.4
rollup-plugin-livereload: ^1.0.0
rollup-plugin-svelte: ^5.0.3
@@ -1517,6 +1557,13 @@ fsevents@~2.1.2:
languageName: node
linkType: hard
"tslib@npm:^2.0.3":
version: 2.0.3
resolution: "tslib@npm:2.0.3"
checksum: 3/447bfca5deaa157806c3f77eaba74d05dd0b38b014e47ce79d98b5c77ce7d91b00a687ba13ca1b5a74d35ca1098ac7a072c0a97fad06f0266612f2a03a6c8e8f
languageName: node
linkType: hard
"tunnel-agent@npm:^0.6.0":
version: 0.6.0
resolution: "tunnel-agent@npm:0.6.0"

View File

@@ -39,7 +39,7 @@ for (const fixture of fs.readdirSync(fixturesPath)) {
// few foreign tests
const buildersToTestWith = ['now-next', 'now-node', 'now-static-build'];
const buildersToTestWith = ['now-node'];
// eslint-disable-next-line no-restricted-syntax
for (const builder of buildersToTestWith) {

View File

@@ -182,7 +182,7 @@ describe('Test `detectBuilders`', () => {
const { builders } = await detectBuilders(files);
expect(builders!.length).toBe(7);
expect(builders!.some(b => b.src.endsWith('_test.go'))).toBe(false);
expect(builders!.some(b => b.src!.endsWith('_test.go'))).toBe(false);
});
it('just public', async () => {
@@ -1341,7 +1341,7 @@ describe('Test `detectBuilders` with `featHandleMiss=true`', () => {
featHandleMiss,
});
expect(builders!.length).toBe(7);
expect(builders!.some(b => b.src.endsWith('_test.go'))).toBe(false);
expect(builders!.some(b => b.src!.endsWith('_test.go'))).toBe(false);
expect(errorRoutes!.length).toBe(1);
expect((errorRoutes![0] as Source).status).toBe(404);
});
@@ -2393,7 +2393,6 @@ it('Test `detectRoutes` with `featHandleMiss=true`', async () => {
{
status: 404,
src: '^/api(/.*)?$',
continue: true,
},
]);
expect(errorRoutes).toStrictEqual([
@@ -2495,7 +2494,6 @@ it('Test `detectRoutes` with `featHandleMiss=true`', async () => {
{
status: 404,
src: '^/api(/.*)?$',
continue: true,
},
]);
}
@@ -2533,7 +2531,6 @@ it('Test `detectRoutes` with `featHandleMiss=true`', async () => {
{
status: 404,
src: '^/api(/.*)?$',
continue: true,
},
]);
}
@@ -2571,7 +2568,6 @@ it('Test `detectRoutes` with `featHandleMiss=true`', async () => {
{
status: 404,
src: '^/api(/.*)?$',
continue: true,
},
]);
}
@@ -2604,7 +2600,6 @@ it('Test `detectRoutes` with `featHandleMiss=true`', async () => {
{
status: 404,
src: '^/api(/.*)?$',
continue: true,
},
]);
}
@@ -2632,7 +2627,6 @@ it('Test `detectRoutes` with `featHandleMiss=true`', async () => {
{
status: 404,
src: '^/api(/.*)?$',
continue: true,
},
]);
}
@@ -2663,7 +2657,6 @@ it('Test `detectRoutes` with `featHandleMiss=true`', async () => {
{
status: 404,
src: '^/api(/.*)?$',
continue: true,
},
]);
}
@@ -2690,7 +2683,6 @@ it('Test `detectRoutes` with `featHandleMiss=true`', async () => {
{
status: 404,
src: '^/api(/.*)?$',
continue: true,
},
]);
}
@@ -2725,7 +2717,6 @@ it('Test `detectRoutes` with `featHandleMiss=true`, `cleanUrls=true`', async ()
{
status: 404,
src: '^/api(/.*)?$',
continue: true,
},
]);
expect(errorRoutes).toStrictEqual([
@@ -2820,7 +2811,6 @@ it('Test `detectRoutes` with `featHandleMiss=true`, `cleanUrls=true`', async ()
{
status: 404,
src: '^/api(/.*)?$',
continue: true,
},
]);
}
@@ -2853,7 +2843,6 @@ it('Test `detectRoutes` with `featHandleMiss=true`, `cleanUrls=true`', async ()
{
status: 404,
src: '^/api(/.*)?$',
continue: true,
},
]);
}
@@ -2887,7 +2876,6 @@ it('Test `detectRoutes` with `featHandleMiss=true`, `cleanUrls=true`', async ()
{
status: 404,
src: '^/api(/.*)?$',
continue: true,
},
]);
}
@@ -2913,7 +2901,6 @@ it('Test `detectRoutes` with `featHandleMiss=true`, `cleanUrls=true`', async ()
{
status: 404,
src: '^/api(/.*)?$',
continue: true,
},
]);
}
@@ -2937,7 +2924,6 @@ it('Test `detectRoutes` with `featHandleMiss=true`, `cleanUrls=true`', async ()
{
status: 404,
src: '^/api(/.*)?$',
continue: true,
},
]);
}
@@ -2962,7 +2948,6 @@ it('Test `detectRoutes` with `featHandleMiss=true`, `cleanUrls=true`', async ()
{
status: 404,
src: '^/api(/.*)?$',
continue: true,
},
]);
}
@@ -2983,7 +2968,6 @@ it('Test `detectRoutes` with `featHandleMiss=true`, `cleanUrls=true`', async ()
{
status: 404,
src: '^/api(/.*)?$',
continue: true,
},
]);
}
@@ -3018,7 +3002,6 @@ it('Test `detectRoutes` with `featHandleMiss=true`, `cleanUrls=true`, `trailingS
{
status: 404,
src: '^/api(/.*)?$',
continue: true,
},
]);
@@ -3076,7 +3059,6 @@ it('Test `detectRoutes` with `featHandleMiss=true`, `cleanUrls=true`, `trailingS
{
status: 404,
src: '^/api(/.*)?$',
continue: true,
},
]);
}
@@ -3109,7 +3091,6 @@ it('Test `detectRoutes` with `featHandleMiss=true`, `cleanUrls=true`, `trailingS
{
status: 404,
src: '^/api(/.*)?$',
continue: true,
},
]);
}
@@ -3143,7 +3124,6 @@ it('Test `detectRoutes` with `featHandleMiss=true`, `cleanUrls=true`, `trailingS
{
status: 404,
src: '^/api(/.*)?$',
continue: true,
},
]);
}
@@ -3162,7 +3142,6 @@ it('Test `detectRoutes` with `featHandleMiss=true`, `cleanUrls=true`, `trailingS
{
status: 404,
src: '^/api(/.*)?$',
continue: true,
},
]);
}
@@ -3186,7 +3165,6 @@ it('Test `detectRoutes` with `featHandleMiss=true`, `cleanUrls=true`, `trailingS
{
status: 404,
src: '^/api(/.*)?$',
continue: true,
},
]);
}
@@ -3211,7 +3189,6 @@ it('Test `detectRoutes` with `featHandleMiss=true`, `cleanUrls=true`, `trailingS
{
status: 404,
src: '^/api(/.*)?$',
continue: true,
},
]);
}
@@ -3232,7 +3209,6 @@ it('Test `detectRoutes` with `featHandleMiss=true`, `cleanUrls=true`, `trailingS
{
status: 404,
src: '^/api(/.*)?$',
continue: true,
},
]);
}

View File

@@ -24,6 +24,19 @@ async function expectBuilderError(promise, pattern) {
);
}
let warningMessages;
const originalConsoleWarn = console.warn;
beforeEach(() => {
warningMessages = [];
console.warn = m => {
warningMessages.push(m);
};
});
afterEach(() => {
console.warn = originalConsoleWarn;
});
it('should re-create symlinks properly', async () => {
if (process.platform === 'win32') {
console.log('Skipping test on windows');
@@ -150,6 +163,39 @@ it('should ignore node version in vercel dev getNodeVersion()', async () => {
).toHaveProperty('runtime', 'nodejs');
});
it('should select project setting from config when no package.json is found', async () => {
expect(
await getNodeVersion('/tmp', undefined, { nodeVersion: '10.x' }, {})
).toHaveProperty('range', '10.x');
expect(warningMessages).toStrictEqual([]);
});
it('should prefer package.json engines over project setting from config and warn', async () => {
expect(
await getNodeVersion(
path.join(__dirname, 'pkg-engine-node'),
undefined,
{ nodeVersion: '12.x' },
{}
)
).toHaveProperty('range', '10.x');
expect(warningMessages).toStrictEqual([
'Warning: Due to `engines` existing in your `package.json` file, the Node.js Version defined in your Project Settings will not apply. Learn More: http://vercel.link/node-version',
]);
});
it('should not warn when package.json engines matches project setting from config', async () => {
expect(
await getNodeVersion(
path.join(__dirname, 'pkg-engine-node'),
undefined,
{ nodeVersion: '10.x' },
{}
)
).toHaveProperty('range', '10.x');
expect(warningMessages).toStrictEqual([]);
});
it('should get latest node version', async () => {
expect(await getLatestNodeVersion()).toHaveProperty('major', 12);
});

View File

@@ -1,6 +1,6 @@
{
"name": "vercel",
"version": "20.1.3",
"version": "21.2.1-canary.0",
"preferGlobal": true,
"license": "Apache-2.0",
"description": "The command-line interface for Vercel",
@@ -61,11 +61,11 @@
"node": ">= 10"
},
"dependencies": {
"@vercel/build-utils": "2.6.0",
"@vercel/go": "1.1.6",
"@vercel/node": "1.8.5",
"@vercel/python": "1.2.3",
"@vercel/ruby": "1.2.4",
"@vercel/build-utils": "2.8.0",
"@vercel/go": "1.1.7",
"@vercel/node": "1.9.0",
"@vercel/python": "1.2.5-canary.0",
"@vercel/ruby": "1.2.5",
"update-notifier": "4.1.0"
},
"devDependencies": {

View File

@@ -2,7 +2,6 @@ import chalk from 'chalk';
import { handleError } from '../../util/error';
import createOutput from '../../util/output';
import getArgs from '../../util/get-args';
import getSubcommand from '../../util/get-subcommand';
import logo from '../../util/output/logo';
@@ -92,15 +91,14 @@ export default async function main(ctx) {
return 2;
}
const output = createOutput({ debug: argv['--debug'] });
const { subcommand, args } = getSubcommand(argv._.slice(1), COMMAND_CONFIG);
switch (subcommand) {
case 'ls':
return ls(ctx, argv, args, output);
return ls(ctx, argv, args);
case 'rm':
return rm(ctx, argv, args, output);
return rm(ctx, argv, args);
default:
return set(ctx, argv, args, output);
return set(ctx, argv, args);
}
}

View File

@@ -10,9 +10,10 @@ import strlen from '../../util/strlen.ts';
import getCommandFlags from '../../util/get-command-flags';
import { getCommandName } from '../../util/pkg-name.ts';
export default async function ls(ctx, opts, args, output) {
export default async function ls(ctx, opts, args) {
const {
authConfig: { token },
output,
config,
} = ctx;
const { currentTeam } = config;
@@ -23,6 +24,7 @@ export default async function ls(ctx, opts, args, output) {
token,
currentTeam,
debug: debugEnabled,
output,
});
let contextName = null;
@@ -47,6 +49,7 @@ export default async function ls(ctx, opts, args, output) {
token,
debug: debugEnabled,
currentTeam,
output,
});
const lsStamp = stamp();
let cancelWait;

View File

@@ -12,9 +12,10 @@ import { isValidName } from '../../util/is-valid-name';
import findAliasByAliasOrId from '../../util/alias/find-alias-by-alias-or-id';
import { getCommandName } from '../../util/pkg-name.ts';
export default async function rm(ctx, opts, args, output) {
export default async function rm(ctx, opts, args) {
const {
authConfig: { token },
output,
config,
} = ctx;
const { currentTeam } = config;
@@ -25,6 +26,7 @@ export default async function rm(ctx, opts, args, output) {
token,
currentTeam,
debug: debugEnabled,
output,
});
let contextName = null;
@@ -39,8 +41,13 @@ export default async function rm(ctx, opts, args, output) {
throw err;
}
// $FlowFixMe
const now = new Now({ apiUrl, token, debug: debugEnabled, currentTeam });
const now = new Now({
apiUrl,
token,
debug: debugEnabled,
currentTeam,
output,
});
const [aliasOrId] = args;
if (args.length !== 1) {

View File

@@ -29,11 +29,11 @@ type Options = {
export default async function set(
ctx: NowContext,
opts: Options,
args: string[],
output: Output
args: string[]
) {
const {
authConfig: { token },
output,
config,
localConfig,
} = ctx;
@@ -49,6 +49,7 @@ export default async function set(
token,
currentTeam,
debug: debugEnabled,
output,
});
let user: User;

View File

@@ -14,7 +14,6 @@ import addBilling from './add';
import exit from '../../util/exit';
import Client from '../../util/client.ts';
import getScope from '../../util/get-scope.ts';
import createOutput from '../../util/output';
import { getPkgName } from '../../util/pkg-name.ts';
const help = () => {
@@ -90,8 +89,9 @@ function buildInquirerChoices(cards) {
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 number = `${chalk.gray('#### ').repeat(3)}${
source.last4 || source.card.last4
}`;
const str = [
id,
indent(source.name || source.owner.name, 2),
@@ -106,11 +106,16 @@ function buildInquirerChoices(cards) {
});
}
async function run({ token, config: { currentTeam } }) {
async function run({ token, output, config: { currentTeam } }) {
const start = new Date();
const creditCards = new NowCreditCards({ apiUrl, token, debug, currentTeam });
const output = createOutput({ debug });
const client = new Client({ apiUrl, token, currentTeam, debug });
const creditCards = new NowCreditCards({
apiUrl,
token,
debug,
currentTeam,
output,
});
const client = new Client({ apiUrl, token, currentTeam, debug, output });
let contextName = null;
try {
@@ -147,8 +152,9 @@ async function run({ token, config: { currentTeam } }) {
const id = `${chalk.gray('-')} ${chalk.cyan(
`ID: ${source.id}`
)}${_default}`;
const number = `${chalk.gray('#### ').repeat(3)}${source.last4 ||
source.card.last4}`;
const number = `${chalk.gray('#### ').repeat(3)}${
source.last4 || source.card.last4
}`;
return [
id,
@@ -231,8 +237,9 @@ async function run({ token, config: { currentTeam } }) {
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}]`)}`
`${card.brand || card.card.brand} ending in ${
card.last4 || card.card.last4
} is now the default ${chalk.gray(`[${elapsed}]`)}`
)
);
} else {
@@ -301,9 +308,9 @@ async function run({ token, config: { currentTeam } }) {
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`;
let text = `${deletedCard.brand || deletedCard.card.brand} ending in ${
deletedCard.last4 || deletedCard.card.last4
} was deleted`;
// ${chalk.gray(`[${elapsed}]`)}
if (cardId === cards.defaultSource) {
@@ -317,11 +324,11 @@ async function run({ token, config: { currentTeam } }) {
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
)}`;
text += `\n${
newDefaultCard.brand || newDefaultCard.card.brand
} ending in ${
newDefaultCard.last4 || newDefaultCard.card.last4
} in now default for ${chalk.bold(contextName)}`;
}
}

View File

@@ -8,7 +8,6 @@ import stamp from '../../util/output/stamp';
import createCertFromFile from '../../util/certs/create-cert-from-file';
import createCertForCns from '../../util/certs/create-cert-for-cns';
import { NowContext } from '../../types';
import { Output } from '../../util/output';
import { getCommandName } from '../../util/pkg-name';
interface Options {
@@ -22,11 +21,11 @@ interface Options {
async function add(
ctx: NowContext,
opts: Options,
args: string[],
output: Output
args: string[]
): Promise<number> {
const {
authConfig: { token },
output,
config,
} = ctx;
const { currentTeam } = config;
@@ -49,6 +48,7 @@ async function add(
token,
currentTeam,
debug: debugEnabled,
output,
});
try {
@@ -62,7 +62,13 @@ async function add(
throw err;
}
const now = new Now({ apiUrl, token, debug: debugEnabled, currentTeam });
const now = new Now({
apiUrl,
token,
debug: debugEnabled,
currentTeam,
output,
});
if (overwite) {
output.error('Overwrite option is deprecated');
@@ -71,7 +77,7 @@ async function add(
}
if (crtPath || keyPath || caPath) {
if (args.length !== 0 || (!crtPath || !keyPath || !caPath)) {
if (args.length !== 0 || !crtPath || !keyPath || !caPath) {
output.error(
`Invalid number of arguments to create a custom certificate entry. Usage:`
);

View File

@@ -3,7 +3,6 @@ import chalk from 'chalk';
// @ts-ignore
import { handleError } from '../../util/error';
import createOutput from '../../util/output';
import getArgs from '../../util/get-args';
import getSubcommand from '../../util/get-subcommand';
import logo from '../../util/output/logo';
@@ -104,17 +103,17 @@ export default async function main(ctx: NowContext) {
return 0;
}
const output = createOutput({ debug: argv['--debug'] });
const { output } = ctx;
const { subcommand, args } = getSubcommand(argv._.slice(1), COMMAND_CONFIG);
switch (subcommand) {
case 'issue':
return issue(ctx, argv, args, output);
return issue(ctx, argv, args);
case 'ls':
return ls(ctx, argv, args, output);
return ls(ctx, argv, args);
case 'rm':
return rm(ctx, argv, args, output);
return rm(ctx, argv, args);
case 'add':
return add(ctx, argv, args, output);
return add(ctx, argv, args);
case 'renew':
output.error('Renewing certificates is deprecated, issue a new one.');
return 1;

View File

@@ -28,11 +28,11 @@ type Options = {
export default async function issue(
ctx: NowContext,
opts: Options,
args: string[],
output: Output
args: string[]
) {
const {
authConfig: { token },
output,
config,
} = ctx;
const { currentTeam } = config;
@@ -55,6 +55,7 @@ export default async function issue(
token,
currentTeam,
debug: debugEnabled,
output,
});
let contextName = null;

View File

@@ -8,7 +8,6 @@ import getScope from '../../util/get-scope';
import stamp from '../../util/output/stamp';
import getCerts from '../../util/certs/get-certs';
import strlen from '../../util/strlen';
import { Output } from '../../util/output';
import { NowContext, Cert } from '../../types';
import getCommandFlags from '../../util/get-command-flags';
import { getCommandName } from '../../util/pkg-name';
@@ -21,17 +20,17 @@ interface Options {
async function ls(
ctx: NowContext,
opts: Options,
args: string[],
output: Output
args: string[]
): Promise<number> {
const {
authConfig: { token },
output,
config,
} = ctx;
const { currentTeam } = config;
const { apiUrl } = ctx;
const { '--debug': debug, '--next': nextTimestamp } = opts;
const client = new Client({ apiUrl, token, currentTeam, debug });
const client = new Client({ apiUrl, token, currentTeam, debug, output });
let contextName = null;
try {
@@ -48,7 +47,7 @@ async function ls(
output.error('Please provide a number for flag --next');
return 1;
}
const now = new Now({ apiUrl, token, debug, currentTeam });
const now = new Now({ apiUrl, token, debug, currentTeam, output });
const lsStamp = stamp();
if (args.length !== 0) {

View File

@@ -18,21 +18,17 @@ type Options = {
'--debug': boolean;
};
async function rm(
ctx: NowContext,
opts: Options,
args: string[],
output: Output
) {
async function rm(ctx: NowContext, opts: Options, args: string[]) {
const {
authConfig: { token },
output,
config,
} = ctx;
const { currentTeam } = config;
const { apiUrl } = ctx;
const rmStamp = stamp();
const debug = opts['--debug'];
const client = new Client({ apiUrl, token, currentTeam, debug });
const client = new Client({ apiUrl, token, currentTeam, debug, output });
let contextName = null;

View File

@@ -3,7 +3,6 @@ import { resolve, basename } from 'path';
import { fileNameSymbol } from '@vercel/client';
import Client from '../../util/client.ts';
import getScope from '../../util/get-scope.ts';
import createOutput from '../../util/output';
import code from '../../util/output/code';
import highlight from '../../util/output/highlight';
import { readLocalConfig } from '../../util/config/files';
@@ -15,6 +14,7 @@ import deploy from './latest';
export default async ctx => {
const {
authConfig,
output,
config: { currentTeam },
apiUrl,
} = ctx;
@@ -48,7 +48,6 @@ export default async ctx => {
localConfig = readLocalConfig(paths[0]);
}
const debugEnabled = argv['--debug'];
const output = createOutput({ debug: debugEnabled });
const stats = {};
if (argv['--help']) {
@@ -74,6 +73,7 @@ export default async ctx => {
apiUrl,
token: authConfig.token,
currentTeam,
output,
debug: debugEnabled,
});
try {

View File

@@ -233,8 +233,7 @@ export default async function main(
const paths = Object.keys(stats);
const debugEnabled = argv['--debug'];
// $FlowFixMe
const isTTY = process.stdout.isTTY;
const { isTTY } = process.stdout;
const quiet = !isTTY;
// check paths
@@ -263,6 +262,7 @@ export default async function main(
apiUrl: ctx.apiUrl,
token: ctx.authConfig.token,
debug: debugEnabled,
output,
});
// retrieve `project` and `org` from .vercel
@@ -645,6 +645,7 @@ export default async function main(
token: ctx.authConfig.token,
currentTeam: org.id,
debug: debugEnabled,
output,
}),
err.meta.domain,
contextName
@@ -727,6 +728,7 @@ export default async function main(
token: ctx.authConfig.token,
currentTeam: org.type === 'team' ? org.id : null,
debug: debugEnabled,
output,
}),
deployment,
deployStamp,

View File

@@ -2,16 +2,15 @@ import { resolve, join } from 'path';
import DevServer from '../../util/dev/server';
import parseListen from '../../util/dev/parse-listen';
import { Output } from '../../util/output';
import { NowContext } from '../../types';
import { NowContext, ProjectEnvVariable } from '../../types';
import Client from '../../util/client';
import { getLinkedProject } from '../../util/projects/link';
import { getFrameworks } from '../../util/get-frameworks';
import { isSettingValue } from '../../util/is-setting-value';
import { ProjectSettings, ProjectEnvTarget } from '../../types';
import { ProjectSettings } from '../../types';
import getDecryptedEnvRecords from '../../util/get-decrypted-env-records';
import { Env } from '@vercel/build-utils';
import setupAndLink from '../../util/link/setup-and-link';
import getSystemEnvValues from '../../util/env/get-system-env-values';
type Options = {
'--debug'?: boolean;
@@ -22,9 +21,9 @@ type Options = {
export default async function dev(
ctx: NowContext,
opts: Options,
args: string[],
output: Output
args: string[]
) {
const { output } = ctx;
const [dir = '.'] = args;
let cwd = resolve(dir);
const listen = parseListen(opts['--listen'] || '3000');
@@ -35,6 +34,7 @@ export default async function dev(
token: ctx.authConfig.token,
currentTeam: ctx.config.currentTeam,
debug,
output,
});
// retrieve dev command
@@ -49,7 +49,6 @@ export default async function dev(
link = await setupAndLink(
ctx,
output,
cwd,
forceDelete,
autoConfirm,
@@ -70,7 +69,8 @@ export default async function dev(
let devCommand: string | undefined;
let frameworkSlug: string | undefined;
let projectSettings: ProjectSettings | undefined;
let environmentVars: Env | undefined;
let projectEnvs: ProjectEnvVariable[] = [];
let systemEnvValues: string[] = [];
if (link.status === 'linked') {
const { project, org } = link;
client.currentTeam = org.type === 'team' ? org.id : undefined;
@@ -98,12 +98,12 @@ export default async function dev(
cwd = join(cwd, project.rootDirectory);
}
environmentVars = await getDecryptedEnvRecords(
output,
client,
project,
ProjectEnvTarget.Development
);
[{ envs: projectEnvs }, { systemEnvValues }] = await Promise.all([
getDecryptedEnvRecords(output, client, project.id),
project.autoExposeSystemEnvs
? getSystemEnvValues(output, client, project.id)
: { systemEnvValues: [] },
]);
}
const devServer = new DevServer(cwd, {
@@ -112,7 +112,8 @@ export default async function dev(
devCommand,
frameworkSlug,
projectSettings,
environmentVars,
projectEnvs,
systemEnvValues,
});
process.once('SIGINT', () => devServer.stop());

View File

@@ -7,7 +7,6 @@ import getSubcommand from '../../util/get-subcommand';
import { NowContext } from '../../types';
import { NowError } from '../../util/now-error';
import handleError from '../../util/handle-error';
import createOutput from '../../util/output/create-output';
import logo from '../../util/output/logo';
import cmd from '../../util/output/cmd';
import highlight from '../../util/output/highlight';
@@ -51,7 +50,7 @@ const help = () => {
export default async function main(ctx: NowContext) {
let argv;
let args;
let output;
const { output } = ctx;
try {
argv = getArgs(ctx.argv.slice(2), {
@@ -63,9 +62,7 @@ export default async function main(ctx: NowContext) {
'--port': Number,
'-p': '--port',
});
const debug = argv['--debug'];
args = getSubcommand(argv._.slice(1), COMMAND_CONFIG).args;
output = createOutput({ debug });
if ('--port' in argv) {
output.warn('`--port` is deprecated, please use `--listen` instead');
@@ -120,7 +117,7 @@ export default async function main(ctx: NowContext) {
}
try {
return await dev(ctx, argv, args, output);
return await dev(ctx, argv, args);
} catch (err) {
if (err.code === 'ENOTFOUND') {
// Error message will look like the following:

View File

@@ -6,7 +6,6 @@ import {
DNSInvalidType,
} from '../../util/errors-ts';
import { NowContext } from '../../types';
import { Output } from '../../util/output';
import addDNSRecord from '../../util/dns/add-dns-record';
import Client from '../../util/client';
import getScope from '../../util/get-scope';
@@ -22,17 +21,17 @@ type Options = {
export default async function add(
ctx: NowContext,
opts: Options,
args: string[],
output: Output
args: string[]
) {
const {
apiUrl,
authConfig: { token },
output,
config,
} = ctx;
const { currentTeam } = config;
const { apiUrl } = ctx;
const debug = opts['--debug'];
const client = new Client({ apiUrl, token, currentTeam, debug });
const client = new Client({ apiUrl, token, currentTeam, debug, output });
let contextName = null;
try {

View File

@@ -1,6 +1,5 @@
import chalk from 'chalk';
import { NowContext } from '../../types';
import { Output } from '../../util/output';
import Client from '../../util/client';
import getScope from '../../util/get-scope';
import { DomainNotFound, InvalidDomain } from '../../util/errors-ts';
@@ -15,17 +14,17 @@ type Options = {
export default async function add(
ctx: NowContext,
opts: Options,
args: string[],
output: Output
args: string[]
) {
const {
apiUrl,
authConfig: { token },
output,
config,
} = ctx;
const { currentTeam } = config;
const { apiUrl } = ctx;
const debug = opts['--debug'];
const client = new Client({ apiUrl, token, currentTeam, debug });
const client = new Client({ apiUrl, token, currentTeam, debug, output });
let contextName = null;
try {

View File

@@ -1,7 +1,6 @@
import chalk from 'chalk';
import { NowContext } from '../../types';
import createOutput from '../../util/output';
import getArgs from '../../util/get-args';
import getSubcommand from '../../util/get-subcommand';
import handleError from '../../util/handle-error';
@@ -112,16 +111,15 @@ export default async function main(ctx: NowContext) {
return 2;
}
const output = createOutput({ debug: argv['--debug'] });
const { subcommand, args } = getSubcommand(argv._.slice(1), COMMAND_CONFIG);
switch (subcommand) {
case 'add':
return add(ctx, argv, args, output);
return add(ctx, argv, args);
case 'import':
return importZone(ctx, argv, args, output);
return importZone(ctx, argv, args);
case 'rm':
return rm(ctx, argv, args, output);
return rm(ctx, argv, args);
default:
return ls(ctx, argv, args, output);
return ls(ctx, argv, args);
}
}

View File

@@ -1,6 +1,5 @@
import chalk from 'chalk';
import ms from 'ms';
import { Output } from '../../util/output';
import { DomainNotFound } from '../../util/errors-ts';
import { DNSRecord, NowContext } from '../../types';
import Client from '../../util/client';
@@ -22,17 +21,17 @@ type Options = {
export default async function ls(
ctx: NowContext,
opts: Options,
args: string[],
output: Output
args: string[]
) {
const {
apiUrl,
authConfig: { token },
output,
config,
} = ctx;
const { currentTeam } = config;
const { apiUrl } = ctx;
const { '--debug': debug, '--next': nextTimestamp } = opts;
const client = new Client({ apiUrl, token, currentTeam, debug });
const client = new Client({ apiUrl, token, currentTeam, debug, output });
let contextName = null;
try {

View File

@@ -17,17 +17,17 @@ type Options = {
export default async function rm(
ctx: NowContext,
opts: Options,
args: string[],
output: Output
args: string[]
) {
const {
apiUrl,
authConfig: { token },
output,
config,
} = ctx;
const { currentTeam } = config;
const { apiUrl } = ctx;
const debug = opts['--debug'];
const client = new Client({ apiUrl, token, currentTeam, debug });
const client = new Client({ apiUrl, token, currentTeam, debug, output });
try {
await getScope(client);
@@ -100,12 +100,7 @@ function readConfirmation(
process.stdin
.on('data', d => {
process.stdin.pause();
resolve(
d
.toString()
.trim()
.toLowerCase() === 'y'
);
resolve(d.toString().trim().toLowerCase() === 'y');
})
.resume();
});

View File

@@ -1,7 +1,6 @@
import chalk from 'chalk';
import { NowContext } from '../../types';
import { Output } from '../../util/output';
import * as ERRORS from '../../util/errors-ts';
import Client from '../../util/client';
import formatNSTable from '../../util/format-ns-table';
@@ -24,18 +23,18 @@ type Options = {
export default async function add(
ctx: NowContext,
opts: Options,
args: string[],
output: Output
args: string[]
) {
const {
authConfig: { token },
output,
config,
} = ctx;
const { currentTeam } = config;
const { apiUrl } = ctx;
const debug = opts['--debug'];
const force = opts['--force'];
const client = new Client({ apiUrl, token, currentTeam, debug });
const client = new Client({ apiUrl, token, currentTeam, debug, output });
let contextName = null;
try {

View File

@@ -2,7 +2,6 @@ import chalk from 'chalk';
import psl from 'psl';
import { NowContext } from '../../types';
import { Output } from '../../util/output';
import * as ERRORS from '../../util/errors-ts';
import Client from '../../util/client';
import getDomainPrice from '../../util/domains/get-domain-price';
@@ -21,17 +20,17 @@ type Options = {
export default async function buy(
ctx: NowContext,
opts: Options,
args: string[],
output: Output
args: string[]
) {
const {
authConfig: { token },
output,
config,
} = ctx;
const { currentTeam } = config;
const { apiUrl } = ctx;
const debug = opts['--debug'];
const client = new Client({ apiUrl, token, currentTeam, debug });
const client = new Client({ apiUrl, token, currentTeam, debug, output });
let contextName = null;
try {
@@ -70,7 +69,10 @@ export default async function buy(
}
const availableStamp = stamp();
const domainPrice = await getDomainPrice(client, domainName);
const [domainPrice, renewalPrice] = await Promise.all([
getDomainPrice(client, domainName),
getDomainPrice(client, domainName, 'renewal'),
]);
if (domainPrice instanceof Error) {
output.prettyError(domainPrice);
@@ -102,12 +104,21 @@ export default async function buy(
return 0;
}
const autoRenew = await promptBool(
renewalPrice.period === 1
? `Auto renew yearly for ${chalk.bold(`$${price}`)}?`
: `Auto renew every ${renewalPrice.period} years for ${chalk.bold(
`$${price}`
)}?`,
{ defaultValue: true }
);
let buyResult;
const purchaseStamp = stamp();
const stopPurchaseSpinner = output.spinner('Purchasing');
try {
buyResult = await purchaseDomain(client, domainName, price);
buyResult = await purchaseDomain(client, domainName, price, autoRenew);
} catch (err) {
stopPurchaseSpinner();
output.error(

View File

@@ -1,7 +1,6 @@
import chalk from 'chalk';
import { NowContext } from '../../types';
import createOutput from '../../util/output';
import getArgs from '../../util/get-args';
import getSubcommand from '../../util/get-subcommand';
import handleError from '../../util/handle-error';
@@ -106,24 +105,23 @@ export default async function main(ctx: NowContext) {
return 2;
}
const output = createOutput({ debug: argv['--debug'] });
const { subcommand, args } = getSubcommand(argv._.slice(1), COMMAND_CONFIG);
switch (subcommand) {
case 'add':
return add(ctx, argv, args, output);
return add(ctx, argv, args);
case 'inspect':
return inspect(ctx, argv, args, output);
return inspect(ctx, argv, args);
case 'move':
return move(ctx, argv, args, output);
return move(ctx, argv, args);
case 'buy':
return buy(ctx, argv, args, output);
return buy(ctx, argv, args);
case 'rm':
return rm(ctx, argv, args, output);
return rm(ctx, argv, args);
case 'transferIn':
return transferIn(ctx, argv, args, output);
return transferIn(ctx, argv, args);
case 'verify':
return verify(ctx, argv, args, output);
return verify(ctx, argv, args);
default:
return ls(ctx, argv, args, output);
return ls(ctx, argv, args);
}
}

View File

@@ -24,17 +24,17 @@ type Options = {
export default async function inspect(
ctx: NowContext,
opts: Options,
args: string[],
output: Output
args: string[]
) {
const {
authConfig: { token },
output,
config,
} = ctx;
const { currentTeam } = config;
const { apiUrl } = ctx;
const debug = opts['--debug'];
const client = new Client({ apiUrl, token, currentTeam, debug });
const client = new Client({ apiUrl, token, currentTeam, debug, output });
let contextName = null;

View File

@@ -7,7 +7,6 @@ import Client from '../../util/client';
import getDomains from '../../util/domains/get-domains';
import getScope from '../../util/get-scope';
import stamp from '../../util/output/stamp';
import { Output } from '../../util/output';
import formatTable from '../../util/format-table';
import { formatDateWithoutTime } from '../../util/format-date';
import { Domain, NowContext } from '../../types';
@@ -24,17 +23,17 @@ type Options = {
export default async function ls(
ctx: NowContext,
opts: Options,
args: string[],
output: Output
args: string[]
) {
const {
authConfig: { token },
output,
config,
} = ctx;
const { currentTeam } = config;
const { apiUrl } = ctx;
const { '--debug': debug, '--next': nextTimestamp } = opts;
const client = new Client({ apiUrl, token, currentTeam, debug });
const client = new Client({ apiUrl, token, currentTeam, debug, output });
let contextName = null;
if (typeof nextTimestamp !== undefined && Number.isNaN(nextTimestamp)) {

View File

@@ -2,7 +2,6 @@ import chalk from 'chalk';
import plural from 'pluralize';
import { NowContext, User, Team } from '../../types';
import { Output } from '../../util/output';
import * as ERRORS from '../../util/errors-ts';
import Client from '../../util/client';
import getScope from '../../util/get-scope';
@@ -25,17 +24,17 @@ type Options = {
export default async function move(
ctx: NowContext,
opts: Options,
args: string[],
output: Output
args: string[]
) {
const {
authConfig: { token },
output,
config,
} = ctx;
const { currentTeam } = config;
const { apiUrl } = ctx;
const debug = opts['--debug'];
const client = new Client({ apiUrl, token, currentTeam, debug });
const client = new Client({ apiUrl, token, currentTeam, debug, output });
let contextName = null;
let user = null;

View File

@@ -26,17 +26,17 @@ type Options = {
export default async function rm(
ctx: NowContext,
opts: Options,
args: string[],
output: Output
args: string[]
) {
const {
authConfig: { token },
output,
config,
} = ctx;
const { currentTeam } = config;
const { apiUrl } = ctx;
const debug = opts['--debug'];
const client = new Client({ apiUrl, token, currentTeam, debug });
const client = new Client({ apiUrl, token, currentTeam, debug, output });
const [domainName] = args;
let contextName = null;

View File

@@ -1,7 +1,6 @@
import chalk from 'chalk';
import { NowContext } from '../../types';
import { Output } from '../../util/output';
import * as ERRORS from '../../util/errors-ts';
import Client from '../../util/client';
import getScope from '../../util/get-scope';
@@ -24,17 +23,17 @@ type Options = {
export default async function transferIn(
ctx: NowContext,
opts: Options,
args: string[],
output: Output
args: string[]
) {
const {
authConfig: { token },
output,
config,
} = ctx;
const { currentTeam } = config;
const { apiUrl } = ctx;
const debug = opts['--debug'];
const client = new Client({ apiUrl, token, currentTeam, debug });
const client = new Client({ apiUrl, token, currentTeam, debug, output });
let contextName = null;
try {

View File

@@ -1,13 +1,11 @@
import { NowContext } from '../../types';
import { Output } from '../../util/output';
import { NowBuildError } from '@vercel/build-utils';
import { getCommandName } from '../../util/pkg-name';
export default async function verify(
_ctx: NowContext,
{ output }: NowContext,
_opts: {},
args: string[],
output: Output
args: string[]
) {
const [domainName] = args;

View File

@@ -18,7 +18,7 @@ import withSpinner from '../../util/with-spinner';
import { emoji, prependEmoji } from '../../util/emoji';
import { isKnownError } from '../../util/env/known-error';
import { getCommandName } from '../../util/pkg-name';
import { SYSTEM_ENV_VALUES } from '../../util/env/system-env';
import getSystemEnvValues from '../../util/env/get-system-env-values';
type Options = {
'--debug': boolean;
@@ -91,7 +91,10 @@ export default async function add(
name: `Secret (can be created using ${getCommandName('secret add')})`,
value: ProjectEnvType.Secret,
},
{ name: 'Provided by System', value: ProjectEnvType.System },
{
name: 'Reference to System Environment Variable',
value: ProjectEnvType.System,
},
],
})) as { inputEnvType: ProjectEnvType };
@@ -112,11 +115,24 @@ export default async function add(
}
}
const { envs } = await getEnvVariables(output, client, project.id);
const [{ envs }, { systemEnvValues }] = await Promise.all([
getEnvVariables(output, client, project.id),
getSystemEnvValues(output, client, project.id),
]);
const existing = new Set(
envs.filter(r => r.key === envName).map(r => r.target)
);
const choices = getEnvTargetChoices().filter(c => !existing.has(c.value));
const choices = getEnvTargetChoices().filter(c => {
// hide Development if "Secret" is chosen
if (
envType === ProjectEnvType.Secret &&
c.value === ProjectEnvTarget.Development
) {
return false;
}
return !existing.has(c.value);
});
if (choices.length === 0) {
output.error(
@@ -182,7 +198,7 @@ export default async function add(
name: 'systemEnvValue',
type: 'list',
message: `Whats the value of ${envName}?`,
choices: SYSTEM_ENV_VALUES.map(value => ({ name: value, value })),
choices: systemEnvValues.map(value => ({ name: value, value })),
});
envValue = systemEnvValue;

View File

@@ -1,7 +1,6 @@
import chalk from 'chalk';
import { NowContext } from '../../types';
import createOutput from '../../util/output';
import getArgs from '../../util/get-args';
import getSubcommand from '../../util/get-subcommand';
import getInvalidSubcommand from '../../util/get-invalid-subcommand';
@@ -109,15 +108,15 @@ export default async function main(ctx: NowContext) {
}
const debug = argv['--debug'];
const output = createOutput({ debug });
const { subcommand, args } = getSubcommand(argv._.slice(1), COMMAND_CONFIG);
const {
authConfig: { token },
apiUrl,
output,
config,
} = ctx;
const { currentTeam } = config;
const { apiUrl } = ctx;
const client = new Client({ apiUrl, token, currentTeam, debug });
const client = new Client({ apiUrl, token, currentTeam, debug, output });
const link = await getLinkedProject(output, client);
if (link.status === 'error') {
return link.exitCode;

View File

@@ -1,5 +1,5 @@
import chalk from 'chalk';
import { ProjectEnvTarget, Project } from '../../types';
import { Project } from '../../types';
import { Output } from '../../util/output';
import confirm from '../../util/input/confirm';
import Client from '../../util/client';
@@ -12,7 +12,8 @@ import { promises, openSync, closeSync, readSync } from 'fs';
import { emoji, prependEmoji } from '../../util/emoji';
import { getCommandName } from '../../util/pkg-name';
const { writeFile } = promises;
import { Env } from '@vercel/build-utils';
import exposeSystemEnvs from '../../util/dev/expose-system-envs';
import getSystemEnvValues from '../../util/env/get-system-env-values';
const CONTENTS_PREFIX = '# Created by Vercel CLI\n';
@@ -84,15 +85,22 @@ export default async function pull(
);
const pullStamp = stamp();
const records: Env = await withSpinner(
'Downloading',
async () =>
await getDecryptedEnvRecords(
output,
client,
project,
ProjectEnvTarget.Development
)
const [
{ envs: projectEnvs },
{ systemEnvValues },
] = await withSpinner('Downloading', () =>
Promise.all([
getDecryptedEnvRecords(output, client, project.id),
project.autoExposeSystemEnvs
? getSystemEnvValues(output, client, project.id)
: { systemEnvValues: [] },
])
);
const records = exposeSystemEnvs(
projectEnvs,
systemEnvValues,
project.autoExposeSystemEnvs
);
const contents =

View File

@@ -4,7 +4,6 @@ import getArgs from '../../util/get-args';
import getSubcommand from '../../util/get-subcommand';
import { NowContext } from '../../types';
import handleError from '../../util/handle-error';
import createOutput from '../../util/output/create-output';
import logo from '../../util/output/logo';
import error from '../../util/output/error';
import init from './init';
@@ -47,7 +46,6 @@ const help = () => {
export default async function main(ctx: NowContext) {
let argv;
let args;
let output;
try {
argv = getArgs(ctx.argv.slice(2), {
@@ -55,7 +53,6 @@ export default async function main(ctx: NowContext) {
'-f': Boolean,
});
args = getSubcommand(argv._.slice(1), COMMAND_CONFIG).args;
output = createOutput({ debug: argv['--debug'] });
} catch (err) {
handleError(err);
return 1;
@@ -67,15 +64,15 @@ export default async function main(ctx: NowContext) {
}
if (argv._.length > 3) {
output.error('Too much arguments.');
ctx.output.error('Too much arguments.');
return 1;
}
try {
return await init(ctx, argv, args, output);
return await init(ctx, argv, args);
} catch (err) {
console.log(error(err.message));
output.debug(err.stack);
ctx.output.debug(err.stack);
return 1;
}
}

View File

@@ -34,9 +34,9 @@ const EXAMPLE_API = 'https://now-example-files.zeit.sh';
export default async function init(
ctx: NowContext,
opts: Options,
args: string[],
output: Output
args: string[]
) {
const { output } = ctx;
const [name, dir] = args;
const force = opts['-f'] || opts['--force'];

View File

@@ -3,7 +3,6 @@ import getArgs from '../util/get-args';
import buildsList from '../util/output/builds';
import routesList from '../util/output/routes';
import indent from '../util/output/indent';
import createOutput from '../util/output';
import Now from '../util';
import logo from '../util/output/logo';
import elapsed from '../util/output/elapsed.ts';
@@ -59,9 +58,13 @@ export default async function main(ctx) {
return 2;
}
const apiUrl = ctx.apiUrl;
const {
apiUrl,
output,
authConfig: { token },
config,
} = ctx;
const debugEnabled = argv['--debug'];
const output = createOutput({ debug: debugEnabled });
const { print, log, error } = output;
// extract the first parameter
@@ -73,16 +76,13 @@ export default async function main(ctx) {
return 1;
}
const {
authConfig: { token },
config,
} = ctx;
const { currentTeam } = config;
const client = new Client({
apiUrl,
token,
currentTeam,
debug: debugEnabled,
output,
});
let contextName = null;
@@ -90,25 +90,30 @@ export default async function main(ctx) {
({ contextName } = await getScope(client));
} catch (err) {
if (err.code === 'NOT_AUTHORIZED' || err.code === 'TEAM_DELETED') {
output.error(err.message);
error(err.message);
return 1;
}
throw err;
}
const now = new Now({ apiUrl, token, debug: debugEnabled, currentTeam });
const now = new Now({
apiUrl,
token,
debug: debugEnabled,
currentTeam,
output,
});
// resolve the deployment, since we might have been given an alias
const depFetchStart = Date.now();
const cancelWait = output.spinner(
output.spinner(
`Fetching deployment "${deploymentIdOrHost}" in ${chalk.bold(contextName)}`
);
try {
deployment = await now.findDeployment(deploymentIdOrHost);
} catch (err) {
cancelWait();
if (err.status === 404) {
error(
`Failed to find deployment "${deploymentIdOrHost}" in ${chalk.bold(
@@ -136,7 +141,6 @@ export default async function main(ctx) {
? await now.fetch(`/v1/now/deployments/${id}/builds`)
: { builds: [] };
cancelWait();
log(
`Fetched deployment "${url}" in ${chalk.bold(contextName)} ${elapsed(
Date.now() - depFetchStart

View File

@@ -1,6 +1,5 @@
import chalk from 'chalk';
import { NowContext } from '../../types';
import createOutput from '../../util/output';
import getArgs from '../../util/get-args';
import getSubcommand from '../../util/get-subcommand';
import handleError from '../../util/handle-error';
@@ -66,8 +65,6 @@ export default async function main(ctx: NowContext) {
return 2;
}
const debug = argv['--debug'];
const output = createOutput({ debug });
const { args } = getSubcommand(argv._.slice(1), COMMAND_CONFIG);
const path = args[0] || process.cwd();
const autoConfirm = argv['--confirm'];
@@ -75,7 +72,6 @@ export default async function main(ctx: NowContext) {
const link = await setupAndLink(
ctx,
output,
path,
forceDelete,
autoConfirm,

View File

@@ -3,12 +3,10 @@ import ms from 'ms';
import table from 'text-table';
import Now from '../util';
import getArgs from '../util/get-args';
import createOutput from '../util/output';
import { handleError } from '../util/error';
import cmd from '../util/output/cmd.ts';
import logo from '../util/output/logo';
import elapsed from '../util/output/elapsed.ts';
import wait from '../util/output/wait';
import strlen from '../util/strlen.ts';
import Client from '../util/client.ts';
import getScope from '../util/get-scope.ts';
@@ -51,12 +49,6 @@ const help = () => {
${chalk.cyan(`$ ${getPkgName()} ls my-app`)}
${chalk.gray(
''
)} List all deployments and all instances for the app ${chalk.dim('`my-app`')}
${chalk.cyan(`$ ${getPkgName()} ls my-app --all`)}
${chalk.gray('')} Filter deployments by metadata
${chalk.cyan(`$ ${getPkgName()} ls -m key1=value1 -m key2=value2`)}
@@ -84,11 +76,15 @@ export default async function main(ctx) {
return 1;
}
const debugEnabled = argv['--debug'];
const {
authConfig: { token },
output,
apiUrl,
config,
} = ctx;
const { print, log, error, note, debug } = createOutput({
debug: debugEnabled,
});
const debugEnabled = argv['--debug'];
const { print, log, error, note, debug, spinner } = output;
if (argv._.length > 2) {
error(`${getCommandName('ls [app]')} accepts at most one argument`);
@@ -98,24 +94,19 @@ export default async function main(ctx) {
let app = argv._[1];
let host = null;
const apiUrl = ctx.apiUrl;
if (argv['--help']) {
help();
return 0;
}
const meta = parseMeta(argv['--meta']);
const {
authConfig: { token },
config,
} = ctx;
const { currentTeam, includeScheme } = config;
const client = new Client({
apiUrl,
token,
currentTeam,
debug: debugEnabled,
output,
});
let contextName = null;
@@ -137,11 +128,15 @@ export default async function main(ctx) {
return 1;
}
const stopSpinner = wait(
`Fetching deployments in ${chalk.bold(contextName)}`
);
spinner(`Fetching deployments in ${chalk.bold(contextName)}`);
const now = new Now({ apiUrl, token, debug: debugEnabled, currentTeam });
const now = new Now({
apiUrl,
token,
debug: debugEnabled,
output,
currentTeam,
});
const start = new Date();
if (app && !isValidName(app)) {
@@ -162,7 +157,6 @@ export default async function main(ctx) {
const hostParts = asHost.split('-');
if (hostParts < 2) {
stopSpinner();
error('Only deployment hostnames are allowed, no aliases');
return 1;
}
@@ -171,19 +165,12 @@ export default async function main(ctx) {
host = asHost;
}
let response;
try {
debug('Fetching deployments');
response = await now.list(app, {
version: 6,
meta,
nextTimestamp,
});
} catch (err) {
stopSpinner();
throw err;
}
debug('Fetching deployments');
const response = await now.list(app, {
version: 6,
meta,
nextTimestamp,
});
let { deployments, pagination } = response;
@@ -199,7 +186,6 @@ export default async function main(ctx) {
if (err.status === 404) {
debug('Ignore findDeployment 404');
} else {
stopSpinner();
throw err;
}
}
@@ -216,7 +202,6 @@ export default async function main(ctx) {
deployments = deployments.filter(deployment => deployment.url === host);
}
stopSpinner();
log(
`Deployments under ${chalk.bold(contextName)} ${elapsed(
Date.now() - start

View File

@@ -19,7 +19,6 @@ import getGlobalPathConfig from '../util/config/global-path';
import hp from '../util/humanize-path';
import logo from '../util/output/logo';
import exit from '../util/exit';
import createOutput from '../util/output';
import executeLogin from '../util/login/login.ts';
import { prependEmoji, emoji } from '../util/emoji';
import { getCommandName, getPkgName } from '../util/pkg-name.ts';
@@ -141,15 +140,12 @@ const login = async ctx => {
await exit(0);
}
const debugEnabled = argv['--debug'];
const output = createOutput({ debug: debugEnabled });
const { apiUrl, output } = ctx;
argv._ = argv._.slice(1);
const apiUrl = ctx.apiUrl;
let email;
let emailIsValid = false;
let stopSpinner;
const possibleAddress = argv._[0];
@@ -190,19 +186,18 @@ const login = async ctx => {
let verificationToken;
let securityCode;
stopSpinner = output.spinner('Sending you an email');
output.spinner('Sending you an email');
try {
const data = await executeLogin(apiUrl, email);
verificationToken = data.token;
securityCode = data.securityCode;
} catch (err) {
stopSpinner();
console.log(error(err.message));
output.error(err.message);
return 1;
}
stopSpinner();
output.stopSpinner();
// Clear up `Sending email` success message
process.stdout.write(eraseLines(possibleAddress ? 1 : 2));
@@ -215,7 +210,7 @@ const login = async ctx => {
)}.\n`
);
stopSpinner = output.spinner('Waiting for your confirmation');
output.spinner('Waiting for your confirmation');
let token;
@@ -228,14 +223,13 @@ const login = async ctx => {
// /now/registraton is currently returning plain text in that case
// we just wait for the user to click on the link
} else {
stopSpinner();
console.log(err.message);
output.error(err.message);
return 1;
}
}
}
stopSpinner();
output.stopSpinner();
console.log(ok('Email confirmed'));
// There's no need to save the user since we always

View File

@@ -11,7 +11,7 @@ import {
} from '../util/config/files';
import getArgs from '../util/get-args';
import { NowContext } from '../types';
import createOutput, { Output } from '../util/output';
import { Output } from '../util/output';
import { getPkgName } from '../util/pkg-name';
const help = () => {
@@ -54,9 +54,7 @@ export default async function main(ctx: NowContext): Promise<number> {
return 2;
}
const debugEnabled = argv['--debug'];
const output = createOutput({ debug: debugEnabled });
return logout(ctx.apiUrl, output);
return logout(ctx.apiUrl, ctx.output);
}
const logout = async (apiUrl: string, output: Output) => {

View File

@@ -1,7 +1,6 @@
import mri from 'mri';
import chalk from 'chalk';
import Now from '../util';
import createOutput from '../util/output';
import logo from '../util/output/logo';
import elapsed from '../util/output/elapsed.ts';
import { maybeURL, normalizeURL } from '../util/url';
@@ -59,7 +58,6 @@ export default async function main(ctx) {
let deploymentIdOrURL;
let debug;
let apiUrl;
let head;
let limit;
let follow;
@@ -87,8 +85,12 @@ export default async function main(ctx) {
return 2;
}
const debugEnabled = argv.debug;
const output = createOutput({ debug: debugEnabled });
const {
authConfig: { token },
apiUrl,
output,
config,
} = ctx;
try {
since = argv.since ? toTimestamp(argv.since) : 0;
@@ -117,7 +119,6 @@ export default async function main(ctx) {
}
debug = argv.debug;
apiUrl = ctx.apiUrl;
head = argv.head;
limit = typeof argv.n === 'number' ? argv.n : 100;
@@ -125,17 +126,14 @@ export default async function main(ctx) {
if (follow) until = 0;
outputMode = argv.output in logPrinters ? argv.output : 'short';
const {
authConfig: { token },
config,
} = ctx;
const { currentTeam } = config;
const now = new Now({ apiUrl, token, debug, currentTeam });
const now = new Now({ apiUrl, token, debug, currentTeam, output });
const client = new Client({
apiUrl,
token,
currentTeam,
debug: debugEnabled,
debug,
output,
});
let contextName = null;
@@ -206,6 +204,7 @@ export default async function main(ctx) {
quiet: false,
debug,
findOpts: findOpts1,
output,
});
const printedEventIds = new Set();
@@ -232,6 +231,7 @@ export default async function main(ctx) {
quiet: false,
debug,
findOpts: findOpts2,
output,
});
}
@@ -324,7 +324,7 @@ function printLogRaw(log) {
if (log.object) {
console.log(log.object);
} else {
} else if (typeof log.text === 'string') {
console.log(
log.text
.replace(/\n$/, '')

View File

@@ -8,7 +8,6 @@ import exit from '../util/exit';
import Client from '../util/client.ts';
import logo from '../util/output/logo';
import getScope from '../util/get-scope';
import createOutput from '../util/output';
import getCommandFlags from '../util/get-command-flags';
import wait from '../util/output/wait';
import { getPkgName, getCommandName } from '../util/pkg-name.ts';
@@ -76,13 +75,12 @@ const main = async ctx => {
return exit(2);
}
const output = createOutput({ debug });
const {
authConfig: { token },
config: { currentTeam },
output,
} = ctx;
const client = new Client({ apiUrl, token, currentTeam, debug });
const client = new Client({ apiUrl, token, currentTeam, debug, output });
let contextName = null;
@@ -288,12 +286,7 @@ function readConfirmation(projectName) {
process.stdin
.on('data', d => {
process.stdin.pause();
resolve(
d
.toString()
.trim()
.toLowerCase() === 'y'
);
resolve(d.toString().trim().toLowerCase() === 'y');
})
.resume();
});

View File

@@ -5,7 +5,6 @@ import plural from 'pluralize';
import table from 'text-table';
import Now from '../util';
import getAliases from '../util/alias/get-aliases';
import createOutput from '../util/output';
import logo from '../util/output/logo';
import elapsed from '../util/output/elapsed.ts';
import { normalizeURL } from '../util/url';
@@ -79,12 +78,16 @@ export default async function main(ctx) {
argv._ = argv._.slice(1);
const apiUrl = ctx.apiUrl;
const {
apiUrl,
authConfig: { token },
output,
config,
} = ctx;
const hard = argv.hard || false;
const skipConfirmation = argv.yes || false;
const ids = argv._;
const debugEnabled = argv.debug;
const output = createOutput({ debug: debugEnabled });
const { success, error, log } = output;
if (argv.help || ids[0] === 'help') {
@@ -107,16 +110,13 @@ export default async function main(ctx) {
return 1;
}
const {
authConfig: { token },
config,
} = ctx;
const { currentTeam } = config;
const client = new Client({
apiUrl,
token,
currentTeam,
debug: debugEnabled,
output,
});
let contextName = null;
@@ -235,11 +235,9 @@ export default async function main(ctx) {
}
if (!skipConfirmation) {
const confirmation = (await readConfirmation(
deployments,
projects,
output
)).toLowerCase();
const confirmation = (
await readConfirmation(deployments, projects, output)
).toLowerCase();
if (confirmation !== 'y' && confirmation !== 'yes') {
output.log('Aborted');
@@ -248,7 +246,13 @@ export default async function main(ctx) {
}
}
const now = new Now({ apiUrl, token, debug: debugEnabled, currentTeam });
const now = new Now({
apiUrl,
token,
debug: debugEnabled,
currentTeam,
output,
});
const start = new Date();
await Promise.all([

View File

@@ -9,7 +9,6 @@ import exit from '../util/exit';
import logo from '../util/output/logo';
import Client from '../util/client.ts';
import getScope from '../util/get-scope.ts';
import createOutput from '../util/output';
import confirm from '../util/input/confirm';
import getCommandFlags from '../util/get-command-flags';
import getPrefixedFlags from '../util/get-prefixed-flags';
@@ -104,10 +103,10 @@ const main = async ctx => {
const {
authConfig: { token },
output,
config: { currentTeam },
} = ctx;
const output = createOutput({ debug });
const client = new Client({ apiUrl, token, currentTeam, debug });
const client = new Client({ apiUrl, token, currentTeam, debug, output });
let contextName = null;
try {
@@ -139,7 +138,7 @@ export default async ctx => {
};
async function run({ output, token, contextName, currentTeam, ctx }) {
const secrets = new NowSecrets({ apiUrl, token, debug, currentTeam });
const secrets = new NowSecrets({ apiUrl, token, debug, currentTeam, output });
const args = argv._.slice(1);
const start = Date.now();

View File

@@ -52,7 +52,7 @@ const help = () => {
${chalk.gray('')} Invite new members (interactively)
${chalk.cyan(`$ ${getPkgName()} teams invite`)}
${chalk.gray('')} Paginate results, where ${chalk.dim(
'`1584722256178`'
)} is the time in milliseconds since the UNIX epoch.
@@ -97,10 +97,11 @@ const main = async ctx => {
const {
authConfig: { token },
output,
config,
} = ctx;
return run({ token, config });
return run({ token, config, output });
};
export default async ctx => {
@@ -112,7 +113,7 @@ export default async ctx => {
}
};
async function run({ token, config }) {
async function run({ token, config, output }) {
const { currentTeam } = config;
const teams = new NowTeams({ apiUrl, token, debug, currentTeam });
const args = argv._;
@@ -126,6 +127,7 @@ async function run({ token, config }) {
config,
apiUrl,
token,
output,
argv,
});
break;
@@ -138,6 +140,7 @@ async function run({ token, config }) {
apiUrl,
token,
debug,
output,
});
break;
}
@@ -154,6 +157,7 @@ async function run({ token, config }) {
config,
apiUrl,
token,
output,
});
break;
}

View File

@@ -14,7 +14,6 @@ import success from '../../util/output/success';
import getUser from '../../util/get-user.ts';
import Client from '../../util/client.ts';
import { getCommandName } from '../../util/pkg-name.ts';
import createOutput from '../../util/output';
const validateEmail = data => regexEmail.test(data.trim()) || data.length === 0;
@@ -65,8 +64,8 @@ export default async function ({
noopMsg = 'No changes made',
apiUrl,
token,
output,
} = {}) {
const output = createOutput();
const { currentTeam: currentTeamId } = config;
const stopSpinner = wait('Fetching teams');
@@ -77,7 +76,7 @@ export default async function ({
stopSpinner();
const stopUserSpinner = wait('Fetching user information');
const client = new Client({ apiUrl, token });
const client = new Client({ apiUrl, token, output });
let user;
try {
user = await getUser(client);

View File

@@ -8,7 +8,6 @@ import info from '../../util/output/info';
import error from '../../util/output/error';
import chars from '../../util/output/chars';
import table from '../../util/output/table';
import createOutput from '../../util/output';
import getUser from '../../util/get-user.ts';
import Client from '../../util/client.ts';
import getPrefixedFlags from '../../util/get-prefixed-flags';
@@ -16,9 +15,8 @@ import { getPkgName } from '../../util/pkg-name.ts';
import getCommandFlags from '../../util/get-command-flags';
import cmd from '../../util/output/cmd.ts';
export default async function({ teams, config, apiUrl, token, argv }) {
export default async function ({ teams, config, apiUrl, token, output, argv }) {
const { next } = argv;
const output = createOutput({ debug: argv['--debug'] });
if (typeof next !== 'undefined' && !Number.isInteger(next)) {
output.error('Please provide a number for flag --next');
@@ -36,7 +34,7 @@ export default async function({ teams, config, apiUrl, token, argv }) {
stopSpinner();
const stopUserSpinner = wait('Fetching user information');
const client = new Client({ apiUrl, token, currentTeam });
const client = new Client({ apiUrl, token, currentTeam, output });
let user;
try {
user = await getUser(client);

View File

@@ -24,22 +24,21 @@ const updateCurrentTeam = (config, newTeam) => {
writeToConfigFile(config);
};
export default async function({ apiUrl, token, debug, args, config }) {
let stopSpinner = wait('Fetching teams');
export default async function ({ apiUrl, token, debug, args, config, output }) {
let stopSpinner;
output.spinner('Fetching teams');
// We're loading the teams here without `currentTeam`, so that
// people can use `vercel switch` in the case that their
// current team was deleted.
const teams = new NowTeams({ apiUrl, token, debug });
const teams = new NowTeams({ apiUrl, token, debug, output });
const list = (await teams.ls()).teams;
let { currentTeam } = config;
const accountIsCurrent = !currentTeam;
stopSpinner();
const stopUserSpinner = wait('Fetching user information');
const client = new Client({ apiUrl, token });
output.spinner('Fetching user information');
const client = new Client({ apiUrl, token, output });
let user;
try {
user = await getUser(client);
@@ -52,8 +51,6 @@ export default async function({ apiUrl, token, debug, args, config }) {
throw err;
}
stopUserSpinner();
if (accountIsCurrent) {
currentTeam = {
slug: user.username || user.email,

View File

@@ -5,7 +5,6 @@ import logo from '../util/output/logo';
import handleError from '../util/handle-error';
import getArgs from '../util/get-args';
import { NowContext } from '../types';
import createOutput from '../util/output';
import getUpdateCommand from '../util/get-update-command';
import { getPkgName, getTitleName } from '../util/pkg-name';
@@ -35,6 +34,7 @@ const help = () => {
export default async function main(ctx: NowContext): Promise<number> {
let argv;
const { output } = ctx;
try {
argv = getArgs(ctx.argv.slice(2), {
@@ -55,8 +55,6 @@ export default async function main(ctx: NowContext): Promise<number> {
return 2;
}
const debugEnabled = argv['--debug'];
const output = createOutput({ debug: debugEnabled });
output.log(
`Please run ${cmd(
await getUpdateCommand()

View File

@@ -4,7 +4,6 @@ import logo from '../util/output/logo';
import { handleError } from '../util/error';
import Client from '../util/client.ts';
import getScope from '../util/get-scope.ts';
import createOutput from '../util/output';
import { getPkgName } from '../util/pkg-name.ts';
const help = () => {
@@ -55,10 +54,10 @@ const main = async ctx => {
const debug = argv['--debug'];
const {
authConfig: { token },
output,
apiUrl,
} = ctx;
const output = createOutput({ debug });
const client = new Client({ apiUrl, token, debug });
const client = new Client({ apiUrl, token, debug, output });
let contextName = null;
try {

View File

@@ -291,7 +291,7 @@ const main = async argv_ => {
let authConfig = null;
const subcommandsWithoutToken = ['login', 'help', 'init', 'dev', 'update'];
const subcommandsWithoutToken = ['login', 'help', 'init', 'update'];
if (authConfigExists) {
try {
@@ -346,6 +346,7 @@ const main = async argv_ => {
// the context object to supply to the providers or the commands
const ctx = {
output,
config,
authConfig,
localConfig,
@@ -536,7 +537,7 @@ const main = async argv_ => {
!(targetCommand === 'teams' && argv._[3] !== 'invite')
) {
let user = null;
const client = new Client({ apiUrl, token });
const client = new Client({ apiUrl, token, output });
try {
user = await getUser(client);

View File

@@ -1,4 +1,5 @@
import { NowConfig } from './util/dev/types';
import { Output } from './util/output';
export type ThenArg<T> = T extends Promise<infer U> ? U : T;
@@ -8,6 +9,7 @@ export interface NowContext {
authConfig: {
token: string;
};
output: Output;
config: {
currentTeam: string;
updateChannel: string;
@@ -230,6 +232,8 @@ export interface ProjectSettings {
buildCommand?: string | null;
outputDirectory?: string | null;
rootDirectory?: string | null;
autoExposeSystemEnvs?: boolean;
directoryListing?: boolean;
}
export interface Project extends ProjectSettings {
@@ -243,6 +247,7 @@ export interface Project extends ProjectSettings {
framework?: string | null;
rootDirectory?: string | null;
latestDeployments?: Partial<Deployment>[];
autoExposeSystemEnvs?: boolean;
}
export interface Org {

View File

@@ -33,6 +33,7 @@ export default class Client extends EventEmitter {
forceNew = false,
withCache = false,
debug = false,
output = createOutput({ debug }),
}: {
apiUrl: string;
token: string;
@@ -40,13 +41,14 @@ export default class Client extends EventEmitter {
forceNew?: boolean;
withCache?: boolean;
debug?: boolean;
output?: Output;
}) {
super();
this._token = token;
this._debug = debug;
this._forceNew = forceNew;
this._withCache = withCache;
this._output = createOutput({ debug });
this._output = output;
this._apiUrl = apiUrl;
this._onRetry = this._onRetry.bind(this);
this.currentTeam = currentTeam;

View File

@@ -100,11 +100,7 @@ export default async function processDeployment({
skipAutoDetectionConfirmation,
};
let queuedSpinner = null;
let buildSpinner = null;
let deploySpinner = null;
const deployingSpinner = output.spinner(
output.spinner(
isSettingUpProject
? 'Setting up project'
: `Deploying ${chalk.bold(`${org.slug}/${projectName}`)}`,
@@ -138,16 +134,7 @@ export default async function processDeployment({
.map((sha: string) => event.payload.total.get(sha).data.length)
.reduce((a: number, b: number) => a + b, 0);
if (queuedSpinner) {
queuedSpinner();
}
if (buildSpinner) {
buildSpinner();
}
if (deploySpinner) {
deploySpinner();
}
deployingSpinner();
output.stopSpinner();
bar = new Progress(`${chalk.gray('>')} Upload [:bar] :percent :etas`, {
width: 20,
complete: '=',
@@ -170,8 +157,6 @@ export default async function processDeployment({
}
if (event.type === 'created') {
deployingSpinner();
if (bar && !bar.complete) {
bar.tick(bar.total + 1);
}
@@ -191,63 +176,36 @@ export default async function processDeployment({
now.url = event.payload.url;
output.stopSpinner();
printInspectUrl(output, event.payload.url, deployStamp, org.slug);
if (quiet) {
process.stdout.write(`https://${event.payload.url}`);
}
if (queuedSpinner === null) {
queuedSpinner =
event.payload.readyState === 'QUEUED'
? output.spinner('Queued', 0)
: output.spinner('Building', 0);
}
output.spinner(
event.payload.readyState === 'QUEUED' ? 'Queued' : 'Building',
0
);
}
if (event.type === 'building') {
if (queuedSpinner) {
queuedSpinner();
}
if (buildSpinner === null) {
buildSpinner = output.spinner('Building', 0);
}
output.spinner('Building', 0);
}
if (event.type === 'canceled') {
if (queuedSpinner) {
queuedSpinner();
}
if (buildSpinner) {
buildSpinner();
}
output.stopSpinner();
return event.payload;
}
if (event.type === 'ready') {
if (queuedSpinner) {
queuedSpinner();
}
if (buildSpinner) {
buildSpinner();
}
deploySpinner = output.spinner('Completing', 0);
output.spinner('Completing', 0);
}
// Handle error events
if (event.type === 'error') {
if (queuedSpinner) {
queuedSpinner();
}
if (buildSpinner) {
buildSpinner();
}
if (deploySpinner) {
deploySpinner();
}
deployingSpinner();
output.stopSpinner();
const error = await now.handleDeploymentError(event.payload, {
hashes,
@@ -263,32 +221,12 @@ export default async function processDeployment({
// Handle alias-assigned event
if (event.type === 'alias-assigned') {
if (queuedSpinner) {
queuedSpinner();
}
if (buildSpinner) {
buildSpinner();
}
if (deploySpinner) {
deploySpinner();
}
deployingSpinner();
event.payload.indications = indications;
return event.payload;
}
}
} catch (err) {
if (queuedSpinner) {
queuedSpinner();
}
if (buildSpinner) {
buildSpinner();
}
if (deploySpinner) {
deploySpinner();
}
deployingSpinner();
output.stopSpinner();
throw err;
}
}

View File

@@ -1,3 +1,4 @@
import chalk from 'chalk';
import execa from 'execa';
import semver from 'semver';
import npa from 'npm-package-arg';
@@ -8,9 +9,10 @@ import { mkdirp, readJSON, writeJSON } from 'fs-extra';
import { NowBuildError, PackageJson } from '@vercel/build-utils';
import cliPkg from '../pkg';
import { NoBuilderCacheError } from '../errors-ts';
import cmd from '../output/cmd';
import { Output } from '../output';
import { getDistTag } from '../get-dist-tag';
import { NoBuilderCacheError } from '../errors-ts';
import * as staticBuilder from './static-builder';
import { BuilderWithPackage } from './types';
@@ -261,7 +263,9 @@ async function npmInstall(
throw new NowBuildError({
message:
(result as any).code === 'ENOENT'
? '`npm` is not installed'
? `Command not found: ${chalk.cyan(
'npm'
)}\nPlease ensure that ${cmd('npm')} is properly installed`
: 'Failed to install `vercel dev` dependencies',
code: 'NPM_INSTALL_ERROR',
link: 'https://vercel.link/npm-install-failed-dev',

View File

@@ -149,8 +149,8 @@ export async function executeBuild(
filesRemoved,
// This env distiniction is only necessary to maintain
// backwards compatibility with the `@vercel/next` builder.
env: envConfigs.runEnv,
buildEnv: envConfigs.buildEnv,
env: { ...envConfigs.runEnv },
buildEnv: { ...envConfigs.buildEnv },
},
};
@@ -280,7 +280,6 @@ export async function executeBuild(
path = extensionless;
}
delete output[path];
output[path] = value;
});
@@ -402,7 +401,7 @@ export async function getBuildMatches(
const builds = nowConfig.builds || [{ src: '**', use: '@vercel/static' }];
for (const buildConfig of builds) {
let { src, use } = buildConfig;
let { src = '**', use, config = {} } = buildConfig;
if (!use) {
continue;
@@ -437,6 +436,15 @@ export async function getBuildMatches(
for (const file of files) {
src = relative(cwd, file);
// Remove the output directory prefix
if (config.zeroConfig && config.outputDirectory) {
const outputMatch = config.outputDirectory + '/';
if (src.startsWith(outputMatch)) {
src = src.slice(outputMatch.length);
}
}
const builderWithPkg = await getBuilder(use, output);
matches.push({
...buildConfig,

View File

@@ -0,0 +1,41 @@
import { ProjectEnvType, ProjectEnvVariable } from '../../types';
import { Env } from '@vercel/build-utils';
function getSystemEnvValue(
systemEnvRef: string,
{ vercelUrl }: { vercelUrl?: string }
) {
if (systemEnvRef === 'VERCEL_URL') {
return vercelUrl || '';
}
return '';
}
export default function exposeSystemEnvs(
projectEnvs: ProjectEnvVariable[],
systemEnvValues: string[],
autoExposeSystemEnvs: boolean | undefined,
vercelUrl?: string
) {
const envs: Env = {};
if (autoExposeSystemEnvs) {
envs['VERCEL'] = '1';
envs['VERCEL_ENV'] = 'development';
for (const key of systemEnvValues) {
envs[key] = getSystemEnvValue(key, { vercelUrl });
}
}
for (let env of projectEnvs) {
if (env.type === ProjectEnvType.System) {
envs[env.key] = getSystemEnvValue(env.value, { vercelUrl });
} else {
envs[env.key] = env.value;
}
}
return envs;
}

View File

@@ -43,6 +43,7 @@ import {
} from '@vercel/build-utils';
import _frameworks, { Framework } from '@vercel/frameworks';
import cmd from '../output/cmd';
import link from '../output/link';
import { Output } from '../output';
import { relative } from '../path-helpers';
@@ -85,7 +86,8 @@ import {
HttpHeadersConfig,
EnvConfigs,
} from './types';
import { ProjectSettings } from '../../types';
import { ProjectEnvVariable, ProjectSettings } from '../../types';
import exposeSystemEnvs from './expose-system-envs';
const frameworkList = _frameworks as Framework[];
const frontendRuntimeSet = new Set(
@@ -149,14 +151,16 @@ export default class DevServer {
private updateBuildersTimeout: NodeJS.Timeout | undefined;
private startPromise: Promise<void> | null;
private environmentVars: Env | undefined;
private systemEnvValues: string[];
private projectEnvs: ProjectEnvVariable[];
constructor(cwd: string, options: DevServerOptions) {
this.cwd = cwd;
this.debug = options.debug;
this.output = options.output;
this.envConfigs = { buildEnv: {}, runEnv: {}, allEnv: {} };
this.environmentVars = options.environmentVars;
this.systemEnvValues = options.systemEnvValues || [];
this.projectEnvs = options.projectEnvs || [];
this.files = {};
this.address = '';
this.devCommand = options.devCommand;
@@ -491,7 +495,7 @@ export default class DevServer {
const dotenv = await fs.readFile(filePath, 'utf8');
this.output.debug(`Using local env: ${filePath}`);
env = parseDotenv(dotenv);
env = this.populateVercelEnvVars(env);
env = this.injectSystemValuesInDotenv(env);
} catch (err) {
if (err.code !== 'ENOENT') {
throw err;
@@ -642,10 +646,30 @@ export default class DevServer {
let allEnv = { ...buildEnv, ...runEnv };
// If no .env/.build.env is present, fetch and use cloud environment variables
// If no .env/.build.env is present, use cloud environment variables
if (Object.keys(allEnv).length === 0) {
const cloudEnv = this.populateVercelEnvVars(this.environmentVars);
allEnv = runEnv = buildEnv = cloudEnv;
const cloudEnv = exposeSystemEnvs(
this.projectEnvs || [],
this.systemEnvValues || [],
this.projectSettings?.autoExposeSystemEnvs,
new URL(this.address).host
);
allEnv = { ...cloudEnv };
runEnv = { ...cloudEnv };
buildEnv = { ...cloudEnv };
}
// legacy NOW_REGION env variable
runEnv['NOW_REGION'] = 'dev1';
buildEnv['NOW_REGION'] = 'dev1';
allEnv['NOW_REGION'] = 'dev1';
// mirror how VERCEL_REGION is injected in prod/preview
// only inject in `runEnvs`, because `allEnvs` is exposed to dev command
// and should not contain VERCEL_REGION
if (this.projectSettings?.autoExposeSystemEnvs) {
runEnv['VERCEL_REGION'] = 'dev1';
}
this.envConfigs = { buildEnv, runEnv, allEnv };
@@ -753,23 +777,15 @@ export default class DevServer {
return merged;
}
populateVercelEnvVars(env: Env | undefined): Env {
if (!env) {
return {};
}
injectSystemValuesInDotenv(env: Env): Env {
for (const name of Object.keys(env)) {
if (name === 'VERCEL_URL') {
const host = new URL(this.address).host;
env['VERCEL_URL'] = host;
env['VERCEL_URL'] = new URL(this.address).host;
} else if (name === 'VERCEL_REGION') {
env['VERCEL_REGION'] = 'dev1';
}
}
// Always set NOW_REGION to match production
env['NOW_REGION'] = 'dev1';
return env;
}
@@ -871,7 +887,7 @@ export default class DevServer {
})
.catch(err => {
this.updateBuildersPromise = null;
this.output.error(`Failed to update builders: ${err.message}`);
this.output.prettyError(err);
this.output.debug(err.stack);
});
}, ms('30s'));
@@ -1658,15 +1674,24 @@ export default class DevServer {
isDev: true,
requestPath,
devCacheDir,
env: envConfigs.runEnv,
buildEnv: envConfigs.buildEnv,
env: { ...envConfigs.runEnv },
buildEnv: { ...envConfigs.buildEnv },
},
});
} catch (err) {
// `startDevServer()` threw an error. Most likely this means the dev
// server process exited before sending the port information message
// (missing dependency at runtime, for example).
debug(`Error starting "${builderPkg.name}" dev server: ${err}`);
if (err.code === 'ENOENT') {
err.message = `Command not found: ${chalk.cyan(
err.path,
...err.spawnargs
)}\nPlease ensure that ${cmd(err.path)} is properly installed`;
err.link = 'https://vercel.link/command-not-found';
}
this.output.prettyError(err);
await this.sendError(
req,
res,
@@ -1879,6 +1904,12 @@ export default class DevServer {
requestPath: string,
nowRequestId: string
): boolean {
// If the "directory listing" feature is disabled in the
// Project's settings, then don't render the directory listing
if (this.projectSettings?.directoryListing === false) {
return false;
}
let prefix = requestPath;
if (prefix.length > 0 && !prefix.endsWith('/')) {
prefix += '/';

View File

@@ -1,7 +1,7 @@
import { basename, extname, join } from 'path';
import {
FileFsRef,
BuildOptions,
shouldServe as defaultShouldServe,
ShouldServeOptions,
} from '@vercel/build-utils';
import { BuildResult } from './types';
@@ -11,50 +11,32 @@ export const version = 2;
export function build({
files,
entrypoint,
config,
config: { zeroConfig, outputDirectory },
}: BuildOptions): BuildResult {
let path = entrypoint;
const outputDir = config.zeroConfig ? config.outputDirectory : '';
const outputMatch = outputDir + '/';
if (outputDir && path.startsWith(outputMatch)) {
// static output files are moved to the root directory
path = path.slice(outputMatch.length);
}
const output = {
[path]: files[entrypoint] as FileFsRef,
const path =
zeroConfig && outputDirectory
? `${outputDirectory}/${entrypoint}`
: entrypoint;
return {
output: {
[entrypoint]: files[path] as FileFsRef,
},
routes: [],
watch: [path],
};
const watch = [path];
return { output, routes: [], watch };
}
export function shouldServe({
entrypoint,
files,
requestPath,
config = {},
}: ShouldServeOptions) {
let outputPrefix = '';
const outputDir = config.zeroConfig ? config.outputDirectory : '';
const outputMatch = outputDir + '/';
if (outputDir && entrypoint.startsWith(outputMatch)) {
// static output files are moved to the root directory
entrypoint = entrypoint.slice(outputMatch.length);
outputPrefix = outputMatch;
export function shouldServe(_opts: ShouldServeOptions) {
const opts = { ..._opts };
let {
config: { zeroConfig, outputDirectory },
} = opts;
// Add the output directory prefix
if (zeroConfig && outputDirectory) {
opts.entrypoint = `${outputDirectory}/${opts.entrypoint}`;
opts.requestPath = `${outputDirectory}/${opts.requestPath}`;
}
const isMatch = (f: string) => entrypoint === f && outputPrefix + f in files;
if (isIndex(entrypoint)) {
const indexPath = join(requestPath, basename(entrypoint));
if (isMatch(indexPath)) {
return true;
}
}
return isMatch(requestPath);
}
function isIndex(path: string): boolean {
const ext = extname(path);
const name = basename(path, ext);
return name === 'index';
return defaultShouldServe(opts);
}

View File

@@ -17,7 +17,7 @@ import {
import { NowConfig } from '@vercel/client';
import { HandleValue, Route } from '@vercel/routing-utils';
import { Output } from '../output';
import { ProjectSettings } from '../../types';
import { ProjectEnvVariable, ProjectSettings } from '../../types';
export { NowConfig };
@@ -27,7 +27,8 @@ export interface DevServerOptions {
devCommand?: string;
frameworkSlug?: string;
projectSettings?: ProjectSettings;
environmentVars?: Env;
systemEnvValues?: string[];
projectEnvs?: ProjectEnvVariable[];
}
export interface EnvConfigs {
@@ -49,6 +50,7 @@ export interface EnvConfigs {
export interface BuildMatch extends BuildConfig {
entrypoint: string;
src: string;
builderWithPkg: BuilderWithPackage;
buildOutput: BuilderOutputs;
buildResults: Map<string | null, BuildResult>;

View File

@@ -12,11 +12,12 @@ type Response = {
export default async function purchaseDomain(
client: Client,
name: string,
expectedPrice: number
expectedPrice: number,
renew: boolean = true
) {
try {
return await client.fetch<Response>(`/v3/domains/buy`, {
body: { name, expectedPrice },
body: { name, expectedPrice, renew },
method: 'POST',
});
} catch (error) {

View File

@@ -1,17 +0,0 @@
import { Output } from '../output';
import Client from '../client';
import { Secret } from '../../types';
export default async function getDecryptedSecret(
output: Output,
client: Client,
secretId: string
): Promise<string> {
if (!secretId) {
return '';
}
output.debug(`Fetching decrypted secret ${secretId}`);
const url = `/v2/now/secrets/${secretId}?decrypt=true`;
const secret = await client.fetch<Secret>(url);
return secret.value;
}

View File

@@ -0,0 +1,12 @@
import { Output } from '../output';
import Client from '../client';
export default async function getSystemEnvValues(
output: Output,
client: Client,
projectId: string
) {
output.debug(`Fetching System Environment Values of project ${projectId}`);
const url = `/v6/projects/${projectId}/system-env-values`;
return client.fetch<{ systemEnvValues: string[] }>(url);
}

View File

@@ -1,6 +1,6 @@
import { Output } from '../output';
import Client from '../client';
import { ProjectEnvTarget, Secret, ProjectEnvVariableV5 } from '../../types';
import { ProjectEnvTarget, ProjectEnvVariableV5 } from '../../types';
export default async function removeEnvRecord(
output: Output,
@@ -18,32 +18,7 @@ export default async function removeEnvRecord(
envName
)}${qs}`;
const env = await client.fetch<ProjectEnvVariableV5>(urlProject, {
await client.fetch<ProjectEnvVariableV5>(urlProject, {
method: 'DELETE',
});
if (env && env.value) {
const idOrName = env.value.startsWith('@') ? env.value.slice(1) : env.value;
const urlSecret = `/v2/now/secrets/${idOrName}`;
let secret: Secret | undefined;
try {
secret = await client.fetch<Secret>(urlSecret);
} catch (error) {
if (error && error.status === 404) {
// User likely deleted the secret before the env var, so we can still report success
output.debug(
`Skipped ${env.key} because secret ${idOrName} was already deleted`
);
return;
}
throw error;
}
// Since integrations add global secrets, we must only delete if the secret was
// specifically added to this project
if (secret && secret.projectId === projectId) {
await client.fetch<Secret>(urlSecret, { method: 'DELETE' });
}
}
}

View File

@@ -1,32 +0,0 @@
export const SYSTEM_ENV_VALUES = [
'VERCEL_URL',
'VERCEL_GITHUB_COMMIT_ORG',
'VERCEL_GITHUB_COMMIT_REF',
'VERCEL_GITHUB_ORG',
'VERCEL_GITHUB_DEPLOYMENT',
'VERCEL_GITHUB_COMMIT_REPO',
'VERCEL_GITHUB_REPO',
'VERCEL_GITHUB_COMMIT_AUTHOR_LOGIN',
'VERCEL_GITHUB_COMMIT_AUTHOR_NAME',
'VERCEL_GITHUB_COMMIT_SHA',
'VERCEL_GITLAB_DEPLOYMENT',
'VERCEL_GITLAB_PROJECT_NAMESPACE',
'VERCEL_GITLAB_PROJECT_NAME',
'VERCEL_GITLAB_PROJECT_ID',
'VERCEL_GITLAB_PROJECT_PATH',
'VERCEL_GITLAB_COMMIT_REF',
'VERCEL_GITLAB_COMMIT_SHA',
'VERCEL_GITLAB_COMMIT_MESSAGE',
'VERCEL_GITLAB_COMMIT_AUTHOR_LOGIN',
'VERCEL_GITLAB_COMMIT_AUTHOR_NAME',
'VERCEL_BITBUCKET_DEPLOYMENT',
'VERCEL_BITBUCKET_REPO_OWNER',
'VERCEL_BITBUCKET_REPO_SLUG',
'VERCEL_BITBUCKET_REPO_NAME',
'VERCEL_BITBUCKET_COMMIT_REF',
'VERCEL_BITBUCKET_COMMIT_SHA',
'VERCEL_BITBUCKET_COMMIT_MESSAGE',
'VERCEL_BITBUCKET_COMMIT_AUTHOR_NAME',
'VERCEL_BITBUCKET_COMMIT_AUTHOR_URL',
'VERCEL_BITBUCKET_COMMIT_AUTHOR_AVATAR',
];

View File

@@ -7,16 +7,21 @@ import { eraseLines } from 'ansi-escapes';
import jsonlines from 'jsonlines';
import retry from 'async-retry';
// Utilities
import createOutput from './output';
async function printEvents(
now,
deploymentIdOrURL,
currentTeam = null,
{ mode, onOpen = () => {}, onEvent, quiet, debugEnabled, findOpts } = {}
{
mode,
onOpen = () => {},
onEvent,
quiet,
debugEnabled,
findOpts,
output,
} = {}
) {
const { log, debug } = createOutput({ debug: debugEnabled });
const { log, debug } = output;
let onOpenCalled = false;
function callOnOpenOnce() {

View File

@@ -1,52 +1,65 @@
import getEnvVariables from './env/get-env-records';
import getDecryptedSecret from './env/get-decrypted-secret';
import Client from './client';
import { Output } from './output/create-output';
import { ProjectEnvTarget, Project, ProjectEnvType } from '../types';
import { Env } from '@vercel/build-utils';
import {
ProjectEnvTarget,
ProjectEnvType,
ProjectEnvVariable,
Secret,
} from '../types';
import getEnvRecords from './env/get-env-records';
export default async function getDecryptedEnvRecords(
output: Output,
client: Client,
project: Project,
target: ProjectEnvTarget
): Promise<Env> {
const { envs } = await getEnvVariables(output, client, project.id, target);
const decryptedValues = await Promise.all(
envs.map(async env => {
if (env.type === ProjectEnvType.System) {
return { value: '', found: true };
} else if (env.type === ProjectEnvType.Plaintext) {
return { value: env.value, found: true };
projectId: string
): Promise<{ envs: ProjectEnvVariable[] }> {
const { envs } = await getEnvRecords(
output,
client,
projectId,
ProjectEnvTarget.Development
);
const envsWithDecryptedSecrets = await Promise.all(
envs.map(async ({ type, key, value }) => {
// it's not possible to create secret env variables for development
// anymore but we keep this because legacy env variables with "decryptable"
// secret values still exit in our system
if (type === ProjectEnvType.Secret) {
try {
const secretIdOrName = value;
if (!secretIdOrName) {
return { type, key, value: '', found: true };
}
output.debug(`Fetching decrypted secret ${secretIdOrName}`);
const secret = await client.fetch<Secret>(
`/v2/now/secrets/${secretIdOrName}?decrypt=true`
);
return { type, key, value: secret.value, found: true };
} catch (error) {
if (error && error.status === 404) {
return { type, key, value: '', found: false };
}
throw error;
}
}
try {
const value = await getDecryptedSecret(output, client, env.value);
return { value, found: true };
} catch (error) {
if (error && error.status === 404) {
return { value: '', found: false };
}
throw error;
}
return { type, key, value, found: true };
})
);
const results: Env = {};
for (let i = 0; i < decryptedValues.length; i++) {
const { key } = envs[i];
const { value, found } = decryptedValues[i];
if (!found) {
for (let env of envsWithDecryptedSecrets) {
if (!env.found) {
output.print('');
output.warn(
`Unable to download variable ${key} because associated secret was deleted`
`Unable to download variable ${env.key} because associated secret was deleted`
);
continue;
}
results[key] = value ? value : '';
}
return results;
return { envs: envsWithDecryptedSecrets };
}

View File

@@ -23,6 +23,7 @@ export default class Now extends EventEmitter {
forceNew = false,
withCache = false,
debug = false,
output = createOutput({ debug }),
}) {
super();
@@ -30,7 +31,7 @@ export default class Now extends EventEmitter {
this._debug = debug;
this._forceNew = forceNew;
this._withCache = withCache;
this._output = createOutput({ debug });
this._output = output;
this._apiUrl = apiUrl;
this._onRetry = this._onRetry.bind(this);
this.currentTeam = currentTeam;

View File

@@ -23,7 +23,7 @@ export default async function promptBool(label: string, options: Options = {}) {
trailing = '',
} = options;
return new Promise(resolve => {
return new Promise<boolean>(resolve => {
const isRaw = Boolean(stdin && stdin.isRaw);
if (stdin) {

View File

@@ -3,7 +3,6 @@ import chalk from 'chalk';
import { remove } from 'fs-extra';
import { NowContext, ProjectLinkResult, ProjectSettings } from '../../types';
import { NowConfig } from '../dev/types';
import { Output } from '../output';
import {
getLinkedProject,
linkFolderToProject,
@@ -30,7 +29,6 @@ import Now from '../index';
export default async function setupAndLink(
ctx: NowContext,
output: Output,
path: string,
forceDelete: boolean,
autoConfirm: boolean,
@@ -39,15 +37,17 @@ export default async function setupAndLink(
): Promise<ProjectLinkResult> {
const {
authConfig: { token },
apiUrl,
output,
config,
} = ctx;
const { apiUrl } = ctx;
const debug = output.isDebugEnabled();
const client = new Client({
apiUrl,
token,
currentTeam: config.currentTeam,
debug,
output,
});
const isFile = !isDirectory(path);
@@ -161,6 +161,7 @@ export default async function setupAndLink(
apiUrl,
token,
debug,
output,
currentTeam: client.currentTeam,
});
const createArgs: any = {

View File

@@ -3,16 +3,33 @@ import boxen from 'boxen';
import { format } from 'util';
import { Console } from 'console';
import renderLink from './link';
import wait from './wait';
import wait, { StopSpinner } from './wait';
export type Output = ReturnType<typeof createOutput>;
export type Output = ReturnType<typeof _createOutput>;
export interface OutputOptions {
debug?: boolean;
}
// Singleton
let instance: Output | null = null;
export default function createOutput(opts?: OutputOptions) {
if (!instance) {
instance = _createOutput(opts);
}
return instance;
}
function _createOutput({ debug: debugEnabled = false }: OutputOptions = {}) {
let spinner: StopSpinner | null = null;
export default function createOutput({ debug: debugEnabled = false } = {}) {
function isDebugEnabled() {
return debugEnabled;
}
function print(str: string) {
stopSpinner();
process.stderr.write(str);
}
@@ -94,18 +111,32 @@ export default function createOutput({ debug: debugEnabled = false } = {}) {
}
}
function spinner(message: string, delay: number = 300) {
function setSpinner(message: string, delay: number = 300): StopSpinner {
if (debugEnabled) {
debug(`Spinner invoked (${message}) with a ${delay}ms delay`);
let isEnded = false;
return () => {
const stop = (() => {
if (isEnded) return;
isEnded = true;
debug(`Spinner ended (${message})`);
};
}) as StopSpinner;
stop.text = message;
return stop;
}
return wait(message, delay);
if (spinner) {
spinner.text = message;
} else {
spinner = wait(message, delay);
}
return spinner;
}
function stopSpinner() {
if (spinner) {
spinner();
spinner = null;
}
}
const c = {
@@ -141,6 +172,7 @@ export default function createOutput({ debug: debugEnabled = false } = {}) {
dim,
time,
note,
spinner,
spinner: setSpinner,
stopSpinner,
};
}

View File

@@ -1 +1,2 @@
export { default, Output } from './create-output';
export { StopSpinner } from './wait';

View File

@@ -2,28 +2,50 @@ import ora from 'ora';
import chalk from 'chalk';
import eraseLines from './erase-lines';
export default function wait(msg: string, delay: number = 300, _ora = ora) {
let spinner: ReturnType<typeof _ora>;
let running = false;
export interface StopSpinner {
(): void;
text: string;
}
const planned = setTimeout(() => {
export default function wait(
msg: string,
delay: number = 300,
_ora = ora
): StopSpinner {
let spinner: ReturnType<typeof _ora> | null = null;
const timeout = setTimeout(() => {
spinner = _ora(chalk.gray(msg));
spinner.color = 'gray';
spinner.start();
running = true;
}, delay);
const cancel = () => {
clearTimeout(planned);
if (running) {
const stop = () => {
clearTimeout(timeout);
if (spinner) {
spinner.stop();
spinner = null;
process.stderr.write(eraseLines(1));
running = false;
}
process.removeListener('nowExit', cancel);
};
stop.text = msg;
// Allow `text` property to update the text while the spinner is in action
Object.defineProperty(stop, 'text', {
get() {
return msg;
},
set(v: string) {
msg = v;
if (spinner) {
spinner.text = chalk.gray(v);
}
},
});
// @ts-ignore
process.on('nowExit', cancel);
return cancel;
process.once('nowExit', stop);
return stop;
}

View File

@@ -0,0 +1,4 @@
.next
!public
yarn.lock
.vercel

View File

@@ -1,10 +1,12 @@
{
"private": true,
"scripts": {
"dev": "next dev",
"build": "next build"
},
"dependencies": {
"next": "canary",
"react": "^16.8.6",
"react-dom": "^16.8.6"
"react": "^17.0.0",
"react-dom": "^17.0.0"
}
}

View File

@@ -0,0 +1,15 @@
import Image from 'next/image';
export default function Home() {
return (
<>
<h1>Home Page</h1>
<hr />
<h2>Optimized</h2>
<Image src="/test.png" width="400" height="400" />
<hr />
<h2>Original</h2>
<img src="/test.png" width="400" height="400" />
</>
);
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

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