Compare commits

...

142 Commits

Author SHA1 Message Date
Vercel Release Bot
f43e413ba5 Version Packages (#10302)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2023-08-08 17:10:58 -05:00
Nathan Rajlich
0945d24cbe [remix] Add initial support for Hydrogen v2 (#10305)
Enables support for Hydrogen v2 apps using the "Remix" preset. This initial support works with the Hydrogen demo store template unmodified, and all pages will use Edge functions.

Node.js runtime, and also any other configuration via `export const config`, are not supported at this time, due to some pending blockers.
2023-08-08 21:39:57 +00:00
Steven
a8ecf40d6f [build-utils] Fix getPrefixedEnvVars() to handle VERCEL_BRANCH_URL (#10315)
- Fixes [NEXT-1500](https://linear.app/vercel/issue/NEXT-1500)
- See
[documentation](https://vercel.com/docs/concepts/projects/environment-variables/system-environment-variables#framework-environment-variables)
2023-08-08 14:43:20 -04:00
Steven
2995781a58 Revert "Just testing changeset stuff"
This reverts commit e2096c268d.
2023-08-07 16:36:40 -04:00
Trek Glowacki
e2096c268d Just testing changeset stuff 2023-08-07 15:33:26 -05:00
Amy
663d8e6437 Update discussion templates (#10297)
The current note on discussion templates is not bold enough and easily overlooked.

In an effort to reduce off-topic discussion clutter in this repo and direct folks to the general support community which I monitor more closely, this update makes the note more prominent.
2023-08-07 18:33:41 +00:00
Trek Glowacki
153ca440fa Add basic tests for remove command (#10304)
Adds some minimal unit testing for the `remove` command. There is currently none.
2023-08-07 18:14:21 +00:00
Steven
c5e0cb4812 chore(ci): fix turbo logs to no longer group (#10310)
When we upgraded `turbo`, the default `--log-order` changed from `stream` to `grouped`.

This PR changes it back to `stream` which will prevent the collapsed group view that we currently see in CI.

https://turbo.build/repo/docs/reference/command-line-reference/run#--log-order

> If log order is set to auto and turbo detects that it is running on GitHub Actions, then turbo will create grouped logs(opens in a new tab). You can opt out of this behavior by setting a log order of your own.
2023-08-07 16:44:41 +00:00
Seiya Nuta
5bf1fe4c74 [next] Preserve sourceMappingURL comments in template literals (#10275)
We noticed some edge function builds are failing unexpectedly due to `SyntaxError`. It's triggered by valid code in [Webpack's style-loader](16e401b17a/src/runtime/styleDomAPI.js (L35C1-L40C1)):

```
  if (sourceMap && typeof btoa !== "undefined") {
    css += `\n/*# sourceMappingURL=data:application/json;base64,${btoa(
      unescape(encodeURIComponent(JSON.stringify(sourceMap)))
    )} */`;
  }
```

After minification this script looks like:

```
sourceMap&&typeof btoa!="undefined"&&(css+=`
/*# sourceMappingURL=data:application/json;base64,${btoa(unescape(encodeURIComponent(JSON.stringify(sourceMap))))} */`);
```

The key point here is `\n` is replaced with newline character (`0x0A`) and the second line starts with `/*# sourceMappingURL=`. This confuses `removeComments` API in [convert-source-map](https://github.com/thlorenz/convert-source-map/tree/master) package and it removes the second line:

```
sourceMap&&typeof btoa!="undefined"&&(css+=`

```

In this PR, we implement source map stripping based on that package's regex but add some heuristic checks to prevent false positives like above.
2023-08-07 16:05:39 +00:00
Vercel Release Bot
011f836aa7 [examples][tests] Upgrade Next.js to version 13.4.13 (#10308)
This auto-generated PR updates 3 packages to Next.js version 13.4.13
2023-08-07 15:12:14 +00:00
Trek Glowacki
ec107d7c91 Handle calls for deployment aliases when mocking deployment API (#10303)
Attempting to add tests to unblock a contributor https://github.com/vercel/vercel/pull/10236#issuecomment-1664183891 and finding a deployment also checks its aliases. This PR updates the mock deployment code to also create a handler for its aliases.
2023-08-07 14:34:49 +00:00
Shu Uesugi
0d27ae3b1a Remove unused code (#10309)
Reference was removed in https://github.com/vercel/vercel/pull/8377 but the code wasn't removed.
2023-08-07 14:10:22 +00:00
Vercel Release Bot
08da4b9c92 [remix] Update @remix-run/dev to v1.19.2 (#10299)
This auto-generated PR updates `@remix-run/dev` to version 1.19.2.
2023-08-04 23:17:40 +00:00
Trek Glowacki
877f09ff5c Be looser with mock server URLs (#10300)
Co-authored-by: Trek Glowacki <trek.glowacki@vercel.com>
2023-08-04 10:20:33 -05:00
Vercel Release Bot
5cca9b6c5c Version Packages (#10289)
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@31.2.2

### Patch Changes

- Migrate list command to new structure
([#10284](https://github.com/vercel/vercel/pull/10284))

- Migrate whoami command to new structure
([#10266](https://github.com/vercel/vercel/pull/10266))

- Migrate logs command to new structure
([#10281](https://github.com/vercel/vercel/pull/10281))

- Migrate login command to new structure
([#10283](https://github.com/vercel/vercel/pull/10283))

- Migrate pull command to new structure
([#10280](https://github.com/vercel/vercel/pull/10280))

- Migrate logout command to new structure
([#10282](https://github.com/vercel/vercel/pull/10282))

- Migrate build command to new structure
([#10286](https://github.com/vercel/vercel/pull/10286))

- Migrate inspect command to new structure
([#10277](https://github.com/vercel/vercel/pull/10277))

- Migrate redeploy command to new structure
([#10279](https://github.com/vercel/vercel/pull/10279))

- Migrate link command to new structure
([#10285](https://github.com/vercel/vercel/pull/10285))

- Update spacing of --help output for CLI
([#10287](https://github.com/vercel/vercel/pull/10287))

- Updated dependencies
\[[`4af242af8`](4af242af86),
[`0cbdae141`](0cbdae1411),
[`85dd66778`](85dd667781)]:
    -   @vercel/node@2.15.8
    -   @vercel/remix-builder@1.9.1
    -   @vercel/static-build@1.3.44

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

### Patch Changes

- Updated dependencies
\[[`4af242af8`](4af242af86),
[`85dd66778`](85dd667781)]:
    -   @vercel/node@2.15.8

## @vercel/node@2.15.8

### Patch Changes

- Move `@types/content-type` to dev dependency
([#10292](https://github.com/vercel/vercel/pull/10292))

- fix: compress condition
([#10288](https://github.com/vercel/vercel/pull/10288))

## @vercel/remix-builder@1.9.1

### Patch Changes

- Disable root workspace check in pnpm and yarn when adding deps
([#10291](https://github.com/vercel/vercel/pull/10291))

## @vercel/static-build@1.3.44

### Patch Changes

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

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2023-08-02 15:04:48 -05:00
Nathan Rajlich
4af242af86 [node] Move @types/content-type to dev dependency (#10292)
`@types/content-type` is only needed at build time.
2023-08-02 20:00:14 +00:00
Nathan Rajlich
0cbdae1411 [remix] Disable root workspace check in pnpm and yarn when adding deps (#10291)
The Remix application might _also_ be the root of a workspace, so check for that case and pass the appropriate flag to the package manager to make it not error.

See: https://github.com/orgs/vercel/discussions/3467
2023-08-02 18:36:38 +00:00
Kiko Beats
85dd667781 fix: compress condition (#10288)
I'm experiencing an issue due the condition is wrongly set.

When the response is buffered, it should be `compress: true` since we're acting as proxy, and we're going to recompress again.

When the response is streaming, then it should be `compress: false` since the response is going to be piped.
2023-08-02 18:15:16 +00:00
Trek Glowacki
7d3dda7341 [cli] Update spacing on --help output (#10287)
Indent more items rightwards to tidy up spacing.

Example of unmigrated `bisect` on current `main`:
<img width="561" alt="CleanShot 2023-07-31 at 14 47 38@2x" src="https://github.com/vercel/vercel/assets/9736/7474ac88-775c-43aa-937b-3f544c064c6d">

Migrated example (https://github.com/vercel/vercel/pull/10276) before changes in this PR:
<img width="661" alt="CleanShot 2023-07-31 at 15 07 26@2x" src="https://github.com/vercel/vercel/assets/9736/076b247c-35cf-45ea-8713-3c684b5063e4">


Migrated example after changes in this PR:
<img width="693" alt="CleanShot 2023-07-31 at 14 46 39@2x" src="https://github.com/vercel/vercel/assets/9736/f5f056a1-7f68-42e5-a724-79cf86031d09">
2023-08-02 17:33:59 +00:00
Vercel Release Bot
2144d0b2a9 [tests] Upgrade Turbo to version 1.10.12 (#10270)
This auto-generated PR updates Turbo to version 1.10.12
2023-08-02 17:12:33 +00:00
Trek Glowacki
976e6aedf9 [cli] Migrate link command to new structure (#10285)
Before:
<img width="785" alt="CleanShot 2023-07-31 at 09 55 03@2x" src="https://github.com/vercel/vercel/assets/9736/1f60cbcb-354f-4005-bb74-8ffd68a38fba">

After:
<img width="785" alt="CleanShot 2023-07-31 at 09 55 03@2x" src="https://github.com/vercel/vercel/assets/9736/4eea5e82-0304-4949-9e8f-aeaa1b2e74eb">
2023-08-02 16:48:24 +00:00
Trek Glowacki
6328751e14 [cli] Migrate whoami to new command structure (#10266)
Migrating `whoami` to the command structure created in #10090. Unsure about tests. This is technically a refactor so existing tests should suffice.
2023-08-02 14:21:57 +00:00
Trek Glowacki
8cb49a5136 [cli] Migrate logout command to new structure (#10282)
Co-authored-by: Trek Glowacki <trek.glowacki@vercel.com>
Co-authored-by: Chris Barber <chris.barber@vercel.com>
2023-08-02 08:59:49 -05:00
Trek Glowacki
3fa4f344cc [cli] Migrate pull command to new structure (#10280)
Before:
<img width="797" alt="CleanShot 2023-07-31 at 09 23 44@2x" src="https://github.com/vercel/vercel/assets/9736/88edd229-0aab-4155-9739-de64ad42e2f0">

After:
<img width="765" alt="CleanShot 2023-07-31 at 09 23 59@2x" src="https://github.com/vercel/vercel/assets/9736/574d0af0-5ce4-48dd-ad59-c4ca9893c147">
2023-08-01 20:24:25 +00:00
Trek Glowacki
27610896ed [cli] Migrate inspect command to new structure (#10277)
Before:
<img width="659" alt="CleanShot 2023-07-31 at 10 06 01@2x" src="https://github.com/vercel/vercel/assets/9736/32c91a94-9ae7-479c-8255-e4b554badd2f">


After:
<img width="664" alt="CleanShot 2023-07-31 at 10 06 11@2x" src="https://github.com/vercel/vercel/assets/9736/9237b42a-a015-4d58-8231-5e257342ac90">
2023-08-01 20:05:11 +00:00
Trek Glowacki
b9dae36e37 [cli] Migrate logs command to new structure (#10281)
Before:
<img width="669" alt="CleanShot 2023-07-31 at 09 36 25@2x" src="https://github.com/vercel/vercel/assets/9736/ee28d4b7-33eb-49cf-a565-fd4f56bf8bb6">

After:
<img width="693" alt="CleanShot 2023-07-31 at 09 36 46@2x" src="https://github.com/vercel/vercel/assets/9736/450d7c54-216f-4590-98b9-9f6b1a13d040">
2023-08-01 19:43:16 +00:00
Ilya Komichev
1537ff9c38 chore(README): set correct link to nuxt docs (#10265)
Co-authored-by: Chris Barber <chris.barber@vercel.com>
2023-08-01 13:46:39 -05:00
Trek Glowacki
7e0317775f [cli] Migrate build command to new structure (#10286)
Before:
<img width="854" alt="CleanShot 2023-07-31 at 09 59 18@2x" src="https://github.com/vercel/vercel/assets/9736/7a91572f-1481-4a2a-ac70-2384d6c30560">

After:
<img width="907" alt="CleanShot 2023-07-31 at 09 59 30@2x" src="https://github.com/vercel/vercel/assets/9736/ed358eae-9207-4ebe-ad41-865ce213560e">
2023-08-01 16:59:01 +00:00
Trek Glowacki
2dd27976b3 [cli] Migrate redeploy command to new structure (#10279)
Before:
<img width="816" alt="CleanShot 2023-07-31 at 09 19 16@2x" src="https://github.com/vercel/vercel/assets/9736/cf194706-8071-4f86-9a1c-896383a51a29">

After:
<img width="673" alt="CleanShot 2023-07-31 at 09 19 30@2x" src="https://github.com/vercel/vercel/assets/9736/d0f3faed-83e7-40b9-9252-c8b68855a7a3">
2023-08-01 16:37:32 +00:00
Trek Glowacki
25e2b7efba [cli] Migrate login command to new structure (#10283)
We currently incorrectly specify the argument is required with `<>`, but the examples show you can do `vercel login` without an argument, so I've corrected that as part of this migration.

Before:
<img width="549" alt="CleanShot 2023-07-31 at 09 44 28@2x" src="https://github.com/vercel/vercel/assets/9736/f1b1a328-69e0-4d61-b65c-d2739c81f61c">


After:
<img width="684" alt="CleanShot 2023-07-31 at 09 44 43@2x" src="https://github.com/vercel/vercel/assets/9736/e2bb28bf-e3d8-4d89-ad7e-864dd091ec3d">
2023-08-01 15:43:02 +00:00
Trek Glowacki
3d23d1270c [cli] Migrate list command to new structure (#10284)
Before:

<img width="809" alt="CleanShot 2023-07-31 at 09 51 37@2x" src="https://github.com/vercel/vercel/assets/9736/f5ac2ccc-3ff9-4a52-9c93-4a47fe479497">


After:
<img width="836" alt="CleanShot 2023-07-31 at 09 51 48@2x" src="https://github.com/vercel/vercel/assets/9736/48314715-132a-477a-8a66-a5d23c27ee50">
2023-07-31 22:21:11 +00:00
Vercel Release Bot
fde40e731a Version Packages (#10278)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2023-07-31 13:31:22 -05:00
Trek Glowacki
f353527421 [cli] Migrate bisect command to new structure (#10276)
Migrating `bisect` to the command structure created in https://github.com/vercel/vercel/pull/10090. Unsure about tests. This is technically a refactor so existing tests should suffice.

Before:
<img width="534" alt="CleanShot 2023-07-31 at 08 14 29@2x" src="https://github.com/vercel/vercel/assets/9736/8ae53672-9b1f-444a-94d3-296ac0fa8d30">

After:
<img width="674" alt="CleanShot 2023-07-31 at 08 15 02@2x" src="https://github.com/vercel/vercel/assets/9736/0ba12f19-fb34-41f3-84fb-0c1498d7a485">
2023-07-31 16:03:13 +00:00
Trek Glowacki
c1cdfb3e75 [cli] Migrate remove command to new structure (#10268)
Migrating `remove` to the command structure created in #10090. Unsure about tests. This is technically a refactor so existing tests should suffice.
2023-07-31 15:42:27 +00:00
Kiko Beats
fc413707d0 fix: move content-type as dependency (#10274)
It's required by `serverless-functions/helpers.js` causing unhandled
error because it's missing.

```
Error: Cannot find module 'content-type'
Require stack:
- /Users/kikobeats/Library/pnpm/global/5/.pnpm/@vercel+node@2.15.6/node_modules/@vercel/node/dist/serverless-functions/helpers.js
    at Function.Module._resolveFilename (node:internal/modules/cjs/loader:1077:15)
    at Function.Module._resolveFilename.sharedData.moduleResolveFilenameHook.installedValue [as _resolveFilename] (/Users/kikobeats/Library/pnpm/global/5/.pnpm/@cspotcode+source-map-support@0.8.1/node_modules/@cspotcode/source-map-support/source-map-support.js:811:30)
    at Function.Module._load (node:internal/modules/cjs/loader:922:27)
    at Module.require (node:internal/modules/cjs/loader:1143:19)
    at require (node:internal/modules/cjs/helpers:110:18)
    at Object.<anonymous> (/Users/kikobeats/Library/pnpm/global/5/.pnpm/@vercel+node@2.15.6/node_modules/@vercel/node/dist/serverless-functions/helpers.js:6:24)
    at Module._compile (node:internal/modules/cjs/loader:1256:14)
    at Module._extensions..js (node:internal/modules/cjs/loader:1310:10)
    at Object.require.extensions.<computed> [as .js] (/Users/kikobeats/Library/pnpm/global/5/.pnpm/ts-node@10.9.1_@types+node@14.18.33_typescript@4.9.5/node_modules/ts-node/src/index.ts:1608:43)
    at Module.load (node:internal/modules/cjs/loader:1119:32) {
  code: 'MODULE_NOT_FOUND',
  requireStack: [
    '/Users/kikobeats/Library/pnpm/global/5/.pnpm/@vercel+node@2.15.6/node_modules/@vercel/node/dist/serverless-functions/helpers.js'
  ]
}
```
2023-07-31 16:11:18 +02:00
Vercel Release Bot
e842a8870e Version Packages (#10261)
# Releases
## vercel@31.2.0

### Minor Changes

- Add a "Global Options" section to help output
([#10250](https://github.com/vercel/vercel/pull/10250))

### Patch Changes

- Updated dependencies
\[[`d1b0dbe3a`](d1b0dbe3a7),
[`4a8622a10`](4a8622a10d),
[`6469ef1b8`](6469ef1b8c)]:
    -   @vercel/remix-builder@1.9.0
    -   @vercel/next@3.9.3

## @vercel/remix-builder@1.9.0

### Minor Changes

- Install `@vercel/remix-run-dev` at build-time instead of using symlink
([#9784](https://github.com/vercel/vercel/pull/9784))

### Patch Changes

- Update `@remix-run/dev` fork to v1.19.1
([#10246](https://github.com/vercel/vercel/pull/10246))

## @vercel/next@3.9.3

### Patch Changes

- fix dynamic not found pages
([#10262](https://github.com/vercel/vercel/pull/10262))
2023-07-28 15:40:19 -07:00
Nathan Rajlich
d1b0dbe3a7 [remix] Install @vercel/remix-run-dev at build-time instead of using symlink (#9784)
Instead of including the fork `@remix-run/dev` package as a regular dependency of `@vercel/remix-builder`, install it at build-time by modifying the project's `package.json` file. The reasons for this are:

* Avoids deprecation warnings from a few packages that currently exist on the `@remix-run/dev` package when installing Vercel CLI (those warnings already show up in the build logs anyways, so nothing new there).
* Allows us to install a version as close as possible to the version specified in the user's `package.json` (similar to how we do when auto-injecting the `@vercel/remix` package). This will be especially important once Remix v2 is released, which will have breaking changes compared to v1.

**Note:** `@vercel/remix-run-dev` is still a _dev_ dependency, so that we can use TypeScript types from it, as well as, at runtime, we use the version in the Builder's `package.json` to determine the maximum versions of `@vercel/remix-run-dev` and/or `@vercel/remix` which can safely be installed.

Fixes #10027.
Fixes #10222.
2023-07-28 20:49:32 +00:00
Chris Barber
d614709308 [codeowners] Add @trek to codeowners (#10267)
Adding Trek so he join in on the code review fun!
2023-07-28 13:28:45 -04:00
Trek Glowacki
d5b588bc06 [cli] Add a "Global Options" section to help output (#10250)
Followup PR to #10090. Some of our commands duplicate global options into their `args` structure e.g. 2661f56347/packages/cli/src/commands/logs.ts (L25-L26)

 Others commands omit it entirely.

This updates the `--help` output for commands migrated to our new structure (so far, only `deploy`) will show a "Global Options" section:

```
▲ vercel deploy [project-path] [options]

Deploy your project to Vercel. The `deploy` command is the default command for the Vercel CLI, and can be omitted (`vc deploy my-app` equals `vc my-app`).

Options

  --archive                    Compress the deployment code into a file before uploading it
  -b, --build-env <key=value>  Specify environment variables during build-time (e.g. `-b KEY1=value1 -b KEY2=value2`)
  -e, --env <key=value>        Specify environment variables during run-time (e.g. `-e KEY1=value1 -e KEY2=value2`)
  -f, --force                  Force a new deployment even if nothing has changed
  -m, --meta <key=value>       Specify metadata for the deployment (e.g. `-m KEY1=value1 -m KEY2=value2`)
  --no-wait                    Don't wait for the deployment to finish
  --prebuilt                   Use in combination with `vc build`. Deploy an existing build
  --prod                       Create a production deployment
  -p, --public                 Deployment is public (`/_src`) is exposed)
  --regions                    Set default regions to enable the deployment on
  --with-cache                 Retain build cache when using "--force"
  -y, --yes                    Use default options to skip all prompts

Global Options

  --cwd <DIR>                Sets the current working directory for a single run of a command
  -d, --debug                Debug mode (default off)
  -Q, --global-config <DIR>  Path to the global ${'`.vercel`'} directory
  -h, --help                 Output usage information
  -A, --local-config <FILE>  Path to the local `vercel.json` file
  --no-color                 No color mode (default off)
  -S, --scope                Set a custom scope
  -t, --token <TOKEN>        Login token
  -v, --version              Output the version number
```

As commands are migrated to this new structure, they'll gain this output automatically.
2023-07-27 20:48:07 +00:00
Zack Tanner
4a8622a10d [next] fix dynamic not found pages (#10262)
This updates `getServerlessPages` to consider `/_not-found` pages (which might opt into dynamic rendering)

- Corresponding Next.js changes https://github.com/vercel/next.js/pull/53231
- [slack x-ref](https://vercel.slack.com/archives/C03S8ED1DKM/p1683763412272429)
2023-07-27 16:18:31 +00:00
Vercel Release Bot
6469ef1b8c [remix] Update @remix-run/dev to v1.19.1 (#10246)
This auto-generated PR updates `@remix-run/dev` to version 1.19.1.
2023-07-26 22:22:21 +00:00
Nathan Rajlich
b8d42a521b [examples] Remove "author" field from hydrogen template (#10253) 2023-07-26 14:48:07 -07:00
Vercel Release Bot
b1e8c9cb6e Version Packages (#10260)
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@31.1.1

### Patch Changes

- Updated dependencies
\[[`7c30b13cc`](7c30b13ccb)]:
    -   @vercel/next@3.9.2

## @vercel/next@3.9.2

### Patch Changes

- Fix pages/404 gsp + i18n case
([#10258](https://github.com/vercel/vercel/pull/10258))

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2023-07-26 14:50:22 -04:00
JJ Kasper
7c30b13ccb [next] Fix pages/404 gsp + i18n case (#10258)
This ensures we detect the locale prefixed 404 when it uses `getStaticProps` correctly. 

- x-ref: [slack thread](https://vercel.slack.com/archives/C05JTC58AQJ)
- Closes https://github.com/vercel/vercel/pull/10248
2023-07-26 18:41:25 +00:00
Steven
e5e757de34 [tests] fix vary header in next tests (#10249)
Since we run tests against canary, the latest [v13.4.13-canary.0](https://github.com/vercel/next.js/releases/tag/v13.4.13-canary.0) changed the `vary` header in https://github.com/vercel/next.js/pull/52746 so we need to update the tests to match.
2023-07-26 16:52:13 +00:00
Vercel Release Bot
2661f56347 Version Packages (#10227)
# Releases

## vercel@31.1.0

### Minor Changes

- Add 'Environment' column to 'vc list' with new '--environment' filter
and pipe URLs to stdout
([#10239](https://github.com/vercel/vercel/pull/10239))

### Patch Changes

- Update `proxy-agent` to v6.3.0
([#10226](https://github.com/vercel/vercel/pull/10226))

- Use `getNodeBinPaths()` in `vc dev`
([#10225](https://github.com/vercel/vercel/pull/10225))

- Updated dependencies
\[[`b1c14cde0`](b1c14cde03),
[`ce4633fe4`](ce4633fe4d)]:
    -   @vercel/next@3.9.1
    -   @vercel/static-build@1.3.42

## @vercel/frameworks@1.5.0

### Minor Changes

- Add `ignorePackageJsonScript` configuration for Framework command
settings to ignore the `package.json` script.
([#10228](https://github.com/vercel/vercel/pull/10228))

Enable this mode for Storybook's `buildCommand`, since it should not
invoke the "build" script, which is most likely designated for the
frontend app build.

## @vercel/fs-detectors@4.1.1

### Patch Changes

- Updated dependencies
\[[`ce4633fe4`](ce4633fe4d)]:
    -   @vercel/frameworks@1.5.0

## @vercel/next@3.9.1

### Patch Changes

- Fix pages and app router i18n handling
([#10243](https://github.com/vercel/vercel/pull/10243))

## @vercel/static-build@1.3.42

### Patch Changes

- Add `ignorePackageJsonScript` configuration for Framework command
settings to ignore the `package.json` script.
([#10228](https://github.com/vercel/vercel/pull/10228))

Enable this mode for Storybook's `buildCommand`, since it should not
invoke the "build" script, which is most likely designated for the
frontend app build.
2023-07-24 18:02:38 -07:00
JJ Kasper
b1c14cde03 [next] Fix pages and app router i18n handling (#10243)
Ensures app router paths are handled properly when i18n is configured for pages directory. 

x-ref: [slack thread](https://vercel.slack.com/archives/C03KAR5DCKC/p1687565759424049)
x-ref: [slack thread](https://vercel.slack.com/archives/C03KAR5DCKC/p1687565759424049)
x-ref: [slack thread](https://vercel.slack.com/archives/C03KAR5DCKC/p1672781549860349?thread_ts=1671393204.073649&cid=C03KAR5DCKC)

[VCCLI-780](https://linear.app/vercel/issue/VCCLI-780/add-404i18n-invariant-case-in-nextjs-builder)
2023-07-24 23:26:25 +00:00
Chris Barber
8dd6d021df [cli] Add "Environment" column to vc list (#10239)
Also adds a new `--environment=[preview|production]` filter and ability to pipe deployment URLs to `stdout`!

<img width="380" alt="image" src="https://github.com/vercel/vercel/assets/97262/20de0caa-2d63-4112-8213-cc15d23295c7">

```
vc list --environment preview
```

```
vc list --environment production
```

```
vc list --environment preview > preview_deployments.txt
```
2023-07-24 20:02:25 +00:00
Vercel Release Bot
88ec6e69d6 [examples][tests] Upgrade Next.js to version 13.4.12 (#10240)
This auto-generated PR updates 3 packages to Next.js version 13.4.12
2023-07-23 21:51:14 +00:00
Vercel Release Bot
a6f2e7b136 [tests] Upgrade Turbo to version 1.10.9 (#10241)
This auto-generated PR updates Turbo to version 1.10.9
2023-07-23 21:30:01 +00:00
Vercel Release Bot
e906365909 [examples][tests] Upgrade Next.js to version 13.4.11 (#10233)
This auto-generated PR updates 3 packages to Next.js version 13.4.11
2023-07-21 19:01:10 +00:00
Nathan Rajlich
7b01a07394 [cli] Use getNodeBinPaths() in vc dev (#10225)
This allows for the "dev command" of a Project to work better in monorepos, where the dev server might live up the node_modules hierarchy within the repo.
2023-07-19 22:19:43 +00:00
Nathan Rajlich
ce4633fe4d [frameworks][static-build] Add ignorePackageJsonScript configuration for Framework command settings (#10228)
When this property is set to `true`, then the corresponding `package.json` script will not be invoked, allowing for the default setting value will be executed.

This is enabled for Storybook's `buildCommand`, since we do not want the "build" script to be invoked, since that would belong to the frontend application's build instead of Storybook's.
2023-07-19 20:23:51 +00:00
Nathan Rajlich
fdf86fda03 [cli] Update proxy-agent to v6.3.0 (#10226)
This version includes a refactor for proxies specified via PAC files such that it no longer uses the deprecated `vm2` module.

See https://github.com/TooTallNate/proxy-agents/issues/218.
2023-07-18 20:29:37 +00:00
Vercel Release Bot
56178e6a46 Version Packages (#10213)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2023-07-18 14:02:19 -05:00
Nathan Rajlich
5439d7c0c9 [remix] Create ensured dependency symlink at the start directory, instead of root of repo (#10224)
When the `ensureResolvable()` function runs in a monorepo, the symlink should be created in the `node_modules` directory within the app subdirectory, rather than the root of the repository. Fixes the following issue:

![image](https://github.com/vercel/vercel/assets/71256/c0171533-881a-425e-85b9-82318ef19032)
2023-07-18 18:27:19 +00:00
Zack Tanner
c670e51712 [next] fix 404 page in edge runtime (#10223)
Fixes Next builder to handle edge runtime on 404 pages

[Related Next.js PR](https://github.com/vercel/next.js/pull/52754)
[slack x-ref](https://vercel.slack.com/archives/C03S8ED1DKM/p1689312606770609)
2023-07-17 19:48:45 +00:00
Kiko Beats
b56639b624 [node] fix: runs edge user code inside IIFE (#10220)
In this way, `self` is isolated and modify it doesn't break some Edge Runtime internals

Originally reported by @jawj at https://github.com/jawj/neon-vercel-zapatos-minimal-crash
2023-07-17 16:45:05 +00:00
Kiko Beats
12bbae098c [codeowners] @vercel/edge-compute → @vercel/compute (#10219) 2023-07-17 14:33:25 +00:00
Nathan Rajlich
9969f0ba18 [cli] Use detectFrameworks() during vc link --repo (#10203)
Allows for multiple frameworks to be detectable within the same root directory. This is basically specifically for Storybook.

<img width="700" alt="Screenshot 2023-07-12 at 6 04 37 PM" src="https://github.com/vercel/vercel/assets/71256/5a240f1e-b000-42ad-b36f-3c151d3cd449">
2023-07-14 18:11:49 +00:00
Vercel Release Bot
24e1e3c3be [examples][tests] Upgrade Next.js to version 13.4.10 (#10215)
This auto-generated PR updates 3 packages to Next.js version 13.4.10
2023-07-14 16:27:44 +00:00
Steven
b61674cb2d [tests] Fix next update cron job (#10214)
This was supposed to be fixed in https://github.com/vercel/vercel/pull/10173 but the file was created after git commit.

This PR makes sure the file is created before git commit.
2023-07-14 16:07:26 +00:00
Florentin / 珞辰
cae60155f3 [next] support maxDuration in Next.js deployments (#10069)
Follow up PR to
8703c55f9f
which reads the newly created function config manifest and patches in
the options for resource creation.

---------

Co-authored-by: Steven <steven@ceriously.com>
2023-07-14 15:27:06 +02:00
Vercel Release Bot
a91bde5287 Version Packages (#10192)
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/fs-detectors@4.1.0

### Minor Changes

- Add `detectFrameworks()` function
([#10195](https://github.com/vercel/vercel/pull/10195))

## @vercel/build-utils@6.8.2

### Patch Changes

- Push back `nodejs16.x` discontinue date to `2024-02-06`
([#10209](https://github.com/vercel/vercel/pull/10209))

## vercel@31.0.3

### Patch Changes

- Fix redeploy target to be undefined when null
([#10201](https://github.com/vercel/vercel/pull/10201))

- Respect forbidden API responses
([#10178](https://github.com/vercel/vercel/pull/10178))

- Update `supports-hyperlinks` to v3
([#10208](https://github.com/vercel/vercel/pull/10208))

- Updated dependencies
\[[`0750517af`](0750517af9)]:
    -   @vercel/build-utils@6.8.2
    -   @vercel/static-build@1.3.40
    -   @vercel/node@2.15.5
    -   @vercel/remix-builder@1.8.17

## @vercel/client@12.6.5

### Patch Changes

- Updated dependencies
\[[`0750517af`](0750517af9)]:
    -   @vercel/build-utils@6.8.2

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

### Patch Changes

- Updated dependencies
\[[`0750517af`](0750517af9)]:
    -   @vercel/build-utils@6.8.2
    -   @vercel/node@2.15.5

## @vercel/node@2.15.5

### Patch Changes

- Updated dependencies
\[[`0750517af`](0750517af9)]:
    -   @vercel/build-utils@6.8.2

## @vercel/remix-builder@1.8.17

### Patch Changes

- Updated dependencies
\[[`0750517af`](0750517af9)]:
    -   @vercel/build-utils@6.8.2

## @vercel/static-build@1.3.40

### Patch Changes

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

## @vercel-internals/types@1.0.5

### Patch Changes

- Updated dependencies
\[[`0750517af`](0750517af9)]:
    -   @vercel/build-utils@6.8.2

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2023-07-13 19:17:13 -04:00
Steven
0750517af9 [build-utils] Push back nodejs16.x discontinue date to 2024-02-06 (#10209)
The discontinue date for `nodejs16.x` needs to be pushed back until we can add support for `nodejs20.x`.

Currently `nodejs18.x` can cause problems with native packages that depend on a specific version of glibc so the only solution for that use case is to stay on `nodejs16.x` at this time.


### Related Issues
- https://github.com/orgs/vercel/discussions/3061
- https://github.com/orgs/vercel/discussions/2867
- https://github.com/orgs/vercel/discussions/2258
- https://github.com/orgs/vercel/discussions/2630
- https://github.com/orgs/vercel/discussions/2105
- https://github.com/orgs/vercel/discussions/2596
- https://github.com/orgs/vercel/discussions/2730
2023-07-13 22:47:03 +00:00
Steven
70f6782954 [tests] Add lockfile to 51-puppeteer-build test (#10210)
This test was failing so I'm adding a lockfile to prevent this build error:

```
Running "yarn run build"
--
16:40:10.239 | yarn run v1.22.17
16:40:10.260 | $ node build.js
16:40:40.630 | node:internal/process/promises:288
16:40:40.630 | triggerUncaughtException(err, true /* fromPromise */);
16:40:40.630 | ^
16:40:40.630 |  
16:40:40.630 | TimeoutError: Navigation timeout of 30000 ms exceeded
16:40:40.631 | at /vercel/path1/node_modules/puppeteer/lib/LifecycleWatcher.js:142:21
16:40:40.631 | -- ASYNC --
16:40:40.631 | at Frame.<anonymous> (/vercel/path1/node_modules/puppeteer/lib/helper.js:111:15)
16:40:40.631 | at Page.goto (/vercel/path1/node_modules/puppeteer/lib/Page.js:672:49)
16:40:40.631 | at Page.<anonymous> (/vercel/path1/node_modules/puppeteer/lib/helper.js:112:23)
16:40:40.631 | at /vercel/path1/build.js:10:14
16:40:40.631 | at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
```
2023-07-13 21:29:51 +00:00
Nathan Rajlich
5f1e37ee16 Update supports-hyperlinks to v3 (#10208)
Enables proper detection for VSCode's terminal emulator, and allows us
to remove the in-house types since v3 includes its own types.
2023-07-13 12:24:30 -07:00
Steven
680d666fdc [tests] Add huozhi to cron job for Next.js (#10205)
This adds Jiachi to the default reviewer list for the Next.js update cron job.
2023-07-13 13:48:27 +00:00
Chris Barber
06a5dccfed [cli] Fix redeploy target to be undefined when null (#10201) 2023-07-12 16:26:33 -05:00
Nathan Rajlich
f85df894c0 [fs-detectors] Add detectFrameworks() (#10195)
Similar to `detectFramework()`, but this version returns all detected frameworks at the given path. It also returns the full `Framework` objects instead of just the slug.

This will be useful for detecting when multiple Projects exist at the same root directory (i.e. Storybook).

**Note:** I rearranged some tests, so probably review with [whitespace changes hidden](https://github.com/vercel/vercel/pull/10195/files?diff=split&w=1).
2023-07-11 20:08:55 +00:00
balazs4
e9ec779f1c [rbac]s show forbidden error in case of missing permission (#10178)
fixes
[iam-941](https://linear.app/vercel/issue/IAM-941/cli-improve-handling-of-insufficient-permissions-no-access)
2023-07-11 12:32:43 +02:00
Vercel Release Bot
4333d1e6b2 [examples][tests] Upgrade Next.js to version 13.4.9 (#10183)
This auto-generated PR updates 3 packages to Next.js version 13.4.9
2023-07-11 07:51:06 +00:00
Vercel Release Bot
493a31091d Version Packages (#10158)
## @vercel/build-utils@6.8.1

### Patch Changes

- Revert "[build-utils] Allow file-ref sema to be controlled through env
flag" ([#10167](https://github.com/vercel/vercel/pull/10167))

## vercel@31.0.2

### Patch Changes

- Allow additional project settings in `createProject()`
([#10172](https://github.com/vercel/vercel/pull/10172))

- Run local Project detection during `vc link --repo`.
([#10094](https://github.com/vercel/vercel/pull/10094))
This allows for creation of new Projects that do not yet exist under the
selected scope.

- Redeploy command no longer redeploys preview deployments to production
([#10186](https://github.com/vercel/vercel/pull/10186))

- Added trailing new line at end of help output
([#10170](https://github.com/vercel/vercel/pull/10170))

- Create new help output and arg parsing for deploy command
([#10090](https://github.com/vercel/vercel/pull/10090))

- [cli] Remove `preinstall` script
([#10157](https://github.com/vercel/vercel/pull/10157))

- Updated dependencies
\[[`7021279b2`](7021279b28),
[`5e5332fbc`](5e5332fbc9),
[`027bce00b`](027bce00b3)]:
    -   @vercel/build-utils@6.8.1
    -   @vercel/node@2.15.4
    -   @vercel/remix-builder@1.8.16
    -   @vercel/static-build@1.3.39

## @vercel/client@12.6.4

### Patch Changes

- Updated dependencies
\[[`7021279b2`](7021279b28)]:
    -   @vercel/build-utils@6.8.1

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

### Patch Changes

- Updated dependencies
\[[`7021279b2`](7021279b28),
[`5e5332fbc`](5e5332fbc9)]:
    -   @vercel/build-utils@6.8.1
    -   @vercel/node@2.15.4

## @vercel/node@2.15.4

### Patch Changes

- [node] fix decompress mismatching
([#10184](https://github.com/vercel/vercel/pull/10184))

- Updated dependencies
\[[`7021279b2`](7021279b28)]:
    -   @vercel/build-utils@6.8.1

## @vercel/remix-builder@1.8.16

### Patch Changes

- Update `@remix-run/dev` fork to v1.18.1
([#10180](https://github.com/vercel/vercel/pull/10180))

- Updated dependencies
\[[`7021279b2`](7021279b28)]:
    -   @vercel/build-utils@6.8.1

## @vercel/static-build@1.3.39

### Patch Changes

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

## @vercel-internals/constants@1.0.4

### Patch Changes

- Create new help output and arg parsing for deploy command
([#10090](https://github.com/vercel/vercel/pull/10090))

## @vercel-internals/types@1.0.4

### Patch Changes

- Updated dependencies
\[[`7021279b2`](7021279b28),
[`718bbd365`](718bbd365a)]:
    -   @vercel/build-utils@6.8.1
    -   @vercel-internals/constants@1.0.4

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2023-07-10 15:48:32 -07:00
Nathan Rajlich
8d7206f5b6 [cli] Run local Project detection during vc link --repo (#10094)
Run local Project detection during `vc link --repo`. This allows for creation of new Projects that do not yet exist under the selected scope.
2023-07-10 22:42:13 +00:00
Sean Massa
4bf2ca55ff Revert "Add Changesets "ignore" configuration" (#10189)
Reverts vercel/vercel#10181

This seems to be breaking releases of packages that depend on ignored packages. Example: https://github.com/vercel/vercel/actions/runs/5489491313/jobs/10003713249
2023-07-10 02:17:06 +00:00
Kiko Beats
5e5332fbc9 [node] fix decompress mismatching (#10184)
This PR disabled `node-fetch` default compression handling when the
response is not streamed.

The default behavior in node-fetch is to handle the compression. That's
great if you interact with node-fetch as user, but bad if you use it as
intermediate proxy server, since it's creating a mismatching related
with the body format and length expectations.

Instead, we disable compression, get the buffered response and compress
it again if needed.

---------

Co-authored-by: Sean Massa <EndangeredMassa@gmail.com>
2023-07-07 12:33:05 -06:00
Ethan Arrowood
281ec776a5 Fix redeploy target (#10186)
This PR fixes customer issue
https://github.com/vercel/customer-issues/issues/1310 which identified
an issue with the `redeploy` command.

`redeploy` should not default the `target` to `'production'` as an
`undefined` target is meant to mean `'preview'`.
2023-07-07 11:50:06 -06:00
Nathan Rajlich
ee8f9292b4 Add Changesets "ignore" configuration (#10181)
These packages already don't get published since they have `private: true`, but adding to the "ignore" configuration also removes them from the selection prompt when executing the `changeset` CLI command.
2023-07-05 22:07:15 +00:00
Vercel Release Bot
027bce00b3 [remix] Update @remix-run/dev to v1.18.1 (#10180)
This auto-generated PR updates `@remix-run/dev` to version 1.18.1.
2023-07-05 21:23:48 +00:00
Vercel Release Bot
ca1f41200a [tests] Upgrade Turbo to version 1.10.7 (#10164)
This auto-generated PR updates Turbo to version 1.10.7
2023-07-05 20:46:59 +00:00
Nathan Rajlich
834b3e652b [cli] Allow additional project settings in createProject() (#10172)
Previously, `createProject()` only allowed a string `name` to be provided, and another API call would need to be done after creation with `updateProject()`. This should not be necessary since the `POST` endpoint can accept additional properties already.
2023-07-03 21:42:00 +00:00
Steven
5c6941d18c [tests] Fix update-next.js cron job to include changeset (#10173)
The cron job script only updates the Next.js example so it should have an empty changeset
2023-07-03 21:03:42 +00:00
Vercel Release Bot
761ede2482 [examples][tests] Upgrade Next.js to version 13.4.8 (#10171)
This auto-generated PR updates 3 packages to Next.js version 13.4.8
2023-07-03 20:22:59 +00:00
Chris Barber
1d01703dc3 [cli] Added line return at end of help output (#10170)
I noticed the help output doesn't render a line return at the end.

<img width="290" alt="image" src="https://github.com/vercel/vercel/assets/97262/8aea6be0-9620-4445-b05d-62b838cefb07">
2023-07-03 16:46:29 +00:00
Andrew Healey
7021279b28 Revert "[build-utils] Allow file-ref sema to be controlled through env flag" (#10167)
Reverts vercel/vercel#8681

We ran an experiment here a while ago but it had flaky results.

This PR cleans up the experiment code, and reverts to the previous behavior.
2023-07-03 15:42:16 +00:00
Ethan Arrowood
28acf50bdf Removes a leftover console.log in the new help method (#10162)
Removes a leftover `console.log` in the new `help` method. Whoops!
2023-06-30 15:50:22 +00:00
Ethan Arrowood
718bbd365a Improve help output UX and DX (#10090)
This PR adds a new utility for generating `help` output for CLI
commands. Backed by a JSON data structure that represents the commands,
this utility uses deterministic functions for generating the different
output sections (such as `options` and `examples`). This change will let
contributor easily add/modify command details. The data structure is
also used by the CLI to generate the args parsing structure for the
`deploy` command. Optimistically, this same data structure can be used
to generate documentation output for `front` such that the online docs
will match the `help` output.

---------

Co-authored-by: Sean Massa <EndangeredMassa@gmail.com>
2023-06-29 14:08:17 -06:00
Steven
7e791ee080 [cli] Remove preinstall script (#10157)
This will solve two of the warnings from [Socket.dev](https://socket.dev/npm/package/vercel):

<img width="1126" alt="image" src="https://github.com/vercel/vercel/assets/229881/c890c973-4f5a-44d9-9b96-2580a1e40ed0">

This preinstall script isn't necessary to run Vercel CLI, its only used to improve the error message in two cases:

- The system has `node` version prior to 14.x (3 years ago)
- The system still has `now` installed but should be using `vercel` (last shipped 3 years ago)

The usage of `preinstall` scripts have gone out of style due to security audits so we should remove this script now.
2023-06-28 22:30:49 +00:00
Vercel Release Bot
13769106cb Version Packages (#10156)
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@31.0.1

### Patch Changes

- Updated dependencies
\[[`aa734efc6`](aa734efc6c)]:
    -   @vercel/next@3.8.8

## @vercel/next@3.8.8

### Patch Changes

- [next] Ensure RSC paths handle basePath
([#10155](https://github.com/vercel/vercel/pull/10155))
2023-06-28 14:49:14 -07:00
JJ Kasper
aa734efc6c [next] Ensure RSC paths handle basePath (#10155)
This ensures we properly create the `.rsc` variant of outputs when `basePath` is configured and adds a regression test for this. 

x-ref: [slack thread](https://vercel.slack.com/archives/C03S8ED1DKM/p1687944744404289)
Fixes: https://github.com/vercel/next.js/issues/48305
2023-06-28 21:00:43 +00:00
Vercel Release Bot
acc10e47c7 Version Packages (#10123)
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@31.0.0

### Major Changes

- Update `vc dev` redirect response to match production behavior
([#10143](https://github.com/vercel/vercel/pull/10143))

### Patch Changes

- require `--yes` to promote preview deployment
([#10135](https://github.com/vercel/vercel/pull/10135))

- [cli] Optimize write build result for vc build
([#10154](https://github.com/vercel/vercel/pull/10154))

- Only show relevant Project matches in Project selector
([#10114](https://github.com/vercel/vercel/pull/10114))

- [cli] Fix error message when token is invalid
([#10131](https://github.com/vercel/vercel/pull/10131))

- Updated dependencies
\[[`e4895d979`](e4895d979b),
[`346892210`](3468922108),
[`346892210`](3468922108),
[`a6de052ed`](a6de052ed2)]:
    -   @vercel/next@3.8.7
    -   @vercel/static-build@1.3.38
    -   @vercel/build-utils@6.8.0
    -   @vercel/remix-builder@1.8.15
    -   @vercel/node@2.15.3

## @vercel/build-utils@6.8.0

### Minor Changes

- Add `getNodeBinPaths()` and `traverseUpDirectories()` functions
([#10150](https://github.com/vercel/vercel/pull/10150))

## @vercel/client@12.6.3

### Patch Changes

- Updated dependencies
\[[`346892210`](3468922108)]:
    -   @vercel/build-utils@6.8.0

## @vercel/frameworks@1.4.3

### Patch Changes

- [frameworks] Update `saber.land` to `saber.egoist.dev`
([#10148](https://github.com/vercel/vercel/pull/10148))

## @vercel/fs-detectors@4.0.1

### Patch Changes

- Resolve symlinks in `LocalFileSystemDetector#readdir()`
([#10126](https://github.com/vercel/vercel/pull/10126))

- Updated dependencies
\[[`0867f11a6`](0867f11a6a)]:
    -   @vercel/frameworks@1.4.3

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

### Patch Changes

- Updated dependencies
\[[`346892210`](3468922108)]:
    -   @vercel/build-utils@6.8.0
    -   @vercel/node@2.15.3

## @vercel/next@3.8.7

### Patch Changes

- [next] Update payload flag
([#10147](https://github.com/vercel/vercel/pull/10147))

- Use `getNodeBinPaths()` function to improve monorepo support
([#10150](https://github.com/vercel/vercel/pull/10150))

## @vercel/node@2.15.3

### Patch Changes

- Updated dependencies
\[[`346892210`](3468922108)]:
    -   @vercel/build-utils@6.8.0

## @vercel/remix-builder@1.8.15

### Patch Changes

- Update `@remix-run/dev` fork to v1.18.0
([#10146](https://github.com/vercel/vercel/pull/10146))

- Updated dependencies
\[[`346892210`](3468922108)]:
    -   @vercel/build-utils@6.8.0

## @vercel/static-build@1.3.38

### Patch Changes

- Use `getNodeBinPaths()` function to improve monorepo support
([#10150](https://github.com/vercel/vercel/pull/10150))

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

## @vercel-internals/constants@1.0.3

### Patch Changes

- Updated dependencies
\[[`346892210`](3468922108)]:
    -   @vercel/build-utils@6.8.0

## @vercel-internals/types@1.0.3

### Patch Changes

- Updated dependencies
\[[`346892210`](3468922108)]:
    -   @vercel/build-utils@6.8.0
    -   @vercel-internals/constants@1.0.3
2023-06-28 12:29:31 -07:00
JJ Kasper
6a0e1e0b66 [cli] Optimize write build result for vc build (#10154)
As noticed this is copying all fallback files from output even if they are already on the disk in the build container which should be able to be considered a "pure" environment. This copying can cause disk space/perf issues when there are massive amounts of fallback files generated e.g. 8000 outputs at 200KB/output is already 1.6GB of copying needing to be done that could just be a move operation. 


x-ref: [slack thread](https://vercel.slack.com/archives/C03F2CMNGKG/p1687969734598509?thread_ts=1687963253.178369&cid=C03F2CMNGKG)
x-ref: [slack thread](https://vercel.slack.com/archives/C02SM2K2UM9/p1687970267370669)
x-ref: [slack thread](https://vercel.slack.com/archives/C03DQ3QFV7C/p1687338157228459)
2023-06-28 19:07:52 +00:00
Nathan Rajlich
3468922108 [build-utils] Add getNodeBinPaths() function (#10150)
The `getNodeBinPath()` function is problematic because it assumes that commands are installed in the `node_modules` directory alongside the detected lockfile. This works fine the majority of the time, but ends up not being the case when using a monorepo that uses a package manager in "linked" mode (i.e. pnpm by default).

Consider the following:

```
.
├── pnpm-lock.yaml
├── node_modules
├── blog
│   ├── node_modules
│   │   ├── hexo -> .pnpm/hexo@3.9.0/node_modules/hexo
```

In this setup, adding the root-level `node_modules/.bin` would not make the `hexo` command be visible in the `$PATH`.

To solve this issue, the new `getNodeBinPaths()` function returns an array of all directories up to the specified `root`, which can then be placed into the `$PATH`. It's also more efficient (synchronous) since it does not need to scan for a lockfile anymore (the `root` needs to be specified explicitly).

The new function is being used in `@vercel/next` and `@vercel/static-build`.

The `traverseUpDirectories()` function from CLI was moved to `build-utils` to implement this function. Consequently, that makes the implementations of `walkParentDirs()` and `walkParentDirsMulti()` simpler, since it's using this generator now.
2023-06-28 01:53:34 +00:00
JJ Kasper
e4895d979b [next] Update payload flag (#10147)
x-ref: [slack thread](https://vercel.slack.com/archives/C05E6RJTPJS/p1687898295532579)
2023-06-27 23:34:21 +00:00
Steven
733ff5ed85 [tests] Add x-vercel-id to test error message (#10149)
Including the `x-vercel-id` header when a e2e test fails will help us
debug issues
2023-06-27 19:05:55 -04:00
Steven
0867f11a6a [frameworks] Update saber.land to saber.egoist.dev (#10148)
The website url for Saber changed

x-ref:
[slack](https://vercel.slack.com/archives/C01RUTYJYHW/p1687900208904799)
2023-06-27 15:23:27 -07:00
Vercel Release Bot
a6de052ed2 [remix] Update @remix-run/dev to v1.18.0 (#10146)
This auto-generated PR updates `@remix-run/dev` to version 1.18.0.
2023-06-27 17:32:46 +00:00
Michaël De Boey
f5d48ec3bc [examples] Update remix template (#9846)
Bring in line with latest template
2023-06-26 16:52:24 -07:00
Sean Massa
6ec1180798 require --yes flag to promote preview deployment (#10135)
In order to promote a preview deployment, we want to make sure the user knows that this is not typical. Now the `--yes` flag is required to make this work.
2023-06-26 21:43:48 +00:00
Nathan Rajlich
36db62a491 Delete .changeset/beige-carrots-begin.md (#10144)
We don't want changesets to try and create a release / changelog for the
`examples` dir.
2023-06-26 13:12:21 -07:00
Vercel Release Bot
f9266e069f [tests] Upgrade Turbo to version 1.10.6 (#10140)
This auto-generated PR updates Turbo to version 1.10.6
2023-06-26 20:10:26 +00:00
Nathan Rajlich
8163a153df [cli] Respond with "Redirecting..." to match production behavior (#10143)
Production was recently changed to make redirect responses return the text "Redirecting..." without the destination nor status code in the text.

This caused some tests to start failing, so update `vc dev` to match this new behavior and update the relevant tests.
2023-06-26 19:41:35 +00:00
Steven
c5e6bd1a7a Delete lerna.json (#10133)
We no longer use lerna since switching to changesets so we can delete `lerna.json`

- See https://github.com/vercel/vercel/pull/9914
2023-06-23 16:14:23 +00:00
Steven
c2f1bebd1f [cli] Fix error message when token is invalid (#10131)
This changes the error when a token is invalid or expired from

```
Error: Could not retrieve Project Settings. To link your Project, remove the `.vercel` directory and deploy again.
Learn More: https://vercel.link/cannot-load-project-settings
```

to a better error

```
Error: The specified token is not valid. Use `vercel login` to generate a new token.
```

- This could be considered a follow up to
https://github.com/vercel/vercel/pull/7794
2023-06-23 11:18:58 -04:00
Nathan Rajlich
3138415533 [fs-detectors] Resolve symlinks in LocalFileSystemDetector#readdir() (#10126)
`LocalFileSystemDetector#readdir()` was throwing an error when a symlink was encountered, due to `fs.readdir()` `withFileTypes: true` option performing an lstat instead of a stat operation.

So re-implement the `readdir()` logic to use `fs.stat()` so that the symlink is resolved (only "dir" and "file" types are expected in the result).
2023-06-22 18:19:28 +00:00
Vercel Release Bot
8f6813bb63 [examples][tests] Upgrade Next.js to version 13.4.7 (#10125)
This auto-generated PR updates 3 packages to Next.js version 13.4.7
2023-06-22 00:36:23 +00:00
Nathan Rajlich
734499fc03 [cli] Only show relevant Project matches in Project selector (#10114)
When showing the Project selector and there's more than one Project matched given the current cwd, only show the matched Projects instead of all Projects listed in the `repo.json` file.
2023-06-21 17:22:21 +00:00
Nathan Rajlich
f06988d914 [cli] Add debug logging for repo link root lookup (#10112)
Adds some logging during the root directory resolving logic to help debug any cases where that lookup isn't behaving correctly.
2023-06-20 18:09:40 +00:00
Vercel Release Bot
71ac16220b Version Packages (#10107)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2023-06-20 11:42:04 -05:00
Kiko Beats
8b3a4146af [node][edge][cli] upgrade Edge Runtime (#10051)
We avoided to use `undici.WebSocket` because @jawj found a bug in the
implementation.

The [fix was merged](https://github.com/nodejs/undici/pull/2106) in
[undici@5.22.1](https://github.com/nodejs/undici/releases/tag/v5.22.1),
so we can be back to use it.

The latest version of Edge Runtime exposes `undici.WebSocket` compatible
with node.js 14, 16 & 18

---------

Co-authored-by: Sean Massa <endangeredmassa@gmail.com>
2023-06-20 11:06:43 +02:00
Nathan Rajlich
cfea31e6cf Remove .changeset/curvy-stingrays-cheat.md (#10115) 2023-06-16 15:45:52 -07:00
Vercel Release Bot
7090fec110 [examples][tests] Upgrade Next.js to version 13.4.6 (#10111)
This auto-generated PR updates 3 packages to Next.js version 13.4.6
2023-06-16 22:21:41 +00:00
Sean Massa
bc7c80fb9b [cli] do not force auto-assign value on deployments (#10110)
When `vc deploy` happens, it was forcing a `true`/`false` value for `autoAssignCustomDomains`. This overwrites the project setting in some cases, which is not what we want.

If a CLI command or API call explicitly wants to force `autoAssignCustomDomains` on or off, it can. Otherwise, the project setting value should be used.

This PR only sets `autoAssignCustomDomains` to `false` when the `--skip-domain` flag is passed. Otherwise, the value is `undefined` so that the project setting can take effect.
2023-06-15 22:19:34 +00:00
Sean Massa
91406abdb0 [node] add tests to getBodyParser helper (#10109) 2023-06-15 12:31:47 -05:00
Nathan Rajlich
a5af8381ce [fs-detectors] Add test-e2e script (#10103)
Seems we have not been running the `fs-detectors` integration tests.
2023-06-14 19:20:20 +00:00
Nathan Rajlich
2230ea6cc1 [remix] Link to https://vercel.com/help (#10106) 2023-06-14 18:53:14 +00:00
Vercel Release Bot
f057f0421b Version Packages (#10081) 2023-06-14 10:47:10 -07:00
Nathan Rajlich
42c0b32a8d [fs-detectors] Use LocalFileSystemDetector instead of FixtureFilesystem (#10100)
The code for these two are almost identical, so consolidate into one codebase.

Also adjusts the `pnpm test` script to allow for specifying a file name to be executed, instead of running all tests.
2023-06-13 23:30:00 +00:00
Chris Barber
d61a1a7988 [cli] Fix team validation bug where you are apart of a team (#10092)
When consolidating the deployment team validation, a bug was introduced where the command would error if the user was currently using a team. The logic was meant to copy the logic in `getDeploymentByIdOrURL()`: https://github.com/vercel/vercel/blob/main/packages/cli/src/util/deploy/get-deployment-by-id-or-url.ts#L80.
2023-06-12 16:32:01 +00:00
Vercel Release Bot
c438bbb362 [examples][tests] Upgrade Next.js to version 13.4.5 (#10097)
This auto-generated PR updates 3 packages to Next.js version 13.4.5
2023-06-12 15:02:47 +00:00
Vercel Release Bot
cb5eef0eb5 [tests] Upgrade Turbo to version 1.10.3 (#10096)
This auto-generated PR updates Turbo to version 1.10.3
2023-06-11 07:44:11 +00:00
Nathan Rajlich
79dee367cf [cli] Remove findProjectFromPath() (#10093)
Superceded by the `findProjectsFromPath()` function which may return multiple matches, and it was only being used in tests.

The relevant tests have been updated to use the multiple matches version instead, and updated to include the case where multiple matches are returned.

__Note:__ No changeset for this one since it's an internal function being removed, and doesn't need to be referenced in the public changelog.
2023-06-09 22:59:17 +00:00
Nathan Rajlich
dea58dea7e [cli] Add support for vc deploy --prebuilt command with repo link (#10083)
When repo linked, `vc deploy --prebuilt` will change working directory to the Project root directory, instead of the repo root, so that the project's local `.vercel/output` directory is what gets uploaded + deployed.
2023-06-09 09:54:17 +00:00
Steven
fecebfa7fa [cli] vc env pull should add .env*.local to .gitignore (#10085)
This is a follow up to PR #9892 which changed the default to `.env.local`.

Now that we know local files should never be committed to git, we can automatically add `.env*.local` to `.gitignore`. Note that this is the same ignore pattern that ships with create-next-app [as seen here](06abd63489/packages/create-next-app/templates/app/js/gitignore (L28)), so most Next.js users won't see anything change.

See the related [Linear ticket](https://linear.app/vercel/issue/VCCLI-461/)
2023-06-08 00:09:22 +00:00
Nathan Rajlich
94d5612dce [cli] Move readme copy logic to a helper function for vc link (#10084)
ncc has an issue with detecting + rewriting this file path for some reason. Moving to a helper function should work around that issue.

Fixing:

```
Error: ENOENT: no such file or directory, open '/node_modules/vercel/projects/VERCEL_DIR_README.txt'
```
2023-06-07 21:33:47 +00:00
Nathan Rajlich
3eaf58bb74 [cli] Add support for vc dev command with repo link (#10082)
When repo linked, `vc dev` already works fine when run from the root of the repo. This change makes it so that `vc dev` also works as expected when executed within a project subdirectory.
2023-06-07 18:48:28 +00:00
Nathan Rajlich
e63cf40153 [cli] Add support for vc build command with repo link (#10075)
When the repo is linked to Vercel with `vc link --repo`, the `vc build` command should be invoked from the project subdirectory (otherwise the project selector is displayed). The output directory is at `<projectRoot>/.vercel/output` instead of at the repo root.
2023-06-07 08:26:13 +00:00
Sean Massa
709c9509f4 Revert "Revert "[cli] Add support for vc pull command with repo link"" (#10078)
Reverts vercel/vercel#10076

Restores the original PR: https://github.com/vercel/vercel/pull/10071

With the fix from: https://github.com/vercel/vercel/pull/10073
2023-06-07 07:49:45 +00:00
Nathan Rajlich
6107c1ed22 Disable "Enforce Changeset" job for release PRs (#10080)
This job leads to a false-positive for the release PR, so disable it.
2023-06-06 23:27:39 -07:00
Vercel Release Bot
7a0f377afe Version Packages (#10074)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2023-06-07 01:23:29 -05:00
Sean Massa
a04bf557fc handle undefined content type in vc dev (#10077)
When no content type header is sent to an API request during `vc dev`, the request fails:

```
/Users/smassa/source/vercel/vercel-2/node_modules/.pnpm/content-type@1.0.4/node_modules/content-type/index.js:108
    throw new TypeError('argument string is required')
          ^
TypeError: argument string is required
...
```

This comes from some runtime type validation happening in the `content-type` package, for which we do not have types, which is why typescript didn't catch this.
2023-06-07 06:20:19 +00:00
Sean Massa
b6736e82cf Clear revert out of changelog (#10079) 2023-06-07 00:46:21 -05:00
Sean Massa
7923056bc0 Revert "[cli] Add support for vc pull command with repo link" (#10076) 2023-06-07 00:35:12 -05:00
Nathan Rajlich
71ff193ea3 [cli] Add support for vc pull command with repo link (#10071)
When the repo is linked to Vercel with `vc link --repo`, the `.vercel` directory will be created at the _project root_ instead of the _repo root_, and will still contain the `project.json`.

The difference is that the `orgId` and `projectId` props will not be present in the `project.json` file when repo linked, since that information is available at the root level `repo.json` file.
2023-06-06 22:01:37 +00:00
Vercel Release Bot
c21d93de44 Version Packages (#10062)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2023-06-06 16:34:04 -05:00
Kiko Beats
0039c8b5ce [node] Add isomorphic functions (#9947)
This PR allow using Web APIs in Serverless functions

```js
// api/serverless.js
export const GET = () => {
  return new Response(`new Response('👋 Hello from Serverless Web!)`)
}
```

More about that
https://nextjs.org/docs/app/building-your-application/routing/router-handlers

---------

Co-authored-by: Sean Massa <EndangeredMassa@gmail.com>
2023-06-06 23:15:10 +02:00
Vercel Release Bot
49c7178567 [remix] Update @remix-run/dev to v1.17.0 (#10072)
This auto-generated PR updates `@remix-run/dev` to version 1.17.0.
2023-06-06 20:31:48 +00:00
Ethan Arrowood
b038b29614 Add vercel.json setting overrides to storybook example for zero-config experience (#10067)
This PR adds necessary configuration overrides to the storybook example so that it can have a zero-config experience.
2023-06-06 00:52:38 +00:00
Nathan Rajlich
7a249a2284 Enforce a changeset file to be present (#10065)
Based off of these setup instructions:

https://github.com/changesets/changesets/blob/main/docs/automating-changesets.md#blocking

If a commit should really not publish a release, you can run `changeset
--empty`.
2023-06-05 15:44:40 -07:00
Nathan Rajlich
a5e32ec31d [cli] Add client.fetchPaginated() async iterator (#10054)
Adds async iterator helper function for interacting with endpoints that have pagination.

Implemented for use with `vc bisect` (fetching deployments) and `vc link --repo` (fetching projects).
2023-06-05 17:06:57 +00:00
Florentin / 珞辰
bc5afe24c4 [node] add maxDuration config support for vc node deployments (#10028)
This PR enables specifying `maxDuration` in `config` for node vc
deployments.

---------

Co-authored-by: Nathan Rajlich <n@n8.io>
2023-06-05 10:05:53 +02:00
Vercel Release Bot
5070e3bbbd [tests] Upgrade Turbo to version 1.10.1 (#10059)
This auto-generated PR updates Turbo to version 1.10.1
2023-06-05 03:57:56 +00:00
401 changed files with 39513 additions and 13965 deletions

14
.github/CODEOWNERS vendored
View File

@@ -2,19 +2,19 @@
# https://help.github.com/en/articles/about-code-owners
# Restricted Paths
* @TooTallNate @EndangeredMassa @styfle @cb1kenobi @Ethan-Arrowood
/.github/workflows @TooTallNate @EndangeredMassa @styfle @cb1kenobi @Ethan-Arrowood @ijjk
/packages/fs-detectors @TooTallNate @EndangeredMassa @styfle @cb1kenobi @Ethan-Arrowood @agadzik @chloetedder
/packages/next @TooTallNate @EndangeredMassa @styfle @cb1kenobi @Ethan-Arrowood @ijjk
/packages/routing-utils @TooTallNate @EndangeredMassa @styfle @cb1kenobi @Ethan-Arrowood @ijjk
/packages/edge @vercel/edge-compute
* @TooTallNate @EndangeredMassa @styfle @cb1kenobi @Ethan-Arrowood @trek
/.github/workflows @TooTallNate @EndangeredMassa @styfle @cb1kenobi @Ethan-Arrowood @trek @ijjk
/packages/fs-detectors @TooTallNate @EndangeredMassa @styfle @cb1kenobi @Ethan-Arrowood @trek @agadzik @chloetedder
/packages/next @TooTallNate @EndangeredMassa @styfle @cb1kenobi @Ethan-Arrowood @trek @ijjk
/packages/routing-utils @TooTallNate @EndangeredMassa @styfle @cb1kenobi @Ethan-Arrowood @trek @ijjk
/packages/edge @vercel/compute
/examples @leerob
/examples/create-react-app @Timer
/examples/nextjs @timneutkens @ijjk @styfle
/examples/hugo @styfle
/examples/jekyll @styfle
/examples/zola @styfle
/packages/node @TooTallNate @EndangeredMassa @styfle @cb1kenobi @Ethan-Arrowood @Kikobeats
/packages/node @TooTallNate @EndangeredMassa @styfle @cb1kenobi @Ethan-Arrowood @trek @Kikobeats
# Unrestricted Paths
.changeset/

View File

@@ -2,7 +2,9 @@ body:
- type: markdown
attributes:
value: |
> **Note**: For discussions not related to Vercel CLI or Runtimes, please visit the [Vercel Community](https://github.com/orgs/vercel/discussions)
**Note**: This category is intended for discussions related to Vercel CLI or Runtimes.
If you post in this repository seeking help with other Vercel tools and features, it may be missed by our support team. For help with topics other than the CLI and Runtimes, please visit the [Vercel Community](https://github.com/orgs/vercel/discussions).
- type: textarea
attributes:
label: Description

View File

@@ -2,9 +2,11 @@ body:
- type: markdown
attributes:
value: |
> **Note**: For discussions not related to Vercel CLI or Runtimes, please visit the [Vercel Community](https://github.com/orgs/vercel/discussions/categories/help)
**Note**: This category is intended for discussions related to Vercel CLI or Runtimes.
If you post in this repository seeking help with other Vercel tools and features, it may be missed by our support team. For help with topics other than the CLI and Runtimes, please visit the [Vercel Community](https://github.com/orgs/vercel/discussions).
- type: textarea
attributes:
label: Problem Description
label: Question
validations:
required: true

View File

@@ -2,7 +2,9 @@ body:
- type: markdown
attributes:
value: |
> **Note**: For discussions not related to Vercel CLI or Runtimes, please visit the [Vercel Community](https://github.com/orgs/vercel/discussions/categories/ideas)
**Note**: This category is intended for sharing ideas related to Vercel CLI or Runtimes.
Please visit the [Vercel Community](https://github.com/orgs/vercel/discussions) to share ideas for other Vercel tools and features.
- type: textarea
attributes:
label: Idea

View File

@@ -1,10 +0,0 @@
body:
- type: markdown
attributes:
value: |
> **Note**: For discussions not related to Vercel CLI or Runtimes, please visit the [Vercel Community](https://github.com/orgs/vercel/discussions)
- type: textarea
attributes:
label: Description
validations:
required: true

View File

@@ -2,7 +2,9 @@ body:
- type: markdown
attributes:
value: |
> **Note**: For discussions not related to Vercel CLI or Runtimes, please visit the [Vercel Community](https://github.com/orgs/vercel/discussions)
**Note**: This category is intended for discussions related to Vercel CLI or Runtimes.
For topics related to other Vercel features, please visit the [Vercel Community](https://github.com/orgs/vercel/discussions).
- type: textarea
attributes:
label: Description

View File

@@ -1,5 +0,0 @@
body:
- type: markdown
attributes:
value: |
> **Note**: This category should not be used for new discussions. Please visit the [Vercel Community](https://github.com/orgs/vercel/discussions)

View File

@@ -19,6 +19,22 @@ concurrency:
cancel-in-progress: ${{ github.ref != 'refs/heads/main' }}
jobs:
enforce-changeset:
name: Enforce Changeset
runs-on: ubuntu-latest
if: github.event_name == 'pull_request' && github.event.pull_request.title != 'Version Packages'
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 0
ref: main
- run: git checkout ${{ github.event.pull_request.head.ref }}
- name: install pnpm@8.3.1
run: npm i -g pnpm@8.3.1
- run: pnpm install
# Enforce a changeset file to be present
- run: pnpm exec changeset status --since=main
test:
name: Lint
timeout-minutes: 10

View File

@@ -81,11 +81,11 @@ jobs:
run: echo | openssl s_client -showcerts -servername 'api.vercel.com' -connect 76.76.21.21:443
- name: Build ${{matrix.packageName}} and all its dependencies
run: node utils/gen.js && node_modules/.bin/turbo run build --cache-dir=".turbo" --scope=${{matrix.packageName}} --include-dependencies --no-deps
run: node utils/gen.js && node_modules/.bin/turbo run build --cache-dir=".turbo" --log-order=stream --scope=${{matrix.packageName}} --include-dependencies --no-deps
env:
FORCE_COLOR: '1'
- name: Test ${{matrix.packageName}}
run: node utils/gen.js && node_modules/.bin/turbo run test --cache-dir=".turbo" --scope=${{matrix.packageName}} --no-deps -- ${{ join(matrix.testPaths, ' ') }}
run: node utils/gen.js && node_modules/.bin/turbo run test --cache-dir=".turbo" --log-order=stream --scope=${{matrix.packageName}} --no-deps -- ${{ join(matrix.testPaths, ' ') }}
shell: bash
env:
VERCEL_CLI_VERSION: ${{ needs.setup.outputs.dplUrl }}/tarballs/vercel.tgz

View File

@@ -44,6 +44,5 @@
"react-use": "^17.4.0",
"title": "^3.4.4",
"typographic-base": "^1.0.4"
},
"author": "nrajlich"
}
}

View File

@@ -42,8 +42,8 @@
.grid {
display: grid;
grid-template-columns: repeat(4, minmax(25%, auto));
width: var(--max-width);
max-width: 100%;
width: var(--max-width);
}
.card {

File diff suppressed because it is too large Load Diff

View File

@@ -9,9 +9,9 @@
"lint": "next lint"
},
"dependencies": {
"eslint": "8.41.0",
"eslint-config-next": "13.4.4",
"next": "13.4.4",
"eslint": "8.46.0",
"eslint-config-next": "13.4.13",
"next": "13.4.13",
"react": "18.2.0",
"react-dom": "18.2.0"
}

View File

@@ -45,4 +45,4 @@ Locally preview production build:
npm run preview
```
Checkout the [deployment documentation](https://v3.nuxtjs.org/guide/deploy/presets) for more information.
Checkout the [deployment documentation](https://nuxt.com/docs/getting-started/deployment#presets) for more information.

View File

@@ -9,6 +9,6 @@
},
"devDependencies": {
"@types/jest": "27.4.1",
"@vercel/frameworks": "1.4.2"
"@vercel/frameworks": "1.5.0"
}
}

View File

@@ -1,3 +1,4 @@
/** @type {import('eslint').Linter.Config} */
module.exports = {
extends: ["@remix-run/eslint-config", "@remix-run/eslint-config/node"],
};

View File

@@ -1,11 +1,12 @@
node_modules
.cache
/.cache
/build
/public/build
.env
.vercel
.output
/build/
/public/build
/api/index.js
/api/index.js.map

View File

@@ -1,4 +1,4 @@
import type { MetaFunction } from "@vercel/remix";
import { cssBundleHref } from "@remix-run/css-bundle";
import {
Links,
LiveReload,
@@ -8,17 +8,18 @@ import {
ScrollRestoration,
} from "@remix-run/react";
import { Analytics } from "@vercel/analytics/react";
import type { LinksFunction } from "@vercel/remix";
export const meta: MetaFunction = () => ({
charset: "utf-8",
title: "New Remix App",
viewport: "width=device-width,initial-scale=1",
});
export const links: LinksFunction = () => [
...(cssBundleHref ? [{ rel: "stylesheet", href: cssBundleHref }] : []),
];
export default function App() {
return (
<html lang="en">
<head>
<meta charSet="utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1" />
<Meta />
<Links />
</head>

View File

@@ -1,3 +1,7 @@
import type { V2_MetaFunction } from "@vercel/remix";
export const meta: V2_MetaFunction = () => [{ title: "New Remix App" }];
export default function Index() {
return (
<div style={{ fontFamily: "system-ui, sans-serif", lineHeight: "1.4" }}>

View File

@@ -1,5 +1,9 @@
import type { V2_MetaFunction } from "@vercel/remix";
export const config = { runtime: "edge" };
export const meta: V2_MetaFunction = () => [{ title: "Remix@Edge | New Remix App" }];
export default function Edge() {
return (
<div style={{ fontFamily: "system-ui, sans-serif", lineHeight: "1.4" }}>

View File

@@ -3,24 +3,28 @@
"sideEffects": false,
"scripts": {
"build": "remix build",
"dev": "remix dev"
"dev": "remix dev",
"start": "remix-serve build",
"typecheck": "tsc"
},
"dependencies": {
"@remix-run/node": "^1.15.0",
"@remix-run/react": "^1.15.0",
"@remix-run/serve": "^1.15.0",
"@remix-run/css-bundle": "^1.18.0",
"@remix-run/node": "^1.18.0",
"@remix-run/react": "^1.18.0",
"@remix-run/serve": "^1.18.0",
"@vercel/analytics": "^0.1.11",
"@vercel/remix": "^1.15.0",
"@vercel/remix": "^1.18.0",
"isbot": "^3.6.8",
"react": "^18.2.0",
"react-dom": "^18.2.0"
},
"devDependencies": {
"@remix-run/dev": "^1.15.0",
"@remix-run/eslint-config": "^1.15.0",
"@remix-run/dev": "^1.18.0",
"@remix-run/eslint-config": "^1.18.0",
"@types/react": "^18.0.25",
"@types/react-dom": "^18.0.11",
"eslint": "^8.28.0",
"typescript": "^4.9.3"
"typescript": "^5.1.3"
},
"engines": {
"node": ">=14"

File diff suppressed because it is too large Load Diff

View File

@@ -1,8 +1,15 @@
/**
* @type {import('@remix-run/dev').AppConfig}
*/
/** @type {import('@remix-run/dev').AppConfig} */
module.exports = {
ignoredRouteFiles: ['**/.*'],
future: {
v2_dev: true,
v2_errorBoundary: true,
v2_headers: true,
v2_meta: true,
v2_normalizeFormMethod: true,
v2_routeConvention: true,
},
ignoredRouteFiles: ["**/.*"],
serverModuleFormat: "cjs",
// appDirectory: "app",
// assetsBuildDirectory: "public/build",
// serverBuildPath: "build/index.js",

View File

@@ -2,7 +2,7 @@
# Saber Example
This directory is a brief example of a [Saber](https://saber.land) site that can be deployed to Vercel with zero configuration.
This directory is a brief example of a [Saber](https://saber.egoist.dev) site that can be deployed to Vercel with zero configuration.
## Deploy Your Own

View File

@@ -5,4 +5,4 @@ layout: page
This is the Saber port of the base Jekyll theme. Check out the [GitHub project](https://github.com/egoist/saber-theme-minima) for detailed usages.
You can find out more info about customizing your theme, as well as basic Saber usage documentation at https://saber.land
You can find out more info about customizing your theme, as well as basic Saber usage documentation at https://saber.egoist.dev

View File

@@ -0,0 +1,4 @@
{
"framework": "storybook",
"buildCommand": "storybook build"
}

View File

@@ -1,5 +1,18 @@
# @vercel-internals/constants
## 1.0.4
### Patch Changes
- Create new help output and arg parsing for deploy command ([#10090](https://github.com/vercel/vercel/pull/10090))
## 1.0.3
### Patch Changes
- Updated dependencies [[`346892210`](https://github.com/vercel/vercel/commit/3468922108f411482a72acd0331f0f2ee52a6d4c)]:
- @vercel/build-utils@6.8.0
## 1.0.2
### Patch Changes

View File

@@ -1,16 +1,13 @@
{
"private": true,
"name": "@vercel-internals/constants",
"version": "1.0.2",
"version": "1.0.4",
"types": "dist/index.d.ts",
"main": "dist/index.js",
"scripts": {
"build": "tsc -p tsconfig.json"
},
"dependencies": {
"@vercel/build-utils": "6.7.5",
"@vercel/routing-utils": "2.2.1"
},
"dependencies": {},
"devDependencies": {
"@vercel-internals/tsconfig": "1.0.0",
"@vercel/style-guide": "4.0.2",

View File

@@ -3,3 +3,7 @@ export const PROJECT_ENV_TARGET = [
'preview',
'development',
] as const;
export const LOGO = '▲' as const;
export const NAME = 'vercel' as const;
export const TITLE = 'Vercel' as const;

View File

@@ -1,5 +1,35 @@
# @vercel-internals/types
## 1.0.6
### Patch Changes
- Updated dependencies [[`a8ecf40d6`](https://github.com/vercel/vercel/commit/a8ecf40d6f50e2fc8b13b02c8ef50b3dcafad3a6)]:
- @vercel/build-utils@6.8.3
## 1.0.5
### Patch Changes
- Updated dependencies [[`0750517af`](https://github.com/vercel/vercel/commit/0750517af99aea41410d4f1f772ce427699554e7)]:
- @vercel/build-utils@6.8.2
## 1.0.4
### Patch Changes
- Updated dependencies [[`7021279b2`](https://github.com/vercel/vercel/commit/7021279b284f314a4d1bdbb4306b4c22291efa08), [`718bbd365`](https://github.com/vercel/vercel/commit/718bbd365a50271a980bdca231ca801a0eead32b)]:
- @vercel/build-utils@6.8.1
- @vercel-internals/constants@1.0.4
## 1.0.3
### Patch Changes
- Updated dependencies [[`346892210`](https://github.com/vercel/vercel/commit/3468922108f411482a72acd0331f0f2ee52a6d4c)]:
- @vercel/build-utils@6.8.0
- @vercel-internals/constants@1.0.3
## 1.0.2
### Patch Changes

View File

@@ -1,5 +1,6 @@
import type { BuilderFunctions } from '@vercel/build-utils';
import type { Readable, Writable } from 'stream';
import type * as tty from 'tty';
import type { Route } from '@vercel/routing-utils';
import { PROJECT_ENV_TARGET } from '@vercel-internals/constants';
@@ -378,28 +379,60 @@ export interface ProjectLink {
* to the root directory of the repository.
*/
repoRoot?: string;
/**
* When linked as a repository, contains the relative path
* to the selected project root directory.
*/
projectRootDirectory?: string;
}
export interface PaginationOptions {
prev: number;
/**
* Amount of items in the current page.
* @example 20
*/
count: number;
next?: number;
/**
* Timestamp that must be used to request the next page.
* @example 1540095775951
*/
next: number | null;
/**
* Timestamp that must be used to request the previous page.
* @example 1540095775951
*/
prev: number | null;
}
export type ProjectLinked = {
status: 'linked';
org: Org;
project: Project;
repoRoot?: string;
};
export type ProjectNotLinked = {
status: 'not_linked';
org: null;
project: null;
};
export type ProjectLinkedError = {
status: 'error';
exitCode: number;
reason?:
| 'HEADLESS'
| 'NOT_AUTHORIZED'
| 'TEAM_DELETED'
| 'PATH_IS_FILE'
| 'INVALID_ROOT_DIRECTORY'
| 'MISSING_PROJECT_SETTINGS';
};
export type ProjectLinkResult =
| { status: 'linked'; org: Org; project: Project; repoRoot?: string }
| { status: 'not_linked'; org: null; project: null }
| {
status: 'error';
exitCode: number;
reason?:
| 'HEADLESS'
| 'NOT_AUTHORIZED'
| 'TEAM_DELETED'
| 'PATH_IS_FILE'
| 'INVALID_ROOT_DIRECTORY'
| 'MISSING_PROJECT_SETTINGS';
};
| ProjectLinked
| ProjectNotLinked
| ProjectLinkedError;
/**
* @deprecated - `RollbackJobStatus` has been replace by `LastAliasRequest['jobStatus']`.
@@ -599,6 +632,6 @@ export interface WritableTTY extends Writable {
export interface Stdio {
stdin: ReadableTTY;
stdout: WritableTTY;
stderr: WritableTTY;
stdout: tty.WriteStream;
stderr: tty.WriteStream;
}

View File

@@ -1,13 +1,13 @@
{
"private": true,
"name": "@vercel-internals/types",
"version": "1.0.2",
"version": "1.0.6",
"types": "index.d.ts",
"main": "index.d.ts",
"dependencies": {
"@types/node": "14.14.31",
"@vercel-internals/constants": "1.0.2",
"@vercel/build-utils": "6.7.5",
"@vercel-internals/constants": "1.0.4",
"@vercel/build-utils": "6.8.3",
"@vercel/routing-utils": "2.2.1"
},
"devDependencies": {

View File

@@ -1,13 +0,0 @@
{
"npmClient": "pnpm",
"useWorkspaces": true,
"packages": ["packages/*"],
"command": {
"publish": {
"npmClient": "npm",
"allowBranch": ["main"],
"registry": "https://registry.npmjs.org/"
}
},
"version": "independent"
}

View File

@@ -32,7 +32,7 @@
"source-map-support": "0.5.12",
"ts-eager": "2.0.2",
"ts-jest": "29.1.0",
"turbo": "1.9.9",
"turbo": "1.10.12",
"typescript": "4.9.5"
},
"scripts": {

View File

@@ -1,5 +1,29 @@
# @vercel/build-utils
## 6.8.3
### Patch Changes
- Fix `getPrefixedEnvVars()` to handle `VERCEL_BRANCH_URL` ([#10315](https://github.com/vercel/vercel/pull/10315))
## 6.8.2
### Patch Changes
- Push back `nodejs16.x` discontinue date to `2024-02-06` ([#10209](https://github.com/vercel/vercel/pull/10209))
## 6.8.1
### Patch Changes
- Revert "[build-utils] Allow file-ref sema to be controlled through env flag" ([#10167](https://github.com/vercel/vercel/pull/10167))
## 6.8.0
### Minor Changes
- Add `getNodeBinPaths()` and `traverseUpDirectories()` functions ([#10150](https://github.com/vercel/vercel/pull/10150))
## 6.7.5
### Patch Changes

View File

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

View File

@@ -5,13 +5,7 @@ import path from 'path';
import Sema from 'async-sema';
import { FileBase } from './types';
const DEFAULT_SEMA = 20;
const semaToPreventEMFILE = new Sema(
parseInt(
process.env.VERCEL_INTERNAL_FILE_FS_REF_SEMA || String(DEFAULT_SEMA),
10
) || DEFAULT_SEMA
);
const semaToPreventEMFILE = new Sema(20);
interface FileFsRefOptions {
mode?: number;

View File

@@ -12,13 +12,7 @@ interface FileRefOptions {
mutable?: boolean;
}
const DEFAULT_SEMA = 5;
const semaToDownloadFromS3 = new Sema(
parseInt(
process.env.VERCEL_INTERNAL_FILE_REF_SEMA || String(DEFAULT_SEMA),
10
) || DEFAULT_SEMA
);
const semaToDownloadFromS3 = new Sema(5);
class BailableError extends Error {
public bail: boolean;

View File

@@ -10,7 +10,7 @@ function getOptions() {
major: 16,
range: '16.x',
runtime: 'nodejs16.x',
discontinueDate: new Date('2023-08-15'),
discontinueDate: new Date('2024-02-06'),
},
{
major: 14,

View File

@@ -44,23 +44,33 @@ export interface ScanParentDirsResult {
lockfileVersion?: number;
}
export interface WalkParentDirsProps {
export interface TraverseUpDirectoriesProps {
/**
* The highest directory, typically the workPath root of the project.
* If this directory is reached and it doesn't contain the file, null is returned.
*/
base: string;
/**
* The directory to start searching, typically the same directory of the entrypoint.
* If this directory doesn't contain the file, the parent is checked, etc.
* The directory to start iterating from, typically the same directory of the entrypoint.
*/
start: string;
/**
* The highest directory, typically the workPath root of the project.
*/
base?: string;
}
export interface WalkParentDirsProps
extends Required<TraverseUpDirectoriesProps> {
/**
* The name of the file to search for, typically `package.json` or `Gemfile`.
*/
filename: string;
}
export interface WalkParentDirsMultiProps
extends Required<TraverseUpDirectoriesProps> {
/**
* The name of the file to search for, typically `package.json` or `Gemfile`.
*/
filenames: string[];
}
export interface SpawnOptionsExtended extends SpawnOptions {
/**
* Pretty formatted command that is being spawned for logging purposes.
@@ -131,6 +141,24 @@ export async function execCommand(command: string, options: SpawnOptions = {}) {
return true;
}
export function* traverseUpDirectories({
start,
base,
}: TraverseUpDirectoriesProps) {
let current: string | undefined = path.normalize(start);
const normalizedRoot = base ? path.normalize(base) : undefined;
while (current) {
yield current;
if (current === normalizedRoot) break;
// Go up one directory
const next = path.join(current, '..');
current = next === current ? undefined : next;
}
}
/**
* @deprecated Use `getNodeBinPaths()` instead.
*/
export async function getNodeBinPath({
cwd,
}: {
@@ -141,6 +169,15 @@ export async function getNodeBinPath({
return path.join(dir, 'node_modules', '.bin');
}
export function getNodeBinPaths({
start,
base,
}: TraverseUpDirectoriesProps): string[] {
return Array.from(traverseUpDirectories({ start, base })).map(dir =>
path.join(dir, 'node_modules/.bin')
);
}
async function chmodPlusX(fsPath: string) {
const s = await fs.stat(fsPath);
const newMode = s.mode | 64 | 8 | 1; // eslint-disable-line no-bitwise
@@ -297,22 +334,14 @@ export async function walkParentDirs({
}: WalkParentDirsProps): Promise<string | null> {
assert(path.isAbsolute(base), 'Expected "base" to be absolute path');
assert(path.isAbsolute(start), 'Expected "start" to be absolute path');
let parent = '';
for (let current = start; base.length <= current.length; current = parent) {
const fullPath = path.join(current, filename);
for (const dir of traverseUpDirectories({ start, base })) {
const fullPath = path.join(dir, filename);
// eslint-disable-next-line no-await-in-loop
if (await fs.pathExists(fullPath)) {
return fullPath;
}
parent = path.dirname(current);
if (parent === current) {
// Reached root directory of the filesystem
break;
}
}
return null;
@@ -322,14 +351,9 @@ async function walkParentDirsMulti({
base,
start,
filenames,
}: {
base: string;
start: string;
filenames: string[];
}): Promise<(string | undefined)[]> {
let parent = '';
for (let current = start; base.length <= current.length; current = parent) {
const fullPaths = filenames.map(f => path.join(current, f));
}: WalkParentDirsMultiProps): Promise<(string | undefined)[]> {
for (const dir of traverseUpDirectories({ start, base })) {
const fullPaths = filenames.map(f => path.join(dir, f));
const existResults = await Promise.all(
fullPaths.map(f => fs.pathExists(f))
);
@@ -338,13 +362,6 @@ async function walkParentDirsMulti({
if (foundOneOrMore) {
return fullPaths.map((f, i) => (existResults[i] ? f : undefined));
}
parent = path.dirname(current);
if (parent === current) {
// Reached root directory of the filesystem
break;
}
}
return [];

View File

@@ -14,7 +14,12 @@ export function getPrefixedEnvVars({
envs: Envs;
}): Envs {
const vercelSystemEnvPrefix = 'VERCEL_';
const allowed = ['VERCEL_URL', 'VERCEL_ENV', 'VERCEL_REGION'];
const allowed = [
'VERCEL_URL',
'VERCEL_ENV',
'VERCEL_REGION',
'VERCEL_BRANCH_URL',
];
const newEnvs: Envs = {};
if (envPrefix && envs.VERCEL_URL) {
Object.keys(envs)

View File

@@ -30,7 +30,9 @@ import {
getNodeVersion,
getSpawnOptions,
getNodeBinPath,
getNodeBinPaths,
scanParentDirs,
traverseUpDirectories,
} from './fs/run-user-scripts';
import {
getLatestNodeVersion,
@@ -68,6 +70,7 @@ export {
spawnCommand,
walkParentDirs,
getNodeBinPath,
getNodeBinPaths,
runNpmInstall,
runBundleInstall,
runPipInstall,
@@ -89,6 +92,7 @@ export {
getIgnoreFilter,
cloneEnv,
hardLinkDir,
traverseUpDirectories,
validateNpmrc,
};

View File

@@ -0,0 +1,17 @@
import { join } from 'path';
import { getNodeBinPaths } from '../src/fs/run-user-scripts';
describe('getNodeBinPaths()', () => {
const cwd = process.cwd();
it('should return array of `node_modules/.bin` paths', () => {
const start = join(cwd, 'foo/bar/baz');
const paths = getNodeBinPaths({ start, base: cwd });
expect(paths).toEqual([
join(cwd, 'foo/bar/baz/node_modules/.bin'),
join(cwd, 'foo/bar/node_modules/.bin'),
join(cwd, 'foo/node_modules/.bin'),
join(cwd, 'node_modules/.bin'),
]);
});
});

View File

@@ -13,6 +13,8 @@ describe('Test `getPrefixedEnvVars()`', () => {
envs: {
VERCEL: '1',
VERCEL_URL: 'example.vercel.sh',
VERCEL_ENV: 'production',
VERCEL_BRANCH_URL: 'example-git-main-acme.vercel.app',
USER_ENV_VAR_NOT_VERCEL: 'example.com',
VERCEL_ARTIFACTS_TOKEN: 'abc123',
FOO: 'bar',
@@ -20,6 +22,8 @@ describe('Test `getPrefixedEnvVars()`', () => {
},
want: {
NEXT_PUBLIC_VERCEL_URL: 'example.vercel.sh',
NEXT_PUBLIC_VERCEL_ENV: 'production',
NEXT_PUBLIC_VERCEL_BRANCH_URL: 'example-git-main-acme.vercel.app',
TURBO_CI_VENDOR_ENV_KEY: 'NEXT_PUBLIC_VERCEL_',
},
},

View File

@@ -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('2023-10-01').getTime();
global.Date.now = () => new Date('2024-02-13').getTime();
expect(getSupportedNodeVersion('8.10.x', false)).rejects.toThrow();
expect(getSupportedNodeVersion('8.10.x', true)).rejects.toThrow();
@@ -306,8 +306,8 @@ it('should warn for deprecated versions, soon to be discontinued', async () => {
'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.',
'Error: Node.js version 16.x has reached End-of-Life. Deployments created on or after 2024-02-06 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 2024-02-06 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;

View File

@@ -0,0 +1,50 @@
import { traverseUpDirectories } from '../src/fs/run-user-scripts';
const isWindows = process.platform === 'win32';
describe('traverseUpDirectories()', () => {
test.each(
isWindows
? [
{
start: 'C:\\foo\\bar\\baz',
expected: ['C:\\foo\\bar\\baz', 'C:\\foo\\bar', 'C:\\foo', 'C:\\'],
},
{
start: 'C:\\foo\\..\\bar\\.\\baz',
expected: ['C:\\bar\\baz', 'C:\\bar', 'C:\\'],
},
{
start: 'C:\\foo\\bar\\baz\\another',
base: 'C:\\foo\\bar',
expected: [
'C:\\foo\\bar\\baz\\another',
'C:\\foo\\bar\\baz',
'C:\\foo\\bar',
],
},
]
: [
{
start: '/foo/bar/baz',
expected: ['/foo/bar/baz', '/foo/bar', '/foo', '/'],
},
{
start: '/foo/../bar/./baz',
expected: ['/bar/baz', '/bar', '/'],
},
{
start: '/foo/bar/baz/another',
base: '/foo/bar',
expected: ['/foo/bar/baz/another', '/foo/bar/baz', '/foo/bar'],
},
]
)(
'should traverse start="$start", base="$base"',
({ start, base, expected }) => {
expect(Array.from(traverseUpDirectories({ start, base }))).toEqual(
expected
);
}
);
});

View File

@@ -1,5 +1,233 @@
# vercel
## 31.2.3
### Patch Changes
- Be looser in tests with mock server urls ([#10300](https://github.com/vercel/vercel/pull/10300))
- Handle calls for deployment aliases when mocking deployments ([#10303](https://github.com/vercel/vercel/pull/10303))
- Remove unused code ([#10309](https://github.com/vercel/vercel/pull/10309))
- Updated dependencies [[`5bf1fe4c7`](https://github.com/vercel/vercel/commit/5bf1fe4c743f6be3f7d5a24447ea5b083a68dc67), [`a8ecf40d6`](https://github.com/vercel/vercel/commit/a8ecf40d6f50e2fc8b13b02c8ef50b3dcafad3a6), [`08da4b9c9`](https://github.com/vercel/vercel/commit/08da4b9c923501d9d28eb6e3f26f4605fee83042), [`0945d24cb`](https://github.com/vercel/vercel/commit/0945d24cbe901ca3f0eedd011251ad499c72d472)]:
- @vercel/next@3.9.4
- @vercel/build-utils@6.8.3
- @vercel/remix-builder@1.10.0
- @vercel/node@2.15.9
- @vercel/static-build@1.3.45
## 31.2.2
### Patch Changes
- Migrate list command to new structure ([#10284](https://github.com/vercel/vercel/pull/10284))
- Migrate whoami command to new structure ([#10266](https://github.com/vercel/vercel/pull/10266))
- Migrate logs command to new structure ([#10281](https://github.com/vercel/vercel/pull/10281))
- Migrate login command to new structure ([#10283](https://github.com/vercel/vercel/pull/10283))
- Migrate pull command to new structure ([#10280](https://github.com/vercel/vercel/pull/10280))
- Migrate logout command to new structure ([#10282](https://github.com/vercel/vercel/pull/10282))
- Migrate build command to new structure ([#10286](https://github.com/vercel/vercel/pull/10286))
- Migrate inspect command to new structure ([#10277](https://github.com/vercel/vercel/pull/10277))
- Migrate redeploy command to new structure ([#10279](https://github.com/vercel/vercel/pull/10279))
- Migrate link command to new structure ([#10285](https://github.com/vercel/vercel/pull/10285))
- Update spacing of --help output for CLI ([#10287](https://github.com/vercel/vercel/pull/10287))
- Updated dependencies [[`4af242af8`](https://github.com/vercel/vercel/commit/4af242af8633e58b6a9bf920564416da3ef22ad4), [`0cbdae141`](https://github.com/vercel/vercel/commit/0cbdae1411aa7936ff7dfe551919ca5e56cd6e98), [`85dd66778`](https://github.com/vercel/vercel/commit/85dd667781693539d753d587566e53964bbe189d)]:
- @vercel/node@2.15.8
- @vercel/remix-builder@1.9.1
- @vercel/static-build@1.3.44
## 31.2.1
### Patch Changes
- Migrate bisect command to new structure ([#10276](https://github.com/vercel/vercel/pull/10276))
- Migrate remove command to new structure ([#10268](https://github.com/vercel/vercel/pull/10268))
- Updated dependencies [[`fc413707d`](https://github.com/vercel/vercel/commit/fc413707d017e234d5013b761d885f65f9b981bc)]:
- @vercel/node@2.15.7
- @vercel/static-build@1.3.43
## 31.2.0
### Minor Changes
- Add a "Global Options" section to help output ([#10250](https://github.com/vercel/vercel/pull/10250))
### Patch Changes
- Updated dependencies [[`d1b0dbe3a`](https://github.com/vercel/vercel/commit/d1b0dbe3a7d8754286aa2b7ba0c8b55d3adafdea), [`4a8622a10`](https://github.com/vercel/vercel/commit/4a8622a10d52260cb629a1c4a6f797ade05ea154), [`6469ef1b8`](https://github.com/vercel/vercel/commit/6469ef1b8ce37e93f50ab4a108aa0953d7631fe8)]:
- @vercel/remix-builder@1.9.0
- @vercel/next@3.9.3
## 31.1.1
### Patch Changes
- Updated dependencies [[`7c30b13cc`](https://github.com/vercel/vercel/commit/7c30b13ccb79bdf0ac240282bba4c084f1d0d122)]:
- @vercel/next@3.9.2
## 31.1.0
### Minor Changes
- Add 'Environment' column to 'vc list' with new '--environment' filter and pipe URLs to stdout ([#10239](https://github.com/vercel/vercel/pull/10239))
### Patch Changes
- Update `proxy-agent` to v6.3.0 ([#10226](https://github.com/vercel/vercel/pull/10226))
- Use `getNodeBinPaths()` in `vc dev` ([#10225](https://github.com/vercel/vercel/pull/10225))
- Updated dependencies [[`b1c14cde0`](https://github.com/vercel/vercel/commit/b1c14cde03f94b2c15ba12c9be9d19c72df2fdbb), [`ce4633fe4`](https://github.com/vercel/vercel/commit/ce4633fe4d00cb5c251cdabbfab08f39ec3f3b5f)]:
- @vercel/next@3.9.1
- @vercel/static-build@1.3.42
## 31.0.4
### Patch Changes
- Detect multiple frameworks within the same root directory during `vc link --repo` ([#10203](https://github.com/vercel/vercel/pull/10203))
- Updated dependencies [[`b56639b62`](https://github.com/vercel/vercel/commit/b56639b624e9ad1df048a4c85083e26888696060), [`cae60155f`](https://github.com/vercel/vercel/commit/cae60155f34883f08a5e4f51b547e2a1a5fee694), [`c670e5171`](https://github.com/vercel/vercel/commit/c670e51712022193e078bd68b055f7e61013015d), [`5439d7c0c`](https://github.com/vercel/vercel/commit/5439d7c0c9b79e7161bf4fa84ffdb357365f9e7e)]:
- @vercel/node@2.15.6
- @vercel/next@3.9.0
- @vercel/remix-builder@1.8.18
- @vercel/static-build@1.3.41
## 31.0.3
### Patch Changes
- Fix redeploy target to be undefined when null ([#10201](https://github.com/vercel/vercel/pull/10201))
- Respect forbidden API responses ([#10178](https://github.com/vercel/vercel/pull/10178))
- Update `supports-hyperlinks` to v3 ([#10208](https://github.com/vercel/vercel/pull/10208))
- Updated dependencies [[`0750517af`](https://github.com/vercel/vercel/commit/0750517af99aea41410d4f1f772ce427699554e7)]:
- @vercel/build-utils@6.8.2
- @vercel/static-build@1.3.40
- @vercel/node@2.15.5
- @vercel/remix-builder@1.8.17
## 31.0.2
### Patch Changes
- Allow additional project settings in `createProject()` ([#10172](https://github.com/vercel/vercel/pull/10172))
- Run local Project detection during `vc link --repo`. ([#10094](https://github.com/vercel/vercel/pull/10094))
This allows for creation of new Projects that do not yet exist under the selected scope.
- Redeploy command no longer redeploys preview deployments to production ([#10186](https://github.com/vercel/vercel/pull/10186))
- Added trailing new line at end of help output ([#10170](https://github.com/vercel/vercel/pull/10170))
- Create new help output and arg parsing for deploy command ([#10090](https://github.com/vercel/vercel/pull/10090))
- [cli] Remove `preinstall` script ([#10157](https://github.com/vercel/vercel/pull/10157))
- Updated dependencies [[`7021279b2`](https://github.com/vercel/vercel/commit/7021279b284f314a4d1bdbb4306b4c22291efa08), [`5e5332fbc`](https://github.com/vercel/vercel/commit/5e5332fbc9317a8f3cc4ed0b72ec1a2c76020891), [`027bce00b`](https://github.com/vercel/vercel/commit/027bce00b3821d9b4a8f7ec320cd1c43ab9f4215)]:
- @vercel/build-utils@6.8.1
- @vercel/node@2.15.4
- @vercel/remix-builder@1.8.16
- @vercel/static-build@1.3.39
## 31.0.1
### Patch Changes
- Updated dependencies [[`aa734efc6`](https://github.com/vercel/vercel/commit/aa734efc6c42badd4aa9bf64487904aa64e9bd49)]:
- @vercel/next@3.8.8
## 31.0.0
### Major Changes
- Update `vc dev` redirect response to match production behavior ([#10143](https://github.com/vercel/vercel/pull/10143))
### Patch Changes
- require `--yes` to promote preview deployment ([#10135](https://github.com/vercel/vercel/pull/10135))
- [cli] Optimize write build result for vc build ([#10154](https://github.com/vercel/vercel/pull/10154))
- Only show relevant Project matches in Project selector ([#10114](https://github.com/vercel/vercel/pull/10114))
- [cli] Fix error message when token is invalid ([#10131](https://github.com/vercel/vercel/pull/10131))
- Updated dependencies [[`e4895d979`](https://github.com/vercel/vercel/commit/e4895d979b57e369e0618481c5974243887d72cc), [`346892210`](https://github.com/vercel/vercel/commit/3468922108f411482a72acd0331f0f2ee52a6d4c), [`346892210`](https://github.com/vercel/vercel/commit/3468922108f411482a72acd0331f0f2ee52a6d4c), [`a6de052ed`](https://github.com/vercel/vercel/commit/a6de052ed2f09cc80bf4c2d0f06bedd267a63cdc)]:
- @vercel/next@3.8.7
- @vercel/static-build@1.3.38
- @vercel/build-utils@6.8.0
- @vercel/remix-builder@1.8.15
- @vercel/node@2.15.3
## 30.2.3
### Patch Changes
- [cli] do not force auto-assign value on deployments ([#10110](https://github.com/vercel/vercel/pull/10110))
- Updated dependencies [[`91406abdb`](https://github.com/vercel/vercel/commit/91406abdb0c332152fc6c7c1e4bd3a872b084434), [`2230ea6cc`](https://github.com/vercel/vercel/commit/2230ea6cc1b84c1f03227a4e197b7684635b5955), [`8b3a4146a`](https://github.com/vercel/vercel/commit/8b3a4146af68d2b7288c80a5b919d832dba929b5)]:
- @vercel/node@2.15.2
- @vercel/remix-builder@1.8.14
- @vercel/static-build@1.3.37
## 30.2.2
### Patch Changes
- [cli] vc env pull should add `.env*.local` to `.gitignore` ([#10085](https://github.com/vercel/vercel/pull/10085))
- [cli] Fix team validation bug where you are apart of a team ([#10092](https://github.com/vercel/vercel/pull/10092))
- Add support for `vc dev` command with repo link ([#10082](https://github.com/vercel/vercel/pull/10082))
- Add support for `vc deploy --prebuilt` command with repo link ([#10083](https://github.com/vercel/vercel/pull/10083))
- Move readme copy logic to a helper function for `vc link` ([#10084](https://github.com/vercel/vercel/pull/10084))
- Add support for `vc pull` command with repo link ([#10078](https://github.com/vercel/vercel/pull/10078))
- Add support for `vc build` command with repo link ([#10075](https://github.com/vercel/vercel/pull/10075))
## 30.2.1
### Patch Changes
- Updated dependencies [[`a04bf557f`](https://github.com/vercel/vercel/commit/a04bf557fc6e1080a117428977d0993dec78b004)]:
- @vercel/node@2.15.1
- @vercel/static-build@1.3.36
## 30.2.0
### Minor Changes
- [node] Add isomorphic functions ([#9947](https://github.com/vercel/vercel/pull/9947))
### Patch Changes
- Add `client.fetchPaginated()` helper function ([#10054](https://github.com/vercel/vercel/pull/10054))
- Updated dependencies [[`bc5afe24c`](https://github.com/vercel/vercel/commit/bc5afe24c4547dbf798b939199e8212c4b34038e), [`49c717856`](https://github.com/vercel/vercel/commit/49c7178567ec5bcebe633b598c8c9c0e1aa40fbb), [`0039c8b5c`](https://github.com/vercel/vercel/commit/0039c8b5cea975316a62c4f6aaca5d66d731cc0d)]:
- @vercel/node@2.15.0
- @vercel/remix-builder@1.8.13
- @vercel/static-build@1.3.35
## 30.1.2
### Patch Changes

View File

@@ -1,6 +1,6 @@
{
"name": "vercel",
"version": "30.1.2",
"version": "31.2.3",
"preferGlobal": true,
"license": "Apache-2.0",
"description": "The command-line interface for Vercel",
@@ -11,7 +11,6 @@
"directory": "packages/cli"
},
"scripts": {
"preinstall": "node ./scripts/preinstall.js",
"test": "jest --env node --verbose --bail",
"test-unit": "pnpm test test/unit/",
"test-e2e": "rimraf test/fixtures/integration && pnpm test test/integration-1.test.ts test/integration-2.test.ts test/integration-3.test.ts",
@@ -32,20 +31,20 @@
"node": ">= 14"
},
"dependencies": {
"@vercel/build-utils": "6.7.5",
"@vercel/build-utils": "6.8.3",
"@vercel/go": "2.5.1",
"@vercel/hydrogen": "0.0.64",
"@vercel/next": "3.8.6",
"@vercel/node": "2.14.5",
"@vercel/next": "3.9.4",
"@vercel/node": "2.15.9",
"@vercel/python": "3.1.60",
"@vercel/redwood": "1.1.15",
"@vercel/remix-builder": "1.8.12",
"@vercel/remix-builder": "1.10.0",
"@vercel/ruby": "1.3.76",
"@vercel/static-build": "1.3.34"
"@vercel/static-build": "1.3.45"
},
"devDependencies": {
"@alex_neo/jest-expect-message": "1.0.5",
"@edge-runtime/node-utils": "2.0.3",
"@edge-runtime/node-utils": "2.1.0",
"@next/env": "11.1.2",
"@sentry/node": "5.5.0",
"@sindresorhus/slugify": "0.11.0",
@@ -85,13 +84,13 @@
"@types/which": "3.0.0",
"@types/write-json-file": "2.2.1",
"@types/yauzl-promise": "2.1.0",
"@vercel-internals/constants": "1.0.2",
"@vercel-internals/constants": "1.0.4",
"@vercel-internals/get-package-json": "1.0.0",
"@vercel-internals/types": "1.0.2",
"@vercel/client": "12.6.2",
"@vercel-internals/types": "1.0.6",
"@vercel/client": "12.6.6",
"@vercel/error-utils": "1.0.10",
"@vercel/frameworks": "1.4.2",
"@vercel/fs-detectors": "3.9.3",
"@vercel/frameworks": "1.5.0",
"@vercel/fs-detectors": "4.1.1",
"@vercel/fun": "1.0.4",
"@vercel/ncc": "0.24.0",
"@vercel/routing-utils": "2.2.1",
@@ -150,7 +149,7 @@
"pluralize": "7.0.0",
"promisepipe": "3.0.0",
"proxy": "2.0.0",
"proxy-agent": "6.1.1",
"proxy-agent": "6.3.0",
"psl": "1.1.31",
"qr-image": "3.2.0",
"raw-body": "2.4.1",
@@ -158,8 +157,7 @@
"semver": "5.5.0",
"serve-handler": "6.1.1",
"strip-ansi": "6.0.1",
"stripe": "5.1.0",
"supports-hyperlinks": "2.2.0",
"supports-hyperlinks": "3.0.0",
"tar-fs": "1.16.3",
"test-listen": "1.1.0",
"text-table": "0.2.0",

View File

@@ -1,101 +0,0 @@
#!/usr/bin/env node
const { join } = require('path');
const { statSync } = require('fs');
const pkg = require('../package');
function error(command) {
console.error('> Error:', command);
}
function debug(str) {
if (
process.argv.find(str => str === '--debug') ||
process.env.PREINSTALL_DEBUG
) {
console.log(`[debug] [${new Date().toISOString()}]`, str);
}
}
function isYarn() {
return process.env.npm_config_heading !== 'npm';
}
function isGlobal() {
const cmd = JSON.parse(process.env.npm_config_argv || '{ "original": [] }');
return isYarn()
? cmd.original.includes('global')
: Boolean(process.env.npm_config_global);
}
function isVercel() {
return pkg.name === 'vercel';
}
function validateNodeVersion() {
let semver = '>= 0';
let major = '1';
try {
major = process.versions.node.split('.')[0];
const pkg = require('../package.json');
semver = pkg.engines.node;
} catch (e) {
debug('Failed to read package.json engines');
}
const isValid = eval(`${major} ${semver}`);
return { isValid, expected: semver, actual: process.versions.node };
}
function isInNodeModules(name) {
try {
const nodeModules = join(__dirname, '..', '..');
const stat = statSync(join(nodeModules, name));
return stat.isDirectory();
} catch (err) {
return false;
}
}
async function main() {
if (!isGlobal()) {
debug('Skipping preinstall since Vercel CLI is being installed locally');
return;
}
const ver = validateNodeVersion();
if (!ver.isValid) {
error(
`Detected unsupported Node.js version.\n` +
`Expected "${ver.expected}" but found "${ver.actual}".\n` +
`Please update to the latest Node.js LTS version to install Vercel CLI.`
);
process.exit(1);
}
if (isVercel() && isInNodeModules('now')) {
const uninstall = isYarn()
? 'yarn global remove now'
: 'npm uninstall -g now';
console.error(`NOTE: Run \`${uninstall}\` to uninstall \`now\`\n`);
}
}
process.on('unhandledRejection', err => {
console.error('Unhandled Rejection:');
console.error(err);
process.exit(1);
});
process.on('uncaughtException', err => {
console.error('Uncaught Exception:');
console.error(err);
process.exit(1);
});
main().catch(err => {
console.error(err);
process.exit(1);
});

View File

@@ -0,0 +1,69 @@
import { Command } from '../help';
import { getPkgName } from '../../util/pkg-name';
export const bisectCommand: Command = {
name: 'bisect',
description: 'Bisect the current project interactively.',
arguments: [],
options: [
{
name: 'bad',
description: 'Known bad URL',
argument: 'URL',
shorthand: 'b',
type: 'string',
deprecated: false,
multi: false,
},
{
name: 'good',
description: 'Known good URL',
argument: 'URL',
shorthand: 'g',
type: 'string',
deprecated: false,
multi: false,
},
{
name: 'open',
description: 'Automatically open each URL in the browser',
argument: 'URL',
shorthand: 'o',
type: 'string',
deprecated: false,
multi: false,
},
{
name: 'path',
description: 'Subpath of the deployment URL to test',
argument: 'URL',
shorthand: 'p',
type: 'string',
deprecated: false,
multi: false,
},
{
name: 'run',
description: 'Test script to run for each deployment',
argument: 'URL',
shorthand: 'r',
type: 'string',
deprecated: false,
multi: false,
},
],
examples: [
{
name: 'Bisect the current project interactively',
value: `${getPkgName()} bisect`,
},
{
name: 'Bisect with a known bad deployment',
value: `${getPkgName()} bisect --bad example-310pce9i0.vercel.app`,
},
{
name: 'Automated bisect with a run script',
value: `${getPkgName()} bisect --run ./test.sh`,
},
],
};

View File

@@ -6,57 +6,21 @@ import chalk, { Chalk } from 'chalk';
import { URLSearchParams, parse } from 'url';
import box from '../../util/output/box';
import sleep from '../../util/sleep';
import formatDate from '../../util/format-date';
import link from '../../util/output/link';
import logo from '../../util/output/logo';
import getArgs from '../../util/get-args';
import Client from '../../util/client';
import { getPkgName } from '../../util/pkg-name';
import { Deployment, PaginationOptions } from '@vercel-internals/types';
import { Deployment } from '@vercel-internals/types';
import { normalizeURL } from '../../util/bisect/normalize-url';
import getScope from '../../util/get-scope';
import getDeployment from '../../util/get-deployment';
import { help } from '../help';
import { bisectCommand } from './command';
interface Deployments {
deployments: Deployment[];
pagination: PaginationOptions;
}
const pkgName = getPkgName();
const help = () => {
console.log(`
${chalk.bold(`${logo} ${pkgName} bisect`)} [options]
${chalk.dim('Options:')}
-h, --help Output usage information
-d, --debug Debug mode [off]
--no-color No color mode [off]
-b, --bad Known bad URL
-g, --good Known good URL
-o, --open Automatically open each URL in the browser
-p, --path Subpath of the deployment URL to test
-r, --run Test script to run for each deployment
${chalk.dim('Examples:')}
${chalk.gray('')} Bisect the current project interactively
${chalk.cyan(`$ ${pkgName} bisect`)}
${chalk.gray('')} Bisect with a known bad deployment
${chalk.cyan(`$ ${pkgName} bisect --bad example-310pce9i0.vercel.app`)}
${chalk.gray('')} Automated bisect with a run script
${chalk.cyan(`$ ${pkgName} bisect --run ./test.sh`)}
`);
};
export default async function main(client: Client): Promise<number> {
export default async function bisect(client: Client): Promise<number> {
const { output } = client;
const scope = await getScope(client);
const { contextName } = scope;
@@ -75,7 +39,7 @@ export default async function main(client: Client): Promise<number> {
});
if (argv['--help']) {
help();
output.print(help(bisectCommand, { columns: client.stderr.columns }));
return 2;
}
@@ -206,44 +170,34 @@ export default async function main(client: Client): Promise<number> {
// Fetch all the project's "READY" deployments with the pagination API
let deployments: Deployment[] = [];
let next: number | undefined = badDeployment.createdAt + 1;
do {
const query = new URLSearchParams();
query.set('projectId', projectId);
if (badDeployment.target) {
query.set('target', badDeployment.target);
}
query.set('limit', '100');
query.set('state', 'READY');
if (next) {
query.set('until', String(next));
}
const res = await client.fetch<Deployments>(`/v6/deployments?${query}`, {
const query = new URLSearchParams();
query.set('projectId', projectId);
if (badDeployment.target) {
query.set('target', badDeployment.target);
}
query.set('state', 'READY');
query.set('until', String(badDeployment.createdAt + 1));
for await (const chunk of client.fetchPaginated<Deployments>(
`/v6/deployments?${query}`,
{
accountId: badDeployment.ownerId,
});
next = res.pagination.next;
let newDeployments = res.deployments;
}
)) {
let newDeployments = chunk.deployments;
// If we have the "good" deployment in this chunk, then we're done
for (let i = 0; i < newDeployments.length; i++) {
if (newDeployments[i].url === good) {
// grab all deployments up until the good one
newDeployments = newDeployments.slice(0, i);
next = undefined;
break;
}
}
deployments = deployments.concat(newDeployments);
if (next) {
// Small sleep to avoid rate limiting
await sleep(100);
}
} while (next);
}
if (!deployments.length) {
output.error(

View File

@@ -0,0 +1,46 @@
import { Command } from '../help';
import { getPkgName } from '../../util/pkg-name';
export const buildCommand: Command = {
name: 'build',
description: 'Build the project.',
arguments: [],
options: [
{
name: 'prod',
description: 'Build a production deployment',
shorthand: null,
type: 'string',
deprecated: false,
multi: false,
},
{
name: 'output',
description: 'Directory where built assets should be written to',
shorthand: null,
argument: 'PATH',
type: 'string',
deprecated: false,
multi: false,
},
{
name: 'yes',
description:
'Skip the confirmation prompt about pulling environment variables and project settings when not found locally',
shorthand: 'y',
type: 'boolean',
deprecated: false,
multi: false,
},
],
examples: [
{
name: 'Build the project',
value: `${getPkgName()} build`,
},
{
name: 'Build the project in a specific directory',
value: `${getPkgName()} build --cwd ./path-to-project`,
},
],
};

View File

@@ -1,7 +1,10 @@
import fs from 'fs-extra';
import chalk from 'chalk';
import dotenv from 'dotenv';
import semver from 'semver';
import minimatch from 'minimatch';
import { join, normalize, relative, resolve, sep } from 'path';
import frameworks from '@vercel/frameworks';
import {
getDiscontinuedNodeVersions,
normalizePath,
@@ -22,9 +25,9 @@ import {
import {
detectBuilders,
detectFrameworkRecord,
detectFrameworkVersion,
LocalFileSystemDetector,
} from '@vercel/fs-detectors';
import minimatch from 'minimatch';
import {
appendRoutesToPhase,
getTransformedRoutes,
@@ -35,39 +38,37 @@ import {
import { fileNameSymbol } from '@vercel/client';
import type { VercelConfig } from '@vercel/client';
import pull from './pull';
import { staticFiles as getFiles } from '../util/get-files';
import Client from '../util/client';
import getArgs from '../util/get-args';
import cmd from '../util/output/cmd';
import * as cli from '../util/pkg-name';
import cliPkg from '../util/pkg';
import readJSONFile from '../util/read-json-file';
import { CantParseJSONFile } from '../util/errors-ts';
import pull from '../pull';
import { staticFiles as getFiles } from '../../util/get-files';
import Client from '../../util/client';
import getArgs from '../../util/get-args';
import cmd from '../../util/output/cmd';
import * as cli from '../../util/pkg-name';
import cliPkg from '../../util/pkg';
import readJSONFile from '../../util/read-json-file';
import { CantParseJSONFile } from '../../util/errors-ts';
import {
pickOverrides,
ProjectLinkAndSettings,
readProjectSettings,
} from '../util/projects/project-settings';
import { VERCEL_DIR } from '../util/projects/link';
import confirm from '../util/input/confirm';
import { emoji, prependEmoji } from '../util/emoji';
import stamp from '../util/output/stamp';
} from '../../util/projects/project-settings';
import { getProjectLink, VERCEL_DIR } from '../../util/projects/link';
import confirm from '../../util/input/confirm';
import { emoji, prependEmoji } from '../../util/emoji';
import stamp from '../../util/output/stamp';
import {
OUTPUT_DIR,
PathOverride,
writeBuildResult,
} from '../util/build/write-build-result';
import { importBuilders } from '../util/build/import-builders';
import { initCorepack, cleanupCorepack } from '../util/build/corepack';
import { sortBuilders } from '../util/build/sort-builders';
import { toEnumerableError } from '../util/error';
import { validateConfig } from '../util/validate-config';
import { setMonorepoDefaultSettings } from '../util/build/monorepo';
import frameworks from '@vercel/frameworks';
import { detectFrameworkVersion } from '@vercel/fs-detectors';
import semver from 'semver';
} from '../../util/build/write-build-result';
import { importBuilders } from '../../util/build/import-builders';
import { initCorepack, cleanupCorepack } from '../../util/build/corepack';
import { sortBuilders } from '../../util/build/sort-builders';
import { toEnumerableError } from '../../util/error';
import { validateConfig } from '../../util/validate-config';
import { setMonorepoDefaultSettings } from '../../util/build/monorepo';
import { help } from '../help';
import { buildCommand } from './command';
type BuildResult = BuildResultV2 | BuildResultV3;
@@ -104,37 +105,9 @@ export interface BuildsManifest {
builds?: SerializedBuilder[];
}
const help = () => {
return console.log(`
${chalk.bold(`${cli.logo} ${cli.name} build`)}
${chalk.dim('Options:')}
-h, --help Output usage information
-A ${chalk.bold.underline('FILE')}, --local-config=${chalk.bold.underline(
'FILE'
)} Path to the local ${'`vercel.json`'} file
-Q ${chalk.bold.underline('DIR')}, --global-config=${chalk.bold.underline(
'DIR'
)} Path to the global ${'`.vercel`'} directory
--cwd [path] The current working directory
--output [path] Directory where built assets should be written to
--prod Build a production deployment
-d, --debug Debug mode [off]
--no-color No color mode [off]
-y, --yes Skip the confirmation prompt about pulling environment variables and project settings when not found locally
${chalk.dim('Examples:')}
${chalk.gray('-')} Build the project
${chalk.cyan(`$ ${cli.name} build`)}
${chalk.cyan(`$ ${cli.name} build --cwd ./path-to-project`)}
`);
};
export default async function main(client: Client): Promise<number> {
const { cwd, output } = client;
let { cwd } = client;
const { output } = client;
// Ensure that `vc build` is not being invoked recursively
if (process.env.__VERCEL_BUILD_RUNNING) {
@@ -162,7 +135,7 @@ export default async function main(client: Client): Promise<number> {
});
if (argv['--help']) {
help();
output.print(help(buildCommand, { columns: client.stderr.columns }));
return 2;
}
@@ -177,10 +150,18 @@ export default async function main(client: Client): Promise<number> {
return 1;
}
// If repo linked, update `cwd` to the repo root
const link = await getProjectLink(client, cwd);
const projectRootDirectory = link?.projectRootDirectory ?? '';
if (link?.repoRoot) {
cwd = client.cwd = link.repoRoot;
}
// TODO: read project settings from the API, fall back to local `project.json` if that fails
// Read project settings, and pull them from Vercel if necessary
let project = await readProjectSettings(join(cwd, VERCEL_DIR));
const vercelDir = join(cwd, projectRootDirectory, VERCEL_DIR);
let project = await readProjectSettings(vercelDir);
const isTTY = process.stdin.isTTY;
while (!project?.settings) {
let confirmed = yes;
@@ -207,6 +188,7 @@ export default async function main(client: Client): Promise<number> {
return 0;
}
const { argv: originalArgv } = client;
client.cwd = join(cwd, projectRootDirectory);
client.argv = [
...originalArgv.slice(0, 2),
'pull',
@@ -217,12 +199,13 @@ export default async function main(client: Client): Promise<number> {
if (result !== 0) {
return result;
}
client.cwd = cwd;
client.argv = originalArgv;
project = await readProjectSettings(join(cwd, VERCEL_DIR));
project = await readProjectSettings(vercelDir);
}
// Delete output directory from potential previous build
const defaultOutputDir = join(cwd, OUTPUT_DIR);
const defaultOutputDir = join(cwd, projectRootDirectory, OUTPUT_DIR);
const outputDir = argv['--output']
? resolve(argv['--output'])
: defaultOutputDir;
@@ -241,7 +224,12 @@ export default async function main(client: Client): Promise<number> {
const envToUnset = new Set<string>(['VERCEL', 'NOW_BUILDER']);
try {
const envPath = join(cwd, VERCEL_DIR, `.env.${target}.local`);
const envPath = join(
cwd,
projectRootDirectory,
VERCEL_DIR,
`.env.${target}.local`
);
// TODO (maybe?): load env vars from the API, fall back to the local file if that fails
const dotenvResult = dotenv.config({
path: envPath,

View File

@@ -1,50 +0,0 @@
import chalk from 'chalk';
import logo from '../../util/output/logo';
import { getPkgName } from '../../util/pkg-name';
export const help = () => {
return `
${chalk.bold(`${logo} ${getPkgName()} [deploy]`)} [path-to-project] [options]
--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:')}
${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('')} Deploy with prebuilt outputs
${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

@@ -0,0 +1,181 @@
import { Command } from '../help';
export const deployCommand: Command = {
name: 'deploy',
description:
'Deploy your project to Vercel. The `deploy` command is the default command for the Vercel CLI, and can be omitted (`vc deploy my-app` equals `vc my-app`).',
arguments: [
{
name: 'project-path',
required: false,
},
],
options: [
{
name: 'force',
shorthand: 'f',
type: 'boolean',
deprecated: false,
description: 'Force a new deployment even if nothing has changed',
multi: false,
},
{
name: 'with-cache',
shorthand: null,
type: 'boolean',
deprecated: false,
description: 'Retain build cache when using "--force"',
multi: false,
},
{
name: 'public',
shorthand: 'p',
type: 'boolean',
deprecated: false,
description: 'Deployment is public (`/_src`) is exposed)',
multi: false,
},
{
name: 'env',
shorthand: 'e',
type: 'string',
argument: 'key=value',
deprecated: false,
multi: true,
description:
'Specify environment variables during run-time (e.g. `-e KEY1=value1 -e KEY2=value2`)',
},
{
name: 'build-env',
shorthand: 'b',
type: 'string',
argument: 'key=value',
deprecated: false,
multi: true,
description:
'Specify environment variables during build-time (e.g. `-b KEY1=value1 -b KEY2=value2`)',
},
{
name: 'meta',
shorthand: 'm',
type: 'string',
argument: 'key=value',
deprecated: false,
multi: true,
description:
'Specify metadata for the deployment (e.g. `-m KEY1=value1 -m KEY2=value2`)',
},
{
name: 'regions',
shorthand: null,
type: 'string',
deprecated: false,
description: 'Set default regions to enable the deployment on',
multi: false,
},
{
name: 'prebuilt',
shorthand: null,
type: 'boolean',
deprecated: false,
description:
'Use in combination with `vc build`. Deploy an existing build',
multi: false,
},
{
name: 'prod',
shorthand: null,
type: 'boolean',
deprecated: false,
description: 'Create a production deployment',
multi: false,
},
{
name: 'archive',
shorthand: null,
type: 'string',
deprecated: false,
description:
'Compress the deployment code into a file before uploading it',
multi: false,
},
{
name: 'no-wait',
shorthand: null,
type: 'boolean',
deprecated: false,
description: "Don't wait for the deployment to finish",
multi: false,
},
{
name: 'skip-domain',
shorthand: null,
type: 'boolean',
deprecated: false,
description: undefined,
multi: false,
},
{
name: 'yes',
shorthand: 'y',
type: 'boolean',
deprecated: false,
description: 'Use default options to skip all prompts',
multi: false,
},
{
name: 'name',
shorthand: 'n',
type: 'string',
deprecated: true,
description: 'Provide a Vercel Project name',
multi: false,
},
{
name: 'no-clipboard',
shorthand: null,
type: 'boolean',
deprecated: true,
description: 'Do not copy deployment URL to clipboard',
multi: false,
},
{
name: 'target',
shorthand: null,
type: 'string',
deprecated: true,
description: 'Specify the target deployment environment',
multi: false,
},
{
name: 'confirm',
shorthand: 'c',
type: 'boolean',
deprecated: true,
description: 'Use default options to skip all prompts',
multi: false,
},
],
examples: [
{
name: 'Deploy the current directory',
value: 'vercel',
},
{
name: 'Deploy a custom path',
value: 'vercel /usr/src/project',
},
{
name: 'Deploy with run-time Environment Variables',
value: 'vercel -e NODE_ENV=production',
},
{
name: 'Deploy with prebuilt outputs',
value: ['vercel build', 'vercel deploy --prebuilt'],
},
{
name: 'Write Deployment URL to a file',
value: 'vercel > deployment-url.txt',
},
],
};

View File

@@ -59,7 +59,6 @@ import validatePaths, {
} from '../../util/validate-paths';
import { getCommandName } from '../../util/pkg-name';
import { Output } from '../../util/output';
import { help } from './args';
import { getDeploymentChecks } from '../../util/deploy/get-deployment-checks';
import parseTarget from '../../util/deploy/parse-target';
import getPrebuiltJson from '../../util/deploy/get-prebuilt-json';
@@ -69,44 +68,38 @@ import { parseEnv } from '../../util/parse-env';
import { errorToString, isErrnoException, isError } from '@vercel/error-utils';
import { pickOverrides } from '../../util/projects/project-settings';
import { printDeploymentStatus } from '../../util/deploy/print-deployment-status';
import { help } from '../help';
import { deployCommand } from './command';
export default async (client: Client): Promise<number> => {
const { output } = client;
let argv = null;
try {
argv = getArgs(client.argv.slice(2), {
'--force': Boolean,
'--with-cache': Boolean,
'--public': Boolean,
'--env': [String],
'--build-env': [String],
'--meta': [String],
// This is not an array in favor of matching
// the config property name.
'--regions': String,
'--prebuilt': Boolean,
'--prod': Boolean,
'--archive': String,
'--no-wait': Boolean,
'--skip-domain': Boolean,
'--yes': Boolean,
'-f': '--force',
'-p': '--public',
'-e': '--env',
'-b': '--build-env',
'-m': '--meta',
'-y': '--yes',
const argOptions: {
[k: string]:
| BooleanConstructor
| StringConstructor
| string
| [StringConstructor];
} = {};
for (const option of deployCommand.options) {
argOptions[`--${option.name}`] =
option.type === 'boolean' ? Boolean : String;
if (option.shorthand) {
argOptions[`-${option.shorthand}`] = `--${option.name}`;
}
if (
option.name === 'env' ||
option.name === 'build-env' ||
option.name === 'meta'
) {
argOptions[`--${option.name}`] = [String];
}
}
// deprecated
'--name': String,
'-n': '--name',
'--no-clipboard': Boolean,
'--target': String,
'--confirm': Boolean,
'-c': '--confirm',
});
try {
argv = getArgs(client.argv.slice(2), argOptions);
if ('--confirm' in argv) {
output.warn('`--confirm` is deprecated, please use `--yes` instead');
@@ -118,7 +111,7 @@ export default async (client: Client): Promise<number> => {
}
if (argv['--help']) {
output.print(help());
output.print(help(deployCommand, { columns: client.stderr.columns }));
return 2;
}
@@ -203,56 +196,6 @@ export default async (client: Client): Promise<number> => {
return target;
}
// build `--prebuilt`
if (argv['--prebuilt']) {
const prebuiltExists = await fs.pathExists(join(cwd, '.vercel/output'));
if (!prebuiltExists) {
error(
`The ${param(
'--prebuilt'
)} option was used, but no prebuilt output found in ".vercel/output". Run ${getCommandName(
'build'
)} to generate a local build.`
);
return 1;
}
const prebuiltBuild = await getPrebuiltJson(cwd);
// Ensure that there was not a build error
const prebuiltError =
prebuiltBuild?.error ||
prebuiltBuild?.builds?.find(build => 'error' in build)?.error;
if (prebuiltError) {
output.log(
`Prebuilt deployment cannot be created because ${getCommandName(
'build'
)} failed with error:\n`
);
prettyError(prebuiltError);
return 1;
}
// Ensure that the deploy target matches the build target
const assumedTarget = target || 'preview';
if (prebuiltBuild?.target && prebuiltBuild.target !== assumedTarget) {
let specifyTarget = '';
if (prebuiltBuild.target === 'production') {
specifyTarget = ` --prod`;
}
prettyError({
message: `The ${param(
'--prebuilt'
)} option was used with the target environment "${assumedTarget}", but the prebuilt output found in ".vercel/output" was built with target environment "${
prebuiltBuild.target
}". Please run ${getCommandName(`--prebuilt${specifyTarget}`)}.`,
link: 'https://vercel.link/prebuilt-environment-mismatch',
});
return 1;
}
}
const archive = argv['--archive'];
if (typeof archive === 'string' && !isValidArchive(archive)) {
output.error(`Format must be one of: ${VALID_ARCHIVE_FORMATS.join(', ')}`);
@@ -355,6 +298,66 @@ export default async (client: Client): Promise<number> => {
throw new Error(`"org" is not defined`);
}
// build `--prebuilt`
if (argv['--prebuilt']) {
// For repo-style linking, update `cwd` to be the Project
// subdirectory when `rootDirectory` setting is defined
if (
link.status === 'linked' &&
link.repoRoot &&
link.project.rootDirectory
) {
cwd = join(cwd, link.project.rootDirectory);
}
const prebuiltExists = await fs.pathExists(join(cwd, '.vercel/output'));
if (!prebuiltExists) {
error(
`The ${param(
'--prebuilt'
)} option was used, but no prebuilt output found in ".vercel/output". Run ${getCommandName(
'build'
)} to generate a local build.`
);
return 1;
}
const prebuiltBuild = await getPrebuiltJson(cwd);
// Ensure that there was not a build error
const prebuiltError =
prebuiltBuild?.error ||
prebuiltBuild?.builds?.find(build => 'error' in build)?.error;
if (prebuiltError) {
output.log(
`Prebuilt deployment cannot be created because ${getCommandName(
'build'
)} failed with error:\n`
);
prettyError(prebuiltError);
return 1;
}
// Ensure that the deploy target matches the build target
const assumedTarget = target || 'preview';
if (prebuiltBuild?.target && prebuiltBuild.target !== assumedTarget) {
let specifyTarget = '';
if (prebuiltBuild.target === 'production') {
specifyTarget = ` --prod`;
}
prettyError({
message: `The ${param(
'--prebuilt'
)} option was used with the target environment "${assumedTarget}", but the prebuilt output found in ".vercel/output" was built with target environment "${
prebuiltBuild.target
}". Please run ${getCommandName(`--prebuilt${specifyTarget}`)}.`,
link: 'https://vercel.link/prebuilt-environment-mismatch',
});
return 1;
}
}
// Set the `contextName` and `currentTeam` as specified by the
// Project Settings, so that API calls happen with the proper scope
const contextName = org.slug;
@@ -511,7 +514,8 @@ export default async (client: Client): Promise<number> => {
}
try {
const autoAssignCustomDomains = !argv['--skip-domain'];
// if this flag is not set, use `undefined` to allow the project setting to be used
const autoAssignCustomDomains = argv['--skip-domain'] ? false : undefined;
const createArgs: CreateOptions = {
name,

View File

@@ -57,8 +57,15 @@ export default async function dev(
let projectSettings: ProjectSettings | undefined;
let envValues: Record<string, string> = {};
let repoRoot: string | undefined;
if (link.status === 'linked') {
const { project, org } = link;
// If repo linked, update `cwd` to the repo root
if (link.repoRoot) {
repoRoot = cwd = link.repoRoot;
}
client.config.currentTeam = org.type === 'team' ? org.id : undefined;
projectSettings = project;
@@ -76,6 +83,7 @@ export default async function dev(
output,
projectSettings,
envValues,
repoRoot,
});
// listen to SIGTERM for graceful shutdown

View File

@@ -167,6 +167,7 @@ export default async function main(client: Client) {
case 'pull':
return pull(
client,
link,
project,
target,
argv,

View File

@@ -2,7 +2,11 @@ import chalk from 'chalk';
import { outputFile } from 'fs-extra';
import { closeSync, openSync, readSync } from 'fs';
import { resolve } from 'path';
import type { Project, ProjectEnvTarget } from '@vercel-internals/types';
import type {
Project,
ProjectEnvTarget,
ProjectLinked,
} from '@vercel-internals/types';
import Client from '../../util/client';
import { emoji, prependEmoji } from '../../util/emoji';
import confirm from '../../util/input/confirm';
@@ -19,6 +23,7 @@ import {
createEnvObject,
} from '../../util/env/diff-env-files';
import { isErrnoException } from '@vercel/error-utils';
import { addToGitIgnore } from '../../util/link/add-to-gitignore';
const CONTENTS_PREFIX = '# Created by Vercel CLI\n';
@@ -51,6 +56,7 @@ function tryReadHeadSync(path: string, length: number) {
export default async function pull(
client: Client,
link: ProjectLinked,
project: Project,
environment: ProjectEnvTarget,
opts: Partial<Options>,
@@ -136,11 +142,22 @@ export default async function pull(
output.log('No changes found.');
}
let isGitIgnoreUpdated = false;
if (filename === '.env.local') {
// When the file is `.env.local`, we also add it to `.gitignore`
// to avoid accidentally committing it to git.
// We use '.env*.local' to match the default .gitignore from
// create-next-app template. See:
// https://github.com/vercel/next.js/blob/06abd634899095b6cc28e6e8315b1e8b9c8df939/packages/create-next-app/templates/app/js/gitignore#L28
const rootPath = link.repoRoot ?? cwd;
isGitIgnoreUpdated = await addToGitIgnore(rootPath, '.env*.local');
}
output.print(
`${prependEmoji(
`${exists ? 'Updated' : 'Created'} ${chalk.bold(
filename
)} file ${chalk.gray(pullStamp())}`,
`${exists ? 'Updated' : 'Created'} ${chalk.bold(filename)} file ${
isGitIgnoreUpdated ? 'and added it to .gitignore' : ''
} ${chalk.gray(pullStamp())}`,
emoji('success')
)}\n`
);

View File

@@ -0,0 +1,320 @@
import chalk from 'chalk';
import stripAnsi from 'strip-ansi';
import { LOGO, NAME } from '@vercel-internals/constants';
const INDENT = ' '.repeat(2);
const NEWLINE = '\n';
export interface CommandOption {
name: string;
shorthand: string | null;
type: 'boolean' | 'string';
argument?: string;
deprecated: boolean;
description?: string;
multi: boolean;
}
export interface CommandArgument {
name: string;
required: boolean;
}
export interface CommandExample {
name: string;
value: string | string[];
}
export interface Command {
name: string;
description: string;
arguments: CommandArgument[];
options: CommandOption[];
examples: CommandExample[];
}
const globalCommandOptions: CommandOption[] = [
{
name: 'help',
shorthand: 'h',
type: 'string',
description: 'Output usage information',
deprecated: false,
multi: false,
},
{
name: 'version',
shorthand: 'v',
type: 'string',
description: 'Output the version number',
deprecated: false,
multi: false,
},
{
name: 'cwd',
shorthand: null,
type: 'string',
argument: 'DIR',
description:
'Sets the current working directory for a single run of a command',
deprecated: false,
multi: false,
},
{
name: 'local-config',
shorthand: 'A',
type: 'string',
argument: 'FILE',
description: 'Path to the local `vercel.json` file',
deprecated: false,
multi: false,
},
{
name: 'global-config',
shorthand: 'Q',
type: 'string',
argument: 'DIR',
description: 'Path to the global `.vercel` directory',
deprecated: false,
multi: false,
},
{
name: 'debug',
shorthand: 'd',
type: 'string',
description: 'Debug mode (default off)',
deprecated: false,
multi: false,
},
{
name: 'no-color',
shorthand: null,
type: 'string',
description: 'No color mode (default off)',
deprecated: false,
multi: false,
},
{
name: 'scope',
shorthand: 'S',
type: 'string',
description: 'Set a custom scope',
deprecated: false,
multi: false,
},
{
name: 'token',
shorthand: 't',
type: 'string',
argument: 'TOKEN',
description: 'Login token',
deprecated: false,
multi: false,
},
];
export function calcLineLength(line: string[]) {
return stripAnsi(lineToString(line)).length;
}
// Insert spaces in between non-whitespace items only
export function lineToString(line: string[]) {
let string = '';
for (let i = 0; i < line.length; i++) {
if (i === line.length - 1) {
string += line[i];
} else {
const curr = line[i];
const next = line[i + 1];
string += curr;
if (curr.trim() !== '' && next.trim() !== '') {
string += ' ';
}
}
}
return string;
}
export function outputArrayToString(outputArray: (string | null)[]) {
return outputArray.filter(line => line !== null).join(NEWLINE);
}
/**
* Example: `▲ vercel deploy [path] [options]`
* @param command
* @returns
*/
export function buildCommandSynopsisLine(command: Command) {
const line: string[] = [
INDENT,
LOGO,
chalk.bold(NAME),
chalk.bold(command.name),
];
if (command.arguments.length > 0) {
for (const argument of command.arguments) {
line.push(argument.required ? argument.name : `[${argument.name}]`);
}
}
if (command.options.length > 0) {
line.push('[options]');
}
line.push(NEWLINE);
return lineToString(line);
}
export function buildCommandOptionLines(
commandOptions: CommandOption[],
options: BuildHelpOutputOptions,
sectionTitle: String
) {
// Filter out deprecated and intentionally undocumented options
commandOptions = commandOptions.filter(
option => !option.deprecated && option.description !== undefined
);
if (commandOptions.length === 0) {
return null;
}
// Initialize output array with header and empty line
const outputArray: string[] = [`${INDENT}${chalk.dim(sectionTitle)}:`, ''];
// Start building option lines
const optionLines: string[][] = [];
// Sort command options alphabetically
commandOptions.sort((a, b) =>
a.name < b.name ? -1 : a.name > b.name ? 1 : 0
);
// Keep track of longest "start" of an option line to determine description spacing
let maxLineStartLength = 0;
// Iterate over options and create the "start" of each option (e.g. ` -b, --build-env <key=value>`)
for (const option of commandOptions) {
const startLine: string[] = [INDENT, INDENT, INDENT];
if (option.shorthand) {
startLine.push(`-${option.shorthand},`);
}
startLine.push(`--${option.name}`);
if (option.argument) {
startLine.push(`<${option.argument}>`);
}
// the length includes the INDENT
const lineLength = calcLineLength(startLine);
maxLineStartLength = Math.max(lineLength, maxLineStartLength);
optionLines.push(startLine);
}
/*
* Iterate over in-progress option lines to add space-filler and description
* For Example:
* | --archive My description starts here.
* |
* | -b, --build-env <key=value> Start of description here then
* | it wraps here.
* |
* | -e, --env <key=value> My description is short.
*
* Breaking down option lines:
* | -b, --build-env <key=value> Start of description here then
* |[][ ][][ ]
* |↑ ↑ ↑ ↑
* |1 2 3 4
* | it wraps here.
* |[][ ][ ]
* |↑ ↑ ↑
* |5 6 7
* | 1, 5 = indent
* | 2 = start
* | 3, 6 = space-filler
* | 4, 7 = description
*/
for (let i = 0; i < optionLines.length; i++) {
const optionLine = optionLines[i];
const option = commandOptions[i];
// Add only 2 spaces to the longest line, and then make all shorter lines the same length.
optionLine.push(
' '.repeat(2 + (maxLineStartLength - calcLineLength(optionLine)))
);
// Descriptions may be longer than max line length. Wrap them to the same column as the first description line
const lines: string[][] = [optionLine];
if (option.description) {
for (const descriptionWord of option.description.split(' ')) {
// insert a new line when the next word would match or exceed the maximum line length
if (
calcLineLength(lines[lines.length - 1]) +
stripAnsi(descriptionWord).length >=
options.columns
) {
// initialize the new line with the necessary whitespace. The INDENT is apart of `maxLineStartLength`
lines.push([' '.repeat(maxLineStartLength + 2)]);
}
// insert the word to the current last line
lines[lines.length - 1].push(descriptionWord);
}
}
// for every line, transform into a string and push it to the output
for (const line of lines) {
outputArray.push(lineToString(line));
}
}
return `${outputArrayToString(outputArray)}${NEWLINE}`;
}
export function buildCommandExampleLines(command: Command) {
const outputArray: string[] = [`${INDENT}${chalk.dim('Examples:')}`, ''];
for (const example of command.examples) {
const nameLine: string[] = [INDENT];
nameLine.push(chalk.gray('-'));
nameLine.push(example.name);
outputArray.push(lineToString(nameLine));
outputArray.push('');
const buildValueLine = (value: string) => {
return lineToString([INDENT, INDENT, chalk.cyan(`$ ${value}`)]);
};
if (Array.isArray(example.value)) {
for (const line of example.value) {
outputArray.push(buildValueLine(line));
}
} else {
outputArray.push(buildValueLine(example.value));
}
outputArray.push('');
}
return outputArrayToString(outputArray);
}
function buildDescriptionLine(command: Command) {
const line: string[] = [INDENT, command.description, NEWLINE];
return lineToString(line);
}
interface BuildHelpOutputOptions {
columns: number;
}
export function buildHelpOutput(
command: Command,
options: BuildHelpOutputOptions
) {
const outputArray: (string | null)[] = [
'',
buildCommandSynopsisLine(command),
buildDescriptionLine(command),
buildCommandOptionLines(command.options, options, 'Options'),
buildCommandOptionLines(globalCommandOptions, options, 'Global Options'),
buildCommandExampleLines(command),
'',
];
return outputArrayToString(outputArray);
}
export interface HelpOptions {
columns?: number;
}
export function help(command: Command, options: HelpOptions) {
return buildHelpOutput(command, {
columns: options.columns ?? 80,
});
}

View File

@@ -0,0 +1,50 @@
import { Command } from '../help';
import { getPkgName } from '../../util/pkg-name';
export const inspectCommand: Command = {
name: 'inspect',
description: 'Show information about a deployment.',
arguments: [
{
name: 'url',
required: true,
},
],
options: [
{
name: 'timeout',
description: 'Time to wait for deployment completion [3m]',
argument: 'TIME',
shorthand: null,
type: 'string',
deprecated: false,
multi: false,
},
{
name: 'wait',
description: 'Blocks until deployment completes',
shorthand: null,
type: 'string',
deprecated: false,
multi: false,
},
],
examples: [
{
name: 'Get information about a deployment by its unique URL',
value: `${getPkgName()} inspect my-deployment-ji2fjij2.vercel.app`,
},
{
name: 'Get information about the deployment an alias points to',
value: `${getPkgName()} inspect my-deployment.vercel.app`,
},
{
name: 'Get information about a deployment by piping in the URL',
value: `echo my-deployment.vercel.app | ${getPkgName()} inspect`,
},
{
name: 'Wait up to 90 seconds for deployment to complete',
value: `${getPkgName()} inspect my-deployment.vercel.app --wait --timeout 90s`,
},
],
};

View File

@@ -1,71 +1,27 @@
import chalk from 'chalk';
import getArgs from '../util/get-args';
import buildsList from '../util/output/builds';
import routesList from '../util/output/routes';
import indent from '../util/output/indent';
import logo from '../util/output/logo';
import elapsed from '../util/output/elapsed';
import { handleError } from '../util/error';
import getScope from '../util/get-scope';
import { getPkgName, getCommandName } from '../util/pkg-name';
import Client from '../util/client';
import getDeployment from '../util/get-deployment';
import getArgs from '../../util/get-args';
import buildsList from '../../util/output/builds';
import routesList from '../../util/output/routes';
import indent from '../../util/output/indent';
import elapsed from '../../util/output/elapsed';
import { handleError } from '../../util/error';
import getScope from '../../util/get-scope';
import { getCommandName } from '../../util/pkg-name';
import Client from '../../util/client';
import getDeployment from '../../util/get-deployment';
import type { Build, Deployment } from '@vercel-internals/types';
import title from 'title';
import { isErrnoException } from '@vercel/error-utils';
import { URL } from 'url';
import readStandardInput from '../util/input/read-standard-input';
import sleep from '../util/sleep';
import readStandardInput from '../../util/input/read-standard-input';
import sleep from '../../util/sleep';
import ms from 'ms';
import { isDeploying } from '../util/deploy/is-deploying';
import { isDeploying } from '../../util/deploy/is-deploying';
import { help } from '../help';
import { inspectCommand } from './command';
const help = () => {
console.log(`
${chalk.bold(`${logo} ${getPkgName()} inspect`)} <url>
${chalk.dim('Options:')}
-h, --help Output usage information
-A ${chalk.bold.underline('FILE')}, --local-config=${chalk.bold.underline(
'FILE'
)} Path to the local ${'`vercel.json`'} file
-Q ${chalk.bold.underline('DIR')}, --global-config=${chalk.bold.underline(
'DIR'
)} Path to the global ${'`.vercel`'} directory
-t ${chalk.bold.underline('TOKEN')}, --token=${chalk.bold.underline(
'TOKEN'
)} Login token
-d, --debug Debug mode [off]
--no-color No color mode [off]
-S, --scope Set a custom scope
--timeout=${chalk.bold.underline(
'TIME'
)} Time to wait for deployment completion [3m]
--wait Blocks until deployment completes
${chalk.dim('Examples:')}
${chalk.gray('')} Get information about a deployment by its unique URL
${chalk.cyan(`$ ${getPkgName()} inspect my-deployment-ji2fjij2.vercel.app`)}
${chalk.gray('-')} Get information about the deployment an alias points to
${chalk.cyan(`$ ${getPkgName()} inspect my-deployment.vercel.app`)}
${chalk.gray('-')} Get information about a deployment by piping in the URL
${chalk.cyan(`$ echo my-deployment.vercel.app | ${getPkgName()} inspect`)}
${chalk.gray('-')} Wait up to 90 seconds for deployment to complete
${chalk.cyan(
`$ ${getPkgName()} inspect my-deployment.vercel.app --wait --timeout 90s`
)}
`);
};
export default async function main(client: Client) {
export default async function inspect(client: Client) {
const { output } = client;
let argv;
try {
@@ -79,7 +35,7 @@ export default async function main(client: Client) {
}
if (argv['--help']) {
help();
output.print(help(inspectCommand, { columns: client.stderr.columns }));
return 2;
}
@@ -99,7 +55,7 @@ export default async function main(client: Client) {
if (!deploymentIdOrHost) {
error(`${getCommandName('inspect <url>')} expects exactly one argument`);
help();
output.print(help(inspectCommand, { columns: client.stderr.columns }));
return 1;
}

View File

@@ -0,0 +1,54 @@
import { Command } from '../help';
import { getPkgName } from '../../util/pkg-name';
export const linkCommand: Command = {
name: 'link',
description: 'Link a local directory to a Vercel Project.',
arguments: [],
options: [
{
name: 'repo',
description: 'Link multiple projects based on Git repository (alpha)',
shorthand: 'r',
type: 'string',
deprecated: false,
multi: false,
},
{
name: 'project',
description: 'Specify a project name',
shorthand: 'p',
argument: 'NAME',
type: 'string',
deprecated: false,
multi: false,
},
{
name: 'yes',
description:
'Skip questions when setting up new project using default scope and settings',
shorthand: 'y',
type: 'boolean',
deprecated: false,
multi: false,
},
],
examples: [
{
name: 'Link current directory to a Vercel Project',
value: `${getPkgName()} link`,
},
{
name: 'Link current directory with default options and skip questions',
value: `${getPkgName()} link --yes`,
},
{
name: 'Link a specific directory to a Vercel Project',
value: `${getPkgName()} link --cwd /path/to/project`,
},
{
name: 'Link to the current Git repository, allowing for multiple Vercel Projects to be linked simultaneously (useful for monorepos)',
value: `${getPkgName()} link --repo`,
},
],
};

View File

@@ -1,62 +1,12 @@
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';
import { help } from '../help';
import { linkCommand } from './command';
const help = () => {
console.log(`
${chalk.bold(`${logo} ${getPkgName()} link`)} [options]
${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
-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]
-t ${chalk.bold.underline('TOKEN')}, --token=${chalk.bold.underline(
'TOKEN'
)} Login token
-p ${chalk.bold.underline('NAME')}, --project=${chalk.bold.underline(
'NAME'
)} Project name
-y, --yes Skip questions when setting up new project using default scope and settings
${chalk.dim('Examples:')}
${chalk.gray('')} Link current directory to a Vercel Project
${chalk.cyan(`$ ${getPkgName()} link`)}
${chalk.gray(
''
)} Link current directory with default options and skip questions
${chalk.cyan(`$ ${getPkgName()} link --yes`)}
${chalk.gray('')} Link a specific directory to a Vercel 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`)}
`);
};
export default async function main(client: Client) {
export default async function link(client: Client) {
const argv = getArgs(client.argv.slice(2), {
'--yes': Boolean,
'-y': '--yes',
@@ -71,7 +21,7 @@ export default async function main(client: Client) {
});
if (argv['--help']) {
help();
client.output.print(help(linkCommand, { columns: client.stderr.columns }));
return 2;
}
@@ -97,7 +47,7 @@ export default async function main(client: Client) {
client.output.warn(
`The ${cmd('--repo')} flag is in alpha, please report issues`
);
await ensureRepoLink(client, cwd, yes);
await ensureRepoLink(client, cwd, { yes, overwrite: true });
} else {
const link = await ensureLink('link', client, cwd, {
autoConfirm: yes,

View File

@@ -0,0 +1,61 @@
import { Command } from '../help';
import { getPkgName } from '../../util/pkg-name';
export const listCommand: Command = {
name: 'list',
description: 'List app deployments for an app.',
arguments: [
{
name: 'app',
required: false,
},
],
options: [
{
name: 'meta',
description:
'Filter deployments by metadata (e.g.: `-m KEY=value`). Can appear many times.',
argument: 'KEY=value',
shorthand: null,
type: 'string',
deprecated: false,
multi: true,
},
{
name: 'environment',
description: '',
argument: 'production|preview',
shorthand: null,
type: 'string',
deprecated: false,
multi: false,
},
{
name: 'next',
description: 'Show next page of results',
argument: 'MS',
shorthand: 'n',
type: 'string',
deprecated: false,
multi: false,
},
],
examples: [
{
name: 'List all deployments for the currently linked project',
value: `${getPkgName()} list`,
},
{
name: 'List all deployments for the project `my-app` in the team of the currently linked project',
value: `${getPkgName()} list my-app`,
},
{
name: 'Filter deployments by metadata',
value: `${getPkgName()} list -m key1=value1 -m key2=value2`,
},
{
name: 'Paginate deployments for a project, where `1584722256178` is the time in milliseconds since the UNIX epoch',
value: `${getPkgName()} list my-app --next 1584722256178`,
},
],
};

View File

@@ -2,85 +2,37 @@ import chalk from 'chalk';
import ms from 'ms';
import table from 'text-table';
import title from 'title';
import Now from '../util';
import getArgs from '../util/get-args';
import { handleError } from '../util/error';
import logo from '../util/output/logo';
import elapsed from '../util/output/elapsed';
import strlen from '../util/strlen';
import toHost from '../util/to-host';
import parseMeta from '../util/parse-meta';
import { isValidName } from '../util/is-valid-name';
import getCommandFlags from '../util/get-command-flags';
import { getPkgName, getCommandName } from '../util/pkg-name';
import Client from '../util/client';
import Now from '../../util';
import getArgs from '../../util/get-args';
import { handleError } from '../../util/error';
import elapsed from '../../util/output/elapsed';
import strlen from '../../util/strlen';
import toHost from '../../util/to-host';
import parseMeta from '../../util/parse-meta';
import { isValidName } from '../../util/is-valid-name';
import getCommandFlags from '../../util/get-command-flags';
import { getCommandName } from '../../util/pkg-name';
import Client from '../../util/client';
import { Deployment } from '@vercel/client';
import { getLinkedProject } from '../util/projects/link';
import { ensureLink } from '../util/link/ensure-link';
import getScope from '../util/get-scope';
import { isAPIError } from '../util/errors-ts';
import { getLinkedProject } from '../../util/projects/link';
import { ensureLink } from '../../util/link/ensure-link';
import getScope from '../../util/get-scope';
import { isAPIError } from '../../util/errors-ts';
import { isErrnoException } from '@vercel/error-utils';
import { help } from '../help';
import { listCommand } from './command';
const help = () => {
console.log(`
${chalk.bold(`${logo} ${getPkgName()} list`)} [app]
${chalk.dim('Options:')}
-h, --help Output usage information
-A ${chalk.bold.underline('FILE')}, --local-config=${chalk.bold.underline(
'FILE'
)} Path to the local ${'`vercel.json`'} file
-Q ${chalk.bold.underline('DIR')}, --global-config=${chalk.bold.underline(
'DIR'
)} Path to the global ${'`.vercel`'} directory
-d, --debug Debug mode [off]
--no-color No color mode [off]
-y, --yes Skip questions when setting up new project using default scope and settings
-t ${chalk.bold.underline('TOKEN')}, --token=${chalk.bold.underline(
'TOKEN'
)} Login token
-S, --scope Set a custom scope
-m, --meta Filter deployments by metadata (e.g.: ${chalk.dim(
'`-m KEY=value`'
)}). Can appear many times.
--prod Filter for production URLs
-N, --next Show next page of results
${chalk.dim('Examples:')}
${chalk.gray('')} List all deployments for the currently linked project
${chalk.cyan(`$ ${getPkgName()} ls`)}
${chalk.gray('')} List all deployments for the project ${chalk.dim(
'`my-app`'
)} in the team of the currently linked project
${chalk.cyan(`$ ${getPkgName()} ls my-app`)}
${chalk.gray('')} Filter deployments by metadata
${chalk.cyan(`$ ${getPkgName()} ls -m key1=value1 -m key2=value2`)}
${chalk.gray('')} Paginate deployments for a project, where ${chalk.dim(
'`1584722256178`'
)} is the time in milliseconds since the UNIX epoch.
${chalk.cyan(`$ ${getPkgName()} ls my-app --next 1584722256178`)}
`);
};
export default async function main(client: Client) {
export default async function list(client: Client) {
let argv;
try {
argv = getArgs(client.argv.slice(2), {
'--environment': String,
'--meta': [String],
'-m': '--meta',
'--next': Number,
'-N': '--next',
'--prod': Boolean,
'--prod': Boolean, // this can be deprecated someday
'--yes': Boolean,
'-y': '--yes',
@@ -108,14 +60,19 @@ export default async function main(client: Client) {
}
if (argv['--help']) {
help();
output.print(help(listCommand, { columns: client.stderr.columns }));
return 2;
}
const autoConfirm = !!argv['--yes'];
const prod = argv['--prod'] || false;
const meta = parseMeta(argv['--meta']);
const target = argv['--prod']
? 'production'
: typeof argv['--environment'] === 'string'
? argv['--environment'].toLowerCase()
: undefined;
// retrieve `project` and `org` from .vercel
let link = await getLinkedProject(client, cwd);
@@ -219,15 +176,12 @@ export default async function main(client: Client) {
debug('Fetching deployments');
const response = await now.list(
app,
{
version: 6,
meta,
nextTimestamp,
},
prod
);
const response = await now.list(app, {
version: 6,
meta,
nextTimestamp,
target,
});
let {
deployments,
@@ -280,9 +234,11 @@ export default async function main(client: Client) {
}
log(
`${prod ? `Production deployments` : `Deployments`} for ${chalk.bold(
app
)} under ${chalk.bold(contextName)} ${elapsed(Date.now() - start)}`
`${
target === 'production' ? `Production deployments` : `Deployments`
} for ${chalk.bold(app)} under ${chalk.bold(contextName)} ${elapsed(
Date.now() - start
)}`
);
// information to help the user find other deployments or instances
@@ -292,8 +248,9 @@ export default async function main(client: Client) {
print('\n');
const headers = ['Age', 'Deployment', 'Status', 'Duration'];
const headers = ['Age', 'Deployment', 'Status', 'Environment', 'Duration'];
if (showUsername) headers.push('Username');
const urls: string[] = [];
client.output.print(
`${table(
@@ -301,18 +258,17 @@ export default async function main(client: Client) {
headers.map(header => chalk.bold(chalk.cyan(header))),
...deployments
.sort(sortRecent())
.map(dep => [
[
.map(dep => {
urls.push(`https://${dep.url}`);
return [
chalk.gray(ms(Date.now() - dep.createdAt)),
`https://${dep.url}`,
stateString(dep.state || ''),
dep.target === 'production' ? 'Production' : 'Preview',
chalk.gray(getDeploymentDuration(dep)),
showUsername ? chalk.gray(dep.creator?.username) : '',
],
])
// flatten since the previous step returns a nested
// array of the deployment and (optionally) its instances
.flat()
];
})
.filter(app =>
// if an app wasn't supplied to filter by,
// we only want to render one deployment per app
@@ -327,6 +283,11 @@ export default async function main(client: Client) {
).replace(/^/gm, ' ')}\n\n`
);
if (!client.stdout.isTTY) {
client.stdout.write(urls.join('\n'));
client.stdout.write('\n');
}
if (pagination && pagination.count === 20) {
const flags = getCommandFlags(argv, ['_', '--next']);
log(

View File

@@ -0,0 +1,49 @@
import { Command } from '../help';
import { getPkgName } from '../../util/pkg-name';
export const loginCommand: Command = {
name: 'login',
description: 'Authenticate using your email or team id.',
arguments: [
{
name: 'email or team id',
required: false,
},
],
options: [
{
name: 'github',
description: 'Log in with GitHub',
shorthand: null,
type: 'string',
deprecated: false,
multi: false,
},
{
name: 'oob',
description: 'Log in with "out of band" authentication',
shorthand: null,
type: 'string',
deprecated: false,
multi: false,
},
],
examples: [
{
name: 'Log into the Vercel platform',
value: `${getPkgName()} login`,
},
{
name: 'Log in using a specific email address',
value: `${getPkgName()} login username@example.com`,
},
{
name: 'Log in using a specific team "slug" for SAML Single Sign-On',
value: `${getPkgName()} login acme`,
},
{
name: 'Log in using GitHub in "out-of-band" mode',
value: `${getPkgName()} login --github --oob`,
},
],
};

View File

@@ -1,55 +1,24 @@
import { validate as validateEmail } from 'email-validator';
import chalk from 'chalk';
import hp from '../util/humanize-path';
import getArgs from '../util/get-args';
import logo from '../util/output/logo';
import prompt from '../util/login/prompt';
import doSamlLogin from '../util/login/saml';
import doEmailLogin from '../util/login/email';
import doGithubLogin from '../util/login/github';
import doGitlabLogin from '../util/login/gitlab';
import doBitbucketLogin from '../util/login/bitbucket';
import { prependEmoji, emoji } from '../util/emoji';
import { getCommandName, getPkgName } from '../util/pkg-name';
import getGlobalPathConfig from '../util/config/global-path';
import { writeToAuthConfigFile, writeToConfigFile } from '../util/config/files';
import Client from '../util/client';
import { LoginResult } from '../util/login/types';
const help = () => {
console.log(`
${chalk.bold(`${logo} ${getPkgName()} login`)} <email or team>
${chalk.dim('Options:')}
-h, --help Output usage information
--no-color No color mode [off]
-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
${chalk.dim('Examples:')}
${chalk.gray('')} Log into the Vercel platform
${chalk.cyan(`$ ${getPkgName()} login`)}
${chalk.gray('')} Log in using a specific email address
${chalk.cyan(`$ ${getPkgName()} login john@doe.com`)}
${chalk.gray('')} Log in using a specific team "slug" for SAML Single Sign-On
${chalk.cyan(`$ ${getPkgName()} login acme`)}
${chalk.gray('')} Log in using GitHub in "out-of-band" mode
${chalk.cyan(`$ ${getPkgName()} login --github --oob`)}
`);
};
import hp from '../../util/humanize-path';
import getArgs from '../../util/get-args';
import prompt from '../../util/login/prompt';
import doSamlLogin from '../../util/login/saml';
import doEmailLogin from '../../util/login/email';
import doGithubLogin from '../../util/login/github';
import doGitlabLogin from '../../util/login/gitlab';
import doBitbucketLogin from '../../util/login/bitbucket';
import { prependEmoji, emoji } from '../../util/emoji';
import { getCommandName } from '../../util/pkg-name';
import getGlobalPathConfig from '../../util/config/global-path';
import {
writeToAuthConfigFile,
writeToConfigFile,
} from '../../util/config/files';
import Client from '../../util/client';
import { LoginResult } from '../../util/login/types';
import { help } from '../help';
import { loginCommand } from './command';
export default async function login(client: Client): Promise<number> {
const { output } = client;
@@ -62,7 +31,7 @@ export default async function login(client: Client): Promise<number> {
});
if (argv['--help']) {
help();
output.print(help(loginCommand, { columns: client.stderr.columns }));
return 2;
}

View File

@@ -0,0 +1,15 @@
import { Command } from '../help';
import { getPkgName } from '../../util/pkg-name';
export const logoutCommand: Command = {
name: 'logout',
description: 'Logout the current authenticated user or team.',
arguments: [],
options: [],
examples: [
{
name: 'Logout from the CLI',
value: `${getPkgName()} logout`,
},
],
};

View File

@@ -1,37 +1,19 @@
import chalk from 'chalk';
import logo from '../util/output/logo';
import { handleError } from '../util/error';
import { writeToConfigFile, writeToAuthConfigFile } from '../util/config/files';
import getArgs from '../util/get-args';
import Client from '../util/client';
import { getCommandName, getPkgName } from '../util/pkg-name';
import { isAPIError } from '../util/errors-ts';
import { handleError } from '../../util/error';
import {
writeToConfigFile,
writeToAuthConfigFile,
} from '../../util/config/files';
import getArgs from '../../util/get-args';
import Client from '../../util/client';
import { getCommandName } from '../../util/pkg-name';
import { isAPIError } from '../../util/errors-ts';
import { errorToString } from '@vercel/error-utils';
const help = () => {
console.log(`
${chalk.bold(`${logo} ${getPkgName()} logout`)}
${chalk.dim('Options:')}
-h, --help Output usage information
-A ${chalk.bold.underline('FILE')}, --local-config=${chalk.bold.underline(
'FILE'
)} Path to the local ${'`vercel.json`'} file
-Q ${chalk.bold.underline('DIR')}, --global-config=${chalk.bold.underline(
'DIR'
)} Path to the global ${'`.vercel`'} directory
${chalk.dim('Examples:')}
${chalk.gray('')} Logout from the CLI:
${chalk.cyan(`$ ${getPkgName()} logout`)}
`);
};
import { help } from '../help';
import { logoutCommand } from './command';
export default async function main(client: Client): Promise<number> {
let argv;
const { authConfig, config, output } = client;
try {
argv = getArgs(client.argv.slice(2), {
@@ -44,12 +26,10 @@ export default async function main(client: Client): Promise<number> {
}
if (argv['--help']) {
help();
output.print(help(logoutCommand, { columns: client.stderr.columns }));
return 2;
}
const { authConfig, config, output } = client;
if (!authConfig.token) {
output.note(
`Not currently logged in, so ${getCommandName('logout')} did nothing`

View File

@@ -0,0 +1,66 @@
import { Command } from '../help';
import { getPkgName } from '../../util/pkg-name';
export const logsCommand: Command = {
name: 'logs',
description: 'Display logs for a specific deployment.',
arguments: [
{
name: 'url|deploymentId',
required: true,
},
],
options: [
{
name: 'follow',
shorthand: 'f',
description: 'Wait for additional data [off]',
type: 'string',
deprecated: false,
multi: false,
},
{
name: 'limit',
shorthand: 'n',
description: 'Number of log entries [100]',
argument: 'NUMBER',
type: 'string',
deprecated: false,
multi: false,
},
{
name: 'since',
shorthand: null,
description: 'Only return logs after date (ISO 8601)',
argument: 'SINCE',
type: 'string',
deprecated: false,
multi: false,
},
{
name: 'until',
shorthand: null,
description:
'Only return logs before date (ISO 8601), ignored when used with --follow',
argument: 'UNTIL',
type: 'string',
deprecated: false,
multi: false,
},
{
name: 'output',
shorthand: 'o',
description: `Specify the output format (short|raw) [short]`,
argument: 'MODE',
type: 'string',
deprecated: false,
multi: false,
},
],
examples: [
{
name: 'Print the logs for the deployment DEPLOYMENT_ID',
value: `${getPkgName()} logs DEPLOYMENT_ID`,
},
],
};

View File

@@ -1,60 +1,15 @@
import chalk from 'chalk';
import logo from '../util/output/logo';
import elapsed from '../util/output/elapsed';
import { maybeURL, normalizeURL } from '../util/url';
import printEvents, { DeploymentEvent } from '../util/events';
import getScope from '../util/get-scope';
import { getPkgName } from '../util/pkg-name';
import getArgs from '../util/get-args';
import Client from '../util/client';
import getDeployment from '../util/get-deployment';
import elapsed from '../../util/output/elapsed';
import { maybeURL, normalizeURL } from '../../util/url';
import printEvents, { DeploymentEvent } from '../../util/events';
import getScope from '../../util/get-scope';
import getArgs from '../../util/get-args';
import Client from '../../util/client';
import getDeployment from '../../util/get-deployment';
import { help } from '../help';
import { logsCommand } from './command';
const help = () => {
console.log(`
${chalk.bold(`${logo} ${getPkgName()} logs`)} <url|deploymentId>
${chalk.dim('Options:')}
-h, --help Output usage information
-A ${chalk.bold.underline('FILE')}, --local-config=${chalk.bold.underline(
'FILE'
)} Path to the local ${'`vercel.json`'} file
-Q ${chalk.bold.underline('DIR')}, --global-config=${chalk.bold.underline(
'DIR'
)} Path to the global ${'`.vercel`'} directory
-d, --debug Debug mode [off]
--no-color No color mode [off]
-f, --follow Wait for additional data [off]
-n ${chalk.bold.underline(
'NUMBER'
)} Number of logs [100]
-t ${chalk.bold.underline('TOKEN')}, --token=${chalk.bold.underline(
'TOKEN'
)} Login token
--since=${chalk.bold.underline(
'SINCE'
)} Only return logs after date (ISO 8601)
--until=${chalk.bold.underline(
'UNTIL'
)} Only return logs before date (ISO 8601), ignored for ${'`-f`'}
-S, --scope Set a custom scope
-o ${chalk.bold.underline('MODE')}, --output=${chalk.bold.underline(
'MODE'
)} Specify the output format (${Object.keys(logPrinters).join(
'|'
)}) [short]
${chalk.dim('Examples:')}
${chalk.gray('')} Print the logs for the deployment ${chalk.dim(
'`deploymentId`'
)}
${chalk.cyan(`$ ${getPkgName()} logs deploymentId`)}
`);
};
export default async function main(client: Client) {
export default async function logs(client: Client) {
let head;
let limit;
let follow;
@@ -76,14 +31,13 @@ export default async function main(client: Client) {
argv._ = argv._.slice(1);
deploymentIdOrURL = argv._[0];
const { output } = client;
if (argv['--help'] || !deploymentIdOrURL || deploymentIdOrURL === 'help') {
help();
output.print(help(logsCommand, { columns: client.stderr.columns }));
return 2;
}
const { output } = client;
try {
since = argv['--since'] ? toTimestamp(argv['--since']) : 0;
} catch (err) {

View File

@@ -38,6 +38,10 @@ export default async function rm(client: Client, args: string[]) {
client.output.error('No such project exists');
return 1;
}
if (isAPIError(err) && err.status === 403) {
client.output.error(err.message);
return 1;
}
}
const elapsed = ms(Date.now() - start);
client.output.log(

View File

@@ -73,6 +73,8 @@ export default async (client: Client): Promise<number> => {
return 2;
}
const yes = argv['--yes'] ?? false;
// validate the timeout
let timeout = argv['--timeout'];
if (timeout && ms(timeout) === undefined) {
@@ -103,6 +105,7 @@ export default async (client: Client): Promise<number> => {
client,
deployId: actionOrDeployId,
timeout,
yes,
});
} catch (err) {
if (isErrnoException(err)) {

View File

@@ -4,6 +4,7 @@ import { getCommandName } from '../../util/pkg-name';
import getProjectByDeployment from '../../util/projects/get-project-by-deployment';
import ms from 'ms';
import promoteStatus from './status';
import confirm from '../../util/input/confirm';
/**
* Requests a promotion and waits for it complete.
@@ -16,10 +17,12 @@ export default async function requestPromote({
client,
deployId,
timeout,
yes,
}: {
client: Client;
deployId: string;
timeout?: string;
yes: boolean;
}): Promise<number> {
const { output } = client;
@@ -29,6 +32,16 @@ export default async function requestPromote({
output: client.output,
});
if (deployment.target !== 'production' && !yes) {
const question =
'This deployment does not target production, therefore promotion will not apply production environment variables. Are you sure you want to continue?';
const answer = await confirm(client, question, false);
if (!answer) {
output.error('Canceled');
return 0;
}
}
// request the promotion
await client.fetch(`/v9/projects/${project.id}/promote/${deployment.id}`, {
body: {}, // required

View File

@@ -1,157 +0,0 @@
import chalk from 'chalk';
import { join } from 'path';
import Client from '../util/client';
import type { Project, ProjectEnvTarget } from '@vercel-internals/types';
import { emoji, prependEmoji } from '../util/emoji';
import getArgs from '../util/get-args';
import logo from '../util/output/logo';
import stamp from '../util/output/stamp';
import { getPkgName } from '../util/pkg-name';
import { VERCEL_DIR, VERCEL_DIR_PROJECT } from '../util/projects/link';
import { writeProjectSettings } from '../util/projects/project-settings';
import envPull from './env/pull';
import {
isValidEnvTarget,
getEnvTargetPlaceholder,
} from '../util/env/env-target';
import { ensureLink } from '../util/link/ensure-link';
const help = () => {
return console.log(`
${chalk.bold(`${logo} ${getPkgName()} pull`)} [project-path]
${chalk.dim('Options:')}
-h, --help Output usage information
-A ${chalk.bold.underline('FILE')}, --local-config=${chalk.bold.underline(
'FILE'
)} Path to the local ${'`vercel.json`'} file
-Q ${chalk.bold.underline('DIR')}, --global-config=${chalk.bold.underline(
'DIR'
)} Path to the global ${'`.vercel`'} directory
-d, --debug Debug mode [off]
--no-color No color mode [off]
--environment [environment] Deployment environment [development]
-y, --yes Skip questions when setting up new project using default scope and settings
${chalk.dim('Examples:')}
${chalk.gray(
''
)} Pull the latest Environment Variables and Project Settings from the cloud
and stores them in \`.vercel/.env.\${target}.local\` and \`.vercel/project.json\` respectively.
${chalk.cyan(`$ ${getPkgName()} pull`)}
${chalk.cyan(`$ ${getPkgName()} pull ./path-to-project`)}
${chalk.gray('')} Pull for a specific environment
${chalk.cyan(
`$ ${getPkgName()} pull --environment=${getEnvTargetPlaceholder()}`
)}
${chalk.gray(
'If you want to download environment variables to a specific file, use `vercel env pull` instead.'
)}
`);
};
function processArgs(client: Client) {
return getArgs(client.argv.slice(2), {
'--yes': Boolean,
'--environment': String,
'--git-branch': String,
'--debug': Boolean,
'-d': '--debug',
'-y': '--yes',
});
}
function parseArgs(client: Client) {
const argv = processArgs(client);
if (argv['--help']) {
help();
return 2;
}
return argv;
}
async function pullAllEnvFiles(
environment: ProjectEnvTarget,
client: Client,
project: Project,
argv: ReturnType<typeof processArgs>,
cwd: string
): Promise<number> {
const environmentFile = `.env.${environment}.local`;
return envPull(
client,
project,
environment,
argv,
[join('.vercel', environmentFile)],
client.output,
cwd,
'vercel-cli:pull'
);
}
export function parseEnvironment(
environment = 'development'
): ProjectEnvTarget {
if (!isValidEnvTarget(environment)) {
throw new Error(
`environment "${environment}" not supported; must be one of ${getEnvTargetPlaceholder()}`
);
}
return environment;
}
export default async function main(client: Client) {
const argv = parseArgs(client);
if (typeof argv === 'number') {
return argv;
}
const cwd = argv._[1] || process.cwd();
const autoConfirm = Boolean(argv['--yes']);
const environment = parseEnvironment(argv['--environment'] || undefined);
const link = await ensureLink('pull', client, cwd, { autoConfirm });
if (typeof link === 'number') {
return link;
}
const { project, org } = link;
client.config.currentTeam = org.type === 'team' ? org.id : undefined;
const pullResultCode = await pullAllEnvFiles(
environment,
client,
project,
argv,
cwd
);
if (pullResultCode !== 0) {
return pullResultCode;
}
client.output.print('\n');
client.output.log('Downloading project settings');
await writeProjectSettings(cwd, project, org);
const settingsStamp = stamp();
client.output.print(
`${prependEmoji(
`Downloaded project settings to ${chalk.bold(
join(VERCEL_DIR, VERCEL_DIR_PROJECT)
)} ${chalk.gray(settingsStamp())}`,
emoji('success')
)}\n`
);
return 0;
}

View File

@@ -0,0 +1,53 @@
import { Command } from '../help';
import { getPkgName } from '../../util/pkg-name';
import { getEnvTargetPlaceholder } from '../../util/env/env-target';
export const pullCommand: Command = {
name: 'pull',
description:
'Pull latest environment variables and project settings from Vercel. ',
arguments: [
{
name: 'project-path',
required: false,
},
],
options: [
{
name: 'environment',
description: 'Deployment environment [development]',
argument: 'environment',
shorthand: null,
type: 'string',
deprecated: false,
multi: false,
},
{
name: 'yes',
description:
'Skip questions when setting up new project using default scope and settings',
shorthand: 'y',
type: 'string',
deprecated: false,
multi: false,
},
],
examples: [
{
name: 'Pull the latest Environment Variables and Project Settings from the cloud',
value: `${getPkgName()} pull`,
},
{
name: 'Pull the latest Environment Variables and Project Settings from the cloud targeting a directory',
value: `${getPkgName()} pull ./path-to-project`,
},
{
name: 'Pull for a specific environment',
value: `${getPkgName()} pull --environment=${getEnvTargetPlaceholder()}`,
},
{
name: 'If you want to download environment variables to a specific file, use `vercel env pull` instead',
value: `${getPkgName()} env pull`,
},
],
};

View File

@@ -0,0 +1,131 @@
import chalk from 'chalk';
import { join } from 'path';
import Client from '../../util/client';
import type {
Project,
ProjectEnvTarget,
ProjectLinked,
} from '@vercel-internals/types';
import { emoji, prependEmoji } from '../../util/emoji';
import getArgs from '../../util/get-args';
import stamp from '../../util/output/stamp';
import { VERCEL_DIR, VERCEL_DIR_PROJECT } from '../../util/projects/link';
import { writeProjectSettings } from '../../util/projects/project-settings';
import envPull from '../env/pull';
import {
isValidEnvTarget,
getEnvTargetPlaceholder,
} from '../../util/env/env-target';
import { ensureLink } from '../../util/link/ensure-link';
import humanizePath from '../../util/humanize-path';
import { help } from '../help';
import { pullCommand } from './command';
function processArgs(client: Client) {
return getArgs(client.argv.slice(2), {
'--yes': Boolean,
'--environment': String,
'--git-branch': String,
'--debug': Boolean,
'-d': '--debug',
'-y': '--yes',
});
}
function parseArgs(client: Client) {
const argv = processArgs(client);
if (argv['--help']) {
client.output.print(help(pullCommand, { columns: client.stderr.columns }));
return 2;
}
return argv;
}
async function pullAllEnvFiles(
environment: ProjectEnvTarget,
client: Client,
link: ProjectLinked,
project: Project,
argv: ReturnType<typeof processArgs>,
cwd: string
): Promise<number> {
const environmentFile = `.env.${environment}.local`;
return envPull(
client,
link,
project,
environment,
argv,
[join('.vercel', environmentFile)],
client.output,
cwd,
'vercel-cli:pull'
);
}
export function parseEnvironment(
environment = 'development'
): ProjectEnvTarget {
if (!isValidEnvTarget(environment)) {
throw new Error(
`environment "${environment}" not supported; must be one of ${getEnvTargetPlaceholder()}`
);
}
return environment;
}
export default async function main(client: Client) {
const argv = parseArgs(client);
if (typeof argv === 'number') {
return argv;
}
let cwd = argv._[1] || client.cwd;
const autoConfirm = Boolean(argv['--yes']);
const environment = parseEnvironment(argv['--environment'] || undefined);
const link = await ensureLink('pull', client, cwd, { autoConfirm });
if (typeof link === 'number') {
return link;
}
const { project, org, repoRoot } = link;
if (repoRoot) {
cwd = join(repoRoot, project.rootDirectory || '');
}
client.config.currentTeam = org.type === 'team' ? org.id : undefined;
const pullResultCode = await pullAllEnvFiles(
environment,
client,
link,
project,
argv,
cwd
);
if (pullResultCode !== 0) {
return pullResultCode;
}
client.output.print('\n');
client.output.log('Downloading project settings');
const isRepoLinked = typeof repoRoot === 'string';
await writeProjectSettings(cwd, project, org, isRepoLinked);
const settingsStamp = stamp();
client.output.print(
`${prependEmoji(
`Downloaded project settings to ${chalk.bold(
humanizePath(join(cwd, VERCEL_DIR, VERCEL_DIR_PROJECT))
)} ${chalk.gray(settingsStamp())}`,
emoji('success')
)}\n`
);
return 0;
}

View File

@@ -0,0 +1,33 @@
import { Command } from '../help';
import { getPkgName } from '../../util/pkg-name';
export const redeployCommand: Command = {
name: 'redeploy',
description: 'Rebuild and deploy a previous deployment.',
arguments: [
{
name: 'deploymentId|deploymentName',
required: false,
},
],
options: [
{
name: 'no-wait',
shorthand: null,
description: "Don't wait for the redeploy to finish",
type: 'boolean',
deprecated: false,
multi: false,
},
],
examples: [
{
name: 'Rebuild and deploy an existing deployment using id or url',
value: `${getPkgName()} redeploy my-deployment.vercel.app`,
},
{
name: 'Write Deployment URL to a file',
value: `${getPkgName()} redeploy my-deployment.vercel.app > deployment-url.txt`,
},
],
};

View File

@@ -1,66 +1,29 @@
import chalk from 'chalk';
import { checkDeploymentStatus } from '@vercel/client';
import type Client from '../util/client';
import { emoji, prependEmoji } from '../util/emoji';
import getArgs from '../util/get-args';
import { getCommandName, getPkgName } from '../util/pkg-name';
import { getDeploymentByIdOrURL } from '../util/deploy/get-deployment-by-id-or-url';
import getScope from '../util/get-scope';
import handleError from '../util/handle-error';
import type Client from '../../util/client';
import { emoji, prependEmoji } from '../../util/emoji';
import getArgs from '../../util/get-args';
import { getCommandName } from '../../util/pkg-name';
import { getDeploymentByIdOrURL } from '../../util/deploy/get-deployment-by-id-or-url';
import getScope from '../../util/get-scope';
import handleError from '../../util/handle-error';
import { isErrnoException } from '@vercel/error-utils';
import logo from '../util/output/logo';
import Now from '../util';
import { printDeploymentStatus } from '../util/deploy/print-deployment-status';
import stamp from '../util/output/stamp';
import ua from '../util/ua';
import Now from '../../util';
import { printDeploymentStatus } from '../../util/deploy/print-deployment-status';
import stamp from '../../util/output/stamp';
import ua from '../../util/ua';
import type { VercelClientOptions } from '@vercel/client';
const help = () => {
console.log(`
${chalk.bold(
`${logo} ${getPkgName()} redeploy`
)} [deploymentId|deploymentName]
Rebuild and deploy a previous deployment.
${chalk.dim('Options:')}
-h, --help Output usage information
-A ${chalk.bold.underline('FILE')}, --local-config=${chalk.bold.underline(
'FILE'
)} Path to the local ${'`vercel.json`'} file
-Q ${chalk.bold.underline('DIR')}, --global-config=${chalk.bold.underline(
'DIR'
)} Path to the global ${'`.vercel`'} directory
-d, --debug Debug mode [off]
--no-color No color mode [off]
--no-wait Don't wait for the redeploy to finish
-t ${chalk.bold.underline('TOKEN')}, --token=${chalk.bold.underline(
'TOKEN'
)} Login token
-y, --yes Skip questions when setting up new project using default scope and settings
${chalk.dim('Examples:')}
${chalk.gray('')} Rebuild and deploy an existing deployment using id or url
${chalk.cyan(`$ ${getPkgName()} redeploy my-deployment.vercel.app`)}
${chalk.gray('')} Write Deployment URL to a file
${chalk.cyan(
`$ ${getPkgName()} redeploy my-deployment.vercel.app > deployment-url.txt`
)}
`);
};
import { help } from '../help';
import { redeployCommand } from './command';
/**
* `vc redeploy` command
* @param {Client} client
* @returns {Promise<number>} Resolves an exit code; 0 on success
*/
export default async (client: Client): Promise<number> => {
export default async function redeploy(client: Client): Promise<number> {
let argv;
const { output } = client;
try {
argv = getArgs(client.argv.slice(2), {
'--no-wait': Boolean,
@@ -73,11 +36,10 @@ export default async (client: Client): Promise<number> => {
}
if (argv['--help'] || argv._[0] === 'help') {
help();
output.print(help(redeployCommand, { columns: client.stderr.columns }));
return 2;
}
const { output } = client;
const deployIdOrUrl = argv._[1];
if (!deployIdOrUrl) {
output.error(
@@ -108,7 +70,7 @@ export default async (client: Client): Promise<number> => {
action: 'redeploy',
},
name: fromDeployment.name,
target: fromDeployment.target || 'production',
target: fromDeployment.target ?? undefined,
},
method: 'POST',
});
@@ -201,4 +163,4 @@ export default async (client: Client): Promise<number> => {
}
return 1;
}
};
}

View File

@@ -0,0 +1,45 @@
import { Command } from '../help';
import { getPkgName } from '../../util/pkg-name';
export const removeCommand: Command = {
name: 'remove',
description: 'Remove a deployment by name or id.',
arguments: [
{
name: '...deploymentId|deploymentName',
required: true,
},
],
options: [
{
name: 'yes',
shorthand: 'y',
type: 'boolean',
deprecated: false,
description: 'Skip confirmation',
multi: false,
},
{
name: 'safe',
shorthand: 's',
type: 'boolean',
deprecated: false,
description: 'Skip deployments with an active alias',
multi: false,
},
],
examples: [
{
name: 'Remove a deployment identified by `deploymentId`',
value: `${getPkgName()} remove my-app`,
},
{
name: 'Remove all deployments with name `my-app`',
value: `${getPkgName()} remove deploymentId`,
},
{
name: 'Remove two deployments with IDs `eyWt6zuSdeus` and `uWHoA9RQ1d1o`',
value: `${getPkgName()} remove eyWt6zuSdeus uWHoA9RQ1d1o`,
},
],
};

View File

@@ -2,74 +2,31 @@ import chalk from 'chalk';
import ms from 'ms';
import plural from 'pluralize';
import table from 'text-table';
import Now from '../util';
import getAliases from '../util/alias/get-aliases';
import logo from '../util/output/logo';
import elapsed from '../util/output/elapsed';
import { normalizeURL } from '../util/url';
import getScope from '../util/get-scope';
import { isValidName } from '../util/is-valid-name';
import removeProject from '../util/projects/remove-project';
import getProjectByIdOrName from '../util/projects/get-project-by-id-or-name';
import getDeployment from '../util/get-deployment';
import getDeploymentsByProjectId from '../util/deploy/get-deployments-by-project-id';
import { getPkgName, getCommandName } from '../util/pkg-name';
import getArgs from '../util/get-args';
import handleError from '../util/handle-error';
import type Client from '../util/client';
import { Output } from '../util/output';
import Now from '../../util';
import getAliases from '../../util/alias/get-aliases';
import elapsed from '../../util/output/elapsed';
import { normalizeURL } from '../../util/url';
import getScope from '../../util/get-scope';
import { isValidName } from '../../util/is-valid-name';
import removeProject from '../../util/projects/remove-project';
import getProjectByIdOrName from '../../util/projects/get-project-by-id-or-name';
import getDeployment from '../../util/get-deployment';
import getDeploymentsByProjectId from '../../util/deploy/get-deployments-by-project-id';
import { getCommandName } from '../../util/pkg-name';
import getArgs from '../../util/get-args';
import handleError from '../../util/handle-error';
import type Client from '../../util/client';
import { Output } from '../../util/output';
import { Alias, Deployment, Project } from '@vercel-internals/types';
import { NowError } from '../util/now-error';
import { NowError } from '../../util/now-error';
import { help } from '../help';
import { removeCommand } from './command';
type DeploymentWithAliases = Deployment & {
aliases: Alias[];
};
const help = () => {
console.log(`
${chalk.bold(
`${logo} ${getPkgName()} remove`
)} [...deploymentId|deploymentName]
${chalk.dim('Options:')}
-h, --help Output usage information
-A ${chalk.bold.underline('FILE')}, --local-config=${chalk.bold.underline(
'FILE'
)} Path to the local ${'`vercel.json`'} file
-Q ${chalk.bold.underline('DIR')}, --global-config=${chalk.bold.underline(
'DIR'
)} Path to the global ${'`.vercel`'} directory
-d, --debug Debug mode [off]
--no-color No color mode [off]
-t ${chalk.bold.underline('TOKEN')}, --token=${chalk.bold.underline(
'TOKEN'
)} Login token
-y, --yes Skip confirmation
-s, --safe Skip deployments with an active alias
-S, --scope Set a custom scope
${chalk.dim('Examples:')}
${chalk.gray('')} Remove a deployment identified by ${chalk.dim(
'`deploymentId`'
)}
${chalk.cyan(`$ ${getPkgName()} rm deploymentId`)}
${chalk.gray('')} Remove all deployments with name ${chalk.dim('`my-app`')}
${chalk.cyan(`$ ${getPkgName()} rm my-app`)}
${chalk.gray('')} Remove two deployments with IDs ${chalk.dim(
'`eyWt6zuSdeus`'
)} and ${chalk.dim('`uWHoA9RQ1d1o`')}
${chalk.cyan(`$ ${getPkgName()} rm eyWt6zuSdeus uWHoA9RQ1d1o`)}
`);
};
export default async function main(client: Client) {
export default async function remove(client: Client) {
let argv;
try {
@@ -98,13 +55,13 @@ export default async function main(client: Client) {
const { success, error, log } = output;
if (argv['--help'] || ids[0] === 'help') {
help();
output.print(help(removeCommand, { columns: client.stderr.columns }));
return 2;
}
if (ids.length < 1) {
error(`${getCommandName('rm')} expects at least one argument`);
help();
output.print(help(removeCommand, { columns: client.stderr.columns }));
return 1;
}

View File

@@ -1,56 +0,0 @@
import chalk from 'chalk';
import logo from '../util/output/logo';
import getScope from '../util/get-scope';
import { getPkgName } from '../util/pkg-name';
import getArgs from '../util/get-args';
import Client from '../util/client';
const help = () => {
console.log(`
${chalk.bold(`${logo} ${getPkgName()} whoami`)}
${chalk.dim('Options:')}
-h, --help Output usage information
-A ${chalk.bold.underline('FILE')}, --local-config=${chalk.bold.underline(
'FILE'
)} Path to the local ${'`vercel.json`'} file
-Q ${chalk.bold.underline('DIR')}, --global-config=${chalk.bold.underline(
'DIR'
)} Path to the global ${'`.vercel`'} directory
-d, --debug Debug mode [off]
--no-color No color mode [off]
-t ${chalk.bold.underline('TOKEN')}, --token=${chalk.bold.underline(
'TOKEN'
)} Login token
${chalk.dim('Examples:')}
${chalk.gray('')} Shows the username of the currently logged in user
${chalk.cyan(`$ ${getPkgName()} whoami`)}
`);
};
export default async (client: Client): Promise<number> => {
const { output } = client;
const argv = getArgs(client.argv.slice(2), {});
argv._ = argv._.slice(1);
if (argv['--help'] || argv._[0] === 'help') {
help();
return 2;
}
const { contextName } = await getScope(client, { getTeam: false });
if (client.stdout.isTTY) {
output.log(contextName);
} else {
// If stdout is not a TTY, then only print the username
// to support piping the output to another file / exe
client.stdout.write(`${contextName}\n`);
}
return 0;
};

View File

@@ -0,0 +1,15 @@
import { Command } from '../help';
import { getPkgName } from '../../util/pkg-name';
export const whoamiCommand: Command = {
name: 'whoami',
description: 'Shows the username of the currently logged in user.',
arguments: [],
options: [],
examples: [
{
name: 'Shows the username of the currently logged in user',
value: `${getPkgName()} whoami`,
},
],
};

View File

@@ -0,0 +1,29 @@
import { help } from '../help';
import { whoamiCommand } from './command';
import getScope from '../../util/get-scope';
import getArgs from '../../util/get-args';
import Client from '../../util/client';
export default async function whoami(client: Client): Promise<number> {
const { output } = client;
const argv = getArgs(client.argv.slice(2), {});
argv._ = argv._.slice(1);
if (argv['--help'] || argv._[0] === 'help') {
output.print(help(whoamiCommand, { columns: client.stderr.columns }));
return 2;
}
const { contextName } = await getScope(client, { getTeam: false });
if (client.stdout.isTTY) {
output.log(contextName);
} else {
// If stdout is not a TTY, then only print the username
// to support piping the output to another file / exe
client.stdout.write(`${contextName}\n`);
}
return 0;
}

View File

@@ -131,6 +131,7 @@ async function writeBuildResultV2(
const lambdas = new Map<Lambda, string>();
const overrides: Record<string, PathOverride> = {};
for (const [path, output] of Object.entries(buildResult.output)) {
const normalizedPath = stripDuplicateSlashes(path);
if (isLambda(output)) {
@@ -156,11 +157,26 @@ async function writeBuildResultV2(
const ext = getFileExtension(fallback);
const fallbackName = `${normalizedPath}.prerender-fallback${ext}`;
const fallbackPath = join(outputDir, 'functions', fallbackName);
const stream = fallback.toStream();
await pipe(
stream,
fs.createWriteStream(fallbackPath, { mode: fallback.mode })
);
// if file is already on the disk we can hard link
// instead of creating a new copy
let usedHardLink = false;
if ('fsPath' in fallback) {
try {
await fs.link(fallback.fsPath, fallbackPath);
usedHardLink = true;
} catch (_) {
// if link fails we continue attempting to copy
}
}
if (!usedHardLink) {
const stream = fallback.toStream();
await pipe(
stream,
fs.createWriteStream(fallbackPath, { mode: fallback.mode })
);
}
fallback = new FileFsRef({
...output.fallback,
fsPath: basename(fallbackName),
@@ -288,6 +304,14 @@ async function writeStaticFile(
const dest = join(outputDir, 'static', fsPath);
await fs.mkdirp(dirname(dest));
// if already on disk hard link instead of copying
if ('fsPath' in file) {
try {
return await fs.link(file.fsPath, dest);
} catch (_) {
// if link fails we continue attempting to copy
}
}
await downloadFile(file, dest);
}

View File

@@ -18,12 +18,14 @@ import type {
JSONObject,
Stdio,
ReadableTTY,
WritableTTY,
PaginationOptions,
} from '@vercel-internals/types';
import { sharedPromise } from './promise';
import { APIError } from './errors-ts';
import { normalizeError } from '@vercel/error-utils';
import type { Agent } from 'http';
import sleep from './sleep';
import type * as tty from 'tty';
const isSAMLError = (v: any): v is SAMLError => {
return v && v.saml;
@@ -57,8 +59,8 @@ export default class Client extends EventEmitter implements Stdio {
apiUrl: string;
authConfig: AuthConfig;
stdin: ReadableTTY;
stdout: WritableTTY;
stderr: WritableTTY;
stdout: tty.WriteStream;
stderr: tty.WriteStream;
output: Output;
config: GlobalConfig;
agent?: Agent;
@@ -144,7 +146,8 @@ export default class Client extends EventEmitter implements Stdio {
if (!res.ok) {
const error = await responseError(res);
if (isSAMLError(error)) {
// we should force reauth only if error has a teamId
if (isSAMLError(error) && error.teamId) {
try {
// A SAML error means the token is expired, or is not
// designated for the requested team, so the user needs
@@ -176,6 +179,31 @@ export default class Client extends EventEmitter implements Stdio {
}, opts.retry);
}
async *fetchPaginated<T>(
url: string | URL,
opts?: FetchOptions
): AsyncGenerator<T & { pagination: PaginationOptions }> {
const endpoint =
typeof url === 'string' ? new URL(url, this.apiUrl) : new URL(url.href);
if (!endpoint.searchParams.has('limit')) {
endpoint.searchParams.set('limit', '100');
}
let next: number | null | undefined;
do {
if (next) {
// Small sleep to avoid rate limiting
await sleep(100);
endpoint.searchParams.set('until', String(next));
}
const res = await this.fetch<T & { pagination: PaginationOptions }>(
endpoint.href,
opts
);
yield res;
next = res.pagination?.next;
} while (next);
}
reauthenticate = sharedPromise(async function (
this: Client,
error: SAMLError

View File

@@ -1,82 +0,0 @@
import stripeFactory from 'stripe';
import Now from '.';
const stripe = stripeFactory('pk_live_alyEi3lN0kSwbdevK0nrGwTw');
export default class CreditCards extends Now {
async ls() {
const res = await this._fetch('/stripe/sources/');
const body = await res.json();
if (res.status !== 200) {
const e = new Error(body.error.message);
e.code = body.error.code;
throw e;
}
return body;
}
async setDefault(source) {
await this._fetch('/stripe/sources/', {
method: 'POST',
body: {
source,
makeDefault: true,
},
});
return true;
}
async rm(source) {
await this._fetch(`/stripe/sources/`, {
method: 'DELETE',
body: { source },
});
return true;
}
async add(card) {
if (!card.expDate) {
throw new Error(`Please define an expiration date for your card`);
}
const expDateParts = card.expDate.split(' / ');
card = {
name: card.name,
number: card.cardNumber,
cvc: card.ccv,
};
card.exp_month = expDateParts[0];
card.exp_year = expDateParts[1];
try {
const token = (await stripe.tokens.create({ card })).id;
const res = await this._fetch('/stripe/sources/', {
method: 'POST',
body: {
source: token,
},
});
const { source, error } = await res.json();
if (source && source.id) {
return {
last4: source.last4,
};
} else if (error && error.message) {
throw new Error(error.message);
} else {
throw new Error('Unknown error');
}
} catch (err) {
throw new Error(err.message || 'Unknown error');
}
}
}

View File

@@ -234,6 +234,10 @@ export default async function processDeployment({
return error;
}
if (error.code === 'forbidden') {
return error;
}
throw error;
}

View File

@@ -32,7 +32,7 @@ import {
Builder,
cloneEnv,
Env,
getNodeBinPath,
getNodeBinPaths,
StartDevServerResult,
FileFsRef,
PackageJson,
@@ -124,6 +124,7 @@ function sortBuilders(buildA: Builder, buildB: Builder) {
export default class DevServer {
public cwd: string;
public repoRoot: string;
public output: Output;
public proxy: httpProxy;
public envConfigs: EnvConfigs;
@@ -169,6 +170,7 @@ export default class DevServer {
constructor(cwd: string, options: DevServerOptions) {
this.cwd = cwd;
this.repoRoot = options.repoRoot ?? cwd;
this.output = options.output;
this.envConfigs = { buildEnv: {}, runEnv: {}, allEnv: {} };
this.envValues = options.envValues || {};
@@ -1131,7 +1133,7 @@ export default class DevServer {
body = redirectTemplate({ location, statusCode });
} else {
res.setHeader('content-type', 'text/plain; charset=utf-8');
body = `Redirecting to ${location} (${statusCode})\n`;
body = `Redirecting...\n`;
}
res.end(body);
}
@@ -1412,7 +1414,7 @@ export default class DevServer {
files,
entrypoint: middleware.entrypoint,
workPath,
repoRootPath: this.cwd,
repoRootPath: this.repoRoot,
config: middleware.config || {},
meta: {
isDev: true,
@@ -1849,7 +1851,7 @@ export default class DevServer {
entrypoint: match.entrypoint,
workPath,
config: match.config || {},
repoRootPath: this.cwd,
repoRootPath: this.repoRoot,
meta: {
isDev: true,
requestPath,
@@ -2237,7 +2239,8 @@ export default class DevServer {
);
// add the node_modules/.bin directory to the PATH
const nodeBinPath = await getNodeBinPath({ cwd });
const nodeBinPaths = getNodeBinPaths({ base: this.repoRoot, start: cwd });
const nodeBinPath = nodeBinPaths.join(path.delimiter);
env.PATH = `${nodeBinPath}${path.delimiter}${env.PATH}`;
// This is necesary so that the dev command in the Project

View File

@@ -25,6 +25,7 @@ export interface DevServerOptions {
output: Output;
projectSettings?: ProjectSettings;
envValues?: Record<string, string>;
repoRoot?: string;
}
export interface EnvConfigs {

View File

@@ -8,10 +8,7 @@ import {
import type { Server } from 'http';
import type Client from '../client';
const toHeaders = buildToHeaders({
// @ts-expect-error - `node-fetch` Headers is missing `getAll()`
Headers,
});
const toHeaders = buildToHeaders({ Headers });
export function createProxy(client: Client): Server {
return createServer(async (req, res) => {

View File

@@ -2,7 +2,7 @@ import { basename } from 'path';
import { VercelConfig } from '@vercel/client';
export interface GetProjectNameOptions {
argv: { '--name'?: string };
argv: { [k: string]: string | undefined };
nowConfig?: VercelConfig;
paths?: string[];
}

View File

@@ -0,0 +1,5 @@
import type { RepoInfo } from './connect-git-provider';
export function repoInfoToUrl(info: RepoInfo): string {
return `https://${info.provider}.com/${info.org}/${info.repo}`;
}

View File

@@ -50,7 +50,7 @@ export interface CreateOptions {
projectSettings?: any;
skipAutoDetectionConfirmation?: boolean;
noWait?: boolean;
autoAssignCustomDomains: boolean;
autoAssignCustomDomains?: boolean;
}
export interface RemoveOptions {
@@ -61,6 +61,7 @@ export interface ListOptions {
version?: number;
meta?: Dictionary<string>;
nextTimestamp?: number;
target?: string;
}
export default class Now extends EventEmitter {
@@ -339,7 +340,7 @@ export default class Now extends EventEmitter {
async list(
app?: string,
{ version = 4, meta = {}, nextTimestamp }: ListOptions = {},
{ version = 4, meta = {}, nextTimestamp, target }: ListOptions = {},
prod?: boolean
) {
const fetchRetry = async (url: string, options: FetchOptions = {}) => {
@@ -405,6 +406,8 @@ export default class Now extends EventEmitter {
}
if (prod) {
query.set('target', 'production');
} else if (target) {
query.set('target', target);
}
const response = await fetchRetry(`/v${version}/now/deployments?${query}`);

View File

@@ -1,15 +1,10 @@
import { Org, Project } from '@vercel-internals/types';
import Client from '../client';
import setupAndLink from '../link/setup-and-link';
import param from '../output/param';
import { getCommandName } from '../pkg-name';
import { getLinkedProject } from '../projects/link';
import type { SetupAndLinkOptions } from '../link/setup-and-link';
type LinkResult = {
org: Org;
project: Project;
};
import type { ProjectLinked } from '@vercel-internals/types';
/**
* Checks if a project is already linked and if not, links the project and
@@ -23,15 +18,15 @@ type LinkResult = {
* directory
* @param opts.projectName - The project name to use when linking, otherwise
* the current directory
* @returns {Promise<LinkResult|number>} Returns a numeric exit code when aborted or
* @returns {Promise<ProjectLinked|number>} Returns a numeric exit code when aborted or
* error, otherwise an object containing the org an project
*/
export async function ensureLink(
commandName: string,
client: Client,
cwd: string,
opts: SetupAndLinkOptions
): Promise<LinkResult | number> {
opts: SetupAndLinkOptions = {}
): Promise<ProjectLinked | number> {
let { link } = opts;
if (!link) {
link = await getLinkedProject(client, cwd);
@@ -61,5 +56,5 @@ export async function ensureLink(
return link.exitCode;
}
return { org: link.org, project: link.project };
return link;
}

View File

@@ -1,23 +1,26 @@
import chalk from 'chalk';
import inquirer from 'inquirer';
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 slugify from '@sindresorhus/slugify';
import { basename, join, normalize } from 'path';
import { normalizePath, traverseUpDirectories } from '@vercel/build-utils';
import { lstat, readJSON, outputJSON } 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 { VERCEL_DIR, VERCEL_DIR_REPO, writeReadme } 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 { Framework } from '@vercel/frameworks';
import type { Project } from '@vercel-internals/types';
import createProject from '../projects/create-project';
import { detectProjects } from '../projects/detect-projects';
import { repoInfoToUrl } from '../git/repo-info-to-url';
import { connectGitProvider, parseRepoUrl } from '../git/connect-git-provider';
const home = homedir();
@@ -39,14 +42,22 @@ export interface RepoLink {
repoConfig?: RepoProjectsConfig;
}
export interface EnsureRepoLinkOptions {
yes: boolean;
overwrite: boolean;
}
/**
* 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> {
export async function getRepoLink(
client: Client,
cwd: string
): Promise<RepoLink | undefined> {
// Determine where the root of the repo is
const rootPath = await findRepoRoot(cwd);
const rootPath = await findRepoRoot(client, cwd);
if (!rootPath) return undefined;
// Read the `repo.json`, if this repo has already been linked
@@ -63,11 +74,11 @@ export async function getRepoLink(cwd: string): Promise<RepoLink | undefined> {
export async function ensureRepoLink(
client: Client,
cwd: string,
yes = false
{ yes, overwrite }: EnsureRepoLinkOptions
): Promise<RepoLink | undefined> {
const { output } = client;
const repoLink = await getRepoLink(cwd);
const repoLink = await getRepoLink(client, cwd);
if (repoLink) {
output.debug(`Found Git repository root directory: ${repoLink.rootPath}`);
} else {
@@ -75,7 +86,14 @@ export async function ensureRepoLink(
}
let { rootPath, repoConfig, repoConfigPath } = repoLink;
if (!repoConfig) {
if (overwrite || !repoConfig) {
// Detect the projects on the filesystem out of band, so that
// they will be ready by the time the projects are listed
const detectedProjectsPromise = detectProjects(rootPath).catch(err => {
output.debug(`Failed to detect local projects: ${err}`);
return new Map<string, Framework[]>();
});
// Not yet linked, so prompt user to begin linking
let shouldLink =
yes ||
@@ -112,64 +130,163 @@ export async function ensureRepoLink(
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 => {
choices: remoteNames.sort().map(name => {
return { name: name, value: name };
}),
default: originIndex === -1 ? 0 : originIndex,
default: remoteNames.includes('origin') ? 'origin' : undefined,
});
remoteName = answer.value;
}
const repoUrl = remoteUrls[remoteName];
const parsedRepoUrl = parseRepoUrl(repoUrl);
if (!parsedRepoUrl) {
throw new Error(`Failed to parse Git URL: ${repoUrl}`);
}
const repoUrlLink = output.link(repoUrl, repoInfoToUrl(parsedRepoUrl), {
fallback: () => link(repoUrl),
});
output.spinner(
`Fetching Projects for ${link(repoUrl)} under ${chalk.bold(org.slug)}`
`Fetching Projects for ${repoUrlLink} 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}`);
let projects: Project[] = [];
const query = new URLSearchParams({ repoUrl });
const projectsIterator = client.fetchPaginated<{
projects: Project[];
}>(`/v9/projects?${query}`);
const detectedProjects = await detectedProjectsPromise;
for await (const chunk of projectsIterator) {
projects = projects.concat(chunk.projects);
if (chunk.pagination.next) {
output.spinner(`Found ${chalk.bold(projects.length)} Projects…`, 0);
}
}
if (projects.length === 0) {
output.log(
`No Projects are linked to ${link(repoUrl)} under ${chalk.bold(
`No Projects are linked to ${repoUrlLink} 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(
`Found ${pluralize(
'Project',
projects.length
)} linked to ${link(repoUrl)} under ${chalk.bold(org.slug)}:`
projects.length,
true
)} linked to ${repoUrlLink} under ${chalk.bold(org.slug)}`
);
}
// For any projects that already exists on Vercel, remove them from the
// locally detected directories. Any remaining ones will be prompted to
// create new Projects for.
for (const project of projects) {
output.print(` * ${chalk.cyan(`${org.slug}/${project.name}\n`)}`);
detectedProjects.delete(project.rootDirectory ?? '');
}
shouldLink =
yes ||
(await confirm(
client,
`Link to ${projects.length === 1 ? 'it' : 'them'}?`,
true
));
const detectedProjectsCount = Array.from(detectedProjects.values()).reduce(
(o, f) => o + f.length,
0
);
if (detectedProjectsCount > 0) {
output.log(
`Detected ${pluralize(
'new Project',
detectedProjectsCount,
true
)} that may be created.`
);
}
if (!shouldLink) {
output.print(`Canceled. Repository not linked.\n`);
const addSeparators = projects.length > 0 && detectedProjectsCount > 0;
const { selected } = await client.prompt({
type: 'checkbox',
name: 'selected',
message: `Which Projects should be ${
projects.length ? 'linked to' : 'created'
}?`,
choices: [
...(addSeparators
? [new inquirer.Separator('----- Existing Projects -----')]
: []),
...projects.map(project => {
return {
name: `${org.slug}/${project.name}`,
value: project,
checked: true,
};
}),
...(addSeparators
? [new inquirer.Separator('----- New Projects to be created -----')]
: []),
...Array.from(detectedProjects.entries()).flatMap(
([rootDirectory, frameworks]) =>
frameworks.map((framework, i) => {
const name = slugify(
[
basename(rootPath),
basename(rootDirectory),
i > 0 ? framework.slug : '',
]
.filter(Boolean)
.join('-')
);
return {
name: `${org.slug}/${name} (${framework.name})`,
value: {
newProject: true,
rootDirectory,
name,
framework,
},
// Checked by default when there are no other existing Projects
checked: projects.length === 0,
};
})
),
],
});
if (selected.length === 0) {
output.print(`No Projects were selected. Repository not linked.\n`);
return;
}
for (let i = 0; i < selected.length; i++) {
const selection = selected[i];
if (!selection.newProject) continue;
const orgAndName = `${org.slug}/${selection.name}`;
output.spinner(`Creating new Project: ${orgAndName}`);
delete selection.newProject;
if (!selection.rootDirectory) delete selection.rootDirectory;
selected[i] = await createProject(client, {
...selection,
framework: selection.framework.slug,
});
await connectGitProvider(
client,
org,
selected[i].id,
parsedRepoUrl.provider,
`${parsedRepoUrl.org}/${parsedRepoUrl.repo}`
);
output.log(
`Created new Project: ${output.link(
orgAndName,
`https://vercel.com/${orgAndName}`,
{ fallback: false }
)}`
);
}
repoConfig = {
orgId: org.id,
remoteName,
projects: projects.map(project => {
projects: selected.map((project: Project) => {
return {
id: project.id,
name: project.name,
@@ -179,19 +296,18 @@ export async function ensureRepoLink(
};
await outputJSON(repoConfigPath, repoConfig, { spaces: 2 });
await writeFile(
join(rootPath, VERCEL_DIR, VERCEL_DIR_README),
await readFile(join(__dirname, 'VERCEL_DIR_README.txt'), 'utf8')
);
await writeReadme(rootPath);
// update .gitignore
const isGitIgnoreUpdated = await addToGitIgnore(rootPath);
output.print(
prependEmoji(
`Linked to ${link(repoUrl)} under ${chalk.bold(
org.slug
)} (created ${VERCEL_DIR}${
`Linked to ${pluralize(
'Project',
selected.length,
true
)} under ${chalk.bold(org.slug)} (created ${VERCEL_DIR}${
isGitIgnoreUpdated ? ' and added it to .gitignore' : ''
})`,
emoji('link')
@@ -211,45 +327,47 @@ export async function ensureRepoLink(
* 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)) {
export async function findRepoRoot(
client: Client,
start: string
): Promise<string | undefined> {
const { debug } = client.output;
const REPO_JSON_PATH = join(VERCEL_DIR, VERCEL_DIR_REPO);
const GIT_CONFIG_PATH = normalize('.git/config');
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.
debug('Arrived at home directory');
break;
}
// if `.vercel/repo.json` exists (already linked),
// then consider this the repo root
const repoConfigPath = join(current, VERCEL_DIR, VERCEL_DIR_REPO);
const repoConfigPath = join(current, REPO_JSON_PATH);
let stat = await lstat(repoConfigPath).catch(err => {
if (err.code !== 'ENOENT') throw err;
});
if (stat) {
debug(`Found "${REPO_JSON_PATH}" - detected "${current}" as repo root`);
return current;
}
// if `.git/config` exists (unlinked),
// then consider this the repo root
const gitConfigPath = join(current, '.git/config');
const gitConfigPath = join(current, GIT_CONFIG_PATH);
stat = await lstat(gitConfigPath).catch(err => {
if (err.code !== 'ENOENT') throw err;
});
if (stat) {
debug(`Found "${GIT_CONFIG_PATH}" - detected "${current}" as repo root`);
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;
}
debug('Aborting search for repo root');
}
function sortByDirectory(a: RepoProjectConfig, b: RepoProjectConfig): number {
@@ -268,7 +386,7 @@ export function findProjectsFromPath(
path: string
): RepoProjectConfig[] {
const normalizedPath = normalizePath(path);
return projects
const matches = projects
.slice()
.sort(sortByDirectory)
.filter(project => {
@@ -281,14 +399,9 @@ export function findProjectsFromPath(
normalizedPath.startsWith(`${project.directory}/`)
);
});
}
/**
* TODO: remove
*/
export function findProjectFromPath(
projects: RepoProjectConfig[],
path: string
): RepoProjectConfig | undefined {
return findProjectsFromPath(projects, path)[0];
// If there are multiple matches, we only want the most relevant
// selections (with the deepest directory depth), so pick the first
// one and filter on those matches.
const firstMatch = matches[0];
return matches.filter(match => match.directory === firstMatch.directory);
}

View File

@@ -10,7 +10,6 @@ import {
VERCEL_DIR_PROJECT,
} from '../projects/link';
import createProject from '../projects/create-project';
import updateProject from '../projects/update-project';
import Client from '../client';
import handleError from '../handle-error';
import confirm from '../input/confirm';
@@ -245,10 +244,10 @@ export default async function setupAndLink(
settings.rootDirectory = rootDirectory;
}
const project = await createProject(client, newProjectName);
await updateProject(client, project.id, settings);
Object.assign(project, settings);
const project = await createProject(client, {
...settings,
name: newProjectName,
});
await linkFolderToProject(
client,

View File

@@ -3,9 +3,9 @@ import * as ansiEscapes from 'ansi-escapes';
import { supportsHyperlink as detectSupportsHyperlink } from 'supports-hyperlinks';
import renderLink from './link';
import wait, { StopSpinner } from './wait';
import type { WritableTTY } from '@vercel-internals/types';
import { errorToString } from '@vercel/error-utils';
import { removeEmoji } from '../emoji';
import type * as tty from 'tty';
const IS_TEST = process.env.NODE_ENV === 'test';
@@ -24,7 +24,7 @@ interface LinkOptions {
}
export class Output {
stream: WritableTTY;
stream: tty.WriteStream;
debugEnabled: boolean;
supportsHyperlink: boolean;
colorDisabled: boolean;
@@ -32,7 +32,7 @@ export class Output {
private _spinner: StopSpinner | null;
constructor(
stream: WritableTTY,
stream: tty.WriteStream,
{
debug: debugEnabled = false,
supportsHyperlink = detectSupportsHyperlink(stream),

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