Compare commits

..

39 Commits

Author SHA1 Message Date
Joe Haddad
de3701c045 Publish Canary
- @now/ruby@1.0.3-canary.1
2020-02-28 10:18:46 -05:00
m5o
9f9b7934cb [now-ruby] Fix ruby test indentation (#3854)
* fix ruby indentation example
  * 💅 indent with 2 spaces for consistency
2020-02-28 14:34:47 +00:00
Steven
bcded1dd17 Publish Canary
- @now/build-utils@2.0.1-canary.3
 - @now/next@2.3.19-canary.2
 - @now/python@1.1.5-canary.1
2020-02-28 08:57:36 -05:00
Steven
8503af75ba [now-python] Fix space encoding 2020-02-28 08:35:27 -05:00
Joe Haddad
158a50f1aa [now-next] Allow Immutable Fallback Artifact (#3850)
* [now-next] Allow Immutable Fallback Artifact

* trigger
2020-02-27 22:00:09 -05:00
Steven
61da552dd6 [now-build-utils] Add test for NODE_ENV behavior (#3852)
This PR adds a test for #3847.
2020-02-27 21:47:27 +00:00
Steven
fa838eecac Publish Canary
- @now/go@1.0.5-canary.0
 - @now/next@2.3.19-canary.1
 - @now/node@1.4.2-canary.0
 - @now/python@1.1.5-canary.0
 - @now/ruby@1.0.3-canary.0
 - @now/static-build@0.14.13-canary.1
2020-02-27 15:52:36 -05:00
Steven
71b6a58783 [all] Exclude @now/build-utils in bundled builder to fix tests (#3848)
This fixes a bug in our `@now/build-utils` tests that pair the current build-utils with a stable builder. Since ncc was bundling `@now/build-utils`, we weren't able to configure a different version and these tests were not actually testing the correct version of build-utils.

A nice side-effect is that each builder will be about 50% smaller (compared by measuring `dist`).
2020-02-27 15:48:03 -05:00
Joe Haddad
22dd78e286 Publish Canary
- @now/build-utils@2.0.1-canary.2
 - @now/next@2.3.19-canary.0
2020-02-27 12:35:52 -05:00
Joe Haddad
e63fcf2630 [now-next] Add Support for Prerender v2 (#3845)
* [now-next] Add Support for Prerender v2

* Copy test suite

* Test that fallback doesn't work for fallback: false

* record omitted lambdas

* Improve test case

* improve omitted routes logic

Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2020-02-27 12:33:31 -05:00
Steven
98e1553c2e [now-build-utils] Install all deps regardless of NODE_ENV (#3847)
Some build utilities and SSG Frameworks instruct users to set `NODE_ENV=production` which typically means updating `now.json` to the following:

```json
{
  "build": {
    "env": {
      "NODE_ENV": "production"
    }
  }
}
```

The problem is that this environment variable is assigned during `npm install` or `yarn install` which is the equivalent of running install with the `--production` flag. This flag prevents `devDependencies` from installing. This is almost never what the user intends so they have to remove `now.json` and instead updating their build script to `NODE_ENV=production yarn build`.

This PR improves the experience by deleting `NODE_ENV` during the install step.
2020-02-27 12:14:06 -05:00
Steven
4dc506f17a Publish Canary
- @now/build-utils@2.0.1-canary.1
 - now@17.0.5-canary.5
 - @now/routing-utils@1.6.1-canary.1
2020-02-27 08:05:05 -05:00
Steven
3b396f29e9 [now-build-utils][now-routing-utils] Fix appendRoutesToPhase() when input is null (#3843)
This PR fixes `@now/routing-utils` when the input routes are null. It used to return the empty array but now it will still append.

I also added many more tests to `@now/build-utils` when `featureHandleMiss: true` and refactored the code a bit to make dynamic routes and api routes a little more clear.
2020-02-26 23:30:50 +00:00
JJ Kasper
1f128e69e6 Publish Stable
- @now/next@2.3.18
2020-02-26 15:56:41 -06:00
JJ Kasper
f1487c92cb Publish Canary
- now@17.0.5-canary.4
 - @now/next@2.3.18-canary.0
2020-02-26 15:51:27 -06:00
JJ Kasper
45066cdf44 [now-next] Add /_next/data routes for getServerProps pages (#3771)
This adds the `/_next/data` routes for `getServerProps` pages if they exist

x-ref: https://github.com/zeit/next.js/pull/10077
x-ref: https://github.com/zeit/next.js/pull/10622
2020-02-26 21:47:51 +00:00
Ana Trajkovska
7dde9c8207 Publish Canary
- now@17.0.5-canary.3
 - now-client@7.0.1-canary.1
2020-02-26 08:00:09 +01:00
Ana Trajkovska
fd964f825d Handle deployment cancellation (#3823)
Co-authored-by: Max <8418866+rdev@users.noreply.github.com>
2020-02-26 07:21:27 +01:00
Steven
1656c9874e Publish Canary
- @now/build-utils@2.0.1-canary.0
 - now@17.0.5-canary.2
 - @now/routing-utils@1.6.1-canary.0
2020-02-25 17:32:37 -05:00
Steven
d999a3b2ad [now-routing-utils][now-build-utils] Refactor zero-config rewrites (#3832)
This PR refactors the rewrites (the dynamic routes as well as the route that prevents directory listing for zero config deployments) so they are not in the `handle: miss` phase.

This is necessary because the behavior of `handle: miss` will change in an upcoming release.

The solution is to separate these into `rewriteRoutes` that can then be merged properly with the user's routes. They will be appended to the `handle: filesystem` phase (or add the phase if it doesn't exist).
2020-02-25 22:30:36 +00:00
Joe Haddad
cfb7c9e632 Publish Stable
- @now/next@2.3.17
2020-02-25 15:19:05 -05:00
Joe Haddad
06e8472cf7 Publish Canary
- now@17.0.5-canary.1
 - @now/next@2.3.17-canary.0
 - @now/static-build@0.14.13-canary.0
2020-02-25 14:57:42 -05:00
Joe Haddad
92404135d8 [now-next] Always Emit Prerenders (#3837)
This ensures we always emit Prerender objects for bypass tokens.
2020-02-25 19:51:07 +00:00
Nathan Rajlich
8b5cc23d7c [now-cli] Remove v1 "static build" integration tests (#3833)
* [now-cli] Remove v1 "static build" integration tests

These are the last remaining v1 static type deployments being created.

* Remove from `integration.js` as well

Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2020-02-24 17:19:22 -08:00
Shu Uesugi
e10b42bfdc Rename zeit.co/new → zeit.co/import (#3834)
* zeit.co/new → zeit.co/import

* Update docs URL
2020-02-25 01:35:12 +01:00
M. Heide
36f7ec4836 [examples] Fix Go build if Windows Git is configured to checkout CRLF line endings (#3821)
Fixes #3820
2020-02-24 19:53:15 +00:00
Joe Haddad
f89d1c463e Remove Backticks from URL Output (#3830)
![image](https://user-images.githubusercontent.com/616428/75176976-44434880-5703-11ea-8ca5-cb49e4a921cf.png)

![image](https://user-images.githubusercontent.com/616428/75176982-473e3900-5703-11ea-90f6-b31844a486ce.png)
2020-02-24 19:22:58 +00:00
Steven
5dc652eba9 Publish Canary
- now@17.0.5-canary.0
 - now-client@7.0.1-canary.0
2020-02-24 12:53:13 -05:00
Steven
821b2bd50b [now-client] Fix root directory with trailing slash (#3827)
There was a bug in `now-client` when deploying a directory that ends with a slash, for example `/Users/styfle/Code/myapp/` instead of the usual `/Users/styfle/Code/myapp`.

This never affected `now-cli` until we added support for defining the `rootDirectory` which allows the user to type whatever they wish in the Project Settings.

The fix is to use `path.relative()` instead of substring.
2020-02-24 17:45:10 +00:00
Steven
8d5f6f1fa3 Publish Stable
- @now/frameworks@0.0.10
 - @now/build-utils@2.0.0
 - now@17.0.4
 - @now/go@1.0.4
 - @now/next@2.3.16
 - @now/python@1.1.4
 - @now/routing-utils@1.6.0
2020-02-21 18:09:20 -05:00
Steven
617f610c07 [tests] Update cancel workflow (#3817)
Update to latest version, [0.2.0](https://github.com/styfle/cancel-workflow-action/releases/tag/0.2.0), which will fix cancelling on push.

Also this action is available in the [marketplace](https://github.com/marketplace/actions/cancel-workflow-action).
2020-02-21 18:00:27 -05:00
Steven
3229ba0106 Publish Canary
- @now/go@1.0.4-canary.3
 - @now/python@1.1.4-canary.1
2020-02-21 17:27:20 -05:00
Steven
d16a6e4716 [now-go] Fix path segments in now dev (#3816)
The combination of file renaming and brackets doesn't work well for go build so we need to add the file extension back before building.

I also simplified the `.now/cache/folder` logic because it should be the same whether `builds` is defined or not (aka zero config).
2020-02-21 22:25:44 +00:00
Steven
4e5156fb0b [now-python] Fix path segments in now dev (#3815)
The combination of file renaming and brackets doesn't work well for python imports so we need too add the file extension back before importing the python module.

I also simplified the `.now/cache/folder` logic because it should be the same whether `builds` is defined or not (aka zero config).
2020-02-21 20:45:41 +00:00
Joe Haddad
3c013cc36b Publish Canary
- @now/build-utils@2.0.0-canary.2
 - @now/next@2.3.16-canary.1
2020-02-21 11:06:44 -05:00
Joe Haddad
1ed83135be Add Bypass Token to Next.js Builder (#3811)
This pull request adds the bypass token to returned `Prerender` output objects.

There's no tests as this doesn't exist in production yet.
2020-02-21 16:05:22 +00:00
Joe Haddad
0e71ff2df1 [now-build-utils] Add bypassToken to Prerender for iSSG (#3810)
This pull request adds a new `bypassToken` field to the `Prerender` output.

The field is required to be at least 128 bits for security reasons!
2020-02-20 01:59:45 +00:00
JJ Kasper
d8e98ee476 Publish Canary
- @now/next@2.3.16-canary.0
 - @now/routing-utils@1.5.4-canary.0
2020-02-19 16:23:25 -06:00
JJ Kasper
1bc1e8a9b1 [now-routing-utils] Update to not pass used segments in redirect query (#3809)
This updates to not pass segments already used in a redirect's destination query since these values are most likely unwanted and can still be manually added if desired. This change does not affect rewrites and we still pass through all segments in the query
2020-02-19 22:13:36 +00:00
146 changed files with 2873 additions and 705 deletions

4
.gitattributes vendored
View File

@@ -1,3 +1,7 @@
# Ignore test fixtures in GitHub Languages
# See https://github.com/github/linguist#vendored-code
packages/*/test/* linguist-vendored
# Go build fails with Windows line endings.
*.go text eol=lf
go.mod text eol=lf

View File

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

View File

@@ -4,13 +4,14 @@ on:
branches:
- '*'
- '!master'
jobs:
cancel:
name: 'Cancel Previous Runs'
runs-on: ubuntu-latest
timeout-minutes: 3
steps:
- uses: styfle/cancel-workflow-action@master
- uses: styfle/cancel-workflow-action@0.2.0
with:
workflow_id: 435869
access_token: ${{ secrets.GITHUB_WORKFLOW_TOKEN }}

View File

@@ -21,7 +21,7 @@ now # Deploy to the cloud
## Documentation
For details on how to use Now CLI, check out our [documentation](https://zeit.co/docs).
For details on how to use Now CLI, check out our [documentation](https://zeit.co/docs/now-cli).
## Caught a Bug?

View File

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

View File

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

View File

@@ -6,7 +6,7 @@ This directory is a brief example of a [Assemble](http://assemble.io/) app that
Deploy your own Assemble project with ZEIT Now.
[![Deploy with ZEIT Now](https://zeit.co/button)](https://zeit.co/new/project?template=https://github.com/zeit/now/tree/master/examples/assemble)
[![Deploy with ZEIT Now](https://zeit.co/button)](https://zeit.co/import/project?template=https://github.com/zeit/now/tree/master/examples/assemble)
_Live Example: https://assemble.now-examples.now.sh_

View File

@@ -6,7 +6,7 @@ This directory is a brief example of an [Aurelia](https://aurelia.io/) app that
Deploy your own Aurelia project with ZEIT Now.
[![Deploy with ZEIT Now](https://zeit.co/button)](https://zeit.co/new/project?template=https://github.com/zeit/now/tree/master/examples/aurelia)
[![Deploy with ZEIT Now](https://zeit.co/button)](https://zeit.co/import/project?template=https://github.com/zeit/now/tree/master/examples/aurelia)
_Live Example: https://aurelia.now-examples.now.sh_

View File

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

View File

@@ -6,7 +6,7 @@ This directory is a brief example of a [Charge.js](https://charge.js.org/) site
Deploy your own Charge.js project with ZEIT Now.
[![Deploy with ZEIT Now](https://zeit.co/button)](https://zeit.co/new/project?template=https://github.com/zeit/now/tree/master/examples/charge)
[![Deploy with ZEIT Now](https://zeit.co/button)](https://zeit.co/import/project?template=https://github.com/zeit/now/tree/master/examples/charge)
_Live Example: https://charge.now-examples.now.sh_

View File

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

View File

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

View File

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

View File

@@ -6,7 +6,7 @@ This directory is a brief example of a [Docz](https://www.docz.site/) site that
Deploy your own Docz project with ZEIT Now.
[![Deploy with ZEIT Now](https://zeit.co/button)](https://zeit.co/new/project?template=https://github.com/zeit/now/tree/master/examples/docz)
[![Deploy with ZEIT Now](https://zeit.co/button)](https://zeit.co/import/project?template=https://github.com/zeit/now/tree/master/examples/docz)
_Live Example: https://docz.now-examples.now.sh_

View File

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

View File

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

View File

@@ -6,7 +6,7 @@ This directory is a brief example of a [Foundation](https://foundation.zurb.com/
Deploy your own Foundation project with ZEIT Now.
[![Deploy with ZEIT Now](https://zeit.co/button)](https://zeit.co/new/project?template=https://github.com/zeit/now/tree/master/examples/foundation)
[![Deploy with ZEIT Now](https://zeit.co/button)](https://zeit.co/import/project?template=https://github.com/zeit/now/tree/master/examples/foundation)
_Live Example: https://foundation.now-examples.now.sh_

View File

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

View File

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

View File

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

View File

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

View File

@@ -6,7 +6,7 @@ This directory is a brief example of a [HyperApp](https://github.com/jorgebucara
Deploy your own HyperApp project with ZEIT Now.
[![Deploy with ZEIT Now](https://zeit.co/button)](https://zeit.co/new/project?template=https://github.com/zeit/now/tree/master/examples/hyperapp)
[![Deploy with ZEIT Now](https://zeit.co/button)](https://zeit.co/import/project?template=https://github.com/zeit/now/tree/master/examples/hyperapp)
_Live Example: https://hyperapp.now-examples.now.sh_

View File

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

View File

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

View File

@@ -6,7 +6,7 @@ This directory is a brief example of a [Marko.js](https://markojs.com/) app that
Deploy your own Marko.js project with ZEIT Now.
[![Deploy with ZEIT Now](https://zeit.co/button)](https://zeit.co/new/project?template=https://github.com/zeit/now/tree/master/examples/marko)
[![Deploy with ZEIT Now](https://zeit.co/button)](https://zeit.co/import/project?template=https://github.com/zeit/now/tree/master/examples/marko)
_Live Example: https://marko.now-examples.now.sh_

View File

@@ -6,7 +6,7 @@ This directory is a brief example of a [mdx-deck](https://github.com/jxnblk/mdx-
Deploy your own mdx-deck project with ZEIT Now.
[![Deploy with ZEIT Now](https://zeit.co/button)](https://zeit.co/new/project?template=https://github.com/zeit/now/tree/master/examples/mdx-deck)
[![Deploy with ZEIT Now](https://zeit.co/button)](https://zeit.co/import/project?template=https://github.com/zeit/now/tree/master/examples/mdx-deck)
_Live Example: https://mdx-deck.now-examples.now.sh_

View File

@@ -6,7 +6,7 @@ This directory is a brief example of a [Metalsmith](https://metalsmith.io/) app
Deploy your own Metalsmith project with ZEIT Now.
[![Deploy with ZEIT Now](https://zeit.co/button)](https://zeit.co/new/project?template=https://github.com/zeit/now/tree/master/examples/metalsmith)
[![Deploy with ZEIT Now](https://zeit.co/button)](https://zeit.co/import/project?template=https://github.com/zeit/now/tree/master/examples/metalsmith)
_Live Example: https://metalsmith.now-examples.now.sh_

View File

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

View File

@@ -6,7 +6,7 @@ This directory is a brief example of a [Mithril](https://mithril.js.org/) app th
Deploy your own Mithril project with ZEIT Now.
[![Deploy with ZEIT Now](https://zeit.co/button)](https://zeit.co/new/project?template=https://github.com/zeit/now/tree/master/examples/mithril)
[![Deploy with ZEIT Now](https://zeit.co/button)](https://zeit.co/import/project?template=https://github.com/zeit/now/tree/master/examples/mithril)
_Live Example: https://mithril.now-examples.now.sh_

View File

@@ -6,7 +6,7 @@ This directory is a brief example of a [MkDocs](https://www.mkdocs.org/) site th
Deploy your own MkDocs project with ZEIT Now.
[![Deploy with ZEIT Now](https://zeit.co/button)](https://zeit.co/new/project?template=https://github.com/zeit/now/tree/master/examples/mkdocs)
[![Deploy with ZEIT Now](https://zeit.co/button)](https://zeit.co/import/project?template=https://github.com/zeit/now/tree/master/examples/mkdocs)
_Live Example: https://mkdocs.now-examples.now.sh_

View File

@@ -8,7 +8,7 @@ This directory is a brief example of a [Next.js](https://nextjs.org) app that ca
Deploy your own Next.js project with ZEIT Now.
[![Deploy with ZEIT Now](https://zeit.co/button)](https://zeit.co/new/project?template=https://github.com/zeit/now/tree/master/examples/nextjs)
[![Deploy with ZEIT Now](https://zeit.co/button)](https://zeit.co/import/project?template=https://github.com/zeit/now/tree/master/examples/nextjs)
_Live Example: https://nextjs.now-examples.now.sh_

View File

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

View File

@@ -6,7 +6,7 @@ This directory is a brief example of a [Pelican](https://docs.getpelican.com/en/
Deploy your own Pelican project with ZEIT Now.
[![Deploy with ZEIT Now](https://zeit.co/button)](https://zeit.co/new/project?template=https://github.com/zeit/now/tree/master/examples/pelican)
[![Deploy with ZEIT Now](https://zeit.co/button)](https://zeit.co/import/project?template=https://github.com/zeit/now/tree/master/examples/pelican)
_Live Example: https://pelican.now-examples.now.sh_

View File

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

View File

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

View File

@@ -6,7 +6,7 @@ This directory is a brief example of a [Riot.js](https://riot.js.org/) app that
Deploy your own Riot.js project with ZEIT Now.
[![Deploy with ZEIT Now](https://zeit.co/button)](https://zeit.co/new/project?template=https://github.com/zeit/now/tree/master/examples/riot)
[![Deploy with ZEIT Now](https://zeit.co/button)](https://zeit.co/import/project?template=https://github.com/zeit/now/tree/master/examples/riot)
_Live Example: https://riot.now-examples.now.sh_

View File

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

View File

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

View File

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

View File

@@ -6,7 +6,7 @@ This directory is a brief example of a [Storybook](https://storybook.js.org/) ap
Deploy your own Storybook project with ZEIT Now.
[![Deploy with ZEIT Now](https://zeit.co/button)](https://zeit.co/new/project?template=https://github.com/zeit/now/tree/master/examples/storybook)
[![Deploy with ZEIT Now](https://zeit.co/button)](https://zeit.co/import/project?template=https://github.com/zeit/now/tree/master/examples/storybook)
_Live Example: https://storybook.now-examples.now.sh_

View File

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

View File

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

View File

@@ -6,7 +6,7 @@ This directory is a brief example of a vanilla site that can be deployed with ZE
Deploy your own vanilla website, along with Serverless Functions, with ZEIT Now.
[![Deploy with ZEIT Now](https://zeit.co/button)](https://zeit.co/new/project?template=https://github.com/zeit/now/tree/master/examples/vanilla)
[![Deploy with ZEIT Now](https://zeit.co/button)](https://zeit.co/import/project?template=https://github.com/zeit/now/tree/master/examples/vanilla)
_Live Example: https://vanilla.now-examples.now.sh_

View File

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

View File

@@ -6,7 +6,7 @@ This directory is a brief example of a [VuePress](https://vuepress.vuejs.org/) a
Deploy your own VuePress project with ZEIT Now.
[![Deploy with ZEIT Now](https://zeit.co/button)](https://zeit.co/new/project?template=https://github.com/zeit/now/tree/master/examples/vuepress)
[![Deploy with ZEIT Now](https://zeit.co/button)](https://zeit.co/import/project?template=https://github.com/zeit/now/tree/master/examples/vuepress)
_Live Example: https://vuepress.now-examples.now.sh_

View File

@@ -6,7 +6,7 @@ This directory is a brief example of a [Zola](https://www.getzola.org/) site tha
Deploy your own Zola project with ZEIT Now.
[![Deploy with ZEIT Now](https://zeit.co/button)](https://zeit.co/new/project?template=https://github.com/zeit/now/tree/master/examples/zola)
[![Deploy with ZEIT Now](https://zeit.co/button)](https://zeit.co/import/project?template=https://github.com/zeit/now/tree/master/examples/zola)
_Live Example: https://zola.now-examples.now.sh_

View File

@@ -1,6 +1,6 @@
{
"name": "@now/frameworks",
"version": "0.0.10-canary.0",
"version": "0.0.10",
"main": "frameworks.json",
"license": "UNLICENSED"
}

View File

@@ -1,6 +1,6 @@
{
"name": "@now/build-utils",
"version": "2.0.0-canary.1",
"version": "2.0.1-canary.3",
"license": "MIT",
"main": "./dist/index.js",
"types": "./dist/index.d.js",

View File

@@ -80,6 +80,7 @@ export async function detectBuilders(
warnings: ErrorResponse[];
defaultRoutes: Route[] | null;
redirectRoutes: Route[] | null;
rewriteRoutes: Route[] | null;
}> {
const errors: ErrorResponse[] = [];
const warnings: ErrorResponse[] = [];
@@ -87,9 +88,6 @@ export async function detectBuilders(
const apiBuilders: Builder[] = [];
let frontendBuilder: Builder | null = null;
const defaultRoutes: Route[] = [];
const redirectRoutes: Route[] = [];
const functionError = validateFunctions(options);
if (functionError) {
@@ -99,6 +97,7 @@ export async function detectBuilders(
warnings,
defaultRoutes: null,
redirectRoutes: null,
rewriteRoutes: null,
};
}
@@ -131,15 +130,15 @@ export async function detectBuilders(
let fallbackEntrypoint: string | null = null;
const preDefaultRoutes: Source[] = [];
const preDynamicRoutes: Source[] = [];
const apiRoutes: Source[] = [];
const dynamicRoutes: Source[] = [];
// API
for (const fileName of sortedFiles) {
const apiBuilder = maybeGetApiBuilder(fileName, apiMatches, options);
if (apiBuilder) {
const { routeError, defaultRoute, dynamicRoute } = getApiRoute(
const { routeError, apiRoute, isDynamic } = getApiRoute(
fileName,
apiSortedFiles,
options,
@@ -153,15 +152,15 @@ export async function detectBuilders(
warnings,
defaultRoutes: null,
redirectRoutes: null,
rewriteRoutes: null,
};
}
if (dynamicRoute) {
preDynamicRoutes.push(dynamicRoute);
}
if (defaultRoute) {
preDefaultRoutes.push(defaultRoute);
if (apiRoute) {
apiRoutes.push(apiRoute);
if (isDynamic) {
dynamicRoutes.push(apiRoute);
}
}
addToUsedFunctions(apiBuilder);
@@ -224,6 +223,7 @@ export async function detectBuilders(
builders: null,
redirectRoutes: null,
defaultRoutes: null,
rewriteRoutes: null,
};
}
@@ -264,6 +264,7 @@ export async function detectBuilders(
warnings,
redirectRoutes: null,
defaultRoutes: null,
rewriteRoutes: null,
};
}
@@ -285,24 +286,22 @@ export async function detectBuilders(
}
}
const routesResult = mergeRoutes(
preDefaultRoutes,
preDynamicRoutes,
const routesResult = getRouteResult(
apiRoutes,
dynamicRoutes,
usedOutputDirectory,
apiBuilders,
frontendBuilder,
options
);
defaultRoutes.push(...routesResult.defaultRoutes);
redirectRoutes.push(...routesResult.redirectRoutes);
return {
warnings,
builders: builders.length ? builders : null,
errors: errors.length ? errors : null,
redirectRoutes,
defaultRoutes,
redirectRoutes: routesResult.redirectRoutes,
defaultRoutes: routesResult.defaultRoutes,
rewriteRoutes: routesResult.rewriteRoutes,
};
}
@@ -622,16 +621,16 @@ function getApiRoute(
options: Options,
absolutePathCache: Map<string, string>
): {
defaultRoute: Source | null;
dynamicRoute: Source | null;
apiRoute: Source | null;
isDynamic: boolean;
routeError: ErrorResponse | null;
} {
const conflictingSegment = getConflictingSegment(fileName);
if (conflictingSegment) {
return {
defaultRoute: null,
dynamicRoute: null,
apiRoute: null,
isDynamic: false,
routeError: {
code: 'conflicting_path_segment',
message:
@@ -650,8 +649,8 @@ function getApiRoute(
);
return {
defaultRoute: null,
dynamicRoute: null,
apiRoute: null,
isDynamic: false,
routeError: {
code: 'conflicting_file_path',
message:
@@ -669,8 +668,8 @@ function getApiRoute(
);
return {
defaultRoute: out.route,
dynamicRoute: out.isDynamic ? out.route : null,
apiRoute: out.route,
isDynamic: out.isDynamic,
routeError: null,
};
}
@@ -835,10 +834,10 @@ function createRouteFromPath(
} else if (isLast) {
const { name: fileName, ext } = parsePath(segment);
const isIndex = fileName === 'index';
const prefix = isIndex ? '\\/' : '';
const prefix = isIndex ? '/' : '';
const names = [
isIndex ? prefix : `${fileName}\\/`,
isIndex ? prefix : `${fileName}/`,
prefix + escapeName(fileName),
featHandleMiss && cleanUrls
? ''
@@ -881,9 +880,9 @@ function createRouteFromPath(
return { route, isDynamic };
}
function mergeRoutes(
preDefaultRoutes: Source[],
preDynamicRoutes: Source[],
function getRouteResult(
apiRoutes: Source[],
dynamicRoutes: Source[],
outputDirectory: string,
apiBuilders: Builder[],
frontendBuilder: Builder | null,
@@ -891,14 +890,14 @@ function mergeRoutes(
): {
defaultRoutes: Route[];
redirectRoutes: Route[];
rewriteRoutes: Route[];
} {
const defaultRoutes: Route[] = [];
const redirectRoutes: Route[] = [];
const rewriteRoutes: Route[] = [];
if (preDefaultRoutes && preDefaultRoutes.length > 0) {
if (apiRoutes && apiRoutes.length > 0) {
if (options.featHandleMiss) {
defaultRoutes.push({ handle: 'miss' });
const extSet = detectApiExtensions(apiBuilders);
if (extSet.size > 0) {
@@ -923,6 +922,7 @@ function mergeRoutes(
status: 308,
});
} else {
defaultRoutes.push({ handle: 'miss' });
defaultRoutes.push({
src: `^/api/(.+)${extGroup}$`,
dest: '/api/$1',
@@ -931,21 +931,16 @@ function mergeRoutes(
}
}
if (preDynamicRoutes) {
defaultRoutes.push(...preDynamicRoutes);
}
if (preDefaultRoutes.length) {
defaultRoutes.push({
src: '^/api(/.*)?$',
status: 404,
continue: true,
});
}
rewriteRoutes.push(...dynamicRoutes);
rewriteRoutes.push({
src: '^/api(/.*)?$',
status: 404,
continue: true,
});
} else {
defaultRoutes.push(...preDefaultRoutes);
defaultRoutes.push(...apiRoutes);
if (preDefaultRoutes.length) {
if (apiRoutes.length) {
defaultRoutes.push({
status: 404,
src: '^/api(/.*)?$',
@@ -969,6 +964,7 @@ function mergeRoutes(
return {
defaultRoutes,
redirectRoutes,
rewriteRoutes,
};
}

View File

@@ -206,10 +206,10 @@ export async function runNpmInstall(
debug(`Installing to ${destPath}`);
const { hasPackageLockJson } = await scanParentDirs(destPath);
const opts = { cwd: destPath, ...spawnOpts } || {
cwd: destPath,
env: process.env,
};
const opts: SpawnOptions = { cwd: destPath, ...spawnOpts };
const env = opts.env || { ...process.env };
delete env.NODE_ENV;
opts.env = env;
if (hasPackageLockJson) {
commandArgs = args.filter(a => a !== '--prefer-offline');
@@ -239,10 +239,7 @@ export async function runBundleInstall(
}
assert(path.isAbsolute(destPath));
const opts = { cwd: destPath, ...spawnOpts } || {
cwd: destPath,
env: process.env,
};
const opts = { cwd: destPath, ...spawnOpts };
await spawnAsync(
'bundle',
@@ -270,10 +267,7 @@ export async function runPipInstall(
}
assert(path.isAbsolute(destPath));
const opts = { cwd: destPath, ...spawnOpts } || {
cwd: destPath,
env: process.env,
};
const opts = { cwd: destPath, ...spawnOpts };
await spawnAsync(
'pip3',

View File

@@ -4,20 +4,28 @@ import FileRef from './file-ref';
import { Lambda } from './lambda';
interface PrerenderOptions {
expiration: number;
expiration: number | false;
lambda: Lambda;
fallback: FileBlob | FileFsRef | FileRef | null;
group?: number;
bypassToken?: string | null /* optional to be non-breaking change */;
}
export class Prerender {
public type: 'Prerender';
public expiration: number;
public expiration: number | false;
public lambda: Lambda;
public fallback: FileBlob | FileFsRef | FileRef | null;
public group?: number;
public bypassToken: string | null;
constructor({ expiration, lambda, fallback, group }: PrerenderOptions) {
constructor({
expiration,
lambda,
fallback,
group,
bypassToken,
}: PrerenderOptions) {
this.type = 'Prerender';
this.expiration = expiration;
this.lambda = lambda;
@@ -32,6 +40,22 @@ export class Prerender {
}
this.group = group;
if (bypassToken == null) {
this.bypassToken = null;
} else if (typeof bypassToken === 'string') {
if (bypassToken.length < 32) {
// Enforce 128 bits of entropy for safety reasons (UUIDv4 size)
throw new Error(
'The `bypassToken` argument for `Prerender` must be 32 characters or more.'
);
}
this.bypassToken = bypassToken;
} else {
throw new Error(
'The `bypassToken` argument for `Prerender` must be a `string`.'
);
}
if (typeof fallback === 'undefined') {
throw new Error(
'The `fallback` argument for `Prerender` needs to be a `FileBlob`, `FileFsRef`, `FileRef`, or null.'

View File

@@ -0,0 +1,21 @@
function checkPkgOrThrow(pkgname) {
try {
const dep = require(pkgname);
if (!dep) {
throw new Error('Undefined');
}
} catch (e) {
console.error(`Expected package "${pkgname}" to be installed.`);
process.exit(1);
}
}
// We expect both `dependencies` and `devDependencies` to be installed
// even when NODE_ENV=production.
checkPkgOrThrow('tls-check');
checkPkgOrThrow('exeggcute');
// This is to satisfy `@now/static-build` which needs a `dist` directory.
const { exec } = require('exeggcute');
exec('mkdir dist', __dirname);
exec('echo "node-env:RANDOMNESS_PLACEHOLDER" > dist/index.html', __dirname);

View File

@@ -0,0 +1,6 @@
{
"version": 2,
"builds": [{ "src": "package.json", "use": "@now/static-build" }],
"build": { "env": { "NODE_ENV": "production" } },
"probes": [{ "path": "/", "mustContain": "node-env:RANDOMNESS_PLACEHOLDER" }]
}

View File

@@ -0,0 +1,12 @@
{
"private": true,
"scripts": {
"build": "node build.js"
},
"dependencies": {
"tls-check": "1.0.0"
},
"devDependencies": {
"exeggcute": "1.0.0"
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
{
"name": "now",
"version": "17.0.4-canary.3",
"version": "17.0.5-canary.5",
"preferGlobal": true,
"license": "Apache-2.0",
"description": "The command-line interface for Now",

View File

@@ -566,6 +566,11 @@ export default async function main(
return 1;
}
if (deployment.readyState === 'CANCELED') {
output.log('The deployment has been canceled');
return 1;
}
const deploymentResponse = await getDeploymentByIdOrHost(
now,
contextName,
@@ -652,7 +657,12 @@ export default async function main(
output.error('Build failed');
output.error(
`Check your logs at ${now.url}/_logs or run ${code(
`now logs ${now.url}`
`now logs ${now.url}`,
{
// Backticks are interpreted as part of the URL, causing CMD+Click
// behavior to fail in editors like VSCode.
backticks: false,
}
)}`
);

View File

@@ -202,6 +202,13 @@ export default async function processDeployment({
}
}
if (event.type === 'canceled') {
if (buildSpinner) {
buildSpinner();
}
return event.payload;
}
if (event.type === 'ready') {
if (queuedSpinner) {
queuedSpinner();

View File

@@ -133,6 +133,9 @@ export async function devRouter(
);
if (missResult.found) {
return missResult;
} else {
reqPathname = destPath;
continue;
}
} else {
if (routeConfig.status && phase === 'miss') {

View File

@@ -12,7 +12,11 @@ import serveHandler from 'serve-handler';
import { watch, FSWatcher } from 'chokidar';
import { parse as parseDotenv } from 'dotenv';
import { basename, dirname, extname, join } from 'path';
import { getTransformedRoutes, HandleValue } from '@now/routing-utils';
import {
getTransformedRoutes,
appendRoutesToPhase,
HandleValue,
} from '@now/routing-utils';
import directoryTemplate from 'serve-handler/src/directory';
import getPort from 'get-port';
import { ChildProcess } from 'child_process';
@@ -553,6 +557,7 @@ export default class DevServer {
errors,
defaultRoutes,
redirectRoutes,
rewriteRoutes,
} = await detectBuilders(files, pkg, {
tag: getDistTag(cliVersion) === 'canary' ? 'canary' : 'latest',
functions: config.functions,
@@ -582,7 +587,13 @@ export default class DevServer {
const routes: RouteConfig[] = [];
const { routes: nowConfigRoutes } = config;
routes.push(...(redirectRoutes || []));
routes.push(...(nowConfigRoutes || []));
routes.push(
...appendRoutesToPhase({
routes: nowConfigRoutes,
newRoutes: rewriteRoutes,
phase: 'filesystem',
})
);
routes.push(...(defaultRoutes || []));
config.routes = routes;
}
@@ -1176,6 +1187,35 @@ export default class DevServer {
}
};
/**
* This is the equivalent to now-proxy exit_with_status() function.
*/
exitWithStatus = async (
match: BuildMatch | null,
routeResult: RouteResult,
phase: HandleValue | null,
req: http.IncomingMessage,
res: http.ServerResponse,
nowRequestId: string
): Promise<boolean> => {
const { status, headers, dest } = routeResult;
const location = headers['location'] || dest;
if (status && location && (300 <= status && status <= 399)) {
this.output.debug(`Route found with redirect status code ${status}`);
await this.sendRedirect(req, res, nowRequestId, location, status);
return true;
}
if (!match && status && phase !== 'miss') {
this.output.debug(`Route found with with status code ${status}`);
await this.sendError(req, res, nowRequestId, '', status);
return true;
}
return false;
};
/**
* Serve project directory as a Now v2 deployment.
*/
@@ -1259,6 +1299,19 @@ export default class DevServer {
this
);
if (
await this.exitWithStatus(
match,
routeResult,
phase,
req,
res,
nowRequestId
)
) {
return;
}
if (!match && missRoutes.length > 0) {
// Since there was no build match, enter the miss phase
routeResult = await devRouter(
@@ -1277,6 +1330,18 @@ export default class DevServer {
routeResult.dest,
this
);
if (
await this.exitWithStatus(
match,
routeResult,
phase,
req,
res,
nowRequestId
)
) {
return;
}
} else if (match && hitRoutes.length > 0) {
// Since there was a build match, enter the hit phase.
// The hit phase must not set status code.
@@ -1295,28 +1360,6 @@ export default class DevServer {
statusCode = routeResult.status;
if (match && statusCode === 404 && routeResult.phase === 'miss') {
statusCode = undefined;
}
const location = routeResult.headers['location'] || routeResult.dest;
if (statusCode && location && (300 <= statusCode && statusCode <= 399)) {
// Equivalent to now-proxy exit_with_status() function
this.output.debug(
`Route found with redirect status code ${statusCode}`
);
await this.sendRedirect(req, res, nowRequestId, location, statusCode);
return;
}
if (!match && statusCode && routeResult.phase !== 'miss') {
// Equivalent to now-proxy exit_with_status() function
this.output.debug(`Route found with with status code ${statusCode}`);
await this.sendError(req, res, nowRequestId, '', statusCode);
return;
}
if (match) {
// end the phase
break;

View File

@@ -4,6 +4,7 @@ import chalk from 'chalk';
// The equivalent of <code>, for embedding anything
// you may want to take a look at ./cmd.js
export default function code(cmd: string): string {
return `${chalk.gray('`')}${chalk.bold(cmd)}${chalk.gray('`')}`;
export default function code(cmd: string, { backticks = true } = {}): string {
const tick = backticks ? chalk.gray('`') : '';
return `${tick}${chalk.bold(cmd)}${tick}`;
}

View File

@@ -1,10 +0,0 @@
{
"version": 2,
"routes": [
{ "handle": "filesystem" },
{ "src": "/([^/]+/dir/.+)", "dest": "/$1.html", "check": true },
{ "handle": "miss" },
{ "src": "/pathA(?:/.+)?", "status": 404, "continue": true },
{ "src": "/pathB(?:/.+)?", "status": 404, "continue": true }
]
}

View File

@@ -10,14 +10,14 @@
"dest": "/blog/$1",
"check": true
},
{
"handle": "miss"
},
{
"src": "/.*",
"status": 404,
"continue": true
},
{
"handle": "miss"
},
{
"src": "/(.*)",
"dest": "/src/$1",

View File

@@ -1,8 +0,0 @@
{
"version": 2,
"routes": [
{ "handle": "miss" },
{ "src": "/pathA(?:/.+)?", "status": 404, "continue": true },
{ "src": "/pathB(?:/.+)?", "status": 404, "continue": true }
]
}

View File

@@ -1,7 +0,0 @@
{
"functions": {
"server/**/*.js": {
"runtime": "@now/node@1.2.1"
}
}
}

View File

@@ -1,3 +0,0 @@
export default (req, res) => {
res.end(`current hour: ${Math.floor(Date.now() / 10000)}`);
};

View File

@@ -0,0 +1,2 @@
module.exports = (req, res) =>
res.send(req.query.username);

View File

@@ -0,0 +1 @@
module.exports = (req, res) => res.end('42');

View File

@@ -0,0 +1,2 @@
module.exports = (req, res) =>
res.send(req.query.id);

View File

@@ -0,0 +1,3 @@
{
"rewrites": [{ "source": "/rand", "destination": "/api/fourty-two" }]
}

View File

@@ -411,28 +411,6 @@ test(
})
);
test(
'[now dev] does not display directory listing after multiple 404',
testFixtureStdio('handle-miss-multiple-404', async (t, port) => {
t.is((await fetch(`http://localhost:${port}/pathA/dir`)).status, 404);
t.is((await fetch(`http://localhost:${port}/pathB/dir`)).status, 404);
t.is((await fetch(`http://localhost:${port}/pathC/dir`)).status, 200);
})
);
test(
'[now dev] does not display directory listing after `handle: miss` and 404',
testFixtureStdio('handle-miss-handle-filesystem-404', async (t, port) => {
t.is((await fetch(`http://localhost:${port}/pathA/dir`)).status, 404);
t.is((await fetch(`http://localhost:${port}/pathB/dir`)).status, 404);
t.is((await fetch(`http://localhost:${port}/pathC/dir`)).status, 200);
t.is((await fetch(`http://localhost:${port}/pathA/dir/one`)).status, 200);
t.is((await fetch(`http://localhost:${port}/pathB/dir/two`)).status, 200);
t.is((await fetch(`http://localhost:${port}/pathC/dir/three`)).status, 200);
})
);
test(
'[now dev] handles hit after handle: filesystem',
testFixtureStdio('handle-hit-after-fs', async (t, port) => {
@@ -483,6 +461,8 @@ test(
t.regex(await rand.text(), /random number/gm);
const rand2 = await fetchWithRetry(`http://localhost:${port}/api/rand.js`);
t.regex(await rand2.text(), /random number/gm);
const notfound = await fetch(`http://localhost:${port}/api`);
t.is(notfound.status, 404);
})
);
@@ -606,6 +586,31 @@ test('[now dev] validate env var names', async t => {
t.pass();
});
test(
'[now dev] test rewrites with segments serve correct content',
testFixtureStdio('test-rewrites-with-segments', async (t, port) => {
const users = await fetchWithRetry(
`http://localhost:${port}/api/users/first`,
3
);
t.regex(await users.text(), /first/gm);
const fourtytwo = await fetchWithRetry(
`http://localhost:${port}/api/fourty-two`,
3
);
t.regex(await fourtytwo.text(), /42/gm);
const rand = await fetchWithRetry(`http://localhost:${port}/rand`, 3);
t.regex(await rand.text(), /42/gm);
const dynamic = await fetchWithRetry(
`http://localhost:${port}/api/dynamic`,
3
);
t.regex(await dynamic.text(), /dynamic/gm);
const notfound = await fetch(`http://localhost:${port}/api`);
t.is(notfound.status, 404);
})
);
test(
'[now dev] test rewrites serve correct content',
testFixtureStdio('test-rewrites', async (t, port) => {

View File

@@ -156,33 +156,6 @@ RUN mkdir /public
RUN echo hello > /public/index.html
`,
},
'build-env': {
'now.json': JSON.stringify({
version: 1,
type: 'static',
build: {
env: { FOO: 'bar' },
},
}),
Dockerfile: `
FROM alpine
ARG FOO
RUN mkdir /public
RUN echo $FOO > /public/index.html
`,
},
'build-env-arg': {
'now.json': JSON.stringify({
version: 1,
type: 'static',
}),
Dockerfile: `
FROM alpine
ARG NONCE
RUN mkdir /public
RUN echo $NONCE > /public/index.html
`,
},
'build-env-debug': {
'now.json':
'{ "builds": [ { "src": "index.js", "use": "@now/node" } ], "version": 2 }',

View File

@@ -1859,79 +1859,6 @@ test('deploy a static build deployment', async t => {
t.is(content.trim(), 'hello');
});
test('use build-env', async t => {
const directory = fixture('build-env');
const { stdout, stderr, exitCode } = await execa(
binaryPath,
[directory, '--public', '--name', session, ...defaultArgs],
{
reject: false,
}
);
console.log(stderr);
console.log(stdout);
console.log(exitCode);
// Ensure the exit code is right
t.is(exitCode, 0);
// Test if the output is really a URL
const deploymentUrl = pickUrl(stdout);
const { href, host } = new URL(deploymentUrl);
t.is(host.split('-')[0], session);
await waitForDeployment(href);
// get the content
const response = await fetch(href);
const content = await response.text();
t.is(content.trim(), 'bar');
});
test('use `--build-env` CLI flag', async t => {
const directory = fixture('build-env-arg');
const nonce = Math.random()
.toString(36)
.substring(2);
const { stderr, stdout, exitCode } = await execa(
binaryPath,
[
directory,
'--public',
'--name',
session,
'--build-env',
`NONCE=${nonce}`,
...defaultArgs,
],
{
reject: false,
}
);
console.log(stderr);
console.log(stdout);
console.log(exitCode);
// Ensure the exit code is right
t.is(exitCode, 0, `Received:\n"${stderr}"\n"${stdout}"`);
// Test if the output is really a URL
const deploymentUrl = pickUrl(stdout);
const { href, host } = new URL(deploymentUrl);
t.is(host.split('-')[0], session);
await waitForDeployment(href);
// get the content
const response = await fetch(href);
const content = await response.text();
t.is(content.trim(), nonce);
});
test('use `--debug` CLI flag', async t => {
const directory = fixture('build-env-debug');

View File

@@ -1329,80 +1329,6 @@ test('deploying more than 1 path should fail', async t => {
t.true(stderr.trim().endsWith(`Can't deploy more than one path.`));
});
test('use build-env', async t => {
const directory = fixture('build-env');
const { stdout, stderr, exitCode } = await execa(
binaryPath,
[directory, '--public', '--name', session, ...defaultArgs, '--confirm'],
{
reject: false,
}
);
console.log(stderr);
console.log(stdout);
console.log(exitCode);
// Ensure the exit code is right
t.is(exitCode, 0);
// Test if the output is really a URL
const deploymentUrl = pickUrl(stdout);
const { href, host } = new URL(deploymentUrl);
t.is(host.split('-')[0], session);
await waitForDeployment(href);
// get the content
const response = await fetch(href);
const content = await response.text();
t.is(content.trim(), 'bar');
});
test('use `--build-env` CLI flag', async t => {
const directory = fixture('build-env-arg');
const nonce = Math.random()
.toString(36)
.substring(2);
const { stderr, stdout, exitCode } = await execa(
binaryPath,
[
directory,
'--public',
'--name',
session,
'--build-env',
`NONCE=${nonce}`,
...defaultArgs,
'--confirm',
],
{
reject: false,
}
);
console.log(stderr);
console.log(stdout);
console.log(exitCode);
// Ensure the exit code is right
t.is(exitCode, 0, `Received:\n"${stderr}"\n"${stdout}"`);
// Test if the output is really a URL
const deploymentUrl = pickUrl(stdout);
const { href, host } = new URL(deploymentUrl);
t.is(host.split('-')[0], session);
await waitForDeployment(href);
// get the content
const response = await fetch(href);
const content = await response.text();
t.is(content.trim(), nonce);
});
test('use `--debug` CLI flag', async t => {
const directory = fixture('build-env-debug');

View File

@@ -69,9 +69,7 @@ const getStaticFiles = async (dir, isBuilds = false) => {
const normalizeWindowsPaths = files => {
if (process.platform === 'win32') {
const prefix = 'D:/a/now/now/packages/now-cli/test/fixtures/unit/';
return files.map(f =>
f.replace(/\\/g, '/').slice(prefix.length)
);
return files.map(f => f.replace(/\\/g, '/').slice(prefix.length));
}
return files;
};

View File

@@ -1,6 +1,6 @@
{
"name": "now-client",
"version": "7.0.0",
"version": "7.0.1-canary.1",
"main": "dist/index.js",
"typings": "dist/index.d.ts",
"homepage": "https://zeit.co",

View File

@@ -77,6 +77,15 @@ export async function* checkDeploymentStatus(
yield { type: 'building', payload: deploymentUpdate };
}
if (
deploymentUpdate.readyState === 'CANCELED' &&
!finishedEvents.has('canceled')
) {
debug('Deployment state changed to CANCELED');
finishedEvents.add('canceled');
yield { type: 'canceled', payload: deploymentUpdate };
}
if (isReady(deploymentUpdate) && !finishedEvents.has('ready')) {
debug('Deployment state changed to READY');
finishedEvents.add('ready');

View File

@@ -2,7 +2,7 @@ import { DeploymentFile } from './hashes';
import { parse as parseUrl } from 'url';
import { FetchOptions } from '@zeit/fetch';
import { nodeFetch, zeitFetch } from './fetch';
import { join, sep } from 'path';
import { join, sep, relative } from 'path';
import qs from 'querystring';
import ignore from 'ignore';
import { pkgVersion } from '../pkg';
@@ -29,6 +29,7 @@ const EVENTS_ARRAY = [
'error',
'notice',
'tip',
'canceled',
] as const;
export type DeploymentEventType = (typeof EVENTS_ARRAY)[number];
@@ -191,9 +192,10 @@ export const prepareFiles = (
if (clientOptions.isDirectory) {
// Directory
fileName = clientOptions.path
? name.substring(clientOptions.path.length + 1)
: name;
fileName =
typeof clientOptions.path === 'string'
? relative(clientOptions.path, name)
: name;
} else {
// Array of files or single file
const segments = name.split(sep);

View File

@@ -87,10 +87,12 @@ describe('create v2 deployment', () => {
});
it('will create a v2 deployment with correct file permissions', async () => {
let error = null;
for await (const event of createDeployment(
{
token,
path: path.resolve(__dirname, 'fixtures', 'v2-file-permissions'),
skipAutoDetectionConfirmation: true,
},
{
name: 'now-client-tests-v2',
@@ -104,9 +106,16 @@ describe('create v2 deployment', () => {
if (event.type === 'ready') {
deployment = event.payload;
break;
} else if (event.type === 'error') {
error = event.payload;
console.error(error.message);
break;
}
}
expect(error).toBe(null);
expect(deployment.readyState).toEqual('READY');
const url = `https://${deployment.url}/api/index.js`;
console.log('testing url ' + url);
const response = await fetch_(url);
@@ -116,10 +125,12 @@ describe('create v2 deployment', () => {
});
it('will create a v2 deployment and ignore files specified in .nowignore', async () => {
let error = null;
for await (const event of createDeployment(
{
token,
path: path.resolve(__dirname, 'fixtures', 'nowignore'),
skipAutoDetectionConfirmation: true,
},
{
name: 'now-client-tests-v2',
@@ -132,11 +143,17 @@ describe('create v2 deployment', () => {
)) {
if (event.type === 'ready') {
deployment = event.payload;
expect(deployment.readyState).toEqual('READY');
break;
} else if (event.type === 'error') {
error = event.payload;
console.error(error.message);
break;
}
}
expect(error).toBe(null);
expect(deployment.readyState).toEqual('READY');
const index = await fetch_(`https://${deployment.url}`);
expect(index.status).toBe(200);
expect(await index.text()).toBe('Hello World!');

View File

@@ -1,4 +1,4 @@
ncc build index.ts -o dist
ncc build install.ts -o dist/install
ncc build index.ts -e @now/build-utils -o dist
ncc build install.ts -e @now/build-utils -o dist/install
mv dist/install/index.js dist/install.js
rm -rf dist/install

View File

@@ -12,6 +12,7 @@ import {
shouldServe,
Files,
debug,
Meta,
} from '@now/build-utils';
import { createGo, getAnalyzedEntrypoint, OUT_EXTENSION } from './go-helpers';
@@ -40,12 +41,18 @@ async function initPrivateGit(credentials: string) {
* Since `go build` does not support files that begin with a square bracket,
* we must rename to something temporary to support Path Segments.
* The output file is not renamed because v3 builders can't rename outputs
* which works great for this feature.
* which works great for this feature. We also need to add a suffix during `now dev`
* since the entrypoint is already stripped of its suffix before build() is called.
*/
async function getRenamedEntrypoint(entrypoint: string, files: Files) {
async function getRenamedEntrypoint(
entrypoint: string,
files: Files,
meta: Meta
) {
const filename = basename(entrypoint);
if (filename.startsWith('[')) {
const newEntrypoint = entrypoint.replace('/[', '/now-bracket[');
const suffix = meta.isDev && !entrypoint.endsWith('.go') ? '.go' : '';
const newEntrypoint = entrypoint.replace('/[', '/now-bracket[') + suffix;
const file = files[entrypoint];
delete files[entrypoint];
files[newEntrypoint] = file;
@@ -81,7 +88,7 @@ We highly recommend you leverage Go Modules in your project.
Learn more: https://github.com/golang/go/wiki/Modules
`);
}
entrypoint = await getRenamedEntrypoint(entrypoint, files);
entrypoint = await getRenamedEntrypoint(entrypoint, files, meta);
const entrypointArr = entrypoint.split(sep);
// eslint-disable-next-line prefer-const
@@ -126,26 +133,16 @@ Learn more: https://zeit.co/docs/v2/advanced/builders/#go
const parsedAnalyzed = JSON.parse(analyzed) as Analyzed;
if (meta.isDev) {
let base = null;
if (config && config.zeroConfig) {
base = workPath;
} else {
base = dirname(downloadedFiles['now.json'].fsPath);
}
const destNow = join(
base,
// Create cache so Go rebuilds fast with `now dev`
goPath = join(
workPath,
'.now',
'cache',
basename(entrypoint, '.go'),
'src',
'lambda'
'now-go',
basename(entrypoint, '.go')
);
// this will ensure Go rebuilt fast
goPath = join(base, '.now', 'cache', basename(entrypoint, '.go'));
const destNow = join(goPath, 'src', 'lambda');
await download(downloadedFiles, destNow);
downloadedFiles = await glob('**', destNow);
downloadPath = destNow;
}

View File

@@ -1,6 +1,6 @@
{
"name": "@now/go",
"version": "1.0.4-canary.2",
"version": "1.0.5-canary.0",
"license": "MIT",
"main": "./dist/index",
"homepage": "https://zeit.co/docs/runtimes#official-runtimes/go",

View File

@@ -7,10 +7,10 @@ cp -v "$bridge_defs" src/now__bridge.ts
tsc
ncc build src/dev-server.ts -o dist/dev
ncc build src/dev-server.ts -e @now/build-utils -o dist/dev
mv dist/dev/index.js dist/dev-server.js
rm -rf dist/dev
ncc build src/index.ts -o dist/main
ncc build src/index.ts -e @now/build-utils -o dist/main
mv dist/main/index.js dist/index.js
rm -rf dist/main

View File

@@ -1,6 +1,6 @@
{
"name": "@now/next",
"version": "2.3.15",
"version": "2.3.19-canary.2",
"license": "MIT",
"main": "./dist/index",
"homepage": "https://zeit.co/docs/runtimes#official-runtimes/next-js",

View File

@@ -18,11 +18,11 @@ import {
runNpmInstall,
runPackageJsonScript,
} from '@now/build-utils';
import { Route, Source } from '@now/routing-utils';
import { Route } from '@now/routing-utils';
import {
convertHeaders,
convertRedirects,
convertRewrites,
convertHeaders,
} from '@now/routing-utils/dist/superstatic';
import nodeFileTrace, { NodeFileTraceReasons } from '@zeit/node-file-trace';
import { ChildProcess, fork } from 'child_process';
@@ -92,6 +92,8 @@ const nowDevChildProcesses = new Set<ChildProcess>();
});
});
const MAX_AGE_ONE_YEAR = 31536000;
/**
* Read package.json from files
*/
@@ -334,11 +336,17 @@ export const build = async ({
env.NODE_OPTIONS = `--max_old_space_size=${memoryToConsume}`;
await runPackageJsonScript(entryPath, shouldRunScript, { ...spawnOpts, env });
const appMountPrefixNoTrailingSlash = path.posix
.join('/', entryDirectory)
.replace(/\/+$/, '');
const routesManifest = await getRoutesManifest(entryPath, realNextVersion);
const prerenderManifest = await getPrerenderManifest(entryPath);
const headers: Route[] = [];
const rewrites: Route[] = [];
const redirects: Route[] = [];
const nextBasePathRoute: Route[] = [];
const dataRoutes: Route[] = [];
let nextBasePath: string | undefined;
// whether they have enabled pages/404.js as the custom 404 page
let hasPages404 = false;
@@ -354,6 +362,40 @@ export const build = async ({
headers.push(...convertHeaders(routesManifest.headers));
}
if (routesManifest.dataRoutes) {
// Load the /_next/data routes for both dynamic SSG and SSP pages.
// These must be combined and sorted to prevent conflicts
for (const dataRoute of routesManifest.dataRoutes) {
const ssgDataRoute =
prerenderManifest.fallbackRoutes[dataRoute.page] ||
prerenderManifest.legacyBlockingRoutes[dataRoute.page];
// we don't need to add routes for non-lazy SSG routes since
// they have outputs which would override the routes anyways
if (
prerenderManifest.staticRoutes[dataRoute.page] ||
prerenderManifest.omittedRoutes.includes(dataRoute.page)
) {
continue;
}
dataRoutes.push({
src: dataRoute.dataRouteRegex.replace(
/^\^/,
`^${appMountPrefixNoTrailingSlash}`
),
dest: path.join(
'/',
entryDirectory,
// make sure to route SSG data route to the data prerender
// output, we don't do this for SSP routes since they don't
// have a separate data output
(ssgDataRoute && ssgDataRoute.dataRoute) || dataRoute.page
),
});
}
}
if (routesManifest.pages404) {
hasPages404 = true;
}
@@ -455,7 +497,9 @@ export const build = async ({
),
// Next.js assets contain a hash or entropy in their filenames, so they
// are guaranteed to be unique and cacheable indefinitely.
headers: { 'cache-control': 'public,max-age=31536000,immutable' },
headers: {
'cache-control': `public,max-age=${MAX_AGE_ONE_YEAR},immutable`,
},
continue: true,
},
{
@@ -517,13 +561,8 @@ export const build = async ({
const prerenders: { [key: string]: Prerender | FileFsRef } = {};
const staticPages: { [key: string]: FileFsRef } = {};
const dynamicPages: string[] = [];
const dynamicDataRoutes: Array<Source> = [];
let static404Page: string | undefined;
const appMountPrefixNoTrailingSlash = path.posix
.join('/', entryDirectory)
.replace(/\/+$/, '');
if (isLegacy) {
const filesAfterBuild = await glob('**', entryPath);
@@ -625,7 +664,6 @@ export const build = async ({
const pages = await glob('**/*.js', pagesDir);
const staticPageFiles = await glob('**/*.html', pagesDir);
const prerenderManifest = await getPrerenderManifest(entryPath);
Object.keys(staticPageFiles).forEach((page: string) => {
const pathname = page.replace(/\.html$/, '');
@@ -636,9 +674,8 @@ export const build = async ({
// Lazily prerendered routes have a fallback `.html` file on newer
// Next.js versions so we need to also not treat it as a static page here.
if (
prerenderManifest.routes[routeName] ||
(prerenderManifest.lazyRoutes[routeName] &&
prerenderManifest.lazyRoutes[routeName].fallback)
prerenderManifest.staticRoutes[routeName] ||
prerenderManifest.fallbackRoutes[routeName]
) {
return;
}
@@ -880,33 +917,49 @@ export const build = async ({
}
let prerenderGroup = 1;
const onPrerenderRoute = (routeKey: string, isLazy: boolean) => {
const onPrerenderRoute = (
routeKey: string,
{ isBlocking, isFallback }: { isBlocking: boolean; isFallback: boolean }
) => {
if (isBlocking && isFallback) {
throw new Error(
'invariant: isBlocking and isFallback cannot both be true'
);
}
// Get the route file as it'd be mounted in the builder output
const routeFileNoExt = routeKey === '/' ? '/index' : routeKey;
const lazyHtmlFallback =
isLazy && prerenderManifest.lazyRoutes[routeKey].fallback;
const htmlFsRef =
isLazy && !lazyHtmlFallback
const htmlFsRef = isBlocking
? // Blocking pages do not have an HTML fallback
null
: new FileFsRef({
fsPath: path.join(
pagesDir,
isFallback
? // Fallback pages have a special file.
prerenderManifest.fallbackRoutes[routeKey].fallback
: // Otherwise, the route itself should exist as a static HTML
// file.
`${routeFileNoExt}.html`
),
});
const jsonFsRef =
// JSON data does not exist for fallback or blocking pages
isFallback || isBlocking
? null
: new FileFsRef({
fsPath: path.join(
pagesDir,
`${lazyHtmlFallback || routeFileNoExt + '.html'}`
),
fsPath: path.join(pagesDir, `${routeFileNoExt}.json`),
});
const jsonFsRef = isLazy
? null
: new FileFsRef({
fsPath: path.join(pagesDir, `${routeFileNoExt}.json`),
});
let initialRevalidate: false | number;
let srcRoute: string | null;
let dataRoute: string;
if (isLazy) {
const pr = prerenderManifest.lazyRoutes[routeKey];
if (isFallback || isBlocking) {
const pr = isFallback
? prerenderManifest.fallbackRoutes[routeKey]
: prerenderManifest.legacyBlockingRoutes[routeKey];
initialRevalidate = 1; // TODO: should Next.js provide this default?
// @ts-ignore
if (initialRevalidate === false) {
@@ -916,7 +969,7 @@ export const build = async ({
srcRoute = null;
dataRoute = pr.dataRoute;
} else {
const pr = prerenderManifest.routes[routeKey];
const pr = prerenderManifest.staticRoutes[routeKey];
({ initialRevalidate, srcRoute, dataRoute } = pr);
}
@@ -930,55 +983,64 @@ export const build = async ({
);
const outputPathData = path.posix.join(entryDirectory, dataRoute);
const lambda = lambdas[outputSrcPathPage];
if (lambda == null) {
throw new Error(`Unable to find lambda for route: ${routeFileNoExt}`);
}
if (initialRevalidate === false) {
if (htmlFsRef == null || jsonFsRef == null) {
throw new Error('invariant: htmlFsRef != null && jsonFsRef != null');
}
htmlFsRef.contentType = htmlContentType;
prerenders[outputPathPage] = htmlFsRef;
prerenders[outputPathData] = jsonFsRef;
} else {
const lambda = lambdas[outputSrcPathPage];
if (lambda == null) {
throw new Error(`Unable to find lambda for route: ${routeFileNoExt}`);
}
prerenders[outputPathPage] = new Prerender({
expiration: initialRevalidate,
lambda,
fallback: htmlFsRef,
group: prerenderGroup,
});
prerenders[outputPathData] = new Prerender({
expiration: initialRevalidate,
lambda,
fallback: jsonFsRef,
group: prerenderGroup,
});
++prerenderGroup;
}
prerenders[outputPathPage] = new Prerender({
expiration: initialRevalidate,
lambda,
fallback: htmlFsRef,
group: prerenderGroup,
bypassToken: prerenderManifest.bypassToken,
});
prerenders[outputPathData] = new Prerender({
expiration: initialRevalidate,
lambda,
fallback: jsonFsRef,
group: prerenderGroup,
bypassToken: prerenderManifest.bypassToken,
});
++prerenderGroup;
};
Object.keys(prerenderManifest.routes).forEach(route =>
onPrerenderRoute(route, false)
Object.keys(prerenderManifest.staticRoutes).forEach(route =>
onPrerenderRoute(route, { isBlocking: false, isFallback: false })
);
Object.keys(prerenderManifest.lazyRoutes).forEach(route =>
onPrerenderRoute(route, true)
Object.keys(prerenderManifest.fallbackRoutes).forEach(route =>
onPrerenderRoute(route, { isBlocking: false, isFallback: true })
);
Object.keys(prerenderManifest.legacyBlockingRoutes).forEach(route =>
onPrerenderRoute(route, { isBlocking: true, isFallback: false })
);
// Dynamic pages for lazy routes should be handled by the lambda flow.
Object.keys(prerenderManifest.lazyRoutes).forEach(lazyRoute => {
const { dataRouteRegex, dataRoute } = prerenderManifest.lazyRoutes[
lazyRoute
];
dynamicDataRoutes.push({
// Next.js provided data route regex
src: dataRouteRegex.replace(/^\^/, `^${appMountPrefixNoTrailingSlash}`),
// Location of lambda in builder output
dest: path.posix.join(entryDirectory, dataRoute),
// We still need to use lazyRoutes if the dataRoutes field
// isn't available for backwards compatibility
if (!(routesManifest && routesManifest.dataRoutes)) {
// Dynamic pages for lazy routes should be handled by the lambda flow.
[
...Object.entries(prerenderManifest.fallbackRoutes),
...Object.entries(prerenderManifest.legacyBlockingRoutes),
].forEach(([, { dataRouteRegex, dataRoute }]) => {
dataRoutes.push({
// Next.js provided data route regex
src: dataRouteRegex.replace(
/^\^/,
`^${appMountPrefixNoTrailingSlash}`
),
// Location of lambda in builder output
dest: path.posix.join(entryDirectory, dataRoute),
});
});
});
}
}
const nextStaticFiles = await glob(
@@ -1023,7 +1085,8 @@ export const build = async ({
entryDirectory,
dynamicPages,
false,
routesManifest
routesManifest,
new Set(prerenderManifest.omittedRoutes)
).then(arr =>
arr.map(route => {
route.src = route.src.replace('^', `^${dynamicPrefix}`);
@@ -1031,6 +1094,23 @@ export const build = async ({
})
);
// We need to delete lambdas from output instead of omitting them from the
// start since we rely on them for powering Preview Mode (read above in
// onPrerenderRoute).
prerenderManifest.omittedRoutes.forEach(routeKey => {
// Get the route file as it'd be mounted in the builder output
const routeFileNoExt = path.posix.join(
entryDirectory,
routeKey === '/' ? '/index' : routeKey
);
if (typeof lambdas[routeFileNoExt] === undefined) {
throw new Error(
`invariant: unknown lambda ${routeKey} (lookup: ${routeFileNoExt}) | please report this immediately`
);
}
delete lambdas[routeFileNoExt];
});
return {
output: {
...publicDirectoryFiles,
@@ -1071,10 +1151,13 @@ export const build = async ({
),
// Next.js assets contain a hash or entropy in their filenames, so they
// are guaranteed to be unique and cacheable indefinitely.
headers: { 'cache-control': 'public,max-age=31536000,immutable' },
headers: {
'cache-control': `public,max-age=${MAX_AGE_ONE_YEAR},immutable`,
},
continue: true,
},
{ src: path.join('/', entryDirectory, '_next(?!/data(?:/|$))(?:/.*)?') },
// Next.js page lambdas, `static/` folder, reserved assets, and `public/`
// folder
{ handle: 'filesystem' },
@@ -1096,7 +1179,10 @@ export const build = async ({
...rewrites,
// Dynamic routes
...dynamicRoutes,
...dynamicDataRoutes,
// /_next/data routes for getServerProps/getStaticProps pages
...dataRoutes,
// Custom Next.js 404 page (TODO: do we want to remove this?)
...(isLegacy
? []

View File

@@ -316,6 +316,7 @@ export type RoutesManifest = {
regex: string;
}[];
version: number;
dataRoutes?: Array<{ page: string; dataRouteRegex: string }>;
};
export async function getRoutesManifest(
@@ -353,7 +354,8 @@ export async function getDynamicRoutes(
entryDirectory: string,
dynamicPages: string[],
isDev?: boolean,
routesManifest?: RoutesManifest
routesManifest?: RoutesManifest,
omittedRoutes?: Set<string>
): Promise<Source[]> {
if (!dynamicPages.length) {
return [];
@@ -363,14 +365,16 @@ export async function getDynamicRoutes(
switch (routesManifest.version) {
case 1:
case 2: {
return routesManifest.dynamicRoutes.map(
({ page, regex }: { page: string; regex: string }) => {
return routesManifest.dynamicRoutes
.filter(({ page }) =>
omittedRoutes ? !omittedRoutes.has(page) : true
)
.map(({ page, regex }: { page: string; regex: string }) => {
return {
src: regex,
dest: !isDev ? path.join('/', entryDirectory, page) : page,
};
}
);
});
}
default: {
// update MIN_ROUTES_MANIFEST_VERSION
@@ -614,7 +618,9 @@ export async function createLambdaFromPseudoLayers({
}
export type NextPrerenderedRoutes = {
routes: {
bypassToken: string | null;
staticRoutes: {
[route: string]: {
initialRevalidate: number | false;
dataRoute: string;
@@ -622,14 +628,24 @@ export type NextPrerenderedRoutes = {
};
};
lazyRoutes: {
legacyBlockingRoutes: {
[route: string]: {
fallback?: string;
routeRegex: string;
dataRoute: string;
dataRouteRegex: string;
};
};
fallbackRoutes: {
[route: string]: {
fallback: string;
routeRegex: string;
dataRoute: string;
dataRouteRegex: string;
};
};
omittedRoutes: string[];
};
export async function getExportIntent(
@@ -714,34 +730,72 @@ export async function getPrerenderManifest(
.catch(() => false);
if (!hasManifest) {
return { routes: {}, lazyRoutes: {} };
return {
staticRoutes: {},
legacyBlockingRoutes: {},
fallbackRoutes: {},
bypassToken: null,
omittedRoutes: [],
};
}
const manifest: {
version: 1;
routes: {
[key: string]: {
initialRevalidateSeconds: number | false;
dataRoute: string;
srcRoute: string | null;
};
};
dynamicRoutes: {
[key: string]: {
fallback?: string;
routeRegex: string;
dataRoute: string;
dataRouteRegex: string;
};
};
} = JSON.parse(await fs.readFile(pathPrerenderManifest, 'utf8'));
const manifest:
| {
version: 1;
routes: {
[key: string]: {
initialRevalidateSeconds: number | false;
dataRoute: string;
srcRoute: string | null;
};
};
dynamicRoutes: {
[key: string]: {
fallback?: string;
routeRegex: string;
dataRoute: string;
dataRouteRegex: string;
};
};
preview?: {
previewModeId: string;
};
}
| {
version: 2;
routes: {
[route: string]: {
initialRevalidateSeconds: number | false;
srcRoute: string | null;
dataRoute: string;
};
};
dynamicRoutes: {
[route: string]: {
routeRegex: string;
fallback: string | false;
dataRoute: string;
dataRouteRegex: string;
};
};
preview: {
previewModeId: string;
};
} = JSON.parse(await fs.readFile(pathPrerenderManifest, 'utf8'));
switch (manifest.version) {
case 1: {
const routes = Object.keys(manifest.routes);
const lazyRoutes = Object.keys(manifest.dynamicRoutes);
const ret: NextPrerenderedRoutes = { routes: {}, lazyRoutes: {} };
const ret: NextPrerenderedRoutes = {
staticRoutes: {},
legacyBlockingRoutes: {},
fallbackRoutes: {},
bypassToken:
(manifest.preview && manifest.preview.previewModeId) || null,
omittedRoutes: [],
};
routes.forEach(route => {
const {
@@ -749,7 +803,7 @@ export async function getPrerenderManifest(
dataRoute,
srcRoute,
} = manifest.routes[route];
ret.routes[route] = {
ret.staticRoutes[route] = {
initialRevalidate:
initialRevalidateSeconds === false
? false
@@ -767,7 +821,68 @@ export async function getPrerenderManifest(
dataRouteRegex,
} = manifest.dynamicRoutes[lazyRoute];
ret.lazyRoutes[lazyRoute] = {
if (fallback) {
ret.fallbackRoutes[lazyRoute] = {
routeRegex,
fallback,
dataRoute,
dataRouteRegex,
};
} else {
ret.legacyBlockingRoutes[lazyRoute] = {
routeRegex,
dataRoute,
dataRouteRegex,
};
}
});
return ret;
}
case 2: {
const routes = Object.keys(manifest.routes);
const lazyRoutes = Object.keys(manifest.dynamicRoutes);
const ret: NextPrerenderedRoutes = {
staticRoutes: {},
legacyBlockingRoutes: {},
fallbackRoutes: {},
bypassToken: manifest.preview.previewModeId,
omittedRoutes: [],
};
routes.forEach(route => {
const {
initialRevalidateSeconds,
dataRoute,
srcRoute,
} = manifest.routes[route];
ret.staticRoutes[route] = {
initialRevalidate:
initialRevalidateSeconds === false
? false
: Math.max(1, initialRevalidateSeconds),
dataRoute,
srcRoute,
};
});
lazyRoutes.forEach(lazyRoute => {
const {
routeRegex,
fallback,
dataRoute,
dataRouteRegex,
} = manifest.dynamicRoutes[lazyRoute];
if (!fallback) {
// Fallback behavior is disabled, all routes would've been provided
// in the top-level `routes` key (`staticRoutes`).
ret.omittedRoutes.push(lazyRoute);
return;
}
ret.fallbackRoutes[lazyRoute] = {
routeRegex,
fallback,
dataRoute,
@@ -778,7 +893,13 @@ export async function getPrerenderManifest(
return ret;
}
default: {
return { routes: {}, lazyRoutes: {} };
return {
staticRoutes: {},
legacyBlockingRoutes: {},
fallbackRoutes: {},
bypassToken: null,
omittedRoutes: [],
};
}
}
}

View File

@@ -13,7 +13,7 @@
"path": "/forever",
"status": 200,
"responseHeaders": {
"x-now-cache": "MISS"
"x-now-cache": "PRERENDER"
}
},
{ "delay": 2000 },
@@ -84,6 +84,21 @@
"x-now-cache": "/HIT|STALE/"
}
},
{
"path": "/_next/data/testing-build-id/blog/post-4.json",
"status": 200,
"responseHeaders": {
"x-now-cache": "MISS"
}
},
{ "delay": 2000 },
{
"path": "/_next/data/testing-build-id/blog/post-4.json",
"status": 200,
"responseHeaders": {
"x-now-cache": "/HIT|STALE/"
}
},
{
"path": "/blog/post-1/comment-1",
"status": 200,
@@ -113,7 +128,7 @@
"path": "/_next/data/testing-build-id/forever.json",
"status": 200,
"responseHeaders": {
"x-now-cache": "MISS"
"x-now-cache": "/PRERENDER|HIT/"
}
},
{ "delay": 2000 },

View File

@@ -13,7 +13,7 @@
"path": "/forever",
"status": 200,
"responseHeaders": {
"x-now-cache": "MISS"
"x-now-cache": "PRERENDER"
}
},
{ "delay": 2000 },
@@ -82,6 +82,21 @@
"x-now-cache": "/HIT|STALE/"
}
},
{
"path": "/_next/data/testing-build-id/blog/post-4.json",
"status": 200,
"responseHeaders": {
"x-now-cache": "MISS"
}
},
{ "delay": 2000 },
{
"path": "/_next/data/testing-build-id/blog/post-4.json",
"status": 200,
"responseHeaders": {
"x-now-cache": "/HIT|STALE/"
}
},
{
"path": "/blog/post-3",
"status": 200,
@@ -114,7 +129,7 @@
"path": "/_next/data/testing-build-id/forever.json",
"status": 200,
"responseHeaders": {
"x-now-cache": "MISS"
"x-now-cache": "/PRERENDER|HIT/"
}
},
{ "delay": 2000 },

View File

@@ -1,6 +1,6 @@
{
"dependencies": {
"next": "9.2.2-canary.16",
"next": "9.2.3-canary.13",
"react": "^16.8.6",
"react-dom": "^16.8.6"
}

View File

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

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