Compare commits

..

47 Commits

Author SHA1 Message Date
Steven
0d4be3f5f2 Publish Canary
- @vercel/build-utils@2.5.2-canary.2
 - vercel@20.1.1-canary.3
 - @vercel/client@9.0.2-canary.2
 - @vercel/next@2.6.25-canary.2
 - @vercel/node@1.8.2-canary.2
2020-09-05 15:16:56 -04:00
Steven
a971c2aa3a [tests] Delay deprecation message (#5158)
This PR is a follow up to #5157 which was unable to deprecate a package immediately after publishing and failed with E405. So this PR adds a delay between publish and deprecate. It also uses the `-f` flag suggested in the error message. https://github.com/vercel/vercel/runs/1076007594#step:6:649
2020-09-05 15:13:48 -04:00
Steven
66414bd65a Publish Canary
- @vercel/build-utils@2.5.2-canary.1
 - vercel@20.1.1-canary.2
 - @vercel/client@9.0.2-canary.1
 - @vercel/next@2.6.25-canary.1
 - @vercel/node@1.8.2-canary.1
2020-09-05 13:54:34 -04:00
Steven
3b021993b5 [tests] Fix deprecation message (#5157)
This PR is a follow up to #5119 which had the names backwards.

I fixed the variable names to avoid the confusion since "new" and "old" were ambiguous.

I also removed the incorrect deprecation message from packages published during commit c9597dc199.
2020-09-05 13:49:20 -04:00
JJ Kasper
c9597dc199 Publish Canary
- @vercel/build-utils@2.5.2-canary.0
 - vercel@20.1.1-canary.1
 - @vercel/client@9.0.2-canary.0
 - @vercel/next@2.6.25-canary.0
 - @vercel/node@1.8.2-canary.0
2020-09-05 00:16:18 -05:00
Steven
1b5c94b392 [node][next] Fix nft tracing for monorepos (#5143)
Co-authored-by: Joe Haddad <joe.haddad@zeit.co>
Co-authored-by: JJ Kasper <jj@jjsweb.site>
2020-09-05 01:13:54 -04:00
dav-is
bd1393a9d6 Publish Canary
- vercel@20.1.1-canary.0
2020-09-04 18:40:25 -04:00
dependabot[bot]
47ddd36fec [cli] Update http-proxy to v1.18.1 (#5152)
Bumps [http-proxy](https://github.com/http-party/node-http-proxy) from 1.17.0 to 1.18.1.
- [Release notes](https://github.com/http-party/node-http-proxy/releases)
- [Changelog](https://github.com/http-party/node-http-proxy/blob/master/CHANGELOG.md)
- [Commits](https://github.com/http-party/node-http-proxy/compare/1.17.0...1.18.1)

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2020-09-04 14:33:56 -07:00
dependabot[bot]
bd047cdc6a Update tree-kill to v1.2.2 (#5151)
Bumps [tree-kill](https://github.com/pkrumins/node-tree-kill) from 1.2.1 to 1.2.2.
- [Release notes](https://github.com/pkrumins/node-tree-kill/releases)
- [Commits](https://github.com/pkrumins/node-tree-kill/compare/v1.2.1...v1.2.2)

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2020-09-04 14:33:32 -07:00
Connor Davis
477f9e8753 [cli] Fix color and indention in learn more link (#5148)
CH-568

Before:
![image](https://user-images.githubusercontent.com/11590024/92258007-2883e080-eea4-11ea-8c14-af0f3c9e27a6.png)

After:
![Screenshot-2020-09-04-11:44](https://user-images.githubusercontent.com/11590024/92258023-3174b200-eea4-11ea-9170-0acd4d6f883b.png)
2020-09-04 21:25:52 +00:00
Mike Hartington
183ea42f94 [examples] chore(ionic-angular): remove Google Maps API key (#5130) 2020-08-31 11:58:52 -07:00
Steven
57a17eb416 [tests] Deprecate "now" npm package in favor of "vercel" (#5119)
We deprecated all the `now` scoped packages in favor of the `vercel` equivalents, however the deprecation message disappears after each publish, so we must to run `npm deprecate` after `npm publish` for legacy packages.
2020-08-28 15:39:06 -04:00
Steven
be24510f16 Publish Stable
- @vercel/frameworks@0.1.1
 - @vercel/build-utils@2.5.1
 - vercel@20.1.0
 - @vercel/client@9.0.1
 - @vercel/go@1.1.6
 - @vercel/next@2.6.24
 - @vercel/node@1.8.1
 - @vercel/python@1.2.3
 - @vercel/ruby@1.2.4
 - @vercel/static-build@0.17.8
 - @vercel/redwood@0.1.1
2020-08-27 20:01:15 -04:00
Steven
05c534d855 Publish Canary
- @vercel/frameworks@0.1.1-canary.0
 - @vercel/build-utils@2.5.1-canary.1
 - vercel@20.0.1-canary.2
 - @vercel/client@9.0.1-canary.1
 - @vercel/go@1.1.6-canary.0
 - @vercel/node@1.8.1-canary.0
 - @vercel/python@1.2.3-canary.0
 - @vercel/ruby@1.2.4-canary.0
 - @vercel/static-build@0.17.8-canary.0
 - @vercel/redwood@0.1.1-canary.0
2020-08-27 19:39:21 -04:00
Shu Ding
910169dba7 [cli] Add support for the sourceFilesOutsideRootDirectory option (#5113)
If the value is set to `true` in the project settings, we don't cd into `rootDirectory`. 
We default it to `true` for new projects.
2020-08-27 23:37:50 +00:00
Steven
0fc74feddc [all] Bump ncc to 0.24.0 (#5103)
- Bump ncc to [0.24.0](https://github.com/vercel/ncc/releases/tag/0.24.0)
- Install ncc in each package that depends on it
2020-08-26 11:03:34 -04:00
Naoyuki Kanezawa
a9ea294514 fix framework names (#5104) 2020-08-26 22:14:39 +09:00
Steven
42e0450d87 Publish Canary
- vercel@20.0.1-canary.1
 - @vercel/next@2.6.24-canary.0
2020-08-25 22:13:26 -04:00
Guy Bedford
08c0feb58a [cli] Improve startup perf - disable source maps and minification (#5100)
Currently on my machine, vercel --version takes 850ms to run. With this change, that execution time is reduced to ~550ms.

The reason is that source maps seem to significantly slow down Node.js execution, compounded by the size of the source map, even if it is not used.

I believe the benefits of this perf improvement outweigh the direct source maps and minification benefits.

If it is felt that direct per-file source maps are necessary for internal errors, then reducing the source map size should be investigated further to improve the runtime performance here.
2020-08-25 22:12:25 -04:00
Mark Glagola
60aaf76ed2 [cli] Fix vc domains ls pagination (#5099)
Fixes pagination for vercel cli `domains ls --next <timestamp>`
2020-08-25 19:39:34 -04:00
Matthew Sweeney
bd706beba5 [examples] Update README Files (#5092)
This PR updates the `README.md` files with the following changes:

- Removal of CLI deployment.
- Amends Blitz.js to be in line with other `README.md` formats.
2020-08-25 14:51:46 -04:00
JJ Kasper
b85a0a349a [next] Add test case for root optional revalidate params (#5072)
This adds test cases from https://github.com/vercel/next.js/pull/16451 which are currently failing and will pass after a new canary of Next.js has been published
2020-08-24 16:32:30 +00:00
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
126 changed files with 15659 additions and 1189 deletions

View File

@@ -68,12 +68,12 @@ In such cases you can visit the URL of the failed deployment and append `/_logs`
The logs of this deployment will contain the actual error which may help you to understand what went wrong.
### @zeit/node-file-trace
### @vercel/nft
Some of the Builders use `@zeit/node-file-trace` to tree-shake files before deployment. If you suspect an error with this tree-shaking mechanism, you can create the following script in your project:
Some of the Builders use `@vercel/nft` to tree-shake files before deployment. If you suspect an error with this tree-shaking mechanism, you can create the following script in your project:
```js
const trace = require('@zeit/node-file-trace');
const trace = require('@vercel/nft');
trace(['path/to/entrypoint.js'], {
ts: true,
mixedModules: true,
@@ -82,7 +82,7 @@ trace(['path/to/entrypoint.js'], {
.then(e => console.error(e));
```
When you run this script, you'll see all imported files. If anything file is missing, the bug is in [@zeit/node-file-trace](https://github.com/vercel/node-file-trace) and not the Builder.
When you run this script, you'll see all imported files. If anything file is missing, the bug is in [@vercel/nft](https://github.com/vercel/nft) and not the Builder.
## Deploy a Builder with existing project

View File

@@ -15,7 +15,7 @@ jobs:
- uses: actions/checkout@v1
- uses: actions/setup-node@v1
with:
node-version: 10
node-version: 12
- name: Install
run: yarn install --check-files --frozen-lockfile
- name: Build

View File

@@ -17,11 +17,3 @@ To get started deploying AMP with Vercel, you can use the [Vercel CLI](https://v
```shell
$ vercel init amp
```
### Deploying From Your Terminal
You can deploy your new AMP project with a single command from your terminal using Vercel CLI:
```shell
$ vercel
```

View File

@@ -19,11 +19,3 @@ To get started with Angular, you can use the [Angular CLI](https://cli.angular.i
```shell
$ ng new
```
### Deploying From Your Terminal
You can deploy your new Angular project with a single command from your terminal using [Vercel CLI](https://vercel.com/download):
```shell
$ vercel
```

View File

@@ -1,21 +1,19 @@
![Blitz Logo](https://github.com/vercel/vercel/blob/master/packages/frameworks/logos/blitz.svg)
This is a [Blitz.js](https://blitzjs.com/) project bootstrapped with `npx blitz new`.
# Blitz.js Example
## Getting Started
This directory is a brief example of a [Blitz.js](https://blitzjs.com/) project that can be deployed with Vercel and zero configuration.
First, run the development server:
## Deploy Your Own
```bash
npx blitz start
Deploy your own Blitz.js project with Vercel by viewing the [documentation on deploying to Vercel](https://blitzjs.com/docs/deploy-vercel)
[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/import/project?template=https://github.com/vercel/vercel/tree/master/examples/blitzjs)
### How We Created This Example
To get started with Blitz.js, you can use [npx](https://www.npmjs.com/package/npx) to initialize the project:
```shell
$ npx blitz new
```
Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
## Learn More
To learn more about Blitz.js, view [Blitzjs.com](https://blitzjs.com)
## Deploy on Vercel
View the [documentation on deploying to Vercel](https://blitzjs.com/docs/deploy-vercel)

View File

@@ -17,11 +17,3 @@ To get started deploying Brunch with Vercel, you can use the [Brunch CLI](https:
```shell
$ brunch new project-name -s es6
```
### Deploying From Your Terminal
You can deploy your new Brunch project with a single command from your terminal using [Vercel CLI](https://vercel.com/download):
```shell
$ vercel
```

View File

@@ -19,11 +19,3 @@ To get started with React, along with [Serverless Functions](https://vercel.com/
```shell
$ npx create-react-app my-app
```
### Deploying From Your Terminal
You can deploy your new React 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

@@ -17,11 +17,3 @@ To get started deploying a Custom Built project with Vercel, you can use the [Ve
```shell
$ vercel init custom-build
```
### Deploying From Your Terminal
You can deploy your new Custom Built project with a single command from your terminal using [Vercel CLI](https://vercel.com/download):
```shell
$ vercel
```

View File

@@ -15,11 +15,3 @@ To get started with Docusaurus on Vercel, you can use the [Docusaurus CLI](https
```shell
$ npx @docusaurus/init@next init my-website classic
```
### Deploying From Your Terminal
You can deploy your new Docusaurus project with a single command from your terminal using [Vercel CLI](https://vercel.com/download):
```shell
$ vercel
```

View File

@@ -19,11 +19,3 @@ To get started with Docusaurus for deployment with Vercel, you can use the [Docu
```shell
$ docusaurus-init
```
### Deploying From Your Terminal
You can deploy your new Docusaurus project with a single command from your terminal using [Vercel CLI](https://vercel.com/download):
```shell
$ vercel
```

View File

@@ -15,11 +15,3 @@ To get started with Dojo on Vercel, you can use the [Dojo CLI](https://github.co
```shell
$ vercel init dojo
```
### Deploying From Your Terminal
You can deploy your new Dojo project with a single command from your terminal using [Vercel CLI](https://vercel.com/download):
```shell
$ vercel
```

View File

@@ -19,11 +19,3 @@ To get started with Eleventy for deployment with Vercel, you can use [npx](https
```shell
$ npx degit 11ty/eleventy-base-blog my-11ty-project
```
### Deploying From Your Terminal
You can deploy your new Eleventy project with a single command from your terminal using [Vercel CLI](https://vercel.com/download):
```shell
$ vercel
```

View File

@@ -19,11 +19,3 @@ To get started with Ember for deployment with Vercel, you can use the [Ember CLI
```shell
$ npx ember-cli new ember-project
```
### Deploying From Your Terminal
You can deploy your new Ember project with a single command from your terminal using [Vercel CLI](https://vercel.com/download):
```shell
$ vercel
```

View File

@@ -19,11 +19,3 @@ To get started with Gatsby on Vercel, you can use the [Gatsby CLI](https://www.g
```shell
$ gatsby new gatsby-site
```
### Deploying From Your Terminal
You can deploy your new Gatsby 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

@@ -19,11 +19,3 @@ To get started with Gridsome for deployment with Vercel, you can use the [Gridso
```shell
$ gridsome create my-website
```
### Deploying From Your Terminal
You can deploy your new Gridsome project with a single command from your terminal using [Vercel CLI](https://vercel.com/download):
```shell
$ vercel
```

View File

@@ -19,11 +19,3 @@ To get started with Hexo for deployment with Vercel, you can use the [Hexo CLI](
```shell
$ hexo init project-name
```
### Deploying From Your Terminal
You can deploy your new Hexo project with a single command from your terminal using [Vercel CLI](https://vercel.com/download):
```shell
$ vercel
```

View File

@@ -17,11 +17,3 @@ To get started with Hugo for deployment with Vercel, you can use the [Hugo CLI](
```shell
$ hugo new site project-name
```
### Deploying From Your Terminal
You can deploy your new Hugo project with a single command from your terminal using [Vercel CLI](https://vercel.com/download):
```shell
$ vercel
```

View File

@@ -17,11 +17,3 @@ To get started with Ionic Angular deployed with Vercel, you can use the [Ionic C
```shell
$ npx @ionic/cli start [project-name] conference --type angular && cd [project-name]
```
### Deploying From Your Terminal
You can deploy your new Ionic Angular project with a single command from your terminal using [Vercel CLI](https://vercel.com/download):
```shell
$ vercel
```

View File

@@ -27,7 +27,7 @@ export class MapPage implements AfterViewInit {
}
const googleMaps = await getGoogleMaps(
'AIzaSyB8pf6ZdFQj5qw7rc_HSGrhUwQKfIe9ICw'
'YOUR_API_KEY_HERE'
);
let map;

View File

@@ -17,11 +17,3 @@ To get started with Ionic React deployed with Vercel, you can use the [Ionic CLI
```shell
$ npx ionic start [project-name] conference --type react && cd [project-name]
```
### Deploying From Your Terminal
You can deploy your new Ionic React project with a single command from your terminal using [Vercel CLI](https://vercel.com/download):
```shell
$ vercel
```

View File

@@ -17,11 +17,3 @@ To get started with Jekyll for deployment with Vercel, you can use the [Jekyll C
```shell
$ jekyll new my-blog
```
### Deploying From Your Terminal
You can deploy your new Jekyll project with a single command from your terminal using [Vercel CLI](https://vercel.com/download):
```shell
$ vercel
```

View File

@@ -17,11 +17,3 @@ To get started with Middleman for deployment with Vercel, you can use the [Middl
```shell
$ middleman init project-name
```
### Deploying From Your Terminal
You can deploy your new Middleman project with a single command from your terminal using [Vercel CLI](https://vercel.com/download):
```shell
$ vercel
```

View File

@@ -19,11 +19,3 @@ $ npx create-nuxt-app my-app
```
> The only change made is to amend the output directory in `nuxt.config.js` to `"/public"`.
### Deploying From Your Terminal
You can deploy your new Nuxt.js project with a single command from your terminal using [Vercel CLI](https://vercel.com/download):
```shell
$ vercel
```

View File

@@ -19,11 +19,3 @@ To get started with Polymer deployed with Vercel, you can use the [Polymer CLI](
```shell
$ polymer init
```
### Deploying From Your Terminal
You can deploy your new Polymer project with a single command from your terminal using [Vercel CLI](https://vercel.com/download):
```shell
$ vercel
```

View File

@@ -19,11 +19,3 @@ To get started with Preact for deployment with Vercel, you can use the [Preact C
```shell
$ preact create default my-project
```
### Deploying From Your Terminal
You can deploy your new Preact project with a single command from your terminal using [Vercel CLI](https://vercel.com/download):
```shell
$ vercel
```

View File

@@ -19,11 +19,3 @@ To get started with RedwoodJS on Vercel, you can [use Yarn to initialize](https:
```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

@@ -19,11 +19,3 @@ To get started with Saber on Vercel, you can use [`npm init`](https://docs.npmjs
```shell
$ npm init site my-saber-site
```
### Deploying From Your Terminal
You can deploy your new Saber project with a single command from your terminal using [Vercel CLI](https://vercel.com/download):
```shell
$ vercel
```

View File

@@ -19,11 +19,3 @@ $ npx degit "sveltejs/sapper-template#webpack" my-sapper-app
```
> The only change made is to change the build script in `package.json` to be `"sapper export"`.
### Deploying From Your Terminal
You can deploy your new Sapper project with a single command from your terminal using [Vercel CLI](https://vercel.com/download):
```shell
$ vercel
```

View File

@@ -19,11 +19,3 @@ To get started deploying Scully with Vercel, you can use the [Vercel CLI](https:
```shell
$ vercel init scully
```
### Deploying From Your Terminal
You can deploy your new Scully project with a single command from your terminal using [Vercel CLI](https://vercel.com/download):
```shell
$ vercel
```

View File

@@ -17,11 +17,3 @@ To get started with Stencil deployed with Vercel, you can use the [Stencil proje
```shell
$ npm init stencil
```
### Deploying From Your Terminal
You can deploy your new Stencil project with a single command from your terminal using [Vercel CLI](https://vercel.com/download):
```shell
$ vercel
```

View File

@@ -17,11 +17,3 @@ To get started with Svelte, along with [Serverless Functions](https://vercel.com
```shell
$ npx degit sveltejs/template my-svelte-project
```
### Deploying From Your Terminal
You can deploy your new Svelte 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

@@ -19,11 +19,3 @@ To get started with UmiJS deployed with Vercel, you can use the [Umi CLI](https:
```shell
$ yarn create umi app-name
```
### Deploying From Your Terminal
You can deploy your new UmiJS project with a single command from your terminal using [Vercel CLI](https://vercel.com/download):
```shell
$ vercel
```

View File

@@ -17,7 +17,7 @@
"devDependencies": {
"@typescript-eslint/eslint-plugin": "2.0.0",
"@typescript-eslint/parser": "2.0.0",
"@zeit/ncc": "0.20.4",
"@vercel/ncc": "0.24.0",
"async-retry": "1.2.3",
"buffer-replace": "1.0.0",
"cheerio": "1.0.0-rc.3",

View File

@@ -59,7 +59,7 @@
}
},
{
"name": "Gatsby",
"name": "Gatsby.js",
"slug": "gatsby",
"demo": "https://gatsby.now-examples.now.sh",
"logo": "https://raw.githubusercontent.com/vercel/vercel/master/packages/frameworks/logos/gatsby.svg",
@@ -261,7 +261,7 @@
}
},
{
"name": "Ember",
"name": "Ember.js",
"slug": "ember",
"demo": "https://ember.now-examples.now.sh",
"logo": "https://raw.githubusercontent.com/vercel/vercel/master/packages/frameworks/logos/ember.svg",

View File

@@ -1,6 +1,6 @@
{
"name": "@vercel/frameworks",
"version": "0.0.18-canary.5",
"version": "0.1.1",
"main": "frameworks.json",
"license": "UNLICENSED",
"scripts": {

View File

@@ -1,6 +1,6 @@
{
"name": "@vercel/build-utils",
"version": "2.4.3-canary.4",
"version": "2.5.2-canary.2",
"license": "MIT",
"main": "./dist/index.js",
"types": "./dist/index.d.js",
@@ -29,7 +29,8 @@
"@types/node-fetch": "^2.1.6",
"@types/semver": "6.0.0",
"@types/yazl": "^2.4.1",
"@vercel/frameworks": "0.0.18-canary.5",
"@vercel/frameworks": "0.1.1",
"@vercel/ncc": "0.24.0",
"aggregate-error": "3.0.1",
"async-retry": "1.2.3",
"async-sema": "2.1.4",

View File

@@ -26,6 +26,7 @@ interface Options {
devCommand?: string | null;
buildCommand?: string | null;
outputDirectory?: string | null;
createdAt?: number;
};
cleanUrls?: boolean;
trailingSlash?: boolean;
@@ -434,6 +435,7 @@ function detectFrontBuilder(
): Builder {
const { tag, projectSettings = {} } = options;
const withTag = tag ? `@${tag}` : '';
const { createdAt = 0 } = projectSettings;
let { framework } = projectSettings;
const config: Config = {
@@ -456,7 +458,7 @@ function detectFrontBuilder(
config.outputDirectory = projectSettings.outputDirectory;
}
if (pkg && framework !== null) {
if (pkg && (framework !== null || createdAt < Date.parse('2020-03-01'))) {
const deps: PackageJson['dependencies'] = {
...pkg.dependencies,
...pkg.devDependencies,
@@ -929,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) {
@@ -971,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);

View File

@@ -107,6 +107,13 @@ export interface BuildOptions {
*/
workPath: string;
/**
* The "Root Directory" is assigned to the `workPath` so the `repoRootPath`
* is the Git Repository Root. This is only relevant for Monorepos.
* See https://vercel.com/blog/monorepos
*/
repoRootPath?: string;
/**
* An arbitrary object passed by the user in the build definition defined
* in `vercel.json`.

View File

@@ -1080,7 +1080,7 @@ describe('Test `detectBuilders` with `featHandleMiss=true`', () => {
expect(errorRoutes).toStrictEqual([]);
});
it('Using "Other" framework with Storybook should NOT autodetect Next.js', async () => {
it('Using "Other" framework with Storybook should NOT autodetect Next.js for new projects', async () => {
const pkg = {
scripts: {
dev: 'next dev',
@@ -1104,6 +1104,7 @@ describe('Test `detectBuilders` with `featHandleMiss=true`', () => {
const projectSettings = {
framework: null, // Selected "Other" framework
buildCommand: 'yarn build-storybook',
createdAt: Date.parse('2020-07-01'),
};
const { builders, errorRoutes } = await detectBuilders(files, pkg, {
@@ -1125,6 +1126,41 @@ describe('Test `detectBuilders` with `featHandleMiss=true`', () => {
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'];
@@ -1905,7 +1941,7 @@ describe('Test `detectBuilders` with `featHandleMiss=true`', () => {
]);
});
it('RedwoodJS should allow usage of non-js API', async () => {
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',
@@ -1953,13 +1989,7 @@ describe('Test `detectBuilders` with `featHandleMiss=true`', () => {
check: true,
},
]);
expect(rewriteRoutes).toStrictEqual([
{
status: 404,
src: '^/api(/.*)?$',
continue: true,
},
]);
expect(rewriteRoutes).toStrictEqual([]);
expect(errorRoutes).toStrictEqual([
{
status: 404,

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": "20.0.0-canary.21",
"version": "20.1.1-canary.3",
"preferGlobal": true,
"license": "Apache-2.0",
"description": "The command-line interface for Vercel",
@@ -61,14 +61,11 @@
"node": ">= 10"
},
"dependencies": {
"@vercel/build-utils": "2.4.3-canary.4",
"@vercel/go": "1.1.5-canary.0",
"@vercel/next": "2.6.20",
"@vercel/node": "1.7.5-canary.1",
"@vercel/python": "1.2.2",
"@vercel/redwood": "0.0.2-canary.7",
"@vercel/ruby": "1.2.3",
"@vercel/static-build": "0.17.7-canary.3",
"@vercel/build-utils": "2.5.2-canary.2",
"@vercel/go": "1.1.6",
"@vercel/node": "1.8.2-canary.2",
"@vercel/python": "1.2.3",
"@vercel/ruby": "1.2.4",
"update-notifier": "4.1.0"
},
"devDependencies": {
@@ -103,9 +100,9 @@
"@types/universal-analytics": "0.4.2",
"@types/which": "1.3.2",
"@types/write-json-file": "2.2.1",
"@vercel/frameworks": "0.0.18-canary.5",
"@vercel/frameworks": "0.1.1",
"@vercel/ncc": "0.24.0",
"@zeit/fun": "0.11.2",
"@zeit/ncc": "0.18.5",
"@zeit/source-map-support": "0.6.2",
"ajv": "6.12.2",
"alpha-sort": "2.0.1",
@@ -124,13 +121,9 @@
"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 +134,7 @@
"fs-extra": "7.0.1",
"get-port": "5.1.1",
"glob": "7.1.2",
"http-proxy": "1.17.0",
"ignore": "4.0.6",
"http-proxy": "1.18.1",
"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,17 +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",
"tree-kill": "1.2.2",
"ts-node": "8.3.0",
"typescript": "3.9.3",
"universal-analytics": "0.4.20",
"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,18 +49,12 @@ async function main() {
// Do the initial `ncc` build
console.log();
const src = join(dirRoot, 'src');
const args = [
'@zeit/ncc',
'build',
'--source-map',
'--external',
'update-notifier',
];
if (!isDev) {
args.push('--minify');
const args = ['ncc', 'build', '--external', 'update-notifier'];
if (isDev) {
args.push('--source-map');
}
args.push(src);
await execa('npx', args, { stdio: 'inherit' });
await execa('yarn', args, { stdio: 'inherit' });
// `ncc` has some issues with `@zeit/fun`'s runtime files:
// - Executable bits on the `bootstrap` files appear to be lost:

View File

@@ -157,13 +157,22 @@ const printDeploymentStatus = async (
}
if (indications) {
const indent = process.stdout.isTTY ? ' ' : ''; // if using emojis
const newline = '\n';
for (let indication of indications) {
output.print(
prependEmoji(
`${chalk.dim(indication.payload)}`,
emoji(indication.type)
) + `\n`
);
const message =
prependEmoji(chalk.dim(indication.payload), emoji(indication.type)) +
newline;
let link = '';
if (indication.link)
link =
indent +
chalk.dim(
`${indication.action || 'Learn More'}: ${indication.link}`
) +
newline +
newline;
output.print(message + link);
}
}
};
@@ -272,6 +281,7 @@ export default async function main(
let { org, project, status } = link;
let newProjectName = null;
let rootDirectory = project ? project.rootDirectory : null;
let sourceFilesOutsideRootDirectory = true;
if (status === 'not_linked') {
const shouldStartSetup =
@@ -329,6 +339,7 @@ export default async function main(
} else {
project = projectOrNewProjectName;
rootDirectory = project.rootDirectory;
sourceFilesOutsideRootDirectory = project.sourceFilesOutsideRootDirectory;
// we can already link the project
await linkFolderToProject(
@@ -345,7 +356,12 @@ export default async function main(
}
}
const sourcePath = rootDirectory ? join(path, rootDirectory) : path;
// if we have `sourceFilesOutsideRootDirectory` set to `true`, we use the current path
// and upload the entire directory.
const sourcePath =
rootDirectory && !sourceFilesOutsideRootDirectory
? join(path, rootDirectory)
: path;
if (
rootDirectory &&
@@ -364,7 +380,7 @@ export default async function main(
// If Root Directory is used we'll try to read the config
// from there instead and use it if it exists.
if (rootDirectory) {
const rootDirectoryConfig = readLocalConfig(sourcePath);
const rootDirectoryConfig = readLocalConfig(join(path, rootDirectory));
if (rootDirectoryConfig) {
debug(`Read local config from root directory (${rootDirectory})`);
@@ -521,6 +537,11 @@ export default async function main(
skipAutoDetectionConfirmation: autoConfirm,
};
if (!localConfig.builds || localConfig.builds.length === 0) {
// Only add projectSettings for zero config deployments
createArgs.projectSettings = { sourceFilesOutsideRootDirectory };
}
deployment = await createDeploy(
output,
now,
@@ -542,6 +563,10 @@ export default async function main(
projectSettings.rootDirectory = rootDirectory;
}
if (typeof sourceFilesOutsideRootDirectory !== 'undefined') {
projectSettings.sourceFilesOutsideRootDirectory = sourceFilesOutsideRootDirectory;
}
const settings = await editProjectSettings(
output,
projectSettings,

View File

@@ -111,12 +111,11 @@ export default async function inspect(
` ${chalk.cyan('Created At')}\t\t\t${formatDate(domain.createdAt)}\n`
);
output.print(` ${chalk.cyan('Edge Network')}\t\tyes\n`);
if (renewalPrice && domain.boughtAt) {
output.print(
` ${chalk.cyan('Renewal Price')}\t\t$${renewalPrice} USD\n`
);
}
output.print(
` ${chalk.cyan('Renewal Price')}\t\t${
domain.boughtAt && renewalPrice ? `$${renewalPrice} USD` : chalk.gray('-')
}\n`
);
output.print('\n');

View File

@@ -66,7 +66,10 @@ export default async function ls(
const cancelWait = wait(`Fetching Domains under ${chalk.bold(contextName)}`);
const { domains, pagination } = await getDomains(client).finally(() => {
const { domains, pagination } = await getDomains(
client,
nextTimestamp
).finally(() => {
cancelWait();
});

View File

@@ -13,7 +13,6 @@ 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';
@@ -135,7 +134,7 @@ const main = async argv_ => {
// (as in: `vercel ls`)
const targetOrSubcommand = argv._[2];
if (notifier.update && isTTY) {
if (notifier.update && notifier.update.latest !== pkg.version && isTTY) {
const { latest } = notifier.update;
console.log(
info(

View File

@@ -1475,34 +1475,6 @@ export default class DevServer {
routeResult.status = prevStatus;
}
if (!match && errorRoutes.length > 0) {
// error phase
const routeResultForError = await devRouter(
getReqUrl(routeResult),
req.method,
errorRoutes,
this,
nowConfig,
routeResult.headers,
[],
'error'
);
const matchForError = await findBuildMatch(
this.buildMatches,
this.files,
routeResultForError.dest,
this,
nowConfig
);
if (matchForError) {
// error phase only applies if the file was found
routeResult = routeResultForError;
match = matchForError;
}
}
statusCode = routeResult.status;
if (match) {
@@ -1511,6 +1483,52 @@ export default class DevServer {
}
}
if (!match && routeResult && errorRoutes.length > 0) {
// error phase
const routeResultForError = await devRouter(
getReqUrl(routeResult),
req.method,
errorRoutes,
this,
nowConfig,
routeResult.headers,
[],
'error'
);
const { matched_route } = routeResultForError;
const matchForError = await findBuildMatch(
this.buildMatches,
this.files,
routeResultForError.dest,
this,
nowConfig
);
if (matchForError) {
debug(`Route match detected in error phase, breaking loop`);
routeResult = routeResultForError;
statusCode = routeResultForError.status;
match = matchForError;
} else if (matched_route && matched_route.src && !matched_route.dest) {
debug(
'Route without `dest` detected in error phase, attempting to exit early'
);
if (
await this.exitWithStatus(
matchForError,
routeResultForError,
'error',
req,
res,
nowRequestId
)
) {
return;
}
}
}
if (!routeResult) {
throw new Error('Expected Route Result but none was found.');
}

View File

@@ -1,13 +1,9 @@
import { resolve } from 'path';
import ignore from 'ignore';
import _glob, { IOptions as GlobOptions } from 'glob';
import fs from 'fs-extra';
import { getVercelIgnore } from '@vercel/client';
import IGNORED from './ignored';
import uniqueStrings from './unique-strings';
import getLocalConfigPath from './config/local-path';
import { Output } from './output/create-output';
import { NowConfig } from './dev/types';
type NullableString = string | null;
@@ -33,103 +29,6 @@ async function glob(pattern: string, options: GlobOptions): Promise<string[]> {
});
}
interface WalkOptions {
output: Output;
}
/**
* Will recursivly walk through a directory and return an array of the files found within.
* @param {string} dir the directory to walk
* @param {string} path the path to this directory
* @param {Array[string]} filelist a list of files so far identified
* @param {Object} options
* - `output` {Object} "output" helper object
* @returns {Array}
*/
async function walk(
dir: string,
path: string,
filelist: string[] = [],
opts: WalkOptions
) {
const { debug } = opts.output;
const dirc = await fs.readdir(asAbsolute(dir, path));
for (let file of dirc) {
file = asAbsolute(file, dir);
try {
const fileStat = await fs.stat(file);
filelist = fileStat.isDirectory()
? await walk(file, path, filelist, opts)
: filelist.concat(file);
} catch (e) {
debug(`Ignoring invalid file ${file}`);
}
}
return filelist;
}
interface FilesInWhitelistOptions {
output: Output;
}
/**
* Will return an array containing the expaneded list of all files included in the whitelist.
* @param {Array[string]} whitelist array of files and directories to include.
* @param {string} path the path of the deployment.
* @param {Object} options
* - `output` {Object} "output" helper object
* @returns {Array} the expanded list of whitelisted files.
*/
const getFilesInWhitelist = async function (
whitelist: string[],
path: string,
opts: FilesInWhitelistOptions
) {
const { debug } = opts.output;
const files: string[] = [];
await Promise.all(
whitelist.map(async (file: string) => {
file = asAbsolute(file, path);
try {
const fileStat = await fs.stat(file);
if (fileStat.isDirectory()) {
const dirFiles = await walk(file, path, [], opts);
files.push(...dirFiles);
} else {
files.push(file);
}
} catch (e) {
debug(`Ignoring invalid file ${file}`);
}
})
);
return files;
};
/**
* Remove leading `./` from the beginning of ignores
* because ignore doesn't like them :|
*/
const clearRelative = function (str: string) {
return str.replace(/(\n|^)\.\//g, '$1');
};
/**
* Returns the contents of a file if it exists.
*
* @return {String} results or `''`
*/
const maybeRead = async function <T>(path: string, default_: T) {
try {
return await fs.readFile(path, 'utf8');
} catch (err) {
return default_;
}
};
/**
* Transform relative paths into absolutes,
* and maintains absolutes as such.
@@ -215,100 +114,6 @@ export async function staticFiles(
return uniqueStrings(files);
}
interface NpmOptions {
hasNowJson: boolean;
output: Output;
}
/**
* Returns a list of files in the given
* directory that are subject to be
* synchronized for npm.
*
* @param {String} full path to directory
* @param {String} contents of `package.json` to avoid lookup
* @param {Object} options:
* - `limit` {Number|null} byte limit
* - `output` {Object} "output" helper object
* @return {Array} comprehensive list of paths to sync
*/
export async function npm(
path: string,
pkg: { files?: string[]; now?: { files?: string[] } } = {},
nowConfig: NowConfig = {},
{ hasNowJson = false, output }: NpmOptions
) {
const { debug, time } = output;
const whitelist = nowConfig.files || pkg.files || (pkg.now && pkg.now.files);
let files: string[] = [];
if (whitelist) {
files = await getFilesInWhitelist(whitelist, path, { output });
} else {
// The package.json `files` whitelist still
// honors ignores: https://docs.npmjs.com/files/package.json#files
const search_ = ['.'];
// Convert all filenames into absolute paths
const search = Array.prototype.concat.apply(
[],
await Promise.all(
search_.map(file =>
glob(file, { cwd: path, absolute: true, dot: true })
)
)
);
// Compile list of ignored patterns and files
const npmIgnore = await maybeRead(resolve(path, '.npmignore'), null);
const filter = ignore()
.add(
`${IGNORED}\n${clearRelative(
npmIgnore === null
? await maybeRead(resolve(path, '.gitignore'), '')
: npmIgnore
)}`
)
.createFilter();
const prefixLength = path.length + 1;
const accepts = (file: string) => {
const relativePath = file.substr(prefixLength);
if (relativePath === '') {
return true;
}
const accepted = filter(relativePath);
if (!accepted) {
debug(`Ignoring ${file}`);
}
return accepted;
};
// Locate files
files = await time(
`Locating files ${path}`,
explode(search, {
accepts,
output,
})
);
}
// Always include manifest as npm does not allow ignoring it
// source: https://docs.npmjs.com/files/package.json#files
files.push(asAbsolute('package.json', path));
if (hasNowJson) {
files.push(asAbsolute(getLocalConfigPath(path), path));
}
// Get files
return uniqueStrings(files);
}
interface ExplodeOptions {
accepts: (file: string) => boolean;
output: Output;

View File

@@ -1,26 +0,0 @@
// Base `.gitignore` to which we add entries
// supplied by the user
export default `.hg
.git
.gitmodules
.svn
.cache
.next
.now
.vercel
.npmignore
.dockerignore
.gitignore
.*.swp
.DS_Store
.wafpicke-*
.lock-wscript
.env.local
.env.*.local
.venv
npm-debug.log
config.gypi
node_modules
__pycache__
venv
CVS`;

View File

@@ -59,6 +59,7 @@ export default async function setupAndLink(
const isTTY = process.stdout.isTTY;
const quiet = !isTTY;
let rootDirectory: string | null = null;
let sourceFilesOutsideRootDirectory = true;
let newProjectName: string;
let org;
@@ -129,7 +130,13 @@ export default async function setupAndLink(
);
return { status: 'linked', org, project };
}
const sourcePath = rootDirectory ? join(path, rootDirectory) : path;
// if we have `sourceFilesOutsideRootDirectory` set to `true`, we use the current path
// and upload the entire directory.
const sourcePath =
rootDirectory && !sourceFilesOutsideRootDirectory
? join(path, rootDirectory)
: path;
if (
rootDirectory &&
@@ -174,6 +181,11 @@ export default async function setupAndLink(
skipAutoDetectionConfirmation: false,
};
if (!localConfig.builds || localConfig.builds.length === 0) {
// Only add projectSettings for zero config deployments
createArgs.projectSettings = { sourceFilesOutsideRootDirectory };
}
const deployment = await createDeploy(
output,
now,

View File

@@ -1,8 +1,7 @@
import chalk from 'chalk';
import printf from 'printf';
const printLine = (data, sizes) =>
data.reduce((line, col, i) => line + printf(`%-${sizes[i]}s`, col), '');
data.reduce((line, col, i) => line + col.padEnd(sizes[i]), '');
// Print a table
export default (fieldNames = [], data = [], margins = []) => {

View File

@@ -6,13 +6,13 @@ import { isIP } from 'net';
import { join, resolve, delimiter } from 'path';
import _execa from 'execa';
import fetch from 'node-fetch';
import sleep from 'then-sleep';
import retry from 'async-retry';
import { satisfies } from 'semver';
import { getDistTag } from '../../src/util/get-dist-tag';
import { version as cliVersion } from '../../package.json';
import { fetchTokenWithRetry } from '../../../../test/lib/deployment/now-deploy';
const sleep = ms => new Promise(resolve => setTimeout(resolve, ms));
const isCanary = () => getDistTag(cliVersion) === 'canary';
let port = 3000;

View File

@@ -241,6 +241,24 @@ module.exports = async function prepare(session) {
},
}),
},
'zero-config-next-js-functions-warning': {
'pages/index.js':
'export default () => <div><h1>Vercel CLI test</h1><p>Zero-config + Next.js</p></div>',
'vercel.json':
'{"version":2,"functions":{"pages/index.js":{"runtime": "@vercel/php@0.1.0"}}}',
'package.json': JSON.stringify({
scripts: {
dev: 'next',
start: 'next start',
build: 'next build',
},
dependencies: {
next: 'latest',
react: 'latest',
'react-dom': 'latest',
},
}),
},
'lambda-with-128-memory': {
'api/memory.js': `
module.exports = (req, res) => {

View File

@@ -1052,7 +1052,7 @@ test('domains inspect', async t => {
}
);
t.true(!stderr.includes(`Renewal Price`));
t.true(stderr.includes(`Renewal Price`));
t.is(exitCode, 0, formatOutput({ stdout, stderr }));
{
@@ -2219,6 +2219,33 @@ test('create zero-config deployment', async t => {
);
});
test('next unsupported functions config shows warning link', async t => {
const fixturePath = fixture('zero-config-next-js-functions-warning');
const output = await execute([
fixturePath,
'--force',
'--public',
'--confirm',
]);
console.log('isCanary', isCanary);
console.log(output.stderr);
console.log(output.stdout);
console.log(output.exitCode);
t.is(output.exitCode, 0, formatOutput(output));
t.regex(
output.stderr,
/Ignoring function property `runtime`\. When using Next\.js, only `memory` and `maxDuration` can be used\./gm,
formatOutput(output)
);
t.regex(
output.stderr,
/Learn More: https:\/\/vercel\.link\/functions-property-next/gm,
formatOutput(output)
);
});
test('vercel secret add', async t => {
context.secretName = `my-secret-${Date.now().toString(36)}`;
const value = 'https://my-secret-endpoint.com';

View File

@@ -1,6 +1,6 @@
{
"name": "@vercel/client",
"version": "8.2.2-canary.8",
"version": "9.0.2-canary.2",
"main": "dist/index.js",
"typings": "dist/index.d.ts",
"homepage": "https://vercel.com",
@@ -26,7 +26,6 @@
"@types/node": "12.0.4",
"@types/node-fetch": "2.5.4",
"@types/recursive-readdir": "2.2.0",
"@zeit/ncc": "0.18.5",
"typescript": "3.9.3"
},
"jest": {
@@ -38,7 +37,7 @@
]
},
"dependencies": {
"@vercel/build-utils": "2.4.3-canary.4",
"@vercel/build-utils": "2.5.2-canary.2",
"@zeit/fetch": "5.2.0",
"async-retry": "1.2.3",
"async-sema": "3.0.0",

View File

@@ -19,7 +19,12 @@ async function* postDeployment(
files: Map<string, DeploymentFile>,
clientOptions: NowClientOptions,
deploymentOptions: DeploymentOptions
): AsyncIterableIterator<{ type: DeploymentEventType; payload: any }> {
): AsyncIterableIterator<{
type: DeploymentEventType;
payload: any;
action?: string;
link?: string;
}> {
const debug = createDebug(clientOptions.debug);
const preparedFiles = prepareFiles(files, clientOptions);
const apiDeployments = getApiDeploymentsUrl(deploymentOptions);
@@ -63,19 +68,19 @@ async function* postDeployment(
};
}
for (const [name, value] of response.headers.entries()) {
if (name.startsWith('x-now-warning-')) {
debug('Deployment created with a warning:', value);
yield { type: 'warning', payload: value };
}
const indications = new Set(['warning', 'notice', 'tip']);
const regex = /^x-(?:vercel|now)-(warning|notice|tip)-(.*)$/;
for (const [name, payload] of response.headers.entries()) {
const match = name.match(regex);
if (match) {
const [, type, identifier] = match;
const action = response.headers.get(`x-vercel-action-${identifier}`);
const link = response.headers.get(`x-vercel-link-${identifier}`);
if (name.startsWith('x-now-notice-')) {
debug('Deployment created with a notice:', value);
yield { type: 'notice', payload: value };
}
if (name.startsWith('x-now-tip-')) {
debug('Deployment created with a tip:', value);
yield { type: 'tip', payload: value };
if (indications.has(type)) {
debug(`Deployment created with a ${type}: `, payload);
yield { type, payload, action, link };
}
}
}
yield { type: 'created', payload: deployment };

View File

@@ -1,6 +1,6 @@
{
"name": "@vercel/go",
"version": "1.1.5-canary.0",
"version": "1.1.6",
"license": "MIT",
"main": "./dist/index",
"homepage": "https://vercel.com/docs/runtimes#official-runtimes/go",
@@ -25,6 +25,7 @@
"@types/fs-extra": "^5.0.5",
"@types/node-fetch": "^2.3.0",
"@types/tar": "^4.0.0",
"@vercel/ncc": "0.24.0",
"async-retry": "1.3.1",
"execa": "^1.0.0",
"fs-extra": "^7.0.0",

View File

@@ -1,6 +1,6 @@
{
"name": "@vercel/next",
"version": "2.6.20",
"version": "2.6.25-canary.2",
"license": "MIT",
"main": "./dist/index",
"homepage": "https://vercel.com/docs/runtimes#official-runtimes/next-js",
@@ -26,7 +26,7 @@
"@types/resolve-from": "5.0.1",
"@types/semver": "6.0.0",
"@types/yazl": "2.4.1",
"@zeit/node-file-trace": "0.8.2",
"@vercel/nft": "0.9.2",
"async-sema": "3.0.1",
"buffer-crc32": "0.2.13",
"escape-string-regexp": "3.0.0",

View File

@@ -16,7 +16,7 @@ import {
convertRedirects,
convertRewrites,
} from '@vercel/routing-utils/dist/superstatic';
import { nodeFileTrace, NodeFileTraceReasons } from '@zeit/node-file-trace';
import { nodeFileTrace, NodeFileTraceReasons } from '@vercel/nft';
import { Sema } from 'async-sema';
import { ChildProcess, fork } from 'child_process';
import escapeStringRegexp from 'escape-string-regexp';
@@ -213,6 +213,7 @@ function startDevServer(entryPath: string, runtimeEnv: EnvConfig) {
export const build = async ({
files,
workPath,
repoRootPath,
entrypoint,
config = {} as Config,
meta = {} as BuildParamsMeta,
@@ -231,6 +232,7 @@ export const build = async ({
const entryPath = path.join(workPath, entryDirectory);
const outputDirectory = config.outputDirectory || '.next';
const dotNextStatic = path.join(entryPath, outputDirectory, 'static');
const baseDir = repoRootPath || workPath;
await download(files, workPath, meta);
@@ -240,7 +242,7 @@ export const build = async ({
const spawnOpts = getSpawnOptions(meta, nodeVersion);
const nowJsonPath = await findUp(['now.json', 'vercel.json'], {
cwd: path.join(workPath, path.dirname(entrypoint)),
cwd: entryPath,
});
let hasLegacyRoutes = false;
@@ -623,7 +625,8 @@ export const build = async ({
},
// error handling
...(output[path.join('./', entryDirectory, '404')]
...(output[path.join('./', entryDirectory, '404')] ||
output[path.join('./', entryDirectory, '404/index')]
? [
{ handle: 'error' } as Handler,
@@ -755,7 +758,10 @@ export const build = async ({
};
const lambdaOptions = await getLambdaOptionsFromFunction({
sourceFile: await getSourceFilePathFromPage({ workPath, page }),
sourceFile: await getSourceFilePathFromPage({
workPath: entryPath,
page,
}),
config,
});
@@ -938,12 +944,18 @@ export const build = async ({
const {
fileList: apiFileList,
reasons: apiReasons,
} = await nodeFileTrace(apiPages, { base: workPath });
} = await nodeFileTrace(apiPages, {
base: baseDir,
processCwd: entryPath,
});
const {
fileList,
reasons: nonApiReasons,
} = await nodeFileTrace(nonApiPages, { base: workPath });
const { fileList, reasons: nonApiReasons } = await nodeFileTrace(
nonApiPages,
{
base: baseDir,
processCwd: entryPath,
}
);
debug(`node-file-trace result for pages: ${fileList}`);
@@ -961,7 +973,7 @@ export const build = async ({
// Initial files are manually added to the lambda later
return;
}
const filePath = path.join(workPath, file);
const filePath = path.join(baseDir, file);
if (!lstatResults[filePath]) {
lstatResults[filePath] = lstatSema
@@ -972,7 +984,7 @@ export const build = async ({
const { mode } = await lstatResults[filePath];
files[file] = new FileFsRef({
fsPath: path.join(workPath, file),
fsPath: path.join(baseDir, file),
mode,
});
};
@@ -1157,7 +1169,11 @@ export const build = async ({
const {
pseudoLayer: pageLayer,
pseudoLayerBytes: pageLayerBytes,
} = await createPseudoLayer({ [pageFileName]: pages[page] });
} = await createPseudoLayer({
[path.join(path.relative(baseDir, entryPath), pageFileName)]: pages[
page
],
});
currentLambdaGroup.pages[outputName] = {
pageLayer,
@@ -1193,21 +1209,31 @@ export const build = async ({
}
const pageFileName = path.normalize(
path.relative(workPath, pages[page].fsPath)
path.relative(entryPath, pages[page].fsPath)
);
const launcher = launcherData.replace(
/__LAUNCHER_PAGE_PATH__/g,
JSON.stringify(requiresTracing ? `./${pageFileName}` : './page')
);
const launcherFiles: { [name: string]: FileFsRef | FileBlob } = {
'now__bridge.js': new FileFsRef({
[path.join(
path.relative(baseDir, entryPath),
'now__bridge.js'
)]: new FileFsRef({
fsPath: path.join(__dirname, 'now__bridge.js'),
}),
'now__launcher.js': new FileBlob({ data: launcher }),
[path.join(
path.relative(baseDir, entryPath),
'now__launcher.js'
)]: new FileBlob({ data: launcher }),
};
const lambdaOptions = await getLambdaOptionsFromFunction({
sourceFile: await getSourceFilePathFromPage({ workPath, page }),
sourceFile: await getSourceFilePathFromPage({
workPath: entryPath,
page,
}),
config,
});
@@ -1217,10 +1243,16 @@ export const build = async ({
lambdas[outputName] = await createLambdaFromPseudoLayers({
files: {
...launcherFiles,
[requiresTracing ? pageFileName : 'page.js']: pages[page],
[path.join(
path.relative(baseDir, entryPath),
pageFileName
)]: pages[page],
},
layers: isApiPage(pageFileName) ? apiPseudoLayers : pseudoLayers,
handler: 'now__launcher.launcher',
handler: path.join(
path.relative(baseDir, entryPath),
'now__launcher.launcher'
),
runtime: nodeVersion.runtime,
...lambdaOptions,
});
@@ -1230,7 +1262,7 @@ export const build = async ({
...launcherFiles,
...assets,
...tracedFiles,
[requiresTracing ? pageFileName : 'page.js']: pages[page],
['page.js']: pages[page],
},
handler: 'now__launcher.launcher',
runtime: nodeVersion.runtime,
@@ -1371,10 +1403,16 @@ export const build = async ({
`
);
const launcherFiles: { [name: string]: FileFsRef | FileBlob } = {
'now__bridge.js': new FileFsRef({
[path.join(
path.relative(baseDir, entryPath),
'now__bridge.js'
)]: new FileFsRef({
fsPath: path.join(__dirname, 'now__bridge.js'),
}),
'now__launcher.js': new FileBlob({ data: launcher }),
[path.join(
path.relative(baseDir, entryPath),
'now__launcher.js'
)]: new FileBlob({ data: launcher }),
};
const pageLayers: PseudoLayer[] = [];
@@ -1396,7 +1434,10 @@ export const build = async ({
...(group.isApiLambda ? apiPseudoLayers : pseudoLayers),
...pageLayers,
],
handler: 'now__launcher.launcher',
handler: path.join(
path.relative(baseDir, entryPath),
'now__launcher.launcher'
),
runtime: nodeVersion.runtime,
});
} else {
@@ -1408,7 +1449,10 @@ export const build = async ({
...assets,
},
layers: pageLayers,
handler: 'now__launcher.launcher',
handler: path.join(
path.relative(baseDir, entryPath),
'now__launcher.launcher'
),
runtime: nodeVersion.runtime,
});
}

View File

@@ -1,3 +1,9 @@
// The Next.js builder can emit the project in a subdirectory depending on how
// many folder levels of `node_modules` are traced. To ensure `process.cwd()`
// returns the proper path, we change the directory to the folder with the
// launcher. This mimics `yarn workspace run` behavior.
process.chdir(__dirname);
if (!process.env.NODE_ENV) {
const region = process.env.VERCEL_REGION || process.env.NOW_REGION;
process.env.NODE_ENV = region === 'dev1' ? 'development' : 'production';

View File

@@ -1,3 +1,9 @@
// The Next.js builder can emit the project in a subdirectory depending on how
// many folder levels of `node_modules` are traced. To ensure `process.cwd()`
// returns the proper path, we change the directory to the folder with the
// launcher. This mimics `yarn workspace run` behavior.
process.chdir(__dirname);
if (!process.env.NODE_ENV) {
const region = process.env.VERCEL_REGION || process.env.NOW_REGION;
process.env.NODE_ENV = region === 'dev1' ? 'development' : 'production';

View File

@@ -0,0 +1,52 @@
/* eslint-env jest */
const fetch = require('node-fetch');
const cheerio = require('cheerio');
const waitFor = ms => new Promise(resolve => setTimeout(resolve, ms));
module.exports = function (ctx) {
const getProps = async path => {
const html = await fetch(`${ctx.deploymentUrl}/${path}`).then(res =>
res.text()
);
const $ = cheerio.load(html);
return JSON.parse($('#props').text());
};
it('should render / correctly', async () => {
const props = await getProps('/', { params: {} });
expect(props.params).toEqual({});
await waitFor(2000);
await getProps('/');
const newProps = await getProps('/', { params: {} });
expect(newProps.params).toEqual({});
expect(props.random).not.toBe(newProps.random);
});
it('should render /a correctly', async () => {
const props = await getProps('/a');
expect(props.params).toEqual({ slug: ['a'] });
await waitFor(2000);
await getProps('/a');
const newProps = await getProps('/a');
expect(newProps.params).toEqual({ slug: ['a'] });
expect(props.random).not.toBe(newProps.random);
});
it('should render /hello/world correctly', async () => {
const props = await getProps('/hello/world');
expect(props.params).toEqual({ slug: ['hello', 'world'] });
await waitFor(2000);
await getProps('/hello/world');
const newProps = await getProps('/hello/world');
expect(newProps.params).toEqual({ slug: ['hello', 'world'] });
expect(props.random).not.toBe(newProps.random);
});
};

View File

@@ -0,0 +1,15 @@
{
"version": 2,
"builds": [
{
"src": "package.json",
"use": "@vercel/next"
}
],
"probes": [
{
"path": "/non-existent",
"status": 404
}
]
}

View File

@@ -0,0 +1,7 @@
{
"dependencies": {
"next": "canary",
"react": "^16.8.6",
"react-dom": "^16.8.6"
}
}

View File

@@ -0,0 +1,24 @@
export default function Home(props) {
return <pre id="props">{JSON.stringify(props)}</pre>;
}
export async function getStaticPaths() {
return {
paths: [
{ params: { slug: false } },
{ params: { slug: ['a'] } },
{ params: { slug: ['hello', 'world'] } },
],
fallback: false,
};
}
export async function getStaticProps({ params }) {
return {
props: {
params,
random: Math.random(),
},
revalidate: 1,
};
}

View File

@@ -0,0 +1,6 @@
module.exports = {
generateBuildId() {
return 'testing-build-id';
},
trailingSlash: true,
};

View File

@@ -0,0 +1,26 @@
{
"version": 2,
"builds": [{ "src": "package.json", "use": "@vercel/next" }],
"probes": [
{ "path": "/foo/", "status": 200, "mustContain": "foo page" },
{
"fetchOptions": { "redirect": "manual" },
"path": "/foo",
"status": 308,
"responseHeaders": {
"refresh": "/url=/foo/$/"
}
},
{
"path": "/non-existent",
"status": 404,
"mustContain": "oops 404"
},
{
"path": "/non-existent/",
"status": 404,
"fetchOptions": { "redirect": "manual" },
"mustContain": "oops 404"
}
]
}

View File

@@ -0,0 +1,10 @@
{
"scripts": {
"build": "next build && next export"
},
"dependencies": {
"next": "canary",
"react": "^16.8.6",
"react-dom": "^16.8.6"
}
}

View File

@@ -0,0 +1 @@
export default () => 'oops 404';

View File

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

View File

@@ -40,6 +40,6 @@ declare namespace ncc {
}
}
declare module '@zeit/ncc' {
declare module '@vercel/ncc' {
export = ncc;
}

View File

@@ -1,6 +1,6 @@
{
"name": "@vercel/node",
"version": "1.7.5-canary.1",
"version": "1.8.2-canary.2",
"license": "MIT",
"main": "./dist/index",
"homepage": "https://vercel.com/docs/runtimes#official-runtimes/node-js",
@@ -32,8 +32,8 @@
"@types/cookie": "0.3.3",
"@types/etag": "1.8.0",
"@types/test-listen": "1.1.0",
"@zeit/ncc": "0.20.4",
"@zeit/node-file-trace": "0.8.2",
"@vercel/ncc": "0.24.0",
"@vercel/nft": "0.9.2",
"content-type": "1.0.4",
"cookie": "0.4.0",
"etag": "1.8.1",

View File

@@ -73,6 +73,24 @@ function status(res: NowResponse, statusCode: number): NowResponse {
return res;
}
function redirect(
res: NowResponse,
statusOrUrl: string | number,
url?: string
): NowResponse {
if (typeof statusOrUrl === 'string') {
url = statusOrUrl;
statusOrUrl = 307;
}
if (typeof statusOrUrl !== 'number' || typeof url !== 'string') {
throw new Error(
`Invalid redirect arguments. Please use a single argument URL, e.g. res.redirect('/destination') or use a status code and URL, e.g. res.redirect(307, '/destination').`
);
}
res.writeHead(statusOrUrl, { Location: url }).end();
return res;
}
function setCharset(type: string, charset: string) {
// eslint-disable-next-line @typescript-eslint/no-var-requires
const { parse, format } = require('content-type');
@@ -261,6 +279,7 @@ export function createServerWithHelpers(
setLazyProp<NowRequestBody>(req, 'body', getBodyParser(req, event.body));
res.status = statusCode => status(res, statusCode);
res.redirect = (statusOrUrl, url) => redirect(res, statusOrUrl, url);
res.send = body => send(req, res, body);
res.json = jsonBody => json(req, res, jsonBody);

View File

@@ -19,7 +19,7 @@ import {
// @ts-ignore - `@types/mkdirp-promise` is broken
import mkdirp from 'mkdirp-promise';
import once from '@tootallnate/once';
import { nodeFileTrace } from '@zeit/node-file-trace';
import { nodeFileTrace } from '@vercel/nft';
import buildUtils from './build-utils';
import {
File,
@@ -52,12 +52,6 @@ import { Register, register } from './typescript';
export { shouldServe };
export { NowRequest, NowResponse } from './types';
interface CompilerConfig {
debug?: boolean;
includeFiles?: string | string[];
excludeFiles?: string | string[];
}
interface DownloadOptions {
files: Files;
entrypoint: string;
@@ -125,9 +119,10 @@ async function downloadInstallAndBundle({
async function compile(
workPath: string,
baseDir: string,
entrypointPath: string,
entrypoint: string,
config: CompilerConfig
config: Config
): Promise<{
preparedFiles: Files;
shouldAddSourcemapSupport: boolean;
@@ -150,20 +145,21 @@ async function compile(
for (const pattern of includeFiles) {
const files = await glob(pattern, workPath);
await Promise.all(
Object.keys(files).map(async file => {
const entry = files[file];
fsCache.set(file, entry);
Object.values(files).map(async entry => {
const { fsPath } = entry;
const relPath = relative(baseDir, fsPath);
fsCache.set(relPath, entry);
const stream = entry.toStream();
const { data } = await FileBlob.fromStream({ stream });
if (file.endsWith('.ts') || file.endsWith('.tsx')) {
if (relPath.endsWith('.ts') || relPath.endsWith('.tsx')) {
sourceCache.set(
file,
compileTypeScript(resolve(workPath, file), data.toString())
relPath,
compileTypeScript(fsPath, data.toString())
);
} else {
sourceCache.set(file, data);
sourceCache.set(relPath, data);
}
inputFiles.add(resolve(workPath, file));
inputFiles.add(fsPath);
})
);
}
@@ -178,7 +174,7 @@ async function compile(
let tsCompile: Register;
function compileTypeScript(path: string, source: string): string {
const relPath = relative(workPath, path);
const relPath = relative(baseDir, path);
if (!tsCompile) {
tsCompile = register({
basePath: workPath, // The base is the same as root now.json dir
@@ -201,12 +197,13 @@ async function compile(
const { fileList, esmFileList, warnings } = await nodeFileTrace(
[...inputFiles],
{
base: workPath,
base: baseDir,
processCwd: workPath,
ts: true,
mixedModules: true,
ignore: config.excludeFiles,
readFile(fsPath: string): Buffer | string | null {
const relPath = relative(workPath, fsPath);
const relPath = relative(baseDir, fsPath);
const cached = sourceCache.get(relPath);
if (cached) return cached.toString();
// null represents a not found
@@ -246,7 +243,7 @@ async function compile(
for (const path of fileList) {
let entry = fsCache.get(path);
if (!entry) {
const fsPath = resolve(workPath, path);
const fsPath = resolve(baseDir, path);
const { mode } = lstatSync(fsPath);
if (isSymbolicLink(mode)) {
entry = new FileFsRef({ fsPath, mode });
@@ -258,14 +255,14 @@ async function compile(
if (isSymbolicLink(entry.mode) && entry.fsPath) {
// ensure the symlink target is added to the file list
const symlinkTarget = relative(
workPath,
baseDir,
resolve(dirname(entry.fsPath), readlinkSync(entry.fsPath))
);
if (
!symlinkTarget.startsWith('..' + sep) &&
fileList.indexOf(symlinkTarget) === -1
) {
const stats = statSync(resolve(workPath, symlinkTarget));
const stats = statSync(resolve(baseDir, symlinkTarget));
if (stats.isFile()) {
fileList.push(symlinkTarget);
}
@@ -275,7 +272,7 @@ async function compile(
// There is a bug on Windows where entrypoint uses forward slashes
// and workPath uses backslashes so we use resolve before comparing.
if (
resolve(workPath, path) !== resolve(workPath, entrypoint) &&
resolve(baseDir, path) !== resolve(workPath, entrypoint) &&
tsCompiled.has(path)
) {
preparedFiles[
@@ -339,6 +336,7 @@ export async function build({
files,
entrypoint,
workPath,
repoRootPath,
config = {},
meta = {},
}: BuildOptions) {
@@ -346,6 +344,7 @@ export async function build({
config.helpers === false || process.env.NODEJS_HELPERS === '0'
);
const baseDir = repoRootPath || workPath;
const awsLambdaHandler = getAWSLambdaHandler(entrypoint, config);
const {
@@ -372,6 +371,7 @@ export async function build({
const traceTime = Date.now();
const { preparedFiles, shouldAddSourcemapSupport, watch } = await compile(
workPath,
baseDir,
entrypointPath,
entrypoint,
config
@@ -383,7 +383,7 @@ export async function build({
const launcherFiles: Files = {
[`${LAUNCHER_FILENAME}.js`]: new FileBlob({
data: makeLauncher({
entrypointPath: `./${entrypoint}`,
entrypointPath: `./${relative(baseDir, entrypointPath)}`,
bridgePath: `./${BRIDGE_FILENAME}`,
helpersPath: `./${HELPERS_FILENAME}`,
sourcemapSupportPath: `./${SOURCEMAP_SUPPORT_FILENAME}`,

View File

@@ -14,9 +14,7 @@ export type NowResponse = ServerResponse & {
send: (body: any) => NowResponse;
json: (jsonBody: any) => NowResponse;
status: (statusCode: number) => NowResponse;
redirect: (statusOrUrl: string | number, url?: string) => NowResponse;
};
export type NowApiHandler = (
req: NowRequest,
res: NowResponse
) => void;
export type NowApiHandler = (req: NowRequest, res: NowResponse) => void;

View File

@@ -3,6 +3,7 @@
"strict": true,
"esModuleInterop": true,
"sourceMap": true,
"jsx": "react",
"lib": ["esnext"],
"target": "esnext",
"module": "commonjs"

View File

@@ -65,6 +65,7 @@ describe('all helpers', () => {
['cookies', 0],
['body', 0],
['status', 1],
['redirect', 1],
['send', 1],
['json', 1],
];
@@ -307,6 +308,59 @@ describe('res.status', () => {
});
});
describe('res.redirect', () => {
test('should redirect to login', async () => {
mockListener.mockImplementation((req, res) => {
res.redirect('/login');
res.end();
});
const res = await fetchWithProxyReq(url, { redirect: 'manual' });
expect(res.status).toBe(307);
expect(res.headers.get('location')).toBe(url + '/login');
});
test('should redirect with status code 301', async () => {
mockListener.mockImplementation((req, res) => {
res.redirect(301, '/login');
res.end();
});
const res = await fetchWithProxyReq(url, { redirect: 'manual' });
expect(res.status).toBe(301);
expect(res.headers.get('location')).toBe(url + '/login');
});
test('should show friendly error for invalid redirect', async () => {
let error;
mockListener.mockImplementation((req, res) => {
try {
res.redirect(307);
} catch (err) {
error = err;
}
res.end();
});
await fetchWithProxyReq(url, { redirect: 'manual' });
expect(error.message).toBe(
`Invalid redirect arguments. Please use a single argument URL, e.g. res.redirect('/destination') or use a status code and URL, e.g. res.redirect(307, '/destination').`
);
});
test('should show friendly error in case of passing null as first argument redirect', async () => {
let error;
mockListener.mockImplementation((req, res) => {
try {
res.redirect(null);
} catch (err) {
error = err;
}
res.end();
});
await fetchWithProxyReq(url, { redirect: 'manual' });
expect(error.message).toBe(
`Invalid redirect arguments. Please use a single argument URL, e.g. res.redirect('/destination') or use a status code and URL, e.g. res.redirect(307, '/destination').`
);
});
});
// tests based on expressjs test suite
// see https://github.com/expressjs/express/blob/master/test/res.send.js
describe('res.send', () => {

View File

@@ -1,6 +1,6 @@
{
"name": "@vercel/python",
"version": "1.2.2",
"version": "1.2.3",
"main": "./dist/index.js",
"license": "MIT",
"homepage": "https://vercel.com/docs/runtimes#official-runtimes/python",
@@ -20,6 +20,7 @@
},
"devDependencies": {
"@types/execa": "^0.9.0",
"@vercel/ncc": "0.24.0",
"execa": "^1.0.0",
"typescript": "3.9.3"
}

View File

@@ -1,6 +1,6 @@
{
"name": "@vercel/routing-utils",
"version": "1.8.4-canary.1",
"version": "1.8.4",
"description": "Vercel routing utilities",
"main": "./dist/index.js",
"types": "./dist/index.d.ts",

View File

@@ -201,13 +201,13 @@ function replaceSegments(
.filter(val => typeof val === 'string') as string[]
);
pathname = safelyCompile(pathname, indexes);
hash = hash ? safelyCompile(hash, indexes) : null;
pathname = safelyCompile(pathname, indexes, true);
hash = hash ? safelyCompile(hash, indexes, true) : null;
for (const [key, strOrArray] of Object.entries(query)) {
let value = Array.isArray(strOrArray) ? strOrArray[0] : strOrArray;
if (value) {
value = safelyCompile(value, indexes);
value = safelyCompile(value, indexes, true);
}
query[key] = value;
}
@@ -242,12 +242,24 @@ function replaceSegments(
function safelyCompile(
value: string,
indexes: { [k: string]: string }
indexes: { [k: string]: string },
attemptDirectCompile?: boolean
): string {
if (!value) {
return value;
}
if (attemptDirectCompile) {
try {
// Attempt compiling normally with path-to-regexp first and fall back
// to safely compiling to handle edge cases if path-to-regexp compile
// fails
return compile(value, { validate: false })(indexes);
} catch (e) {
// non-fatal, we continue to safely compile
}
}
for (const key of Object.keys(indexes)) {
if (value.includes(`:${key}`)) {
value = value

View File

@@ -190,6 +190,14 @@ test('convertRedirects', () => {
source: '/optional/:id?',
destination: '/api/optional/:id?',
},
{
source: '/feature-{:slug}',
destination: '/blog-{:slug}',
},
{
source: '/hello/:world',
destination: '/somewhere?else={:world}',
},
]);
const expected = [
@@ -266,6 +274,20 @@ test('convertRedirects', () => {
headers: { Location: '/api/optional/$1' },
status: 308,
},
{
headers: {
Location: '/blog-$1',
},
src: '^\\/feature-([^\\/]+?)$',
status: 308,
},
{
headers: {
Location: '/somewhere?else=$1',
},
src: '^\\/hello(?:\\/([^\\/]+?))$',
status: 308,
},
];
deepEqual(actual, expected);
@@ -285,6 +307,8 @@ test('convertRedirects', () => {
['/hello/world', '/hello/another/world'],
['/external/1', '/external/2'],
['/optional', '/optional/1'],
['/feature-first', '/feature-second'],
['/hello/world', '/hello/again'],
];
const mustNotMatch = [
@@ -302,6 +326,8 @@ test('convertRedirects', () => {
['/not-this-one', '/helloo'],
['/externalnope', '/externally'],
['/optionalnope', '/optionally'],
['/feature/first', '/feature'],
['/hello', '/hello/another/one'],
];
assertRegexMatches(actual, mustMatch, mustNotMatch);
@@ -350,6 +376,14 @@ test('convertRewrites', () => {
{ source: '/:path', destination: '/test?path=:path' },
{ source: '/:path/:two', destination: '/test?path=:path' },
{ source: '/(.*)-:id(\\d+).html', destination: '/blog/:id' },
{
source: '/feature-{:slug}',
destination: '/blog-{:slug}',
},
{
source: '/hello/:world',
destination: '/somewhere?else={:world}',
},
]);
const expected = [
@@ -426,6 +460,16 @@ test('convertRewrites', () => {
src: '^(?:\\/([^\\/]+?))(?:\\/([^\\/]+?))$',
},
{ check: true, dest: '/blog/$2', src: '^(?:\\/(.*))-(\\d+)\\.html$' },
{
dest: '/blog-$1',
src: '^\\/feature-([^\\/]+?)$',
check: true,
},
{
dest: '/somewhere?else=$1&world=$1',
src: '^\\/hello(?:\\/([^\\/]+?))$',
check: true,
},
];
deepEqual(actual, expected);
@@ -447,6 +491,8 @@ test('convertRewrites', () => {
['/first', '/another'],
['/first/second', '/one/two'],
['/hello/post-123.html', '/post-123.html'],
['/feature-first', '/feature-second'],
['/hello/world', '/hello/again'],
];
const mustNotMatch = [
@@ -466,6 +512,8 @@ test('convertRewrites', () => {
['/another/one'],
['/not', '/these'],
['/hello/post.html'],
['/feature/first', '/feature'],
['/hello', '/hello/another/one'],
];
assertRegexMatches(actual, mustMatch, mustNotMatch);

View File

@@ -1,7 +1,7 @@
{
"name": "@vercel/ruby",
"author": "Nathan Cahill <nathan@nathancahill.com>",
"version": "1.2.3",
"version": "1.2.4",
"license": "MIT",
"main": "./dist/index",
"homepage": "https://vercel.com/docs/runtimes#official-runtimes/ruby",
@@ -22,6 +22,7 @@
"devDependencies": {
"@types/fs-extra": "8.0.0",
"@types/semver": "6.0.0",
"@vercel/ncc": "0.24.0",
"execa": "2.0.4",
"fs-extra": "^7.0.1",
"semver": "6.1.1",

View File

@@ -1,9 +1,9 @@
{
"name": "@vercel/static-build",
"version": "0.17.7-canary.3",
"version": "0.17.8",
"license": "MIT",
"main": "./dist/index",
"homepage": "https://vercel.com/docs/runtimes#official-runtimes/static-builds",
"homepage": "https://vercel.com/docs/build-step",
"files": [
"dist"
],
@@ -23,6 +23,7 @@
"@types/ms": "0.7.31",
"@types/node-fetch": "2.5.4",
"@types/promise-timeout": "1.3.0",
"@vercel/ncc": "0.24.0",
"get-port": "5.0.0",
"is-port-reachable": "2.0.1",
"ms": "2.1.2",

View File

@@ -1,6 +1,6 @@
{
"name": "@vercel/redwood",
"version": "0.0.2-canary.7",
"version": "0.1.1",
"main": "./dist/index.js",
"license": "MIT",
"homepage": "https://vercel.com/docs",
@@ -19,7 +19,7 @@
},
"dependencies": {
"@netlify/zip-it-and-ship-it": "1.2.0",
"@vercel/frameworks": "0.0.18-canary.5"
"@vercel/frameworks": "0.1.1"
},
"devDependencies": {
"@types/aws-lambda": "8.10.19",

View File

@@ -1,5 +1,4 @@
import { join, dirname, relative, parse as parsePath, sep } from 'path';
import { readFileSync } from 'fs';
import {
BuildOptions,
Lambda,
@@ -18,6 +17,8 @@ import {
FileFsRef,
PackageJson,
NowBuildError,
getLambdaOptionsFromFunction,
readConfigFile,
} from '@vercel/build-utils';
import { makeAwsLauncher } from './launcher';
import _frameworks, { Framework } from '@vercel/frameworks';
@@ -32,6 +33,12 @@ const BRIDGE_FILENAME = '___vc_bridge';
const HELPERS_FILENAME = '___vc_helpers';
const SOURCEMAP_SUPPORT_FILENAME = '__vc_sourcemap_support';
interface RedwoodToml {
web: { port?: number; apiProxyPath?: string };
api: { port?: number };
browser: { open?: boolean };
}
export const version = 2;
export async function build({
@@ -67,8 +74,11 @@ export async function build({
const { buildCommand } = config;
const frmwrkCmd = frameworks.find(f => f.slug === 'redwoodjs')?.settings
.buildCommand;
const pkgPath = join(workPath, 'package.json');
const pkg = JSON.parse(readFileSync(pkgPath, 'utf8')) as PackageJson;
const pkg = await readConfigFile<PackageJson>(join(workPath, 'package.json'));
const toml = await readConfigFile<RedwoodToml>(
join(workPath, 'redwood.toml')
);
if (buildCommand) {
debug(`Executing build command "${buildCommand}"`);
await execCommand(buildCommand, {
@@ -98,6 +108,7 @@ export async function build({
});
}
const apiDir = toml?.web?.apiProxyPath?.replace(/^\//, '') ?? 'api';
const apiDistPath = join(workPath, 'api', 'dist', 'functions');
const webDistPath = join(workPath, 'web', 'dist');
const lambdaOutputs: { [filePath: string]: Lambda } = {};
@@ -107,7 +118,7 @@ export async function build({
const functionFiles = await glob('*.js', apiDistPath);
for (const [funcName, fileFsRef] of Object.entries(functionFiles)) {
const outputName = join('api', parsePath(funcName).name); // remove `.js` extension
const outputName = join(apiDir, parsePath(funcName).name); // remove `.js` extension
const absEntrypoint = fileFsRef.fsPath;
const dependencies: string[] = await getDependencies(
absEntrypoint,
@@ -115,6 +126,7 @@ export async function build({
);
const relativeEntrypoint = relative(workPath, absEntrypoint);
const awsLambdaHandler = getAWSLambdaHandler(relativeEntrypoint, 'handler');
const sourceFile = relativeEntrypoint.replace('/dist/', '/src/');
const lambdaFiles: Files = {
[`${LAUNCHER_FILENAME}.js`]: new FileBlob({
@@ -141,11 +153,18 @@ export async function build({
lambdaFiles[relative(workPath, fileFsRef.fsPath)] = fileFsRef;
const { memory, maxDuration } = await getLambdaOptionsFromFunction({
sourceFile,
config,
});
const lambda = await createLambda({
files: lambdaFiles,
handler: `${LAUNCHER_FILENAME}.launcher`,
runtime: nodeVersion.runtime,
environment: {},
memory,
maxDuration,
});
lambdaOutputs[outputName] = lambda;
}
@@ -162,7 +181,7 @@ function getAWSLambdaHandler(filePath: string, handlerName: string) {
return `${dir}${dir ? sep : ''}${name}.${handlerName}`;
}
function hasScript(scriptName: string, pkg: PackageJson) {
function hasScript(scriptName: string, pkg: PackageJson | null) {
const scripts = (pkg && pkg.scripts) || {};
return typeof scripts[scriptName] === 'string';
}
@@ -170,6 +189,6 @@ function hasScript(scriptName: string, pkg: PackageJson) {
export async function prepareCache({
workPath,
}: PrepareCacheOptions): Promise<Files> {
const cache = await glob('node_modules/**', workPath);
const cache = await glob('**/node_modules/**', workPath);
return cache;
}

View File

@@ -0,0 +1,9 @@
async function handler() {
return {
statusCode: 200,
headers: {},
body: `Memory is: ${process.env.AWS_LAMBDA_FUNCTION_MEMORY_SIZE}`,
}
}
module.exports = { handler }

View File

@@ -1,6 +1,19 @@
{
"version": 2,
"builds": [{ "src": "package.json", "use": "@vercel/redwood" }],
"builds": [
{
"src": "package.json",
"use": "@vercel/redwood",
"config": {
"zeroConfig": true,
"functions": {
"api/src/functions/memory.js": {
"memory": 128
}
}
}
}
],
"probes": [
{ "path": "/", "mustContain": "<div id=\"redwood-app\">" },
{ "path": "/about", "mustContain": "<div id=\"redwood-app\">" },
@@ -19,6 +32,10 @@
{
"path": "/api/permission",
"mustContain": "File is executable: true"
},
{
"path": "/api/memory",
"mustContain": "Memory is: 128"
}
]
}

View File

@@ -1,15 +1,6 @@
# This file contains the configuration settings for your Redwood app.
# This file is also what makes your Redwood app a Redwood app.
# If you remove it and try to run `yarn rw dev`, you'll get an error.
#
# For the full list of options, see the "App Configuration: redwood.toml" doc:
# https://redwoodjs.com/docs/app-configuration-redwood-toml
#
# By default, a Redwood app's apiProxyPath is the same as Netlify's.
[web]
port = 8910
apiProxyPath = "/api/functions"
apiProxyPath = "/api"
[api]
port = 8911
[browser]

View File

@@ -0,0 +1,10 @@
# 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.
# schema.prisma defaults
DATABASE_URL=file:./dev.db
# disables Prisma CLI update notifier
PRISMA_HIDE_UPDATE_MESSAGE=true

View File

@@ -0,0 +1,10 @@
.idea
.DS_Store
.env
.netlify
dev.db
dist
dist-babel
node_modules
yarn-error.log
web/public/mockServiceWorker.js

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,25 @@
# Redwood
> **WARNING:** RedwoodJS software has not reached a stable version 1.0 and should not be considered suitable for production use. In the "make it work; make it right; make it fast" paradigm, Redwood is in the later stages of the "make it work" phase.
## Getting Started
- [Tutorial](https://redwoodjs.com/tutorial/welcome-to-redwood): getting started and complete overview guide.
- [Docs](https://redwoodjs.com/docs/introduction): using the Redwood Router, handling assets and files, list of command-line tools, and more.
- [Redwood Community](https://community.redwoodjs.com): get help, share tips and tricks, and collaborate on everything about RedwoodJS.
### Setup
We use Yarn as our package manager. To get the dependencies installed, just do this in the root directory:
```terminal
yarn install
```
### Fire it up
```terminal
yarn redwood dev
```
Your browser should open automatically to `http://localhost:8910` to see the web app. Lambda functions run on `http://localhost:8911` and are also proxied to `http://localhost:8910/.redwood/functions/*`.

View File

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

View File

@@ -0,0 +1,6 @@
const { getConfig } = require('@redwoodjs/core')
const config = getConfig({ type: 'jest', target: 'node' })
config.displayName.name = 'api'
module.exports = config

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.16.0"
}
}

View File

@@ -0,0 +1,20 @@
datasource DS {
// optionally set multiple providers
// example: provider = ["sqlite", "postgresql"]
provider = "sqlite"
url = env("DATABASE_URL")
}
generator client {
provider = "prisma-client-js"
binaryTargets = "native"
}
// 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,17 @@
import {
createGraphQLHandler,
makeMergedSchema,
makeServices,
} from '@redwoodjs/api'
import schemas from 'src/graphql/**/*.{js,ts}'
import services from 'src/services/**/*.{js,ts}'
import { db } from 'src/lib/db'
export const handler = createGraphQLHandler({
schema: makeMergedSchema({
schemas,
services: makeServices({ services }),
}),
db,
})

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