Compare commits

..

52 Commits

Author SHA1 Message Date
Vercel Release Bot
46950633f4 Version Packages (#9994) 2023-05-22 10:47:47 -07:00
Vercel Release Bot
44b1dfe7c5 [tests] Upgrade Turbo to version 1.9.8 (#9996)
This auto-generated PR updates Turbo to version 1.9.8
2023-05-22 08:50:23 +00:00
Vercel Release Bot
0278c7b7b9 [examples] Upgrade Next.js to version 13.4.3 (#9980)
This auto-generated PR updates Next.js to version 13.4.3
2023-05-19 21:08:38 +00:00
Sean Massa
761db597be clarify next.js dupe api directory warning (#9979)
In a Next.js app, if you have Node.js Functions in both `api` and `pages/api`, you'll get a warning about moving those `api` functions into `pages/api`. This does specify Node.js Functions already, but a user was confused and tried to move their non-Node.js functions into `pages/api` as well.

This PR tries to make it clearer that you can do both. Note that this still requires a non-ideal local dev setup where you:

- update next config to fall back to the `vc dev` port for `api` handlers
- run `next dev` for the Next.js app on one port
- run `vc dev` for the top-level `api` Functions on another port

---

Example repo: https://github.com/rexfordessilfie/vercel-api-languages
2023-05-19 19:31:32 +00:00
Nathan Rajlich
671e63e7b8 [cli] Add vc link --repo flag (alpha) (#8931)
Adding a new `--repo` flag to `vc link` which is a new linking style to
link to a Git URL rather then directly to a single Vercel Project. This
allows for multiple Projects to be linked simultaneously, which is
useful for monorepo setups.

Utilization of this new linking style in other commands will be done in
a follow-up PR.
2023-05-19 11:53:49 -07:00
Vercel Release Bot
f7bdc6cc26 Version Packages (#9991)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2023-05-19 12:08:50 -05:00
Sean Massa
e94a153b2f add repo details for packages that are missing it (#9990) 2023-05-19 12:02:12 -05:00
Vercel Release Bot
74e639a772 Version Packages (#9978)
This PR was opened by the [Changesets
release](https://github.com/changesets/action) GitHub action. When
you're ready to do a release, you can merge this and the packages will
be published to npm automatically. If you're not ready to do a release
yet, that's fine, whenever you add more changesets to main, this PR will
be updated.


# Releases
## @vercel/build-utils@6.7.3

### Patch Changes

- Deprecate Node.js 14.x and 16.x with warning
([#9976](https://github.com/vercel/vercel/pull/9976))

## vercel@29.3.5

### Patch Changes

- Updated dependencies
\[[`2c950d47a`](2c950d47ae),
[`71b9f3a94`](71b9f3a94b),
[`f00b08a82`](f00b08a820)]:
    -   @vercel/static-build@1.3.31
    -   @vercel/build-utils@6.7.3
    -   @vercel/next@3.8.5
    -   @vercel/node@2.14.3
    -   @vercel/remix-builder@1.8.10

## @vercel/client@12.5.1

### Patch Changes

- Updated dependencies
\[[`71b9f3a94`](71b9f3a94b)]:
    -   @vercel/build-utils@6.7.3

## @vercel/gatsby-plugin-vercel-builder@1.3.4

### Patch Changes

- Updated dependencies
\[[`71b9f3a94`](71b9f3a94b)]:
    -   @vercel/build-utils@6.7.3
    -   @vercel/node@2.14.3

## @vercel/next@3.8.5

### Patch Changes

- [next] Ensure app functions are detected/separated properly
([#9989](https://github.com/vercel/vercel/pull/9989))

## @vercel/node@2.14.3

### Patch Changes

- Updated dependencies
\[[`71b9f3a94`](71b9f3a94b)]:
    -   @vercel/build-utils@6.7.3

## @vercel/remix-builder@1.8.10

### Patch Changes

- Updated dependencies
\[[`71b9f3a94`](71b9f3a94b)]:
    -   @vercel/build-utils@6.7.3

## @vercel/static-build@1.3.31

### Patch Changes

- ensure cleanup after gatsby plugin runs
([#9975](https://github.com/vercel/vercel/pull/9975))

-   Updated dependencies \[]:
    -   @vercel/gatsby-plugin-vercel-builder@1.3.4

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2023-05-19 09:34:26 -07:00
JJ Kasper
f00b08a820 [next] Ensure app functions are detected/separated properly (#9989)
Follow-up to https://github.com/vercel/vercel/pull/9974 this uses the `lambdaAppPaths` as the source of truth instead of the manifest which is more accurate for separating/detecting. Also adds additional test cases for new root and index app functions. 

x-ref: https://github.com/vercel/next.js/issues/49169
2023-05-19 16:22:44 +00:00
Chris Barber
6cdd38d130 [tests] Pin Next.js version in test fixtures (#9910)
Co-authored-by: Sean Massa <EndangeredMassa@gmail.com>
2023-05-19 09:06:08 -05:00
Sean Massa
2c950d47ae ensure cleanup after the gatsby plugin runs (#9975)
There is an issue where you can interrupt a gatsby project build and the temporary files are not cleaned up. If you try to build again, it will get stuck in an infinite loop.

This PR adds an exit hook to make sure the cleanup happens.
2023-05-18 19:42:08 +00:00
Sean Massa
71b9f3a94b Deprecate Node.js 14.x and 16.x with warning (#9976)
Node.js 14 and 16 are being deprecated. This PR will cause a warning when they are used today and an error after August 15.
2023-05-18 19:03:01 +00:00
Vercel Release Bot
91b7f6dcd9 Version Packages (#9973)
This PR was opened by the [Changesets
release](https://github.com/changesets/action) GitHub action. When
you're ready to do a release, you can merge this and the packages will
be published to npm automatically. If you're not ready to do a release
yet, that's fine, whenever you add more changesets to main, this PR will
be updated.


# Releases
## vercel@29.3.4

### Patch Changes

- Updated dependencies
\[[`67e556bc8`](67e556bc80),
[`ba10fb4dd`](ba10fb4dd4)]:
    -   @vercel/remix-builder@1.8.9
    -   @vercel/next@3.8.4

## @vercel/next@3.8.4

### Patch Changes

- Update handling for react prebundled flag
([#9974](https://github.com/vercel/vercel/pull/9974))

## @vercel/remix-builder@1.8.9

### Patch Changes

- Upgrade `@remix-run/dev` fork to v1.16.1
([#9971](https://github.com/vercel/vercel/pull/9971))

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2023-05-18 10:38:24 -07:00
JJ Kasper
ba10fb4dd4 [next] Update handling for react prebundled flag (#9974)
This ensures we properly set the prebundled react flag conditionally for
app and pages ensuring they are kept separate when bundling.

x-ref: https://github.com/vercel/next.js/issues/49169
2023-05-18 06:37:50 -07:00
Nathan Rajlich
18c1c45ce3 Add changeset for @remix-run/dev updater GH Action (#9972)
Adds a changeset file when the GH Action to update `@remix-run/dev`
creates a pull request.
2023-05-17 16:39:09 -07:00
Vercel Release Bot
67e556bc80 [remix] Upgrade @remix-run/dev to version 1.16.1 (#9971)
This auto-generated PR updates @remix-run/dev to version 1.16.1
2023-05-17 19:34:12 +00:00
Sean Massa
7235000181 fix release script (#9959)
The [previous PR](https://github.com/vercel/vercel/pull/9942) used `github.repos`, but I think this needs to be `github.rest.repos`.

- [Docs](https://octokit.github.io/rest.js/v19#repos)
- [Failed GH Action](https://github.com/vercel/vercel/actions/runs/4994578940/jobs/8945329301)
2023-05-17 18:26:30 +00:00
Sean Massa
5124d431ea fix deploy from local (#9969)
Local deployments of this repo would fail because this repo assumes that it's only ever deployed via git connection.
2023-05-17 17:45:28 +00:00
Vercel Release Bot
c879401bbc Version Packages (#9966)
This PR was opened by the [Changesets
release](https://github.com/changesets/action) GitHub action. When
you're ready to do a release, you can merge this and the packages will
be published to npm automatically. If you're not ready to do a release
yet, that's fine, whenever you add more changesets to main, this PR will
be updated.


# Releases
## vercel@29.3.3

### Patch Changes

- Updated dependencies
\[[`6c6f3ce9d`](6c6f3ce9d2)]:
    -   @vercel/next@3.8.3

## @vercel/next@3.8.3

### Patch Changes

- Ensure un-necessary rsc routes are not added
([#9963](https://github.com/vercel/vercel/pull/9963))

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2023-05-17 10:45:41 +02:00
JJ Kasper
6c6f3ce9d2 [next] Ensure un-necessary rsc routes are not added (#9963)
These routes aren't needed for RSC as `_next/data` isn't used for
routing app paths.

x-ref: [slack
thread](https://vercel.slack.com/archives/C058FQFKAGH/p1684272340654679?thread_ts=1684271538.503259&cid=C058FQFKAGH)
2023-05-16 16:33:09 -07:00
Vercel Release Bot
aa83680832 [examples] Upgrade Next.js to version 13.4.2 (#9943)
This auto-generated PR updates Next.js to version 13.4.2
2023-05-16 22:54:10 +00:00
Vercel Release Bot
06113d3e39 Version Packages (#9962)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2023-05-16 15:53:57 -05:00
Luc Leray
5150f21404 [vc dev] Fix serverless function size limit condition (#9961)
Co-authored-by: Sean Massa <EndangeredMassa@gmail.com>
2023-05-16 14:17:00 -05:00
Sean Massa
eb6bb98406 empty push test 2023-05-16 11:49:25 -05:00
Vercel Release Bot
d8e3b6e738 Version Packages (#9950)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Sean Massa <EndangeredMassa@gmail.com>
2023-05-16 11:48:06 -05:00
Luc Leray
25da051d62 [vc dev] Skip 50MB zip size limit for Python (#9944)
Do not apply the 50MB zip size limit for Python serverless function during `vc dev`.

Related to internal PR: https://github.com/vercel/api/pull/19279
2023-05-16 15:58:04 +00:00
Vercel Release Bot
f57af66dc2 [tests] Upgrade Turbo to version 1.9.4 (#9952)
This auto-generated PR updates Turbo to version 1.9.4
2023-05-16 03:21:01 +00:00
Nathan Rajlich
b2f71d5352 Remove "Required PR Label" workflow (#9958)
Changesets enforces this, so no need for this extra workflow anymore.
2023-05-15 14:38:30 -07:00
Lee Robinson
6115f0d74a Update README (#9951)
To be more clear about what Vercel is actually providing you, as a developer.
2023-05-13 20:32:46 +00:00
Nathan Rajlich
14c877e468 [cli] Sort keys in vc env pull (#9949)
Makes sense to have the output be deterministic. Alphabetical sort seems like a logical choice.
2023-05-12 22:08:59 +00:00
Nathan Rajlich
d80732d74f [release] Promote vercel CLI release to "latest" (#9942)
It's possible that changesets will promote a release to latest that is
not the Vercel CLI release. This script ensures that a `vercel@` release
is always the latest after a publish happens.
2023-05-11 13:40:34 -07:00
Vercel Release Bot
b739c1845c Version Packages (#9939)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2023-05-11 10:46:10 -05:00
Sean Massa
e9f0fcf397 [cli] remove vc rollback beta label (#9928)
Remove the "beta" label from `vc rollback`.
2023-05-11 15:16:48 +00:00
Sean Massa
924a20a0fc [tests] make prettier ignore .changeset (#9938) 2023-05-11 09:50:14 -05:00
Nathan Rajlich
fc3b74d06f Add id-token: write permissions to enable npm publish provenance (#9936)
Was previously added in https://github.com/vercel/vercel/pull/9583, was
missed in the changesets switchover.
2023-05-10 16:24:28 -07:00
Vercel Release Bot
380ed38c71 Version Packages (#9935)
This PR was opened by the [Changesets release](https://github.com/changesets/action) GitHub action. When you're ready to do a release, you can merge this and the packages will be published to npm automatically. If you're not ready to do a release yet, that's fine, whenever you add more changesets to main, this PR will be updated.


# Releases
## vercel@29.2.1

### Patch Changes

-   Updated dependencies \[[`6d5983eaa`](6d5983eaae)]:
    -   @vercel/remix-builder@1.8.8

## @vercel/remix-builder@1.8.8

### Patch Changes

-   Upgrade `@remix-run/dev` to v1.16.0-patch.1 to fix erroneous "not found in your node_modules" warning ([#9930](https://github.com/vercel/vercel/pull/9930))
2023-05-10 22:55:42 +00:00
Nathan Rajlich
e228cdd373 Add @svitejs/changesets-changelog-github-compact to package.json (#9934)
Forgot to add the package in #9933 :doh:
2023-05-10 14:47:42 -07:00
Nathan Rajlich
9bd92535d6 Use Svelte's changelog generator and don't delete CHANGELOG.md (#9933)
Partial revert of #9932, since apparently [that didn't
work](https://github.com/vercel/vercel/actions/runs/4941266637/jobs/8833725828).

We can investigate that more later, but this gets it working again
properly at least.

Also use Svelte's changelog generator, since it includes the PR numbers
which Sean wanted.
2023-05-10 14:41:19 -07:00
Nathan Rajlich
5825e30700 Create release PR as "Vercel Bot" and remove CHANGELOG.md files (#9932)
* Have changesets create the release PR as "Vercel Bot", so that the
tests run.
* Remove the `CHANGELOG.md` files. They are redundant with the GH
Releases which contain the same information.
2023-05-10 13:58:29 -07:00
Vercel Release Bot
6d5983eaae [remix] Upgrade @remix-run/dev to version 1.16.0-patch.1 (#9930)
This auto-generated PR updates @remix-run/dev to version 1.16.0-patch.1
2023-05-10 20:20:15 +00:00
Nathan Rajlich
2fd59a3b5a Use changesets instead of lerna (#9914)
`changesets` will improve our release workflow, since we will no longer need to manually curate and publish the changelog and GitHub Release. Instead, you simply merge the publish PR that the changesets GH action maintains as we push commits to `main`.
2023-05-10 19:35:17 +00:00
Nathan Rajlich
d1d3e9384d Publish Stable
- vercel@29.2.0
 - @vercel/client@12.5.0
 - @vercel/frameworks@1.4.2
 - @vercel/fs-detectors@3.9.2
 - @vercel/gatsby-plugin-vercel-builder@1.3.3
 - @vercel/node@2.14.2
 - @vercel/static-build@1.3.30
2023-05-10 10:39:11 -07:00
Nathan Rajlich
8428632eb1 [cli] Add support for Vercel CLI Extensions (#9800)
# Vercel CLI Extensions

Adds a new mechanism to add additional sub-commands to Vercel CLI, inspired by how Git handles sub-commands:

* Extensions are standalone executables that Vercel CLI will spawn as a child process.
* The name of the executable must begin with `vercel-`. For example, to add a sub-command called `vercel example`, there should exist an executable called `vercel-example`.
* The executable can either be a npm package with a `"bin"` entry installed into the local project's workspace, or be globally available in the `$PATH`.
* Extensions can access the [Vercel REST API](https://vercel.com/docs/rest-api), pre-authenticated, by utilizing the `VERCEL_API` env var. Vercel CLI spawns a local HTTP server that adds the `Authorization` header and then proxies to the Vercel REST API.

## Environment Variables

A few environment variables which provide features and context to the extension:

| Name      | Description |
| ----------- | ----------- |
| `VERCEL_API`      | HTTP URL to access the pre-authenticated Vercel API.       |
| `VERCEL_TEAM_ID`  | Provided when the currently selected scope is a Team. |
| `VERCEL_DEBUG` | Provided when the `--debug` flag is used. The command _may_ output additional debug logging. |
| `VERCEL_HELP` | Provided when the `--help` flag is used. The command _should_ print the help output and then end with exit code **2**. |

## Example

```bash
#!/usr/bin/env bash
set -euo pipefail

echo Hi, from a Vercel CLI Extension!
user="$(curl -s "$VERCEL_API/v2/user" | jq -r .user.username)"
echo "Logged in as: $user"
```

Usage:

```
$ vc example
Vercel CLI 28.18.5
Hi, from a Vercel CLI Extension!
Logged in as: tootallnate
```
2023-05-10 16:36:58 +00:00
Chris Barber
fa443035f6 [tests] Add test for set-cookie with multiple cookies (#9916)
This adds the missing test for https://github.com/vercel/vercel/pull/9899.
2023-05-06 10:18:52 +00:00
Sean Massa
de2c7e1633 [cli] rename global options in help output (#9917)
Make the distinction between global and deploy options more clear.

The dim note about how to get `deploy`-specific help:

<img width="670" alt="Screenshot 2023-05-05 at 3 55 49 PM" src="https://user-images.githubusercontent.com/41545/236568356-10b9ce4f-2865-4267-a3eb-3488df88a133.png">
2023-05-05 22:00:27 +00:00
Sean Massa
75d2435138 [cli] refactor help output around default and deploy commands (#9912)
When getting help output for the default command `vc --help`, you get a list of commands. When you specify the `deploy` command with `vc deploy --help`, you get the same list of commands.

This PR makes a distinction between an explicit `deploy` command and a default one for the purposes of help output.

Should show CLI help:

- `vc -h`
- `vc --help`
- `vc help`

Should show `deploy` command help:

- `vc deploy -h`
- `vc deploy --help`
- `vc help deploy`
2023-05-05 18:54:50 +00:00
Vercel Release Bot
5328bb69e2 [examples] Upgrade Next.js to version 13.4.1 (#9911)
This auto-generated PR updates Next.js to version 13.4.1
2023-05-05 16:25:11 +00:00
Kiko Beats
e3fe368baa [cli][node] upgrade async-listen to 3.0.0 (#9907)
The `async-listen@3.0.0` is ESM ready and always returns a `URL`
instance; That helps us to unify code.
2023-05-05 16:09:10 +02:00
Nathan Rajlich
99832587c5 [cli] Add support for HTTPS_PROXY env var (#9880)
By leveraging the [`proxy-agent`](https://www.npmjs.com/package/proxy-agent) npm module, enable CLI to support making HTTP requests over a proxy, by leveraging the `HTTPS_PROXY` or `ALL_PROXY` env var (same convention as `curl` uses).

Finally closes this ancient issue: https://github.com/vercel/vercel/issues/255
2023-05-05 00:52:18 +00:00
Vercel Release Bot
47d0d4f84a [examples] Upgrade Next.js to version 13.4.0 (#9902)
This auto-generated PR updates Next.js to version 13.4.0
2023-05-04 20:25:08 +00:00
Sean Massa
aba54ee6cf [frameworks] validate getOutputDirName and other missing values on framework entries (#9900)
Covers the validation whole in Framework entries over function properties and other missing holes.
2023-05-03 23:36:26 +00:00
Chris Barber
cd7d3ef1c5 [node] Explicitly set 'set-cookie' header (#9899)
When looping over the response headers returned by `node-fetch`, it will join `set-cookie` header value into a single string. The whatwg calls for a `headers.getSetCookie()` function to handle the `set-cookie` special case, but `node-fetch@2.x` doesn't support it.

As a workaround, we need to grab the raw `set-cookie` header value.
2023-05-03 22:12:45 +00:00
265 changed files with 4745 additions and 4754 deletions

8
.changeset/README.md Normal file
View File

@@ -0,0 +1,8 @@
# Changesets
Hello and welcome! This folder has been automatically generated by `@changesets/cli`, a build tool that works
with multi-package repos, or single-package repos to help you version and publish your code. You can
find the full documentation for it [in our repository](https://github.com/changesets/changesets)
We have a quick list of common questions to get you started engaging with this project in
[our documentation](https://github.com/changesets/changesets/blob/main/docs/common-questions.md)

14
.changeset/config.json Normal file
View File

@@ -0,0 +1,14 @@
{
"$schema": "https://unpkg.com/@changesets/config@2.3.0/schema.json",
"changelog": [
"@svitejs/changesets-changelog-github-compact",
{ "repo": "vercel/vercel" }
],
"commit": false,
"fixed": [],
"linked": [],
"access": "public",
"baseBranch": "main",
"updateInternalDependencies": "patch",
"ignore": []
}

View File

@@ -1,72 +1,72 @@
name: Publish
name: Release
on:
push:
branches:
- main
tags:
- '!*'
env:
TURBO_REMOTE_ONLY: 'true'
TURBO_TEAM: 'vercel'
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
concurrency: ${{ github.workflow }}-${{ github.ref }}
jobs:
publish:
name: Publish
release:
name: Release
runs-on: ubuntu-latest
permissions:
contents: write
id-token: write
steps:
- name: Checkout
- name: Checkout Repo
uses: actions/checkout@v3
with:
fetch-depth: 0
- name: Check Release
id: check-release
run: |
tag="$(git describe --tags --exact-match 2> /dev/null || :)"
if [[ -z "$tag" ]];
then
echo "IS_RELEASE=false" >> $GITHUB_OUTPUT
else
echo "IS_RELEASE=true" >> $GITHUB_OUTPUT
fi
- name: Setup Go
if: ${{ steps.check-release.outputs.IS_RELEASE == 'true' }}
uses: actions/setup-go@v3
with:
go-version: '1.13.15'
- name: Setup Node
if: ${{ steps.check-release.outputs.IS_RELEASE == 'true' }}
uses: actions/setup-node@v3
with:
node-version: 16
- name: install npm@9
run: npm i -g npm@9
- name: install pnpm@8.3.1
run: npm i -g pnpm@8.3.1
- name: Install
if: ${{ steps.check-release.outputs.IS_RELEASE == 'true' }}
run: pnpm install
- name: Build
if: ${{ steps.check-release.outputs.IS_RELEASE == 'true' }}
- name: Install Dependencies
run: pnpm install --frozen-lockfile
- name: Build Packages
run: pnpm build
env:
GA_TRACKING_ID: ${{ secrets.GA_TRACKING_ID }}
SENTRY_DSN: ${{ secrets.SENTRY_DSN }}
- name: Publish
if: ${{ steps.check-release.outputs.IS_RELEASE == 'true' }}
run: pnpm publish-from-github
- name: Create Release Pull Request or Publish to npm
id: changesets
uses: changesets/action@v1
with:
version: pnpm version:prepare
publish: pnpm release
env:
GITHUB_TOKEN: ${{ secrets.GH_TOKEN_PULL_REQUESTS }}
NPM_CONFIG_PROVENANCE: 'true'
NPM_TOKEN: ${{ secrets.NPM_TOKEN_ELEVATED }}
GA_TRACKING_ID: ${{ secrets.GA_TRACKING_ID }}
SENTRY_DSN: ${{ secrets.SENTRY_DSN }}
- name: Trigger Update
if: ${{ steps.check-release.outputs.IS_RELEASE == 'true' }}
- name: Set latest Release to `vercel` (if a Publish Happened)
if: steps.changesets.outputs.published == 'true'
uses: actions/github-script@v6
with:
github-token: ${{ secrets.GH_TOKEN_PULL_REQUESTS }}
script: |
const script = require('./utils/update-latest-release.js')
await script({ github, context })
- name: Trigger Update (if a Publish Happened)
if: steps.changesets.outputs.published == 'true'
uses: actions/github-script@v6
with:
github-token: ${{ secrets.GH_TOKEN_PULL_REQUESTS }}

View File

@@ -1,26 +0,0 @@
name: Required PR Label
on:
pull_request:
types: [opened, labeled, unlabeled, synchronize]
jobs:
label:
runs-on: ubuntu-latest
steps:
- name: Check PR Labels
uses: actions/github-script@v6
with:
script: |
let missing = false;
const labels = context.payload.pull_request.labels.map(l => l.name);
if (labels.filter(l => l.startsWith('area:')).length === 0) {
console.error('::error::Missing label: Please add at least one "area" label.');
missing = true;
}
if (labels.filter(l => l.startsWith('semver:')).length !== 1) {
console.error('::error::Missing label: Please add exactly one "semver" label.');
missing = true;
}
if (missing) {
process.exit(1);
}
console.log('::notice::Success: This pull request has correct labels, thanks!');

View File

@@ -12,6 +12,7 @@ packages/gatsby-plugin-vercel-analytics
node_modules
dist
pnpm-lock.yaml
.changeset
.vscode
.DS_Store
.next
@@ -31,4 +32,4 @@ packages/**/test/fixtures
packages/**/test/dev/fixtures
packages/**/test/build-fixtures
packages/**/test/cache-fixtures
packages/cli/src/util/dev/templates/*.ts

View File

@@ -19,9 +19,7 @@
## Vercel
Vercel is the platform for frontend developers, providing the speed and reliability innovators need to create at the moment of inspiration.
We enable teams to iterate quickly and develop, preview, and ship delightful user experiences. Vercel has zero-configuration support for 35+ frontend frameworks and integrates with your headless content, commerce, or database of choice.
Vercel's frontend cloud gives developers frameworks, workflows, and infrastructure to build a faster, more personalized web.
## Deploy

View File

@@ -14,11 +14,7 @@ pnpm dev
Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
You can start editing the page by modifying `pages/index.js`. The page auto-updates as you edit the file.
[API routes](https://nextjs.org/docs/api-routes/introduction) can be accessed on [http://localhost:3000/api/hello](http://localhost:3000/api/hello). This endpoint can be edited in `pages/api/hello.js`.
The `pages/api` directory is mapped to `/api/*`. Files in this directory are treated as [API routes](https://nextjs.org/docs/api-routes/introduction) instead of React pages.
You can start editing the page by modifying `app/page.js`. The page auto-updates as you edit the file.
This project uses [`next/font`](https://nextjs.org/docs/basic-features/font-optimization) to automatically optimize and load Inter, a custom Google Font.

View File

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 25 KiB

View File

@@ -0,0 +1,17 @@
import './globals.css'
import { Inter } from 'next/font/google'
const inter = Inter({ subsets: ['latin'] })
export const metadata = {
title: 'Create Next App',
description: 'Generated by create next app',
}
export default function RootLayout({ children }) {
return (
<html lang="en">
<body className={inter.className}>{children}</body>
</html>
)
}

View File

@@ -0,0 +1,95 @@
import Image from 'next/image'
import styles from './page.module.css'
export default function Home() {
return (
<main className={styles.main}>
<div className={styles.description}>
<p>
Get started by editing&nbsp;
<code className={styles.code}>app/page.js</code>
</p>
<div>
<a
href="https://vercel.com?utm_source=create-next-app&utm_medium=appdir-template&utm_campaign=create-next-app"
target="_blank"
rel="noopener noreferrer"
>
By{' '}
<Image
src="/vercel.svg"
alt="Vercel Logo"
className={styles.vercelLogo}
width={100}
height={24}
priority
/>
</a>
</div>
</div>
<div className={styles.center}>
<Image
className={styles.logo}
src="/next.svg"
alt="Next.js Logo"
width={180}
height={37}
priority
/>
</div>
<div className={styles.grid}>
<a
href="https://nextjs.org/docs?utm_source=create-next-app&utm_medium=appdir-template&utm_campaign=create-next-app"
className={styles.card}
target="_blank"
rel="noopener noreferrer"
>
<h2>
Docs <span>-&gt;</span>
</h2>
<p>Find in-depth information about Next.js features and API.</p>
</a>
<a
href="https://nextjs.org/learn?utm_source=create-next-app&utm_medium=appdir-template&utm_campaign=create-next-app"
className={styles.card}
target="_blank"
rel="noopener noreferrer"
>
<h2>
Learn <span>-&gt;</span>
</h2>
<p>Learn about Next.js in an interactive course with&nbsp;quizzes!</p>
</a>
<a
href="https://vercel.com/templates?framework=next.js&utm_source=create-next-app&utm_medium=appdir-template&utm_campaign=create-next-app"
className={styles.card}
target="_blank"
rel="noopener noreferrer"
>
<h2>
Templates <span>-&gt;</span>
</h2>
<p>Explore the Next.js 13 playground.</p>
</a>
<a
href="https://vercel.com/new?utm_source=create-next-app&utm_medium=appdir-template&utm_campaign=create-next-app"
className={styles.card}
target="_blank"
rel="noopener noreferrer"
>
<h2>
Deploy <span>-&gt;</span>
</h2>
<p>
Instantly deploy your Next.js site to a shareable URL with Vercel.
</p>
</a>
</div>
</main>
)
}

View File

@@ -1,6 +1,4 @@
/** @type {import('next').NextConfig} */
const nextConfig = {
reactStrictMode: true,
}
const nextConfig = {}
module.exports = nextConfig

File diff suppressed because it is too large Load Diff

View File

@@ -9,9 +9,9 @@
"lint": "next lint"
},
"dependencies": {
"eslint": "8.39.0",
"eslint-config-next": "13.3.4",
"next": "13.3.4",
"eslint": "8.40.0",
"eslint-config-next": "13.4.3",
"next": "13.4.3",
"react": "18.2.0",
"react-dom": "18.2.0"
}

View File

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

View File

@@ -1,13 +0,0 @@
import { Html, Head, Main, NextScript } from 'next/document'
export default function Document() {
return (
<Html lang="en">
<Head />
<body>
<Main />
<NextScript />
</body>
</Html>
)
}

View File

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

View File

@@ -1,114 +0,0 @@
import Head from 'next/head'
import Image from 'next/image'
import { Inter } from 'next/font/google'
import styles from '@/styles/Home.module.css'
const inter = Inter({ subsets: ['latin'] })
export default function Home() {
return (
<>
<Head>
<title>Create Next App</title>
<meta name="description" content="Generated by create next app" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="icon" href="/favicon.ico" />
</Head>
<main className={`${styles.main} ${inter.className}`}>
<div className={styles.description}>
<p>
Get started by editing&nbsp;
<code className={styles.code}>pages/index.js</code>
</p>
<div>
<a
href="https://vercel.com?utm_source=create-next-app&utm_medium=default-template&utm_campaign=create-next-app"
target="_blank"
rel="noopener noreferrer"
>
By{' '}
<Image
src="/vercel.svg"
alt="Vercel Logo"
className={styles.vercelLogo}
width={100}
height={24}
priority
/>
</a>
</div>
</div>
<div className={styles.center}>
<Image
className={styles.logo}
src="/next.svg"
alt="Next.js Logo"
width={180}
height={37}
priority
/>
</div>
<div className={styles.grid}>
<a
href="https://nextjs.org/docs?utm_source=create-next-app&utm_medium=default-template&utm_campaign=create-next-app"
className={styles.card}
target="_blank"
rel="noopener noreferrer"
>
<h2>
Docs <span>-&gt;</span>
</h2>
<p>
Find in-depth information about Next.js features and&nbsp;API.
</p>
</a>
<a
href="https://nextjs.org/learn?utm_source=create-next-app&utm_medium=default-template&utm_campaign=create-next-app"
className={styles.card}
target="_blank"
rel="noopener noreferrer"
>
<h2>
Learn <span>-&gt;</span>
</h2>
<p>
Learn about Next.js in an interactive course with&nbsp;quizzes!
</p>
</a>
<a
href="https://vercel.com/templates?framework=next.js&utm_source=create-next-app&utm_medium=default-template&utm_campaign=create-next-app"
className={styles.card}
target="_blank"
rel="noopener noreferrer"
>
<h2>
Templates <span>-&gt;</span>
</h2>
<p>
Discover and deploy boilerplate example Next.js&nbsp;projects.
</p>
</a>
<a
href="https://vercel.com/new?utm_source=create-next-app&utm_medium=default-template&utm_campaign=create-next-app"
className={styles.card}
target="_blank"
rel="noopener noreferrer"
>
<h2>
Deploy <span>-&gt;</span>
</h2>
<p>
Instantly deploy your Next.js site to a shareable URL
with&nbsp;Vercel.
</p>
</a>
</div>
</main>
</>
)
}

View File

@@ -4,10 +4,9 @@
"private": true,
"license": "Apache-2.0",
"packageManager": "pnpm@8.3.1",
"dependencies": {
"lerna": "5.6.2"
},
"devDependencies": {
"@changesets/cli": "2.26.1",
"@svitejs/changesets-changelog-github-compact": "1.1.0",
"@types/node": "14.18.33",
"@typescript-eslint/eslint-plugin": "5.21.0",
"@typescript-eslint/parser": "5.21.0",
@@ -22,6 +21,7 @@
"eslint-plugin-jest": "26.1.5",
"execa": "3.2.0",
"fs-extra": "11.1.0",
"glob": "10.2.3",
"husky": "7.0.4",
"jest": "29.5.0",
"json5": "2.1.1",
@@ -32,16 +32,10 @@
"source-map-support": "0.5.12",
"ts-eager": "2.0.2",
"ts-jest": "29.1.0",
"typescript": "4.9.5",
"turbo": "1.9.3"
"turbo": "1.9.8",
"typescript": "4.9.5"
},
"scripts": {
"lerna": "lerna",
"version": "pnpm install && git add pnpm-lock.yaml",
"bootstrap": "lerna bootstrap",
"publish-stable": "echo 'Run `pnpm changelog` for instructions'",
"publish-from-github": "./utils/publish.sh",
"changelog": "node utils/changelog.js",
"build": "node utils/gen.js && turbo --no-update-notifier run build",
"vercel-build": "pnpm build && pnpm run pack && cd api && node -r ts-eager/register ./_lib/script/build.ts",
"pre-commit": "lint-staged",
@@ -53,7 +47,9 @@
"lint": "eslint . --cache --ext .ts,.js",
"prettier-check": "prettier --check .",
"prepare": "husky install",
"pack": "cd utils && node -r ts-eager/register ./pack.ts"
"pack": "cd utils && node -r ts-eager/register ./pack.ts",
"version:prepare": "changeset version && pnpm install --no-frozen-lockfile",
"release": "changeset publish"
},
"lint-staged": {
"./{*,{api,packages,test,utils}/**/*}.{js,ts}": [

View File

@@ -0,0 +1,7 @@
# @vercel/build-utils
## 6.7.3
### Patch Changes
- Deprecate Node.js 14.x and 16.x with warning ([#9976](https://github.com/vercel/vercel/pull/9976))

View File

@@ -1,6 +1,6 @@
{
"name": "@vercel/build-utils",
"version": "6.7.2",
"version": "6.7.3",
"license": "Apache-2.0",
"main": "./dist/index.js",
"types": "./dist/index.d.js",

View File

@@ -6,8 +6,18 @@ import debug from '../debug';
function getOptions() {
const options = [
{ major: 18, range: '18.x', runtime: 'nodejs18.x' },
{ major: 16, range: '16.x', runtime: 'nodejs16.x' },
{ major: 14, range: '14.x', runtime: 'nodejs14.x' },
{
major: 16,
range: '16.x',
runtime: 'nodejs16.x',
discontinueDate: new Date('2023-08-15'),
},
{
major: 14,
range: '14.x',
runtime: 'nodejs14.x',
discontinueDate: new Date('2023-08-15'),
},
{
major: 12,
range: '12.x',

View File

@@ -1,6 +1,6 @@
{
"private": true,
"engines": {
"node": "16.14.0"
"node": "18.2.0"
}
}

View File

@@ -1,6 +1,6 @@
{
"private": true,
"engines": {
"node": "14.x"
"node": "18.x"
}
}

View File

@@ -140,20 +140,20 @@ it('should ignore node version in vercel dev getNodeVersion()', async () => {
it('should select project setting from config when no package.json is found and fallback undefined', async () => {
expect(
await getNodeVersion('/tmp', undefined, { nodeVersion: '16.x' }, {})
).toHaveProperty('range', '16.x');
await getNodeVersion('/tmp', undefined, { nodeVersion: '18.x' }, {})
).toHaveProperty('range', '18.x');
expect(warningMessages).toStrictEqual([]);
});
it('should select project setting from config when no package.json is found and fallback is null', async () => {
expect(
await getNodeVersion('/tmp', null as any, { nodeVersion: '16.x' }, {})
).toHaveProperty('range', '16.x');
await getNodeVersion('/tmp', null as any, { nodeVersion: '18.x' }, {})
).toHaveProperty('range', '18.x');
expect(warningMessages).toStrictEqual([]);
});
it('should select project setting from fallback when no package.json is found', async () => {
expect(await getNodeVersion('/tmp', '16.x')).toHaveProperty('range', '16.x');
expect(await getNodeVersion('/tmp', '18.x')).toHaveProperty('range', '18.x');
expect(warningMessages).toStrictEqual([]);
});
@@ -165,9 +165,9 @@ it('should prefer package.json engines over project setting from config and warn
{ nodeVersion: '12.x' },
{}
)
).toHaveProperty('range', '14.x');
).toHaveProperty('range', '18.x');
expect(warningMessages).toStrictEqual([
'Warning: Due to "engines": { "node": "14.x" } in your `package.json` file, the Node.js Version defined in your Project Settings ("12.x") will not apply. Learn More: http://vercel.link/node-version',
'Warning: Due to "engines": { "node": "18.x" } in your `package.json` file, the Node.js Version defined in your Project Settings ("12.x") will not apply. Learn More: http://vercel.link/node-version',
]);
});
@@ -179,9 +179,9 @@ it('should warn when package.json engines is exact version', async () => {
{},
{}
)
).toHaveProperty('range', '16.x');
).toHaveProperty('range', '18.x');
expect(warningMessages).toStrictEqual([
'Warning: Detected "engines": { "node": "16.14.0" } in your `package.json` with major.minor.patch, but only major Node.js Version can be selected. Learn More: http://vercel.link/node-version',
'Warning: Detected "engines": { "node": "18.2.0" } in your `package.json` with major.minor.patch, but only major Node.js Version can be selected. Learn More: http://vercel.link/node-version',
]);
});
@@ -204,30 +204,30 @@ it('should not warn when package.json engines matches project setting from confi
await getNodeVersion(
path.join(__dirname, 'pkg-engine-node'),
undefined,
{ nodeVersion: '14' },
{ nodeVersion: '18' },
{}
)
).toHaveProperty('range', '14.x');
).toHaveProperty('range', '18.x');
expect(warningMessages).toStrictEqual([]);
expect(
await getNodeVersion(
path.join(__dirname, 'pkg-engine-node'),
undefined,
{ nodeVersion: '14.x' },
{ nodeVersion: '18.x' },
{}
)
).toHaveProperty('range', '14.x');
).toHaveProperty('range', '18.x');
expect(warningMessages).toStrictEqual([]);
expect(
await getNodeVersion(
path.join(__dirname, 'pkg-engine-node'),
undefined,
{ nodeVersion: '<15' },
{ nodeVersion: '<19' },
{}
)
).toHaveProperty('range', '14.x');
).toHaveProperty('range', '18.x');
expect(warningMessages).toStrictEqual([]);
});
@@ -238,7 +238,7 @@ it('should get latest node version', async () => {
it('should throw for discontinued versions', async () => {
// Mock a future date so that Node 8 and 10 become discontinued
const realDateNow = Date.now.bind(global.Date);
global.Date.now = () => new Date('2022-10-15').getTime();
global.Date.now = () => new Date('2023-10-01').getTime();
expect(getSupportedNodeVersion('8.10.x', false)).rejects.toThrow();
expect(getSupportedNodeVersion('8.10.x', true)).rejects.toThrow();
@@ -246,12 +246,18 @@ it('should throw for discontinued versions', async () => {
expect(getSupportedNodeVersion('10.x', true)).rejects.toThrow();
expect(getSupportedNodeVersion('12.x', false)).rejects.toThrow();
expect(getSupportedNodeVersion('12.x', true)).rejects.toThrow();
expect(getSupportedNodeVersion('14.x', false)).rejects.toThrow();
expect(getSupportedNodeVersion('14.x', true)).rejects.toThrow();
expect(getSupportedNodeVersion('16.x', false)).rejects.toThrow();
expect(getSupportedNodeVersion('16.x', true)).rejects.toThrow();
const discontinued = getDiscontinuedNodeVersions();
expect(discontinued.length).toBe(3);
expect(discontinued[0]).toHaveProperty('range', '12.x');
expect(discontinued[1]).toHaveProperty('range', '10.x');
expect(discontinued[2]).toHaveProperty('range', '8.10.x');
expect(discontinued.length).toBe(5);
expect(discontinued[0]).toHaveProperty('range', '16.x');
expect(discontinued[1]).toHaveProperty('range', '14.x');
expect(discontinued[2]).toHaveProperty('range', '12.x');
expect(discontinued[3]).toHaveProperty('range', '10.x');
expect(discontinued[4]).toHaveProperty('range', '8.10.x');
global.Date.now = realDateNow;
});
@@ -277,11 +283,31 @@ it('should warn for deprecated versions, soon to be discontinued', async () => {
'major',
12
);
expect(await getSupportedNodeVersion('14.x', false)).toHaveProperty(
'major',
14
);
expect(await getSupportedNodeVersion('14.x', true)).toHaveProperty(
'major',
14
);
expect(await getSupportedNodeVersion('16.x', false)).toHaveProperty(
'major',
16
);
expect(await getSupportedNodeVersion('16.x', true)).toHaveProperty(
'major',
16
);
expect(warningMessages).toStrictEqual([
'Error: Node.js version 10.x has reached End-of-Life. Deployments created on or after 2021-04-20 will fail to build. Please set "engines": { "node": "18.x" } in your `package.json` file to use Node.js 18.',
'Error: Node.js version 10.x has reached End-of-Life. Deployments created on or after 2021-04-20 will fail to build. Please set Node.js Version to 18.x in your Project Settings to use Node.js 18.',
'Error: Node.js version 12.x has reached End-of-Life. Deployments created on or after 2022-10-03 will fail to build. Please set "engines": { "node": "18.x" } in your `package.json` file to use Node.js 18.',
'Error: Node.js version 12.x has reached End-of-Life. Deployments created on or after 2022-10-03 will fail to build. Please set Node.js Version to 18.x in your Project Settings to use Node.js 18.',
'Error: Node.js version 14.x has reached End-of-Life. Deployments created on or after 2023-08-15 will fail to build. Please set "engines": { "node": "18.x" } in your `package.json` file to use Node.js 18.',
'Error: Node.js version 14.x has reached End-of-Life. Deployments created on or after 2023-08-15 will fail to build. Please set Node.js Version to 18.x in your Project Settings to use Node.js 18.',
'Error: Node.js version 16.x has reached End-of-Life. Deployments created on or after 2023-08-15 will fail to build. Please set "engines": { "node": "18.x" } in your `package.json` file to use Node.js 18.',
'Error: Node.js version 16.x has reached End-of-Life. Deployments created on or after 2023-08-15 will fail to build. Please set Node.js Version to 18.x in your Project Settings to use Node.js 18.',
]);
global.Date.now = realDateNow;

66
packages/cli/CHANGELOG.md Normal file
View File

@@ -0,0 +1,66 @@
# vercel
## 29.4.0
### Minor Changes
- Add `vercel link --repo` flag to link to repository (multiple projects), rather than an individual project (alpha) ([#8931](https://github.com/vercel/vercel/pull/8931))
## 29.3.6
### Patch Changes
- Updated dependencies []:
- @vercel/static-build@1.3.32
## 29.3.5
### Patch Changes
- Updated dependencies [[`2c950d47a`](https://github.com/vercel/vercel/commit/2c950d47aeb22a3de16f983259ea6f37a4555189), [`71b9f3a94`](https://github.com/vercel/vercel/commit/71b9f3a94b7922607f8f24bf7b2bd1742e62cc05), [`f00b08a82`](https://github.com/vercel/vercel/commit/f00b08a82085c3a63059f34f67f10ced92f2979c)]:
- @vercel/static-build@1.3.31
- @vercel/build-utils@6.7.3
- @vercel/next@3.8.5
- @vercel/node@2.14.3
- @vercel/remix-builder@1.8.10
## 29.3.4
### Patch Changes
- Updated dependencies [[`67e556bc8`](https://github.com/vercel/vercel/commit/67e556bc80c821c233120a2ec1611adb8e195baa), [`ba10fb4dd`](https://github.com/vercel/vercel/commit/ba10fb4dd4155a75df79b98a0c43a6c42eac7b62)]:
- @vercel/remix-builder@1.8.9
- @vercel/next@3.8.4
## 29.3.3
### Patch Changes
- Updated dependencies [[`6c6f3ce9d`](https://github.com/vercel/vercel/commit/6c6f3ce9d228b1e038641e4bafb38c3487e7dff7)]:
- @vercel/next@3.8.3
## 29.3.2
### Patch Changes
- [vc dev] Fix serverless function size limit condition ([#9961](https://github.com/vercel/vercel/pull/9961))
## 29.3.1
### Patch Changes
- Sort environment variables alphabetically in `vercel env pull` ([#9949](https://github.com/vercel/vercel/pull/9949))
- Skip 50MB zip size limit for Python ([#9944](https://github.com/vercel/vercel/pull/9944))
## 29.3.0
### Minor Changes
- [cli] remove `vc rollback` beta label ([#9928](https://github.com/vercel/vercel/pull/9928))
## 29.2.1
### Patch Changes
- Updated dependencies [[`6d5983eaa`](https://github.com/vercel/vercel/commit/6d5983eaaefe3fd2204f49c3228718ac64a452e3)]:
- @vercel/remix-builder@1.8.8

View File

@@ -10,9 +10,7 @@
## Usage
Vercel is the platform for frontend developers, providing the speed and reliability innovators need to create at the moment of inspiration.
We enable teams to iterate quickly and develop, preview, and ship delightful user experiences. Vercel has zero-configuration support for 35+ frontend frameworks and integrates with your headless content, commerce, or database of choice.
Vercel's frontend cloud gives developers frameworks, workflows, and infrastructure to build a faster, more personalized web.
To install the latest version of Vercel CLI, run this command:

View File

@@ -1,6 +1,6 @@
{
"name": "vercel",
"version": "29.1.1",
"version": "29.4.0",
"preferGlobal": true,
"license": "Apache-2.0",
"description": "The command-line interface for Vercel",
@@ -32,19 +32,20 @@
"node": ">= 14"
},
"dependencies": {
"@vercel/build-utils": "6.7.2",
"@vercel/build-utils": "6.7.3",
"@vercel/go": "2.5.1",
"@vercel/hydrogen": "0.0.64",
"@vercel/next": "3.8.2",
"@vercel/node": "2.14.1",
"@vercel/next": "3.8.5",
"@vercel/node": "2.14.3",
"@vercel/python": "3.1.60",
"@vercel/redwood": "1.1.15",
"@vercel/remix-builder": "1.8.7",
"@vercel/remix-builder": "1.8.10",
"@vercel/ruby": "1.3.76",
"@vercel/static-build": "1.3.29"
"@vercel/static-build": "1.3.32"
},
"devDependencies": {
"@alex_neo/jest-expect-message": "1.0.5",
"@edge-runtime/node-utils": "2.0.3",
"@next/env": "11.1.2",
"@sentry/node": "5.5.0",
"@sindresorhus/slugify": "0.11.0",
@@ -81,16 +82,16 @@
"@types/title": "3.4.1",
"@types/universal-analytics": "0.4.2",
"@types/update-notifier": "5.1.0",
"@types/which": "1.3.2",
"@types/which": "3.0.0",
"@types/write-json-file": "2.2.1",
"@types/yauzl-promise": "2.1.0",
"@vercel-internals/constants": "*",
"@vercel-internals/get-package-json": "*",
"@vercel-internals/types": "*",
"@vercel/client": "12.4.12",
"@vercel/client": "12.5.1",
"@vercel/error-utils": "1.0.10",
"@vercel/frameworks": "1.4.1",
"@vercel/fs-detectors": "3.9.1",
"@vercel/frameworks": "1.4.2",
"@vercel/fs-detectors": "3.9.3",
"@vercel/fun": "1.0.4",
"@vercel/ncc": "0.24.0",
"@vercel/routing-utils": "2.2.1",
@@ -100,7 +101,7 @@
"ansi-escapes": "4.3.2",
"ansi-regex": "5.0.1",
"arg": "5.0.0",
"async-listen": "1.2.0",
"async-listen": "3.0.0",
"async-retry": "1.1.3",
"async-sema": "2.1.4",
"bytes": "3.0.0",
@@ -147,6 +148,8 @@
"pcre-to-regexp": "1.0.0",
"pluralize": "7.0.0",
"promisepipe": "3.0.0",
"proxy": "2.0.0",
"proxy-agent": "6.1.1",
"psl": "1.1.31",
"qr-image": "3.2.0",
"raw-body": "2.4.1",
@@ -165,6 +168,7 @@
"ts-node": "10.9.1",
"universal-analytics": "0.4.20",
"utility-types": "2.1.0",
"which": "3.0.0",
"write-json-file": "2.2.0",
"xdg-app-paths": "5.1.0",
"yauzl-promise": "2.1.3"

82
packages/cli/src/args.ts Normal file
View File

@@ -0,0 +1,82 @@
import chalk from 'chalk';
import logo from './util/output/logo';
import { getPkgName } from './util/pkg-name';
export const help = () => `
${chalk.bold(`${logo} ${getPkgName()}`)} [options] <command | path>
${chalk.dim('For deploy command help, run `vercel deploy --help`')}
${chalk.dim('Commands:')}
${chalk.dim('Basic')}
deploy [path] Performs a deployment ${chalk.bold(
'(default)'
)}
dev Start a local development server
env Manages the Environment Variables for your current Project
git Manage Git provider repository for your current Project
help [cmd] Displays complete help for [cmd]
init [example] Initialize an example project
inspect [id] Displays information related to a deployment
link [path] Link local directory to a Vercel Project
ls | list [app] Lists deployments
login [email] Logs into your account or creates a new one
logout Logs out of your account
pull [path] Pull your Project Settings from the cloud
rollback [url|id] Quickly revert back to a previous deployment
switch [scope] Switches between teams and your personal account
${chalk.dim('Advanced')}
alias [cmd] Manages your domain aliases
bisect Use binary search to find the deployment that introduced a bug
certs [cmd] Manages your SSL certificates
dns [name] Manages your DNS records
domains [name] Manages your domain names
logs [url] Displays the logs for a deployment
projects Manages your Projects
rm | remove [id] Removes a deployment
secrets [name] Manages your global Secrets, for use in Environment Variables
teams Manages your teams
whoami Shows the username of the currently logged in user
${chalk.dim('Global Options:')}
-h, --help Output usage information
-v, --version Output the version number
--cwd Current working directory
-A ${chalk.bold.underline('FILE')}, --local-config=${chalk.bold.underline(
'FILE'
)} Path to the local ${'`vercel.json`'} file
-Q ${chalk.bold.underline('DIR')}, --global-config=${chalk.bold.underline(
'DIR'
)} Path to the global ${'`.vercel`'} directory
-d, --debug Debug mode [off]
--no-color No color mode [off]
-S, --scope Set a custom scope
-t ${chalk.underline('TOKEN')}, --token=${chalk.underline(
'TOKEN'
)} Login token
${chalk.dim('Examples:')}
${chalk.gray('')} Deploy the current directory
${chalk.cyan(`$ ${getPkgName()}`)}
${chalk.gray('')} Deploy a custom path
${chalk.cyan(`$ ${getPkgName()} /usr/src/project`)}
${chalk.gray('')} Deploy with Environment Variables
${chalk.cyan(`$ ${getPkgName()} -e NODE_ENV=production`)}
${chalk.gray('')} Show the usage information for the sub command ${chalk.dim(
'`list`'
)}
${chalk.cyan(`$ ${getPkgName()} help list`)}
`;

View File

@@ -2,80 +2,27 @@ import chalk from 'chalk';
import logo from '../../util/output/logo';
import { getPkgName } from '../../util/pkg-name';
export const help = () => `
${chalk.bold(`${logo} ${getPkgName()}`)} [options] <command | path>
export const help = () => {
return `
${chalk.bold(`${logo} ${getPkgName()} [deploy]`)} [path-to-project] [options]
${chalk.dim('Commands:')}
${chalk.dim('Basic')}
deploy [path] Performs a deployment ${chalk.bold(
'(default)'
)}
dev Start a local development server
env Manages the Environment Variables for your current Project
git Manage Git provider repository for your current Project
help [cmd] Displays complete help for [cmd]
init [example] Initialize an example project
inspect [id] Displays information related to a deployment
link [path] Link local directory to a Vercel Project
ls | list [app] Lists deployments
login [email] Logs into your account or creates a new one
logout Logs out of your account
pull [path] Pull your Project Settings from the cloud
rollback [url|id] Quickly revert back to a previous deployment [beta]
switch [scope] Switches between teams and your personal account
${chalk.dim('Advanced')}
alias [cmd] Manages your domain aliases
bisect Use binary search to find the deployment that introduced a bug
certs [cmd] Manages your SSL certificates
dns [name] Manages your DNS records
domains [name] Manages your domain names
logs [url] Displays the logs for a deployment
projects Manages your Projects
rm | remove [id] Removes a deployment
secrets [name] Manages your global Secrets, for use in Environment Variables
teams Manages your teams
whoami Shows the username of the currently logged in user
${chalk.dim('Options:')}
-h, --help Output usage information
-v, --version Output the version number
--cwd Current working directory
-V, --platform-version Set the platform version to deploy to
-A ${chalk.bold.underline('FILE')}, --local-config=${chalk.bold.underline(
'FILE'
)} Path to the local ${'`vercel.json`'} file
-Q ${chalk.bold.underline('DIR')}, --global-config=${chalk.bold.underline(
'DIR'
)} Path to the global ${'`.vercel`'} directory
-d, --debug Debug mode [off]
--no-color No color mode [off]
-f, --force Force a new deployment even if nothing has changed
--with-cache Retain build cache when using "--force"
-t ${chalk.underline('TOKEN')}, --token=${chalk.underline(
'TOKEN'
)} Login token
-p, --public Deployment is public (${chalk.dim(
'`/_src`'
)} is exposed)
-e, --env Include an env var during run time (e.g.: ${chalk.dim(
'`-e KEY=value`'
)}). Can appear many times.
-b, --build-env Similar to ${chalk.dim(
'`--env`'
)} but for build time only.
-m, --meta Add metadata for the deployment (e.g.: ${chalk.dim(
'`-m KEY=value`'
)}). Can appear many times.
--no-wait Don't wait for the deployment to finish
-S, --scope Set a custom scope
--regions Set default regions to enable the deployment on
--prod Create a production deployment
-y, --yes Skip questions when setting up new project using default scope and settings
--prod Create a production deployment
-p, --public Deployment is public (${chalk.dim(
'`/_src`'
)} is exposed)
-e, --env Include an env var during run time (e.g.: ${chalk.dim(
'`-e KEY=value`'
)}). Can appear many times.
-b, --build-env Similar to ${chalk.dim(
'`--env`'
)} but for build time only.
-m, --meta Add metadata for the deployment (e.g.: ${chalk.dim(
'`-m KEY=value`'
)}). Can appear many times.
--no-wait Don't wait for the deployment to finish
-f, --force Force a new deployment even if nothing has changed
--with-cache Retain build cache when using "--force"
--regions Set default regions to enable the deployment on
${chalk.dim('Examples:')}
@@ -89,14 +36,15 @@ export const help = () => `
${chalk.gray('')} Deploy with Environment Variables
${chalk.cyan(
`$ ${getPkgName()} -e NODE_ENV=production -e SECRET=@mysql-secret`
)}
${chalk.cyan(`$ ${getPkgName()} -e NODE_ENV=production`)}
${chalk.gray('')} Show the usage information for the sub command ${chalk.dim(
'`list`'
)}
${chalk.gray('')} Deploy with prebuilt outputs
${chalk.cyan(`$ ${getPkgName()} help list`)}
${chalk.cyan(`$ ${getPkgName()} build`)}
${chalk.cyan(`$ ${getPkgName()} deploy --prebuilt`)}
${chalk.gray('')} Write Deployment URL to a file
${chalk.cyan(`$ ${getPkgName()} > deployment-url.txt`)}
`;
};

View File

@@ -122,8 +122,9 @@ export default async function pull(
const contents =
CONTENTS_PREFIX +
Object.entries(records)
.map(([key, value]) => `${key}="${escapeValue(value)}"`)
Object.keys(records)
.sort()
.map(key => `${key}="${escapeValue(records[key])}"`)
.join('\n') +
'\n';

View File

@@ -52,7 +52,7 @@ export default async function main(client: Client) {
try {
argv = getArgs(client.argv.slice(2), {
'--force': Boolean,
'-f': Boolean,
'-f': '--force',
});
args = getSubcommand(argv._.slice(1), COMMAND_CONFIG).args;
} catch (err) {

View File

@@ -35,7 +35,7 @@ export default async function init(
) {
const { output } = client;
const [name, dir] = args;
const force = opts['-f'] || opts['--force'];
const force = opts['--force'];
const examples = await fetchExampleList(client);

View File

@@ -2,8 +2,10 @@ import chalk from 'chalk';
import Client from '../../util/client';
import getArgs from '../../util/get-args';
import logo from '../../util/output/logo';
import cmd from '../../util/output/cmd';
import { getPkgName } from '../../util/pkg-name';
import { ensureLink } from '../../util/link/ensure-link';
import { ensureRepoLink } from '../../util/link/repo';
const help = () => {
console.log(`
@@ -12,6 +14,7 @@ const help = () => {
${chalk.dim('Options:')}
-h, --help Output usage information
-r, --repo Link multiple projects based on Git repository (alpha)
-A ${chalk.bold.underline('FILE')}, --local-config=${chalk.bold.underline(
'FILE'
)} Path to the local ${'`vercel.json`'} file
@@ -42,7 +45,14 @@ const help = () => {
${chalk.gray('')} Link a specific directory to a Vercel Project
${chalk.cyan(`$ ${getPkgName()} link /usr/src/project`)}
${chalk.cyan(`$ ${getPkgName()} link --cwd /path/to/project`)}
${chalk.gray('')} ${chalk.yellow(
'(alpha)'
)} Link to the current Git repository, allowing for multiple
Vercel Projects to be linked simultaneously (useful for monorepos)
${chalk.cyan(`$ ${getPkgName()} link --repo`)}
`);
};
@@ -52,6 +62,8 @@ export default async function main(client: Client) {
'-y': '--yes',
'--project': String,
'-p': '--project',
'--repo': Boolean,
'-r': '--repo',
// deprecated
'--confirm': Boolean,
@@ -68,17 +80,36 @@ export default async function main(client: Client) {
argv['--yes'] = argv['--confirm'];
}
const cwd = argv._[1] || process.cwd();
const yes = !!argv['--yes'];
const link = await ensureLink('link', client, cwd, {
autoConfirm: !!argv['--yes'],
forceDelete: true,
projectName: argv['--project'],
successEmoji: 'success',
});
if (typeof link === 'number') {
return link;
let cwd = argv._[1];
if (cwd) {
client.output.warn(
`The ${cmd('vc link <directory>')} syntax is deprecated, please use ${cmd(
`vc link --cwd ${cwd}`
)} instead`
);
} else {
cwd = process.cwd();
}
if (argv['--repo']) {
client.output.warn(
`The ${cmd('--repo')} flag is in alpha, please report issues`
);
await ensureRepoLink(client, cwd, yes);
} else {
const link = await ensureLink('link', client, cwd, {
autoConfirm: yes,
forceDelete: true,
projectName: argv['--project'],
successEmoji: 'success',
});
if (typeof link === 'number') {
return link;
}
}
return 0;
}

View File

@@ -52,7 +52,10 @@ import { getCommandName, getTitleName } from './util/pkg-name';
import doLoginPrompt from './util/login/prompt';
import type { AuthConfig, GlobalConfig } from '@vercel-internals/types';
import { VercelConfig } from '@vercel/client';
import { ProxyAgent } from 'proxy-agent';
import box from './util/output/box';
import { execExtension } from './util/extension/exec';
import { help } from './args';
const VERCEL_DIR = getGlobalPathConfig();
const VERCEL_CONFIG_PATH = configFiles.getConfigFilePath();
@@ -141,19 +144,21 @@ const main = async () => {
return 1;
}
const cwd = argv['--cwd'];
let cwd = argv['--cwd'];
if (cwd) {
process.chdir(cwd);
}
cwd = process.cwd();
// The second argument to the command can be:
//
// * a path to deploy (as in: `vercel path/`)
// * a subcommand (as in: `vercel ls`)
const targetOrSubcommand = argv._[2];
const subSubCommand = argv._[3];
// Currently no beta commands - add here as needed
const betaCommands: string[] = ['rollback'];
// If empty, leave this code here for easy adding of beta commands later
const betaCommands: string[] = [];
if (betaCommands.includes(targetOrSubcommand)) {
console.log(
`${chalk.grey(
@@ -172,6 +177,14 @@ const main = async () => {
return 0;
}
// Handle bare `-h` directly
const bareHelpOption = !targetOrSubcommand && argv['--help'];
const bareHelpSubcommand = targetOrSubcommand === 'help' && !subSubCommand;
if (bareHelpOption || bareHelpSubcommand) {
output.print(help());
return 2;
}
// Ensure that the Vercel global configuration directory exists
try {
await mkdirp(VERCEL_DIR);
@@ -251,6 +264,7 @@ const main = async () => {
// Shared API `Client` instance for all sub-commands to utilize
client = new Client({
agent: new ProxyAgent({ keepAlive: true }),
apiUrl,
stdin: process.stdin,
stdout: process.stdout,
@@ -263,11 +277,13 @@ const main = async () => {
argv: process.argv,
});
let subcommand;
// Gets populated to the subcommand name when a built-in is
// provided, otherwise it remains undefined for an extension
let subcommand: string | undefined = undefined;
// Check if we are deploying something
if (targetOrSubcommand) {
const targetPath = join(process.cwd(), targetOrSubcommand);
const targetPath = join(cwd, targetOrSubcommand);
const targetPathExists = existsSync(targetPath);
const subcommandExists =
GLOBAL_COMMANDS.has(targetOrSubcommand) ||
@@ -289,8 +305,7 @@ const main = async () => {
debug(`user supplied known subcommand: "${targetOrSubcommand}"`);
subcommand = targetOrSubcommand;
} else {
debug('user supplied a possible target for deployment');
subcommand = 'deploy';
debug('user supplied a possible target for deployment or an extension');
}
} else {
debug('user supplied no target, defaulting to deploy');
@@ -298,7 +313,7 @@ const main = async () => {
}
if (subcommand === 'help') {
subcommand = argv._[3] || 'deploy';
subcommand = subSubCommand || 'deploy';
client.argv.push('-h');
}
@@ -310,6 +325,7 @@ const main = async () => {
!client.argv.includes('-h') &&
!client.argv.includes('--help') &&
!argv['--token'] &&
subcommand &&
!subcommandsWithoutToken.includes(subcommand)
) {
if (isTTY) {
@@ -401,7 +417,8 @@ const main = async () => {
);
}
const targetCommand = commands.get(subcommand);
let targetCommand =
typeof subcommand === 'string' ? commands.get(subcommand) : undefined;
const scope = argv['--scope'] || argv['--team'] || localConfig?.scope;
if (
@@ -409,7 +426,7 @@ const main = async () => {
targetCommand !== 'login' &&
targetCommand !== 'dev' &&
targetCommand !== 'build' &&
!(targetCommand === 'teams' && argv._[3] !== 'invite')
!(targetCommand === 'teams' && subSubCommand !== 'invite')
) {
let user = null;
@@ -472,96 +489,123 @@ const main = async () => {
try {
const start = Date.now();
let func: any;
switch (targetCommand) {
case 'alias':
func = require('./commands/alias').default;
break;
case 'bisect':
func = require('./commands/bisect').default;
break;
case 'build':
func = require('./commands/build').default;
break;
case 'certs':
func = require('./commands/certs').default;
break;
case 'deploy':
func = require('./commands/deploy').default;
break;
case 'dev':
func = require('./commands/dev').default;
break;
case 'dns':
func = require('./commands/dns').default;
break;
case 'domains':
func = require('./commands/domains').default;
break;
case 'env':
func = require('./commands/env').default;
break;
case 'git':
func = require('./commands/git').default;
break;
case 'init':
func = require('./commands/init').default;
break;
case 'inspect':
func = require('./commands/inspect').default;
break;
case 'link':
func = require('./commands/link').default;
break;
case 'list':
func = require('./commands/list').default;
break;
case 'logs':
func = require('./commands/logs').default;
break;
case 'login':
func = require('./commands/login').default;
break;
case 'logout':
func = require('./commands/logout').default;
break;
case 'project':
func = require('./commands/project').default;
break;
case 'pull':
func = require('./commands/pull').default;
break;
case 'remove':
func = require('./commands/remove').default;
break;
case 'rollback':
func = require('./commands/rollback').default;
break;
case 'secrets':
func = require('./commands/secrets').default;
break;
case 'teams':
func = require('./commands/teams').default;
break;
case 'whoami':
func = require('./commands/whoami').default;
break;
default:
func = null;
break;
if (!targetCommand) {
// Set this for the metrics to record it at the end
targetCommand = argv._[2];
// Try to execute as an extension
try {
exitCode = await execExtension(
client,
targetCommand,
argv._.slice(3),
cwd
);
} catch (err: unknown) {
if (isErrnoException(err) && err.code === 'ENOENT') {
// Fall back to `vc deploy <dir>`
targetCommand = subcommand = 'deploy';
} else {
throw err;
}
}
}
if (!func || !targetCommand) {
const sub = param(subcommand);
output.error(`The ${sub} subcommand does not exist`);
return 1;
}
// Not using an `else` here because if the CLI extension
// was not found then we have to fall back to `vc deploy`
if (subcommand) {
let func: any;
switch (targetCommand) {
case 'alias':
func = require('./commands/alias').default;
break;
case 'bisect':
func = require('./commands/bisect').default;
break;
case 'build':
func = require('./commands/build').default;
break;
case 'certs':
func = require('./commands/certs').default;
break;
case 'deploy':
func = require('./commands/deploy').default;
break;
case 'dev':
func = require('./commands/dev').default;
break;
case 'dns':
func = require('./commands/dns').default;
break;
case 'domains':
func = require('./commands/domains').default;
break;
case 'env':
func = require('./commands/env').default;
break;
case 'git':
func = require('./commands/git').default;
break;
case 'init':
func = require('./commands/init').default;
break;
case 'inspect':
func = require('./commands/inspect').default;
break;
case 'link':
func = require('./commands/link').default;
break;
case 'list':
func = require('./commands/list').default;
break;
case 'logs':
func = require('./commands/logs').default;
break;
case 'login':
func = require('./commands/login').default;
break;
case 'logout':
func = require('./commands/logout').default;
break;
case 'project':
func = require('./commands/project').default;
break;
case 'pull':
func = require('./commands/pull').default;
break;
case 'remove':
func = require('./commands/remove').default;
break;
case 'rollback':
func = require('./commands/rollback').default;
break;
case 'secrets':
func = require('./commands/secrets').default;
break;
case 'teams':
func = require('./commands/teams').default;
break;
case 'whoami':
func = require('./commands/whoami').default;
break;
default:
func = null;
break;
}
if (func.default) {
func = func.default;
}
if (!func || !targetCommand) {
const sub = param(subcommand);
output.error(`The ${sub} subcommand does not exist`);
return 1;
}
exitCode = await func(client);
if (func.default) {
func = func.default;
}
exitCode = await func(client);
}
const end = Date.now() - start;
if (shouldCollectMetrics) {

View File

@@ -2,9 +2,6 @@ const ARG_COMMON = {
'--help': Boolean,
'-h': '--help',
'--platform-version': Number,
'-V': '--platform-version',
'--debug': Boolean,
'-d': '--debug',
@@ -28,6 +25,10 @@ const ARG_COMMON = {
'--api': String,
'--cwd': String,
// Deprecated
'--platform-version': Number,
'-V': '--platform-version',
};
export default () => ARG_COMMON;

View File

@@ -23,6 +23,7 @@ import type {
import { sharedPromise } from './promise';
import { APIError } from './errors-ts';
import { normalizeError } from '@vercel/error-utils';
import type { Agent } from 'http';
const isSAMLError = (v: any): v is SAMLError => {
return v && v.saml;
@@ -44,6 +45,7 @@ export interface ClientOptions extends Stdio {
config: GlobalConfig;
localConfig?: VercelConfig;
localConfigPath?: string;
agent?: Agent;
}
export const isJSONObject = (v: any): v is JSONObject => {
@@ -59,13 +61,15 @@ export default class Client extends EventEmitter implements Stdio {
stderr: WritableTTY;
output: Output;
config: GlobalConfig;
agent?: Agent;
localConfig?: VercelConfig;
localConfigPath?: string;
prompt!: inquirer.PromptModule;
private requestIdCounter: number;
requestIdCounter: number;
constructor(opts: ClientOptions) {
super();
this.agent = opts.agent;
this.argv = opts.argv;
this.apiUrl = opts.apiUrl;
this.authConfig = opts.authConfig;
@@ -126,10 +130,10 @@ export default class Client extends EventEmitter implements Stdio {
} else {
return `#${requestId}${opts.method || 'GET'} ${url.href}`;
}
}, fetch(url, { ...opts, headers, body }));
}, fetch(url, { agent: this.agent, ...opts, headers, body }));
}
fetch(url: string, opts: { json: false }): Promise<Response>;
fetch(url: string, opts: FetchOptions & { json: false }): Promise<Response>;
fetch<T>(url: string, opts?: FetchOptions): Promise<T>;
fetch(url: string, opts: FetchOptions = {}) {
return this.retry(async bail => {

View File

@@ -13,6 +13,7 @@ import type { Org } from '@vercel-internals/types';
import ua from '../ua';
import { linkFolderToProject } from '../projects/link';
import { prependEmoji, emoji } from '../emoji';
import type { Agent } from 'http';
function printInspectUrl(
output: Output,
@@ -35,6 +36,7 @@ export default async function processDeployment({
archive,
skipAutoDetectionConfirmation,
noWait,
agent,
...args
}: {
now: Now;
@@ -55,6 +57,7 @@ export default async function processDeployment({
cwd?: string;
rootDirectory?: string | null;
noWait?: boolean;
agent?: Agent;
}) {
let {
now,
@@ -91,6 +94,7 @@ export default async function processDeployment({
rootDirectory,
skipAutoDetectionConfirmation,
archive,
agent,
};
const deployingSpinnerVal = isSettingUpProject

View File

@@ -337,7 +337,10 @@ export async function executeBuild(
// Enforce the lambda zip size soft watermark
const maxLambdaBytes = bytes('50mb');
for (const asset of Object.values(result.output)) {
if (asset.type === 'Lambda') {
if (
asset.type === 'Lambda' &&
!(typeof asset.runtime === 'string' && asset.runtime.startsWith('python'))
) {
const size = asset.zipBuffer.length;
if (size > maxLambdaBytes) {
throw new LambdaSizeExceededError(size, maxLambdaBytes);

View File

@@ -5,7 +5,7 @@ import chalk from 'chalk';
import fetch from 'node-fetch';
import plural from 'pluralize';
import rawBody from 'raw-body';
import listen from 'async-listen';
import { listen } from 'async-listen';
import minimatch from 'minimatch';
import httpProxy from 'http-proxy';
import { randomBytes } from 'crypto';
@@ -860,7 +860,7 @@ export default class DevServer {
let address: string | null = null;
while (typeof address !== 'string') {
try {
address = await listen(this.server, ...listenSpec);
address = (await listen(this.server, ...listenSpec)).toString();
} catch (err: unknown) {
if (isErrnoException(err)) {
this.output.debug(`Got listen error: ${err.code}`);

View File

@@ -0,0 +1,90 @@
import which from 'which';
import execa from 'execa';
import { dirname } from 'path';
import { listen } from 'async-listen';
import { scanParentDirs, walkParentDirs } from '@vercel/build-utils';
import { createProxy } from './proxy';
import type Client from '../client';
/**
* Attempts to execute a Vercel CLI Extension.
*
* If the extension was found and executed, then the
* exit code is returned.
*
* If the program could not be found, then an `ENOENT`
* error is thrown.
*/
export async function execExtension(
client: Client,
name: string,
args: string[],
cwd: string
): Promise<number> {
const { debug } = client.output;
const extensionCommand = `vercel-${name}`;
const { packageJsonPath, lockfilePath } = await scanParentDirs(cwd);
const baseFile = lockfilePath || packageJsonPath;
let extensionPath: string | null = null;
if (baseFile) {
// Scan `node_modules/.bin` works for npm / pnpm / yarn v1
// TOOD: add support for Yarn PnP
extensionPath = await walkParentDirs({
base: dirname(baseFile),
start: cwd,
filename: `node_modules/.bin/${extensionCommand}`,
});
}
if (!extensionPath) {
// Attempt global `$PATH` lookup
extensionPath = which.sync(extensionCommand, { nothrow: true });
}
if (!extensionPath) {
debug(`failed to find extension command with name "${extensionCommand}"`);
throw new ENOENT(extensionCommand);
}
debug(`invoking extension: ${extensionPath}`);
const proxy = createProxy(client);
proxy.once('close', () => {
debug(`extension proxy server shut down`);
});
const proxyUrl = await listen(proxy, { port: 0, host: '127.0.0.1' });
const VERCEL_API = proxyUrl.href.replace(/\/$/, '');
debug(`extension proxy server listening at ${VERCEL_API}`);
const result = await execa(extensionPath, args, {
cwd,
reject: false,
stdio: 'inherit',
env: {
...process.env,
VERCEL_API,
// TODO:
// VERCEL_SCOPE
// VERCEL_DEBUG
// VERCEL_HELP
},
});
proxy.close();
if (result instanceof Error) {
debug(`error running extension: ${result.message}`);
}
return result.exitCode;
}
class ENOENT extends Error {
code = 'ENOENT';
constructor(command: string) {
super(`Command "${command}" not found`);
}
}

View File

@@ -0,0 +1,44 @@
import { createServer } from 'http';
import { Headers } from 'node-fetch';
import {
toOutgoingHeaders,
mergeIntoServerResponse,
buildToHeaders,
} from '@edge-runtime/node-utils';
import type { Server } from 'http';
import type Client from '../client';
const toHeaders = buildToHeaders({
// @ts-expect-error - `node-fetch` Headers is missing `getAll()`
Headers,
});
export function createProxy(client: Client): Server {
return createServer(async (req, res) => {
try {
// Proxy to the upstream Vercel REST API
const headers = toHeaders(req.headers);
headers.delete('host');
const fetchRes = await client.fetch(req.url || '/', {
headers,
method: req.method,
body: req.method === 'GET' || req.method === 'HEAD' ? undefined : req,
useCurrentTeam: false,
json: false,
});
res.statusCode = fetchRes.status;
mergeIntoServerResponse(
// @ts-expect-error - `node-fetch` Headers is missing `getAll()`
toOutgoingHeaders(fetchRes.headers),
res
);
fetchRes.body.pipe(res);
} catch (err: unknown) {
client.output.prettyError(err);
if (!res.headersSent) {
res.statusCode = 500;
res.end('Unexpected error during API call');
}
}
});
}

View File

@@ -161,6 +161,7 @@ export default class Now extends EventEmitter {
const deployment = await processDeployment({
now: this,
output: this._output,
agent: this._client.agent,
paths,
requestBody,
uploadStamp,
@@ -294,7 +295,7 @@ export default class Now extends EventEmitter {
if (
error.errorCode === 'BUILD_FAILED' ||
error.errorCode === 'UNEXPECTED_ERROR' ||
error.errorCode.includes('BUILD_UTILS_SPAWN_')
error.errorCode?.includes('BUILD_UTILS_SPAWN_')
) {
return new BuildError({
message: error.errorMessage,

View File

@@ -0,0 +1,31 @@
import os from 'os';
import { join } from 'path';
import { readFile, writeFile } from 'fs-extra';
import { VERCEL_DIR } from '../projects/link';
export async function addToGitIgnore(path: string, ignore = VERCEL_DIR) {
let isGitIgnoreUpdated = false;
try {
const gitIgnorePath = join(path, '.gitignore');
let gitIgnore =
(await readFile(gitIgnorePath, 'utf8').catch(() => null)) ?? '';
const EOL = gitIgnore.includes('\r\n') ? '\r\n' : os.EOL;
let contentModified = false;
if (!gitIgnore.split(EOL).includes(ignore)) {
gitIgnore += `${
gitIgnore.endsWith(EOL) || gitIgnore.length === 0 ? '' : EOL
}${ignore}${EOL}`;
contentModified = true;
}
if (contentModified) {
await writeFile(gitIgnorePath, gitIgnore);
isGitIgnoreUpdated = true;
}
} catch (error) {
// ignore errors since this is non-critical
}
return isGitIgnoreUpdated;
}

View File

@@ -0,0 +1,272 @@
import chalk from 'chalk';
import pluralize from 'pluralize';
import { homedir } from 'os';
import { join, normalize } from 'path';
import { normalizePath } from '@vercel/build-utils';
import { lstat, readJSON, outputJSON, writeFile, readFile } from 'fs-extra';
import confirm from '../input/confirm';
import toHumanPath from '../humanize-path';
import {
VERCEL_DIR,
VERCEL_DIR_README,
VERCEL_DIR_REPO,
} from '../projects/link';
import { getRemoteUrls } from '../create-git-meta';
import link from '../output/link';
import { emoji, prependEmoji } from '../emoji';
import selectOrg from '../input/select-org';
import { addToGitIgnore } from './add-to-gitignore';
import type Client from '../client';
import type { Project } from '@vercel-internals/types';
const home = homedir();
export interface RepoProjectConfig {
id: string;
name: string;
directory: string;
}
export interface RepoProjectsConfig {
orgId: string;
remoteName: string;
projects: RepoProjectConfig[];
}
export interface RepoLink {
rootPath: string;
repoConfigPath: string;
repoConfig?: RepoProjectsConfig;
}
/**
* Given a directory path `cwd`, finds the root of the Git repository
* and returns the parsed `.vercel/repo.json` file if the repository
* has already been linked.
*/
export async function getRepoLink(cwd: string): Promise<RepoLink | undefined> {
// Determine where the root of the repo is
const rootPath = await findRepoRoot(cwd);
if (!rootPath) return undefined;
// Read the `repo.json`, if this repo has already been linked
const repoConfigPath = join(rootPath, VERCEL_DIR, VERCEL_DIR_REPO);
const repoConfig: RepoProjectsConfig = await readJSON(repoConfigPath).catch(
err => {
if (err.code !== 'ENOENT') throw err;
}
);
return { rootPath, repoConfig, repoConfigPath };
}
export async function ensureRepoLink(
client: Client,
cwd: string,
yes = false
): Promise<RepoLink | undefined> {
const { output } = client;
const repoLink = await getRepoLink(cwd);
if (repoLink) {
output.debug(`Found Git repository root directory: ${repoLink.rootPath}`);
} else {
throw new Error('Could not determine Git repository root directory');
}
let { rootPath, repoConfig, repoConfigPath } = repoLink;
if (!repoConfig) {
// Not yet linked, so prompt user to begin linking
let shouldLink =
yes ||
(await confirm(
client,
`Link Git repository at ${chalk.cyan(
`${toHumanPath(rootPath)}`
)} to your Project(s)?`,
true
));
if (!shouldLink) {
output.print(`Canceled. Repository not linked.\n`);
return;
}
const org = await selectOrg(
client,
'Which scope should contain your Project(s)?',
yes
);
client.config.currentTeam = org.type === 'team' ? org.id : undefined;
const remoteUrls = await getRemoteUrls(
join(rootPath, '.git/config'),
output
);
if (!remoteUrls) {
throw new Error('Could not determine Git remote URLs');
}
const remoteNames = Object.keys(remoteUrls);
let remoteName: string;
if (remoteNames.length === 1) {
remoteName = remoteNames[0];
} else {
// Prompt user to select which remote to use
const originIndex = remoteNames.indexOf('origin');
const answer = await client.prompt({
type: 'list',
name: 'value',
message: 'Which Git remote should be used?',
choices: remoteNames.map(name => {
return { name: name, value: name };
}),
default: originIndex === -1 ? 0 : originIndex,
});
remoteName = answer.value;
}
const repoUrl = remoteUrls[remoteName];
output.spinner(
`Fetching Projects for ${link(repoUrl)} under ${chalk.bold(org.slug)}`
);
// TODO: Add pagination to fetch all Projects
const query = new URLSearchParams({ repoUrl, limit: '100' });
const projects: Project[] = await client.fetch(`/v2/projects?${query}`);
if (projects.length === 0) {
output.log(
`No Projects are linked to ${link(repoUrl)} under ${chalk.bold(
org.slug
)}.`
);
// TODO: run detection logic to find potential projects.
// then prompt user to select valid projects.
// then create new Projects
} else {
output.log(
`Found ${chalk.bold(projects.length)} ${pluralize(
'Project',
projects.length
)} linked to ${link(repoUrl)} under ${chalk.bold(org.slug)}:`
);
}
for (const project of projects) {
output.print(` * ${chalk.cyan(`${org.slug}/${project.name}\n`)}`);
}
shouldLink =
yes ||
(await confirm(
client,
`Link to ${projects.length === 1 ? 'it' : 'them'}?`,
true
));
if (!shouldLink) {
output.print(`Canceled. Repository not linked.\n`);
return;
}
repoConfig = {
orgId: org.id,
remoteName,
projects: projects.map(project => {
return {
id: project.id,
name: project.name,
directory: normalize(project.rootDirectory || ''),
};
}),
};
await outputJSON(repoConfigPath, repoConfig, { spaces: 2 });
await writeFile(
join(rootPath, VERCEL_DIR, VERCEL_DIR_README),
await readFile(join(__dirname, 'VERCEL_DIR_README.txt'), 'utf8')
);
// update .gitignore
const isGitIgnoreUpdated = await addToGitIgnore(rootPath);
output.print(
prependEmoji(
`Linked to ${link(repoUrl)} under ${chalk.bold(
org.slug
)} (created ${VERCEL_DIR}${
isGitIgnoreUpdated ? ' and added it to .gitignore' : ''
})`,
emoji('link')
) + '\n'
);
}
return {
repoConfig,
repoConfigPath,
rootPath,
};
}
/**
* Given a `start` directory, traverses up the directory hierarchy until
* the nearest `.git/config` file is found. Returns the directory where
* the Git config was found, or `undefined` when no Git repo was found.
*/
export async function findRepoRoot(start: string): Promise<string | undefined> {
for (const current of traverseUpDirectories(start)) {
if (current === home) {
// Sometimes the $HOME directory is set up as a Git repo
// (for dotfiles, etc.). In this case it's safe to say that
// this isn't the repo we're looking for. Bail.
break;
}
const gitConfigPath = join(current, '.git/config');
const stat = await lstat(gitConfigPath).catch(err => {
if (err.code !== 'ENOENT') throw err;
});
if (stat) {
return current;
}
}
}
export function* traverseUpDirectories(start: string) {
let current: string | undefined = normalize(start);
while (current) {
yield current;
// Go up one directory
const next = join(current, '..');
current = next === current ? undefined : next;
}
}
function sortByDirectory(a: RepoProjectConfig, b: RepoProjectConfig): number {
const aParts = a.directory.split('/');
const bParts = b.directory.split('/');
return bParts.length - aParts.length;
}
/**
* Finds the matching Project from an array of Project links
* where the provided relative path is within the Project's
* root directory.
*/
export function findProjectFromPath(
projects: RepoProjectConfig[],
path: string
): RepoProjectConfig | undefined {
const normalizedPath = normalizePath(path);
return projects
.slice()
.sort(sortByDirectory)
.find(project => {
if (project.directory === '.') {
// Project has no "Root Directory" setting, so any path is valid
return true;
}
return (
normalizedPath === project.directory ||
normalizedPath.startsWith(`${project.directory}/`)
);
});
}

View File

@@ -1,7 +1,7 @@
import http from 'http';
import open from 'open';
import { URL } from 'url';
import listen from 'async-listen';
import { listen } from 'async-listen';
import isDocker from 'is-docker';
import Client from '../client';
import prompt, { readInput } from './prompt';
@@ -64,8 +64,7 @@ async function getVerificationTokenInBand(
) {
const { output } = client;
const server = http.createServer();
const address = await listen(server, 0, '127.0.0.1');
const { port } = new URL(address);
const { port } = await listen(server, 0, '127.0.0.1');
url.searchParams.set('next', `http://localhost:${port}`);
output.log(`Please visit the following URL in your web browser:`);

View File

@@ -1,9 +1,8 @@
import fs from 'fs';
import os from 'os';
import AJV from 'ajv';
import chalk from 'chalk';
import { join } from 'path';
import { ensureDir } from 'fs-extra';
import { join, relative } from 'path';
import { ensureDir, readJSON } from 'fs-extra';
import { promisify } from 'util';
import getProjectByIdOrName from '../projects/get-project-by-id-or-name';
@@ -23,6 +22,8 @@ import { isDirectory } from '../config/global-path';
import { NowBuildError, getPlatformEnv } from '@vercel/build-utils';
import outputCode from '../output/code';
import { isErrnoException, isError } from '@vercel/error-utils';
import { findProjectFromPath, findRepoRoot } from '../link/repo';
import { addToGitIgnore } from '../link/add-to-gitignore';
const readFile = promisify(fs.readFile);
const writeFile = promisify(fs.writeFile);
@@ -31,6 +32,7 @@ export const VERCEL_DIR = '.vercel';
export const VERCEL_DIR_FALLBACK = '.now';
export const VERCEL_DIR_README = 'README.txt';
export const VERCEL_DIR_PROJECT = 'project.json';
export const VERCEL_DIR_REPO = 'repo.json';
const linkSchema = {
type: 'object',
@@ -53,7 +55,7 @@ const linkSchema = {
*
* Throws an error if *both* `.vercel` and `.now` directories exist.
*/
export function getVercelDirectory(cwd: string = process.cwd()): string {
export function getVercelDirectory(cwd: string): string {
const possibleDirs = [join(cwd, VERCEL_DIR), join(cwd, VERCEL_DIR_FALLBACK)];
const existingDirs = possibleDirs.filter(d => isDirectory(d));
if (existingDirs.length > 1) {
@@ -67,9 +69,40 @@ export function getVercelDirectory(cwd: string = process.cwd()): string {
return existingDirs[0] || possibleDirs[0];
}
async function getLink(path?: string): Promise<ProjectLink | null> {
async function getLink(path: string): Promise<ProjectLink | null> {
// Try the individual project linking style (`${cwd}/.vercel/project.json`)
const dir = getVercelDirectory(path);
return getLinkFromDir(dir);
const linkFromProject = await getLinkFromDir(dir);
if (linkFromProject) {
return linkFromProject;
}
// Try the repo linking style (`${repoRoot}/.vercel/repo.json`)
return getLinkFromRepo(path);
}
async function getLinkFromRepo(path: string): Promise<ProjectLink | null> {
const repoRoot = await findRepoRoot(path);
if (!repoRoot) {
return null;
}
try {
const vercelDir = join(repoRoot, VERCEL_DIR);
const repoJsonPath = join(vercelDir, VERCEL_DIR_REPO);
const repoJson = await readJSON(repoJsonPath);
const project = findProjectFromPath(
repoJson.projects,
relative(repoRoot, path)
);
if (project) {
return { orgId: repoJson.orgId, projectId: project.id };
}
} catch (err: unknown) {
if (!isErrnoException(err) || err.code !== 'ENOENT') {
throw err;
}
}
return null;
}
export async function getLinkFromDir<T = ProjectLink>(
@@ -123,7 +156,7 @@ async function getOrgById(client: Client, orgId: string): Promise<Org | null> {
export async function getLinkedProject(
client: Client,
path?: string
path = process.cwd()
): Promise<ProjectLinkResult> {
const { output } = client;
const VERCEL_ORG_ID = getPlatformEnv('ORG_ID');
@@ -252,29 +285,7 @@ export async function linkFolderToProject(
);
// update .gitignore
let isGitIgnoreUpdated = false;
try {
const gitIgnorePath = join(path, '.gitignore');
let gitIgnore =
(await readFile(gitIgnorePath, 'utf8').catch(() => null)) ?? '';
const EOL = gitIgnore.includes('\r\n') ? '\r\n' : os.EOL;
let contentModified = false;
if (!gitIgnore.split(EOL).includes(VERCEL_DIR)) {
gitIgnore += `${
gitIgnore.endsWith(EOL) || gitIgnore.length === 0 ? '' : EOL
}${VERCEL_DIR}${EOL}`;
contentModified = true;
}
if (contentModified) {
await writeFile(gitIgnorePath, gitIgnore);
isGitIgnoreUpdated = true;
}
} catch (error) {
// ignore errors since this is non-critical
}
const isGitIgnoreUpdated = await addToGitIgnore(path);
output.print(
prependEmoji(

View File

@@ -1,5 +1,6 @@
{
"name": "nextjs-node",
"version": "0.0.0",
"license": "MIT",
"scripts": {
"dev": "next",
@@ -9,5 +10,6 @@
"next": "13.0.4",
"react": "18.2.0",
"react-dom": "18.2.0"
}
},
"ignoreNextjsUpdates": true
}

View File

@@ -1,8 +1,11 @@
{
"name": "10a-nextjs-routes",
"version": "0.0.0",
"private": true,
"dependencies": {
"next": "13.0.4",
"react": "18.2.0",
"react-dom": "18.2.0"
}
},
"ignoreNextjsUpdates": true
}

View File

@@ -1,5 +1,6 @@
{
"name": "nextjs-src-dir",
"version": "0.0.0",
"license": "MIT",
"scripts": {
"dev": "next",
@@ -9,5 +10,6 @@
"next": "13.0.4",
"react": "18.2.0",
"react-dom": "18.2.0"
}
},
"ignoreNextjsUpdates": true
}

View File

@@ -8,5 +8,6 @@
"next": "latest",
"react": "latest",
"react-dom": "latest"
}
},
"ignoreNextjsUpdates": true
}

View File

@@ -6,5 +6,6 @@
"next": "9.0.0",
"react": "16.8.6",
"react-dom": "16.8.6"
}
},
"ignoreNextjsUpdates": true
}

View File

@@ -8,5 +8,6 @@
"next": "12.1.6",
"react": "18.1.0",
"react-dom": "18.1.0"
}
},
"ignoreNextjsUpdates": true
}

View File

@@ -1,5 +1,6 @@
{
"name": "force-module-commonjs",
"version": "0.0.0",
"private": true,
"scripts": {
"dev": "next",
@@ -15,5 +16,6 @@
"@types/node": "18.x",
"@types/react": "18.0.25",
"typescript": "4.9.3"
}
},
"ignoreNextjsUpdates": true
}

View File

@@ -1,5 +1,6 @@
{
"name": "nested-tsconfig",
"version": "0.0.0",
"private": true,
"scripts": {
"dev": "next",
@@ -15,5 +16,6 @@
"@types/node": "18.x",
"@types/react": "18.0.25",
"typescript": "4.9.3"
}
},
"ignoreNextjsUpdates": true
}

View File

@@ -2,7 +2,7 @@ import os from 'os';
import url from 'url';
import fs from 'fs-extra';
import { join } from 'path';
import listen from 'async-listen';
import { listen } from 'async-listen';
import stripAnsi from 'strip-ansi';
import { createServer } from 'http';

View File

@@ -0,0 +1,16 @@
#!/usr/bin/env node
const { get } = require('http');
const { VERCEL_API } = process.env;
console.log('Hello from a CLI extension!');
console.log(`VERCEL_API: ${VERCEL_API}`);
get(`${VERCEL_API}/v2/user`, async (res) => {
let body = '';
res.setEncoding('utf8');
for await (const chunk of res) {
body += chunk;
}
const data = JSON.parse(body);
console.log(`Username: ${data.user.username}`);
});

View File

@@ -0,0 +1,6 @@
{
"name": "cli-extension-whoami",
"bin": {
"vercel-mywhoami": "bin.js"
}
}

View File

@@ -0,0 +1,26 @@
{
"name": "cli-extension",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"dependencies": {
"cli-extension-whoami": "file:../cli-extension-whoami"
}
},
"../cli-extension-whoami": {
"bin": {
"vercel-mywhoami": "bin.js"
}
},
"node_modules/cli-extension-whoami": {
"resolved": "../cli-extension-whoami",
"link": true
}
},
"dependencies": {
"cli-extension-whoami": {
"version": "file:../cli-extension-whoami"
}
}
}

View File

@@ -0,0 +1,6 @@
{
"private": true,
"dependencies": {
"cli-extension-whoami": "file:../cli-extension-whoami"
}
}

View File

@@ -1,5 +1,6 @@
{
"dependencies": {
"next": "13.0.4"
}
},
"ignoreNextjsUpdates": true
}

View File

@@ -8,5 +8,6 @@
"next": "^8.0.0",
"react": "^16.7.0",
"react-dom": "^16.7.0"
}
},
"ignoreNextjsUpdates": true
}

View File

@@ -8,5 +8,6 @@
"next": "^8.0.0",
"react": "^16.7.0",
"react-dom": "^16.7.0"
}
},
"ignoreNextjsUpdates": true
}

View File

@@ -8,5 +8,6 @@
"next": "^8.0.0",
"react": "^16.7.0",
"react-dom": "^16.7.0"
}
},
"ignoreNextjsUpdates": true
}

View File

@@ -8,5 +8,6 @@
"next": "^8.0.0",
"react": "^16.7.0",
"react-dom": "^16.7.0"
}
},
"ignoreNextjsUpdates": true
}

View File

@@ -5,6 +5,7 @@ import { URL, parse as parseUrl } from 'url';
import semVer from 'semver';
import { Readable } from 'stream';
import { homedir } from 'os';
import { runNpmInstall } from '@vercel/build-utils';
import { execCli } from './helpers/exec';
import fetch, { RequestInit, RequestInfo } from 'node-fetch';
import retry from 'async-retry';
@@ -1420,6 +1421,19 @@ test('use build-env', async () => {
expect(content.trim()).toBe('bar');
});
test('should invoke CLI extension', async () => {
const fixture = path.join(__dirname, 'fixtures/e2e/cli-extension');
// Ensure the `.bin` is populated in the fixture
await runNpmInstall(fixture);
const output = await execCli(binaryPath, ['mywhoami'], { cwd: fixture });
const formatted = formatOutput(output);
expect(output.stdout, formatted).toContain('Hello from a CLI extension!');
expect(output.stdout, formatted).toContain('VERCEL_API: http://127.0.0.1:');
expect(output.stdout, formatted).toContain(`Username: ${contextName}`);
});
// NOTE: Order matters here. This must be the last test in the file.
test('default command should prompt login with empty auth.json', async () => {
await clearAuthConfig();

View File

@@ -5,7 +5,7 @@ import chalk from 'chalk';
import { PassThrough } from 'stream';
import { createServer, Server } from 'http';
import express, { Express, Router } from 'express';
import listen from 'async-listen';
import { listen } from 'async-listen';
import Client from '../../src/util/client';
import { Output } from '../../src/util/output';
@@ -101,6 +101,9 @@ export class MockClient extends Client {
this.localConfig = {};
this.scenario = Router();
this.agent?.destroy();
this.agent = undefined;
}
async startMockServer() {

View File

@@ -31,6 +31,16 @@ const envs: ProjectEnvVariable[] = [
updatedAt: 1557241361455,
createdAt: 1557241361455,
},
{
type: 'encrypted',
id: '781dt89g8r2h789g',
key: 'ANOTHER',
value: 'one',
target: ['preview'],
configurationId: null,
updatedAt: 1557241361455,
createdAt: 1557241361455,
},
{
type: 'encrypted',
id: 'r124t6frtu25df16',

View File

@@ -1,5 +1,6 @@
import fs from 'fs-extra';
import path from 'path';
import { parse } from 'dotenv';
import env from '../../../src/commands/env';
import { setupUnitFixture } from '../../helpers/setup-unit-fixture';
import { client } from '../../mocks/client';
@@ -103,6 +104,13 @@ describe('env', () => {
expect(rawDevEnv).toContain(
'BRANCH_ENV_VAR="env var for a specific branch"'
);
const parsed = parse(rawDevEnv);
const keys = Object.keys(parsed);
expect(keys).toHaveLength(3);
expect(keys[0]).toEqual('ANOTHER');
expect(keys[1]).toEqual('BRANCH_ENV_VAR');
expect(keys[2]).toEqual('REDIS_CONNECTION_STRING');
});
it('should handle alternate filename', async () => {

View File

@@ -0,0 +1,35 @@
import { listen } from 'async-listen';
import { createProxy } from 'proxy';
import { ProxyAgent } from 'proxy-agent';
import { client } from '../../mocks/client';
describe('Client', () => {
describe('fetch()', () => {
beforeEach(() => {
delete process.env.HTTPS_PROXY;
});
it('should respect the `HTTPS_PROXY` env var', async () => {
let connectCount = 0;
const proxy = createProxy();
const proxyUrl = await listen(proxy);
proxy.on('connect', () => {
connectCount++;
});
try {
process.env.HTTPS_PROXY = proxyUrl.href;
client.agent = new ProxyAgent({ keepAlive: true });
expect(connectCount).toEqual(0);
const res = await client.fetch('https://example.com/', { json: false });
expect(connectCount).toEqual(1);
expect(res.status).toEqual(200);
} finally {
client.agent?.destroy();
proxy.close();
}
});
});
});

View File

@@ -1,5 +1,5 @@
import fetch from 'node-fetch';
import listen from 'async-listen';
import { listen } from 'async-listen';
import { createServer, IncomingMessage, Server, ServerResponse } from 'http';
import type { JSONValue } from '@vercel-internals/types';
import {
@@ -22,7 +22,7 @@ describe('responseError()', () => {
beforeAll(async () => {
server = createServer((req, res) => handler(req, res));
url = await listen(server);
url = (await listen(server)).toString();
});
afterAll(() => {

View File

@@ -0,0 +1,91 @@
import { tmpdir } from 'node:os';
import { join, sep } from 'node:path';
import {
findProjectFromPath,
findRepoRoot,
RepoProjectConfig,
traverseUpDirectories,
} from '../../../../src/util/link/repo';
const isWindows = process.platform === 'win32';
// Root of `vercel/vercel` repo
const vercelRepoRoot = join(__dirname, '../../../../../..');
describe('findRepoRoot()', () => {
it('should find Git repo root from root', async () => {
const repoRoot = await findRepoRoot(vercelRepoRoot);
expect(repoRoot).toEqual(vercelRepoRoot);
});
it('should find Git repo root sub directory', async () => {
const repoRoot = await findRepoRoot(__dirname);
expect(repoRoot).toEqual(vercelRepoRoot);
});
it('should return `undefined` when no Git root found', async () => {
const repoRoot = await findRepoRoot(tmpdir());
expect(repoRoot).toEqual(undefined);
});
});
describe('traverseUpDirectories()', () => {
test.each(
isWindows
? [
{
input: 'C:\\foo\\bar\\baz',
expected: ['C:\\foo\\bar\\baz', 'C:\\foo\\bar', 'C:\\foo', 'C:\\'],
},
{
input: 'C:\\foo\\..\\bar\\.\\baz',
expected: ['C:\\bar\\baz', 'C:\\bar', 'C:\\'],
},
]
: [
{
input: '/foo/bar/baz',
expected: ['/foo/bar/baz', '/foo/bar', '/foo', '/'],
},
{
input: '/foo/../bar/./baz',
expected: ['/bar/baz', '/bar', '/'],
},
]
)('should traverse "$input"', ({ input, expected }) => {
expect(Array.from(traverseUpDirectories(input))).toEqual(expected);
});
});
describe('findProjectFromPath()', () => {
const projects: RepoProjectConfig[] = [
{ id: 'root', name: 'r', directory: '.' },
{ id: 'site', name: 'a', directory: 'apps/site' },
{ id: 'site2', name: 'a', directory: 'apps/site2' },
{ id: 'other', name: 'b', directory: 'apps/other' },
{ id: 'nested', name: 'n', directory: 'apps/other/nested' },
];
it.each([
{ id: 'root', path: '.' },
{ id: 'root', path: 'lib' },
{ id: 'root', path: 'lib' },
{ id: 'site', path: `apps${sep}site` },
{ id: 'site', path: `apps${sep}site` },
{ id: 'site', path: `apps${sep}site${sep}components` },
{ id: 'site2', path: `apps${sep}site2` },
{ id: 'site2', path: `apps${sep}site2${sep}inner` },
{ id: 'other', path: `apps${sep}other` },
{ id: 'other', path: `apps${sep}other${sep}lib` },
{ id: 'nested', path: `apps${sep}other${sep}nested` },
{ id: 'nested', path: `apps${sep}other${sep}nested${sep}foo` },
])('should find Project "$id" for path "$path"', ({ path, id }) => {
const actual = findProjectFromPath(projects, path);
expect(actual?.id).toEqual(id);
});
it('should return `undefined` when there are no matching Projects', () => {
const actual = findProjectFromPath([projects[1]], '.');
expect(actual).toBeUndefined();
});
});

View File

@@ -0,0 +1,8 @@
# @vercel/client
## 12.5.1
### Patch Changes
- Updated dependencies [[`71b9f3a94`](https://github.com/vercel/vercel/commit/71b9f3a94b7922607f8f24bf7b2bd1742e62cc05)]:
- @vercel/build-utils@6.7.3

View File

@@ -1,6 +1,6 @@
{
"name": "@vercel/client",
"version": "12.4.12",
"version": "12.5.1",
"main": "dist/index.js",
"typings": "dist/index.d.ts",
"homepage": "https://vercel.com",
@@ -35,7 +35,7 @@
"typescript": "4.9.5"
},
"dependencies": {
"@vercel/build-utils": "6.7.2",
"@vercel/build-utils": "6.7.3",
"@vercel/routing-utils": "2.2.1",
"@zeit/fetch": "5.2.0",
"async-retry": "1.2.3",

View File

@@ -56,7 +56,7 @@ export async function* checkDeploymentStatus(
teamId ? `?teamId=${teamId}` : ''
}`,
token,
{ apiUrl, userAgent }
{ apiUrl, userAgent, agent: clientOptions.agent }
);
const deploymentUpdate = await deploymentData.json();

View File

@@ -46,6 +46,7 @@ async function* postDeployment(
}),
apiUrl: clientOptions.apiUrl,
userAgent: clientOptions.userAgent,
agent: clientOptions.agent,
}
);

View File

@@ -1,3 +1,4 @@
import type { Agent } from 'http';
import type {
Builder,
BuilderFunctions,
@@ -31,6 +32,7 @@ export interface VercelClientOptions {
isDirectory?: boolean;
skipAutoDetectionConfirmation?: boolean;
archive?: ArchiveFormat;
agent?: Agent;
}
/** @deprecated Use VercelClientOptions instead. */

View File

@@ -79,7 +79,7 @@ export async function* upload(
debug('Building an upload list...');
const semaphore = new Sema(50, { capacity: 50 });
const agent = apiUrl?.startsWith('https://')
const defaultAgent = apiUrl?.startsWith('https://')
? new https.Agent({ keepAlive: true })
: new http.Agent({ keepAlive: true });
@@ -132,7 +132,7 @@ export async function* upload(
API_FILES,
token,
{
agent,
agent: clientOptions.agent || defaultAgent,
method: 'POST',
headers: {
'Content-Type': 'application/octet-stream',

View File

@@ -1,11 +1,16 @@
{
"name": "@vercel/frameworks",
"version": "1.4.1",
"version": "1.4.2",
"main": "./dist/frameworks.js",
"types": "./dist/frameworks.d.ts",
"files": [
"dist"
],
"repository": {
"type": "git",
"url": "https://github.com/vercel/vercel.git",
"directory": "packages/frameworks"
},
"license": "Apache-2.0",
"scripts": {
"build": "tsc",

View File

@@ -62,16 +62,40 @@ const SchemaSettings = {
],
};
const RouteSchema = {
type: 'array',
items: {
properties: {
src: { type: 'string' },
dest: { type: 'string' },
status: { type: 'number' },
handle: { type: 'string' },
headers: { type: 'object' },
continue: { type: 'boolean' },
},
},
};
const Schema = {
type: 'array',
items: {
type: 'object',
required: ['name', 'slug', 'logo', 'description', 'settings'],
additionalProperties: false,
required: [
'name',
'slug',
'logo',
'description',
'settings',
'getOutputDirName',
],
properties: {
name: { type: 'string' },
slug: { type: ['string', 'null'] },
sort: { type: 'number' },
logo: { type: 'string' },
darkModeLogo: { type: 'string' },
screenshot: { type: 'string' },
demo: { type: 'string' },
tagline: { type: 'string' },
website: { type: 'string' },
@@ -116,6 +140,26 @@ const Schema = {
outputDirectory: SchemaSettings,
},
},
getOutputDirName: {
isFunction: true,
},
defaultRoutes: {
oneOf: [{ isFunction: true }, RouteSchema],
},
defaulHeaders: {
type: 'array',
items: {
properties: {
source: { type: 'string' },
regex: { type: 'string' },
headers: { type: 'object' },
continue: { type: 'boolean' },
},
},
},
disableRootMiddleware: {
type: 'boolean',
},
recommendedIntegrations: {
type: 'array',
items: {
@@ -167,7 +211,8 @@ describe('frameworks', () => {
});
it('ensure schema', async () => {
const ajv = new Ajv();
const ajv = getValidator();
const result = ajv.validate(Schema, frameworkList);
if (ajv.errors) {
@@ -246,3 +291,16 @@ describe('frameworks', () => {
);
});
});
function getValidator() {
const ajv = new Ajv();
ajv.addKeyword('isFunction', {
compile: shouldMatch => data => {
const matches = typeof data === 'function';
return (shouldMatch && matches) || (!shouldMatch && !matches);
},
});
return ajv;
}

View File

@@ -0,0 +1,7 @@
# @vercel/fs-detectors
## 3.9.3
### Patch Changes
- clarify next.js dupe api directory warning ([#9979](https://github.com/vercel/vercel/pull/9979))

View File

@@ -1,6 +1,6 @@
{
"name": "@vercel/fs-detectors",
"version": "3.9.1",
"version": "3.9.3",
"description": "Vercel filesystem detectors",
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
@@ -20,7 +20,7 @@
},
"dependencies": {
"@vercel/error-utils": "1.0.10",
"@vercel/frameworks": "1.4.1",
"@vercel/frameworks": "1.4.2",
"@vercel/routing-utils": "2.2.1",
"glob": "8.0.3",
"js-yaml": "4.1.0",
@@ -35,7 +35,7 @@
"@types/minimatch": "3.0.5",
"@types/node": "14.18.33",
"@types/semver": "7.3.10",
"@vercel/build-utils": "6.7.2",
"@vercel/build-utils": "6.7.3",
"typescript": "4.9.5"
}
}

View File

@@ -339,7 +339,7 @@ export async function detectBuilders(
warnings.push({
code: 'conflicting_files',
message:
'When using Next.js, it is recommended to place Node.js Serverless Functions inside of the `pages/api` (provided by Next.js) directory instead of `api` (provided by Vercel).',
'When using Next.js, it is recommended to place JavaScript Functions inside of the `pages/api` (provided by Next.js) directory instead of `api` (provided by Vercel). Other languages (Python, Go, etc) should still go in the `api` directory.',
link: 'https://nextjs.org/docs/api-routes/introduction',
action: 'Learn More',
});

View File

@@ -11,5 +11,6 @@
"license": "ISC",
"dependencies": {
"next": "^4.3.2"
}
},
"ignoreNextjsUpdates": true
}

View File

@@ -11,5 +11,6 @@
"license": "ISC",
"dependencies": {
"next": "^4.3.2"
}
},
"ignoreNextjsUpdates": true
}

View File

@@ -11,5 +11,6 @@
"license": "ISC",
"dependencies": {
"next": "^4.3.2"
}
},
"ignoreNextjsUpdates": true
}

View File

@@ -11,5 +11,6 @@
"license": "ISC",
"dependencies": {
"next": "^4.3.2"
}
},
"ignoreNextjsUpdates": true
}

View File

@@ -11,5 +11,6 @@
"license": "ISC",
"dependencies": {
"next": "^4.3.2"
}
},
"ignoreNextjsUpdates": true
}

View File

@@ -11,5 +11,6 @@
"license": "ISC",
"dependencies": {
"next": "^4.3.2"
}
},
"ignoreNextjsUpdates": true
}

View File

@@ -11,5 +11,6 @@
"license": "ISC",
"dependencies": {
"next": "^4.3.2"
}
},
"ignoreNextjsUpdates": true
}

View File

@@ -11,5 +11,6 @@
"license": "ISC",
"dependencies": {
"next": "^4.3.2"
}
},
"ignoreNextjsUpdates": true
}

View File

@@ -11,5 +11,6 @@
"license": "ISC",
"dependencies": {
"next": "^4.3.2"
}
},
"ignoreNextjsUpdates": true
}

View File

@@ -5,5 +5,6 @@
},
"dependencies": {
"next": "^4.3.2"
}
},
"ignoreNextjsUpdates": true
}

View File

@@ -1,5 +1,6 @@
{
"dependencies": {
"next": "1.2.3"
}
},
"ignoreNextjsUpdates": true
}

View File

@@ -2785,7 +2785,7 @@ it('Test `detectRoutes` with `featHandleMiss=true`', async () => {
{
code: 'conflicting_files',
message:
'When using Next.js, it is recommended to place Node.js Serverless Functions inside of the `pages/api` (provided by Next.js) directory instead of `api` (provided by Vercel).',
'When using Next.js, it is recommended to place JavaScript Functions inside of the `pages/api` (provided by Next.js) directory instead of `api` (provided by Vercel). Other languages (Python, Go, etc) should still go in the `api` directory.',
link: 'https://nextjs.org/docs/api-routes/introduction',
action: 'Learn More',
},
@@ -2820,7 +2820,7 @@ it('Test `detectRoutes` with `featHandleMiss=true`', async () => {
{
code: 'conflicting_files',
message:
'When using Next.js, it is recommended to place Node.js Serverless Functions inside of the `pages/api` (provided by Next.js) directory instead of `api` (provided by Vercel).',
'When using Next.js, it is recommended to place JavaScript Functions inside of the `pages/api` (provided by Next.js) directory instead of `api` (provided by Vercel). Other languages (Python, Go, etc) should still go in the `api` directory.',
link: 'https://nextjs.org/docs/api-routes/introduction',
action: 'Learn More',
},

View File

@@ -0,0 +1,15 @@
# @vercel/gatsby-plugin-vercel-builder
## 1.3.5
### Patch Changes
- add repo details for gatsby-plugin-vercel-builder ([#9990](https://github.com/vercel/vercel/pull/9990))
## 1.3.4
### Patch Changes
- Updated dependencies [[`71b9f3a94`](https://github.com/vercel/vercel/commit/71b9f3a94b7922607f8f24bf7b2bd1742e62cc05)]:
- @vercel/build-utils@6.7.3
- @vercel/node@2.14.3

View File

@@ -1,6 +1,6 @@
{
"name": "@vercel/gatsby-plugin-vercel-builder",
"version": "1.3.2",
"version": "1.3.5",
"main": "dist/index.js",
"files": [
"dist",
@@ -8,6 +8,11 @@
"gatsby-node.js",
"gatsby-node.js.map"
],
"repository": {
"type": "git",
"url": "https://github.com/vercel/vercel.git",
"directory": "packages/gatsby-plugin-vercel-builder"
},
"scripts": {
"build": "pnpm build:src && pnpm build:gatsby",
"build:gatsby": "tsc -p tsconfig.gatsby.json",
@@ -15,8 +20,8 @@
},
"dependencies": {
"@sinclair/typebox": "0.25.24",
"@vercel/build-utils": "6.7.2",
"@vercel/node": "2.14.1",
"@vercel/build-utils": "6.7.3",
"@vercel/node": "2.14.3",
"@vercel/routing-utils": "2.2.1",
"esbuild": "0.14.47",
"etag": "1.8.1",

View File

@@ -27,7 +27,7 @@
"@types/node-fetch": "^2.3.0",
"@types/tar": "^4.0.0",
"@types/yauzl-promise": "2.1.0",
"@vercel/build-utils": "6.7.2",
"@vercel/build-utils": "6.7.3",
"@vercel/ncc": "0.24.0",
"async-retry": "1.3.1",
"execa": "^1.0.0",

View File

@@ -21,7 +21,7 @@
"devDependencies": {
"@types/jest": "27.5.1",
"@types/node": "14.18.33",
"@vercel/build-utils": "6.7.2",
"@vercel/build-utils": "6.7.3",
"@vercel/static-config": "2.0.17",
"execa": "3.2.0",
"fs-extra": "11.1.0",

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