Compare commits

..

122 Commits

Author SHA1 Message Date
Steven
a97bc2af6d Publish Canary
- @vercel/build-utils@2.5.1-canary.0
 - vercel@20.0.1-canary.0
 - @vercel/client@9.0.1-canary.0
2020-08-24 11:19:44 -04:00
Steven
a5dc90c8cd Revert "[build-utils] Use handle: error for api 404" (#5088)
* Revert "[build-utils] Use `handle: error` for api 404 (#5064)"

This reverts commit 02e1c921ac.

* Re-enable cli changes

* Re-enable redwood changes

* Minor cleanup
2020-08-24 11:16:32 -04:00
Steven
e90521b5a4 Publish Stable
- @vercel/frameworks@0.1.0
 - @vercel/build-utils@2.5.0
 - vercel@20.0.0
 - @vercel/client@9.0.0
 - @vercel/go@1.1.5
 - @vercel/next@2.6.23
 - @vercel/node@1.8.0
 - @vercel/routing-utils@1.8.4
 - @vercel/static-build@0.17.7
 - @vercel/redwood@0.1.0
2020-08-24 08:25:30 -04:00
Steven
10c598834a Publish Canary
- @vercel/build-utils@2.4.3-canary.5
 - vercel@20.0.0-canary.26
 - @vercel/client@8.2.2-canary.10
2020-08-21 17:53:14 -04:00
Steven
e58b34b82c [build-utils] Fix framework "Other" with Next.js for old projects (#5074)
Since released PR #5009, we had some users that depend on the broken behavior. This adds an arbitrary date so that old projects will use the old broken behavior, and new projects will use the new behavior from #5009.

The reason for the date is that we don't want to force users to create new projects who noticed this broken behavior months ago like this discussion: https://github.com/vercel/vercel/discussions/4138

Its better to be a bit aggressive and set the date far back because users with that want the old behavior have a workaround: select Next.js in the framework dropdown. However, users that want the new behavior with an old project will be stuck without a solution.
2020-08-21 21:51:40 +00:00
Steven
02e1c921ac [cli][build-utils] Use handle: error for api 404 (#5064)
This PR does a few things:

- Consolidates build-utils to use both 404 routes in `handle: error` phase
- Fixes `vc dev` to process `handle: error` phase after the `handle: filesystem` phase
- Updates `/api` 404 route to be excluded for frameworks like RedwoodJS which use their own functions
2020-08-21 20:29:39 +00:00
Steven
fcb14fa70e Publish Canary
- @vercel/redwood@0.0.2-canary.8
2020-08-19 18:26:12 -04:00
Steven
46e18128c7 [redwood] Add support for functions and apiProxyPath config (#5063)
This PR adds two missing features to `@vercel/redwood`:

1. Support for [`functions`](https://vercel.com/docs/configuration#project/functions) in `vercel.json`
2. Support for [`apiProxyPath`](https://redwoodjs.com/docs/app-configuration-redwood-toml#apiproxypath) in `redwood.toml`
3. Support for caching yarn workspaces which has `node_modules` outside the root directory
2020-08-19 22:16:53 +00:00
dav-is
e06d5247ef Publish Canary
- vercel@20.0.0-canary.25
 - @vercel/client@8.2.2-canary.9
2020-08-19 16:40:45 -04:00
Connor Davis
02195b92a7 [client][cli] Add Learn More Option to Warnings (#5022)
The deployment API might return a learn more link to address warnings, tips, or notices

CH-568

```
HTTP/2 200
...
x-vercel-warning-bad-error: Something went wrong
x-vercel-link-bad-error: https://vercel.link/bad-error

x-vercel-notice-low-funds: You are low on funds
x-vercel-link-low-funds: https://vercel.link/increase-funds
...
```

Should output as:
```
Warning: Something went wrong
Learn More: https://vercel.link/bad-error

Notice: You are low on funds
Learn More: https://vercel.link/increase-funds
```
2020-08-19 20:19:28 +00:00
Andy Bitz
7945155a0f Publish Canary
- vercel@20.0.0-canary.24
 - @vercel/next@2.6.23-canary.0
 - @vercel/node@1.7.5-canary.2
 - @vercel/routing-utils@1.8.4-canary.5
2020-08-19 15:01:35 +02:00
Andy
edb6043c2c [cli] Always show renewal price for domains inspect (#5060)
* [cli] Always show renwal price for `domains inspect`

* Update test
2020-08-19 15:00:33 +02:00
Markoz Peña
971481ba51 [node] Add res.redirect() helper method (#4947)
This pull request adds a method redirect, the behavior will be the same as that of Next.js with `pages/api`. This PR takes into account the change that is being made in Next.js with redirect due to the [error that was reported](https://github.com/vercel/next.js/issues/15594) a few hours ago.

Co-authored-by: Steven <steven@ceriously.com>
2020-08-18 18:58:54 -04:00
JJ Kasper
db8e456603 Publish canary
- @vercel/routing-utils@1.8.4-canary.4
2020-08-18 12:37:22 -05:00
JJ Kasper
d1b4f24a4a Publish Stable
- @vercel/next@2.6.22
2020-08-18 12:25:16 -05:00
JJ Kasper
4eb39ed53b Publish Canary
- @vercel/next@2.6.22-canary.1
- @vercel/routing-utils@1.8.4-canary.3
2020-08-18 12:19:58 -05:00
JJ Kasper
6c7236ffd5 [routing-utils] Attempt normal path-to-regexp replacing before custom (#5053)
This corrects behavior that was changed when switching to use `safelyCompile` which caused some `path-to-regexp` replacing behavior to be changed like not removing brackets `{}` around params which are removed in Next.js. This updates to first attempt to use the normal replacing behavior from `path-to-regexp` falling back to the safe compiling logic added when that fails. 

Additional tests have been added to ensure the brackets are stripped as expected when compiling with `path-to-regexp`
2020-08-18 16:57:38 +00:00
Steven
f0def040ac Publish Canary
- vercel@20.0.0-canary.23
 - @vercel/next@2.6.22-canary.0
2020-08-17 17:32:42 -04:00
Steven
ee8dff2dc9 [cli] Remove unused dependencies (#5052)
This PR is a follow up to removing v1 in PR #5011
2020-08-17 21:28:17 +00:00
Steven
df86a6d48c [cli] Fix update message (#5051)
The update message was sometimes printed when there was no update.

This PR fixes the update check to ensure the that it is only printed when the latest version differs from the current version.
2020-08-17 16:15:12 -04:00
JJ Kasper
b3958b3d12 Publish Stable
- @vercel/next@2.6.21
2020-08-17 09:48:34 -05:00
JJ Kasper
8dd22703a7 Publish Canary
- @vercel/next@2.6.21-canary.1
2020-08-17 09:36:53 -05:00
JJ Kasper
c7e2280d23 [next] Fix trailing slash and 404 with next export (#5048)
This fixes the case where `trailingSlash: true` is used with `next export` causing the `404` page to be output as `404/index.html` and we were previously only checking for the 404 output at `404`. This also adds a regression test for this behavior. 

Closes: https://github.com/vercel/next.js/issues/16218
2020-08-17 14:12:22 +00:00
Steven
2c239a6ee2 Publish Canary
- vercel@20.0.0-canary.22
 - @vercel/next@2.6.21-canary.0
 - @vercel/routing-utils@1.8.4-canary.2
2020-08-14 18:32:42 -04:00
Steven
43709fbc74 Remove unused dependencies (#5042)
The CLI doesn't depend on `@vercel/redwood` for `vc dev` since PR #5036 so we can remove it.

Similarly, zero config does not install `@vercel/next` or `@vercel/static-build` during `vc dev` so these can be removed too.

In the case where the user defines `builds` in vercel.json, the runtimes will be installed during the first run of `vc dev`.
2020-08-14 18:26:46 -04:00
JJ Kasper
52cd486e1d Publish Stable
- @vercel/next@2.6.20
2020-08-14 16:17:28 -05:00
JJ Kasper
175f302a7f Publish Canary
- @vercel/next@2.6.20-canary.1
- @vercel/routing-utils@1.8.4-canary.1
2020-08-14 15:36:58 -05:00
JJ Kasper
070bbd5825 [routing-utils] Update rewrite params query appending (#5040)
This updates to not automatically append params to the query for rewrites if one or more of the params are already used in the destination's path. No other behavior is being changed and if the user still wants the params in the query after using them in the destination's path they can manually add them like with redirects.

x-ref: https://github.com/vercel/next.js/pull/16189
2020-08-14 15:59:21 +00:00
Steven
cdeb4004c9 Publish Canary
- vercel@20.0.0-canary.21
 - @vercel/redwood@0.0.2-canary.7
2020-08-13 19:58:42 -04:00
Steven
6bb162e5f5 [cli][redwood] Update frontend framework detection in vc dev (#5036)
This PR uses the the new property added in PR #5034 to determine if a framework has a frontend runtime defined.

It also reverts a couple workarounds add in PR #4937 which was added for RedwoodJS which is no longer necessary since RedwoodJS defines a frontend framework the same way Next.js does.
2020-08-13 23:53:17 +00:00
Steven
8282442939 Publish Canary
- @vercel/frameworks@0.0.18-canary.5
 - @vercel/build-utils@2.4.3-canary.4
 - vercel@20.0.0-canary.20
 - @vercel/client@8.2.2-canary.8
 - @vercel/redwood@0.0.2-canary.6
2020-08-13 15:22:15 -04:00
Steven
1f4f2af2f2 [build-utils][frameworks] Add support for runtime properties (#5034)
This PR adds two properties to `frameworks.json`:

1. `useRuntime` - this moves the special case for non `@vercel/static-build` frontends, so that any framework can do the same as Next.js and RedwoodJS
2. `ignoreRuntimes` - this allows a framework to opt out of api detection such as RedwoodJS which handle's its own `.js` extensions

This also fixes 2 bugs discovered during implementing the feature:

1. `test-unit.yml` was not testing Node 12, it was testing 10 for both runs
2. `sortFilesBySegmentCount()` was non-deterministic causing node 10 and 12 to sort differently
2020-08-13 19:18:31 +00:00
Steven
f1bb0e465a [tests] Increase git fetch depth to support git 2.28 (#5032)
This PR fixes the CI error `fatal: origin/master...HEAD: no merge base`.

Git 2.28 change here: 1457886ce2

More info here: https://github.com/pre-commit/pre-commit/issues/1554
2020-08-13 13:31:08 +00:00
Steven
6dab4399c2 Publish Canary
- vercel@20.0.0-canary.19
 - @vercel/client@8.2.2-canary.7
2020-08-12 19:25:14 -04:00
Max Leiter
e87465d076 [cli][client] (Major) Remove legacy Now CLI 1.0 code (#5011)
It was easier to make a new PR than re-base the old ones:

https://github.com/zeit/now/pull/4017
https://github.com/vercel/vercel/pull/4602

Story: https://app.clubhouse.io/vercel/story/425/remove-all-the-zeit-now-1-0-code-from-vercel-cli
2020-08-12 23:22:21 +00:00
Steven
2f53b0dcdb Publish Canary
- @vercel/frameworks@0.0.18-canary.4
 - vercel@20.0.0-canary.18
 - @vercel/redwood@0.0.2-canary.5
2020-08-12 08:08:57 -04:00
Steven
b2e5212d0f [redwood] Update default build command (#5020)
This PR updates the default build command per @thedavidprice 

It also uses `@vercel/frameworks` as a source of truth for the build command.

If the user has a `build` script in `package.json`, that will take precedence over the default.
2020-08-11 19:08:18 +00:00
Steven
28c9999c10 Publish Canary
- vercel@20.0.0-canary.17
 - @vercel/redwood@0.0.2-canary.4
2020-08-10 17:54:00 -04:00
Steven
edb31fb412 [redwood] Fix file permissions and add support for build script (#4999)
Some files require execution privileges, such as Prisma, so we must preserve the file mode.

We also want redwood to behave the same as other frameworks and use `yarn build` if available.
2020-08-10 21:20:13 +00:00
Andy Bitz
2f0ea24552 Publish Canary
- vercel@20.0.0-canary.16
2020-08-10 22:05:12 +02:00
Andy
7d09fdc5a4 [cli] Update domains command output (#5018)
* [cli] Update `domain` command output

* Undo debug output

* Empty commit
2020-08-10 22:04:18 +02:00
Steven
23dce48b23 Publish Canary
- @vercel/build-utils@2.4.3-canary.3
 - vercel@20.0.0-canary.15
 - @vercel/client@8.2.2-canary.6
 - @vercel/static-build@0.17.7-canary.3
2020-08-10 13:00:57 -04:00
Steven
d7d308ca09 [build-utils] Fix framework "Other" when Next.js is dependency (#5009)
- Fixes discussion #4138
- Related to issue #3774
2020-08-10 16:49:26 +00:00
Steven
0e412361ad [tests] Fix Github Actions CI (#5010)
We're seeing some strange failures during `git diff` so I added it earlier to see if it fails earlier.
2020-08-07 19:37:17 -04:00
Yamagishi Kazutoshi
d563a99fb8 [static-build] Add Docusaurus v2 default routes for assets (#4973)
Reference: https://v2.docusaurus.io/docs/markdown-features/#assets

Co-authored-by: Steven <steven@ceriously.com>
2020-08-07 16:14:55 -04:00
Steven
6ce4c2e182 Publish Canary
- vercel@20.0.0-canary.14
 - @vercel/client@8.2.2-canary.5
 - @vercel/next@2.6.20-canary.0
 - @vercel/static-build@0.17.7-canary.2
2020-08-06 10:10:40 -04:00
Max Leiter
e31d6d4062 [client] Lower upload sempahore size from 700 to 50 connections (#4993)
CH Story:

https://app.clubhouse.io/vercel/story/5291/cli-connection-pooling

(Mac) script for watching count of active connections to API: 
`watch -n 0.1 'netstat -a -n | grep -E \'(13.52.46.156|52.9.164.177)\' | wc -l'`

Limiting the active connections to 50 greatly decreased the amount of active connections but did not noticeably increase the time to upload files. 

For each semaphore size below, I tested uploading 200 files of random size (between 50kb and 100kb) 5 times. More tests on varying file sizes / amounts showed similar results. 

Results:
```
Size: Average time in ms
50  : 25740 ms 
700 : 28763 ms
10  : 29339 ms
```

I also moved the the agent creation outside of the forEach loop when uploading. This is functionally the same (as node would use the same socket for the same hostname) but makes it easier to access the agent object if we need to debug in the future, and it seems unnecessary to create a new object each call.
2020-08-06 03:41:56 +00:00
Nathan Rajlich
f2f421b494 [cli] Default to os.EOL when creating .gitignore file (#4991)
This fixes the scenario where 1) is Windows and 2) there is no
`.gitignore` file in the project, so `vc link` creates one. Before, it was
defaulting to `\n`. Updated to use `os.EOL` so that `\r\n` is used on
Windows.

Also removed `encoding` option to `writeFile()` since `utf8` is already the
default.

Related to #4965.
2020-08-05 22:57:28 +00:00
Nathan Rajlich
46836e12b5 [client] (Major) Remove trailing / from default ignore list (#4810)
The trailing slash ends up making the root directory be accepted,
causing the ignore logic to traverse one level deep into the directory
that should be ignored. Removing the slash makes the root directory be
correctly ignored so that traversing one level into the ignored
directory is avoided, which is less work to do and thus is faster.

Before:

```
> [debug] [2020-07-07T23:22:05.768Z] Locating files /Users/nrajlich/project
> [debug] [2020-07-07T23:22:05.769Z] Ignoring /Users/nrajlich/project/.editorconfig
> [debug] [2020-07-07T23:22:05.769Z] Ignoring /Users/nrajlich/project/.git
> [debug] [2020-07-07T23:22:05.769Z] Ignoring /Users/nrajlich/project/.gitignore
> [debug] [2020-07-07T23:22:05.769Z] Ignoring /Users/nrajlich/project/.next
> [debug] [2020-07-07T23:22:05.769Z] Ignoring /Users/nrajlich/project/.vercel
> [debug] [2020-07-07T23:22:05.770Z] Ignoring /Users/nrajlich/project/.vercelignore
> [debug] [2020-07-07T23:22:05.770Z] Ignoring /Users/nrajlich/project/README.md
> [debug] [2020-07-07T23:22:05.770Z] Ignoring /Users/nrajlich/project/dist
> [debug] [2020-07-07T23:22:05.774Z] Ignoring /Users/nrajlich/project/node_modules/.bin
> [debug] [2020-07-07T23:22:05.775Z] Ignoring /Users/nrajlich/project/node_modules/.yarn-integrity
> [debug] [2020-07-07T23:22:05.775Z] Ignoring /Users/nrajlich/project/node_modules/@ampproject
> [debug] [2020-07-07T23:22:05.775Z] Ignoring /Users/nrajlich/project/node_modules/@babel
> [debug] [2020-07-07T23:22:05.775Z] Ignoring /Users/nrajlich/project/node_modules/@mrmlnc
> [debug] [2020-07-07T23:22:05.775Z] Ignoring /Users/nrajlich/project/node_modules/@next
> [debug] [2020-07-07T23:22:05.775Z] Ignoring /Users/nrajlich/project/node_modules/@nodelib
> [debug] [2020-07-07T23:22:05.775Z] Ignoring /Users/nrajlich/project/node_modules/@tootallnate
> [debug] [2020-07-07T23:22:05.775Z] Ignoring /Users/nrajlich/project/node_modules/@types
> [debug] [2020-07-07T23:22:05.775Z] Ignoring /Users/nrajlich/project/node_modules/@typescript-eslint
> [debug] [2020-07-07T23:22:05.775Z] Ignoring /Users/nrajlich/project/node_modules/@vercel
> [debug] [2020-07-07T23:22:05.775Z] Ignoring /Users/nrajlich/project/node_modules/@webassemblyjs
> [debug] [2020-07-07T23:22:05.775Z] Ignoring /Users/nrajlich/project/node_modules/@xtuc
> [debug] [2020-07-07T23:22:05.775Z] Ignoring /Users/nrajlich/project/node_modules/acorn
> [debug] [2020-07-07T23:22:05.775Z] Ignoring /Users/nrajlich/project/node_modules/acorn-jsx
> [debug] [2020-07-07T23:22:05.775Z] Ignoring /Users/nrajlich/project/node_modules/adjust-sourcemap-loader
> [debug] [2020-07-07T23:22:05.775Z] Ignoring /Users/nrajlich/project/node_modules/aggregate-error
> [debug] [2020-07-07T23:22:05.775Z] Ignoring /Users/nrajlich/project/node_modules/ajv
> [debug] [2020-07-07T23:22:05.775Z] Ignoring /Users/nrajlich/project/node_modules/ajv-errors
> [debug] [2020-07-07T23:22:05.775Z] Ignoring /Users/nrajlich/project/node_modules/ajv-keywords
> [debug] [2020-07-07T23:22:05.775Z] Ignoring /Users/nrajlich/project/node_modules/ally.js
> [debug] [2020-07-07T23:22:05.775Z] Ignoring /Users/nrajlich/project/node_modules/alphanum-sort
> [debug] [2020-07-07T23:22:05.776Z] Ignoring /Users/nrajlich/project/node_modules/anser
> [debug] [2020-07-07T23:22:05.776Z] Ignoring /Users/nrajlich/project/node_modules/ansi-escapes
> [debug] [2020-07-07T23:22:05.776Z] Ignoring /Users/nrajlich/project/node_modules/ansi-regex
> [debug] [2020-07-07T23:22:05.776Z] Ignoring /Users/nrajlich/project/node_modules/ansi-styles
> [debug] [2020-07-07T23:22:05.776Z] Ignoring /Users/nrajlich/project/node_modules/anymatch
> [debug] [2020-07-07T23:22:05.776Z] Ignoring /Users/nrajlich/project/node_modules/aproba
> [debug] [2020-07-07T23:22:05.776Z] Ignoring /Users/nrajlich/project/node_modules/argparse
> [debug] [2020-07-07T23:22:05.776Z] Ignoring /Users/nrajlich/project/node_modules/aria-query
> [debug] [2020-07-07T23:22:05.776Z] Ignoring /Users/nrajlich/project/node_modules/arity-n
> [debug] [2020-07-07T23:22:05.776Z] Ignoring /Users/nrajlich/project/node_modules/arr-diff
> [debug] [2020-07-07T23:22:05.776Z] Ignoring /Users/nrajlich/project/node_modules/arr-flatten
> [debug] [2020-07-07T23:22:05.776Z] Ignoring /Users/nrajlich/project/node_modules/arr-union
> [debug] [2020-07-07T23:22:05.776Z] Ignoring /Users/nrajlich/project/node_modules/array-find-index
> [debug] [2020-07-07T23:22:05.776Z] Ignoring /Users/nrajlich/project/node_modules/array-includes
…
… many more
…
> [debug] [2020-07-07T23:25:19.854Z] Locating files /Users/nrajlich/project: 20.117ms
```

After:

```
> [debug] [2020-07-07T23:24:37.403Z] Locating files /Users/nrajlich/project
> [debug] [2020-07-07T23:24:37.413Z] Ignoring /Users/nrajlich/project/.editorconfig
> [debug] [2020-07-07T23:24:37.414Z] Ignoring /Users/nrajlich/project/.git
> [debug] [2020-07-07T23:24:37.414Z] Ignoring /Users/nrajlich/project/.gitignore
> [debug] [2020-07-07T23:24:37.414Z] Ignoring /Users/nrajlich/project/.next
> [debug] [2020-07-07T23:24:37.414Z] Ignoring /Users/nrajlich/project/.vercel
> [debug] [2020-07-07T23:24:37.415Z] Ignoring /Users/nrajlich/project/.vercelignore
> [debug] [2020-07-07T23:24:37.415Z] Ignoring /Users/nrajlich/project/README.md
> [debug] [2020-07-07T23:24:37.415Z] Ignoring /Users/nrajlich/project/dist
> [debug] [2020-07-07T23:24:37.416Z] Ignoring /Users/nrajlich/project/node_modules
> [debug] [2020-07-07T23:25:19.854Z] Locating files /Users/nrajlich/project: 2.117ms
```

- Related to #3980
- Related to #3747
- Related to #4325 
- Related to #4627
2020-08-05 16:26:58 -04:00
Luis Alvarez D
3fc2611bf0 [examples] Update Next.js Example (#4984)
Related to https://github.com/vercel/docs/pull/2047 - The Next.js docs page will mention features from 9.5, but the current starter is quite outdated.
2020-08-05 19:11:12 +00:00
Steven
b4d8c411bd [examples][static-build] Update ionic-angular to 5.1.1 (#4986)
This PR adds the missing `package-lock.json` file and updates to latest `ionic-angular` boilerplate.
2020-08-05 13:53:29 -04:00
JJ Kasper
82b7d6980c Publish Stable
- @vercel/next@2.6.19
2020-08-05 11:31:59 -05:00
JJ Kasper
895b233605 Publish Canary
- @vercel/frameworks@0.0.18-canary.3
 - vercel@20.0.0-canary.13
 - @vercel/next@2.6.19-canary.1
2020-08-05 11:31:00 -05:00
Joe Haddad
d9e6b5348b [next] Blocking Fallback (ISG) Support (#4985) 2020-08-05 11:49:45 -04:00
Mark Glagola
1934a64864 [cli][certs rm] (Major) Bump to v5 certs API (#4943)
Bumps certs API from `/v3` to  `/v5`.

_Removing auto generated certs will be blocked in `/v5`_
2020-08-04 23:38:53 +00:00
Steven
c412642668 [cli] Handle unauthorized errors when user is not logged in (#4975)
We don't need to report these errors to Sentry because the user attempted a command that requires them to login.

https://sentry.io/organizations/vercel/issues/1498276418/?project=1323225

This will print:
- `NOT_AUTHORIZED`: "The specified token is not valid. Use vc login to generate a new token."
- `TEAM_DELETED`: "Your team was deleted. You can switch to a different one using vc switch"

### Related 
- Related to #4082 
- Related to #3857
2020-08-04 22:34:55 +00:00
Steven
793fe9aee1 [tests] Update cancel action to 0.4.1 (#4980)
Bump `cancel-workflow-action` to [0.4.1](https://github.com/styfle/cancel-workflow-action/releases/tag/0.4.1) to fix a bug where the wrong branch was cancelled.
2020-08-04 17:15:25 -04:00
Joe Haddad
ddc54d2ca4 [next] Run prettier (#4979) 2020-08-04 14:01:33 -07:00
Steven
39e5f0a364 [frameworks][examples] Adjust redwood logo size (#4976)
- Rename redwood logo to redwoodjs to match framework slug
- Update size to 48x48 to match other logos
2020-08-04 15:49:09 -04:00
Steven
43ed9ec859 [cli] Fix tests for .gitignore append during project link (#4978)
Fixes tests from #4965
2020-08-04 18:58:22 +00:00
Kid
8ba44fca79 [cli] Fix .gitignore append during project link (#4965)
* [cli] fix .gitignore updating check

* Detect file EOL

* Semicolon

* Add trailing newline

Co-authored-by: Nathan Rajlich <n@n8.io>
2020-08-04 10:29:08 -07:00
Andy Bitz
27dbefaecf Publish Canary
- vercel@20.0.0-canary.12
 - @vercel/next@2.6.19-canary.0
2020-08-04 19:10:28 +02:00
Andy
b4a13913c7 [cli] Adjust output for recently changed domain commands (#4959)
* [cli] Adjust output for recently changed domain commands

* Update the inspect and list page

* Remove test

* Update packages/now-cli/src/commands/domains/inspect.ts

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

* Change output

* Remove workaround

* Update error

* Include contextName

Co-authored-by: Steven <steven@ceriously.com>
2020-08-04 18:49:46 +02:00
JJ Kasper
f842266b2e Publish Stable
- @vercel/next@2.6.18
2020-08-04 09:27:01 -05:00
JJ Kasper
ad0cc858ed Publish Canary
- vercel@20.0.0-canary.11
 - @vercel/next@2.6.18-canary.0
 - @vercel/node@1.7.5-canary.1
2020-08-04 09:06:29 -05:00
JJ Kasper
4bb7180de9 [next] Ensure trailing slash resolves to functions correctly (#4972)
This makes sure the routes mapping to the serverless functions handle the trailing slash being present or not. This also adds additional test cases to ensure it is functioning correctly
2020-08-04 03:35:50 +00:00
Steven
7a4faa480d [node][next] Bump node-file-trace to 0.8.2 (#4969)
Bump `node-file-trace` to [0.8.2](https://github.com/vercel/node-file-trace/releases/tag/0.8.2)
2020-08-03 11:09:52 -04:00
JJ Kasper
421be5d738 Publish Stable
- @vercel/next@2.6.17
2020-07-31 10:29:04 -05:00
JJ Kasper
b8eaf10974 Publish Canary
- vercel@20.0.0-canary.10
 - @vercel/next@2.6.17-canary.1
2020-07-31 10:20:59 -05:00
JJ Kasper
92a4bf27cf [next] Fix monorepo build edge case without build script (#4956)
This fixes an edge case where a monorepo Next.js app fails to build due to running `next build` manually instead of via `package.json` scripts like we previously were doing. 

Failing project with current `@vercel/next` running `next build` manually: https://vercel.com/jj4/test2020/15fhg1ko3
Succeeding project running changing back to running `next build` through a `package.json` script: https://vercel.com/jj4/test2020/dths5f1nv

x-ref: https://github.com/vercel/vercel/pull/4863
Fixes: https://github.com/vercel/next.js/issues/15713
2020-07-31 15:17:14 +00:00
Steven
2672838b64 Publish Canary
- vercel@20.0.0-canary.9
 - @vercel/next@2.6.17-canary.0
 - @vercel/redwood@0.0.2-canary.3
2020-07-31 09:15:51 -04:00
Steven
1c96071ddc [cli][redwood] Update redwood port detection in vc dev (#4937)
Fixes redwood support for `vc dev`
2020-07-31 13:14:04 +00:00
Steven
71cdf759da [tests] Ignore custom next 404 (#4955)
Commenting out this test assertion until we can fix it in #4946
2020-07-30 20:40:41 -04:00
Torsten Dittmann
93ebd213de [examples] Update deprecated and outdated Svelte example packages (#4706) 2020-07-30 15:44:39 -07:00
JJ Kasper
a32ba8f214 Publish Stable
- @vercel/next@2.6.16
2020-07-30 14:15:29 -05:00
JJ Kasper
d416f70a6e Publish Canary
- @vercel/frameworks@0.0.18-canary.2
 - vercel@20.0.0-canary.8
 - @vercel/next@2.6.16-canary.0
 - @vercel/node@1.7.5-canary.0
 - @vercel/routing-utils@1.8.4-canary.0
 - @vercel/redwood@0.0.2-canary.2
2020-07-30 13:45:26 -05:00
JJ Kasper
ba9e1dd0ba [routing-utils] Update header replacing to handle more cases (#4942)
This adds handling for more cases while updating header values to make sure to escape any characters that could break compiling with `path-to-regexp`

x-ref: https://github.com/vercel/next.js/pull/15592
2020-07-30 18:22:30 +00:00
Steven
d513f74b70 [frameworks][redwood] Bump to RedwoodJS to 0.15.0 (#4953)
Implements https://github.com/redwoodjs/redwood/pull/904
2020-07-30 10:52:04 -04:00
JJ Kasper
47f92f8f14 Publish Stable
- @vercel/next@2.6.15
- @vercel/node@1.7.4
2020-07-29 15:43:53 -05:00
JJ Kasper
9aa669d735 Publish Canary
- vercel@20.0.0-canary.7
 - @vercel/next@2.6.15-canary.0
2020-07-29 15:41:14 -05:00
JJ Kasper
99cab6f34a [next] Fix lambda opt-out for index page (#4950)
This makes sure we detect a fully static `/index` page correctly since the `prerender-manifest` lists it as `/` and we were expecting `/index` during the lambda building opt-out check

x-ref: https://github.com/vercel/next.js/discussions/15619
2020-07-29 16:36:13 -04:00
JJ Kasper
547e7dccf7 [next] Add test cases for route /index handling (#4002)
This adds some test cases for `/index` handling to ensure we are being consistent with local development and production
2020-07-29 17:49:28 +00:00
JJ Kasper
dce9a89407 Publish Stable
- @vercel/next@2.6.14
2020-07-29 08:55:06 -05:00
Steven
c1cdddd974 Publish Canary
- vercel@20.0.0-canary.6
2020-07-28 17:29:59 -04:00
Steven
4bce77dd4b [cli] Fix vercel link command when vercel.json contains builds (#4940)
This PR fixes a bug when running `vercel link` command in a directory with `vercel.json` that contains `builds` property. The problem is that framework auto-detection doesn't run when `builds` is used, only for zero config. So we skip the API and instead create the project without auto-detection during `vc link`.
2020-07-28 17:28:53 -04:00
Steven
3a79810944 Publish Canary
- @vercel/frameworks@0.0.18-canary.1
 - vercel@20.0.0-canary.5
 - @vercel/client@8.2.2-canary.4
 - @vercel/next@2.6.14-canary.2
 - @vercel/node@1.7.4-canary.1
2020-07-27 20:53:09 -04:00
Steven
5b3fbdddb3 [node][next] Bump node-file-trace to 0.8.1 (#4933)
This PR bumps `node-file-trace` to [0.8.1](https://github.com/vercel/node-file-trace/releases/tag/0.8.1) to fix ESM packages used with `require()`.

Fixes https://github.com/vercel/node-file-trace/issues/141
2020-07-27 20:50:45 -04:00
JJ Kasper
25e9ecb5a2 [next] Update test fixtures to latest canary (#4935)
This updates our Next.js test fixtures to run against canary. 

It looks like the mono-repo fixtures will need to be updated to run against canary separately since there may be a conflict with legacy routes and the trailing slash redirect
2020-07-27 19:39:25 -04:00
Nathan Rajlich
415515840c [frameworks] Update "Other" build command placeholder to use "vercel-build" (#4934) 2020-07-27 14:03:16 -07:00
Nathan Rajlich
9ba14be12e [client] Export buildFileTree() and return the ignore list (#4917)
* [client] Export `buildFileTree()` and return the ignore list

The ignore list from this function will be used in production to
render a warning if there are files listed in the `.vercelignore` file
that are being comitted to the project's git repo.

* Tests for `ignoreList`

* Update packages/now-client/src/utils/index.ts

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

Co-authored-by: Steven <steven@ceriously.com>
2020-07-24 17:11:44 -07:00
Steven
f1321946c5 [frameworks] Add link to demo for RedwoodJS (#4921) 2020-07-24 16:22:19 -07:00
Max Leiter
230b69abf4 [cli] Fix external rewrites/redirects in dev (#4914)
Co-authored-by: Steven <steven@ceriously.com>
2020-07-24 16:21:52 -07:00
Igor Klopov
0d96f80ac8 [cli] Print build error message instead of 'Build failed' (#4908)
This PR fixes the `lambda-with-invalid-runtime` CLI test
2020-07-24 12:38:24 -04:00
Steven
178f5e7f00 Publish Canary
- @vercel/build-utils@2.4.3-canary.2
 - vercel@20.0.0-canary.4
 - @vercel/client@8.2.2-canary.3
2020-07-23 21:00:25 -04:00
Steven
dbfdf20c9d [build-utils] Fix redwood /api detection (#4916)
Previously, the zero detection would see js files in the `/api` directory and attempt to use `@vercel/node`. However, this doesn't work with RedwoodJS which uses a different format for [`/api`](https://github.com/vercel/vercel/tree/master/examples/redwoodjs/api) so we must only use `@vercel/redwood` when RedwoodJS is detected.
2020-07-24 00:33:06 +00:00
Juan Campa
a80ed4ec9b [api] Fix api-framework cache header (#4913)
## Problem

`api-frameworks` is not being cached by our edge when used via the fallback-to-front mechanism. That's because the headers are only applied to `/api/frameworks` but not `/api/v1/frameworks` which is the actual path being used.

## Solution

Also apply the caching headers to `/api/<version>/frameworks`
2020-07-23 23:48:47 +00:00
Steven
833c3d7c01 Publish Canary
- @vercel/frameworks@0.0.18-canary.0
 - @vercel/build-utils@2.4.3-canary.1
 - vercel@20.0.0-canary.3
 - @vercel/client@8.2.2-canary.2
 - @vercel/redwood@0.0.2-canary.1
2020-07-23 18:29:16 -04:00
Steven
19d0db7270 [frameworks] Add autodetection for RedwoodJS (#4915)
- Supersedes #4539
- Related to #4676

Co-authored-by: Lachlan Campbell <lachlan@hackclub.com>
2020-07-23 18:02:50 -04:00
Steven
041e8cc601 Publish Canary
- @vercel/build-utils@2.4.3-canary.0
 - vercel@20.0.0-canary.2
 - @vercel/client@8.2.2-canary.1
 - @vercel/static-build@0.17.7-canary.1
 - @vercel/redwood@0.0.2-canary.0
2020-07-23 16:28:44 -04:00
Steven
b118e461b1 [redwoood] Add runtime @vercel/redwood (#4676)
### Changes
- Add new Runtime: `@vercel/redwood`
- Update `run.js` script to sort packages properly

### Related
- Related to #4539 
- Related to #4535
- Depends on https://github.com/apollographql/apollo-server/pull/4311
2020-07-23 16:22:48 -04:00
Andy Bitz
e519d49d7b Publish Canary
- vercel@20.0.0-canary.1
2020-07-23 15:42:46 +02:00
Andy Bitz
27683818ba Bump vercel to 20.0.0-canary.0 2020-07-23 15:42:30 +02:00
Andy
e016e38229 [cli] (Major) Change vc domains add workflow to match the dashboard (#3325)
* [now-cli] Change `now domains add` workflow to match dashboard

* Fix error throwing and add --force

* Remove `now domains verify`

* Count projects when removing a domain

* Include projects for `now inspect`

* Updated `now domains ls`

* Fix `now domains ls`

* Use Finally

* Add tests

* Use --force for domain tests

* Adjust tests

* Adjust test and fix output

* Fix padding

* Adjust more tests

* Ensure the project exists

* Log the deployment

* Fix tests again

* Remove project after test

* Fix command

* Fix test

* Change user after a couple of tests

* Add check

* Use random name for domain

* More logging

* Add more logging

* Remove logging

* Remove .now/project.json every time

* Skip test

* Add test to change user

* More logging

* Use now login

* Remove duplicated test

* Consider linked project for `domains add`

* Fix linting

* Remove unused docs

* Undo changes

* Undo more changes

* Fix typo

* Do not sort projects when there is only one record

* Fix loop

* Deploy with `-V 2`

* Remove verification

* Remove outdated link

* Update packages/now-cli/src/commands/domains/add.ts

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

* Update packages/now-cli/src/commands/domains/add.ts

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

* Use utils for public suffix and service type

* Update packages/now-cli/src/commands/domains/ls.ts

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

* Update packages/now-cli/src/commands/domains/add.ts

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

* Change link

* Undo test changes

* Fix type issues

* Update packages/now-cli/src/commands/domains/add.ts

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

* Use domain config to print info

* Check apex domain when removing a domain

* Remove psl

* List projects when removing a domain

* Do not list projects

* Change user earlier

* Update packages/now-cli/src/commands/domains/inspect.ts

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

* Apply suggestions from code review

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

* Apply suggestions from code review

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

Co-authored-by: Mark Glagola <mark.glagola@gmail.com>
Co-authored-by: Steven <steven@ceriously.com>
2020-07-23 15:41:25 +02:00
Max Leiter
5db1c5e610 [tests] Skip try to purchase a domain CLI test if a VERCEL_TOKEN is provided (#4909)
I placed the check inside the test because otherwise `test.only` and other test functions break with this test.
2020-07-23 01:39:05 +00:00
Steven
24c228569f Publish Canary
- vercel@19.2.1-canary.2
 - @vercel/next@2.6.14-canary.1
2020-07-22 16:09:26 -04:00
Steven
963de9b64f [cli] Update "set up" message (#4907)
Follow up to #4897 

* `vc link` - Update message to say `Set up ~/proj`
* `vc dev` - Update message  to say `Set up and develop ~/proj`
* `vc link --help` - Updated example message to include `vc link ~/proj` usage
2020-07-22 19:13:41 +00:00
JJ Kasper
ab7fd52305 [next] Don't trace pages for lambdas when not needed (#4841)
When pages are fully static and don't leverage preview mode or revalidation we can skip tracing the pages for generating the lambdas since lambdas aren't needed in this case
2020-07-22 18:14:47 +00:00
Nathan Rajlich
0fdb0dac91 [cli] Send the Vercel platform proxy request headers to frontend dev server (#4874)
This is a follow up to #4850 that does the same thing when
proxying to the frontend dev server.
2020-07-22 14:36:15 +00:00
Andy Bitz
bb0b632dcf Publish Canary
- vercel@19.2.1-canary.1
 - @vercel/go@1.1.5-canary.0
 - @vercel/next@2.6.14-canary.0
 - @vercel/node@1.7.4-canary.0
 - @vercel/static-build@0.17.7-canary.0
2020-07-22 13:34:40 +02:00
Andy
ced9495143 Revert "Revert "[go] Implement startDevServer() function (#4662)" (#4899)" (#4906)
This reverts commit 8d1afc026f.
2020-07-22 13:33:36 +02:00
Nathan Rajlich
fadc3f2588 [next][node][static-build] Execute "vercel-build" script if defined (#4863)
* [build-utils] Make `runPackageJsonScript()` run the `vercel-` or `now-` if defined in `package.json`

* [build-utils] Export `getScriptName()`

* [next] Use `getScriptName()` and `remove()`

* [node] Update for `vercel-`

* [static-build] Update for `vercel-`

* Remove debug

* Add `getScriptName()` unit tests

* Test for `vercel-build` in e2e

* Make platform name behavior be opt-in

So that it's not a breaking behavior change.

* Check for only "vercel-build" or "now-build", but not "build"

* Simplify `getScriptName()` to return the first existing script in a possible set

* Revert change

* Fix test

Co-authored-by: Joe Haddad <joe.haddad@zeit.co>
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2020-07-21 20:04:24 -07:00
Steven
a1d548dfef Publish Canary
- vercel@19.2.1-canary.0
 - @vercel/client@8.2.2-canary.0
2020-07-21 18:56:21 -04:00
Steven
754090a8ab [cli] Add vercel link command (#4897)
- [x] Add `vercel link` command
- [x] Update `vercel dev` to automatically link 
- [x] Updated `vercel env` error message to mention `vercel link`
- [x] Test `vercel link`
- [x] Test `vercel dev`
- [x] Add `--confirm` flag

### Related

- Fixes #3767
- Fixes #4889 
- Fixes #3991 
- Fixes #4534 
- Related to #3989
2020-07-21 18:52:46 -04:00
dependabot[bot]
8269a48ee0 Bump codecov from 3.6.5 to 3.7.1 (#4900)
Bumps [codecov](https://github.com/codecov/codecov-node) from 3.6.5 to 3.7.1.
<details>
<summary>Commits</summary>
<ul>
<li><a href="29dd5b6b03"><code>29dd5b6</code></a> 3.7.1</li>
<li><a href="c0711c6566"><code>c0711c6</code></a> Switch from execSync to execFileSync (<a href="https://github-redirect.dependabot.com/codecov/codecov-node/issues/180">#180</a>)</li>
<li><a href="5f6cc62d4f"><code>5f6cc62</code></a> Bump lodash from 4.17.15 to 4.17.19 (<a href="https://github-redirect.dependabot.com/codecov/codecov-node/issues/183">#183</a>)</li>
<li><a href="0c4d7f3c6f"><code>0c4d7f3</code></a> Merge pull request <a href="https://github-redirect.dependabot.com/codecov/codecov-node/issues/182">#182</a> from codecov/update-readme-badges</li>
<li><a href="cc5e121842"><code>cc5e121</code></a> Update depstat image and urls</li>
<li><a href="b44b44e1f8"><code>b44b44e</code></a> Update readme with 400 error info (<a href="https://github-redirect.dependabot.com/codecov/codecov-node/issues/181">#181</a>)</li>
<li><a href="bb79335719"><code>bb79335</code></a> V3.7.0 (<a href="https://github-redirect.dependabot.com/codecov/codecov-node/issues/179">#179</a>)</li>
<li><a href="0d7b9b01cb"><code>0d7b9b0</code></a> Remove <code>'x-amz-acl': 'public-read'</code> header (<a href="https://github-redirect.dependabot.com/codecov/codecov-node/issues/178">#178</a>)</li>
<li><a href="eeff4e1953"><code>eeff4e1</code></a> Bump acorn from 5.7.3 to 5.7.4 (<a href="https://github-redirect.dependabot.com/codecov/codecov-node/issues/174">#174</a>)</li>
<li><a href="eb8a527470"><code>eb8a527</code></a> Merge pull request <a href="https://github-redirect.dependabot.com/codecov/codecov-node/issues/172">#172</a> from RoboCafaz/bugfix/codebuild-pr-parser</li>
<li>Additional commits viewable in <a href="https://github.com/codecov/codecov-node/compare/v3.6.5...v3.7.1">compare view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=codecov&package-manager=npm_and_yarn&previous-version=3.6.5&new-version=3.7.1)](https://help.github.com/articles/configuring-automated-security-fixes)

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-07-21 17:25:56 +00:00
Andy Bitz
9f05a1865c Publish Stable
- @vercel/frameworks@0.0.17
 - @vercel/build-utils@2.4.2
 - vercel@19.2.0
 - @vercel/client@8.2.1
 - @vercel/go@1.1.4
 - @vercel/next@2.6.13
 - @vercel/node@1.7.3
 - @vercel/static-build@0.17.6
2020-07-21 14:58:27 +02:00
Andy
8d1afc026f Revert "[go] Implement startDevServer() function (#4662)" (#4899)
This reverts commit 04bea1e3cd.
2020-07-21 14:55:59 +02:00
Nathan Rajlich
130f36aad6 Publish Canary
- vercel@19.1.3-canary.6
2020-07-20 23:11:46 -07:00
Nathan Rajlich
dd87c9b0c6 Publish Canary
- vercel@19.1.3-canary.5
 - @vercel/next@2.6.13-canary.2
2020-07-20 19:51:15 -07:00
Max Leiter
f813b3340b [cli] Fetch environment variables from Project Settings if .env file is not present (#4562)
Previously, users would have to run `vc env pull` to fetch cloud environment
variables into `.env`. After this PR, if no `.env` or `.build.env` file is present,
environment variables will be pulled by `vc dev` from your Vercel Environment
Variables settings, no file necessary.
2020-07-20 19:47:13 -07:00
Joe Haddad
976b02e895 [next] Add tests for trailing slash behavior (#4894) 2020-07-20 22:29:09 -04:00
Nathan Rajlich
843be9658c [cli] Use update-notifier instead of update-check (#4896)
This makes the updating logic be asynchronous instead of synchronous, and as such will make boot-up of CLI be faster.

The actual update notification display is identical to previous, we are not using `update-notifier`'s default boxen rendering.
2020-07-20 22:31:15 +00:00
dependabot[bot]
ad501a4cd0 Bump lodash from 4.17.15 to 4.17.19 (#4877)
Bumps [lodash](https://github.com/lodash/lodash) from 4.17.15 to 4.17.19.
- [Release notes](https://github.com/lodash/lodash/releases)
- [Commits](https://github.com/lodash/lodash/compare/4.17.15...4.17.19)

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2020-07-20 14:00:32 -04:00
602 changed files with 98073 additions and 11370 deletions

View File

@@ -34,3 +34,6 @@ packages/now-node-bridge/bridge.*
# now-static-build
packages/now-static-build/test/fixtures
# redwood
packages/redwood/test/fixtures

View File

@@ -11,7 +11,7 @@ jobs:
runs-on: ubuntu-latest
timeout-minutes: 2
steps:
- uses: styfle/cancel-workflow-action@0.3.2
- uses: styfle/cancel-workflow-action@0.4.1
with:
workflow_id: 849295, 849296, 849297, 849298
access_token: ${{ github.token }}

View File

@@ -20,8 +20,12 @@ jobs:
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v2
- run: git fetch origin master --depth=10
- run: git fetch origin ${{ github.ref }} --depth=10
with:
fetch-depth: 100
- run: git --version
- run: git fetch origin master --depth=100
- run: git fetch origin ${{ github.ref }} --depth=100
- run: git diff origin/master...HEAD --name-only
- run: yarn install
- run: yarn run build
- uses: actions/setup-node@v1

View File

@@ -20,8 +20,12 @@ jobs:
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v2
- run: git fetch origin master --depth=10
- run: git fetch origin ${{ github.ref }} --depth=10
with:
fetch-depth: 100
- run: git --version
- run: git fetch origin master --depth=100
- run: git fetch origin ${{ github.ref }} --depth=100
- run: git diff origin/master...HEAD --name-only
- name: Install Hugo
if: matrix.os == 'macos-latest'
run: curl -L -O https://github.com/gohugoio/hugo/releases/download/v0.56.0/hugo_0.56.0_macOS-64bit.tar.gz && tar -xzf hugo_0.56.0_macOS-64bit.tar.gz && mv ./hugo packages/now-cli/test/dev/fixtures/08-hugo/

View File

@@ -15,8 +15,12 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- run: git fetch origin master --depth=10
- run: git fetch origin ${{ github.ref }} --depth=10
with:
fetch-depth: 100
- run: git --version
- run: git fetch origin master --depth=100
- run: git fetch origin ${{ github.ref }} --depth=100
- run: git diff origin/master...HEAD --name-only
- run: yarn install
- run: yarn run build
- run: yarn test-integration-once --clean false

View File

@@ -20,9 +20,15 @@ jobs:
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v2
- run: git fetch origin master --depth=10
- run: git fetch origin ${{ github.ref }} --depth=10
with:
fetch-depth: 100
- run: git --version
- run: git fetch origin master --depth=100
- run: git fetch origin ${{ github.ref }} --depth=100
- run: git diff origin/master...HEAD --name-only
- uses: actions/setup-node@v1
with:
node-version: ${{ matrix.node }}
- run: yarn install
- run: yarn run build
- run: yarn run lint

1
.gitignore vendored
View File

@@ -1,5 +1,4 @@
node_modules
package-lock.json
dist
.vscode
npm-debug.log

View File

@@ -2,16 +2,14 @@
#### Why This Error Occurred
The domain you supplied cannot be verified using either the intended set of nameservers or the given verification TXT record.
The domain you supplied cannot be verified using the intended nameservers.
#### Possible Ways to Fix It
#### Possible Way to Fix It
Apply the intended set of nameservers to your domain or add the given TXT verification record through your domain provider.
Apply the intended set of nameservers to your domain.
You can retrieve both the intended nameservers and TXT verification record for the domain you wish to verify by running `vercel domains inspect <domain>`.
When you have added either verification method to your domain, you can run `vercel domains verify <domain>` again to complete verification for your domain.
Vercel will also automatically check periodically that your domain has been verified and automatically mark it as such if we detect either verification method on the domain.
If you would not like to verify your domain, you can remove it from your account using `vercel domains rm <domain>`.

View File

@@ -15,7 +15,7 @@ _Live Example: https://ionic-angular.now-examples.now.sh_
To get started with Ionic Angular deployed with Vercel, you can use the [Ionic CLI](https://ionicframework.com/docs/cli) to initialize the project:
```shell
$ npx ionic start [project-name] conference --type angular && cd [project-name]
$ npx @ionic/cli start [project-name] conference --type angular && cd [project-name]
```
### Deploying From Your Terminal

13585
examples/ionic-angular/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -31,7 +31,7 @@
"@ionic-native/in-app-browser": "5.0.0-beta.15",
"@ionic-native/splash-screen": "5.0.0-beta.15",
"@ionic-native/status-bar": "5.0.0-beta.15",
"@ionic/angular": "^5.0.6",
"@ionic/angular": "^5.1.1",
"@ionic/storage": "^2.1.3",
"cordova-android": "^8.1.0",
"cordova-ios": "^5.1.1",

View File

@@ -11,20 +11,24 @@
# next.js
/.next/
/out/
!public/
# production
/build
# misc
.DS_Store
.env*
*.pem
# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# Environment Variables
.env
.env.build
# local env files
.env.local
.env.development.local
.env.test.local
.env.production.local
# vercel
.vercel

View File

@@ -1,4 +1,4 @@
This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/zeit/next.js/tree/canary/packages/create-next-app).
This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app).
## Getting Started
@@ -21,7 +21,7 @@ To learn more about Next.js, take a look at the following resources:
- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
You can check out [the Next.js GitHub repository](https://github.com/zeit/next.js) - your feedback and contributions are welcome!
You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome!
## Deploy on Vercel

View File

@@ -8,8 +8,8 @@
"start": "next start"
},
"dependencies": {
"next": "^9.3.3",
"react": "^16.13.0",
"react-dom": "^16.13.0"
"next": "9.5.1",
"react": "16.13.1",
"react-dom": "16.13.1"
}
}

View File

@@ -0,0 +1,7 @@
import '../styles/globals.css'
function MyApp({ Component, pageProps }) {
return <Component {...pageProps} />
}
export default MyApp

View File

@@ -0,0 +1,6 @@
// Next.js API route support: https://nextjs.org/docs/api-routes/introduction
export default (req, res) => {
res.statusCode = 200
res.json({ name: 'John Doe' })
}

View File

@@ -1,203 +1,65 @@
import Head from 'next/head'
import styles from '../styles/Home.module.css'
const Home = () => (
<div className="container">
<Head>
<title>Create Next App</title>
<link rel="icon" href="/favicon.ico" />
</Head>
export default function Home() {
return (
<div className={styles.container}>
<Head>
<title>Create Next App</title>
<link rel="icon" href="/favicon.ico" />
</Head>
<main>
<h1 className="title">
Welcome to <a href="https://nextjs.org">Next.js!</a>
</h1>
<main className={styles.main}>
<h1 className={styles.title}>
Welcome to <a href="https://nextjs.org">Next.js!</a>
</h1>
<p className="description">
Get started by editing <code>pages/index.js</code>
</p>
<p className={styles.description}>
Get started by editing{' '}
<code className={styles.code}>pages/index.js</code>
</p>
<div className="grid">
<a href="https://nextjs.org/docs" className="card">
<h3>Documentation &rarr;</h3>
<p>Find in-depth information about Next.js features and API.</p>
</a>
<div className={styles.grid}>
<a href="https://nextjs.org/docs" className={styles.card}>
<h3>Documentation &rarr;</h3>
<p>Find in-depth information about Next.js features and API.</p>
</a>
<a href="https://nextjs.org/learn" className="card">
<h3>Learn &rarr;</h3>
<p>Learn about Next.js in an interactive course with quizzes!</p>
</a>
<a href="https://nextjs.org/learn" className={styles.card}>
<h3>Learn &rarr;</h3>
<p>Learn about Next.js in an interactive course with quizzes!</p>
</a>
<a
href="https://github.com/vercel/next.js/tree/master/examples"
className={styles.card}
>
<h3>Examples &rarr;</h3>
<p>Discover and deploy boilerplate example Next.js projects.</p>
</a>
<a
href="https://vercel.com/import?filter=next.js&utm_source=create-next-app&utm_medium=default-template&utm_campaign=create-next-app"
className={styles.card}
>
<h3>Deploy &rarr;</h3>
<p>
Instantly deploy your Next.js site to a public URL with Vercel.
</p>
</a>
</div>
</main>
<footer className={styles.footer}>
<a
href="https://github.com/zeit/next.js/tree/master/examples"
className="card"
href="https://vercel.com?utm_source=create-next-app&utm_medium=default-template&utm_campaign=create-next-app"
target="_blank"
rel="noopener noreferrer"
>
<h3>Examples &rarr;</h3>
<p>Discover and deploy boilerplate example Next.js projects.</p>
Powered by{' '}
<img src="/vercel.svg" alt="Vercel Logo" className={styles.logo} />
</a>
<a
href="https://vercel.com/new?filter=next.js&utm_source=create-next-app&utm_medium=default-template&utm_campaign=create-next-app"
className="card"
>
<h3>Deploy &rarr;</h3>
<p>
Instantly deploy your Next.js site to a public URL with Vercel.
</p>
</a>
</div>
</main>
<footer>
<a
href="https://vercel.com?utm_source=create-next-app&utm_medium=default-template&utm_campaign=create-next-app"
target="_blank"
rel="noopener noreferrer"
>
Powered by <img src="/vercel.svg" alt="Vercel Logo" />
</a>
</footer>
<style jsx>{`
.container {
min-height: 100vh;
padding: 0 0.5rem;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
main {
padding: 5rem 0;
flex: 1;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
footer {
width: 100%;
height: 100px;
border-top: 1px solid #eaeaea;
display: flex;
justify-content: center;
align-items: center;
}
footer img {
margin-left: 0.5rem;
}
footer a {
display: flex;
justify-content: center;
align-items: center;
}
a {
color: inherit;
text-decoration: none;
}
.title a {
color: #0070f3;
text-decoration: none;
}
.title a:hover,
.title a:focus,
.title a:active {
text-decoration: underline;
}
.title {
margin: 0;
line-height: 1.15;
font-size: 4rem;
}
.title,
.description {
text-align: center;
}
.description {
line-height: 1.5;
font-size: 1.5rem;
}
code {
background: #fafafa;
border-radius: 5px;
padding: 0.75rem;
font-size: 1.1rem;
font-family: Menlo, Monaco, Lucida Console, Liberation Mono,
DejaVu Sans Mono, Bitstream Vera Sans Mono, Courier New, monospace;
}
.grid {
display: flex;
align-items: center;
justify-content: center;
flex-wrap: wrap;
max-width: 800px;
margin-top: 3rem;
}
.card {
margin: 1rem;
flex-basis: 45%;
padding: 1.5rem;
text-align: left;
color: inherit;
text-decoration: none;
border: 1px solid #eaeaea;
border-radius: 10px;
transition: color 0.15s ease, border-color 0.15s ease;
}
.card:hover,
.card:focus,
.card:active {
color: #0070f3;
border-color: #0070f3;
}
.card h3 {
margin: 0 0 1rem 0;
font-size: 1.5rem;
}
.card p {
margin: 0;
font-size: 1.25rem;
line-height: 1.5;
}
@media (max-width: 600px) {
.grid {
width: 100%;
flex-direction: column;
}
}
`}</style>
<style jsx global>{`
html,
body {
padding: 0;
margin: 0;
font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen,
Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif;
}
* {
box-sizing: border-box;
}
`}</style>
</div>
)
export default Home
</footer>
</div>
)
}

View File

@@ -1,3 +1,4 @@
<svg width="70" height="16" viewBox="0 0 70 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M9.255.05l9.108 15.753H.148L9.255.05zM39.434 8.418c0-2.535-1.87-4.307-4.554-4.307-2.683 0-4.554 1.772-4.554 4.307 0 2.487 2.019 4.308 4.8 4.308 1.526 0 2.905-.566 3.79-1.6l-1.673-.96c-.517.517-1.28.837-2.117.837-1.23 0-2.29-.665-2.658-1.674l-.074-.172h6.966a3.76 3.76 0 00.074-.739zm-7.065-.738l.05-.148c.32-1.058 1.255-1.698 2.436-1.698 1.207 0 2.117.64 2.437 1.698l.05.148h-4.973zM65.945 8.418c0-2.535-1.871-4.307-4.554-4.307-2.683 0-4.554 1.772-4.554 4.307 0 2.487 2.018 4.308 4.8 4.308 1.526 0 2.904-.566 3.79-1.6l-1.673-.96c-.517.517-1.28.837-2.117.837-1.23 0-2.29-.665-2.659-1.674l-.073-.172h6.966a3.76 3.76 0 00.074-.739zM58.88 7.68l.05-.148c.32-1.058 1.255-1.698 2.436-1.698 1.206 0 2.117.64 2.437 1.698l.05.148H58.88zM54.13 7.015l1.673-.96c-.788-1.23-2.19-1.92-3.89-1.92-2.682 0-4.553 1.773-4.553 4.308 0 2.536 1.87 4.308 4.554 4.308 1.698 0 3.101-.69 3.89-1.92l-1.675-.96c-.443.738-1.23 1.157-2.215 1.157-1.55 0-2.585-1.034-2.585-2.585 0-1.55 1.034-2.585 2.585-2.585.96 0 1.772.419 2.215 1.157zM69.637 1.428h-1.97v11.077h1.97V1.428zM31.779 1.428h-2.265L25.182 8.91l-4.333-7.483H18.56l6.622 11.421 6.597-11.421zM45.71 6.4c.222 0 .444.025.665.074V4.382c-1.673.049-3.249.984-3.249 2.141V4.382h-1.97v8.123h1.97v-3.52c0-1.527 1.059-2.585 2.585-2.585z" fill="#000"/>
</svg>
<svg width="283" height="64" viewBox="0 0 283 64" fill="none"
xmlns="http://www.w3.org/2000/svg">
<path d="M141.04 16c-11.04 0-19 7.2-19 18s8.96 18 20 18c6.67 0 12.55-2.64 16.19-7.09l-7.65-4.42c-2.02 2.21-5.09 3.5-8.54 3.5-4.79 0-8.86-2.5-10.37-6.5h28.02c.22-1.12.35-2.28.35-3.5 0-10.79-7.96-17.99-19-17.99zm-9.46 14.5c1.25-3.99 4.67-6.5 9.45-6.5 4.79 0 8.21 2.51 9.45 6.5h-18.9zM248.72 16c-11.04 0-19 7.2-19 18s8.96 18 20 18c6.67 0 12.55-2.64 16.19-7.09l-7.65-4.42c-2.02 2.21-5.09 3.5-8.54 3.5-4.79 0-8.86-2.5-10.37-6.5h28.02c.22-1.12.35-2.28.35-3.5 0-10.79-7.96-17.99-19-17.99zm-9.45 14.5c1.25-3.99 4.67-6.5 9.45-6.5 4.79 0 8.21 2.51 9.45 6.5h-18.9zM200.24 34c0 6 3.92 10 10 10 4.12 0 7.21-1.87 8.8-4.92l7.68 4.43c-3.18 5.3-9.14 8.49-16.48 8.49-11.05 0-19-7.2-19-18s7.96-18 19-18c7.34 0 13.29 3.19 16.48 8.49l-7.68 4.43c-1.59-3.05-4.68-4.92-8.8-4.92-6.07 0-10 4-10 10zm82.48-29v46h-9V5h9zM36.95 0L73.9 64H0L36.95 0zm92.38 5l-27.71 48L73.91 5H84.3l17.32 30 17.32-30h10.39zm58.91 12v9.69c-1-.29-2.06-.49-3.2-.49-5.81 0-10 4-10 10V51h-9V17h9v9.2c0-5.08 5.91-9.2 13.2-9.2z" fill="#000"/>
</svg>

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@@ -0,0 +1,123 @@
.container {
min-height: 100vh;
padding: 0 0.5rem;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
.main {
padding: 5rem 0;
flex: 1;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
.footer {
width: 100%;
height: 100px;
border-top: 1px solid #eaeaea;
display: flex;
justify-content: center;
align-items: center;
}
.footer img {
margin-left: 0.5rem;
}
.footer a {
display: flex;
justify-content: center;
align-items: center;
}
.title a {
color: #0070f3;
text-decoration: none;
}
.title a:hover,
.title a:focus,
.title a:active {
text-decoration: underline;
}
.title {
margin: 0;
line-height: 1.15;
font-size: 4rem;
}
.title,
.description {
text-align: center;
}
.description {
line-height: 1.5;
font-size: 1.5rem;
}
.code {
background: #fafafa;
border-radius: 5px;
padding: 0.75rem;
font-size: 1.1rem;
font-family: Menlo, Monaco, Lucida Console, Liberation Mono, DejaVu Sans Mono,
Bitstream Vera Sans Mono, Courier New, monospace;
}
.grid {
display: flex;
align-items: center;
justify-content: center;
flex-wrap: wrap;
max-width: 800px;
margin-top: 3rem;
}
.card {
margin: 1rem;
flex-basis: 45%;
padding: 1.5rem;
text-align: left;
color: inherit;
text-decoration: none;
border: 1px solid #eaeaea;
border-radius: 10px;
transition: color 0.15s ease, border-color 0.15s ease;
}
.card:hover,
.card:focus,
.card:active {
color: #0070f3;
border-color: #0070f3;
}
.card h3 {
margin: 0 0 1rem 0;
font-size: 1.5rem;
}
.card p {
margin: 0;
font-size: 1.25rem;
line-height: 1.5;
}
.logo {
height: 1em;
}
@media (max-width: 600px) {
.grid {
width: 100%;
flex-direction: column;
}
}

View File

@@ -0,0 +1,16 @@
html,
body {
padding: 0;
margin: 0;
font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen,
Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif;
}
a {
color: inherit;
text-decoration: none;
}
* {
box-sizing: border-box;
}

View File

@@ -0,0 +1,10 @@
# editorconfig.org
root = true
[*]
charset = utf-8
end_of_line = lf
indent_size = 2
indent_style = space
insert_final_newline = true
trim_trailing_whitespace = true

View File

@@ -0,0 +1,7 @@
# These environment variables will be used by default if you do not create any
# yourself in .env. This file should be safe to check into your version control
# system. Any custom values should go in .env and .env should *not* be checked
# into version control.
DATABASE_URL=file:./dev.db
BINARY_TARGET=native

10
examples/redwoodjs/.gitignore vendored Normal file
View File

@@ -0,0 +1,10 @@
.DS_Store
.env
.netlify
dev.db
dist
dist-babel
node_modules
yarn-error.log
.vercel

View File

@@ -0,0 +1 @@
lts/*

View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2020 Redwood
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -0,0 +1,29 @@
![RedwoodJS Logo](https://github.com/vercel/vercel/blob/master/packages/frameworks/logos/redwoodjs.svg)
# RedwoodJS Example
This directory is a brief example of a [RedwoodJS](https://redwoodjs.com) app with [Serverless Functions](https://vercel.com/docs/v2/serverless-functions/introduction) that can be deployed with Vercel and zero configuration.
## Deploy Your Own
Deploy your own RedwoodJS project, along with Serverless Functions, with Vercel.
[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/import/project?template=https://github.com/vercel/vercel/tree/master/examples/redwoodjs)
_Live Example: https://redwoodjs.now-examples.now.sh_
### How We Created This Example
To get started with RedwoodJS on Vercel, you can [use Yarn to initialize](https://redwoodjs.com/tutorial/installation-starting-development) the project:
```shell
$ yarn create redwood-app ./my-redwood-app
```
### Deploying From Your Terminal
You can deploy your new RedwoodJS project, along with [Serverless Functions](https://vercel.com/docs/v2/serverless-functions/introduction), with a single command from your terminal using [Vercel CLI](https://vercel.com/download):
```shell
$ vercel
```

View File

@@ -0,0 +1 @@
module.exports = { extends: '../babel.config.js' }

View File

@@ -0,0 +1,9 @@
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"src/*": ["./src/*"]
}
},
"include": ["src/**/*"]
}

View File

@@ -0,0 +1,8 @@
{
"name": "api",
"version": "0.0.0",
"private": true,
"dependencies": {
"@redwoodjs/api": "0.15.0"
}
}

View File

@@ -0,0 +1,18 @@
datasource DS {
provider = "sqlite"
url = env("DATABASE_URL")
}
generator client {
provider = "prisma-client-js"
binaryTargets = env("BINARY_TARGET")
}
// Define your own datamodels here and run `yarn redwood db save` to create
// migrations for them.
// TODO: Please remove the following example:
model UserExample {
id Int @id @default(autoincrement())
email String @unique
name String?
}

View File

@@ -0,0 +1,26 @@
/* eslint-disable no-console */
const { PrismaClient } = require('@prisma/client')
const dotenv = require('dotenv')
dotenv.config()
const db = new PrismaClient()
async function main() {
// Seed data is database data that needs to exist for your app to run.
// Ideally this file should be idempotent: running it multiple times
// will result in the same database state (usually by checking for the
// existence of a record before trying to create it). For example:
//
// const existing = await db.user.findMany({ where: { email: 'admin@email.com' }})
// if (!existing.length) {
// await db.user.create({ data: { name: 'Admin', email: 'admin@email.com' }})
// }
console.info('No data to seed. See api/prisma/seeds.js for info.')
}
main()
.catch((e) => console.error(e))
.finally(async () => {
await db.disconnect()
})

View File

@@ -0,0 +1,19 @@
import {
createGraphQLHandler,
makeMergedSchema,
makeServices,
} from '@redwoodjs/api'
import importAll from '@redwoodjs/api/importAll.macro'
import { db } from 'src/lib/db'
const schemas = importAll('api', 'graphql')
const services = importAll('api', 'services')
export const handler = createGraphQLHandler({
schema: makeMergedSchema({
schemas,
services: makeServices({ services }),
}),
db,
})

View File

@@ -0,0 +1,6 @@
// See https://github.com/prisma/prisma2/blob/master/docs/prisma-client-js/api.md#constructor
// for options.
import { PrismaClient } from '@prisma/client'
export const db = new PrismaClient()

View File

@@ -0,0 +1,3 @@
module.exports = {
presets: ['@redwoodjs/core/config/babel-preset'],
}

View File

@@ -0,0 +1,19 @@
{
"private": true,
"workspaces": {
"packages": [
"api",
"web"
]
},
"devDependencies": {
"@redwoodjs/core": "0.15.0"
},
"eslintConfig": {
"extends": "@redwoodjs/eslint-config"
},
"engines": {
"node": ">=12",
"yarn": ">=1.15"
}
}

View File

@@ -0,0 +1,9 @@
// https://prettier.io/docs/en/options.html
module.exports = {
trailingComma: 'es5',
bracketSpacing: true,
tabWidth: 2,
semi: false,
singleQuote: true,
arrowParens: 'always',
}

View File

@@ -0,0 +1,7 @@
[web]
port = 8910
apiProxyPath = "/api"
[api]
port = 8911
[browser]
open = false

View File

@@ -0,0 +1 @@
module.exports = { extends: '../babel.config.js' }

View File

@@ -0,0 +1,10 @@
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"src/*": ["./src/*"]
},
"jsx": "preserve"
},
"include": ["src/**/*"]
}

View File

@@ -0,0 +1,15 @@
{
"name": "web",
"version": "0.0.0",
"private": true,
"browserslist": [
"defaults"
],
"dependencies": {
"@redwoodjs/router": "0.15.0",
"@redwoodjs/web": "0.15.0",
"prop-types": "^15.7.2",
"react": "^16.13.1",
"react-dom": "^16.13.1"
}
}

View File

@@ -0,0 +1,36 @@
# Static Assets
Use this folder to add static files directly to your app. All included files and folders will be copied directly into the `/dist` folder (created when Webpack builds for production). They will also be available during development when you run `yarn rw dev`.
>Note: files will *not* hot reload while the development server is running. You'll need to manually stop/start to access file changes.
### Example Use
A file like `favicon.png` will be copied to `/dist/favicon.png`. A folder containing a file such as `static-files/my-logo.jpg` will be copied to `/dist/static-files/my-logo.jpg`. These can be referenced in your code directly without any special handling, e.g.
```
<link rel="icon" type="image/png" href="/favicon.png" />
```
and
```
<img src="/static-files/my-logo.jpg"> alt="Logo" />
```
Behind the scenes, we are using Webpack's ["copy-webpack-plugin"](https://github.com/webpack-contrib/copy-webpack-plugin).
## Best Practices
Because assets in this folder are bypassing the javascript module system, **this folder should be used sparingly** for assets such as favicons, robots.txt, manifests, libraries incompatible with Webpack, etc.
In general, it's best to import files directly into a template, page, or component. This allows Webpack to include that file in the bundle, which ensures Webpack will correctly process and move assets into the distribution folder, providing error checks and correct paths along the way.
### Example Asset Import with Webpack
Instead of handling our logo image as a static file per the example above, we can do the following:
```
import React from "react"
import logo from "./my-logo.jpg"
function Header() {
return <img src={logo} alt="Logo" />
}
export default Header
```
Behind the scenes, we are using Webpack's ["file-loader"](https://webpack.js.org/loaders/file-loader/) and ["url-loader](https://webpack.js.org/loaders/url-loader/) (for files smaller than 10kb).

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

@@ -0,0 +1,2 @@
User-agent: *
Disallow:

View File

@@ -0,0 +1,22 @@
// In this file, all Page components from 'src/pages` are auto-imported. Nested
// directories are supported, and should be uppercase. Each subdirectory will be
// prepended onto the component name.
//
// Examples:
//
// 'src/pages/HomePage/HomePage.js' -> HomePage
// 'src/pages/Admin/BooksPage/BooksPage.js' -> AdminBooksPage
import { Router, Route } from '@redwoodjs/router'
const Routes = () => {
return (
<Router>
<Route path="/" page={HomePage} name="home" />
<Route path="/about" page={AboutPage} name="about" />
<Route notfound page={NotFoundPage} />
</Router>
)
}
export default Routes

View File

@@ -0,0 +1,12 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="icon" type="image/png" href="/favicon.png" />
<title><%= htmlWebpackPlugin.options.title %></title>
</head>
<body>
<div id="redwood-app"></div>
</body>
</html>

View File

@@ -0,0 +1,16 @@
import ReactDOM from 'react-dom'
import { RedwoodProvider, FatalErrorBoundary } from '@redwoodjs/web'
import FatalErrorPage from 'src/pages/FatalErrorPage'
import Routes from 'src/Routes'
import './index.css'
ReactDOM.render(
<FatalErrorBoundary page={FatalErrorPage}>
<RedwoodProvider>
<Routes />
</RedwoodProvider>
</FatalErrorBoundary>,
document.getElementById('redwood-app')
)

View File

@@ -0,0 +1,44 @@
export default () => (
<main>
<style
dangerouslySetInnerHTML={{
__html: `
html, body {
margin: 0;
}
html * {
box-sizing: border-box;
}
main {
display: flex;
align-items: center;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif;
text-align: center;
background-color: #E2E8F0;
height: 100vh;
}
section {
background-color: white;
border-radius: 0.25rem;
width: 32rem;
padding: 1rem;
margin: 0 auto;
box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06);
}
h1 {
font-size: 2rem;
margin: 0;
font-weight: 500;
line-height: 1;
color: #2D3748;
}
`,
}}
/>
<section>
<h1>
<span>About RedwoodJS</span>
</h1>
</section>
</main>
)

View File

@@ -0,0 +1,53 @@
// This page will be rendered when an error makes it all the way to the top of the
// application without being handled by a Javascript catch statement or React error
// boundary.
//
// You can modify this page as you wish, but it is important to keep things simple to
// avoid the possibility that it will cause its own error. If it does, Redwood will
// still render a generic error page, but your users will prefer something a bit more
// thoughtful. =)
export default () => (
<main>
<style
dangerouslySetInnerHTML={{
__html: `
html, body {
margin: 0;
}
html * {
box-sizing: border-box;
}
main {
display: flex;
align-items: center;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif;
text-align: center;
background-color: #E2E8F0;
height: 100vh;
}
section {
background-color: white;
border-radius: 0.25rem;
width: 32rem;
padding: 1rem;
margin: 0 auto;
box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06);
}
h1 {
font-size: 2rem;
margin: 0;
font-weight: 500;
line-height: 1;
color: #2D3748;
}
`,
}}
/>
<section>
<h1>
<span>Something went wrong</span>
</h1>
</section>
</main>
)

View File

@@ -0,0 +1,44 @@
export default () => (
<main>
<style
dangerouslySetInnerHTML={{
__html: `
html, body {
margin: 0;
}
html * {
box-sizing: border-box;
}
main {
display: flex;
align-items: center;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif;
text-align: center;
background-color: #E2E8F0;
height: 100vh;
}
section {
background-color: white;
border-radius: 0.25rem;
width: 32rem;
padding: 1rem;
margin: 0 auto;
box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06);
}
h1 {
font-size: 2rem;
margin: 0;
font-weight: 500;
line-height: 1;
color: #2D3748;
}
`,
}}
/>
<section>
<h1>
<span>Welcome to RedwoodJS!</span>
</h1>
</section>
</main>
)

View File

@@ -0,0 +1,44 @@
export default () => (
<main>
<style
dangerouslySetInnerHTML={{
__html: `
html, body {
margin: 0;
}
html * {
box-sizing: border-box;
}
main {
display: flex;
align-items: center;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif;
text-align: center;
background-color: #E2E8F0;
height: 100vh;
}
section {
background-color: white;
border-radius: 0.25rem;
width: 32rem;
padding: 1rem;
margin: 0 auto;
box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06);
}
h1 {
font-size: 2rem;
margin: 0;
font-weight: 500;
line-height: 1;
color: #2D3748;
}
`,
}}
/>
<section>
<h1>
<span>404 Page Not Found</span>
</h1>
</section>
</main>
)

14339
examples/redwoodjs/yarn.lock Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -2,17 +2,17 @@
"name": "svelte-app",
"version": "1.0.0",
"devDependencies": {
"@rollup/plugin-commonjs": "^13.0.0",
"@rollup/plugin-node-resolve": "^8.1.0",
"npm-run-all": "^4.1.5",
"rollup": "^1.10.1",
"rollup-plugin-commonjs": "^9.3.4",
"rollup": "^2.18.0",
"rollup-plugin-livereload": "^1.0.0",
"rollup-plugin-node-resolve": "^4.2.3",
"rollup-plugin-svelte": "^5.0.3",
"rollup-plugin-terser": "^4.0.4",
"rollup-plugin-terser": "^6.1.0",
"svelte": "^3.0.0"
},
"dependencies": {
"sirv-cli": "^0.4.4"
"sirv-cli": "^1.0.1"
},
"scripts": {
"build": "rollup -c",

View File

@@ -1,6 +1,6 @@
import svelte from 'rollup-plugin-svelte';
import resolve from 'rollup-plugin-node-resolve';
import commonjs from 'rollup-plugin-commonjs';
import resolve from '@rollup/plugin-node-resolve';
import commonjs from '@rollup/plugin-commonjs';
import livereload from 'rollup-plugin-livereload';
import { terser } from 'rollup-plugin-terser';

View File

@@ -7,6 +7,7 @@
"tagline": "Blitz.js: The Fullstack React Framework",
"description": "A brand new Blitz.js app - the result of running `npx blitz new`.",
"website": "https://blitzjs.com",
"useRuntime": { "src": "package.json", "use": "@vercel/next" },
"detectors": {
"every": [
{
@@ -36,6 +37,7 @@
"description": "A Next.js app and a Serverless Function API.",
"website": "https://nextjs.org",
"sort": 1,
"useRuntime": { "src": "package.json", "use": "@vercel/next" },
"detectors": {
"every": [
{
@@ -682,6 +684,36 @@
}
}
},
{
"name": "RedwoodJS",
"slug": "redwoodjs",
"demo": "https://redwoodjs.now-examples.now.sh",
"logo": "https://raw.githubusercontent.com/vercel/vercel/master/packages/frameworks/logos/redwoodjs.svg",
"tagline": "RedwoodJS is a full-stack framework for the Jamstack.",
"description": "A RedwoodJS app, bootstraped with create-redwood-app.",
"website": "https://redwoodjs.com",
"useRuntime": { "src": "package.json", "use": "@vercel/redwood" },
"ignoreRuntimes": ["@vercel/node"],
"detectors": {
"every": [
{
"path": "package.json",
"matchContent": "\"(dev)?(d|D)ependencies\":\\s*{[^}]*\"@redwoodjs\\/core\":\\s*\".+?\"[^}]*}"
}
]
},
"settings": {
"buildCommand": {
"value": "yarn rw build && yarn rw db up --no-db-client --auto-approve && yarn rw dataMigrate up"
},
"devCommand": {
"value": "yarn rw dev --fwd=\"--port=$PORT --open=false\""
},
"outputDirectory": {
"placeholder": "RedwoodJS default"
}
}
},
{
"name": "Hugo",
"slug": "hugo",
@@ -804,7 +836,7 @@
"description": "No framework or a unoptimized framework.",
"settings": {
"buildCommand": {
"placeholder": "`npm run now-build` or `npm run build`"
"placeholder": "`npm run vercel-build` or `npm run build`"
},
"devCommand": {
"placeholder": "None"

View File

@@ -20,6 +20,8 @@ export interface Framework {
website?: string;
description: string;
sort?: number;
useRuntime?: { src: string; use: string };
ignoreRuntimes?: string[];
detectors?: {
every?: FrameworkDetectionItem[];
some?: FrameworkDetectionItem[];

View File

@@ -0,0 +1 @@
<svg fill="none" width="48" height="48" viewBox="0 0 917 1000" xmlns="http://www.w3.org/2000/svg"><path clip-rule="evenodd" d="m249.557 144.582 194.171 132.54c4.383 2.918 9.502 4.516 14.755 4.606 5.261-.038 10.394-1.641 14.755-4.606l194.319-132.986c7.55-5.406 11.714-14.418 10.957-23.717-.757-9.298-6.322-17.507-14.646-21.6024l-194.171-96.13614c-7.366-3.573948-15.947-3.573948-23.313 0l-193.581 96.13614c-8.474 4.1174-14.113 12.4854-14.783 21.9354-.67 9.451 3.73 18.541 11.537 23.83zm274.879 174.144c.016 8.789 4.318 17.01 11.509 21.991l155.662 106.389c9.965 6.87 23.298 6.012 32.313-2.081l130.579-116.789c5.819-5.199 9.051-12.729 8.823-20.56s-3.892-15.158-10.004-20.005l-124.677-99.702c-9.062-7.199-21.704-7.68-31.28-1.189l-161.416 110.401c-7.064 4.89-11.35 12.914-11.509 21.545zm-387.163 144.724c6.292 5.652 9.526 13.988 8.706 22.437-.817 8.499-5.726 16.052-13.132 20.208l-92.9545 55.72c-9.4227 5.633-21.32 4.82-29.90183-2.041-8.5818-6.861-12.06543-18.346-8.75546-28.865l34.37839-108.172c2.6969-8.57 9.5328-15.175 18.1483-17.533 8.609-2.505 17.8924-.309 24.4928 5.795zm504.168 11.293-168.056-115.007c-8.931-6.01-20.578-6.01-29.509 0l-168.056 115.007c-6.684 4.626-10.919 12.061-11.509 20.208-.435 8.203 2.816 16.169 8.853 21.693l167.909 150.222c4.842 4.319 11.089 6.698 17.558 6.687 6.465-.002 12.708-2.38 17.558-6.687l167.908-150.222c6.056-5.501 9.265-13.5 8.705-21.693-.469-8.146-4.666-15.612-11.361-20.208zm-448.247-29.718-130.4316-116.79c-5.8687-5.331-9.1073-12.995-8.8528-20.95.1419-7.841 3.7705-15.204 9.8856-20.06l124.6768-100.296c9.126-7.179 21.793-7.658 31.428-1.189l161.269 110.401c7.484 4.908 11.998 13.293 11.998 22.288 0 8.994-4.514 17.38-11.998 22.288l-155.515 106.388c-10.025 6.841-23.376 5.985-32.46-2.08zm669.715 167.756-132.792-79.495c-9.862-5.943-22.415-4.739-30.985 2.972l-162.301 144.873c-6.846 6.114-10.062 15.362-8.499 24.441 1.563 9.08 7.681 16.698 16.171 20.135l225.157 91.233c3.088 1.283 6.397 1.939 9.738 1.932 10.449.033 19.936-6.142 24.197-15.751l69.79-156.314c5.68-12.37 1.157-27.062-10.476-34.026zm18.443-190.043 34.379 108.171h-.295c2.542 8.091 1.097 16.919-3.889 23.761-4.986 6.841-12.915 10.876-21.342 10.86-4.728.016-9.37-1.269-13.427-3.715l-93.102-55.72c-7.254-4.243-11.992-11.789-12.689-20.208-.87-8.456 2.373-16.814 8.705-22.436l59.019-52.6c6.668-5.976 15.881-8.156 24.493-5.795 8.609 2.459 15.423 9.098 18.148 17.682zm-492.511 282.761c1.587-9.042-1.597-18.266-8.41-24.368l-162.302-144.873c-8.57-7.711-21.123-8.915-30.985-2.972l-132.7921 79.495c-11.4977 6.995-16.0467 21.502-10.6233 33.878l69.9374 156.314c5.794 13.034 20.774 19.134 33.936 13.818l225.009-91.232c8.492-3.407 14.632-10.995 16.23-20.06zm79.675 44.577 180.598 73.105c8.83 3.779 14.93 12.084 15.935 21.694 1.143 9.729-3.178 19.291-11.214 24.814l-180.745 125.556c-4.331 3.043-9.473 4.7-14.754 4.755-5.277-.082-10.411-1.737-14.755-4.755l-180.597-125.556c-8.066-5.508-12.439-15.061-11.362-24.814 1.206-9.71 7.526-18.006 16.526-21.694l180.597-73.105c6.351-2.532 13.421-2.532 19.771 0z" fill="#bf4722" fill-rule="evenodd"/></svg>

After

Width:  |  Height:  |  Size: 3.0 KiB

View File

@@ -1,6 +1,6 @@
{
"name": "@vercel/frameworks",
"version": "0.0.17-canary.2",
"version": "0.1.0",
"main": "frameworks.json",
"license": "UNLICENSED",
"scripts": {

View File

@@ -64,6 +64,21 @@ const Schema = {
tagline: { type: 'string' },
website: { type: 'string' },
description: { type: 'string' },
useRuntime: {
type: 'object',
required: ['src', 'use'],
additionalProperties: false,
properties: {
src: { type: 'string' },
use: { type: 'string' },
},
},
ignoreRuntimes: {
type: 'array',
items: {
type: 'string',
},
},
detectors: {
type: 'object',
additionalProperties: false,

View File

@@ -1,6 +1,6 @@
{
"name": "@vercel/build-utils",
"version": "2.4.2-canary.1",
"version": "2.5.1-canary.0",
"license": "MIT",
"main": "./dist/index.js",
"types": "./dist/index.d.js",
@@ -29,6 +29,7 @@
"@types/node-fetch": "^2.1.6",
"@types/semver": "6.0.0",
"@types/yazl": "^2.4.1",
"@vercel/frameworks": "0.1.0",
"aggregate-error": "3.0.1",
"async-retry": "1.2.3",
"async-sema": "2.1.4",

View File

@@ -2,8 +2,13 @@ import minimatch from 'minimatch';
import { valid as validSemver } from 'semver';
import { parse as parsePath, extname } from 'path';
import { Route, Source } from '@vercel/routing-utils';
import _frameworks, { Framework } from '@vercel/frameworks';
import { PackageJson, Builder, Config, BuilderFunctions } from './types';
import { isOfficialRuntime } from './';
const frameworkList = _frameworks as Framework[];
const slugToFramework = new Map<string | null, Framework>(
frameworkList.map(f => [f.slug, f])
);
interface ErrorResponse {
code: string;
@@ -21,6 +26,7 @@ interface Options {
devCommand?: string | null;
buildCommand?: string | null;
outputDirectory?: string | null;
createdAt?: number;
};
cleanUrls?: boolean;
trailingSlash?: boolean;
@@ -106,7 +112,6 @@ export async function detectBuilders(
};
}
const apiMatches = getApiMatches(options);
const sortedFiles = files.sort(sortFiles);
const apiSortedFiles = files.sort(sortFilesBySegmentCount);
@@ -122,6 +127,16 @@ export async function detectBuilders(
const { projectSettings = {} } = options;
const { buildCommand, outputDirectory, framework } = projectSettings;
const ignoreRuntimes = new Set(
slugToFramework.get(framework || '')?.ignoreRuntimes
);
const withTag = options.tag ? `@${options.tag}` : '';
const apiMatches = getApiMatches()
.filter(b => !ignoreRuntimes.has(b.use))
.map(b => {
b.use = `${b.use}${withTag}`;
return b;
});
// If either is missing we'll make the frontend static
const makeFrontendStatic = buildCommand === '' || outputDirectory === '';
@@ -394,16 +409,15 @@ function getFunction(fileName: string, { functions = {} }: Options) {
: { fnPattern: null, func: null };
}
function getApiMatches({ tag }: Options = {}) {
const withTag = tag ? `@${tag}` : '';
function getApiMatches() {
const config = { zeroConfig: true };
return [
{ src: 'api/**/*.js', use: `@vercel/node${withTag}`, config },
{ src: 'api/**/*.ts', use: `@vercel/node${withTag}`, config },
{ src: 'api/**/!(*_test).go', use: `@vercel/go${withTag}`, config },
{ src: 'api/**/*.py', use: `@vercel/python${withTag}`, config },
{ src: 'api/**/*.rb', use: `@vercel/ruby${withTag}`, config },
{ src: 'api/**/*.js', use: `@vercel/node`, config },
{ src: 'api/**/*.ts', use: `@vercel/node`, config },
{ src: 'api/**/!(*_test).go', use: `@vercel/go`, config },
{ src: 'api/**/*.py', use: `@vercel/python`, config },
{ src: 'api/**/*.rb', use: `@vercel/ruby`, config },
];
}
@@ -421,6 +435,7 @@ function detectFrontBuilder(
): Builder {
const { tag, projectSettings = {} } = options;
const withTag = tag ? `@${tag}` : '';
const { createdAt = 0 } = projectSettings;
let { framework } = projectSettings;
const config: Config = {
@@ -443,7 +458,7 @@ function detectFrontBuilder(
config.outputDirectory = projectSettings.outputDirectory;
}
if (pkg) {
if (pkg && (framework !== null || createdAt < Date.parse('2020-03-01'))) {
const deps: PackageJson['dependencies'] = {
...pkg.dependencies,
...pkg.devDependencies,
@@ -464,8 +479,10 @@ function detectFrontBuilder(
});
}
if (framework === 'nextjs' || framework === 'blitzjs') {
return { src: 'package.json', use: `@vercel/next${withTag}`, config };
const f = slugToFramework.get(framework || '');
if (f && f.useRuntime) {
const { src, use } = f.useRuntime;
return { src, use: `${use}${withTag}`, config };
}
// Entrypoints for other frameworks
@@ -914,11 +931,10 @@ function getRouteResult(
const redirectRoutes: Route[] = [];
const rewriteRoutes: Route[] = [];
const errorRoutes: Route[] = [];
const isNextjs =
frontendBuilder &&
((frontendBuilder.use && frontendBuilder.use.startsWith('@vercel/next')) ||
(frontendBuilder.config &&
frontendBuilder.config.framework === 'nextjs'));
const framework = frontendBuilder?.config?.framework || '';
const use = frontendBuilder?.use || '';
const isNextjs = framework === 'nextjs' || use.startsWith('@vercel/next');
const ignoreRuntimes = slugToFramework.get(framework)?.ignoreRuntimes;
if (apiRoutes && apiRoutes.length > 0) {
if (options.featHandleMiss) {
@@ -956,11 +972,18 @@ function getRouteResult(
}
rewriteRoutes.push(...dynamicRoutes);
rewriteRoutes.push({
src: '^/api(/.*)?$',
status: 404,
continue: true,
});
if (typeof ignoreRuntimes === 'undefined') {
// This route is only necessary to hide the directory listing
// to avoid enumerating serverless function names.
// But it causes issues in `vc dev` for frameworks that handle
// their own functions such as redwood, so we ignore.
rewriteRoutes.push({
src: '^/api(/.*)?$',
status: 404,
continue: true,
});
}
} else {
defaultRoutes.push(...apiRoutes);
@@ -1030,5 +1053,5 @@ function sortFilesBySegmentCount(fileA: string, fileB: string): number {
return -1;
}
return 0;
return fileA.localeCompare(fileB);
}

View File

@@ -15,7 +15,9 @@ async function readFileOrNull(file: string) {
return null;
}
export async function readConfigFile<T>(files: string | string[]) {
export async function readConfigFile<T>(
files: string | string[]
): Promise<T | null> {
files = Array.isArray(files) ? files : [files];
for (const name of files) {
@@ -24,11 +26,11 @@ export async function readConfigFile<T>(files: string | string[]) {
if (data) {
const str = data.toString('utf8');
if (name.endsWith('.json')) {
return JSON.parse(str);
return JSON.parse(str) as T;
} else if (name.endsWith('.toml')) {
return (toml.parse(str) as unknown) as T;
} else if (name.endsWith('.yaml') || name.endsWith('.yml')) {
return yaml.safeLoad(str, { filename: name });
return yaml.safeLoad(str, { filename: name }) as T;
}
}
}

View File

@@ -1080,6 +1080,87 @@ describe('Test `detectBuilders` with `featHandleMiss=true`', () => {
expect(errorRoutes).toStrictEqual([]);
});
it('Using "Other" framework with Storybook should NOT autodetect Next.js for new projects', async () => {
const pkg = {
scripts: {
dev: 'next dev',
build: 'next build',
storybook: 'start-storybook -p 6006',
'build-storybook': 'build-storybook',
},
dependencies: {
next: '9.3.5',
react: '16.13.1',
'react-dom': '16.13.1',
},
devDependencies: {
'@babel/core': '7.9.0',
'@storybook/addon-links': '5.3.18',
'@storybook/addons': '5.3.18',
'@storybook/react': '5.3.18',
},
};
const files = ['package.json', 'pages/api/foo.js', 'index.html'];
const projectSettings = {
framework: null, // Selected "Other" framework
buildCommand: 'yarn build-storybook',
createdAt: Date.parse('2020-07-01'),
};
const { builders, errorRoutes } = await detectBuilders(files, pkg, {
projectSettings,
featHandleMiss,
});
expect(builders).toEqual([
{
use: '@vercel/static-build',
src: 'package.json',
config: {
zeroConfig: true,
buildCommand: projectSettings.buildCommand,
},
},
]);
expect(errorRoutes!.length).toBe(1);
expect((errorRoutes![0] as Source).status).toBe(404);
});
it('Using "Other" framework should autodetect Next.js for old projects', async () => {
const pkg = {
scripts: {
dev: 'next dev',
build: 'next build',
},
dependencies: {
next: '9.3.5',
react: '16.13.1',
'react-dom': '16.13.1',
},
};
const files = ['package.json', 'pages/api/foo.js', 'index.html'];
const projectSettings = {
framework: null, // Selected "Other" framework
createdAt: Date.parse('2020-02-01'),
};
const { builders, errorRoutes } = await detectBuilders(files, pkg, {
projectSettings,
featHandleMiss,
});
expect(builders).toEqual([
{
use: '@vercel/next',
src: 'package.json',
config: {
zeroConfig: true,
},
},
]);
expect(errorRoutes).toStrictEqual([]);
});
it('api + raw static', async () => {
const files = ['api/endpoint.js', 'index.html', 'favicon.ico'];
@@ -1808,6 +1889,116 @@ describe('Test `detectBuilders` with `featHandleMiss=true`', () => {
expect((errorRoutes![0] as Source).status).toBe(404);
});
const redwoodFiles = [
'package.json',
'web/package.json',
'web/public/robots.txt',
'web/src/index.html',
'web/src/index.css',
'web/src/index.js',
'api/package.json',
'api/prisma/seeds.js',
'api/src/functions/graphql.js',
'api/src/graphql/.keep',
'api/src/services/.keep',
'api/src/lib/db.js',
];
it('RedwoodJS should only use Redwood builder and not Node builder', async () => {
const files = [...redwoodFiles].sort();
const projectSettings = {
framework: 'redwoodjs',
};
const {
builders,
defaultRoutes,
rewriteRoutes,
errorRoutes,
} = await detectBuilders(files, null, {
projectSettings,
featHandleMiss,
});
expect(builders).toStrictEqual([
{
use: '@vercel/redwood',
src: 'package.json',
config: {
zeroConfig: true,
framework: 'redwoodjs',
},
},
]);
expect(defaultRoutes).toStrictEqual([]);
expect(rewriteRoutes).toStrictEqual([]);
expect(errorRoutes).toStrictEqual([
{
status: 404,
src: '^/(?!.*api).*$',
dest: '/404.html',
},
]);
});
it('RedwoodJS should allow usage of non-js API and not add 404 api route', async () => {
const files = [...redwoodFiles, 'api/golang.go', 'api/python.py'].sort();
const projectSettings = {
framework: 'redwoodjs',
};
const {
builders,
defaultRoutes,
rewriteRoutes,
errorRoutes,
} = await detectBuilders(files, null, {
projectSettings,
featHandleMiss,
});
expect(builders).toStrictEqual([
{
use: '@vercel/go',
src: 'api/golang.go',
config: {
zeroConfig: true,
},
},
{
use: '@vercel/python',
src: 'api/python.py',
config: {
zeroConfig: true,
},
},
{
use: '@vercel/redwood',
src: 'package.json',
config: {
zeroConfig: true,
framework: 'redwoodjs',
},
},
]);
expect(defaultRoutes).toStrictEqual([
{ handle: 'miss' },
{
src: '^/api/(.+)(?:\\.(?:go|py))$',
dest: '/api/$1',
check: true,
},
]);
expect(rewriteRoutes).toStrictEqual([]);
expect(errorRoutes).toStrictEqual([
{
status: 404,
src: '^/(?!.*api).*$',
dest: '/404.html',
},
]);
});
it('No framework, only package.json', async () => {
const files = ['package.json'];
const pkg = {

View File

@@ -13,7 +13,7 @@
"outDir": "./dist",
"types": ["node", "jest"],
"strict": true,
"target": "esnext"
"target": "es2019"
},
"include": ["src/**/*"],
"exclude": ["node_modules"]

View File

@@ -1,3 +0,0 @@
declare module 'which-promise' {
export default function (name: string): Promise<string>;
}

View File

@@ -1,6 +1,6 @@
{
"name": "vercel",
"version": "19.1.3-canary.4",
"version": "20.0.1-canary.0",
"preferGlobal": true,
"license": "Apache-2.0",
"description": "The command-line interface for Vercel",
@@ -14,7 +14,6 @@
"preinstall": "node ./scripts/preinstall.js",
"test-unit": "nyc ava test/unit.js test/dev-builder.unit.js test/dev-router.unit.js test/dev-server.unit.js test/dev-validate.unit.js --serial --fail-fast --verbose",
"test-integration-cli": "ava test/integration.js --serial --fail-fast --verbose",
"test-integration-v1": "ava test/integration-v1.js --serial --fail-fast",
"test-integration-dev": "ava test/dev/integration.js --serial --fail-fast --verbose",
"prepublishOnly": "yarn build",
"coverage": "nyc report --reporter=text-lcov > coverage.lcov && codecov",
@@ -62,13 +61,12 @@
"node": ">= 10"
},
"dependencies": {
"@vercel/build-utils": "2.4.2-canary.1",
"@vercel/go": "1.1.4-canary.0",
"@vercel/next": "2.6.13-canary.1",
"@vercel/node": "1.7.3-canary.0",
"@vercel/build-utils": "2.5.1-canary.0",
"@vercel/go": "1.1.5",
"@vercel/node": "1.8.0",
"@vercel/python": "1.2.2",
"@vercel/ruby": "1.2.3",
"@vercel/static-build": "0.17.6-canary.1"
"update-notifier": "4.1.0"
},
"devDependencies": {
"@sentry/node": "5.5.0",
@@ -102,7 +100,7 @@
"@types/universal-analytics": "0.4.2",
"@types/which": "1.3.2",
"@types/write-json-file": "2.2.1",
"@zeit/dockerignore": "0.0.5",
"@vercel/frameworks": "0.1.0",
"@zeit/fun": "0.11.2",
"@zeit/ncc": "0.18.5",
"@zeit/source-map-support": "0.6.2",
@@ -119,17 +117,13 @@
"chalk": "2.4.2",
"chokidar": "3.3.1",
"clipboardy": "2.1.0",
"codecov": "3.6.5",
"codecov": "3.7.1",
"cpy": "7.2.0",
"credit-card": "3.0.1",
"date-fns": "1.29.0",
"death": "1.1.0",
"debug": "3.1.0",
"deployment-type": "1.0.1",
"docker-file-parser": "1.0.2",
"dot": "1.1.3",
"dotenv": "4.0.0",
"download": "6.2.5",
"email-prompt": "0.3.2",
"email-validator": "1.1.1",
"epipebomb": "1.0.0",
@@ -141,8 +135,6 @@
"get-port": "5.1.1",
"glob": "7.1.2",
"http-proxy": "1.17.0",
"ignore": "4.0.6",
"ini": "1.3.4",
"inquirer": "7.0.4",
"is-port-reachable": "3.0.0",
"is-url": "1.2.2",
@@ -161,14 +153,11 @@
"ora": "3.4.0",
"pcre-to-regexp": "1.0.0",
"pluralize": "7.0.0",
"printf": "0.2.5",
"progress": "2.0.3",
"promisepipe": "3.0.0",
"psl": "1.1.31",
"qr-image": "3.2.0",
"raw-body": "2.4.1",
"read-pkg": "2.0.0",
"rx-lite-aggregates": "4.0.8",
"semver": "5.5.0",
"serve-handler": "6.1.1",
"sinon": "4.4.2",
@@ -177,18 +166,14 @@
"tar-fs": "1.16.3",
"test-listen": "1.1.0",
"text-table": "0.2.0",
"then-sleep": "1.0.1",
"through2": "2.0.3",
"title": "3.4.1",
"tmp-promise": "1.0.3",
"tree-kill": "1.2.1",
"ts-node": "8.3.0",
"typescript": "3.9.3",
"universal-analytics": "0.4.20",
"update-check": "1.5.3",
"utility-types": "2.1.0",
"which": "2.0.2",
"which-promise": "1.0.0",
"write-json-file": "2.2.0",
"xdg-app-paths": "5.1.0"
}

View File

@@ -49,7 +49,13 @@ async function main() {
// Do the initial `ncc` build
console.log();
const src = join(dirRoot, 'src');
const args = ['@zeit/ncc', 'build', '--source-map'];
const args = [
'@zeit/ncc',
'build',
'--source-map',
'--external',
'update-notifier',
];
if (!isDev) {
args.push('--minify');
}
@@ -86,7 +92,7 @@ async function main() {
// A bunch of source `.ts` files from CLI's `util` directory
await remove(join(dirRoot, 'dist', 'util'));
console.log('Finished building `now-cli`');
console.log('Finished building Vercel CLI');
}
process.on('unhandledRejection', (reason: any, promise: Promise<any>) => {

View File

@@ -1,4 +1,3 @@
//
import chalk from 'chalk';
import { handleError } from '../../util/error';
@@ -32,15 +31,11 @@ const help = () => {
-Q ${chalk.bold.underline('DIR')}, --global-config=${chalk.bold.underline(
'DIR'
)} Path to the global ${'`.vercel`'} directory
-r ${chalk.bold.underline('RULES_FILE')}, --rules=${chalk.bold.underline(
'RULES_FILE'
)} Rules file
-d, --debug Debug mode [off]
-t ${chalk.bold.underline('TOKEN')}, --token=${chalk.bold.underline(
'TOKEN'
)} Login token
-S, --scope Set a custom scope
-n, --no-verify Don't wait until instance count meets the previous alias constraints
-N, --next Show next page of results
${chalk.dim('Examples:')}
@@ -66,30 +61,6 @@ const help = () => {
${chalk.dim('')} ${chalk.dim(
'Protocols'
)} in the URLs are unneeded and ignored.
${chalk.gray('')} Add and modify path based aliases for ${chalk.underline(
'example.com'
)}
${chalk.cyan(
`$ ${getPkgName()} alias ${chalk.underline(
'example.com'
)} -r ${chalk.underline('rules.json')}`
)}
Export effective routing rules
${chalk.cyan(
`$ ${getPkgName()} alias ls aliasId --json > ${chalk.underline(
'rules.json'
)}`
)}
${chalk.gray('')} Paginate results, where ${chalk.dim(
'`1584722256178`'
)} is the time in milliseconds since the UNIX epoch.
${chalk.cyan(`$ ${getPkgName()} alias ls --next 1584722256178`)}
`);
};
@@ -106,12 +77,8 @@ export default async function main(ctx) {
try {
argv = getArgs(ctx.argv.slice(2), {
'--json': Boolean,
'--no-verify': Boolean,
'--rules': String,
'--yes': Boolean,
'--next': Number,
'-n': '--no-verify',
'-r': '--rules',
'-y': '--yes',
'-N': '--next',
});

View File

@@ -1,4 +1,3 @@
import ms from 'ms';
import chalk from 'chalk';
import { SetDifference } from 'utility-types';
import { AliasRecord } from '../../util/alias/create-alias';
@@ -7,28 +6,24 @@ import { Output } from '../../util/output';
import * as ERRORS from '../../util/errors-ts';
import assignAlias from '../../util/alias/assign-alias';
import Client from '../../util/client';
import formatDnsTable from '../../util/format-dns-table';
import formatNSTable from '../../util/format-ns-table';
import getDeploymentForAlias from '../../util/alias/get-deployment-for-alias';
import getRulesFromFile from '../../util/alias/get-rules-from-file';
import getDeploymentByIdOrHost from '../../util/deploy/get-deployment-by-id-or-host';
import { getDeploymentForAlias } from '../../util/alias/get-deployment-by-alias';
import getScope from '../../util/get-scope';
import { getTargetsForAlias } from '../../util/alias/get-targets-for-alias';
import humanizePath from '../../util/humanize-path';
import setupDomain from '../../util/domains/setup-domain';
import stamp from '../../util/output/stamp';
import { isValidName } from '../../util/is-valid-name';
import upsertPathAlias from '../../util/alias/upsert-path-alias';
import handleCertError from '../../util/certs/handle-cert-error';
import isWildcardAlias from '../../util/alias/is-wildcard-alias';
import link from '../../util/output/link';
import { User } from '../../types';
import { getCommandName } from '../../util/pkg-name';
import toHost from '../../util/to-host';
import { NowConfig } from '../../util/dev/types';
type Options = {
'--debug': boolean;
'--local-config': string;
'--no-verify': boolean;
'--rules': string;
};
export default async function set(
@@ -47,11 +42,7 @@ export default async function set(
const { apiUrl } = ctx;
const setStamp = stamp();
const {
'--debug': debugEnabled,
'--no-verify': noVerify,
'--rules': rulesPath,
} = opts;
const { '--debug': debugEnabled } = opts;
const client = new Client({
apiUrl,
@@ -96,35 +87,7 @@ export default async function set(
return 1;
}
// Read the path alias rules in case there is is given
const rules = await getRulesFromFile(rulesPath);
if (rules instanceof ERRORS.FileNotFound) {
output.error(`Can't find the provided rules file at location:`);
output.print(` ${chalk.gray('-')} ${rules.meta.file}\n`);
return 1;
}
if (rules instanceof ERRORS.CantParseJSONFile) {
output.error(`Error parsing provided rules.json file at location:`);
output.print(` ${chalk.gray('-')} ${rules.meta.file}\n`);
return 1;
}
if (rules instanceof ERRORS.RulesFileValidationError) {
output.error(`Path Alias validation error: ${rules.meta.message}`);
output.print(` ${chalk.gray('-')} ${rules.meta.location}\n`);
return 1;
}
// If the user provided rules and also a deployment target, we should fail
if (args.length === 2 && rules) {
output.error(
`You can't supply a deployment target and target rules simultaneously.`
);
return 1;
}
if (args.length === 0 && !rules) {
if (args.length === 0) {
output.error(
`To ship to production, optionally configure your domains (${link(
'https://vercel.com/docs/v2/custom-domains'
@@ -133,62 +96,79 @@ export default async function set(
return 1;
}
// Find the targets to perform the alias
const targets = getTargetsForAlias(args, localConfig);
if (targets instanceof ERRORS.NoAliasInConfig) {
output.error(`Couldn't find an alias in config`);
return 1;
}
if (targets instanceof ERRORS.InvalidAliasInConfig) {
output.error(
`Wrong value for alias found in config. It must be a string or array of string.`
// For `now alias set <argument>`
if (args.length === 1) {
const deployment = handleCertError(
output,
await getDeploymentForAlias(
client,
output,
args,
opts['--local-config'],
user,
contextName,
localConfig
)
);
return 1;
}
if (rules) {
// If we have rules for path alias we assign them to the domain
for (const target of targets) {
output.log(
`Assigning path alias rules from ${humanizePath(
rulesPath
)} to ${target}`
if (deployment === 1) {
return deployment;
}
if (deployment instanceof Error) {
output.error(deployment.message);
return 1;
}
if (!deployment) {
output.error(
`Couldn't find a deployment to alias. Please provide one as an argument.`
);
const pathAlias = await upsertPathAlias(
return 1;
}
// Find the targets to perform the alias
const targets = getTargetsForAlias(args, localConfig);
if (targets instanceof Error) {
output.prettyError(targets);
return 1;
}
for (const target of targets) {
output.log(`Assigning alias ${target} to deployment ${deployment.url}`);
const record = await assignAlias(
output,
client,
rules,
deployment,
target,
contextName
);
const remaining = handleCreateAliasError(output, pathAlias);
if (handleSetupDomainError(output, remaining) !== 1) {
console.log(
`${chalk.cyan('> Success!')} ${
rules.length
} rules configured for ${chalk.underline(target)} ${setStamp()}`
);
const handleResult = handleSetupDomainError(
output,
handleCreateAliasError(output, record)
);
if (handleResult === 1) {
return 1;
}
console.log(
`${chalk.cyan('> Success!')} ${chalk.bold(
`${isWildcardAlias(target) ? '' : 'https://'}${handleResult.alias}`
)} now points to https://${deployment.url} ${setStamp()}`
);
}
return 0;
}
// If there are no rules for path alias we should find out a deployment and perform the alias
const [deploymentIdOrHost, aliasTarget] = args;
const deployment = handleCertError(
output,
await getDeploymentForAlias(
client,
output,
args,
opts['--local-config'],
user,
contextName,
localConfig
)
await getDeploymentByIdOrHost(client, contextName, deploymentIdOrHost)
);
if (deployment === 1) {
@@ -225,37 +205,32 @@ export default async function set(
return 1;
}
// Assign the alias for each of the targets in the array
for (const target of targets) {
output.log(`Assigning alias ${target} to deployment ${deployment.url}`);
output.log(`Assigning alias ${aliasTarget} to deployment ${deployment.url}`);
const isWildcard = isWildcardAlias(target);
const record = await assignAlias(
output,
client,
deployment,
target,
contextName,
noVerify
);
const handleResult = handleSetupDomainError(
output,
handleCreateAliasError(output, record),
isWildcard
);
if (handleResult === 1) {
return 1;
}
const prefix = isWildcard ? '' : 'https://';
console.log(
`${chalk.cyan('> Success!')} ${chalk.bold(
`${prefix}${handleResult.alias}`
)} now points to https://${deployment.url} ${setStamp()}`
);
const isWildcard = isWildcardAlias(aliasTarget);
const record = await assignAlias(
output,
client,
deployment,
aliasTarget,
contextName
);
const handleResult = handleSetupDomainError(
output,
handleCreateAliasError(output, record)
);
if (handleResult === 1) {
return 1;
}
const prefix = isWildcard ? '' : 'https://';
console.log(
`${chalk.cyan('> Success!')} ${chalk.bold(
`${prefix}${handleResult.alias}`
)} now points to https://${deployment.url} ${setStamp()}`
);
return 0;
}
@@ -265,8 +240,7 @@ type SetupDomainError = Exclude<SetupDomainResolve, Domain>;
function handleSetupDomainError<T>(
output: Output,
error: SetupDomainError | T,
isWildcard: boolean = false
error: SetupDomainError | T
): T | 1 {
if (
error instanceof ERRORS.DomainVerificationFailed ||
@@ -278,9 +252,7 @@ function handleSetupDomainError<T>(
`We could not alias since the domain ${domain} could not be verified due to the following reasons:\n`
);
output.print(
` ${chalk.gray(
'a)'
)} Nameservers verification failed since we see a different set than the intended set:`
`Nameservers verification failed since we see a different set than the intended set:`
);
output.print(
`\n${formatNSTable(
@@ -289,34 +261,6 @@ function handleSetupDomainError<T>(
{ extraSpace: ' ' }
)}\n\n`
);
if (error instanceof ERRORS.DomainVerificationFailed && !isWildcard) {
const { txtVerification } = error.meta;
output.print(
` ${chalk.gray(
'b)'
)} DNS TXT verification failed since found no matching records.`
);
output.print(
`\n${formatDnsTable(
[['_now', 'TXT', txtVerification.verificationRecord]],
{ extraSpace: ' ' }
)}\n\n`
);
output.print(
` Once your domain uses either the nameservers or the TXT DNS record from above, run again ${getCommandName(
'domains verify <domain>'
)}.\n`
);
output.print(
` We will also periodically run a verification check for you and you will receive an email once your domain is verified.\n`
);
} else {
output.print(
` Once your domain uses the nameservers from above, run again ${getCommandName(
'domains verify <domain>'
)}.\n`
);
}
output.print(' Read more: https://err.sh/now/domain-verification\n');
return 1;
}
@@ -457,66 +401,6 @@ function handleCreateAliasError<T>(
return 1;
}
if (error instanceof ERRORS.RuleValidationFailed) {
output.error(`Rule validation error: ${error.meta.message}.`);
output.print(` Make sure your rules file is written correctly.\n`);
return 1;
}
if (error instanceof ERRORS.VerifyScaleTimeout) {
output.error(`Instance verification timed out (${ms(error.meta.timeout)})`);
output.log('Read more: https://err.sh/now/verification-timeout');
return 1;
}
if (error instanceof ERRORS.NotSupportedMinScaleSlots) {
output.error(
`Scale rules from previous aliased deployment ${chalk.dim(
error.meta.url
)} could not be copied since Cloud v2 deployments cannot have a non-zero min`
);
output.log(
`Update the scale settings on ${chalk.dim(
error.meta.url
)} with ${getCommandName('scale')} and try again`
);
output.log('Read more: https://err.sh/now/v2-no-min');
return 1;
}
if (error instanceof ERRORS.ForbiddenScaleMaxInstances) {
output.error(
`Scale rules from previous aliased deployment ${chalk.dim(
error.meta.url
)} could not be copied since the given number of max instances (${
error.meta.max
}) is not allowed.`
);
output.log(
`Update the scale settings on ${chalk.dim(
error.meta.url
)} with ${getCommandName('scale')} and try again`
);
return 1;
}
if (error instanceof ERRORS.ForbiddenScaleMinInstances) {
output.error(
`You can't scale to more than ${error.meta.max} min instances with your current plan.`
);
return 1;
}
if (error instanceof ERRORS.InvalidScaleMinMaxRelation) {
output.error(
`Scale rules from previous aliased deployment ${chalk.dim(
error.meta.url
)} could not be copied becuase the relation between min and max instances is wrong.`
);
output.log(
`Update the scale settings on ${chalk.dim(
error.meta.url
)} with ${getCommandName('scale')} and try again`
);
return 1;
}
if (error instanceof ERRORS.CertMissing) {
output.error(
`There is no certificate for the domain ${error.meta.domain} and it could not be created.`
@@ -552,3 +436,22 @@ function handleCreateAliasError<T>(
return error;
}
function getTargetsForAlias(args: string[], { alias }: NowConfig) {
if (args.length) {
return [args[args.length - 1]]
.map(target => (target.indexOf('.') !== -1 ? toHost(target) : target))
.filter((x): x is string => !!x && typeof x === 'string');
}
if (!alias) {
return new ERRORS.NoAliasInConfig();
}
// Check the type for the option aliases
if (typeof alias !== 'string' && !Array.isArray(alias)) {
return new ERRORS.InvalidAliasInConfig(alias);
}
return typeof alias === 'string' ? [alias] : alias;
}

View File

@@ -7,7 +7,7 @@ import * as ERRORS from '../../util/errors-ts';
import { Output } from '../../util/output';
import deleteCertById from '../../util/certs/delete-cert-by-id';
import getCertById from '../../util/certs/get-cert-by-id';
import getCertsForDomain from '../../util/certs/get-certs-for-domain';
import { getCustomCertsForDomain } from '../../util/certs/get-custom-certs-for-domain';
import Client from '../../util/client';
import getScope from '../../util/get-scope';
import stamp from '../../util/output/stamp';
@@ -66,9 +66,17 @@ async function rm(
}
if (certs.length === 0) {
output.error(
`No certificates found by id "${id}" under ${chalk.bold(contextName)}`
);
if (id.includes('.')) {
output.error(
`No custom certificates found for "${id}" under ${chalk.bold(
contextName
)}`
);
} else {
output.error(
`No certificates found by id "${id}" under ${chalk.bold(contextName)}`
);
}
return 1;
}
@@ -101,7 +109,7 @@ async function getCertsToDelete(
) {
const cert = await getCertById(client, id);
if (cert instanceof ERRORS.CertNotFound) {
const certs = await getCertsForDomain(output, client, contextName, id);
const certs = await getCustomCertsForDomain(client, contextName, id);
if (certs instanceof ERRORS.CertsPermissionDenied) {
return certs;
}
@@ -125,12 +133,7 @@ function readConfirmation(output: Output, msg: string, certs: Cert[]) {
process.stdin
.on('data', d => {
process.stdin.pause();
resolve(
d
.toString()
.trim()
.toLowerCase() === 'y'
);
resolve(d.toString().trim().toLowerCase() === 'y');
})
.resume();
});

View File

@@ -4,7 +4,7 @@ import code from '../../util/output/code';
import note from '../../util/output/note';
import { getPkgName } from '../../util/pkg-name.ts';
export const latestHelp = () => `
export const help = () => `
${chalk.bold(`${logo} ${getPkgName()}`)} [options] <command | path>
${chalk.dim('Commands:')}
@@ -100,7 +100,7 @@ export const latestHelp = () => `
`;
export const latestArgs = {
export const args = {
'--force': Boolean,
'--with-cache': Boolean,
'--public': Boolean,
@@ -108,7 +108,6 @@ export const latestArgs = {
'--env': [String],
'--build-env': [String],
'--meta': [String],
'--no-scale': Boolean,
// This is not an array in favor of matching
// the config property name.
'--regions': String,
@@ -127,180 +126,3 @@ export const latestArgs = {
'-n': '--name',
'--target': String,
};
export const legacyArgsMri = {
string: [
'name',
'build-env',
'alias',
'meta',
'session-affinity',
'regions',
'dotenv',
'target',
],
boolean: [
'help',
'version',
'debug',
'force',
'links',
'C',
'clipboard',
'forward-npm',
'docker',
'npm',
'static',
'public',
'no-scale',
'no-verify',
'dotenv',
'prod',
],
default: {
C: false,
clipboard: true,
},
alias: {
env: 'e',
meta: 'm',
'build-env': 'b',
dotenv: 'E',
help: 'h',
debug: 'd',
version: 'v',
force: 'f',
links: 'l',
public: 'p',
'forward-npm': 'N',
'session-affinity': 'S',
name: 'n',
project: 'P',
alias: 'a',
},
};
// The following arg parsing is simply to make it compatible
// with the index. Let's not migrate it to the new args parsing, as
// we are gonna delete this file soon anyways.
const argList = {};
for (const item of legacyArgsMri.string) {
argList[`--${item}`] = String;
}
for (const item of legacyArgsMri.boolean) {
argList[`--${item}`] = Boolean;
}
for (const item of Object.keys(legacyArgsMri.alias)) {
argList[`-${legacyArgsMri.alias[item]}`] = `--${item}`;
}
export const legacyArgs = argList;
export const legacyHelp = () => `
${chalk.bold(`${logo} now`)} [options] <command | path>
${chalk.dim('Commands:')}
${chalk.dim('Cloud')}
deploy [path] Performs a deployment ${chalk.bold(
'(default)'
)}
ls | list [app] Lists deployments
rm | remove [id] Removes a deployment
ln | alias [id] [url] Configures aliases for deployments
inspect [id] Displays information related to a deployment
domains [name] Manages your domain names
certs [cmd] Manages your SSL certificates
secrets [name] Manages your secret environment variables
dns [name] Manages your DNS records
logs [url] Displays the logs for a deployment
scale [args] Scales the instance count of a deployment
init [example] Initialize an example project
help [cmd] Displays complete help for [cmd]
${chalk.dim('Administrative')}
billing | cc [cmd] Manages your credit cards and billing methods
upgrade | downgrade [plan] Upgrades or downgrades your plan
teams Manages your teams
switch [scope] Switches between teams and your account
login [email] Logs into your account or creates a new one
logout Logs out of your account
whoami Shows the username of the currently logged in user
${chalk.dim('Options:')}
-h, --help Output usage information
-v, --version Output the version number
-V, --platform-version Set the platform version to deploy to
-n, --name Set the project name of the deployment
-A ${chalk.bold.underline('FILE')}, --local-config=${chalk.bold.underline(
'FILE'
)} Path to the local ${'`vercel.json`'} file
-Q ${chalk.bold.underline('DIR')}, --global-config=${chalk.bold.underline(
'DIR'
)} Path to the global ${'`.vercel`'} directory
-d, --debug Debug mode [off]
-f, --force Force a new deployment even if nothing has changed
-t ${chalk.underline('TOKEN')}, --token=${chalk.underline(
'TOKEN'
)} Login token
-l, --links Copy symlinks without resolving their target
-p, --public Deployment is public (${chalk.dim(
'`/_src`'
)} is exposed) [on for oss, off for premium]
-e, --env Include an env var during run time (e.g.: ${chalk.dim(
'`-e KEY=value`'
)}). Can appear many times.
-b, --build-env Similar to ${chalk.dim(
'`--env`'
)} but for build time only.
-m, --meta Add metadata for the deployment (e.g.: ${chalk.dim(
'`-m KEY=value`'
)}). Can appear many times.
-E ${chalk.underline('FILE')}, --dotenv=${chalk.underline(
'FILE'
)} Include env vars from .env file. Defaults to '.env'
-C, --no-clipboard Do not attempt to copy URL to clipboard
-N, --forward-npm Forward login information to install private npm modules
--session-affinity Session affinity, \`ip\` or \`random\` (default) to control session affinity
-S, --scope Set a custom scope
--regions Set default regions or DCs to enable the deployment on
--no-scale Skip scaling rules deploying with the default presets
--no-verify Skip step of waiting until instance count meets given constraints
${chalk.dim(`Enforceable Types (by default, it's detected automatically):`)}
--npm Node.js application
--docker Docker container
--static Static file hosting
${chalk.dim('Examples:')}
${chalk.gray('')} Deploy the current directory
${chalk.cyan('$ now')}
${chalk.gray('')} Deploy a custom path
${chalk.cyan('$ now /usr/src/project')}
${chalk.gray('')} Deploy a GitHub repository
${chalk.cyan('$ now user/repo#ref')}
${chalk.gray('')} Deploy with environment variables
${chalk.cyan('$ now -e NODE_ENV=production -e SECRET=@mysql-secret')}
${chalk.gray('')} Show the usage information for the sub command ${chalk.dim(
'`list`'
)}
${chalk.cyan('$ now help list')}
`;

View File

@@ -1,22 +1,16 @@
import fs from 'fs-extra';
import { resolve, basename, parse, join } from 'path';
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 param from '../../util/output/param.ts';
import { readLocalConfig } from '../../util/config/files';
import getArgs from '../../util/get-args';
import * as parts from './args';
import { handleError } from '../../util/error';
import readPackage from '../../util/read-package';
import preferV2Deployment, {
hasDockerfile,
hasServerfile,
} from '../../util/prefer-v2-deployment';
import getProjectName from '../../util/get-project-name';
import { help, args } from './args';
import deploy from './latest';
export default async ctx => {
const {
@@ -24,14 +18,12 @@ export default async ctx => {
config: { currentTeam },
apiUrl,
} = ctx;
const combinedArgs = Object.assign({}, parts.legacyArgs, parts.latestArgs);
let platformVersion = null;
let contextName = currentTeam || 'current user';
let argv = null;
try {
argv = getArgs(ctx.argv.slice(2), combinedArgs);
argv = getArgs(ctx.argv.slice(2), args);
} catch (error) {
handleError(error);
return 1;
@@ -58,12 +50,8 @@ export default async ctx => {
const debugEnabled = argv['--debug'];
const output = createOutput({ debug: debugEnabled });
const stats = {};
const versionFlag = argv['--platform-version'];
if (argv['--help']) {
const lastArg = argv._[argv._.length - 1];
const help = lastArg === 'deploy-v1' ? parts.legacyHelp : parts.latestHelp;
output.print(help());
return 2;
}
@@ -72,28 +60,15 @@ export default async ctx => {
try {
stats[path] = await fs.lstat(path);
} catch (err) {
const { ext } = parse(path);
if (versionFlag === 1 && !ext) {
// This will ensure `-V 1 zeit/serve` (GitHub deployments) work. Since
// GitHub repositories are never just one file, we need to set
// the `isFile` property accordingly.
stats[path] = {
isFile: () => false,
};
} else {
output.error(
`The specified file or directory "${basename(path)}" does not exist.`
);
return 1;
}
output.error(
`The specified file or directory "${basename(path)}" does not exist.`
);
return 1;
}
}
let client = null;
const isFile = Object.keys(stats).length === 1 && stats[paths[0]].isFile();
if (authConfig && authConfig.token) {
client = new Client({
apiUrl,
@@ -102,7 +77,7 @@ export default async ctx => {
debug: debugEnabled,
});
try {
({ contextName, platformVersion } = await getScope(client));
({ contextName } = await getScope(client));
} catch (err) {
if (err.code === 'NOT_AUTHORIZED' || err.code === 'TEAM_DELETED') {
output.error(err.message);
@@ -120,17 +95,14 @@ export default async ctx => {
if (version) {
if (typeof version === 'number') {
if (version !== 1 && version !== 2) {
const first = code(1);
const second = code(2);
if (version !== 2) {
const two = code(2);
output.error(
`The value of the ${prop} property within ${file} can only be ${first} or ${second}.`
`The value of the ${prop} property within ${file} can only be ${two}.`
);
return 1;
}
platformVersion = version;
} else {
output.error(
`The ${prop} property inside your ${file} file must be a number.`
@@ -140,61 +112,5 @@ export default async ctx => {
}
}
if (versionFlag) {
if (versionFlag !== 1 && versionFlag !== 2) {
output.error(
`The ${param('--platform-version')} option must be either ${code(
'1'
)} or ${code('2')}.`
);
return 1;
}
platformVersion = versionFlag;
}
if (
platformVersion === 1 &&
versionFlag !== 1 &&
!argv['--docker'] &&
!argv['--npm']
) {
// Only check when it was not set via CLI flag
const reason = await preferV2Deployment({
client,
localConfig,
projectName: getProjectName({
argv,
nowConfig: localConfig || {},
isFile,
paths,
}),
hasServerfile: await hasServerfile(paths[0]),
hasDockerfile: await hasDockerfile(paths[0]),
pkg: await readPackage(join(paths[0], 'package.json')),
});
if (reason) {
output.note(reason);
platformVersion = 2;
}
}
if (platformVersion === null || platformVersion > 1) {
return require('./latest').default(
ctx,
contextName,
output,
stats,
localConfig,
parts.latestArgs
);
}
return require('./legacy').default(
ctx,
contextName,
output,
parts.legacyArgsMri
);
return deploy(ctx, contextName, output, stats, localConfig, args);
};

View File

@@ -162,7 +162,11 @@ const printDeploymentStatus = async (
prependEmoji(
`${chalk.dim(indication.payload)}`,
emoji(indication.type)
) + `\n`
) +
`\n` +
(indication.link
? `${indication.action || 'Learn More'}: ${indication.link}\n\n`
: '')
);
}
}
@@ -669,7 +673,7 @@ export default async function main(
}
if (err instanceof BuildError) {
output.error('Build failed');
output.error(err.message || 'Build failed');
output.error(
`Check your logs at https://${now.url}/_logs or run ${getCommandName(
`logs ${now.url}`,

File diff suppressed because it is too large Load Diff

View File

@@ -8,12 +8,15 @@ 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 { getCommandName } from '../../util/pkg-name';
import { ProjectSettings } from '../../types';
import { ProjectSettings, ProjectEnvTarget } from '../../types';
import getDecryptedEnvRecords from '../../util/get-decrypted-env-records';
import { Env } from '@vercel/build-utils';
import setupAndLink from '../../util/link/setup-and-link';
type Options = {
'--debug'?: boolean;
'--listen'?: string;
'--confirm': boolean;
};
export default async function dev(
@@ -35,25 +38,39 @@ export default async function dev(
});
// retrieve dev command
const [link, frameworks] = await Promise.all([
let [link, frameworks] = await Promise.all([
getLinkedProject(output, client, cwd),
getFrameworks(client),
]);
if (link.status === 'not_linked' && !process.env.__VERCEL_SKIP_DEV_CMD) {
const autoConfirm = opts['--confirm'];
const forceDelete = false;
link = await setupAndLink(
ctx,
output,
cwd,
forceDelete,
autoConfirm,
'link',
'Set up and develop'
);
if (link.status === 'not_linked') {
// User aborted project linking questions
return 0;
}
}
if (link.status === 'error') {
return link.exitCode;
}
if (link.status === 'not_linked' && !process.env.__VERCEL_SKIP_DEV_CMD) {
output.error(
`Your codebase isnt linked to a project on Vercel. Run ${getCommandName()} to link it.`
);
return 1;
}
let devCommand: string | undefined;
let frameworkSlug: string | undefined;
let projectSettings: ProjectSettings | undefined;
let environmentVars: Env | undefined;
if (link.status === 'linked') {
const { project, org } = link;
client.currentTeam = org.type === 'team' ? org.id : undefined;
@@ -80,6 +97,13 @@ export default async function dev(
if (project.rootDirectory) {
cwd = join(cwd, project.rootDirectory);
}
environmentVars = await getDecryptedEnvRecords(
output,
client,
project,
ProjectEnvTarget.Development
);
}
const devServer = new DevServer(cwd, {
@@ -88,6 +112,7 @@ export default async function dev(
devCommand,
frameworkSlug,
projectSettings,
environmentVars,
});
process.once('SIGINT', () => devServer.stop());

View File

@@ -32,6 +32,7 @@ const help = () => {
-d, --debug Debug mode [off]
-l, --listen [uri] Specify a URI endpoint on which to listen [0.0.0.0:3000]
-t, --token [token] Specify an Authorization Token
--confirm Skip questions and use defaults when setting up a new project
${chalk.dim('Examples:')}
@@ -56,6 +57,7 @@ export default async function main(ctx: NowContext) {
argv = getArgs(ctx.argv.slice(2), {
'--listen': String,
'-l': '--listen',
'--confirm': Boolean,
// Deprecated
'--port': Number,

View File

@@ -1,23 +1,24 @@
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 addDomain from '../../util/domains/add-domain';
import Client from '../../util/client';
import cmd from '../../util/output/cmd';
import formatDnsTable from '../../util/format-dns-table';
import formatNSTable from '../../util/format-ns-table';
import getScope from '../../util/get-scope';
import stamp from '../../util/output/stamp';
import param from '../../util/output/param';
import { getCommandName, getTitleName } from '../../util/pkg-name';
import { getCommandName } from '../../util/pkg-name';
import { getDomain } from '../../util/domains/get-domain';
import { getLinkedProject } from '../../util/projects/link';
import { isPublicSuffix } from '../../util/domains/is-public-suffix';
import { getDomainConfig } from '../../util/domains/get-domain-config';
import { addDomainToProject } from '../../util/projects/add-domain-to-project';
import { removeDomainFromProject } from '../../util/projects/remove-domain-from-project';
import code from '../../util/output/code';
type Options = {
'--cdn': boolean;
'--debug': boolean;
'--no-cdn': boolean;
'--force': boolean;
};
export default async function add(
@@ -33,6 +34,7 @@ export default async function add(
const { currentTeam } = config;
const { apiUrl } = ctx;
const debug = opts['--debug'];
const force = opts['--force'];
const client = new Client({ apiUrl, token, currentTeam, debug });
let contextName = null;
@@ -47,105 +49,116 @@ export default async function add(
throw err;
}
if (opts['--cdn'] !== undefined || opts['--no-cdn'] !== undefined) {
output.error(`Toggling CF from ${getTitleName()} CLI is deprecated.`);
return 1;
}
const project = await getLinkedProject(output, client).then(result => {
if (result.status === 'linked') {
return result.project;
}
if (args.length !== 1) {
return null;
});
if (project && args.length !== 1) {
output.error(
`${getCommandName('domains add <domain>')} expects one argument`
`${getCommandName('domains add <domain>')} expects one argument.`
);
return 1;
} else if (!project && args.length !== 2) {
output.error(
`${getCommandName(
'domains add <domain> <project>'
)} expects two arguments.`
);
return 1;
}
const domainName = String(args[0]);
const parsedDomain = psl.parse(domainName);
if (parsedDomain.error) {
output.error(`The provided domain name ${param(domainName)} is invalid`);
return 1;
}
const { domain, subdomain } = parsedDomain;
if (!domain) {
output.error(`The provided domain '${param(domainName)}' is not valid.`);
return 1;
}
if (subdomain) {
output.error(
`You are adding '${domainName}' as a domain name containing a subdomain part '${subdomain}'\n` +
` This feature is deprecated, please add just the root domain: ${chalk.cyan(
`${getCommandName(`domain add ${domain}`)}`
)}`
);
return 1;
}
const projectName = project ? project.name : String(args[1]);
const addStamp = stamp();
const addedDomain = await addDomain(client, domainName, contextName);
if (addedDomain instanceof ERRORS.InvalidDomain) {
output.error(
`The provided domain name "${addedDomain.meta.domain}" is invalid`
);
return 1;
}
let aliasTarget = await addDomainToProject(client, projectName, domainName);
if (addedDomain instanceof ERRORS.DomainAlreadyExists) {
output.error(
`The domain ${chalk.underline(
addedDomain.meta.domain
)} is already registered by a different account.\n` +
` If this seems like a mistake, please contact us at support@vercel.com`
);
return 1;
if (aliasTarget instanceof Error) {
if (
aliasTarget instanceof ERRORS.APIError &&
aliasTarget.code === 'ALIAS_DOMAIN_EXIST' &&
aliasTarget.project &&
aliasTarget.project.id
) {
if (force) {
const removeResponse = await removeDomainFromProject(
client,
aliasTarget.project.id,
domainName
);
if (removeResponse instanceof Error) {
output.prettyError(removeResponse);
return 1;
}
aliasTarget = await addDomainToProject(client, projectName, domainName);
}
}
if (aliasTarget instanceof Error) {
output.prettyError(aliasTarget);
return 1;
}
}
// We can cast the information because we've just added the domain and it should be there
console.log(
`${chalk.cyan('> Success!')} Domain ${chalk.bold(
addedDomain.name
)} added correctly. ${addStamp()}\n`
domainName
)} added to project ${chalk.bold(projectName)}. ${addStamp()}`
);
if (!addedDomain.verified) {
if (isPublicSuffix(domainName)) {
output.log(
`The domain will automatically get assigned to your latest production deployment.`
);
return 0;
}
const domainResponse = await getDomain(client, contextName, domainName);
if (domainResponse instanceof Error) {
output.prettyError(domainResponse);
return 1;
}
const domainConfig = await getDomainConfig(client, domainName);
if (domainConfig.misconfigured) {
output.warn(
`The domain was added but it is not verified. To verify it, you should either:`
`This domain is not configured properly. To configure it you should either:`
);
output.print(
` ${chalk.gray(
'a)'
)} Change your domain nameservers to the following intended set: ${chalk.gray(
'[recommended]'
)}\n`
` ${chalk.grey('a)')} ` +
`Set the following record on your DNS provider to continue: ` +
`${code(`A ${domainName} 76.76.21.21`)} ` +
`${chalk.grey('[recommended]')}\n`
);
output.print(
` ${chalk.grey('b)')} ` +
`Change your Domains's nameservers to the intended set`
);
output.print(
`\n${formatNSTable(
addedDomain.intendedNameservers,
addedDomain.nameservers,
domainResponse.intendedNameservers,
domainResponse.nameservers,
{ extraSpace: ' ' }
)}\n\n`
);
output.print(
` ${chalk.gray(
'b)'
)} Add a DNS TXT record with the name and value shown below.\n`
);
output.print(
`\n${formatDnsTable([['_now', 'TXT', addedDomain.verificationRecord]], {
extraSpace: ' ',
})}\n\n`
);
output.print(
` We will run a verification for you and you will receive an email upon completion.\n`
);
output.print(
` If you want to force running a verification, you can run ${cmd(
`${getCommandName('domains verify <domain>')}`
)}\n`
output.print(' Read more: https://vercel.link/domain-configuration\n\n');
} else {
output.log(
`The domain will automatically get assigned to your latest production deployment.`
);
output.print(' Read more: https://err.sh/now/domain-verification\n\n');
}
return 0;

View File

@@ -71,8 +71,9 @@ export default async function buy(
const availableStamp = stamp();
const domainPrice = await getDomainPrice(client, domainName);
if (domainPrice instanceof ERRORS.UnsupportedTLD) {
output.error(`The TLD for ${param(domainName)} is not supported.`);
if (domainPrice instanceof Error) {
output.prettyError(domainPrice);
return 1;
}

View File

@@ -13,8 +13,8 @@ import transferIn from './transfer-in';
import inspect from './inspect';
import ls from './ls';
import rm from './rm';
import verify from './verify';
import move from './move';
import verify from './verify';
import { getPkgName } from '../../util/pkg-name';
const help = () => {
@@ -25,17 +25,17 @@ const help = () => {
ls Show all domains in a list
inspect [name] Displays information related to a domain
add [name] Add a new domain that you already own
add [name] [project] Add a new domain that you already own
rm [name] Remove a domain
buy [name] Buy a domain that you don't yet own
move [name] [destination] Move a domain to another user or team.
transfer-in [name] Transfer in a domain to Vercel
verify [name] Run a verification for a domain
${chalk.dim('Options:')}
-h, --help Output usage information
-d, --debug Debug mode [off]
-f, --force Force a domain on a project and remove it from an existing one
-A ${chalk.bold.underline('FILE')}, --local-config=${chalk.bold.underline(
'FILE'
)} Path to the local ${'`vercel.json`'} file
@@ -90,10 +90,9 @@ export default async function main(ctx: NowContext) {
try {
argv = getArgs(ctx.argv.slice(2), {
'--cdn': Boolean,
'--code': String,
'--no-cdn': Boolean,
'--yes': Boolean,
'--force': Boolean,
'--next': Number,
'-N': '--next',
});

View File

@@ -4,13 +4,18 @@ import { NowContext } from '../../types';
import { Output } from '../../util/output';
import Client from '../../util/client';
import stamp from '../../util/output/stamp';
import dnsTable from '../../util/format-dns-table';
import formatDate from '../../util/format-date';
import formatNSTable from '../../util/format-ns-table';
import getDomainByName from '../../util/domains/get-domain-by-name';
import getScope from '../../util/get-scope';
import formatTable from '../../util/format-table';
import { findProjectsForDomain } from '../../util/projects/find-projects-for-domain';
import getDomainPrice from '../../util/domains/get-domain-price';
import { getCommandName } from '../../util/pkg-name';
import { getDomainConfig } from '../../util/domains/get-domain-config';
import code from '../../util/output/code';
import wait from '../../util/output/wait';
import { getDomainRegistrar } from '../../util/domains/get-domain-registrar';
type Options = {
'--debug': boolean;
@@ -64,29 +69,26 @@ export default async function inspect(
}
output.debug(`Fetching domain info`);
const [domain, renewalPrice] = await Promise.all([
getDomainByName(client, contextName, domainName),
getDomainPrice(client, domainName, 'renewal')
.then(res => (res instanceof Error ? null : res.price))
.catch(() => null),
]);
if (domain instanceof DomainNotFound) {
output.error(
`Domain not found by "${domainName}" under ${chalk.bold(contextName)}`
);
output.log(`Run ${getCommandName(`domains ls`)} to see your domains.`);
return 1;
const cancelWait = wait(
`Fetching Domain ${domainName} under ${chalk.bold(contextName)}`
);
const information = await fetchInformation({
output,
client,
contextName,
domainName,
cancelWait,
}).finally(() => {
cancelWait();
});
if (typeof information === 'number') {
return information;
}
if (domain instanceof DomainPermissionDenied) {
output.error(
`You don't have access to the domain ${domainName} under ${chalk.bold(
contextName
)}`
);
output.log(`Run ${getCommandName(`domains ls`)} to see your domains.`);
return 1;
}
const { domain, projects, renewalPrice, domainConfig } = information;
output.log(
`Domain ${domainName} found under ${chalk.bold(contextName)} ${chalk.gray(
@@ -96,45 +98,25 @@ export default async function inspect(
output.print('\n');
output.print(chalk.bold(' General\n\n'));
output.print(` ${chalk.cyan('Name')}\t\t\t${domain.name}\n`);
output.print(` ${chalk.cyan('Service Type')}\t\t${domain.serviceType}\n`);
output.print(
` ${chalk.cyan('Ordered At')}\t\t\t${formatDate(domain.orderedAt)}\n`
` ${chalk.cyan('Registrar')}\t\t\t${getDomainRegistrar(domain)}\n`
);
output.print(
` ${chalk.cyan('Transfer Started At')}\t\t${formatDate(
domain.transferStartedAt
)}\n`
` ${chalk.cyan('Expiration Date')}\t\t${formatDate(domain.expiresAt)}\n`
);
output.print(
` ${chalk.cyan('Creator')}\t\t\t${domain.creator.username}\n`
);
output.print(
` ${chalk.cyan('Created At')}\t\t\t${formatDate(domain.createdAt)}\n`
);
output.print(` ${chalk.cyan('Edge Network')}\t\tyes\n`);
output.print(
` ${chalk.cyan('Bought At')}\t\t\t${formatDate(domain.boughtAt)}\n`
` ${chalk.cyan('Renewal Price')}\t\t${
domain.boughtAt && renewalPrice ? `$${renewalPrice} USD` : chalk.gray('-')
}\n`
);
output.print(
` ${chalk.cyan('Transferred At')}\t\t${formatDate(
domain.transferredAt
)}\n`
);
output.print(
` ${chalk.cyan('Expires At')}\t\t\t${formatDate(domain.expiresAt)}\n`
);
output.print(
` ${chalk.cyan('NS Verified At')}\t\t${formatDate(
domain.nsVerifiedAt
)}\n`
);
output.print(
` ${chalk.cyan('TXT Verified At')}\t\t${formatDate(
domain.txtVerifiedAt
)}\n`
);
if (renewalPrice && domain.boughtAt) {
output.print(
` ${chalk.cyan('Renewal Price')}\t\t$${renewalPrice} USD\n`
);
}
output.print(` ${chalk.cyan('CDN Enabled')}\t\t\t${true}\n`);
output.print('\n');
output.print(chalk.bold(' Nameservers\n\n'));
@@ -145,38 +127,139 @@ export default async function inspect(
);
output.print('\n');
output.print(chalk.bold(' Verification Record\n\n'));
output.print(
`${dnsTable([['_now', 'TXT', domain.verificationRecord]], {
extraSpace: ' ',
})}\n`
);
output.print('\n');
if (Array.isArray(projects) && projects.length > 0) {
output.print(chalk.bold(' Projects\n'));
const table = formatTable(
['Project', 'Domains'],
['l', 'l'],
[
{
rows: projects.map(project => {
const name = project.name;
const domains = (project.alias || [])
.map(target => target.domain)
.filter(alias => alias.endsWith(domainName));
const cols = domains.length ? domains.join(', ') : '-';
return [name, cols];
}),
},
]
);
if (!domain.verified) {
output.warn(`This domain is not verified. To verify it you should either:`);
output.print(
` ${chalk.gray(
'a)'
)} Change your domain nameservers to the intended set detailed above. ${chalk.gray(
'[recommended]'
)}\n`
table
.split('\n')
.map(line => ` ${line}`)
.join('\n')
);
output.print('\n');
}
if (domainConfig.misconfigured) {
output.warn(
`This Domain is not configured properly. To configure it you should either:`,
null,
null,
null,
{
boxen: {
margin: {
left: 2,
right: 0,
bottom: 0,
top: 0,
},
},
}
);
output.print(
` ${chalk.gray(
'b)'
)} Add a DNS TXT record with the name and value shown above.\n\n`
` ${chalk.grey('a)')} ` +
`Set the following record on your DNS provider to continue: ` +
`${code(`A ${domainName} 76.76.21.21`)} ` +
`${chalk.grey('[recommended]')}\n`
);
output.print(
` ${chalk.grey('b)')} ` +
`Change your Domains's nameservers to the intended set detailed above.\n\n`
);
output.print(
` We will run a verification for you and you will receive an email upon completion.\n`
);
output.print(
` If you want to force running a verification, you can run ${getCommandName(
`domains verify <domain>`
)}\n`
const contextNameConst = contextName;
const projectNames = Array.from(
new Set(projects.map(project => project.name))
);
output.print(' Read more: https://err.sh/now/domain-verification\n\n');
if (projectNames.length) {
projectNames.forEach((name, index) => {
const prefix = index === 0 ? ' Read more:' : ' '.repeat(12);
output.print(
`${prefix} https://vercel.com/${contextNameConst}/${name}/settings/domains\n`
);
});
} else {
output.print(` Read more: https://vercel.link/domain-configuration\n`);
}
output.print('\n');
}
return null;
}
async function fetchInformation({
output,
client,
contextName,
domainName,
cancelWait,
}: {
output: Output;
client: Client;
contextName: string;
domainName: string;
cancelWait: () => void;
}) {
const [domain, renewalPrice] = await Promise.all([
getDomainByName(client, contextName, domainName, { ignoreWait: true }),
getDomainPrice(client, domainName, 'renewal')
.then(res => (res instanceof Error ? null : res.price))
.catch(() => null),
]);
if (domain instanceof DomainNotFound) {
cancelWait();
output.prettyError(domain);
return 1;
}
if (domain instanceof DomainPermissionDenied) {
cancelWait();
output.prettyError(domain);
output.log(`Run ${getCommandName(`domains ls`)} to see your domains.`);
return 1;
}
const projects = await findProjectsForDomain(client, domainName);
if (projects instanceof Error) {
cancelWait();
output.prettyError(projects);
return 1;
}
const domainConfig = await getDomainConfig(client, domainName);
return {
domain,
projects,
renewalPrice,
domainConfig,
};
}

View File

@@ -1,16 +1,20 @@
import ms from 'ms';
import chalk from 'chalk';
import table from 'text-table';
import plural from 'pluralize';
import wait from '../../util/output/wait';
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 strlen from '../../util/strlen';
import { Output } from '../../util/output';
import formatTable from '../../util/format-table';
import { formatDateWithoutTime } from '../../util/format-date';
import { Domain, NowContext } from '../../types';
import getCommandFlags from '../../util/get-command-flags';
import { getCommandName } from '../../util/pkg-name';
import isDomainExternal from '../../util/domains/is-domain-external';
import { getDomainRegistrar } from '../../util/domains/get-domain-registrar';
type Options = {
'--debug': boolean;
@@ -60,22 +64,29 @@ export default async function ls(
return 1;
}
const { domains, pagination } = await getDomains(
client,
contextName,
nextTimestamp
);
const cancelWait = wait(`Fetching Domains under ${chalk.bold(contextName)}`);
const { domains, pagination } = await getDomains(client).finally(() => {
cancelWait();
});
output.log(
`Domains found under ${chalk.bold(contextName)} ${chalk.gray(lsStamp())}\n`
`${plural('Domain', domains.length, true)} found under ${chalk.bold(
contextName
)} ${chalk.gray(lsStamp())}`
);
if (domains.length > 0) {
console.log(`${formatDomainsTable(domains)}\n`);
output.print(
formatDomainsTable(domains).replace(/^(.*)/gm, `${' '.repeat(1)}$1`)
);
output.print('\n\n');
}
if (pagination && pagination.count === 20) {
const flags = getCommandFlags(opts, ['_', '--next']);
output.log(
`To display the next page run ${getCommandName(
`To display the next page, run ${getCommandName(
`domains ls${flags} --next ${pagination.next}`
)}`
);
@@ -85,27 +96,25 @@ export default async function ls(
}
function formatDomainsTable(domains: Domain[]) {
const current = new Date();
return table(
[
[
'',
chalk.gray('domain'),
chalk.gray('serviceType'),
chalk.gray('verified'),
chalk.gray('cdn'),
chalk.gray('age'),
].map(s => chalk.dim(s)),
...domains.map(domain => {
const url = chalk.bold(domain.name);
const time = chalk.gray(ms(current.getTime() - domain.createdAt));
return ['', url, domain.serviceType, domain.verified, true, time];
}),
],
{
align: ['l', 'l', 'l', 'l', 'l'],
hsep: ' '.repeat(4),
stringLength: strlen,
}
const current = Date.now();
const rows: string[][] = domains.map(domain => {
const expiration = formatDateWithoutTime(domain.expiresAt);
const age = domain.createdAt ? ms(current - domain.createdAt) : '-';
return [
domain.name,
getDomainRegistrar(domain),
isDomainExternal(domain) ? 'Third Party' : 'Vercel',
expiration,
domain.creator.username,
chalk.gray(age),
];
});
return formatTable(
['Domain', 'Registrar', 'Nameservers', 'Expiration Date', 'Creator', 'Age'],
['l', 'l', 'l', 'l', 'l', 'l'],
[{ rows }]
);
}

View File

@@ -15,6 +15,7 @@ import * as ERRORS from '../../util/errors-ts';
import param from '../../util/output/param';
import promptBool from '../../util/input/prompt-bool';
import setCustomSuffix from '../../util/domains/set-custom-suffix';
import { findProjectsForDomain } from '../../util/projects/find-projects-for-domain';
import { getCommandName } from '../../util/pkg-name';
type Options = {
@@ -67,7 +68,7 @@ export default async function rm(
}
const domain = await getDomainByName(client, contextName, domainName);
if (domain instanceof DomainNotFound) {
if (domain instanceof DomainNotFound || domain.name !== domainName) {
output.error(
`Domain not found by "${domainName}" under ${chalk.bold(contextName)}`
);
@@ -85,6 +86,18 @@ export default async function rm(
return 1;
}
const projects = await findProjectsForDomain(client, domain.name);
if (Array.isArray(projects) && projects.length > 0) {
output.warn(
`The domain is currently used by ${plural(
'project',
projects.length,
true
)}.`
);
}
const skipConfirmation = opts['--yes'];
if (
!skipConfirmation &&

View File

@@ -71,8 +71,8 @@ export default async function transferIn(
checkTransfer(client, domainName),
]);
if (domainPrice instanceof ERRORS.UnsupportedTLD) {
output.error(`The TLD for ${param(domainName)} is not supported.`);
if (domainPrice instanceof Error) {
output.prettyError(domainPrice);
return 1;
}

View File

@@ -1,147 +1,33 @@
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 formatDnsTable from '../../util/format-dns-table';
import formatNSTable from '../../util/format-ns-table';
import getDomainByName from '../../util/domains/get-domain-by-name';
import getScope from '../../util/get-scope';
import stamp from '../../util/output/stamp';
import verifyDomain from '../../util/domains/verify-domain';
import { NowBuildError } from '@vercel/build-utils';
import { getCommandName } from '../../util/pkg-name';
type Options = {
'--debug': boolean;
};
export default async function verify(
ctx: NowContext,
opts: Options,
_ctx: NowContext,
_opts: {},
args: string[],
output: Output
) {
const {
authConfig: { token },
config,
} = ctx;
const { currentTeam } = config;
const { apiUrl } = ctx;
const debug = opts['--debug'];
const client = new Client({ apiUrl, token, currentTeam, debug });
let contextName = null;
try {
({ contextName } = await getScope(client));
} catch (err) {
if (err.code === 'NOT_AUTHORIZED' || err.code === 'TEAM_DELETED') {
output.error(err.message);
return 1;
}
throw err;
}
const [domainName] = args;
if (!domainName) {
output.error(
`${getCommandName(`domains verify <domain>`)} expects one argument`
);
return 1;
}
if (args.length !== 1) {
output.error(
`Invalid number of arguments. Usage: ${chalk.cyan(
`${getCommandName('domains verify <domain>')}`
)}`
);
return 1;
}
const error = new NowBuildError({
code: 'domains_verify_command_deprecated',
message: `It's not necessary to verify Domains anymore. Instead, you can run ${getCommandName(
`domains inspect ${domainName}`
)} to see what you need to do in order to configure it properly.`,
link: 'https://vercel.link/domain-verification-via-cli',
});
const domain = await getDomainByName(client, contextName, domainName);
if (domain instanceof ERRORS.DomainNotFound) {
output.error(
`Domain not found by "${domainName}" under ${chalk.bold(contextName)}`
);
output.log(`Run ${getCommandName(`domains ls`)} to see your domains.`);
return 1;
}
output.prettyError(error);
if (domain instanceof ERRORS.DomainPermissionDenied) {
output.error(
`You don't have access to the domain ${domainName} under ${chalk.bold(
contextName
)}`
);
output.log(`Run ${getCommandName(`domains ls`)} to see your domains.`);
return 1;
}
const verifyStamp = stamp();
const result = await verifyDomain(client, domain.name, contextName);
if (result instanceof ERRORS.DomainVerificationFailed) {
const { nsVerification, txtVerification } = result.meta;
output.error(
`The domain ${
domain.name
} could not be verified due to the following reasons: ${verifyStamp()}\n`
);
output.print(
` ${chalk.gray(
'a)'
)} Nameservers verification failed since we see a different set than the intended set:`
);
output.print(
`\n${formatNSTable(
nsVerification.intendedNameservers,
nsVerification.nameservers,
{ extraSpace: ' ' }
)}\n\n`
);
output.print(
` ${chalk.gray(
'b)'
)} DNS TXT verification failed since found no matching records.`
);
output.print(
`\n${formatDnsTable(
[['_now', 'TXT', txtVerification.verificationRecord]],
{ extraSpace: ' ' }
)}\n\n`
);
output.print(
` Once your domain uses either the nameservers or the TXT DNS record from above, run again ${getCommandName(
`domains verify <domain>`
)}.\n`
);
output.print(
` We will also periodically run a verification check for you and you will receive an email once your domain is verified.\n`
);
output.print(' Read more: https://err.sh/now/domain-verification\n\n');
return 1;
}
if (result.nsVerifiedAt) {
console.log(
`${chalk.cyan('> Success!')} Domain ${chalk.bold(
domain.name
)} was verified using nameservers. ${verifyStamp()}`
);
return 0;
}
console.log(
`${chalk.cyan('> Success!')} Domain ${chalk.bold(
domain.name
)} was verified using DNS TXT record. ${verifyStamp()}`
);
output.print(
` You can verify with nameservers too. Run ${getCommandName(
`domains inspect ${domain.name}`
)} to find out the intended set.\n`
);
return 0;
}

View File

@@ -124,7 +124,9 @@ export default async function main(ctx: NowContext) {
return link.exitCode;
} else if (link.status === 'not_linked') {
output.error(
`Your codebase isnt linked to a project on Vercel. Run ${getCommandName()} to link it.`
`Your codebase isnt linked to a project on Vercel. Run ${getCommandName(
'link'
)} to begin.`
);
return 1;
} else {

View File

@@ -4,8 +4,7 @@ import { Output } from '../../util/output';
import promptBool from '../../util/prompt-bool';
import Client from '../../util/client';
import stamp from '../../util/output/stamp';
import getEnvVariables from '../../util/env/get-env-records';
import getDecryptedSecret from '../../util/env/get-decrypted-secret';
import getDecryptedEnvRecords from '../../util/get-decrypted-env-records';
import param from '../../util/output/param';
import withSpinner from '../../util/with-spinner';
import { join } from 'path';
@@ -13,6 +12,7 @@ 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';
const CONTENTS_PREFIX = '# Created by Vercel CLI\n';
@@ -84,45 +84,21 @@ export default async function pull(
);
const pullStamp = stamp();
const records = await withSpinner('Downloading', async () => {
const dev = ProjectEnvTarget.Development;
const envs = await getEnvVariables(output, client, project.id, 4, dev);
const decryptedValues = await Promise.all(
envs.map(async env => {
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;
}
})
);
const results: { key: string; value: string; found: boolean }[] = [];
for (let i = 0; i < decryptedValues.length; i++) {
const { key } = envs[i];
const { value, found } = decryptedValues[i];
results.push({ key, value, found });
}
return results;
});
const records: Env = await withSpinner(
'Downloading',
async () =>
await getDecryptedEnvRecords(
output,
client,
project,
ProjectEnvTarget.Development
)
);
const contents =
CONTENTS_PREFIX +
records
.filter(obj => {
if (!obj.found) {
output.print('');
output.warn(
`Unable to download variable ${obj.key} because associated secret was deleted`
);
return false;
}
return true;
})
.map(({ key, value }) => `${key}="${escapeValue(value)}"`)
Object.entries(records)
.map(([key, value]) => `${key}="${escapeValue(value)}"`)
.join('\n') +
'\n';
@@ -139,8 +115,10 @@ export default async function pull(
return 0;
}
function escapeValue(value: string) {
function escapeValue(value: string | undefined) {
return value
.replace(new RegExp('\n', 'g'), '\\n') // combine newlines (unix) into one line
.replace(new RegExp('\r', 'g'), '\\r'); // combine newlines (windows) into one line
? value
.replace(new RegExp('\n', 'g'), '\\n') // combine newlines (unix) into one line
.replace(new RegExp('\r', 'g'), '\\r') // combine newlines (windows) into one line
: '';
}

View File

@@ -6,8 +6,6 @@ export default new Map([
['cert', 'certs'],
['certs', 'certs'],
['deploy', 'deploy'],
['deploy-v1', 'deploy'],
['deploy-v2', 'deploy'],
['dev', 'dev'],
['dns', 'dns'],
['domain', 'domains'],
@@ -17,6 +15,7 @@ export default new Map([
['help', 'help'],
['init', 'init'],
['inspect', 'inspect'],
['link', 'link'],
['list', 'list'],
['ln', 'alias'],
['log', 'logs'],
@@ -28,7 +27,6 @@ export default new Map([
['projects', 'projects'],
['remove', 'remove'],
['rm', 'remove'],
['scale', 'scale'],
['secret', 'secrets'],
['secrets', 'secrets'],
['switch', 'teams'],

View File

@@ -1,5 +1,4 @@
import chalk from 'chalk';
import table from 'text-table';
import getArgs from '../util/get-args';
import buildsList from '../util/output/builds';
import routesList from '../util/output/routes';
@@ -9,13 +8,10 @@ import Now from '../util';
import logo from '../util/output/logo';
import elapsed from '../util/output/elapsed.ts';
import { handleError } from '../util/error';
import strlen from '../util/strlen.ts';
import Client from '../util/client.ts';
import getScope from '../util/get-scope.ts';
import { getPkgName, getCommandName } from '../util/pkg-name.ts';
const STATIC = 'STATIC';
const help = () => {
console.log(`
${chalk.bold(`${logo} ${getPkgName()} inspect`)} <url>
@@ -48,7 +44,6 @@ const help = () => {
};
export default async function main(ctx) {
let id;
let deployment;
let argv;
@@ -70,7 +65,7 @@ export default async function main(ctx) {
const { print, log, error } = output;
// extract the first parameter
id = argv._[1];
const [, deploymentIdOrHost] = argv._;
if (argv._.length !== 2) {
error(`${getCommandName('inspect <url>')} expects exactly one argument`);
@@ -107,20 +102,24 @@ export default async function main(ctx) {
// resolve the deployment, since we might have been given an alias
const depFetchStart = Date.now();
const cancelWait = output.spinner(
`Fetching deployment "${id}" in ${chalk.bold(contextName)}`
`Fetching deployment "${deploymentIdOrHost}" in ${chalk.bold(contextName)}`
);
try {
deployment = await now.findDeployment(id);
deployment = await now.findDeployment(deploymentIdOrHost);
} catch (err) {
cancelWait();
if (err.status === 404) {
error(`Failed to find deployment "${id}" in ${chalk.bold(contextName)}`);
error(
`Failed to find deployment "${deploymentIdOrHost}" in ${chalk.bold(
contextName
)}`
);
return 1;
}
if (err.status === 403) {
error(
`No permission to access deployment "${id}" in ${chalk.bold(
`No permission to access deployment "${deploymentIdOrHost}" in ${chalk.bold(
contextName
)}`
);
@@ -130,35 +129,12 @@ export default async function main(ctx) {
throw err;
}
const {
id: finalId,
name,
state,
type,
slot,
sessionAffinity,
url,
created,
limits,
version,
routes,
readyState,
} = deployment;
const { id, name, url, created, routes, readyState } = deployment;
const isBuilds = version === 2;
const buildsUrl = `/v1/now/deployments/${finalId}/builds`;
const [scale, events, { builds }] = await Promise.all([
caught(
now.fetch(`/v3/now/deployments/${encodeURIComponent(finalId)}/instances`)
),
type === STATIC
? null
: caught(
now.fetch(`/v1/now/deployments/${encodeURIComponent(finalId)}/events`)
),
isBuilds ? now.fetch(buildsUrl) : { builds: [] },
]);
const { builds } =
deployment.version === 2
? await now.fetch(`/v1/now/deployments/${id}/builds`)
: { builds: [] };
cancelWait();
log(
@@ -169,21 +145,9 @@ export default async function main(ctx) {
print('\n');
print(chalk.bold(' General\n\n'));
print(` ${chalk.cyan('version')}\t${version}\n`);
print(` ${chalk.cyan('id')}\t\t${finalId}\n`);
print(` ${chalk.cyan('id')}\t\t${id}\n`);
print(` ${chalk.cyan('name')}\t${name}\n`);
print(
` ${chalk.cyan('readyState')}\t${stateString(state || readyState)}\n`
);
if (!isBuilds) {
print(` ${chalk.cyan('type')}\t${type}\n`);
}
if (slot) {
print(` ${chalk.cyan('slot')}\t${slot}\n`);
}
if (sessionAffinity) {
print(` ${chalk.cyan('affinity')}\t${sessionAffinity}\n`);
}
print(` ${chalk.cyan('readyState')}\t${stateString(readyState)}\n`);
print(` ${chalk.cyan('url')}\t\t${url}\n`);
if (created) {
print(
@@ -214,95 +178,7 @@ export default async function main(ctx) {
print(`\n\n`);
}
if (limits) {
print(chalk.bold(' Limits\n\n'));
print(
` ${chalk.dim('duration')}\t\t${limits.duration} ${elapsed(
limits.duration
)}\n`
);
print(
` ${chalk.dim('maxConcurrentReqs')}\t${limits.maxConcurrentReqs}\n`
);
print(
` ${chalk.dim('timeout')}\t\t${limits.timeout} ${elapsed(
limits.timeout
)}\n`
);
print('\n');
}
if (type === STATIC || isBuilds) {
return 0;
}
print(chalk.bold(' Scale\n\n'));
let exitCode = 0;
if (scale instanceof Error) {
error(`Scale information unavailable: ${scale}`);
exitCode = 1;
} else {
const dcs = Object.keys(scale);
const t = [['dc', 'min', 'max', 'current'].map(v => chalk.gray(v))];
for (const dc of dcs) {
const { instances } = scale[dc];
const cfg = deployment.scale[dc] || {};
t.push([dc, cfg.min || 0, cfg.max || 0, instances.length]);
}
print(
`${table(t, {
align: ['l', 'c', 'c', 'c'],
hsep: ' '.repeat(8),
stringLength: strlen,
}).replace(/^(.*)/gm, ' $1')}\n`
);
print('\n');
}
print(chalk.bold(' Events\n\n'));
if (events instanceof Error) {
error(`Events unavailable: ${scale}`);
exitCode = 1;
} else if (events) {
events.forEach(data => {
if (!data.event) return; // keepalive
print(
` ${chalk.gray(new Date(data.created).toISOString())} ${
data.event
} ${getEventMetadata(data)}\n`
);
});
print('\n');
}
return exitCode;
}
// gets the metadata that should be printed next to
// each event
function getEventMetadata({ event, payload }) {
if (event === 'state') {
return chalk.bold(payload.value);
}
if (event === 'instance-start' || event === 'instance-stop') {
if (payload.dc != null) {
return chalk.green(`(${payload.dc})`);
}
}
return '';
}
// makes sure the promise never rejects, exposing the error
// as the resolved value instead
function caught(p) {
return new Promise(r => {
p.then(r).catch(r);
});
return 0;
}
// renders the state string
@@ -318,6 +194,6 @@ function stateString(s) {
return s;
default:
return chalk.gray('UNKNOWN');
return chalk.gray(s || 'UNKNOWN');
}
}

View File

@@ -0,0 +1,98 @@
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';
import logo from '../../util/output/logo';
import { getPkgName } from '../../util/pkg-name';
import setupAndLink from '../../util/link/setup-and-link';
const help = () => {
console.log(`
${chalk.bold(`${logo} ${getPkgName()} link`)} [options]
${chalk.dim('Options:')}
-h, --help Output usage information
-A ${chalk.bold.underline('FILE')}, --local-config=${chalk.bold.underline(
'FILE'
)} Path to the local ${'`vercel.json`'} file
-Q ${chalk.bold.underline('DIR')}, --global-config=${chalk.bold.underline(
'DIR'
)} Path to the global ${'`.vercel`'} directory
-d, --debug Debug mode [off]
-t ${chalk.bold.underline('TOKEN')}, --token=${chalk.bold.underline(
'TOKEN'
)} Login token
--confirm Confirm default options and skip questions
${chalk.dim('Examples:')}
${chalk.gray('')} Link current directory to a Vercel Project
${chalk.cyan(`$ ${getPkgName()} link`)}
${chalk.gray(
''
)} Link current directory with default options and skip questions
${chalk.cyan(`$ ${getPkgName()} link --confirm`)}
${chalk.gray('')} Link a specific directory to a Vercel Project
${chalk.cyan(`$ ${getPkgName()} link /usr/src/project`)}
`);
};
const COMMAND_CONFIG = {
// No subcommands yet
};
export default async function main(ctx: NowContext) {
let argv;
try {
argv = getArgs(ctx.argv.slice(2), {
'--confirm': Boolean,
});
} catch (error) {
handleError(error);
return 1;
}
if (argv['--help']) {
help();
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'];
const forceDelete = true;
const link = await setupAndLink(
ctx,
output,
path,
forceDelete,
autoConfirm,
'success',
'Set up'
);
if (link.status === 'error') {
return link.exitCode;
} else if (link.status === 'not_linked') {
// User aborted project linking questions
return 0;
} else if (link.status === 'linked') {
// Successfully linked
return 0;
} else {
const err: never = link;
throw new Error('Unknown link status: ' + err);
}
}

View File

@@ -2,9 +2,7 @@ import chalk from 'chalk';
import ms from 'ms';
import table from 'text-table';
import Now from '../util';
import getAliases from '../util/alias/get-aliases';
import getArgs from '../util/get-args';
import getDeploymentInstances from '../util/deploy/get-deployment-instances';
import createOutput from '../util/output';
import { handleError } from '../util/error';
import cmd from '../util/output/cmd.ts';
@@ -38,7 +36,6 @@ const help = () => {
'TOKEN'
)} Login token
-S, --scope Set a custom scope
-a, --all See all instances for each deployment (requires [app])
-m, --meta Filter deployments by metadata (e.g.: ${chalk.dim(
'`-m KEY=value`'
)}). Can appear many times.
@@ -72,16 +69,12 @@ const help = () => {
`);
};
// Options
// $FlowFixMe
export default async function main(ctx) {
let argv;
try {
argv = getArgs(ctx.argv.slice(2), {
'--all': Boolean,
'--meta': [String],
'-a': '--all',
'-m': '--meta',
'--next': Number,
'-N': '--next',
@@ -151,11 +144,6 @@ export default async function main(ctx) {
const now = new Now({ apiUrl, token, debug: debugEnabled, currentTeam });
const start = new Date();
if (argv['--all'] && !app) {
error('You must define an app when using `-a` / `--all`');
return 1;
}
if (app && !isValidName(app)) {
error(`The provided argument "${app}" is not a valid project name`);
return 1;
@@ -222,51 +210,8 @@ export default async function main(ctx) {
}
}
if (app && !deployments.length) {
debug(
'No deployments: attempting to find aliases that matches supplied app name'
);
const { aliases } = await getAliases(now);
const item = aliases.find(e => e.uid === app || e.alias === app);
if (item) {
debug(`Found alias that matches app name: ${item.alias}`);
if (Array.isArray(item.rules)) {
now.close();
stopSpinner();
log(`Found matching path alias: ${chalk.cyan(item.alias)}`);
log(`Please run ${getCommandName(`alias ls ${item.alias}`)} instead`);
return 0;
}
const match = await now.findDeployment(item.deploymentId);
const instances = await getDeploymentInstances(
now,
item.deploymentId,
'now_cli_alias_instances'
);
match.instanceCount = Object.keys(instances).reduce(
(count, dc) => count + instances[dc].instances.length,
0
);
if (match !== null && typeof match !== 'undefined') {
deployments = Array.of(match);
}
}
}
now.close();
if (argv['--all']) {
await Promise.all(
deployments.map(async ({ uid, instanceCount }, i) => {
deployments[i].instances =
instanceCount > 0 ? await now.listInstances(uid) : [];
})
);
}
if (host) {
deployments = deployments.filter(deployment => deployment.url === host);
}
@@ -290,12 +235,6 @@ export default async function main(ctx) {
`${getCommandName('ls [project]')}`
)}`
);
} else if (!argv['--all']) {
log(
`To list deployment instances run ${cmd(
`${getCommandName('ls --all [project]')}`
)}`
);
}
print('\n');
@@ -316,15 +255,6 @@ export default async function main(ctx) {
chalk.gray(ms(Date.now() - new Date(dep.createdAt))),
dep.creator.username,
],
...(argv['--all']
? dep.instances.map(i => [
'',
` ${chalk.gray('-')} ${i.url} `,
'',
'',
'',
])
: []),
])
// flatten since the previous step returns a nested
// array of the deployment and (optionally) its instances

View File

@@ -1,341 +0,0 @@
import ms from 'ms';
import chalk from 'chalk';
import cmd from '../util/output/cmd.ts';
import createOutput from '../util/output';
import logo from '../util/output/logo';
import stamp from '../util/output/stamp.ts';
import Now from '../util';
import getArgs from '../util/get-args';
import Client from '../util/client.ts';
import getScope from '../util/get-scope.ts';
import getDCsFromArgs from '../util/scale/get-dcs-from-args';
import getDeploymentByIdOrHost from '../util/deploy/get-deployment-by-id-or-host';
import getDeploymentByIdOrThrow from '../util/deploy/get-deployment-by-id-or-throw';
import getMaxFromArgs from '../util/scale/get-max-from-args';
import getMinFromArgs from '../util/scale/get-min-from-args';
import patchDeploymentScale from '../util/scale/patch-deployment-scale';
import waitVerifyDeploymentScale from '../util/scale/wait-verify-deployment-scale';
import { handleError } from '../util/error';
import {
VerifyScaleTimeout,
DeploymentTypeUnsupported,
} from '../util/errors-ts';
import {
DeploymentNotFound,
DeploymentPermissionDenied,
ForbiddenScaleMaxInstances,
ForbiddenScaleMinInstances,
InvalidArgsForMinMaxScale,
InvalidMaxForScale,
InvalidMinForScale,
InvalidScaleMinMaxRelation,
NotSupportedMinScaleSlots,
} from '../util/errors-ts';
import { InvalidAllForScale, InvalidRegionOrDCForScale } from '../util/errors';
import handleCertError from '../util/certs/handle-cert-error';
import { getPkgName } from '../util/pkg-name.ts';
const help = () => {
console.log(`
${chalk.bold(`${logo} ${getPkgName()} scale`)} <url> <dc> [min] [max]
${chalk.dim('Options:')}
-h, --help Output usage information
-A ${chalk.bold.underline('FILE')}, --local-config=${chalk.bold.underline(
'FILE'
)} Path to the local ${'`vercel.json`'} file
-Q ${chalk.bold.underline('DIR')}, --global-config=${chalk.bold.underline(
'DIR'
)} Path to the global ${'`.vercel`'} directory
-t ${chalk.bold.underline('TOKEN')}, --token=${chalk.bold.underline(
'TOKEN'
)} Login token
-d, --debug Debug mode [off]
-S, --scope Set a custom scope
-n, --no-verify Skip step of waiting until instance count meets given constraints
-t, --verify-timeout How long to wait for verification to complete [5m]
${chalk.dim('Examples:')}
${chalk.gray(
''
)} Enable your deployment in all datacenters (min: 0, max: auto)
${chalk.cyan(`$ ${getPkgName()} scale my-deployment-123.now.sh all`)}
${chalk.gray(
'-'
)} Enable your deployment in the SFO datacenter (min: 0, max: auto)
${chalk.cyan(`$ ${getPkgName()} scale my-deployment-123.now.sh sfo`)}
${chalk.gray(
''
)} Scale a deployment in all datacenters to 3 instances at all times (no sleep)
${chalk.cyan(`$ ${getPkgName()} scale my-deployment-123.now.sh all 3`)}
${chalk.gray(
''
)} Enable your deployment in all datacenters, with auto-scaling
${chalk.cyan(`$ ${getPkgName()} scale my-deployment-123.now.sh all auto`)}
`);
};
export default async function main(ctx) {
let argv;
try {
argv = getArgs(ctx.argv.slice(2), {
'--verify-timeout': Number,
'--no-verify': Boolean,
'-n': '--no-verify',
});
} catch (err) {
handleError(err);
return 1;
}
if (argv['--help']) {
help();
return 2;
}
// Prepare the context
const {
authConfig: { token },
config,
} = ctx;
const { currentTeam } = config;
const { apiUrl } = ctx;
const debug = argv['--debug'];
// $FlowFixMe
const now = new Now({ apiUrl, token, debug, currentTeam });
const output = createOutput({ debug });
const client = new Client({ apiUrl, token, currentTeam, debug });
let contextName = null;
try {
({ contextName } = await getScope(client));
} catch (err) {
if (err.code === 'NOT_AUTHORIZED' || err.code === 'TEAM_DELETED') {
output.error(err.message);
return 1;
}
throw err;
}
// Fail if the user is providing an old command
if (argv._[1] === 'ls') {
output.error(
`${cmd(`${getPkgName()} scale ls`)} has been deprecated. Use ${cmd(
'now ls'
)} and ${cmd(`${getPkgName()} inspect <url>`)}`
);
now.close();
return 1;
}
// Ensure the number of arguments is between the allower range
if (argv._.length < 3 || argv._.length > 5) {
output.error(
`${cmd(
`${getPkgName()} scale <url> <dc> [min] [max]`
)} expects at least two arguments`
);
help();
now.close();
return 1;
}
const dcs = getDCsFromArgs(argv._);
if (dcs instanceof InvalidAllForScale) {
output.error(
'The region value "all" was used, but it cannot be used alongside other region or dc identifiers'
);
now.close();
return 1;
}
if (dcs instanceof InvalidRegionOrDCForScale) {
output.error(
`The value "${dcs.meta.regionOrDC}" is not a valid region or DC identifier`
);
now.close();
return 1;
}
const min = getMinFromArgs(argv._);
if (min instanceof InvalidMinForScale) {
output.error(
`Invalid <min> parameter "${min.meta.value}". A number or "auto" were expected`
);
now.close();
return 1;
}
const max = getMaxFromArgs(argv._);
if (max instanceof InvalidMinForScale) {
output.error(
`Invalid <min> parameter "${max.meta.value}". A number or "auto" were expected`
);
now.close();
return 1;
}
if (max instanceof InvalidArgsForMinMaxScale) {
output.error(
`Invalid number of arguments: expected <min> ("${max.meta.min}") and [max]`
);
now.close();
return 1;
}
if (max instanceof InvalidMaxForScale) {
output.error(
`Invalid <max> parameter "${max.meta.value}". A number or "auto" were expected`
);
now.close();
return 1;
}
// Fetch the deployment
const deploymentStamp = stamp();
const deployment = handleCertError(
output,
await getDeploymentByIdOrHost(now, contextName, argv._[1])
);
if (deployment === 1) {
return deployment;
}
if (deployment instanceof DeploymentPermissionDenied) {
output.error(
`No permission to access deployment ${chalk.dim(
deployment.meta.id
)} under ${chalk.bold(deployment.meta.context)}`
);
now.close();
return 1;
}
if (deployment instanceof DeploymentNotFound) {
output.error(
`Failed to find deployment "${argv._[1]}" in ${chalk.bold(contextName)}`
);
now.close();
return 1;
}
output.log(`Fetched deployment "${deployment.url}" ${deploymentStamp()}`);
// Make sure the deployment can be scaled
if (deployment.type === 'STATIC') {
output.error('Scaling rules cannot be set on static deployments');
now.close();
return 1;
}
if (deployment.state === 'ERROR') {
output.error('Cannot scale a deployment in the ERROR state');
now.close();
return 1;
}
if (deployment.version === 2) {
output.error('Cannot scale a Now 2.0 deployment');
now.close();
return 1;
}
const scaleArgs = dcs.reduce(
(result, dc) => ({ ...result, [dc]: { min, max } }),
{}
);
output.debug(
`Setting scale deployment presets to ${JSON.stringify(scaleArgs)}`
);
// Set the deployment scale
const scaleStamp = stamp();
const result = await patchDeploymentScale(
output,
now,
deployment.uid,
scaleArgs,
deployment.url
);
if (result instanceof ForbiddenScaleMinInstances) {
output.error(
`You can't scale to more than ${result.meta.max} min instances with your current plan.`
);
now.close();
return 1;
}
if (result instanceof ForbiddenScaleMaxInstances) {
output.error(
`You can't scale to more than ${result.meta.max} max instances with your current plan.`
);
now.close();
return 1;
}
if (result instanceof InvalidScaleMinMaxRelation) {
output.error(`Min number of instances can't be higher than max.`);
now.close();
return 1;
}
if (result instanceof NotSupportedMinScaleSlots) {
output.error(
`Cloud v2 does not yet support setting a non-zero min number of instances.`
);
output.log('Read more: https://err.sh/now/v2-no-min');
now.close();
return 1;
}
if (result instanceof DeploymentTypeUnsupported) {
output.error(`This region only accepts Serverless Docker Deployments.`);
now.close();
return 1;
}
console.log(
`${chalk.gray('>')} Scale rules for ${dcs
.map(d => chalk.bold(d))
.join(', ')} (min: ${chalk.bold(min)}, max: ${chalk.bold(
max
)}) saved ${scaleStamp()}`
);
if (argv['--no-verify']) {
now.close();
return 0;
}
// Verify that the scale presets are there
const verifyStamp = stamp();
const updatedDeployment = await getDeploymentByIdOrThrow(
now,
contextName,
deployment.uid
);
if (updatedDeployment.type === 'NPM' || updatedDeployment.type === 'DOCKER') {
const result = await waitVerifyDeploymentScale(
output,
now,
deployment.uid,
updatedDeployment.scale
);
if (result instanceof VerifyScaleTimeout) {
output.error(
`Instance verification timed out (${ms(result.meta.timeout)})`,
'verification-timeout'
);
now.close();
return 1;
}
output.success(`Scale state verified ${verifyStamp()}`);
}
now.close();
return 0;
}

View File

@@ -1,7 +1,6 @@
import chalk from 'chalk';
import { email as regexEmail } from '../../util/input/regexes';
import wait from '../../util/output/wait';
import fatalError from '../../util/fatal-error';
import cmd from '../../util/output/cmd.ts';
import info from '../../util/output/info';
import stamp from '../../util/output/stamp.ts';
@@ -15,6 +14,7 @@ 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;
@@ -57,7 +57,7 @@ const emailAutoComplete = (value, teamSlug) => {
return false;
};
export default async function({
export default async function ({
teams,
args,
config,
@@ -66,6 +66,7 @@ export default async function({
apiUrl,
token,
} = {}) {
const output = createOutput();
const { currentTeam: currentTeamId } = config;
const stopSpinner = wait('Fetching teams');
@@ -100,7 +101,8 @@ export default async function({
)}.\nPlease select a team scope using ${getCommandName(
`switch`
)} or use ${cmd('--scope')}`;
return fatalError(err);
output.error(err);
return 1;
}
console.log(

View File

@@ -13,15 +13,13 @@ try {
process.exit(1);
}
}
import 'core-js/modules/es7.symbol.async-iterator';
import { join } from 'path';
import { existsSync, lstatSync } from 'fs';
import sourceMap from '@zeit/source-map-support';
import { mkdirp } from 'fs-extra';
import chalk from 'chalk';
import epipebomb from 'epipebomb';
import checkForUpdate from 'update-check';
import ms from 'ms';
import updateNotifier from 'update-notifier';
import { URL } from 'url';
import * as Sentry from '@sentry/node';
import { NowBuildError } from '@vercel/build-utils';
@@ -52,6 +50,14 @@ import getUpdateCommand from './util/get-update-command';
import { metrics, shouldCollectMetrics } from './util/metrics.ts';
import { getCommandName, getTitleName } from './util/pkg-name.ts';
const isCanary = pkg.version.includes('canary');
// Checks for available update and returns an instance
const notifier = updateNotifier({
pkg,
distTag: isCanary ? 'canary' : 'latest',
});
const VERCEL_DIR = getGlobalPathConfig();
const VERCEL_CONFIG_PATH = configFiles.getConfigFilePath();
const VERCEL_AUTH_CONFIG_PATH = configFiles.getAuthConfigFilePath();
@@ -66,7 +72,7 @@ sourceMap.install();
Sentry.init({
dsn: SENTRY_DSN,
release: `vercel-cli@${pkg.version}`,
environment: pkg.version.includes('canary') ? 'canary' : 'stable',
environment: isCanary ? 'canary' : 'stable',
});
let debug = () => {};
@@ -128,38 +134,20 @@ const main = async argv_ => {
// (as in: `vercel ls`)
const targetOrSubcommand = argv._[2];
let update = null;
try {
if (targetOrSubcommand !== 'update') {
update = await checkForUpdate(pkg, {
interval: ms('1d'),
distTag: pkg.version.includes('canary') ? 'canary' : 'latest',
});
}
} catch (err) {
console.error(
error(`Checking for updates failed${isDebugging ? ':' : ''}`)
);
if (isDebugging) {
console.error(err);
}
}
if (update && isTTY) {
if (notifier.update && notifier.update.latest !== pkg.version && isTTY) {
const { latest } = notifier.update;
console.log(
info(
`${chalk.bgRed('UPDATE AVAILABLE')} ` +
`Run ${cmd(
await getUpdateCommand()
)} to install ${getTitleName()} CLI ${update.latest}`
)} to install ${getTitleName()} CLI ${latest}`
)
);
console.log(
info(
`Changelog: https://github.com/vercel/vercel/releases/tag/vercel@${update.latest}`
`Changelog: https://github.com/vercel/vercel/releases/tag/vercel@${latest}`
)
);
}
@@ -169,7 +157,7 @@ const main = async argv_ => {
`${getTitleName()} CLI ${pkg.version}${
targetOrSubcommand === 'dev' ? ' dev (beta)' : ''
}${
pkg.version.includes('canary') || targetOrSubcommand === 'dev'
isCanary || targetOrSubcommand === 'dev'
? ' — https://vercel.com/feedback'
: ''
}`
@@ -191,9 +179,7 @@ const main = async argv_ => {
} catch (err) {
console.error(
error(
`An unexpected error occurred while trying to find the global directory: ${
err.message
}`
`An unexpected error occurred while trying to find the global directory: ${err.message}`
)
);
@@ -653,6 +639,11 @@ const main = async argv_ => {
return 1;
}
if (err.code === 'NOT_AUTHORIZED' || err.code === 'TEAM_DELETED') {
output.prettyError(err);
return 1;
}
if (err instanceof APIError && 400 <= err.status && err.status <= 499) {
err.message = err.serverMessage;
output.prettyError(err);

View File

@@ -30,7 +30,6 @@ export type User = {
bio?: string;
date: number;
email: string;
platformVersion: number;
username: string;
website?: string;
billingChecked: boolean;
@@ -56,7 +55,6 @@ export type Team = {
creatorId: string;
membership: { uid: string; role: 'MEMBER' | 'OWNER'; created: number };
name: string;
platformVersion: number;
slug: string;
};
@@ -83,6 +81,16 @@ export type Domain = {
};
};
export type DomainConfig = {
configuredBy: null | 'CNAME' | 'A' | 'http';
misconfigured: boolean;
serviceType: 'zeit.world' | 'external' | 'na';
nameservers: string[];
cnames: string[] & { traceString?: string };
aValues: string[] & { traceString?: string };
dnssecEnabled?: boolean;
};
export type Cert = {
uid: string;
autoRenew: boolean;
@@ -92,59 +100,23 @@ export type Cert = {
expiration: string;
};
export type DeploymentScale = {
[dc: string]: {
min: number;
max: number;
};
};
export type NpmDeployment = {
export type Deployment = {
uid: string;
url: string;
name: string;
type: 'NPM';
state: 'INITIALIZING' | 'FROZEN' | 'READY' | 'ERROR';
type: 'LAMBDAS';
state:
| 'BUILDING'
| 'ERROR'
| 'INITIALIZING'
| 'QUEUED'
| 'READY'
| 'CANCELED';
version?: number;
created: number;
creator: { uid: string };
sessionAffinity: string;
scale: DeploymentScale;
};
export type StaticDeployment = {
uid: string;
url: string;
name: string;
type: 'STATIC';
state: 'INITIALIZING' | 'FROZEN' | 'READY' | 'ERROR';
version?: number;
created: number;
creator: { uid: string };
sessionAffinity: string;
};
export type DockerDeployment = {
uid: string;
url: string;
name: string;
type: 'DOCKER';
state: 'INITIALIZING' | 'FROZEN' | 'READY' | 'ERROR';
version?: number;
created: number;
creator: { uid: string };
sessionAffinity: string;
scale: DeploymentScale;
limits?: {
maxConcurrentReqs: number;
timeout: number;
duration: number;
};
slot?: string;
};
export type Deployment = NpmDeployment | StaticDeployment | DockerDeployment;
type PathAliasRule = {
pathname: string;
method: Array<'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE'>;
@@ -217,6 +189,16 @@ export type DNSRecordData =
| SRVRecordData
| MXRecordData;
export interface ProjectAliasTarget {
createdAt?: number;
domain: string;
redirect?: string | null;
target: 'PRODUCTION' | 'STAGING';
configuredBy?: null | 'CNAME' | 'A';
configuredChangedAt?: null | number;
configuredChangeAttempts?: [number, number];
}
export interface Secret {
uid: string;
name: string;
@@ -258,6 +240,10 @@ export interface Project extends ProjectSettings {
accountId: string;
updatedAt: number;
createdAt: number;
alias?: ProjectAliasTarget[];
devCommand?: string | null;
framework?: string | null;
rootDirectory?: string | null;
latestDeployments?: Partial<Deployment>[];
}
@@ -277,3 +263,8 @@ export interface PaginationOptions {
count: number;
next?: number;
}
export type ProjectLinkResult =
| { status: 'linked'; org: Org; project: Project }
| { status: 'not_linked'; org: null; project: null }
| { status: 'error'; exitCode: number };

View File

@@ -1,89 +1,20 @@
import { Deployment } from '../../types';
import { Output } from '../output';
import * as ERRORS from '../errors-ts';
import Client from '../client';
import createAlias from './create-alias';
import deploymentShouldCopyScale from './deployment-should-copy-scale';
import deploymentShouldDownscale from './deployment-should-downscale';
import findAliasByAliasOrId from './find-alias-by-alias-or-id';
import getDeploymentDownscalePresets from './get-deployment-downscale-presets';
import getDeploymentFromAlias from './get-deployment-from-alias';
import isDomainExternal from '../domains/is-domain-external';
import setDeploymentScale from '../scale/set-deployment-scale';
import setupDomain from '../domains/setup-domain';
import stamp from '../output/stamp';
import waitForScale from '../scale/wait-verify-deployment-scale';
export default async function assignAlias(
output: Output,
client: Client,
deployment: Deployment,
alias: string,
contextName: string,
noVerify: boolean
contextName: string
) {
const prevAlias = await findAliasByAliasOrId(output, client, alias);
let externalDomain = false;
// If there was a previous deployment, we should fetch it to scale and downscale later
let prevDeployment = await getDeploymentFromAlias(
client,
contextName,
prevAlias,
deployment
);
// If there is an alias laying around that points to a deleted
// deployment, we need to account for it here.
if (prevDeployment instanceof ERRORS.DeploymentNotFound) {
prevDeployment = null;
}
if (prevDeployment instanceof Error) {
return prevDeployment;
}
// If there was a prev deployment that wasn't static we have to check if we should scale
if (
prevDeployment !== null &&
prevDeployment.type !== 'STATIC' &&
deployment.type !== 'STATIC'
) {
if (deploymentShouldCopyScale(prevDeployment, deployment)) {
const scaleStamp = stamp();
const result = await setDeploymentScale(
output,
client,
deployment.uid,
prevDeployment.scale,
deployment.url
);
if (result instanceof Error) {
return result;
}
output.log(
`Scale rules copied from previous alias ${
prevDeployment.url
} ${scaleStamp()}`
);
if (!noVerify) {
const result = await waitForScale(
output,
client,
deployment.uid,
prevDeployment.scale
);
if (result instanceof ERRORS.VerifyScaleTimeout) {
return result;
}
}
} else {
output.debug(`Both deployments have the same scaling rules.`);
}
}
// Check if the alias is a custom domain and if case we have a positive
// Check if the alias is a custom domain, because then
// we have to configure the DNS records and certificate
if (
alias.indexOf('.') !== -1 &&
@@ -109,23 +40,6 @@ export default async function assignAlias(
alias,
externalDomain
);
if (record instanceof Error) {
return record;
}
// Downscale if the previous deployment is not static and doesn't have the minimal presets
if (prevDeployment !== null && prevDeployment.type !== 'STATIC') {
if (await deploymentShouldDownscale(output, client, prevDeployment)) {
await setDeploymentScale(
output,
client,
prevDeployment.uid,
getDeploymentDownscalePresets(prevDeployment),
prevDeployment.url
);
output.log(`Previous deployment ${prevDeployment.url} downscaled`);
}
}
return record;
}

View File

@@ -1,11 +0,0 @@
import { Deployment } from '../../types';
import Client from '../client';
import getAliases from './get-aliases';
export default async function deploymentIsAliased(
client: Client,
deployment: Deployment
) {
const { aliases } = await getAliases(client);
return aliases.some(alias => alias.deploymentId === deployment.uid);
}

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