Compare commits

...

45 Commits

Author SHA1 Message Date
Sean Massa
b30f000d2a Publish Stable
- vercel@28.16.4
 - @vercel/next@3.5.2
2023-02-21 18:51:33 -06:00
JJ Kasper
f78051ada9 [next] Ensure .rsc outputs are not created for route handlers (#9503) 2023-02-21 16:41:54 -08:00
Don Alvarez
10bc74904c [docs] Improve docs for @vercel/next no pages built message (#9494)
After many hours of debugging, I tracked down that having an old Node
version (eg 14.x) listed in your Vercel project settings can result in
the build step failing with a confusing and unhelpful error message
"`@vercel/next` No Serverless Pages Built". Note that this is a case
where it "can" cause it to fail, including with NextJS 13.1.6 and Vercel
CLI 28.16.2, but it is not guaranteed to fail. I have six NextJS
projects. They have identical next.config.js, tsconfig.json,
eslintrc.js, and .gitignore files, and other than a few seemingly
non-critical dependencies they have identical package.json files. Four
of the six consistently built and deployed in the cloud without issue.
Two consistently failed to build in the cloud. All built successfully
locally including using vercel build locally, and all would vercel
deploy --prebuilt successfully. Switching all the vercel cloud project
settings from Node 14.x to Node 18.x enabled all the projects to build
and deploy successfully in the vercel cloud without needing local vercel
build and local vercel deploy --prebuilt steps.

---------

Co-authored-by: Steven <steven@ceriously.com>
2023-02-21 11:32:03 -05:00
Steven
c8f7a9a874 [cli] Fix global detection for fnm (#9496)
Fix `--global` install detection when
[fnm](https://github.com/Schniz/fnm) was used to install node
2023-02-21 10:11:08 -05:00
Sean Massa
2fd3315221 Publish Stable
- vercel@28.16.3
 - @vercel/next@3.5.1
 - @vercel/remix@1.3.5
2023-02-21 08:53:23 -06:00
JJ Kasper
54ef027cbe [next] Fix rsc routes order (#9493)
This ensures we maintain the correct order for our rsc routes with
reference to middleware so that they match correctly. This also adds a
regression test to ensure it's working as expected.

Fixes: https://github.com/vercel/next.js/issues/45331
x-ref: [slack
thread](https://vercel.slack.com/archives/C035J346QQL/p1676926522772859?thread_ts=1676926096.412539&cid=C035J346QQL)
2023-02-20 17:54:17 -08:00
Shu Ding
6620c7f600 [next] Add initial vary header to all prerendered pages when RSC is enabled (#9481)
This PR changes the fallback headers that relate to RSC to the defaults that Next.js currently uses. Also, it sets the initial `vary` header to all prerendered routes when RSC is enabled (`routesManifest?.rsc`), even for the pages directory. 

That's because although the pages directory won't return any RSC payload, it can still be used in a project that contains app routes. When the app route requests a page route for RSC data, it's important for the browser to not accidentally cache that result hence we need the `vary` header to set there as well.

More related discussions can be found [here](https://linear.app/vercel/issue/NEXT-382/add-vary-rsc-etc-header-to-all-responses-to-ensure-browser-caching).
2023-02-20 19:35:06 +00:00
JJ Kasper
38f40f1c15 [next] Handle prerender-manifest v4 (#9489)
Co-authored-by: Tim Neutkens <tim@timneutkens.nl>
2023-02-20 11:41:24 +01:00
Nathan Rajlich
63211b8b89 [remix] Add unit tests (#9469)
Moves parts of the `@vercel/remix` builder into util functions that have isolated unit tests. No functionality changes.
2023-02-17 18:46:33 +00:00
Ikko Eltociear Ashimine
83ee5ea2b8 [cli] Fix typo in get-latest-worker.js (#9470)
persistance -> persistence

Co-authored-by: Chris Barber <chris.barber@vercel.com>
2023-02-17 11:45:44 -05:00
Ethan Arrowood
f063645646 [examples] Update remix template (#9472)
Previous PR was merged automatically but need to fix a dependency
version
2023-02-17 09:42:26 -07:00
Ethan Arrowood
4f8c5e344d [examples] update remix template (#9455)
Updates our remix template so that it works with our new Remix changes
2023-02-17 15:10:17 +00:00
Nathan Rajlich
70a53515bd Publish Stable
- vercel@28.16.2
 - @vercel/fs-detectors@3.8.0
 - @vercel/next@3.5.0
 - @vercel/remix@1.3.4
 - @vercel/ruby@1.3.66
 - @vercel/static-build@1.3.10
2023-02-16 15:12:31 -08:00
Sean Massa
4d4f0fa672 [next] Add Operation Types to Next.js Lambdas (#9196)
In order to have Next.js Lambdas show their operation types more specifically in the build output in the dashboard, the builder needs to return the Lambdas with `operationType` set to the appropriate value.

This PR adds those values. This allows the Richer Deployment Outputs to show the different types of serverless functions:
<img width="228" alt="Screenshot 2023-02-03 at 3 49 42 PM" src="https://user-images.githubusercontent.com/41545/216717479-d02fbd4a-fa62-479d-8b65-bd77fdcdb26c.png">
2023-02-16 22:59:14 +00:00
Felix Haus
46c0fd153a [fs-detectors] Remove increments of 64 limit for function memory (#9465)
Missed this occurrence so it still prevents the upload of serverless
functions that have a mem value that is not dividable by 64.
Should be the last place before we can ship the documentation update.

#### Related PRs
- #9440

Co-authored-by: Steven <steven@ceriously.com>
2023-02-16 16:42:42 -05:00
Marc Greenstock
1c8b4717e3 [ruby] fix: HEAD requests (#9436)
When WEBrick receives `HEAD` requests it discards the body (i.e.
`req.body.nil? => true`), this causes Vercel to throw a
`BODY_NOT_A_STRING_FROM_FUNCTION` since it is expecting the serverless
function to respond with a string in the body.

---------

Co-authored-by: Nathan Rajlich <n@n8.io>
Co-authored-by: Steven <steven@ceriously.com>
2023-02-16 16:41:56 -05:00
Nathan Rajlich
d52d26eaac [remix] Install Node globals (#9467)
Fixes https://github.com/vercel/community/discussions/1547.
Fixes https://github.com/vercel/community/discussions/1549.
2023-02-16 21:25:19 +00:00
Vincent Voyer
db65728fc4 Publish Stable
- vercel@28.16.1
2023-02-16 15:36:16 +01:00
Vincent Voyer
a788d06f85 [cli]: fix merging of vercel.json and build result crons (#9464)
Ensures that existing crons and crons from vercel.json are merged
together correctly.
2023-02-16 15:34:56 +01:00
Vincent Voyer
3d98d1cdea Publish Stable
- @vercel/build-utils@6.3.0
 - vercel@28.16.0
 - @vercel/client@12.4.0
 - @vercel/fs-detectors@3.7.14
 - @vercel/gatsby-plugin-vercel-builder@1.1.7
 - @vercel/go@2.3.7
 - @vercel/hydrogen@0.0.53
 - @vercel/next@3.4.7
 - @vercel/node@2.9.6
 - @vercel/python@3.1.49
 - @vercel/redwood@1.1.5
 - @vercel/remix@1.3.3
 - @vercel/ruby@1.3.65
 - @vercel/static-build@1.3.9
 - @vercel/static-config@2.0.13
2023-02-16 12:08:50 +01:00
Vincent Voyer
667af829c4 [build-utils][cli][client][node][next][static-config]: forward crons from vercel.json to config.json (#9454)
This PR changes the way cron jobs are being created in the build output
API. This is my first time contributing here. If you see something
unusual, let me know.

 Good for review

Our goal is to:
- Allow creating cron jobs via the `crons` property of `vercel.json` for
end users
- Allow framework authors to create cron jobs on Vercel via the `crons`
property of the Build Output API configuration

---

As you can see, we removed the previous implementation where cron jobs
could be configured at the function code level (export const cron = ""),
on top of vercel.json `functions` property. Here's why:

- All frameworks would have to implement the configure at the function
code level
- Not all frameworks can easily map a path to a specific function
(example: SvelteKit) and would have to bail on bundling functions inside
the same lambda
- Configuring a path + scheduler provides a better mapping of what cron
jobs are as of today: API routes on a schedule and not functions on a
schedule
- Dynamic routes Cron Jobs will be supported:
/api/crons/sync-slack-team/230
- Query parameters will be supported support:
/api/crons/sync-slack-team/230?secret=32k13l2k13lk21 (= securing cron
jobs v0)
- 100% frameworks compatibility from day one

Next.js and other frameworks may choose to implement their own cron jobs
feature that will then need to be configured through the `crons`
property of `config.json` (build output API).

cc @timneutkens @Rich-Harris 

Internal thread:
https://vercel.slack.com/archives/C04DWF5HB6K/p1676366892714349
2023-02-16 11:49:09 +01:00
Nathan Rajlich
1bb7b37e0c Publish Stable
- vercel@28.15.7
 - @vercel/next@3.4.6
 - @vercel/remix@1.3.2
2023-02-15 23:41:25 -08:00
Nathan Rajlich
ca81c133b9 [remix] Fix root-level splat path (#9457)
Fixes https://github.com/orgs/vercel/discussions/1545.
2023-02-16 07:32:53 +00:00
Luba Kravchenko
f332b7856c Adds framework fields to build outputs (#9449)
Adds framework to Lambda and edge build outputs so that we can distinguish which framework they originated from when certain features should be applied to specific frameworks.

This PR adds framework to the outputs. Part 1: https://github.com/vercel/vercel/pull/9448

ticket: ED-131

x-ref: [slack channel](https://vercel.slack.com/archives/C042LHPJ1NX)
2023-02-15 22:56:29 +00:00
Sean Massa
9308a0fda5 Publish Stable
- @vercel/build-utils@6.2.4
 - vercel@28.15.6
 - @vercel/client@12.3.10
 - @vercel/frameworks@1.3.1
 - @vercel/fs-detectors@3.7.13
 - @vercel/gatsby-plugin-vercel-builder@1.1.6
 - @vercel/go@2.3.6
 - @vercel/hydrogen@0.0.52
 - @vercel/next@3.4.5
 - @vercel/node@2.9.5
 - @vercel/python@3.1.48
 - @vercel/redwood@1.1.4
 - @vercel/remix@1.3.1
 - @vercel/routing-utils@2.1.9
 - @vercel/ruby@1.3.64
 - @vercel/static-build@1.3.8
2023-02-15 12:26:53 -06:00
Steven
c2f8a5990a [tests] Fix gh actions cron job to update next/turbo (#9453)
This token should run the tests automatically
2023-02-15 12:11:54 -05:00
JJ Kasper
fc2f0b919b [next] Ensure app routes are handled (#9450) 2023-02-15 10:00:24 +01:00
Luba Kravchenko
a606ab8678 [build-utils] add framework slug and version to build output (#9448)
Adds framework to Lambda and edge build outputs so that we can distinguish which framework they originated from when certain features should be applied to specific frameworks.

Breaking up https://github.com/vercel/vercel/pull/9447 into two parts:
This PR introduces the frameworks type. Part 2: https://github.com/vercel/vercel/pull/9449

ticket: ED-131

x-ref: [slack channel](https://vercel.slack.com/archives/C042LHPJ1NX)
2023-02-15 03:14:21 +00:00
Chris Barber
13062cd47d [routing-utils] Added middlewareRawSrc to RouteWithSrc type (#9443)
This PR adds a new `middlewareRawSrc` prop to the `RouteWithSrc` type which is used for edge middleware routes. The existing `middlewarePath` prop contains a regex of all the middleware `matcher` routes which isn't very user friendly on the front end.

By preserving the original middleware matchers, the front end can display the list in a much more presentable fashion.

This feature blocks 3 other PRs.
1. https://github.com/vercel/api/pull/17231 depends on `Route` type for schema validation and translates `middlewareRawSrc` to `rawMatchers` for front end consumption
2. https://github.com/vercel/vercel/pull/9435 sets the `middlewareRawSrc` in the BOA output
3. https://github.com/vercel/front/pull/19606 displays the `rawMatchers` under the Edge Middleware section for a deployment

Linear: https://linear.app/vercel/issue/VCCLI-411/display-uncompressed-edge-middleware-matcher-show-the-list-of-matches
2023-02-14 23:45:03 +00:00
Chris Barber
bb9faaed99 [test] Use execFileSync() to get processes (#9445)
`spawnSync()` does not throw if the command can't be found in the PATH or if an error occurs. If we use `execFileSync()`, it will throw and that was likely the desired behavior in this test utility function.
2023-02-14 22:59:52 +00:00
Sean Massa
881e43a0e2 Publish Stable
- @vercel/build-utils@6.2.3
 - vercel@28.15.5
 - @vercel/client@12.3.9
 - @vercel/fs-detectors@3.7.12
 - @vercel/gatsby-plugin-vercel-builder@1.1.5
 - @vercel/go@2.3.5
 - @vercel/hydrogen@0.0.51
 - @vercel/next@3.4.4
 - @vercel/node@2.9.4
 - @vercel/python@3.1.47
 - @vercel/redwood@1.1.3
 - @vercel/remix-entry-server@0.1.0
 - @vercel/remix@1.3.0
 - @vercel/ruby@1.3.63
 - @vercel/static-build@1.3.7
2023-02-14 15:38:34 -06:00
Steven
578a7742fe [next] Improve error message when routes-manifest.json not found (#9441)
The dashboard doesn't handle multiline errors very well because other lines are collapsed.

https://vercel.com/changelog/deployment-logs-filtering-now-available

<img width="1237" alt="image" src="https://user-images.githubusercontent.com/229881/218832497-0bcd9404-b4f5-49fe-9edd-bd6de6d8bb8f.png">


We also show the complete error in the top and it doesn't show newlines properly.

<img width="428" alt="image" src="https://user-images.githubusercontent.com/229881/218832603-065e0d4d-ae6e-4b44-b2db-b2fff063da58.png">

The "Learn More" link already has the relevant information so we can reduce this error message to a single line to improve the call to action.
2023-02-14 20:32:18 +00:00
Nathan Rajlich
2d84a64430 [remix-entry-server] Add initial implementation (#9417)
This adds a new package to the monorepo: `@vercel/remix-entry-server`

The purpose of this package is to provide most of the implementation of
the Remix `app/entry.server.tsx` file with proper support for the Vercel
Serverless and Edge runtimes, specifically in regards to React 18
streaming.

The reason that this package is necessary, as opposed to just updating
our Remix template, is due to the fact that Serverless and Edge runtimes
require a different implementation. This is because the
`react-dom/server` package exports different functions for the Node
(`renderToPipeableStream()`) vs. Edge (`renderToReadableStream()`)
bundles, and thus this package also has two different implementations
(by utilizing the `main` and `browser` fields in `package.json`).

Usage of this package in a Remix application's `app/entry.server.tsx`
file looks like:

```tsx
import handleRequest from "@vercel/remix-entry-server";
import { RemixServer } from "@remix-run/react";
import type { EntryContext } from "@remix-run/server-runtime";

export default function (
  request: Request,
  responseStatusCode: number,
  responseHeaders: Headers,
  remixContext: EntryContext
) {
  const remixServer = <RemixServer context={remixContext} url={request.url} />;
  return handleRequest(request, responseStatusCode, responseHeaders, remixServer)
}
```

Once this package is published then we can update our own Remix template
to utilize it, enabling React 18 streaming for both Vercel runtimes.
2023-02-14 10:09:07 -08:00
Felix Haus
200ac99647 [build-utils][cli] Remove 64 increment limit for serverless functions (#9440)
We no longer require the memory to be provided in steps of 64mb for
serverless functions.
Instead the memory can now be chosen freely from `128mb` to `3008mb` in
`1mb increments`.

Updates the `vercel.json` schema to reflect that change.
2023-02-14 13:08:40 -05:00
Felix Haus
1d3f2b5a62 [build-utils][cli] Update link to project-configuration (#9439)
The link to the documentation has changed.
It is now available under https://vercel.com/docs/concepts/projects/project-configuration

This updates:
- link generation `https://vercel.com/docs/configuration#project/*` -> `https://vercel.com/docs/concepts/projects/project-configuration#*`
-  Updates test files
- Updates static references of https://vercel.com/docs/configuration across the repo
2023-02-14 16:44:24 +00:00
Steven
8f49969585 [tests] Update tests script names (#9433)
### Description 

These script names are currently really long and that makes it difficult
to read.

In particular, the most important part (the package name) is often
truncated.

See before/after below.

## Before

<img width="302" alt="image"
src="https://user-images.githubusercontent.com/229881/218588978-b8ed9a7a-f4da-4d58-af3f-2b1a7087737d.png">

## After

<img width="290" alt="image"
src="https://user-images.githubusercontent.com/229881/218596895-65627ad0-2895-4bd7-8506-f5f545a419d3.png">
2023-02-14 10:31:46 -05:00
Steven
cfbfaa7cd0 [tests] Fix memory limit test (#9438)
This error message was adjusted recently
2023-02-14 09:58:10 -05:00
Nathan Rajlich
25747a7621 [remix] Support per-page Edge functions and replace "@remix-run/vercel" (#9375)
Adds support for server-side rendered Remix using Edge Functions. This runtime can be enabled on a per-page basis, by adding the following to a page within the `app/routes` directory:

```js
export const config = {
  runtime: 'edge'
};
```

Additionally, this PR further supersedes the `@remix-run/vercel` runtime adapter, because we will always inject our own server entrypoint. So the logic to ensure that package exists in the project's `package.json` has been removed (so this closes #9011). The only requirement is that the Remix project has `@remix-run/node` as a dependency, which is the case for the vanilla Remix template so I think that's a fair assumption.

To make Edge Functions work, we need to ensure that `remix build` is executed with a few specific configuration values in place, so this change wraps the existing `remix.config.js` file and adds our own to make sure those values are in place (and then cleans itself up after the build command is executed).

Finally, the reading of the Remix config logic was simplified by using the `readConfig()` function from the `@remix-run/dev` package, which also includes the routes manifest, so the hacky `vm` running logic to retrieve the manifest was able to be removed.

Closes #8784.
Closes #9011.

---

# To test this out:

1. Ensure that Remix dependencies are running _at least version `1.5.0`_ (which is when `writeReadableStreamToWritable()` was added to `@remix-run/node`).

2. Add the following line of code to any page in your Remix application’s `app/routes/*` directory:
    
    ```jsx
    export const config = { runtime: 'edge' };
    ```
    
3. Set an Environment Variable on your Vercel Project:
    - Name: `VERCEL_CLI_VERSION`
    - Value: `https://vercel-git-update-remix-edge.vercel.sh/tarballs/vercel.tgz`
    
4. Make a deployment, either by running `vercel deploy` in the CLI, or by pushing a Git commit to your repository which has a Vercel Git integration enabled.
2023-02-14 01:01:46 +00:00
github-actions[bot]
8d635beed7 [tests] Upgrade Turbo to version 1.7.4 (#9431)
This auto-generated PR updates Turbo to version 1.7.4
2023-02-14 00:01:45 +00:00
JJ Kasper
6225f050fa [next] Fix app-dir tests from new content-type (#9434)
x-ref: https://github.com/vercel/vercel/pull/9408
x-ref:
https://github.com/vercel/vercel/actions/runs/4168082693/jobs/7214887847
2023-02-13 18:22:33 -05:00
Steven
30a899bab1 [tests] Add workflow_dispatch to cron workflows (#9430)
This `workflow_dispatch` prop allows manual runs by clicking a button on
https://github.com/vercel/vercel/actions
2023-02-13 16:39:15 -05:00
Sean Massa
a010d8fe8a [cli] add slash before url for edge function errors (#9428)
Edge Functions and Serverless Functions were both updated to show the path of their entry points, but it turns out Serverless Function paths started with a slash and Edge Function paths did not.

The [related code for Serverless Functions](d628880942/packages/node-bridge/helpers.ts) does not need to be updated.

---

Before:

<img width="996" alt="CleanShot 2023-02-13 at 13 23 42@2x" src="https://user-images.githubusercontent.com/41545/218554154-2b112a68-2d68-4968-bae7-acf6e85a72a2.png">

---

After: 

<img width="921" alt="CleanShot 2023-02-13 at 13 20 22@2x" src="https://user-images.githubusercontent.com/41545/218554029-c3dce6b1-b01f-4a78-aa32-f6891ff16e51.png">
2023-02-13 21:37:16 +00:00
Steven
a9ff23fe22 [test] Switch to turbo main release line (#9429)
Per @tknickman, we don't need to be testing every turbo canary in this
repo, that will happen in the turborepo tests
2023-02-13 16:00:42 -05:00
Steven
b60d3f657a [tests] Update CI to Node.js 16 (#9397)
In an effort to speed up CI, we should update the lowest common
denominator to Node.js 16

Note: In April, Node.js 14 will reach EOL so we can update tsconfig
targets and ship a major semver at that time.

---------

Co-authored-by: JJ Kasper <jj@jjsweb.site>
2023-02-13 15:59:05 -05:00
Nathan Rajlich
e7947a1b33 [node] Make NFT resolve "worker"/"browser" exports for Edge Functions (#9377)
This makes `react-dom` work as expected from within Edge Functions. Otherwise, NFT will only select the Node.js version of the files which do not work with react-dom within an Edge Function.
2023-02-13 20:27:37 +00:00
200 changed files with 20993 additions and 24777 deletions

View File

@@ -1,6 +1,8 @@
name: Cron Update Next
on:
# Allow manual runs
workflow_dispatch:
# Run every 4 hours https://crontab.guru/every-4-hours
schedule:
- cron: '0 */4 * * *'
@@ -14,10 +16,13 @@ jobs:
# 0 means fetch all commits so we can commit and push in the script below
with:
fetch-depth: 0
- name: Enable corepack
run: corepack enable pnpm
- name: Create Pull Request
uses: actions/github-script@v6
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
GITHUB_TOKEN: ${{ secrets.GH_TOKEN_PULL_REQUESTS }}
# See https://github.com/actions/github-script#run-a-separate-file-with-an-async-function
with:
script: |

View File

@@ -1,6 +1,8 @@
name: Cron Update Turbo
on:
# Allow manual runs
workflow_dispatch:
# Run every week https://crontab.guru/every-week
schedule:
- cron: '0 0 * * 0'
@@ -14,12 +16,13 @@ jobs:
# 0 means fetch all commits so we can commit and push in the script below
with:
fetch-depth: 0
- name: install pnpm@7.26.0
run: npm i -g pnpm@7.26.0
- name: Enable corepack
run: corepack enable pnpm
- name: Create Pull Request
uses: actions/github-script@v6
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
GITHUB_TOKEN: ${{ secrets.GH_TOKEN_PULL_REQUESTS }}
# See https://github.com/actions/github-script#run-a-separate-file-with-an-async-function
with:
script: |

View File

@@ -40,7 +40,7 @@ jobs:
if: ${{ steps.check-release.outputs.IS_RELEASE == 'true' }}
uses: actions/setup-node@v3
with:
node-version: 14
node-version: 16
- name: install pnpm@7.24.2
run: npm i -g pnpm@7.24.2
- name: Install

View File

@@ -25,7 +25,7 @@ jobs:
fail-fast: false
matrix:
os: [ubuntu-latest]
node: [14]
node: [16]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v3
@@ -41,7 +41,7 @@ jobs:
run: npm i -g pnpm@7.24.2
- run: pnpm install
- run: pnpm run build
- run: pnpm test-integration-cli
- run: pnpm test-cli
env:
VERCEL_TEST_TOKEN: ${{ secrets.VERCEL_TEST_TOKEN }}
VERCEL_TEST_REGISTRATION_URL: ${{ secrets.VERCEL_TEST_REGISTRATION_URL }}

View File

@@ -25,7 +25,7 @@ jobs:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
node: [14]
node: [16]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/setup-go@v3

View File

@@ -9,7 +9,7 @@ on:
pull_request:
env:
NODE_VERSION: '14'
NODE_VERSION: '16'
TURBO_REMOTE_ONLY: 'true'
TURBO_TEAM: 'vercel'
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}

View File

@@ -11,4 +11,4 @@ Remove the `functions` config from your `now.json` or `vercel.json` to take adva
### Useful Links
- [Functions Config Documentation](https://vercel.com/docs/configuration?query=functions#project/functions)
- [Functions Config Documentation](https://vercel.com/docs/concepts/projects/project-configuration#functions)

View File

@@ -11,6 +11,6 @@ Migrate from using legacy `routes` to the new `rewrites`, `redirects`, and `head
### Useful Links
- [Rewrites Documentation](https://vercel.com/docs/configuration?query=rewrites#project/rewrites)
- [Redirects Documentation](https://vercel.com/docs/configuration?query=rewrites#project/redirects)
- [Headers Documentation](https://vercel.com/docs/configuration?query=rewrites#project/headers)
- [Rewrites Documentation](https://vercel.com/docs/concepts/projects/project-configuration#rewrites)
- [Redirects Documentation](https://vercel.com/docs/concepts/projects/project-configuration#redirects)
- [Headers Documentation](https://vercel.com/docs/concepts/projects/project-configuration#headers)

View File

@@ -14,7 +14,9 @@ In order to create the smallest possible lambdas Next.js has to be configured to
npm install next --save
```
2. Add the `now-build` script to your `package.json`
2. Check [Node.js Version](https://vercel.link/node-version) in your Project Settings. Using an old or incompatible version of Node.js can cause the Build Step to fail with this error message.
3. Add the `now-build` script to your `package.json` [deprecated]
```json
{
@@ -24,7 +26,7 @@ npm install next --save
}
```
3. Add `target: 'serverless'` to `next.config.js` [deprecated]
4. Add `target: 'serverless'` to `next.config.js` [deprecated]
```js
module.exports = {
@@ -33,9 +35,9 @@ module.exports = {
};
```
4. Remove `distDir` from `next.config.js` as `@vercel/next` can't parse this file and expects your build output at `/.next`
5. Remove `distDir` from `next.config.js` as `@vercel/next` can't parse this file and expects your build output at `/.next`
5. Optionally make sure the `"src"` in `"builds"` points to your application `package.json`
6. Optionally make sure the `"src"` in `"builds"` points to your application `package.json`
```js
{

View File

@@ -2,15 +2,15 @@
#### Why This Error Occurred
This could be caused by a misconfigured "Build Command" or "Output Directory" for your Next.js project.
This error is often caused by a misconfigured "Build Command" or "Output Directory" for your Next.js project.
#### Possible Ways to Fix It
In the Vercel dashboard, open your "Project Settings" and draw attention to "Build & Development Settings":
1. Ensure that the "Build Command" setting is not changed, or that it calls `next build`. If this command is not changed but you are seeing this error, double check that your `build` script in `package.json` calls `next build`.
2. Ensure that the "Output Directory" setting is not changed. This value almost never needs to be configured, and is only necessary if you override `distDir` in `next.config.js`.
3. For `next export` users: **do not override the "Output Directory"**. Next.js automatically detects what folder you outputted `next export` to.
1. Ensure that the "Build Command" setting is not overridden, or that it calls `next build`. If this command is not overridden but you are seeing this error, double check that your `build` script in `package.json` calls `next build`. If `buildCommand` exists in `vercel.json`, make sure it calls `next build`.
2. Ensure that the "Output Directory" setting is not overridden. This value almost never needs to be configured, and is only necessary if you override `distDir` in `next.config.js`. If `outputDirectory` exists in `vercel.json`, remove that property.
3. For `next export` users: **do not override the "Output Directory"**, even if you customized the `next export` output directory. It will automatically detects the correct output.
In rare scenarios, this error message can also be caused by a Next.js build failure (if your "Build Command" accidentally returns an exit code that is not 0).
Double check for any error messages above the Routes Manifest error, which may provide additional details.

View File

@@ -4,7 +4,7 @@
"description": "Each subdirectory is an example boilerplate for a framework. This package.json only exists for testing purposes.",
"scripts": {
"test-unit": "pnpm test __tests__/unit/",
"test-integration-once": "pnpm test __tests__/integration/",
"test-e2e": "pnpm test __tests__/integration/",
"test": "jest --env node --verbose --runInBand --bail"
},
"devDependencies": {

View File

@@ -13,10 +13,10 @@ function hydrate() {
});
}
if (window.requestIdleCallback) {
window.requestIdleCallback(hydrate);
if (typeof requestIdleCallback === "function") {
requestIdleCallback(hydrate);
} else {
// Safari doesn't support requestIdleCallback
// https://caniuse.com/requestidlecallback
window.setTimeout(hydrate, 1);
setTimeout(hydrate, 1);
}

View File

@@ -1,21 +1,14 @@
import type { EntryContext } from "@remix-run/node";
import handleRequest from "@vercel/remix-entry-server";
import { RemixServer } from "@remix-run/react";
import { renderToString } from "react-dom/server";
import type { EntryContext } from "@remix-run/server-runtime";
export default function handleRequest(
export default function (
request: Request,
responseStatusCode: number,
responseHeaders: Headers,
remixContext: EntryContext
) {
const markup = renderToString(
<RemixServer context={remixContext} url={request.url} />
);
responseHeaders.set("Content-Type", "text/html");
return new Response("<!DOCTYPE html>" + markup, {
headers: responseHeaders,
status: responseStatusCode,
});
const remixServer = <RemixServer context={remixContext} url={request.url} />;
return handleRequest(request, responseStatusCode, responseHeaders, remixServer)
}

View File

@@ -0,0 +1,11 @@
export const config = {
runtime: 'edge'
};
export default function Edge() {
return (
<div style={{ fontFamily: "system-ui, sans-serif", lineHeight: "1.4" }}>
<h1>Welcome to Remix@Edge</h1>
</div>
);
}

View File

@@ -6,20 +6,20 @@
"dev": "remix dev"
},
"dependencies": {
"@remix-run/node": "^1.7.6",
"@remix-run/react": "^1.7.6",
"@remix-run/vercel": "^1.7.6",
"@vercel/analytics": "^0.1.5",
"@vercel/node": "^2.7.0",
"@remix-run/node": "^1.13.0",
"@remix-run/react": "^1.13.0",
"@remix-run/serve": "^1.13.0",
"@remix-run/server-runtime": "^1.13.0",
"@vercel/analytics": "^0.1.10",
"@vercel/remix-entry-server": "^0.1.0",
"react": "^18.2.0",
"react-dom": "^18.2.0"
},
"devDependencies": {
"@remix-run/dev": "^1.7.6",
"@remix-run/eslint-config": "^1.7.6",
"@remix-run/serve": "^1.7.6",
"@remix-run/dev": "^1.13.0",
"@remix-run/eslint-config": "^1.13.0",
"@types/react": "^18.0.25",
"@types/react-dom": "^18.0.9",
"@types/react-dom": "^18.0.11",
"eslint": "^8.28.0",
"typescript": "^4.9.3"
},

File diff suppressed because it is too large Load Diff

View File

@@ -1,13 +1,10 @@
/** @type {import('@remix-run/dev').AppConfig} */
/**
* @type {import('@remix-run/dev').AppConfig}
*/
module.exports = {
serverBuildTarget: "vercel",
// When running locally in development mode, we use the built in remix
// server. This does not understand the vercel lambda module format,
// so we default back to the standard build output.
server: process.env.NODE_ENV === "development" ? undefined : "./server.js",
ignoredRouteFiles: ["**/.*"],
ignoredRouteFiles: ['**/.*'],
// appDirectory: "app",
// assetsBuildDirectory: "public/build",
// serverBuildPath: "api/index.js",
// serverBuildPath: "build/index.js",
// publicPath: "/build/",
};

View File

@@ -1,4 +0,0 @@
import { createRequestHandler } from "@remix-run/vercel";
import * as build from "@remix-run/dev/server-build";
export default createRequestHandler({ build, mode: process.env.NODE_ENV });

View File

@@ -32,7 +32,7 @@
"source-map-support": "0.5.12",
"ts-eager": "2.0.2",
"ts-jest": "28.0.5",
"turbo": "1.7.0"
"turbo": "1.7.4"
},
"scripts": {
"lerna": "lerna",
@@ -47,9 +47,9 @@
"pre-commit": "lint-staged",
"test": "jest --rootDir=\"test\" --testPathPattern=\"\\.test.js\"",
"test-unit": "pnpm test && node utils/gen.js && turbo run test-unit",
"test-integration-cli": "node utils/gen.js && turbo run test-integration-cli",
"test-integration-once": "node utils/gen.js && turbo run test-integration-once",
"test-integration-dev": "node utils/gen.js && turbo run test-integration-dev",
"test-cli": "node utils/gen.js && turbo run test-cli",
"test-e2e": "node utils/gen.js && turbo run test-e2e",
"test-dev": "node utils/gen.js && turbo run test-dev",
"lint": "eslint . --cache --ext .ts,.js",
"prepare": "husky install",
"pack": "cd utils && node -r ts-eager/register ./pack.ts"

View File

@@ -1,6 +1,6 @@
{
"name": "@vercel/build-utils",
"version": "6.2.2",
"version": "6.3.0",
"license": "MIT",
"main": "./dist/index.js",
"types": "./dist/index.d.js",
@@ -14,7 +14,7 @@
"build": "node build",
"test": "jest --env node --verbose --runInBand --bail",
"test-unit": "pnpm test test/unit.*test.*",
"test-integration-once": "pnpm test test/integration.test.ts"
"test-e2e": "pnpm test test/integration.test.ts"
},
"devDependencies": {
"@iarna/toml": "2.2.3",

View File

@@ -1,4 +1,4 @@
import type { Cron, Files } from './types';
import type { Files, FunctionFramework } from './types';
/**
* An Edge Functions output
@@ -41,8 +41,8 @@ export class EdgeFunction {
/** The regions where the edge function will be executed on */
regions?: string | string[];
/** Cronjob definition for the edge function */
cron?: Cron;
/** The framework */
framework?: FunctionFramework;
constructor(params: Omit<EdgeFunction, 'type'>) {
this.type = 'EdgeFunction';
@@ -53,6 +53,6 @@ export class EdgeFunction {
this.envVarsInUse = params.envVarsInUse;
this.assets = params.assets;
this.regions = params.regions;
this.cron = params.cron;
this.framework = params.framework;
}
}

View File

@@ -44,7 +44,8 @@ export function getPrettyError(obj: {
message?: string;
params: any;
}): NowBuildError {
const docsUrl = 'https://vercel.com/docs/configuration';
const docsUrl =
'https://vercel.com/docs/concepts/projects/project-configuration';
try {
const { dataPath, params, message: ajvMessage } = obj;
const prop = getTopLevelPropertyName(dataPath);
@@ -63,7 +64,7 @@ export function getPrettyError(obj: {
return new NowBuildError({
code: 'INVALID_VERCEL_CONFIG',
message: message,
link: prop ? `${docsUrl}#project/${prop.toLowerCase()}` : docsUrl,
link: prop ? `${docsUrl}#${prop.toLowerCase()}` : docsUrl,
action: 'View Documentation',
});
} catch (e) {

View File

@@ -5,7 +5,7 @@ import minimatch from 'minimatch';
import { readlink } from 'fs-extra';
import { isSymbolicLink, isDirectory } from './fs/download';
import streamToBuffer from './fs/stream-to-buffer';
import type { Files, Config, Cron } from './types';
import type { Files, Config, FunctionFramework } from './types';
interface Environment {
[key: string]: string;
@@ -25,7 +25,7 @@ export interface LambdaOptionsBase {
supportsWrapper?: boolean;
experimentalResponseStreaming?: boolean;
operationType?: string;
cron?: Cron;
framework?: FunctionFramework;
}
export interface LambdaOptionsWithFiles extends LambdaOptionsBase {
@@ -63,7 +63,6 @@ export class Lambda {
environment: Environment;
allowQuery?: string[];
regions?: string[];
cron?: Cron;
/**
* @deprecated Use `await lambda.createZip()` instead.
*/
@@ -71,6 +70,7 @@ export class Lambda {
supportsMultiPayloads?: boolean;
supportsWrapper?: boolean;
experimentalResponseStreaming?: boolean;
framework?: FunctionFramework;
constructor(opts: LambdaOptions) {
const {
@@ -81,11 +81,11 @@ export class Lambda {
environment = {},
allowQuery,
regions,
cron,
supportsMultiPayloads,
supportsWrapper,
experimentalResponseStreaming,
operationType,
framework,
} = opts;
if ('files' in opts) {
assert(typeof opts.files === 'object', '"files" must be an object');
@@ -135,8 +135,18 @@ export class Lambda {
);
}
if (cron !== undefined) {
assert(typeof cron === 'string', '"cron" is not a string');
if (framework !== undefined) {
assert(typeof framework === 'object', '"framework" is not an object');
assert(
typeof framework.slug === 'string',
'"framework.slug" is not a string'
);
if (framework.version !== undefined) {
assert(
typeof framework.version === 'string',
'"framework.version" is not a string'
);
}
}
this.type = 'Lambda';
@@ -149,11 +159,11 @@ export class Lambda {
this.environment = environment;
this.allowQuery = allowQuery;
this.regions = regions;
this.cron = cron;
this.zipBuffer = 'zipBuffer' in opts ? opts.zipBuffer : undefined;
this.supportsMultiPayloads = supportsMultiPayloads;
this.supportsWrapper = supportsWrapper;
this.experimentalResponseStreaming = experimentalResponseStreaming;
this.framework = framework;
}
async createZip(): Promise<Buffer> {
@@ -228,7 +238,7 @@ export async function getLambdaOptionsFromFunction({
sourceFile,
config,
}: GetLambdaOptionsFromFunctionOptions): Promise<
Pick<LambdaOptions, 'memory' | 'maxDuration' | 'cron'>
Pick<LambdaOptions, 'memory' | 'maxDuration'>
> {
if (config?.functions) {
for (const [pattern, fn] of Object.entries(config.functions)) {
@@ -236,7 +246,6 @@ export async function getLambdaOptionsFromFunction({
return {
memory: fn.memory,
maxDuration: fn.maxDuration,
cron: fn.cron,
};
}
}

View File

@@ -13,10 +13,8 @@ export const functionsSchema = {
maxLength: 256,
},
memory: {
// Number between 128 and 3008 in steps of 64
enum: Object.keys(Array.from({ length: 50 }))
.slice(2, 48)
.map(x => Number(x) * 64),
minimum: 128,
maximum: 3008,
},
maxDuration: {
type: 'number',
@@ -31,11 +29,6 @@ export const functionsSchema = {
type: 'string',
maxLength: 256,
},
cron: {
type: 'string',
minLength: 9,
maxLength: 256,
},
},
},
},

View File

@@ -319,7 +319,6 @@ export interface BuilderFunctions {
runtime?: string;
includeFiles?: string;
excludeFiles?: string;
cron?: Cron;
};
}
@@ -411,7 +410,16 @@ export interface BuildResultBuildOutput {
buildOutputPath: string;
}
export type Cron = string;
export interface Cron {
path: string;
schedule: string;
}
/** The framework which created the function */
export interface FunctionFramework {
slug: string;
version?: string;
}
/**
* When a Builder implements `version: 2`, the `build()` function is expected

View File

@@ -502,6 +502,10 @@ it('should retry npm install when peer deps invalid and npm@8 on node@16', async
console.log(`Skipping test on node@${nodeMajor}`);
return;
}
if (process.platform === 'win32') {
console.log('Skipping test on windows');
return;
}
const fixture = path.join(__dirname, 'fixtures', '15-npm-8-legacy-peer-deps');
const nodeVersion = { major: nodeMajor } as any;
await runNpmInstall(fixture, [], {}, {}, nodeVersion);

View File

@@ -1,6 +1,6 @@
{
"name": "vercel",
"version": "28.15.4",
"version": "28.16.4",
"preferGlobal": true,
"license": "Apache-2.0",
"description": "The command-line interface for Vercel",
@@ -14,8 +14,8 @@
"preinstall": "node ./scripts/preinstall.js",
"test": "jest --env node --verbose --bail",
"test-unit": "pnpm test test/unit/",
"test-integration-cli": "rimraf test/fixtures/integration && ava test/integration.js --serial --fail-fast --verbose",
"test-integration-dev": "pnpm test test/dev/",
"test-cli": "rimraf test/fixtures/integration && ava test/integration.js --serial --fail-fast --verbose",
"test-dev": "pnpm test test/dev/",
"coverage": "codecov",
"build": "ts-node ./scripts/build.ts",
"dev": "ts-node ./src/index.ts"
@@ -41,16 +41,16 @@
"node": ">= 14"
},
"dependencies": {
"@vercel/build-utils": "6.2.2",
"@vercel/go": "2.3.4",
"@vercel/hydrogen": "0.0.50",
"@vercel/next": "3.4.3",
"@vercel/node": "2.9.3",
"@vercel/python": "3.1.46",
"@vercel/redwood": "1.1.2",
"@vercel/remix": "1.2.13",
"@vercel/ruby": "1.3.62",
"@vercel/static-build": "1.3.6"
"@vercel/build-utils": "6.3.0",
"@vercel/go": "2.3.7",
"@vercel/hydrogen": "0.0.53",
"@vercel/next": "3.5.2",
"@vercel/node": "2.9.6",
"@vercel/python": "3.1.49",
"@vercel/redwood": "1.1.5",
"@vercel/remix": "1.3.5",
"@vercel/ruby": "1.3.66",
"@vercel/static-build": "1.3.10"
},
"devDependencies": {
"@alex_neo/jest-expect-message": "1.0.5",
@@ -93,13 +93,13 @@
"@types/which": "1.3.2",
"@types/write-json-file": "2.2.1",
"@types/yauzl-promise": "2.1.0",
"@vercel/client": "12.3.8",
"@vercel/client": "12.4.0",
"@vercel/error-utils": "1.0.8",
"@vercel/frameworks": "1.3.0",
"@vercel/fs-detectors": "3.7.11",
"@vercel/frameworks": "1.3.1",
"@vercel/fs-detectors": "3.8.0",
"@vercel/fun": "1.0.4",
"@vercel/ncc": "0.24.0",
"@vercel/routing-utils": "2.1.8",
"@vercel/routing-utils": "2.1.9",
"@zeit/source-map-support": "0.6.2",
"ajv": "6.12.2",
"alpha-sort": "2.0.1",

View File

@@ -16,6 +16,7 @@ import {
BuildResultV2Typical,
BuildResultV3,
NowBuildError,
Cron,
} from '@vercel/build-utils';
import {
detectBuilders,
@@ -88,6 +89,7 @@ interface BuildOutputConfig {
framework?: {
version: string;
};
crons?: Cron[];
}
/**
@@ -623,6 +625,7 @@ async function doBuild(
});
const mergedImages = mergeImages(localConfig.images, buildResults.values());
const mergedCrons = mergeCrons(localConfig.crons, buildResults.values());
const mergedWildcard = mergeWildcard(buildResults.values());
const mergedOverrides: Record<string, PathOverride> =
overrides.length > 0 ? Object.assign({}, ...overrides) : undefined;
@@ -638,6 +641,7 @@ async function doBuild(
wildcard: mergedWildcard,
overrides: mergedOverrides,
framework,
crons: mergedCrons,
};
await fs.writeJSON(join(outputDir, 'config.json'), config, { spaces: 2 });
@@ -701,7 +705,7 @@ function expandBuild(files: string[], build: Builder): Builder[] {
throw new NowBuildError({
code: `invalid_build_specification`,
message: 'Field `use` is missing in build specification',
link: 'https://vercel.com/docs/configuration#project/builds',
link: 'https://vercel.com/docs/concepts/projects/project-configuration#builds',
action: 'View Documentation',
});
}
@@ -711,7 +715,7 @@ function expandBuild(files: string[], build: Builder): Builder[] {
throw new NowBuildError({
code: `invalid_build_specification`,
message: 'A build `src` path resolves to an empty string',
link: 'https://vercel.com/docs/configuration#project/builds',
link: 'https://vercel.com/docs/concepts/projects/project-configuration#builds',
action: 'View Documentation',
});
}
@@ -746,6 +750,18 @@ function mergeImages(
return images;
}
function mergeCrons(
crons: BuildOutputConfig['crons'] = [],
buildResults: Iterable<BuildResult | BuildOutputConfig>
): BuildOutputConfig['crons'] {
for (const result of buildResults) {
if ('crons' in result && result.crons) {
crons = crons.concat(result.crons);
}
}
return crons;
}
function mergeWildcard(
buildResults: Iterable<BuildResult | BuildOutputConfig>
): BuildResultV2Typical['wildcard'] {

View File

@@ -13,7 +13,6 @@ import {
Builder,
BuildResultV2,
BuildResultV3,
Cron,
File,
FileFsRef,
BuilderV2,
@@ -41,7 +40,6 @@ export const OUTPUT_DIR = join(VERCEL_DIR, 'output');
* An entry in the "functions" object in `vercel.json`.
*/
interface FunctionConfiguration {
cron?: Cron;
memory?: number;
maxDuration?: number;
}
@@ -372,14 +370,12 @@ async function writeLambda(
throw new Error('Malformed `Lambda` - no "files" present');
}
const cron = functionConfiguration?.cron ?? lambda.cron;
const memory = functionConfiguration?.memory ?? lambda.memory;
const maxDuration = functionConfiguration?.maxDuration ?? lambda.maxDuration;
const config = {
...lambda,
handler: normalizePath(lambda.handler),
cron,
memory,
maxDuration,
type: undefined,

View File

@@ -22,7 +22,7 @@ const { format, inspect } = require('util');
/**
* An simple output helper which accumulates error and debug log messages in
* memory for potential persistance to disk while immediately outputting errors
* memory for potential persistence to disk while immediately outputting errors
* and debug messages, when the `--debug` flag is set, to `stderr`.
*/
class WorkerOutput {

View File

@@ -76,6 +76,10 @@ async function isGlobal() {
return true;
}
if (installPath.includes(['', 'fnm', 'node-versions', ''].join(sep))) {
return true;
}
const prefixPath =
process.env.PREFIX ||
process.env.npm_config_prefix ||

View File

@@ -93,6 +93,29 @@ const imagesSchema = {
},
};
const cronsSchema = {
type: 'array',
minItems: 0,
items: {
type: 'object',
additionalProperties: false,
required: ['path', 'schedule'],
properties: {
path: {
type: 'string',
minLength: 1,
maxLength: 512,
pattern: '^/.*',
},
schedule: {
type: 'string',
minLength: 9,
maxLength: 256,
},
},
},
};
const vercelConfigSchema = {
type: 'object',
// These are not all possibilities because `vc dev`
@@ -108,6 +131,7 @@ const vercelConfigSchema = {
trailingSlash: trailingSlashSchema,
functions: functionsSchema,
images: imagesSchema,
crons: cronsSchema,
},
};

View File

@@ -1,73 +0,0 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# Runtime data
pids
*.pid
*.seed
*.pid.lock
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
# nyc test coverage
.nyc_output
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# Bower dependency directory (https://bower.io/)
bower_components
# node-waf configuration
.lock-wscript
# Compiled binary addons (http://nodejs.org/api/addons.html)
build/Release
# Dependency directories
node_modules/
jspm_packages/
# Typescript v1 declaration files
typings/
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
*.tgz
# dotenv environment variables file
.env
.env.build
# gatsby files
.cache/
public
# Mac files
.DS_Store
# Yarn
yarn-error.log
.pnp/
.pnp.js
# Yarn Integrity file
.yarn-integrity
.now
.vercel

View File

@@ -1,7 +0,0 @@
/**
* Implement Gatsby's Browser APIs in this file.
*
* See: https://www.gatsbyjs.org/docs/browser-apis/
*/
// You can delete this file if you're not using it

View File

@@ -1,16 +0,0 @@
module.exports = {
siteMetadata: {
title: 'Gatsby Default Starter',
},
plugins: [
{
resolve: `gatsby-plugin-manifest`,
options: {
name: `05-gatsby`,
short_name: `starter`,
start_url: `/`,
icon: 'src/images/gatsby-icon.png',
},
},
],
};

View File

@@ -1,7 +0,0 @@
/**
* Implement Gatsby's Node APIs in this file.
*
* See: https://www.gatsbyjs.org/docs/node-apis/
*/
// You can delete this file if you're not using it

View File

@@ -1,7 +0,0 @@
/**
* Implement Gatsby's SSR (Server Side Rendering) APIs in this file.
*
* See: https://www.gatsbyjs.org/docs/ssr-apis/
*/
// You can delete this file if you're not using it

View File

@@ -1,16 +0,0 @@
{
"private": true,
"name": "gatsby",
"version": "1.0.0",
"dependencies": {
"gatsby": "^2.18.14",
"gatsby-image": "^2.0.15",
"gatsby-plugin-manifest": "^2.0.5",
"react": "^16.5.1",
"react-dom": "^16.5.1"
},
"scripts": {
"dev": "gatsby develop",
"build": "gatsby build"
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

View File

@@ -1,11 +0,0 @@
import React from 'react';
function Custom404() {
return (
<main>
<h1>Custom Gatsby 404</h1>
</main>
);
}
export default Custom404;

View File

@@ -1,12 +0,0 @@
import React from 'react';
function Index() {
return (
<main>
<h1>Gatsby Default Starter</h1>
<p>Hello World</p>
</main>
);
}
export default Index;

File diff suppressed because it is too large Load Diff

View File

@@ -1,12 +0,0 @@
*.log
.cache
.DS_Store
src/.temp
node_modules
dist
.env
.env.*
!yarn.lock
.now
.vercel

View File

@@ -1,2 +0,0 @@
README.md
yarn.lock

View File

@@ -1,19 +0,0 @@
# Gridsome Example
This directory is a brief example of a [Gridsome](https://gridsome.org/) app that can be deployed to Vercel with zero configuration.
## How we created this example
To get started with Gridsome on Vercel, you can use the [Gridsome CLI](https://gridsome.org/docs/gridsome-cli/) to initialize the project:
```shell
$ gridsome create my-website
```
## Deploying this Example
Once initialized, you can deploy the Gridsome example with just a single command:
```shell
$ vercel
```

View File

@@ -1,10 +0,0 @@
// This is where project configuration and plugin options are located.
// Learn more: https://gridsome.org/docs/config
// Changes here require a server restart.
// To restart press CTRL + C in terminal and run `gridsome develop`
module.exports = {
siteName: 'Gridsome',
plugins: []
}

View File

@@ -1,16 +0,0 @@
// Server API makes it possible to hook into various parts of Gridsome
// on server-side and add custom data to the GraphQL data layer.
// Learn more: https://gridsome.org/docs/server-api
// Changes here require a server restart.
// To restart press CTRL + C in terminal and run `gridsome develop`
module.exports = function (api) {
api.loadSource(({ addContentType }) => {
// Use the Data Store API here: https://gridsome.org/docs/data-store-api
})
api.createPages(({ createPage }) => {
// Use the Pages API here: https://gridsome.org/docs/pages-api
})
}

View File

@@ -1,15 +0,0 @@
{
"name": "gridsomee",
"private": true,
"scripts": {
"build": "gridsome build",
"dev": "gridsome develop -p $PORT",
"explore": "gridsome explore"
},
"dependencies": {
"gridsome": "0.7.23"
},
"engines": {
"node": "14.x"
}
}

View File

@@ -1,4 +0,0 @@
Add components that will be imported to Pages and Layouts to this folder.
Learn more about components here: https://gridsome.org/docs/components
You can delete this file.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

View File

@@ -1,50 +0,0 @@
<template>
<div class="layout">
<header class="header">
<strong>
<g-link to="/">{{ $static.metaData.siteName }}</g-link>
</strong>
<nav class="nav">
<g-link class="nav__link" to="/">Home</g-link>
<g-link class="nav__link" to="/about">About</g-link>
</nav>
</header>
<slot/>
</div>
</template>
<static-query>
query {
metaData {
siteName
}
}
</static-query>
<style>
body {
font-family: -apple-system,system-ui,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif;
margin:0;
padding:0;
line-height: 1.5;
}
.layout {
max-width: 760px;
margin: 0 auto;
padding-left: 20px;
padding-right: 20px;
}
.header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
height: 80px;
}
.nav__link {
margin-left: 20px;
}
</style>

View File

@@ -1,5 +0,0 @@
Layout components are used to wrap pages and templates. Layouts should contain components like headers, footers or sidebars that will be used across the site.
Learn more about Layouts: https://gridsome.org/docs/layouts
You can delete this file.

View File

@@ -1,9 +0,0 @@
// This is the main.js file. Import global CSS and scripts here.
// The Client API can be used here. Learn more: gridsome.org/docs/client-api
import DefaultLayout from '~/layouts/Default.vue'
export default function (Vue, { router, head, isClient }) {
// Set default layout as a global component
Vue.component('Layout', DefaultLayout)
}

View File

@@ -1,14 +0,0 @@
<template>
<Layout>
<h1>Not Found</h1>
<p>This is a Custom Gridsome 404.</p>
</Layout>
</template>
<script>
export default {
metaInfo: {
title: 'Not Found'
}
}
</script>

View File

@@ -1,14 +0,0 @@
<template>
<Layout>
<h1>About us</h1>
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Error doloremque omnis animi, eligendi magni a voluptatum, vitae, consequuntur rerum illum odit fugit assumenda rem dolores inventore iste reprehenderit maxime! Iusto.</p>
</Layout>
</template>
<script>
export default {
metaInfo: {
title: 'About us'
}
}
</script>

View File

@@ -1,33 +0,0 @@
<template>
<Layout>
<!-- Learn how to use images here: https://gridsome.org/docs/images -->
<g-image alt="Example image" src="~/favicon.png" width="135" />
<h1>Gridsome on Vercel</h1>
<p>
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Pariatur excepturi labore tempore expedita, et iste tenetur suscipit explicabo! Dolores, aperiam non officia eos quod asperiores
</p>
<p class="home-links">
<a href="https://gridsome.org/docs" target="_blank" rel="noopener">Gridsome Docs</a>
<a href="https://github.com/gridsome/gridsome" target="_blank" rel="noopener">GitHub</a>
</p>
</Layout>
</template>
<script>
export default {
metaInfo: {
title: 'Hello, world!'
}
}
</script>
<style>
.home-links a {
margin-right: 1rem;
}
</style>

View File

@@ -1,5 +0,0 @@
Pages are usually used for normal pages or for listing items from a GraphQL collection.
Add .vue files here to create pages. For example **About.vue** will be **site.com/about**.
Learn more about pages: https://gridsome.org/docs/pages
You can delete this file.

View File

@@ -1,7 +0,0 @@
Templates for **GraphQL collections** should be added here.
To create a template for a collection called `WordPressPost`
create a file named `WordPressPost.vue` in this folder.
Learn more: https://gridsome.org/docs/templates
You can delete this file.

View File

@@ -1,3 +0,0 @@
Add static files here. Files in this directory will be copied directly to `dist` folder during build. For example, /static/robots.txt will be located at https://yoursite.com/robots.txt.
This file should be deleted.

View File

@@ -1,3 +0,0 @@
{
"redirects": [{ "source": "/support", "destination": "/about?ref=support" }]
}

File diff suppressed because it is too large Load Diff

View File

@@ -105,7 +105,7 @@ test('[vercel dev] throws an error when an edge function has no response', async
expect(await res.status).toBe(500);
expect(await res.text()).toMatch('FUNCTION_INVOCATION_FAILED');
expect(stdout).toMatch(
/Error from API Route api\/edge-no-response: Edge Function "api\/edge-no-response.js" did not return a response./g
/Error from API Route \/api\/edge-no-response: Edge Function "api\/edge-no-response.js" did not return a response./g
);
} finally {
await dev.kill();
@@ -161,7 +161,7 @@ test('[vercel dev] should handle runtime errors thrown in edge functions', async
/<strong>500<\/strong>: INTERNAL_SERVER_ERROR/g
);
expect(stdout).toMatch(
/Error from API Route api\/edge-error-runtime: intentional runtime error/g
/Error from API Route \/api\/edge-error-runtime: intentional runtime error/g
);
} finally {
await dev.kill();

View File

@@ -70,27 +70,6 @@ test(
await testPath(200, '/', /React App/m);
})
);
/*
test(
'[vercel dev] 05-gatsby',
testFixtureStdio('05-gatsby', async (testPath: any) => {
await testPath(200, '/', /Gatsby Default Starter/m);
})
);
*/
test(
'[vercel dev] 06-gridsome',
testFixtureStdio('06-gridsome', async (testPath: any) => {
await testPath(200, '/');
await testPath(200, '/about');
await testPath(308, '/support', 'Redirecting to /about?ref=support (308)', {
Location: '/about?ref=support',
});
// Bug with gridsome's dev server: https://github.com/gridsome/gridsome/issues/831
// Works in prod only so leave out for now
// await testPath(404, '/nothing');
})
);
test(
'[vercel dev] 07-hexo-node',

View File

@@ -10,7 +10,7 @@ const { version: cliVersion } = require('../../package.json');
const {
fetchCachedToken,
} = require('../../../../test/lib/deployment/now-deploy');
const { spawnSync } = require('child_process');
const { spawnSync, execFileSync } = require('child_process');
jest.setTimeout(6 * 60 * 1000);
@@ -522,7 +522,7 @@ async function ps(parentPid, pids = {}) {
: ['ps', '-o', 'pid', '--no-headers', '--ppid', parentPid];
try {
const { stdout: buf } = spawnSync(cmd[0], cmd.slice(1), {
const buf = execFileSync(cmd[0], cmd.slice(1), {
encoding: 'utf-8',
});
for (let pid of buf.match(/\d+/g)) {

View File

@@ -0,0 +1,7 @@
{
"orgId": ".",
"projectId": ".",
"settings": {
"framework": null
}
}

View File

@@ -1,3 +1,3 @@
export default function (req, res) {
res.json('hello from the edge');
res.send('Hello from cron job!');
}

View File

@@ -0,0 +1,9 @@
const fs = require('fs');
const path = require('path');
fs.rmSync(path.join(__dirname, '.vercel', 'output'), { recursive: true });
fs.mkdirSync(path.join(__dirname, '.vercel', 'output'));
fs.copyFileSync(
path.join(__dirname, 'output', 'config.json'),
path.join(__dirname, '.vercel', 'output', 'config.json')
);

View File

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

View File

@@ -0,0 +1,9 @@
{
"version": 3,
"crons": [
{
"path": "/api/cron-job-build-output",
"schedule": "0 0 * * *"
}
]
}

View File

@@ -0,0 +1,5 @@
{
"scripts": {
"build": "node build"
}
}

View File

@@ -0,0 +1,8 @@
{
"crons": [
{
"path": "/api/cron-job",
"schedule": "0 0 * * *"
}
]
}

View File

@@ -1,3 +1,3 @@
export default function (req, res) {
res.end('serverless says hello');
res.send('Hello from cron job!');
}

View File

@@ -1,17 +0,0 @@
export const config = {
runtime: 'edge',
cron: '* * * * *',
};
export default async function edge(request, event) {
const requestBody = await request.text();
return new Response(
JSON.stringify({
headerContentType: request.headers.get('content-type'),
url: request.url,
method: request.method,
body: requestBody,
})
);
}

View File

@@ -1,7 +0,0 @@
export default function (req, res) {
res.json({ memory: parseInt(process.env.AWS_LAMBDA_FUNCTION_MEMORY_SIZE) });
}
export const config = {
cron: '* * * * *',
};

View File

@@ -1,10 +1,8 @@
{
"functions": {
"api/overwrite/serverless.js": {
"cron": "0 10-20 * * *"
},
"api/overwrite/edge.js": {
"cron": "10 * * * *"
"crons": [
{
"path": "/api/cron-job",
"schedule": "0 0 * * *"
}
}
]
}

View File

@@ -271,7 +271,7 @@ module.exports = async function prepare(session, binaryPath, tmpFixturesDir) {
},
}),
},
'lambda-with-200-memory': {
'lambda-with-123-memory': {
'api/memory.js': `
module.exports = (req, res) => {
res.json({ memory: parseInt(process.env.AWS_LAMBDA_FUNCTION_MEMORY_SIZE) });
@@ -280,7 +280,7 @@ module.exports = async function prepare(session, binaryPath, tmpFixturesDir) {
'now.json': JSON.stringify({
functions: {
'api/**/*.js': {
memory: 200,
memory: 123,
},
},
}),

View File

@@ -1967,7 +1967,11 @@ test('try to create a builds deployments with wrong now.json', async t => {
'Error: Invalid now.json - should NOT have additional property `builder`. Did you mean `builds`?'
)
);
t.true(stderr.includes('https://vercel.com/docs/configuration'));
t.true(
stderr.includes(
'https://vercel.com/docs/concepts/projects/project-configuration'
)
);
});
test('try to create a builds deployments with wrong vercel.json', async t => {
@@ -1991,7 +1995,11 @@ test('try to create a builds deployments with wrong vercel.json', async t => {
'Error: Invalid vercel.json - should NOT have additional property `fake`. Please remove it.'
)
);
t.true(stderr.includes('https://vercel.com/docs/configuration'));
t.true(
stderr.includes(
'https://vercel.com/docs/concepts/projects/project-configuration'
)
);
});
test('try to create a builds deployments with wrong `build.env` property', async t => {
@@ -2014,7 +2022,9 @@ test('try to create a builds deployments with wrong `build.env` property', async
formatOutput({ stdout, stderr })
);
t.true(
stderr.includes('https://vercel.com/docs/configuration'),
stderr.includes(
'https://vercel.com/docs/concepts/projects/project-configuration'
),
formatOutput({ stdout, stderr })
);
});
@@ -2625,7 +2635,7 @@ test('next unsupported functions config shows warning link', async t => {
t.is(output.exitCode, 0, formatOutput(output));
t.regex(
output.stderr,
/Ignoring function property `runtime`\. When using Next\.js, only `memory`, `maxDuration`, and `cron` can be used\./gm,
/Ignoring function property `runtime`\. When using Next\.js, only `memory` and `maxDuration` can be used\./gm,
formatOutput(output)
);
t.regex(
@@ -2719,11 +2729,15 @@ test('deploy a Lambda with 128MB of memory', async t => {
});
test('fail to deploy a Lambda with an incorrect value for of memory', async t => {
const directory = fixture('lambda-with-200-memory');
const directory = fixture('lambda-with-123-memory');
const output = await execute([directory, '--yes']);
t.is(output.exitCode, 1, formatOutput(output));
t.regex(output.stderr, /steps of 64/gm, formatOutput(output));
t.regex(
output.stderr,
/Serverless Functions.+memory/gm,
formatOutput(output)
);
t.regex(output.stderr, /Learn More/gm, formatOutput(output));
});

View File

@@ -1080,7 +1080,7 @@ describe('build', () => {
await expect(client.stderr).toOutput(
'Error: Invalid vercel.json - `rewrites[2]` should NOT have additional property `src`. Did you mean `source`?' +
'\n' +
'View Documentation: https://vercel.com/docs/configuration#project/rewrites'
'View Documentation: https://vercel.com/docs/concepts/projects/project-configuration#rewrites'
);
const builds = await fs.readJSON(join(output, 'builds.json'));
expect(builds.builds).toBeUndefined();
@@ -1091,7 +1091,7 @@ describe('build', () => {
stack: expect.stringContaining('at validateConfig'),
hideStackTrace: true,
code: 'INVALID_VERCEL_CONFIG',
link: 'https://vercel.com/docs/configuration#project/rewrites',
link: 'https://vercel.com/docs/concepts/projects/project-configuration#rewrites',
action: 'View Documentation',
});
const configJson = await fs.readJSON(join(output, 'config.json'));
@@ -1104,32 +1104,46 @@ describe('build', () => {
it('should include crons property in build output', async () => {
const cwd = fixture('with-cron');
const output = join(cwd, '.vercel', 'output', 'functions', 'api');
const output = join(cwd, '.vercel', 'output');
try {
process.chdir(cwd);
const exitCode = await build(client);
expect(exitCode).toBe(0);
const edge = await fs.readJSON(
join(output, 'edge.func', '.vc-config.json')
);
expect(edge).toHaveProperty('cron', '* * * * *');
const config = await fs.readJSON(join(output, 'config.json'));
expect(config).toHaveProperty('crons', [
{
path: '/api/cron-job',
schedule: '0 0 * * *',
},
]);
} finally {
process.chdir(originalCwd);
delete process.env.__VERCEL_BUILD_RUNNING;
}
});
const serverless = await fs.readJSON(
join(output, 'serverless.func', '.vc-config.json')
);
expect(serverless).toHaveProperty('cron', '* * * * *');
it('should merge crons property from build output with vercel.json crons property', async () => {
const cwd = fixture('with-cron-merge');
const output = join(cwd, '.vercel', 'output');
const overwriteServerless = await fs.readJSON(
join(output, 'overwrite', 'serverless.func', '.vc-config.json')
);
expect(overwriteServerless).toHaveProperty('cron', '0 10-20 * * *');
try {
process.chdir(cwd);
const exitCode = await build(client);
expect(exitCode).toBe(0);
const overwriteEdge = await fs.readJSON(
join(output, 'overwrite', 'edge.func', '.vc-config.json')
);
expect(overwriteEdge).toHaveProperty('cron', '10 * * * *');
const config = await fs.readJSON(join(output, 'config.json'));
expect(config).toHaveProperty('crons', [
{
path: '/api/cron-job',
schedule: '0 0 * * *',
},
{
path: '/api/cron-job-build-output',
schedule: '0 0 * * *',
},
]);
} finally {
process.chdir(originalCwd);
delete process.env.__VERCEL_BUILD_RUNNING;

View File

@@ -41,7 +41,7 @@ describe('validateConfig', () => {
'Invalid vercel.json - `rewrites[0]` should NOT have additional property `src`. Did you mean `source`?'
);
expect(error!.link).toEqual(
'https://vercel.com/docs/configuration#project/rewrites'
'https://vercel.com/docs/concepts/projects/project-configuration#rewrites'
);
});
@@ -54,7 +54,7 @@ describe('validateConfig', () => {
'Invalid vercel.json - `routes[0]` should NOT have additional property `source`. Did you mean `src`?'
);
expect(error!.link).toEqual(
'https://vercel.com/docs/configuration#project/routes'
'https://vercel.com/docs/concepts/projects/project-configuration#routes'
);
});
@@ -67,7 +67,7 @@ describe('validateConfig', () => {
'Invalid vercel.json - `routes` should be array.'
);
expect(error!.link).toEqual(
'https://vercel.com/docs/configuration#project/routes'
'https://vercel.com/docs/concepts/projects/project-configuration#routes'
);
});
@@ -84,7 +84,7 @@ describe('validateConfig', () => {
'Invalid vercel.json - `redirects[0]` missing required property `source`.'
);
expect(error!.link).toEqual(
'https://vercel.com/docs/configuration#project/redirects'
'https://vercel.com/docs/concepts/projects/project-configuration#redirects'
);
});
@@ -97,7 +97,7 @@ describe('validateConfig', () => {
'Invalid vercel.json - `redirects[0].permanent` should be boolean.'
);
expect(error!.link).toEqual(
'https://vercel.com/docs/configuration#project/redirects'
'https://vercel.com/docs/concepts/projects/project-configuration#redirects'
);
});
@@ -110,7 +110,7 @@ describe('validateConfig', () => {
'Invalid vercel.json - `cleanUrls` should be boolean.'
);
expect(error!.link).toEqual(
'https://vercel.com/docs/configuration#project/cleanurls'
'https://vercel.com/docs/concepts/projects/project-configuration#cleanurls'
);
});
@@ -123,7 +123,7 @@ describe('validateConfig', () => {
'Invalid vercel.json - `trailingSlash` should be boolean.'
);
expect(error!.link).toEqual(
'https://vercel.com/docs/configuration#project/trailingslash'
'https://vercel.com/docs/concepts/projects/project-configuration#trailingslash'
);
});
@@ -136,7 +136,7 @@ describe('validateConfig', () => {
'Invalid vercel.json - `headers[0]` should NOT have additional property `Content-Type`. Please remove it.'
);
expect(error!.link).toEqual(
'https://vercel.com/docs/configuration#project/headers'
'https://vercel.com/docs/concepts/projects/project-configuration#headers'
);
});
@@ -149,7 +149,7 @@ describe('validateConfig', () => {
'Invalid vercel.json - `headers[0].source` should be string.'
);
expect(error!.link).toEqual(
'https://vercel.com/docs/configuration#project/headers'
'https://vercel.com/docs/concepts/projects/project-configuration#headers'
);
});
@@ -162,7 +162,7 @@ describe('validateConfig', () => {
'Invalid vercel.json - `headers[0]` should NOT have additional property `stuff`. Please remove it.'
);
expect(error!.link).toEqual(
'https://vercel.com/docs/configuration#project/headers'
'https://vercel.com/docs/concepts/projects/project-configuration#headers'
);
});
@@ -175,7 +175,7 @@ describe('validateConfig', () => {
'Invalid vercel.json - `headers[0].headers[0]` should NOT have additional property `Content-Type`. Please remove it.'
);
expect(error!.link).toEqual(
'https://vercel.com/docs/configuration#project/headers'
'https://vercel.com/docs/concepts/projects/project-configuration#headers'
);
});
@@ -190,7 +190,7 @@ describe('validateConfig', () => {
'Invalid vercel.json - `headers[0].headers[0]` should NOT have additional property `val`. Please remove it.'
);
expect(error!.link).toEqual(
'https://vercel.com/docs/configuration#project/headers'
'https://vercel.com/docs/concepts/projects/project-configuration#headers'
);
});
@@ -205,7 +205,7 @@ describe('validateConfig', () => {
'Invalid vercel.json - `redirects` should NOT have more than 1024 items.'
);
expect(error!.link).toEqual(
'https://vercel.com/docs/configuration#project/redirects'
'https://vercel.com/docs/concepts/projects/project-configuration#redirects'
);
});
@@ -229,7 +229,39 @@ describe('validateConfig', () => {
'Invalid vercel.json - `headers[1].headers` should NOT have more than 1024 items.'
);
expect(error!.link).toEqual(
'https://vercel.com/docs/configuration#project/headers'
'https://vercel.com/docs/concepts/projects/project-configuration#headers'
);
});
it('should error with too low memory value', async () => {
const error = validateConfig({
functions: {
'api/test.js': {
memory: 127,
},
},
});
expect(error!.message).toEqual(
"Invalid vercel.json - `functions['api/test.js'].memory` should be >= 128."
);
expect(error!.link).toEqual(
'https://vercel.com/docs/concepts/projects/project-configuration#functions'
);
});
it('should error with too high memory value', async () => {
const error = validateConfig({
functions: {
'api/test.js': {
memory: 3009,
},
},
});
expect(error!.message).toEqual(
"Invalid vercel.json - `functions['api/test.js'].memory` should be <= 3008."
);
expect(error!.link).toEqual(
'https://vercel.com/docs/concepts/projects/project-configuration#functions'
);
});
@@ -253,4 +285,90 @@ describe('validateConfig', () => {
expect(error!.link).toEqual('https://vercel.link/functions-and-builds');
});
it('should error when crons have missing schedule', () => {
const error = validateConfig({
// @ts-ignore
crons: [{ path: '/api/test.js' }],
});
expect(error!.message).toEqual(
'Invalid vercel.json - `crons[0]` missing required property `schedule`.'
);
expect(error!.link).toEqual(
'https://vercel.com/docs/concepts/projects/project-configuration#crons'
);
});
it('should error when crons have missing path', () => {
const error = validateConfig({
// @ts-ignore
crons: [{ schedule: '* * * * *' }],
});
expect(error!.message).toEqual(
'Invalid vercel.json - `crons[0]` missing required property `path`.'
);
expect(error!.link).toEqual(
'https://vercel.com/docs/concepts/projects/project-configuration#crons'
);
});
it('should error when path is too long', () => {
const error = validateConfig({
crons: [{ path: '/' + 'x'.repeat(512), schedule: '* * * * *' }],
});
expect(error!.message).toEqual(
'Invalid vercel.json - `crons[0].path` should NOT be longer than 512 characters.'
);
expect(error!.link).toEqual(
'https://vercel.com/docs/concepts/projects/project-configuration#crons'
);
});
it('should error when schedule is too long', () => {
const error = validateConfig({
crons: [{ path: '/', schedule: '*'.repeat(257) }],
});
expect(error!.message).toEqual(
'Invalid vercel.json - `crons[0].schedule` should NOT be longer than 256 characters.'
);
expect(error!.link).toEqual(
'https://vercel.com/docs/concepts/projects/project-configuration#crons'
);
});
it('should error when path is empty', () => {
const error = validateConfig({
crons: [{ path: '', schedule: '* * * * *' }],
});
expect(error!.message).toEqual(
'Invalid vercel.json - `crons[0].path` should NOT be shorter than 1 characters.'
);
expect(error!.link).toEqual(
'https://vercel.com/docs/concepts/projects/project-configuration#crons'
);
});
it('should error when schedule is too short', () => {
const error = validateConfig({
crons: [{ path: '/', schedule: '* * * * ' }],
});
expect(error!.message).toEqual(
'Invalid vercel.json - `crons[0].schedule` should NOT be shorter than 9 characters.'
);
expect(error!.link).toEqual(
'https://vercel.com/docs/concepts/projects/project-configuration#crons'
);
});
it("should error when path doesn't start with `/`", () => {
const error = validateConfig({
crons: [{ path: 'api/cron', schedule: '* * * * *' }],
});
expect(error!.message).toEqual(
'Invalid vercel.json - `crons[0].path` should match pattern "^/.*".'
);
expect(error!.link).toEqual(
'https://vercel.com/docs/concepts/projects/project-configuration#crons'
);
});
});

View File

@@ -1,6 +1,6 @@
{
"name": "@vercel/client",
"version": "12.3.8",
"version": "12.4.0",
"main": "dist/index.js",
"typings": "dist/index.d.ts",
"homepage": "https://vercel.com",
@@ -15,7 +15,7 @@
},
"scripts": {
"build": "tsc",
"test-integration-once": "pnpm test tests/create-deployment.test.ts tests/create-legacy-deployment.test.ts tests/paths.test.ts",
"test-e2e": "pnpm test tests/create-deployment.test.ts tests/create-legacy-deployment.test.ts tests/paths.test.ts",
"test": "jest --env node --verbose --runInBand --bail",
"test-unit": "pnpm test tests/unit.*test.*"
},
@@ -43,8 +43,8 @@
]
},
"dependencies": {
"@vercel/build-utils": "6.2.2",
"@vercel/routing-utils": "2.1.8",
"@vercel/build-utils": "6.3.0",
"@vercel/routing-utils": "2.1.9",
"@zeit/fetch": "5.2.0",
"async-retry": "1.2.3",
"async-sema": "3.0.0",

View File

@@ -3,6 +3,7 @@ import type {
BuilderFunctions,
Images,
ProjectSettings,
Cron,
} from '@vercel/build-utils';
import type { Header, Route, Redirect, Rewrite } from '@vercel/routing-utils';
@@ -154,6 +155,7 @@ export interface VercelConfig {
framework?: string | null;
outputDirectory?: string | null;
images?: Images;
crons?: Cron[];
}
export interface GitMetadata {

View File

@@ -1,6 +1,6 @@
{
"name": "@vercel/frameworks",
"version": "1.3.0",
"version": "1.3.1",
"main": "./dist/frameworks.js",
"types": "./dist/frameworks.d.ts",
"files": [
@@ -21,7 +21,7 @@
"@types/js-yaml": "3.12.1",
"@types/node": "14.18.33",
"@types/node-fetch": "2.5.8",
"@vercel/routing-utils": "2.1.8",
"@vercel/routing-utils": "2.1.9",
"ajv": "6.12.2",
"typescript": "4.3.4"
}

View File

@@ -1,6 +1,6 @@
{
"name": "@vercel/fs-detectors",
"version": "3.7.11",
"version": "3.8.0",
"description": "Vercel filesystem detectors",
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
@@ -20,8 +20,8 @@
},
"dependencies": {
"@vercel/error-utils": "1.0.8",
"@vercel/frameworks": "1.3.0",
"@vercel/routing-utils": "2.1.8",
"@vercel/frameworks": "1.3.1",
"@vercel/routing-utils": "2.1.9",
"glob": "8.0.3",
"js-yaml": "4.1.0",
"json5": "2.2.2",
@@ -35,7 +35,7 @@
"@types/minimatch": "3.0.5",
"@types/node": "14.18.33",
"@types/semver": "7.3.10",
"@vercel/build-utils": "6.2.2",
"@vercel/build-utils": "6.3.0",
"typescript": "4.3.4"
}
}

View File

@@ -602,12 +602,11 @@ function validateFunctions({ functions = {} }: Options) {
if (
func.memory !== undefined &&
(func.memory < 128 || func.memory > 3008 || func.memory % 64 !== 0)
(func.memory < 128 || func.memory > 3008)
) {
return {
code: 'invalid_function_memory',
message:
'Functions must have a memory value between 128 and 3008 in steps of 64.',
message: 'Functions must have a memory value between 128 and 3008',
};
}

View File

@@ -473,7 +473,7 @@ describe('Test `detectBuilders`', () => {
});
it('invalid function memory', async () => {
const functions = { 'pages/index.ts': { memory: 200 } };
const functions = { 'pages/index.ts': { memory: 127 } };
const files = ['pages/index.ts'];
const { builders, errors } = await detectBuilders(files, null, {
functions,
@@ -484,6 +484,17 @@ describe('Test `detectBuilders`', () => {
expect(errors![0].code).toBe('invalid_function_memory');
});
it('should build with function memory not dividable by 64', async () => {
const functions = { 'api/index.ts': { memory: 1000 } };
const files = ['api/index.ts'];
const { builders, errors } = await detectBuilders(files, null, {
functions,
});
expect(builders![0].use).toBe('@vercel/node');
expect(errors).toBeNull();
});
it('missing runtime version', async () => {
const functions = { 'pages/index.ts': { runtime: 'haha' } };
const files = ['pages/index.ts'];
@@ -1720,7 +1731,7 @@ describe('Test `detectBuilders` with `featHandleMiss=true`', () => {
});
it('invalid function memory', async () => {
const functions = { 'pages/index.ts': { memory: 200 } };
const functions = { 'pages/index.ts': { memory: 127 } };
const files = ['pages/index.ts'];
const { builders, errors } = await detectBuilders(files, null, {
functions,
@@ -1732,6 +1743,18 @@ describe('Test `detectBuilders` with `featHandleMiss=true`', () => {
expect(errors![0].code).toBe('invalid_function_memory');
});
it('should build with function memory not dividable by 64', async () => {
const functions = { 'api/index.ts': { memory: 1000 } };
const files = ['api/index.ts'];
const { builders, errors } = await detectBuilders(files, null, {
functions,
featHandleMiss,
});
expect(builders![0].use).toBe('@vercel/node');
expect(errors).toBeNull();
});
it('missing runtime version', async () => {
const functions = { 'pages/index.ts': { runtime: 'haha' } };
const files = ['pages/index.ts'];

View File

@@ -1,6 +1,6 @@
{
"name": "@vercel/gatsby-plugin-vercel-builder",
"version": "1.1.4",
"version": "1.1.7",
"main": "dist/index.js",
"files": [
"dist",
@@ -14,9 +14,9 @@
"build:src": "tsc -p tsconfig.src.json"
},
"dependencies": {
"@vercel/build-utils": "6.2.2",
"@vercel/node": "2.9.3",
"@vercel/routing-utils": "2.1.8",
"@vercel/build-utils": "6.3.0",
"@vercel/node": "2.9.6",
"@vercel/routing-utils": "2.1.9",
"ajv": "8.12.0",
"esbuild": "0.14.47",
"etag": "1.8.1",

View File

@@ -1,6 +1,6 @@
{
"name": "@vercel/go",
"version": "2.3.4",
"version": "2.3.7",
"license": "MIT",
"main": "./dist/index",
"homepage": "https://vercel.com/docs/runtimes#official-runtimes/go",
@@ -12,7 +12,7 @@
"scripts": {
"build": "node build",
"test": "jest --env node --verbose --runInBand --bail",
"test-integration-once": "pnpm test"
"test-e2e": "pnpm test"
},
"files": [
"dist"
@@ -36,7 +36,7 @@
"@types/node": "14.18.33",
"@types/node-fetch": "^2.3.0",
"@types/tar": "^4.0.0",
"@vercel/build-utils": "6.2.2",
"@vercel/build-utils": "6.3.0",
"@vercel/ncc": "0.24.0",
"async-retry": "1.3.1",
"execa": "^1.0.0",

View File

@@ -1,6 +1,6 @@
{
"name": "@vercel/hydrogen",
"version": "0.0.50",
"version": "0.0.53",
"license": "MIT",
"main": "./dist/index.js",
"homepage": "https://vercel.com/docs",
@@ -11,7 +11,7 @@
},
"scripts": {
"build": "node build.js",
"test-integration-once": "pnpm test test/test.js",
"test-e2e": "pnpm test test/test.js",
"test": "jest --env node --verbose --bail --runInBand"
},
"files": [
@@ -21,8 +21,8 @@
"devDependencies": {
"@types/jest": "27.5.1",
"@types/node": "14.18.33",
"@vercel/build-utils": "6.2.2",
"@vercel/static-config": "2.0.12",
"@vercel/build-utils": "6.3.0",
"@vercel/static-config": "2.0.13",
"execa": "3.2.0",
"fs-extra": "11.1.0",
"ts-morph": "12.0.0",

View File

@@ -1,6 +1,6 @@
{
"name": "@vercel/next",
"version": "3.4.3",
"version": "3.5.2",
"license": "MIT",
"main": "./dist/index",
"homepage": "https://vercel.com/docs/runtimes#official-runtimes/next-js",
@@ -11,7 +11,7 @@
"test-unit": "pnpm test test/unit/",
"test-next-local": "pnpm test test/integration/*.test.js test/integration/*.test.ts",
"test-next-local:middleware": "pnpm test test/integration/middleware.test.ts",
"test-integration-once": "rm -f test/builder-info.json; pnpm test test/fixtures/**/*.test.js"
"test-e2e": "rm -f test/builder-info.json; pnpm test test/fixtures/**/*.test.js"
},
"repository": {
"type": "git",
@@ -45,9 +45,9 @@
"@types/semver": "6.0.0",
"@types/text-table": "0.2.1",
"@types/webpack-sources": "3.2.0",
"@vercel/build-utils": "6.2.2",
"@vercel/build-utils": "6.3.0",
"@vercel/nft": "0.22.5",
"@vercel/routing-utils": "2.1.8",
"@vercel/routing-utils": "2.1.9",
"async-sema": "3.0.1",
"buffer-crc32": "0.2.13",
"bytes": "3.1.2",

View File

@@ -88,6 +88,8 @@ import {
PseudoLayerResult,
updateRouteSrc,
validateEntrypoint,
getOperationType,
isApiPage,
} from './utils';
export const version = 2;
@@ -1090,10 +1092,14 @@ export const build: BuildV2 = async ({
handler: '___next_launcher.cjs',
runtime: nodeVersion.runtime,
...lambdaOptions,
operationType: 'SSR',
operationType: 'SSR', // always SSR because we're in legacy mode
shouldAddHelpers: false,
shouldAddSourcemapSupport: false,
supportsMultiPayloads: !!process.env.NEXT_PRIVATE_MULTI_PAYLOAD,
framework: {
slug: 'nextjs',
version: nextVersion,
},
});
debug(`Created serverless function for page: "${page}"`);
})
@@ -1122,10 +1128,6 @@ export const build: BuildV2 = async ({
outputDirectory,
appPathRoutesManifest,
});
const isApiPage = (page: string) =>
page
.replace(/\\/g, '/')
.match(/(serverless|server)\/pages\/api(\/|\.js$)/);
const canUsePreviewMode = Object.keys(pages).some(page =>
isApiPage(pages[page].fsPath)
@@ -1594,6 +1596,10 @@ export const build: BuildV2 = async ({
internalPages: [],
});
for (const group of initialApiLambdaGroups) {
group.isApiLambda = true;
}
debug(
JSON.stringify(
{
@@ -1815,7 +1821,12 @@ export const build: BuildV2 = async ({
path.relative(baseDir, entryPath),
'___next_launcher.cjs'
),
operationType: getOperationType({
prerenderManifest,
pageFileName,
}),
runtime: nodeVersion.runtime,
nextVersion,
...lambdaOptions,
});
} else {
@@ -1834,7 +1845,9 @@ export const build: BuildV2 = async ({
path.relative(baseDir, entryPath),
'___next_launcher.cjs'
),
operationType: getOperationType({ pageFileName }), // can only be API or SSR
runtime: nodeVersion.runtime,
nextVersion,
...lambdaOptions,
});
}
@@ -2034,6 +2047,11 @@ export const build: BuildV2 = async ({
pageLambdaMap[page] = group.lambdaIdentifier;
}
const operationType = getOperationType({
group,
prerenderManifest,
});
lambdas[group.lambdaIdentifier] =
await createLambdaFromPseudoLayers({
files: {
@@ -2045,7 +2063,9 @@ export const build: BuildV2 = async ({
path.relative(baseDir, entryPath),
'___next_launcher.cjs'
),
operationType,
runtime: nodeVersion.runtime,
nextVersion,
});
}
)
@@ -2096,6 +2116,8 @@ export const build: BuildV2 = async ({
...Object.entries(prerenderManifest.fallbackRoutes),
...Object.entries(prerenderManifest.blockingFallbackRoutes),
].forEach(([, { dataRouteRegex, dataRoute }]) => {
if (!dataRoute || !dataRouteRegex) return;
dataRoutes.push({
// Next.js provided data route regex
src: dataRouteRegex.replace(
@@ -2644,7 +2666,10 @@ async function getServerlessPages(params: {
const [pages, appPaths, middlewareManifest] = await Promise.all([
glob('**/!(_middleware).js', params.pagesDir),
params.appPathRoutesManifest
? glob('**/page.js', path.join(params.pagesDir, '../app'))
? Promise.all([
glob('**/page.js', path.join(params.pagesDir, '../app')),
glob('**/route.js', path.join(params.pagesDir, '../app')),
]).then(items => Object.assign(...items))
: Promise.resolve({}),
getMiddlewareManifest(params.entryPath, params.outputDirectory),
]);

View File

@@ -43,6 +43,7 @@ import {
getMiddlewareBundle,
getFilesMapFromReasons,
UnwrapPromise,
getOperationType,
} from './utils';
import {
nodeFileTrace,
@@ -181,7 +182,10 @@ export async function serverBuild({
}
const pageMatchesApi = (page: string) => {
return page.startsWith('api/') || page === 'api.js';
return (
!appPathRoutesManifest?.[page] &&
(page.startsWith('api/') || page === 'api.js')
);
};
const { i18n } = routesManifest;
@@ -748,6 +752,10 @@ export async function serverBuild({
internalPages,
});
for (const group of apiLambdaGroups) {
group.isApiLambda = true;
}
debug(
JSON.stringify(
{
@@ -856,6 +864,8 @@ export async function serverBuild({
}
}
const operationType = getOperationType({ group, prerenderManifest });
const lambda = await createLambdaFromPseudoLayers({
files: {
...launcherFiles,
@@ -869,11 +879,12 @@ export async function serverBuild({
),
'___next_launcher.cjs'
),
operationType,
memory: group.memory,
runtime: nodeVersion.runtime,
maxDuration: group.maxDuration,
isStreaming: group.isStreaming,
cron: group.cron,
nextVersion,
});
for (const page of group.pages) {
@@ -974,6 +985,7 @@ export async function serverBuild({
routesManifest,
isCorrectMiddlewareOrder,
prerenderBypassToken: prerenderManifest.bypassToken || '',
nextVersion,
});
const isNextDataServerResolving =
@@ -1127,8 +1139,19 @@ export async function serverBuild({
// to match prerenders so we can route the same when the
// __rsc__ header is present
const edgeFunctions = middleware.edgeFunctions;
// allow looking up original route from normalized route
const inverseAppPathManifest: Record<string, string> = {};
for (const ogRoute of Object.keys(appPathRoutesManifest)) {
inverseAppPathManifest[appPathRoutesManifest[ogRoute]] = ogRoute;
}
for (let route of Object.values(appPathRoutesManifest)) {
const ogRoute = inverseAppPathManifest[route];
if (ogRoute.endsWith('/route')) {
continue;
}
route = path.posix.join('./', route === '/' ? '/index' : route);
if (lambdas[route]) {
@@ -1141,6 +1164,10 @@ export async function serverBuild({
}
const rscHeader = routesManifest.rsc?.header?.toLowerCase() || '__rsc__';
const rscVaryHeader =
routesManifest?.rsc?.varyHeader ||
'RSC, Next-Router-State-Tree, Next-Router-Prefetch';
const completeDynamicRoutes: typeof dynamicRoutes = [];
if (appDir) {
@@ -1413,7 +1440,9 @@ export async function serverBuild({
},
],
dest: path.posix.join('/', entryDirectory, '/index.rsc'),
headers: { vary: rscVaryHeader },
continue: true,
override: true,
},
{
src: `^${path.posix.join(
@@ -1428,7 +1457,9 @@ export async function serverBuild({
},
],
dest: path.posix.join('/', entryDirectory, '/$1.rsc'),
headers: { vary: rscVaryHeader },
continue: true,
override: true,
},
]
: []),

View File

@@ -15,7 +15,6 @@ import {
NodejsLambda,
EdgeFunction,
Images,
Cron,
} from '@vercel/build-utils';
import { NodeFileTraceReasons } from '@vercel/nft';
import type {
@@ -269,12 +268,7 @@ export async function getRoutesManifest(
if (shouldHaveManifest && !hasRoutesManifest) {
throw new NowBuildError({
message:
`The file "${pathRoutesManifest}" couldn't be found. This is normally caused by a misconfiguration in your project.\n` +
'Please check the following, and reach out to support if you cannot resolve the problem:\n' +
' 1. If present, be sure your `build` script in "package.json" calls `next build`.' +
' 2. Navigate to your project\'s settings in the Vercel dashboard, and verify that the "Build Command" is not overridden, or that it calls `next build`.' +
' 3. Navigate to your project\'s settings in the Vercel dashboard, and verify that the "Output Directory" is not overridden. Note that `next export` does **not** require you change this setting, even if you customize the `next export` output directory.',
message: `The file "${pathRoutesManifest}" couldn't be found. This is often caused by a misconfiguration in your project.`,
link: 'https://err.sh/vercel/vercel/now-next-routes-manifest',
code: 'NEXT_NO_ROUTES_MANIFEST',
});
@@ -780,6 +774,7 @@ export async function createPseudoLayer(files: {
interface CreateLambdaFromPseudoLayersOptions extends LambdaOptionsWithFiles {
layers: PseudoLayer[];
isStreaming?: boolean;
nextVersion?: string;
}
// measured with 1, 2, 5, 10, and `os.cpus().length || 5`
@@ -790,6 +785,7 @@ export async function createLambdaFromPseudoLayers({
files: baseFiles,
layers,
isStreaming,
nextVersion,
...lambdaOptions
}: CreateLambdaFromPseudoLayersOptions) {
await createLambdaSema.acquire();
@@ -833,6 +829,10 @@ export async function createLambdaFromPseudoLayers({
shouldAddHelpers: false,
shouldAddSourcemapSupport: false,
supportsMultiPayloads: !!process.env.NEXT_PRIVATE_MULTI_PAYLOAD,
framework: {
slug: 'nextjs',
version: nextVersion,
},
});
}
@@ -849,16 +849,18 @@ export type NextPrerenderedRoutes = {
staticRoutes: {
[route: string]: {
initialRevalidate: number | false;
dataRoute: string;
dataRoute: string | null;
srcRoute: string | null;
initialStatus?: number;
initialHeaders?: Record<string, string>;
};
};
blockingFallbackRoutes: {
[route: string]: {
routeRegex: string;
dataRoute: string;
dataRouteRegex: string;
dataRoute: string | null;
dataRouteRegex: string | null;
};
};
@@ -866,16 +868,16 @@ export type NextPrerenderedRoutes = {
[route: string]: {
fallback: string;
routeRegex: string;
dataRoute: string;
dataRouteRegex: string;
dataRoute: string | null;
dataRouteRegex: string | null;
};
};
omittedRoutes: {
[route: string]: {
routeRegex: string;
dataRoute: string;
dataRouteRegex: string;
dataRoute: string | null;
dataRouteRegex: string | null;
};
};
@@ -1068,6 +1070,30 @@ export async function getPrerenderManifest(
previewModeId: string;
};
notFoundRoutes?: string[];
}
| {
version: 4;
routes: {
[route: string]: {
initialRevalidateSeconds: number | false;
srcRoute: string | null;
dataRoute: string | null;
initialStatus?: number;
initialHeaders?: Record<string, string>;
};
};
dynamicRoutes: {
[route: string]: {
routeRegex: string;
fallback: string | false;
dataRoute: string | null;
dataRouteRegex: string | null;
};
};
preview: {
previewModeId: string;
};
notFoundRoutes?: string[];
} = JSON.parse(await fs.readFile(pathPrerenderManifest, 'utf8'));
switch (manifest.version) {
@@ -1122,7 +1148,8 @@ export async function getPrerenderManifest(
return ret;
}
case 2:
case 3: {
case 3:
case 4: {
const routes = Object.keys(manifest.routes);
const lazyRoutes = Object.keys(manifest.dynamicRoutes);
@@ -1143,6 +1170,15 @@ export async function getPrerenderManifest(
routes.forEach(route => {
const { initialRevalidateSeconds, dataRoute, srcRoute } =
manifest.routes[route];
let initialStatus: undefined | number;
let initialHeaders: undefined | Record<string, string>;
if (manifest.version === 4) {
initialStatus = manifest.routes[route].initialStatus;
initialHeaders = manifest.routes[route].initialHeaders;
}
ret.staticRoutes[route] = {
initialRevalidate:
initialRevalidateSeconds === false
@@ -1150,6 +1186,8 @@ export async function getPrerenderManifest(
: Math.max(1, initialRevalidateSeconds),
dataRoute,
srcRoute,
initialStatus,
initialHeaders,
};
});
@@ -1310,10 +1348,10 @@ export function addLocaleOrDefault(
export type LambdaGroup = {
pages: string[];
memory?: number;
cron?: Cron;
maxDuration?: number;
isStreaming?: boolean;
isPrerenders?: boolean;
isApiLambda: boolean;
pseudoLayer: PseudoLayer;
pseudoLayerBytes: number;
pseudoLayerUncompressedBytes: number;
@@ -1363,7 +1401,7 @@ export async function getPageLambdaGroups({
const routeName = normalizePage(page.replace(/\.js$/, ''));
const isPrerenderRoute = prerenderRoutes.has(routeName);
let opts: { memory?: number; maxDuration?: number; cron?: Cron } = {};
let opts: { memory?: number; maxDuration?: number } = {};
if (config && config.functions) {
const sourceFile = await getSourceFilePathFromPage({
@@ -1381,8 +1419,7 @@ export async function getPageLambdaGroups({
const matches =
group.maxDuration === opts.maxDuration &&
group.memory === opts.memory &&
group.isPrerenders === isPrerenderRoute &&
!opts.cron; // Functions with a cronjob must be on their own
group.isPrerenders === isPrerenderRoute;
if (matches) {
let newTracedFilesSize = group.pseudoLayerBytes;
@@ -1421,6 +1458,7 @@ export async function getPageLambdaGroups({
pages: [page],
...opts,
isPrerenders: isPrerenderRoute,
isApiLambda: !!isApiPage(page),
pseudoLayerBytes: initialPseudoLayer.pseudoLayerBytes,
pseudoLayerUncompressedBytes: initialPseudoLayerUncompressed,
pseudoLayer: Object.assign({}, initialPseudoLayer.pseudoLayer),
@@ -1652,7 +1690,7 @@ export const onPrerenderRouteInitial = (
const { initialRevalidate, srcRoute, dataRoute } = pr;
const route = srcRoute || routeKey;
const isAppPathRoute = appDir && dataRoute?.endsWith('.rsc');
const isAppPathRoute = appDir && (!dataRoute || dataRoute?.endsWith('.rsc'));
const routeNoLocale = routesManifest?.i18n
? normalizeLocalePath(routeKey, routesManifest.i18n.locales).pathname
@@ -1810,7 +1848,9 @@ export const onPrerenderRoute =
let initialRevalidate: false | number;
let srcRoute: string | null;
let dataRoute: string;
let dataRoute: string | null;
let initialStatus: number | undefined;
let initialHeaders: Record<string, string> | undefined;
if (isFallback || isBlocking) {
const pr = isFallback
@@ -1833,44 +1873,60 @@ export const onPrerenderRoute =
dataRoute = prerenderManifest.omittedRoutes[routeKey].dataRoute;
} else {
const pr = prerenderManifest.staticRoutes[routeKey];
({ initialRevalidate, srcRoute, dataRoute } = pr);
({
initialRevalidate,
srcRoute,
dataRoute,
initialHeaders,
initialStatus,
} = pr);
}
let isAppPathRoute = false;
// TODO: leverage manifest to determine app paths more accurately
if (appDir && srcRoute && dataRoute.endsWith('.rsc')) {
if (appDir && srcRoute && (!dataRoute || dataRoute?.endsWith('.rsc'))) {
isAppPathRoute = true;
}
const isOmittedOrNotFound = isOmitted || isNotFound;
const htmlFsRef =
isBlocking || (isNotFound && !static404Page)
? // Blocking pages do not have an HTML fallback
null
: new FileFsRef({
fsPath: path.join(
isAppPathRoute && !isOmittedOrNotFound && appDir
? appDir
: pagesDir,
isFallback
? // Fallback pages have a special file.
addLocaleOrDefault(
prerenderManifest.fallbackRoutes[routeKey].fallback,
routesManifest,
locale
)
: // Otherwise, the route itself should exist as a static HTML
// file.
`${
isOmittedOrNotFound
? addLocaleOrDefault('/404', routesManifest, locale)
: routeFileNoExt
}.html`
),
});
let htmlFsRef: FileFsRef | null;
if (appDir && !dataRoute && isAppPathRoute && !(isBlocking || isFallback)) {
const contentType = initialHeaders?.['content-type'];
htmlFsRef = new FileFsRef({
fsPath: path.join(appDir, `${routeFileNoExt}.body`),
contentType: contentType || 'text/html;charset=utf-8',
});
} else {
htmlFsRef =
isBlocking || (isNotFound && !static404Page)
? // Blocking pages do not have an HTML fallback
null
: new FileFsRef({
fsPath: path.join(
isAppPathRoute && !isOmittedOrNotFound && appDir
? appDir
: pagesDir,
isFallback
? // Fallback pages have a special file.
addLocaleOrDefault(
prerenderManifest.fallbackRoutes[routeKey].fallback,
routesManifest,
locale
)
: // Otherwise, the route itself should exist as a static HTML
// file.
`${
isOmittedOrNotFound
? addLocaleOrDefault('/404', routesManifest, locale)
: routeFileNoExt
}.html`
),
});
}
const jsonFsRef =
// JSON data does not exist for fallback or blocking pages
isFallback || isBlocking || (isNotFound && !static404Page)
isFallback || isBlocking || (isNotFound && !static404Page) || !dataRoute
? null
: new FileFsRef({
fsPath: path.join(
@@ -1908,16 +1964,20 @@ export const onPrerenderRoute =
);
let lambda: undefined | Lambda;
let outputPathData = path.posix.join(entryDirectory, dataRoute);
let outputPathData: null | string = null;
if (nonDynamicSsg || isFallback || isOmitted) {
outputPathData = outputPathData.replace(
new RegExp(`${escapeStringRegexp(origRouteFileNoExt)}.json$`),
// ensure we escape "$" correctly while replacing as "$" is a special
// character, we need to do double escaping as first is for the initial
// replace on the routeFile and then the second on the outputPath
`${routeFileNoExt.replace(/\$/g, '$$$$')}.json`
);
if (dataRoute) {
outputPathData = path.posix.join(entryDirectory, dataRoute);
if (nonDynamicSsg || isFallback || isOmitted) {
outputPathData = outputPathData.replace(
new RegExp(`${escapeStringRegexp(origRouteFileNoExt)}.json$`),
// ensure we escape "$" correctly while replacing as "$" is a special
// character, we need to do double escaping as first is for the initial
// replace on the routeFile and then the second on the outputPath
`${routeFileNoExt.replace(/\$/g, '$$$$')}.json`
);
}
}
if (isSharedLambdas) {
@@ -1954,7 +2014,7 @@ export const onPrerenderRoute =
if (htmlFsRef == null || jsonFsRef == null) {
throw new NowBuildError({
code: 'NEXT_HTMLFSREF_JSONFSREF',
message: 'invariant: htmlFsRef != null && jsonFsRef != null',
message: `invariant: htmlFsRef != null && jsonFsRef != null ${routeFileNoExt}`,
});
}
@@ -1966,7 +2026,10 @@ export const onPrerenderRoute =
) {
htmlFsRef.contentType = htmlContentType;
prerenders[outputPathPage] = htmlFsRef;
prerenders[outputPathData] = jsonFsRef;
if (outputPathData) {
prerenders[outputPathData] = jsonFsRef;
}
}
}
const isNotFoundPreview =
@@ -2028,10 +2091,10 @@ export const onPrerenderRoute =
allowQuery = [];
}
}
const rscVaryHeader =
routesManifest?.rsc?.varyHeader ||
'__rsc__, __next_router_state_tree__, __next_router_prefetch__';
const rscContentTypeHeader = routesManifest?.rsc?.contentTypeHeader || 'application/octet-stream';
const rscEnabled = !!routesManifest?.rsc;
const rscVaryHeader = routesManifest?.rsc?.varyHeader || 'RSC, Next-Router-State-Tree, Next-Router-Prefetch';
const rscContentTypeHeader = routesManifest?.rsc?.contentTypeHeader || 'text/x-component';
prerenders[outputPathPage] = new Prerender({
expiration: initialRevalidate,
@@ -2040,27 +2103,8 @@ export const onPrerenderRoute =
fallback: htmlFsRef,
group: prerenderGroup,
bypassToken: prerenderManifest.bypassToken,
...(isNotFound
? {
initialStatus: 404,
}
: {}),
...(isAppPathRoute
? {
initialHeaders: {
vary: rscVaryHeader,
},
}
: {}),
});
prerenders[outputPathData] = new Prerender({
expiration: initialRevalidate,
lambda,
allowQuery,
fallback: jsonFsRef,
group: prerenderGroup,
bypassToken: prerenderManifest.bypassToken,
initialStatus,
initialHeaders,
...(isNotFound
? {
@@ -2068,16 +2112,42 @@ export const onPrerenderRoute =
}
: {}),
...(isAppPathRoute
...(rscEnabled
? {
initialHeaders: {
'content-type': rscContentTypeHeader,
...initialHeaders,
vary: rscVaryHeader,
},
}
: {}),
});
if (outputPathData) {
prerenders[outputPathData] = new Prerender({
expiration: initialRevalidate,
lambda,
allowQuery,
fallback: jsonFsRef,
group: prerenderGroup,
bypassToken: prerenderManifest.bypassToken,
...(isNotFound
? {
initialStatus: 404,
}
: {}),
...(rscEnabled
? {
initialHeaders: {
'content-type': rscContentTypeHeader,
vary: rscVaryHeader,
},
}
: {}),
});
}
++prerenderGroup;
if (routesManifest?.i18n && isBlocking) {
@@ -2091,29 +2161,30 @@ export const onPrerenderRoute =
path.posix.join(entryDirectory, localeRouteFileNoExt),
isServerMode
);
const localeOutputPathData = outputPathData.replace(
new RegExp(`${escapeStringRegexp(origRouteFileNoExt)}.json$`),
`${localeRouteFileNoExt}${
localeRouteFileNoExt !== origRouteFileNoExt &&
origRouteFileNoExt === '/index'
? '/index'
: ''
}.json`
);
const origPrerenderPage = prerenders[outputPathPage];
const origPrerenderData = prerenders[outputPathData];
prerenders[localeOutputPathPage] = {
...origPrerenderPage,
group: prerenderGroup,
} as Prerender;
prerenders[localeOutputPathData] = {
...origPrerenderData,
group: prerenderGroup,
} as Prerender;
if (outputPathData) {
const localeOutputPathData = outputPathData.replace(
new RegExp(`${escapeStringRegexp(origRouteFileNoExt)}.json$`),
`${localeRouteFileNoExt}${
localeRouteFileNoExt !== origRouteFileNoExt &&
origRouteFileNoExt === '/index'
? '/index'
: ''
}.json`
);
const origPrerenderData = prerenders[outputPathData];
prerenders[localeOutputPathData] = {
...origPrerenderData,
group: prerenderGroup,
} as Prerender;
}
++prerenderGroup;
}
}
@@ -2316,12 +2387,12 @@ interface EdgeFunctionMatcher {
}
export async function getMiddlewareBundle({
config = {},
entryPath,
outputDirectory,
routesManifest,
isCorrectMiddlewareOrder,
prerenderBypassToken,
nextVersion,
}: {
config: Config;
entryPath: string;
@@ -2329,6 +2400,7 @@ export async function getMiddlewareBundle({
prerenderBypassToken: string;
routesManifest: RoutesManifest;
isCorrectMiddlewareOrder: boolean;
nextVersion: string;
}): Promise<{
staticRoutes: Route[];
dynamicRouteMap: Map<string, RouteWithSrc>;
@@ -2379,21 +2451,6 @@ export async function getMiddlewareBundle({
edgeFunction.wasm
);
const edgeFunctionOptions: { cron?: Cron } = {};
if (config.functions) {
const sourceFile = await getSourceFilePathFromPage({
workPath: entryPath,
page: `${edgeFunction.page}.js`,
});
const opts = await getLambdaOptionsFromFunction({
sourceFile,
config,
});
edgeFunctionOptions.cron = opts.cron;
}
return {
type,
page: edgeFunction.page,
@@ -2438,7 +2495,6 @@ export async function getMiddlewareBundle({
);
return new EdgeFunction({
...edgeFunctionOptions,
deploymentTarget: 'v8-worker',
name: edgeFunction.name,
files: {
@@ -2466,6 +2522,10 @@ export async function getMiddlewareBundle({
path: `assets/${name}`,
};
}),
framework: {
slug: 'nextjs',
version: nextVersion,
},
});
})(),
routeMatchers: getRouteMatchers(edgeFunction, routesManifest),
@@ -2501,9 +2561,13 @@ export async function getMiddlewareBundle({
// app/index/page -> index/index
if (shortPath.startsWith('pages/')) {
shortPath = shortPath.replace(/^pages\//, '');
} else if (shortPath.startsWith('app/') && shortPath.endsWith('/page')) {
} else if (
shortPath.startsWith('app/') &&
(shortPath.endsWith('/page') || shortPath.endsWith('/route'))
) {
shortPath =
shortPath.replace(/^app\//, '').replace(/(^|\/)page$/, '') || 'index';
shortPath.replace(/^app\//, '').replace(/(^|\/)(page|route)$/, '') ||
'index';
}
if (routesManifest?.basePath) {
@@ -2689,3 +2753,49 @@ function transformSourceMap(
return { ...sourcemap, sources };
}
interface LambdaGroupTypeInterface {
isApiLambda: boolean;
isPrerenders?: boolean;
}
export function getOperationType({
group,
prerenderManifest,
pageFileName,
}: {
group?: LambdaGroupTypeInterface;
prerenderManifest?: NextPrerenderedRoutes;
pageFileName?: string;
}) {
if (group?.isApiLambda || isApiPage(pageFileName)) {
return 'API';
}
if (group?.isPrerenders) {
return 'ISR';
}
if (pageFileName && prerenderManifest) {
const { blockingFallbackRoutes = {}, fallbackRoutes = {} } =
prerenderManifest;
if (
pageFileName in blockingFallbackRoutes ||
pageFileName in fallbackRoutes
) {
return 'ISR';
}
}
return 'SSR';
}
export function isApiPage(page: string | undefined) {
if (!page) {
return false;
}
return page
.replace(/\\/g, '/')
.match(/(serverless|server)\/pages\/api(\/|\.js$)/);
}

View File

@@ -27,7 +27,7 @@
"RSC": "1"
},
"responseHeaders": {
"content-type": "application/octet-stream"
"content-type": "text/x-component"
}
},
{
@@ -51,7 +51,7 @@
"RSC": "1"
},
"responseHeaders": {
"content-type": "application/octet-stream"
"content-type": "text/x-component"
}
}
]

View File

@@ -0,0 +1,21 @@
import Link from 'next/link';
const paths = ['/', '/shop', '/product', '/who-we-are', '/about', '/contact'];
export default function Page({ params }) {
return (
<>
<p>variant: {params.variant}</p>
<p>slug: {params.rest?.join('/')}</p>
<ul>
{paths.map(path => {
return (
<li key={path}>
<Link href={path}>to {path}</Link>
</li>
);
})}
</ul>
</>
);
}

View File

@@ -0,0 +1,10 @@
export default function Root({ children }) {
return (
<html className="this-is-the-document-html">
<head>
<title>{`hello world`}</title>
</head>
<body className="this-is-the-document-body">{children}</body>
</html>
);
}

View File

@@ -0,0 +1,12 @@
/* eslint-env jest */
const path = require('path');
const { deployAndTest } = require('../../utils');
const ctx = {};
describe(`${__dirname.split(path.sep).pop()}`, () => {
it('should deploy and pass probe checks', async () => {
const info = await deployAndTest(__dirname);
Object.assign(ctx, info);
});
});

View File

@@ -0,0 +1,11 @@
import { NextResponse } from 'next/server';
export function middleware(request) {
request.nextUrl.pathname = `/no-variant${request.nextUrl.pathname}`;
return NextResponse.rewrite(request.nextUrl);
}
// See "Matching Paths" below to learn more
export const config = {
matcher: ['/', '/shop', '/product', '/who-we-are', '/about', '/contact'],
};

View File

@@ -0,0 +1,14 @@
module.exports = {
experimental: {
appDir: true,
runtime: 'nodejs',
},
rewrites: async () => {
return [
{
source: '/rewritten-to-dashboard',
destination: '/dashboard',
},
];
},
};

View File

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

View File

@@ -0,0 +1,3 @@
export default function handler(req, res) {
return res.json({ hello: 'world' });
}

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