Compare commits

...

73 Commits

Author SHA1 Message Date
Steven
15c7ad241a Publish Stable
- @vercel/build-utils@2.17.0
 - vercel@24.2.1
 - @vercel/client@11.0.1
 - @vercel/frameworks@0.9.0
 - @vercel/go@1.4.1
 - @vercel/node@1.15.1
 - @vercel/python@2.3.1
 - @vercel/redwood@0.8.1
 - @vercel/ruby@1.3.4
 - @vercel/static-build@0.24.1
2022-05-09 10:41:56 -04:00
Steven
ec57654b5b Publish Canary
- @vercel/build-utils@2.16.1-canary.4
 - vercel@24.2.1-canary.4
 - @vercel/client@11.0.1-canary.4
 - @vercel/go@1.4.1-canary.4
 - @vercel/node@1.15.1-canary.4
 - @vercel/python@2.3.1-canary.4
 - @vercel/redwood@0.8.1-canary.4
 - @vercel/ruby@1.3.4-canary.4
 - @vercel/static-build@0.24.1-canary.4
2022-05-09 10:13:58 -04:00
Steven
3b9a9878bc [build-utils] Add Node.js 16.x (#7772)
Add support for Node.js 16

- Related to https://github.com/aws/aws-lambda-base-images/issues/14#issuecomment-1120864028
2022-05-09 10:13:11 -04:00
Nathan Rajlich
70b7db1a15 [node] Move @types/jest to "devDependencies" (#7768)
This should be a dev dep.
2022-05-09 09:23:10 -04:00
Nathan Rajlich
41d6666139 Publish Canary
- @vercel/build-utils@2.16.1-canary.3
 - vercel@24.2.1-canary.3
 - @vercel/client@11.0.1-canary.3
 - @vercel/frameworks@0.8.1-canary.1
 - @vercel/go@1.4.1-canary.3
 - @vercel/node@1.15.1-canary.3
 - @vercel/python@2.3.1-canary.3
 - @vercel/redwood@0.8.1-canary.3
 - @vercel/ruby@1.3.4-canary.3
 - @vercel/static-build@0.24.1-canary.3
2022-05-07 16:08:08 -07:00
Nathan Rajlich
2857219f89 Fix "astro" slug 2022-05-07 11:57:14 -07:00
Tony Sullivan
246c2a0f5d [frameworks] Add Astro (#7747) 2022-05-06 16:51:42 -07:00
Ethan Arrowood
d91bca7d6b Publish Canary
- @vercel/build-utils@2.16.1-canary.2
 - vercel@24.2.1-canary.2
 - @vercel/client@11.0.1-canary.2
 - @vercel/frameworks@0.8.1-canary.0
 - @vercel/go@1.4.1-canary.2
 - @vercel/node@1.15.1-canary.2
 - @vercel/python@2.3.1-canary.2
 - @vercel/redwood@0.8.1-canary.2
 - @vercel/ruby@1.3.4-canary.2
 - @vercel/static-build@0.24.1-canary.2
2022-05-06 14:16:00 -06:00
Ethan Arrowood
be54fce67b [build-utils] add pnpm7 to path when lockfile v5.4 is detected (#7758)
### Related Issues

Adds support for `pnpm@7` using a similar lock file detection and path setting method that `npm@7` uses

### 📋 Checklist

<!--
  Please keep your PR as a Draft until the checklist is complete
-->

#### Tests

- [x] The code changed/added as part of this PR has been covered with tests
- [x] All tests pass locally with `yarn test-unit`

#### Code Review

- [x] This PR has a concise title and thorough description useful to a reviewer
- [ ] Issue from task tracker has a link to this PR
2022-05-06 19:47:41 +00:00
Sean Massa
7753bb8d89 [cli] check prebuilt target env against deploy target env (#7748)
This check prevents a user from creating a build output targeting one environment (like `preview`) and deploying targeting another environment (like `production`). It checks `.vercel/output/builds.json` for the `target` property to decide this. If that file is missing, the check is not run.
2022-05-06 07:06:07 +00:00
Logan McAnsh
ce17ac5c35 [frameworks] Update Remix detection to check for "remix.config.js" (#7761)
The "remix" magic package is no longer preferred by Remix,
so anyone who is using a modern template won't have it installed.

Checking for `remix.config.js` is safer detection logic.
2022-05-05 19:13:44 -07:00
Sean Massa
8006fc32b8 [cli] require non-TTY uses of pull to also pass --yes (#7700)
Checks for `TTY` input to decide to handle the command or not. If the input is not TTY and the user did not pass the `--yes` option, the CLi will exit with the message:

> Command `vercel pull` requires confirmation. Use option "--yes" to confirm.
2022-05-05 22:40:26 +00:00
Nathan Rajlich
8038a90db1 [node] Use TypeScript for unit tests (#7756)
Random change I had in my working directory.
2022-05-05 19:13:01 +00:00
agadzik
f88c862e9d Publish Canary
- @vercel/build-utils@2.16.1-canary.1
 - vercel@24.2.1-canary.1
 - @vercel/client@11.0.1-canary.1
 - @vercel/go@1.4.1-canary.1
 - @vercel/node@1.15.1-canary.1
 - @vercel/python@2.3.1-canary.1
 - @vercel/redwood@0.8.1-canary.1
 - @vercel/ruby@1.3.4-canary.1
 - @vercel/static-build@0.24.1-canary.1
2022-05-05 14:11:13 -04:00
Nathan Rajlich
9170820371 [build-utils] Add ShouldServe type (#7755)
Version 3 Builders can define a `shouldServe()` function that is used in `vercel dev`, so add the proper type for that.
2022-05-05 17:06:20 +00:00
Andrew Gadzik
c881546e0e [build-utils] Add supported list of workspace managers (#7737)
In order to support monorepo detection, we need to build out the list of supported workspace managers so that our helper functions and API endpoints can utilize the same `detectFramework` function / logic to detect a workspace manager for a given git repository.

### Related Issues

- Closes https://github.com/vercel/vercel/issues/7731

### 📋 Checklist

<!--
  Please keep your PR as a Draft until the checklist is complete
-->

#### Tests

- [x] The code changed/added as part of this PR has been covered with tests
- [x] All tests pass locally with `yarn test-unit`

#### Code Review

- [x] This PR has a concise title and thorough description useful to a reviewer
- [x] Issue from task tracker has a link to this PR
2022-05-05 14:44:34 +00:00
Nathan Rajlich
fa21db98e4 [cli] Revert "Use @vercel/fetch-retry in CLI integration tests" (#7738)
This reverts commit 03a8fbd3a7 (#7360).
2022-05-04 17:15:54 +00:00
Ethan Arrowood
8eabbfc666 Publish Canary
- @vercel/build-utils@2.16.1-canary.0
 - vercel@24.2.1-canary.0
 - @vercel/client@11.0.1-canary.0
 - @vercel/go@1.4.1-canary.0
 - @vercel/node@1.15.1-canary.0
 - @vercel/python@2.3.1-canary.0
 - @vercel/redwood@0.8.1-canary.0
 - @vercel/ruby@1.3.4-canary.0
 - @vercel/static-build@0.24.1-canary.0
2022-05-02 11:33:22 -06:00
Ethan Arrowood
6783f7afc9 [build-utils] Fix package manager auto detection precedence (#7733)
### Related Issues

improves package manager auto detection so that when multiple lock files are found, the highest priority one is selected rather than defaulting to yarn. 

### 📋 Checklist

<!--
  Please keep your PR as a Draft until the checklist is complete
-->

#### Tests

- [x] The code changed/added as part of this PR has been covered with tests
- [ ] All tests pass locally with `yarn test-unit`

#### Code Review

- [ ] This PR has a concise title and thorough description useful to a reviewer
- [ ] Issue from task tracker has a link to this PR
2022-05-02 17:22:01 +00:00
Steven
a400b9b29d Publish Stable
- @vercel/build-utils@2.16.0
 - vercel@24.2.0
 - @vercel/client@11.0.0
 - @vercel/frameworks@0.8.0
 - @vercel/go@1.4.0
 - @vercel/node-bridge@2.2.1
 - @vercel/node@1.15.0
 - @vercel/python@2.3.0
 - @vercel/redwood@0.8.0
 - @vercel/routing-utils@1.13.2
 - @vercel/ruby@1.3.3
 - @vercel/static-build@0.24.0
 - @vercel/static-config@1.0.0
2022-04-29 14:11:32 -04:00
Steven
b549c37149 [build-utils][static-build] Replace 00a0 with space (#7732) 2022-04-28 22:25:24 -04:00
Nathan Rajlich
30d5e64291 Publish Canary
- @vercel/build-utils@2.15.2-canary.6
 - vercel@24.1.1-canary.8
 - @vercel/client@10.4.2-canary.7
 - @vercel/go@1.3.3-canary.6
 - @vercel/node@1.14.2-canary.7
 - @vercel/python@2.2.3-canary.6
 - @vercel/redwood@0.7.1-canary.6
 - @vercel/ruby@1.3.3-canary.6
 - @vercel/static-build@0.23.2-canary.6
2022-04-28 17:50:59 -07:00
Nathan Rajlich
47c2c361d2 [build-utils] Update "yazl" dependency (#7734)
The older version of "yazl" was using `new Buffer()` which causes
deprecation warnings to be printed. The latest version avoids that.
2022-04-28 17:47:10 -07:00
Nathan Rajlich
438576fc7c Publish Canary
- @vercel/build-utils@2.15.2-canary.5
 - vercel@24.1.1-canary.7
 - @vercel/client@10.4.2-canary.6
 - @vercel/frameworks@0.7.2-canary.1
 - @vercel/go@1.3.3-canary.5
 - @vercel/node@1.14.2-canary.6
 - @vercel/python@2.2.3-canary.5
 - @vercel/redwood@0.7.1-canary.5
 - @vercel/ruby@1.3.3-canary.5
 - @vercel/static-build@0.23.2-canary.5
 - @vercel/static-config@1.0.0-canary.1
2022-04-28 11:53:39 -07:00
Steven
b30343ef7b [tests] Bump dependencies for jest/eslint/prettier/turbo/etc (#7727) 2022-04-28 14:52:46 -04:00
Ethan Arrowood
2dc0dfa572 [node][static-build][redwood] Add root path pattern to prepareCache() (#7710) 2022-04-27 21:17:44 -07:00
Steven
9ee54b3dd6 [static-build] Resolve git.io links (#7722)
https://github.blog/changelog/2022-04-25-git-io-deprecation/
2022-04-26 21:57:12 -04:00
Steven
9d67e0bc06 [python] Add discontinue date for Python 3.6 (#7709)
This PR does a few things:
- Changes the existing warning message for Python 3.6 to print a discontinue date
- Will automatically fail new Python 3.6 deployments created after that date
- Consolidates logic to make Python version selection work in a similar manner to Node.js version selection
- Changes tests from JS to TS
2022-04-26 15:49:19 -04:00
Steven
466135cf84 Publish Canary
- @vercel/build-utils@2.15.2-canary.4
 - vercel@24.1.1-canary.6
 - @vercel/client@10.4.2-canary.5
 - @vercel/go@1.3.3-canary.4
 - @vercel/node@1.14.2-canary.5
 - @vercel/python@2.2.3-canary.4
 - @vercel/redwood@0.7.1-canary.4
 - @vercel/ruby@1.3.3-canary.4
 - @vercel/static-build@0.23.2-canary.4
2022-04-25 12:49:34 -04:00
Steven
eab2e229dc [build-utils] Add warning for experimental Node.js (#7717) 2022-04-25 12:49:04 -04:00
Steven
698b89a2ba Publish Canary
- @vercel/build-utils@2.15.2-canary.3
 - vercel@24.1.1-canary.5
 - @vercel/client@10.4.2-canary.4
 - @vercel/frameworks@0.7.2-canary.0
 - @vercel/go@1.3.3-canary.3
 - @vercel/node@1.14.2-canary.4
 - @vercel/python@2.2.3-canary.3
 - @vercel/redwood@0.7.1-canary.3
 - @vercel/ruby@1.3.3-canary.3
 - @vercel/static-build@0.23.2-canary.3
2022-04-25 12:02:13 -04:00
Steven
bae2a2e4df [python] Upgrade tests (#7711)
* [python] Upgrade tests

* Fix latest sanic asgi

* Bump flask

* Change requirements.txt to Pipfile

* Fix dev test

* Use verbose requirements.txt

* Flip requirements
2022-04-25 12:01:49 -04:00
Steven
57916bb712 [build-utils] Add env var ENABLE_EXPERIMENTAL_NODE16 (#7489)
This PR uses an environment variable since this feature is not available to all accounts yet.
2022-04-25 11:07:16 -04:00
Sean Massa
12bbd4e8eb [cli] Update language of prebuilt error (#7702) 2022-04-22 09:55:27 -07:00
Aaron Morris
4e4c7023dc [frameworks] Add opt-in darkModeLogo to Framework in packages/frameworks (#7693)
Supply light mode logos over the frameworks API to be consumed by front

### Related Issues

### 📋 Checklist

<!--
  Please keep your PR as a Draft until the checklist is complete
-->

#### Tests

- [ ] The code changed/added as part of this PR has been covered with tests
- [ ] All tests pass locally with `yarn test-unit`

#### Code Review

- [x] This PR has a concise title and thorough description useful to a reviewer
- [ ] Issue from task tracker has a link to this PR
2022-04-22 16:49:39 +00:00
Nathan Rajlich
41805790e7 [static-build] Support Build Output API v3 cache property in prepareCache() (#7704)
When a framework that outputs Build Output API v3 specifies the `cache`
property in the `config.json` file then static-build will include those
files in the `prepareCache()` function result.
2022-04-21 18:04:21 -07:00
Ethan Arrowood
6ad77ae8e1 Publish Canary
- vercel@24.1.1-canary.4
 - @vercel/client@10.4.2-canary.3
2022-04-21 16:01:41 -06:00
Ethan Arrowood
e8daf36cd7 only revert override feature (#7701) 2022-04-21 15:59:55 -06:00
Nathan Rajlich
c319a2c499 [client] Remove single file deployment root path / route (#7699)
This is strange behavior, and also inconsistent compared to how a Git deployment is created. Let's remove it.
2022-04-21 00:35:25 +00:00
Sean Massa
a410baa797 [cli] check for prebuilt directory (#7697)
When using `vc deploy --prebuilt`, we need to check for an actual ".vercel/output" before attempting to deploy. This PR adds a a check for that. Now...

In a directory without `.vercel/output`:

```
$ vc deploy --prebuilt
Error! Option `--prebuilt` was used, but no prebuilt deploy found in ".vercel/output"
```

In a direcotry with `.vercel/output` (where I've not linked it yet):

```
$ vc deploy --prebuilt
? Set up and deploy “~/source/vercel/examples/build-output-api/serverless-function”? [Y/n]
```
2022-04-20 19:54:47 +00:00
Dominik Ferber
625568e659 fix ts-eager (#7677)
Switches await import() to require().default so that ts-eager understands them.

closes #7676
2022-04-20 10:30:48 -07:00
Nathan Rajlich
41868c1fe0 [cli] Ensure .vercel directory is created in vc pull (#7695)
When the `.vercel` directory does not exist and the env vars
(`VERCEL_PROJECT_ID`/`VERCEL_ORG_ID`) are used for project
linking, the `vercel pull` command was throwing an error:

```
$ VERCEL_PROJECT_ID=xxxxxxxx VERCEL_ORG_ID=xxxxxxxxxx vercel pull
Vercel CLI 24.1.1-canary.3 — https://vercel.com/feedback
Downloading "development" Environment Variables for Project t
Error! ENOENT: no such file or directory, open '/Code/t/.vercel/.env.development.local'
```
2022-04-19 20:49:44 -07:00
Steven
1b644f1218 [docs] Update available runtimes (#7692) 2022-04-19 12:08:46 -04:00
Sean Massa
29ea0fb06b [examples] Remove amp example (#7686) 2022-04-17 19:29:56 -04:00
Ethan Arrowood
b61f049f11 Publish Canary
- @vercel/build-utils@2.15.2-canary.2
 - vercel@24.1.1-canary.3
 - @vercel/client@10.4.2-canary.2
 - @vercel/go@1.3.3-canary.2
 - @vercel/node@1.14.2-canary.3
 - @vercel/python@2.2.3-canary.2
 - @vercel/redwood@0.7.1-canary.2
 - @vercel/ruby@1.3.3-canary.2
 - @vercel/static-build@0.23.2-canary.2
2022-04-15 15:26:55 -06:00
Ethan Arrowood
16e28f326b [cli] Allow projectSettings override in vercel.json (#7619)
This PR adds the required changes to the CLI to support overridable `vercel.json` `projectSettings` values. This PR is linked with changes on the API side too.

- The core changes are in `packages/cli/src/commands/deploy/index.ts`
- An unused code path was removed from `packages/client/src/create-deployment.ts`, `packages/cli/src/util/deploy/process-deployment.ts` and `packages/cli/src/util/index.ts`.

This will only work for *existing* deployment (not *new* ones). This is implemented by checking the `status` of the deployment (is it linked or not). An error is thrown if the user attempts to send overrided settings to a new project. 

A warning is outputted when overrided settings are discovered and shares what settings are being overridden.

### 📋 Checklist

<!--
  Please keep your PR as a Draft until the checklist is complete
-->

#### Tests

- [x] The code changed/added as part of this PR has been covered with tests
- [x] All tests pass locally with `yarn test-unit`

#### Code Review

- [x] This PR has a concise title and thorough description useful to a reviewer
- [ ] Issue from task tracker has a link to this PR
2022-04-15 21:20:00 +00:00
Sean Massa
276397c940 [examples] fixed vercel deploy link (#7684) 2022-04-15 13:55:42 -05:00
Nathan Rajlich
85d7311199 [static-build] Misc TypeScript cleanup (#7682)
This was just sitting on my local checkout so might as well push it.
2022-04-15 09:42:32 -07:00
Sean Massa
b8114b8b39 [cli] set environment properly in vc pull (#7656)
This PR sets environment properly in `vc pull`. The system env vars were not respecting the target `--environment`.

Adds tests for related `vc env` logic.
2022-04-15 04:41:29 +00:00
QianyuPan
d63e8d3187 [go] Add support for version 1.18 (#7590)
Go 1.18 has been released, but I find that I can't deploy my project on Vercel when `go.mod` has `go 1.18` in it.

1.18 adds some very useful features. It would be very valuable for developers to have Vercel support 1.18.
2022-04-14 19:37:01 -04:00
JJ Kasper
11d3dd04aa Publish Canary
- vercel@24.1.1-canary.2
 - @vercel/node-bridge@2.2.1-canary.0
 - @vercel/node@1.14.2-canary.2
2022-04-14 17:04:05 -05:00
JJ Kasper
46bf95ee36 [node-bridge] Ensure content-type is always set with multi-payloads (#7681)
### Related Issues

This ensures a content-type is always set inside of each part of the multi-part payload as it's needed for proper parsing. This also ensures non-200 status codes/headers are passed back separate when they differ so that they can be handled independently without assuming they all match. 

x-ref: https://vercel.slack.com/archives/C03AYHB6MA9
x-ref: https://github.com/vercel/vercel/pull/7507

### 📋 Checklist

<!--
  Please keep your PR as a Draft until the checklist is complete
-->

#### Tests

- [ ] The code changed/added as part of this PR has been covered with tests
- [ ] All tests pass locally with `yarn test-unit`

#### Code Review

- [ ] This PR has a concise title and thorough description useful to a reviewer
- [ ] Issue from task tracker has a link to this PR
2022-04-14 22:00:10 +00:00
Nathan Rajlich
92252468c2 [tests] Add turbo.json and bump to turbo 1.2.2 (#7620)
* Add `turbo.json`

Ran `npx @turbo/codemod create-turbo-config` like the warnings have been telling us to.

* [tests] Bump `turbo` and allow `turbo.json` in `.vercelignore` (#7623)

Co-authored-by: Jared Palmer <jared@jaredpalmer.com>
Co-authored-by: Steven <steven@ceriously.com>
2022-04-13 11:09:47 -04:00
Nathan Rajlich
71b83d5587 Publish Canary
- @vercel/build-utils@2.15.2-canary.1
 - vercel@24.1.1-canary.1
 - @vercel/client@10.4.2-canary.1
 - @vercel/go@1.3.3-canary.1
 - @vercel/node@1.14.2-canary.1
 - @vercel/python@2.2.3-canary.1
 - @vercel/redwood@0.7.1-canary.1
 - @vercel/ruby@1.3.3-canary.1
 - @vercel/static-build@0.23.2-canary.1
2022-04-12 15:57:36 -07:00
Nathan Rajlich
d9e5fdc5e4 [build-utils] Move "Installing dependencies..." log to runNpmInstall() (#7672)
Follow-up to #7671. Since `runNpmInstall()` might now be de-duped, only
print "Installing dependencies..." when the dependencies are actually
being installed. This avoids printing the log message unnecessarily when
the command won't actually be run, and also removes some duplication in
the Builders' code.
2022-04-12 16:23:01 -04:00
Nathan Rajlich
58f479c603 [static-build] Add support for Build Output v3 detection (#7669)
Adds detection logic for a framework / build script outputting Build Output v3
format to the filesystem. In this case, `static-build` will simply stop processing
after the Build Command since deserialization happens in the build-container side
of things (this is different compared to the v1 output which gets handled in this
Builder. The reason for that is because the v3 output matches what `vc build`
outputs vs. v1 which is a different format).
2022-04-12 09:09:50 -07:00
Nathan Rajlich
d62461d952 [build-utils] Only allow runNpmInstall() to run once per package.json (#7671)
Adds a best-effort optimization to only run `npm install` once per
`pacakge.json` file. This will save a lot of time in the single-sandbox
build world (i.e. `vc build`).
2022-04-12 09:09:10 -07:00
Steven
e7f524defb Publish Canary
- @vercel/build-utils@2.15.2-canary.0
 - vercel@24.1.1-canary.0
 - @vercel/client@10.4.2-canary.0
 - @vercel/go@1.3.3-canary.0
 - @vercel/node@1.14.2-canary.0
 - @vercel/python@2.2.3-canary.0
 - @vercel/redwood@0.7.1-canary.0
 - @vercel/ruby@1.3.3-canary.0
 - @vercel/static-build@0.23.2-canary.0
2022-04-12 08:58:38 -04:00
Steven
bdefd0d05d [docs] Fix links to docs (#7668) 2022-04-12 07:52:12 -04:00
Ethan Arrowood
ca522fc9f1 [node][redwood] update @vercel/nft to 0.18.1 (#7670)
### Related Issues

Updates the @vercel/nft dependency across the repo to v0.18.1.

### 📋 Checklist

<!--
  Please keep your PR as a Draft until the checklist is complete
-->

#### Tests

- [ ] The code changed/added as part of this PR has been covered with tests
- [ ] All tests pass locally with `yarn test-unit`

#### Code Review

- [ ] This PR has a concise title and thorough description useful to a reviewer
- [ ] Issue from task tracker has a link to this PR
2022-04-11 22:07:45 +00:00
Nathan Rajlich
4a8504fc45 Publish Stable
- @vercel/build-utils@2.15.1
 - vercel@24.1.0
 - @vercel/client@10.4.1
 - @vercel/frameworks@0.7.1
 - @vercel/go@1.3.2
 - @vercel/node@1.14.1
 - @vercel/python@2.2.2
 - @vercel/redwood@0.7.0
 - @vercel/routing-utils@1.13.1
 - @vercel/ruby@1.3.2
 - @vercel/static-build@0.23.1
2022-04-11 10:14:53 -07:00
Nathan Rajlich
576217b344 [static-build] Fix prepareCache() when deploying with --prebuilt (#7659)
The `prepareCache()` function was throwing an error when doing `vc deploy --prebuilt` because the `package.json` file is not present.

Also just cleaned up the logic a bit.
2022-04-09 20:51:38 +00:00
Steven
f03129ac7a [tests] Bump mkdocs test fixture (#7660)
* [tests] Bump mkdocs test fixture

* Bump pelican
2022-04-09 14:24:26 -04:00
Sean Massa
3eaad1fd52 [cli] remove deprecation warning for vc env (#7654)
Removes the deprecation warning in the CLI for `vc env`. This command will coexist with `vc pull` for a slightly different purpose.
2022-04-08 19:46:11 -04:00
Nathan Rajlich
4e471491d8 [build-utils] Add repoRootPath to PrepareCacheOptions (#7658)
`repoRootPath` is provided to the `prepareCache()` function and is the same value as provided to the `build()` function.

Also remove `cachePath` since it's no longer used.
2022-04-08 20:39:47 +00:00
Sean Massa
99395fd9fe [cli] trim env file path output (#7653) 2022-04-08 13:14:23 -05:00
Sean Massa
4980fe6513 [cli] remove references to vc pull --env-file (#7647) 2022-04-05 13:30:41 -07:00
Sean Massa
24676ae020 [cli] Add --environment to vc pull (#7624)
* refactor in prep for changes

* pull all target env files

* update `pull` usage

* move .env files into /.vercel

* tighten types in helper functions

* add return type

* remove "any" type

* keep original behavior

* only use --env for root-level env file name

* add --target flag

* deprecate --env for --env-file

* rename --target to --environment

* more renaming

* pr feedback

* update `vc pull --help`

* Update pull.ts

* remove env-file logic from `vc pull`

Co-authored-by: Steven <steven@ceriously.com>
2022-04-05 11:08:29 -07:00
Steven
72ada9abd8 [examples] Bump Next.js + React (#7644)
Bump Next.js + React + ESLint
2022-04-04 09:13:20 -04:00
Steven
da893e7c57 [tests] Increase timeout cli tests (#7640)
* [tests] Increase network timeout for GH Actions

* [tests] Increase CLI integration test timeout
2022-04-01 10:52:47 -04:00
Sean Massa
e40eecafc9 [cli] Add support for vc --cwd <dir> (#7577)
`vc`'s default command is `deploy`, which can lead to ambiguous cli invocations when running `vc dir-or-command` like:

```
$  vc list
Vercel CLI 23.1.2
Error! The supplied argument "list" is ambiguous.
If you wish to deploy the subdirectory "list", first run "cd list".
```

when run in a directory that contains a subdirectory "list". This conflict will happen with any current and future commands, like `vc build`.

In order to make sure the CLI can be invoked either way, this PR deprecates the default behavior. Going forward, a user would see the following.

**Conflicting Command, Run Command**

```bash
$ vc list
# warning: Did you mean to deploy the subdirectory "list"? Use `vc --cwd list` instead.
# ... runs the `list` command
```

**Conflicting Command, Deploy Directory**

```bash
$ vc --cwd list
# ... deploy as normal
```

---

Card: https://linear.app/vercel/issue/BUI-33/prevent-ambiguous-vc-command-oror-dir
2022-03-30 21:06:16 +00:00
Steven
d9e5342eba [examples] Fix angular demo url (#7635) 2022-03-30 11:53:42 -05:00
Steven
b0ab13778d [frameworks] Update comments (#7636)
The comments were outdated
2022-03-30 10:14:03 -04:00
194 changed files with 20794 additions and 3665 deletions

View File

@@ -11,7 +11,7 @@ on:
jobs:
test:
name: CLI
timeout-minutes: 30
timeout-minutes: 40
strategy:
fail-fast: false
matrix:

View File

@@ -6,6 +6,7 @@
!.yarnrc
!yarn.lock
!package.json
!turbo.json
# api
!api/
@@ -14,4 +15,4 @@
# packages
!packages/
!packages/frameworks
!packages/frameworks/**
!packages/frameworks/**

View File

@@ -307,15 +307,15 @@ This is a [class](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Refere
This is an abstract enumeration type that is implemented by one of the following possible `String` values:
- `nodejs14.x`
- `nodejs12.x`
- `nodejs10.x`
- `go1.x`
- `java11`
- `python3.9`
- `python3.6`
- `dotnetcore2.1`
- `ruby2.5`
- `provided`
- `dotnet6`
- `dotnetcore3.1`
- `ruby2.7`
- `provided.al2`
## `@vercel/build-utils` Helper Functions

View File

@@ -16,4 +16,4 @@ If you would not like to verify your domain, you can remove it from your account
#### Resources
- [Vercel Custom Domains Documentation](https://vercel.com/docs/v2/custom-domains)
- [Vercel Custom Domains Documentation](https://vercel.com/docs/concepts/projects/custom-domains)

View File

@@ -2,7 +2,7 @@
#### Why This Error Occurred
You ran `vercel dev` inside a project that contains a `vercel.json` file with `env` or `build.env` properties that use [Vercel Secrets](https://vercel.com/docs/v2/build-step#environment-variables).
You ran `vercel dev` inside a project that contains a `vercel.json` file with `env` or `build.env` properties that use [Vercel Secrets](https://vercel.com/docs/concepts/projects/environment-variables).
In order to use environment variables in your project locally that have values defined using the Vercel Secrets format (e.g. `@my-secret-value`), you will need to provide the value as an environment variable using a `.env`.
@@ -24,4 +24,4 @@ TEST=value
In the above example, `TEST` represents the name of the environment variable and `value` its value.
For more information on Environment Variables in development, [see the documentation](https://vercel.com/docs/v2/build-step#environment-variables).
For more information on Environment Variables in development, [see the documentation](https://vercel.com/docs/concepts/projects/environment-variables).

View File

@@ -1 +0,0 @@
.env

View File

@@ -1,19 +0,0 @@
# AMP Example
This directory is a brief example of an [AMP](https://amp.dev/) site that can be deployed to Vercel with zero configuration.
## Deploy Your Own
Deploy your own AMP project with Vercel.
[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?repository-url=https://github.com/vercel/vercel/tree/main/examples/amp)
_Live Example: https://amp-template.vercel.app_
### How We Created This Example
To get started deploying AMP with Vercel, you can use the [Vercel CLI](https://vercel.com/download) to initialize the project:
```shell
$ vercel init amp
```

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

View File

@@ -1,72 +0,0 @@
<!DOCTYPE html>
<html >
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width,minimum-scale=1" />
<link rel="shortcut icon" href="favicon.png">
<style amp-boilerplate>body{-webkit-animation:-amp-start 8s steps(1,end) 0s 1 normal both;-moz-animation:-amp-start 8s steps(1,end) 0s 1 normal both;-ms-animation:-amp-start 8s steps(1,end) 0s 1 normal both;animation:-amp-start 8s steps(1,end) 0s 1 normal both}@-webkit-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@-moz-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@-ms-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@-o-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}</style><noscript><style amp-boilerplate>body{-webkit-animation:none;-moz-animation:none;-ms-animation:none;animation:none}</style></noscript>
<link rel="canonical" href="index.html" />
<title>AMP Website</title>
<script async src="https://cdn.ampproject.org/v0.js"></script>
<style amp-custom>
body > * {
margin: 3rem 1rem;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
color: #525252;
}
h3 {
font-size: 2rem;
}
h4 {
margin-top: 2rem;
}
p {
font-size: 1.2rem;
line-height: 2rem;
}
.links {
display: flex;
justify-content: center;
margin-bottom: 3rem;
}
.links a {
margin: 0 10px;
font-size: 1rem;
color: #005af0;
}
</style>
</head>
<body>
<center>
<amp-img width=150 height=150 layout="fixed" class="logo" src="logo.png"></amp-img>
<h3>Welcome to your AMP page</h3>
<p>AMP is a web component framework to <br> easily create user-first websites, stories, ads and emails.</p>
<h4>Links</h4>
<div class="links">
<a href="https://amp.dev/">Homepage</a>
<a href="https://amp.dev/documentation/guides-and-tutorials/?format=websites">Tutorials</a>
<a href="https://amp.dev/documentation/examples/">Examples</a>
<a href="https://blog.amp.dev">Blog</a>
</div>
<h4>Ready to get started?</h4>
<div class="links">
<a href="https://amp.dev/documentation/guides-and-tutorials/start/create/?format=websites">Create your first AMP page</a>
</div>
<h4>Get involved</h4>
<div class="links">
<a href="https://twitter.com/amphtml">Twitter</a>
<a href="https://amphtml.slack.com">Slack</a>
<a href="https://amp.dev/events/amp-conf-2019">AMP Conf</a>
<a href="https://amp.dev/events/amp-roadshow">AMP Roadshow</a>
</div>
</center>
</body>
</html>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 43 KiB

View File

@@ -10,7 +10,7 @@ Deploy your own Angular project with Vercel.
[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?repository-url=https://github.com/vercel/vercel/tree/main/examples/angular&template=angular)
_Live Example: https://angular-now-examples.vercel.app_
_Live Example: https://angular-template.vercel.app_
## Development server

20
examples/astro/.gitignore vendored Normal file
View File

@@ -0,0 +1,20 @@
# build output
dist/
.output/
# dependencies
node_modules/
# logs
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
# environment variables
.env
.env.production
# macOS-specific files
.DS_Store

2
examples/astro/.npmrc Normal file
View File

@@ -0,0 +1,2 @@
# Expose Astro dependencies for `pnpm` users
shamefully-hoist=true

View File

@@ -0,0 +1 @@
README.md

42
examples/astro/README.md Normal file
View File

@@ -0,0 +1,42 @@
# Welcome to [Astro](https://astro.build)
[![Open in StackBlitz](https://developer.stackblitz.com/img/open_in_stackblitz.svg)](https://stackblitz.com/github/withastro/astro/tree/latest/examples/starter)
> 🧑‍🚀 **Seasoned astronaut?** Delete this file. Have fun!
## 🚀 Project Structure
Inside of your Astro project, you'll see the following folders and files:
```
/
├── public/
│ └── favicon.ico
├── src/
│ ├── components/
│ │ └── Layout.astro
│ └── pages/
│ └── index.astro
└── package.json
```
Astro looks for `.astro` or `.md` files in the `src/pages/` directory. Each page is exposed as a route based on its file name.
There's nothing special about `src/components/`, but that's where we like to put any Astro/React/Vue/Svelte/Preact components or layouts.
Any static assets, like images, can be placed in the `public/` directory.
## 🧞 Commands
All commands are run from the root of the project, from a terminal:
| Command | Action |
| :---------------- | :------------------------------------------- |
| `npm install` | Installs dependencies |
| `npm run dev` | Starts local dev server at `localhost:3000` |
| `npm run build` | Build your production site to `./dist/` |
| `npm run preview` | Preview your build locally, before deploying |
## 👀 Want to learn more?
Feel free to check [our documentation](https://github.com/withastro/astro) or jump into our [Discord server](https://astro.build/chat).

View File

@@ -0,0 +1,4 @@
import { defineConfig } from 'astro/config';
// https://astro.build/config
export default defineConfig({});

View File

@@ -0,0 +1,14 @@
{
"name": "@example/basics",
"version": "0.0.1",
"private": true,
"scripts": {
"dev": "astro dev",
"start": "astro dev",
"build": "astro build",
"preview": "astro preview"
},
"devDependencies": {
"astro": "^1.0.0-beta.20"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

View File

@@ -0,0 +1,55 @@
---
export interface Props {
title: string;
}
const { title } = Astro.props as Props;
---
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width">
<link rel="icon" type="image/x-icon" href="/favicon.ico" />
<title>{title}</title>
</head>
<body>
<slot />
</body>
</html>
<style>
:root {
--font-size-base: clamp(1rem, 0.34vw + 0.91rem, 1.19rem);
--font-size-lg: clamp(1.2rem, 0.7vw + 1.2rem, 1.5rem);
--font-size-xl: clamp(2.44rem, 2.38vw + 1.85rem, 3.75rem);
--color-text: hsl(12, 5%, 4%);
--color-bg: hsl(10, 21%, 95%);
}
html {
font-family: system-ui, sans-serif;
font-size: var(--font-size-base);
color: var(--color-text);
background-color: var(--color-bg);
}
body {
margin: 0;
}
:global(h1) {
font-size: var(--font-size-xl);
}
:global(h2) {
font-size: var(--font-size-lg);
}
:global(code) {
font-family: Menlo, Monaco, Lucida Console, Liberation Mono, DejaVu Sans Mono,
Bitstream Vera Sans Mono, Courier New, monospace;
}
</style>

View File

@@ -0,0 +1,174 @@
---
import Layout from '../components/Layout.astro';
---
<Layout title="Welcome to Astro.">
<main>
<h1>Welcome to <span class="text-gradient">Astro</span></h1>
<p class="instructions"><strong>Your first mission:</strong> tweak this message to try our hot module reloading. Check the <code>src/pages</code> directory!</p>
<ul role="list" class="link-card-grid">
<li class="link-card">
<a href="https://astro.build/integrations/">
<h2>Integrations <span>&rarr;</span></h2>
<p>Add component frameworks, Tailwind, Partytown, and more!</p>
</a>
</li>
<li class="link-card">
<a href="https://astro.build/themes/">
<h2>Themes <span>&rarr;</span></h2>
<p>Explore a galaxy of community-built starters.</p>
</a>
</li>
<li class="link-card">
<a href="https://docs.astro.build/">
<h2>Docs <span>&rarr;</span></h2>
<p>Learn our complete feature set and explore the API.</p>
</a>
</li>
<li class="link-card">
<a href="https://astro.build/chat/">
<h2>Chat <span>&rarr;</span></h2>
<p>
Ask, contribute, and have fun on our community Discord
<svg
class="heart"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 512 512"
width="16"
height="16"
fill="currentColor"
>
<title>heart</title>
<path d="M256 448l-30.164-27.211C118.718 322.442 48 258.61 48 179.095 48 114.221 97.918 64 162.4 64c36.399 0 70.717 16.742 93.6 43.947C278.882 80.742 313.199 64 349.6 64 414.082 64 464 114.221 464 179.095c0 79.516-70.719 143.348-177.836 241.694L256 448z" />
</svg>
</p>
</a>
</li>
</ul>
</main>
</Layout>
<style>
:root {
--color-border: hsl(17, 24%, 90%);
--astro-gradient: linear-gradient(0deg,#4F39FA, #DA62C4);
--link-gradient: linear-gradient(45deg, #4F39FA, #DA62C4 30%, var(--color-border) 60%);
--night-sky-gradient: linear-gradient(0deg, #392362 -33%, #431f69 10%, #30216b 50%, #1f1638 100%);
}
h2 {
margin: 0;
transition: color 0.6s cubic-bezier(0.22, 1, 0.36, 1);
}
h2 span {
display: inline-block;
transition: transform 0.3s cubic-bezier(0.22, 1, 0.36, 1);
}
code {
font-size: 0.875em;
border: 0.1em solid var(--color-border);
border-radius: 4px;
padding: 0.15em 0.25em;
}
main {
margin: auto;
padding: 1em;
max-width: 60ch;
}
.text-gradient {
font-weight: 900;
background-image: var(--astro-gradient);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-size: 100% 200%;
background-position-y: 100%;
border-radius: 0.4rem;
animation: pulse 4s ease-in-out infinite;
}
@keyframes pulse {
0%, 100% {
background-position-y: 0%;
}
50% {
background-position-y: 80%;
}
}
.instructions {
line-height: 1.8;
margin-bottom: 2rem;
background-image: var(--night-sky-gradient);
padding: 1.5rem;
border-radius: 0.4rem;
color: var(--color-bg);
}
.link-card-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(24ch, 1fr));
gap: 1rem;
padding: 0;
}
.link-card {
list-style: none;
display: flex;
padding: 0.15rem;
background-image: var(--link-gradient);
background-size: 400%;
border-radius: 0.5rem;
background-position: 100%;
transition: background-position 0.6s cubic-bezier(0.22, 1, 0.36, 1);
}
.link-card > a {
width: 100%;
text-decoration: none;
line-height: 1.4;
padding: 1em 1.3em;
border-radius: 0.35rem;
color: var(--text-color);
background-color: white;
opacity: 0.8;
}
.link-card:is(:hover, :focus-within) {
background-position: 0;
}
.link-card:is(:hover, :focus-within) h2 {
color: #4F39FA;
}
.link-card:is(:hover, :focus-within) h2 span {
transform: translateX(2px);
}
.heart {
display: inline-block;
color: #DA62C4;
animation: heartbeat 3s ease-in-out infinite;
}
@keyframes heartbeat {
0%,
50%,
100% {
transform: scale(1);
}
5% {
transform: scale(1.125);
}
10% {
transform: scale(1.05);
}
15% {
transform: scale(1.25);
}
}
</style>

View File

@@ -0,0 +1,15 @@
{
"compilerOptions": {
// Enable top-level await, and other modern ESM features.
"target": "ESNext",
"module": "ESNext",
// Enable node-style module resolution, for things like npm package imports.
"moduleResolution": "node",
// Enable JSON imports.
"resolveJsonModule": true,
// Enable stricter transpilation for better output.
"isolatedModules": true,
// Add type definitions for our Vite runtime.
"types": ["vite/client"]
}
}

3457
examples/astro/yarn.lock Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -6,7 +6,7 @@ This directory is a brief example of a [Dojo](https://dojo.io) site that can be
Deploy your own Dojo project with Vercel.
[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?repository-url=https://github.com/vercel/vercel/tree/main/dojo&template=dojo)
[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?repository-url=https://github.com/vercel/vercel/tree/main/examples/dojo&template=dojo)
### How We Created This Example

View File

@@ -15,7 +15,7 @@ cache:
env:
global:
# See https://git.io/vdao3 for details.
# See https://github.com/ember-cli/ember-cli/blob/master/docs/build-concurrency.md
- JOBS=1
script:

4882
examples/nextjs/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -7,12 +7,12 @@
"lint": "next lint"
},
"dependencies": {
"next": "12.1.0",
"react": "17.0.2",
"react-dom": "17.0.2"
"next": "12.1.4",
"react": "18.0.0",
"react-dom": "18.0.0"
},
"devDependencies": {
"eslint": "8.9.0",
"eslint-config-next": "12.1.0"
"eslint": "8.12.0",
"eslint-config-next": "12.1.4"
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -14,37 +14,24 @@
"dependencies": {
"lerna": "3.16.4"
},
"turbo": {
"baseBranch": "origin/main",
"pipeline": {
"build": {
"dependsOn": [
"^build"
],
"outputs": [
"dist/**"
]
}
}
},
"devDependencies": {
"@typescript-eslint/eslint-plugin": "4.28.0",
"@typescript-eslint/parser": "4.28.0",
"@typescript-eslint/eslint-plugin": "5.21.0",
"@typescript-eslint/parser": "5.21.0",
"async-retry": "1.2.3",
"buffer-replace": "1.0.0",
"eslint": "7.29.0",
"eslint-config-prettier": "8.3.0",
"eslint-plugin-jest": "24.3.6",
"husky": "6.0.0",
"jest": "27.3.1",
"eslint": "8.14.0",
"eslint-config-prettier": "8.5.0",
"eslint-plugin-jest": "26.1.5",
"husky": "7.0.4",
"jest": "28.0.2",
"json5": "2.1.1",
"lint-staged": "9.2.5",
"node-fetch": "2.6.1",
"npm-package-arg": "6.1.0",
"prettier": "2.3.1",
"prettier": "2.6.2",
"ts-eager": "2.0.2",
"ts-jest": "27.0.4",
"turbo": "1.1.9"
"ts-jest": "28.0.0-next.1",
"turbo": "1.2.5"
},
"scripts": {
"lerna": "lerna",

View File

@@ -1,6 +1,6 @@
{
"name": "@vercel/build-utils",
"version": "2.15.1-canary.0",
"version": "2.17.0",
"license": "MIT",
"main": "./dist/index.js",
"types": "./dist/index.d.js",
@@ -23,14 +23,14 @@
"@types/end-of-stream": "^1.4.0",
"@types/fs-extra": "9.0.13",
"@types/glob": "^7.1.1",
"@types/jest": "27.0.1",
"@types/jest": "27.4.1",
"@types/js-yaml": "3.12.1",
"@types/ms": "0.7.31",
"@types/multistream": "2.1.1",
"@types/node-fetch": "^2.1.6",
"@types/semver": "6.0.0",
"@types/yazl": "^2.4.1",
"@vercel/frameworks": "0.7.1-canary.0",
"@types/yazl": "2.4.2",
"@vercel/frameworks": "0.9.0",
"@vercel/ncc": "0.24.0",
"aggregate-error": "3.0.1",
"async-retry": "1.2.3",
@@ -47,6 +47,6 @@
"node-fetch": "2.6.1",
"semver": "6.1.1",
"typescript": "4.3.4",
"yazl": "2.4.3"
"yazl": "2.5.1"
}
}

View File

@@ -538,7 +538,7 @@ function getMissingBuildScriptError() {
code: 'missing_build_script',
message:
'Your `package.json` file is missing a `build` property inside the `scripts` property.' +
'\nLearn More: https://vercel.com/docs/v2/platform/frequently-asked-questions#missing-build-script',
'\nLearn More: https://vercel.link/missing-build-script',
};
}

View File

@@ -4,6 +4,7 @@ import { NowBuildError } from '../errors';
import debug from '../debug';
const allOptions = [
{ major: 16, range: '16.x', runtime: 'nodejs16.x' },
{ major: 14, range: '14.x', runtime: 'nodejs14.x' },
{ major: 12, range: '12.x', runtime: 'nodejs12.x' },
{

View File

@@ -1,15 +1,19 @@
import assert from 'assert';
import fs from 'fs-extra';
import path from 'path';
import debug from '../debug';
import Sema from 'async-sema';
import spawn from 'cross-spawn';
import { SpawnOptions } from 'child_process';
import { deprecate } from 'util';
import debug from '../debug';
import { NowBuildError } from '../errors';
import { Meta, PackageJson, NodeVersion, Config } from '../types';
import { getSupportedNodeVersion, getLatestNodeVersion } from './node-version';
import { readConfigFile } from './read-config-file';
// Only allow one `runNpmInstall()` invocation to run concurrently
const runNpmInstallSema = new Sema(1);
export type CliType = 'yarn' | 'npm' | 'pnpm';
export interface ScanParentDirsResult {
@@ -17,6 +21,11 @@ export interface ScanParentDirsResult {
* "yarn", "npm", or "pnpm" depending on the presence of lockfiles.
*/
cliType: CliType;
/**
* The file path of found `package.json` file, or `undefined` if none was
* found.
*/
packageJsonPath?: string;
/**
* The contents of found `package.json` file, when the `readPackageJson`
* option is enabled.
@@ -237,12 +246,13 @@ export async function scanParentDirs(
let cliType: CliType = 'yarn';
let packageJson: PackageJson | undefined;
let packageJsonPath: string | undefined;
let currentDestPath = destPath;
let lockfileVersion: number | undefined;
// eslint-disable-next-line no-constant-condition
while (true) {
const packageJsonPath = path.join(currentDestPath, 'package.json');
packageJsonPath = path.join(currentDestPath, 'package.json');
// eslint-disable-next-line no-await-in-loop
if (await fs.pathExists(packageJsonPath)) {
// Only read the contents of the *first* `package.json` file found,
@@ -269,15 +279,17 @@ export async function scanParentDirs(
),
]);
if (packageLockJson && !hasYarnLock && !pnpmLockYaml) {
cliType = 'npm';
lockfileVersion = packageLockJson.lockfileVersion;
}
if (!packageLockJson && !hasYarnLock && pnpmLockYaml) {
// Priority order is Yarn > pnpm > npm
// - find highest priority lock file and use that
if (hasYarnLock) {
cliType = 'yarn';
} else if (pnpmLockYaml) {
cliType = 'pnpm';
// just ensure that it is read as a number and not a string
lockfileVersion = Number(pnpmLockYaml.lockfileVersion);
} else if (packageLockJson) {
cliType = 'npm';
lockfileVersion = packageLockJson.lockfileVersion;
}
// Only stop iterating if a lockfile was found, because it's possible
@@ -293,7 +305,7 @@ export async function scanParentDirs(
currentDestPath = newDestPath;
}
return { cliType, packageJson, lockfileVersion };
return { cliType, packageJson, lockfileVersion, packageJsonPath };
}
export async function walkParentDirs({
@@ -319,55 +331,87 @@ export async function walkParentDirs({
return null;
}
function isSet<T>(v: any): v is Set<T> {
return v?.constructor?.name === 'Set';
}
export async function runNpmInstall(
destPath: string,
args: string[] = [],
spawnOpts?: SpawnOptions,
meta?: Meta,
nodeVersion?: NodeVersion
) {
): Promise<boolean> {
if (meta?.isDev) {
debug('Skipping dependency installation because dev mode is enabled');
return;
return false;
}
assert(path.isAbsolute(destPath));
debug(`Installing to ${destPath}`);
const { cliType, lockfileVersion } = await scanParentDirs(destPath);
const opts: SpawnOptionsExtended = { cwd: destPath, ...spawnOpts };
const env = opts.env ? { ...opts.env } : { ...process.env };
delete env.NODE_ENV;
opts.env = getEnvForPackageManager({
cliType,
lockfileVersion,
nodeVersion,
env,
});
let commandArgs: string[];
try {
await runNpmInstallSema.acquire();
const { cliType, packageJsonPath, lockfileVersion } = await scanParentDirs(
destPath
);
if (cliType === 'npm') {
opts.prettyCommand = 'npm install';
commandArgs = args
.filter(a => a !== '--prefer-offline')
.concat(['install', '--no-audit', '--unsafe-perm']);
} else if (cliType === 'pnpm') {
// PNPM's install command is similar to NPM's but without the audit nonsense
// @see options https://pnpm.io/cli/install
opts.prettyCommand = 'pnpm install';
commandArgs = args
.filter(a => a !== '--prefer-offline')
.concat(['install', '--unsafe-perm']);
} else {
opts.prettyCommand = 'yarn install';
commandArgs = ['install', ...args];
// Only allow `runNpmInstall()` to run once per `package.json`
// when doing a default install (no additional args)
if (meta && packageJsonPath && args.length === 0) {
if (!isSet<string>(meta.runNpmInstallSet)) {
meta.runNpmInstallSet = new Set<string>();
}
if (isSet<string>(meta.runNpmInstallSet)) {
if (meta.runNpmInstallSet.has(packageJsonPath)) {
return false;
} else {
meta.runNpmInstallSet.add(packageJsonPath);
}
}
}
const installTime = Date.now();
console.log('Installing dependencies...');
debug(`Installing to ${destPath}`);
const opts: SpawnOptionsExtended = { cwd: destPath, ...spawnOpts };
const env = opts.env ? { ...opts.env } : { ...process.env };
delete env.NODE_ENV;
opts.env = getEnvForPackageManager({
cliType,
lockfileVersion,
nodeVersion,
env,
});
let commandArgs: string[];
if (cliType === 'npm') {
opts.prettyCommand = 'npm install';
commandArgs = args
.filter(a => a !== '--prefer-offline')
.concat(['install', '--no-audit', '--unsafe-perm']);
} else if (cliType === 'pnpm') {
// PNPM's install command is similar to NPM's but without the audit nonsense
// @see options https://pnpm.io/cli/install
opts.prettyCommand = 'pnpm install';
commandArgs = args
.filter(a => a !== '--prefer-offline')
.concat(['install', '--unsafe-perm']);
} else {
opts.prettyCommand = 'yarn install';
commandArgs = ['install', ...args];
}
if (process.env.NPM_ONLY_PRODUCTION) {
commandArgs.push('--production');
}
await spawnAsync(cliType, commandArgs, opts);
debug(`Install complete [${Date.now() - installTime}ms]`);
return true;
} finally {
runNpmInstallSema.release();
}
if (process.env.NPM_ONLY_PRODUCTION) {
commandArgs.push('--production');
}
return spawnAsync(cliType, commandArgs, opts);
}
export function getEnvForPackageManager({
@@ -392,6 +436,16 @@ export function getEnvForPackageManager({
newEnv.PATH = `/node16/bin-npm7:${env.PATH}`;
console.log('Detected `package-lock.json` generated by npm 7...');
}
} else if (cliType === 'pnpm') {
if (
typeof lockfileVersion === 'number' &&
lockfileVersion === 5.4 &&
(nodeVersion?.major || 0) > 12
) {
// Ensure that pnpm 7 is at the beginning of the `$PATH`
newEnv.PATH = `/pnpm7/pnpm:${env.PATH}`;
console.log('Detected `pnpm-lock.yaml` generated by pnpm 7...');
}
} else {
// Yarn v2 PnP mode may be activated, so force "node-modules" linker style
if (!env.YARN_NODE_LINKER) {
@@ -502,7 +556,7 @@ export async function runPipInstall(
meta?: Meta
) {
if (meta && meta.isDev) {
debug('Skipping dependency installation because dev mode is enabled');
debug('Skipping dependency installation because dev mode is enabled');
return;
}

View File

@@ -33,7 +33,6 @@ import {
getDiscontinuedNodeVersions,
} from './fs/node-version';
import streamToBuffer from './fs/stream-to-buffer';
import shouldServe from './should-serve';
import debug from './debug';
import getIgnoreFilter from './get-ignore-filter';
import { getPlatformEnv } from './get-platform-env';
@@ -73,7 +72,6 @@ export {
getSpawnOptions,
getPlatformEnv,
streamToBuffer,
shouldServe,
debug,
isSymbolicLink,
getLambdaOptionsFromFunction,
@@ -94,6 +92,7 @@ export { DetectorFilesystem } from './detectors/filesystem';
export { readConfigFile } from './fs/read-config-file';
export { normalizePath } from './fs/normalize-path';
export * from './should-serve';
export * from './schemas';
export * from './types';
export * from './errors';
@@ -116,3 +115,5 @@ export const isOfficialRuntime = (desired: string, name?: string): boolean => {
export const isStaticRuntime = (name?: string): boolean => {
return isOfficialRuntime('static', name);
};
export { workspaceManagers } from './workspaces/workspace-managers';

View File

@@ -1,12 +1,12 @@
import { parse } from 'path';
import { ShouldServeOptions } from './types';
import FileFsRef from './file-fs-ref';
import type FileFsRef from './file-fs-ref';
import type { ShouldServe } from './types';
export default function shouldServe({
export const shouldServe: ShouldServe = ({
entrypoint,
files,
requestPath,
}: ShouldServeOptions): boolean {
}) => {
requestPath = requestPath.replace(/\/$/, ''); // sanitize trailing '/'
entrypoint = entrypoint.replace(/\\/, '/'); // windows compatibility
@@ -20,7 +20,7 @@ export default function shouldServe({
}
return false;
}
};
function hasProp(obj: { [path: string]: FileFsRef }, key: string): boolean {
return Object.hasOwnProperty.call(obj, key);

View File

@@ -119,10 +119,11 @@ export interface PrepareCacheOptions {
workPath: string;
/**
* A writable temporary directory where you can build a cache to use for
* the next run.
* The "Root Directory" is assigned to the `workPath` so the `repoRootPath`
* is the Git Repository Root. This is only relevant for Monorepos.
* See https://vercel.com/blog/monorepos
*/
cachePath: string;
repoRootPath?: string;
/**
* An arbitrary object passed by the user in the build definition defined
@@ -331,6 +332,7 @@ export interface ProjectSettings {
sourceFilesOutsideRootDirectory?: boolean;
directoryListing?: boolean;
gitForkProtection?: boolean;
commandForIgnoringBuildStep?: string | null;
}
export interface BuilderV2 {
@@ -343,6 +345,7 @@ export interface BuilderV3 {
version: 3;
build: BuildV3;
prepareCache?: PrepareCache;
shouldServe?: ShouldServe;
startDevServer?: StartDevServer;
}
@@ -355,7 +358,29 @@ export interface Images {
formats?: ImageFormat[];
}
export interface BuildResultV2 {
/**
* If a Builder ends up creating filesystem outputs conforming to
* the Build Output API, then the Builder should return this type.
*/
export interface BuildResultBuildOutput {
/**
* Version number of the Build Output API that was created.
* Currently only `3` is a valid value.
* @example 3
*/
buildOutputVersion: 3;
/**
* Filesystem path to the Build Output directory.
* @example "/path/to/.vercel/output"
*/
buildOutputPath: string;
}
/**
* When a Builder implements `version: 2`, the `build()` function is expected
* to return this type.
*/
export interface BuildResultV2Typical {
// TODO: use proper `Route` type from `routing-utils` (perhaps move types to a common package)
routes?: any[];
images?: Images;
@@ -368,6 +393,8 @@ export interface BuildResultV2 {
}>;
}
export type BuildResultV2 = BuildResultV2Typical | BuildResultBuildOutput;
export interface BuildResultV3 {
output: Lambda;
}
@@ -375,6 +402,9 @@ export interface BuildResultV3 {
export type BuildV2 = (options: BuildOptions) => Promise<BuildResultV2>;
export type BuildV3 = (options: BuildOptions) => Promise<BuildResultV3>;
export type PrepareCache = (options: PrepareCacheOptions) => Promise<Files>;
export type ShouldServe = (
options: ShouldServeOptions
) => boolean | Promise<boolean>;
export type StartDevServer = (
options: StartDevServerOptions
) => Promise<StartDevServerResult>;

View File

@@ -0,0 +1,129 @@
import type { Framework } from '@vercel/frameworks';
/**
* The supported list of workspace managers.
*
* This list is designed to work with the @see {@link detectFramework} function.
*
* @example
* import { workspaceManagers as frameworkList } from '@vercel/build-utils/workspaces'
* import { detectFramework } from '@vercel/build-utils'
*
* const fs = new GitDetectorFilesystem(...)
* detectFramwork({ fs, frameworkList }) // returns the 'slug' field if detected, otherwise null
*
* @todo Will be used by the detect-eligible-projects API endpoint for a given git url.
*/
export const workspaceManagers: Array<Framework> = [
{
name: 'Yarn',
slug: 'yarn',
detectors: {
every: [
{
path: 'package.json',
matchContent:
'"workspaces":\\s*(?:\\[[^\\]]*]|{[^}]*"packages":[^}]*})',
},
{
path: 'yarn.lock',
},
],
},
// unused props - needed for typescript
description: '',
logo: '',
settings: {
buildCommand: {
value: '',
placeholder: '',
},
devCommand: {
value: '',
placeholder: '',
},
installCommand: {
value: '',
placeholder: '',
},
outputDirectory: {
value: '',
placeholder: '',
},
},
getOutputDirName: () => Promise.resolve(''),
},
{
name: 'pnpm',
slug: 'pnpm',
detectors: {
every: [
{
path: 'pnpm-workspace.yaml',
},
],
},
// unused props - needed for typescript
description: '',
logo: '',
settings: {
buildCommand: {
value: '',
placeholder: '',
},
devCommand: {
value: '',
placeholder: '',
},
installCommand: {
value: '',
placeholder: '',
},
outputDirectory: {
value: '',
placeholder: '',
},
},
getOutputDirName: () => Promise.resolve(''),
},
{
name: 'npm',
slug: 'npm',
detectors: {
every: [
{
path: 'package.json',
matchContent:
'"workspaces":\\s*(?:\\[[^\\]]*]|{[^}]*"packages":[^}]*})',
},
{
path: 'package-lock.json',
},
],
},
// unused props - needed for typescript
description: '',
logo: '',
settings: {
buildCommand: {
value: '',
placeholder: '',
},
devCommand: {
value: '',
placeholder: '',
},
installCommand: {
value: '',
placeholder: '',
},
outputDirectory: {
value: '',
placeholder: '',
},
},
getOutputDirName: () => Promise.resolve(''),
},
];
export default workspaceManagers;

View File

@@ -0,0 +1,4 @@
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
# yarn lockfile v1

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,14 @@
{
"private": "true",
"name": "25-multiple-lock-files-yarn",
"workspaces": [
"a",
"b"
],
"scripts": {
"build": "mkdir -p public && (printf \"yarn version: \" && yarn -v) > public/index.txt"
},
"dependencies": {
"once": "^1.4.0"
}
}

View File

@@ -0,0 +1,19 @@
lockfileVersion: 5.3
specifiers:
once: ^1.4.0
dependencies:
once: 1.4.0
packages:
/once/1.4.0:
resolution: {integrity: sha1-WDsap3WWHUsROsF9nFC6753Xa9E=}
dependencies:
wrappy: 1.0.2
dev: false
/wrappy/1.0.2:
resolution: {integrity: sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=}
dev: false

View File

@@ -0,0 +1,11 @@
{
"version": 2,
"builds": [{ "src": "package.json", "use": "@vercel/static-build" }],
"probes": [
{
"path": "/",
"mustContain": "yarn version: 1",
"logMustContain": "yarn run build"
}
]
}

View File

@@ -0,0 +1,15 @@
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
# yarn lockfile v1
once@^1.4.0:
version "1.4.0"
resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1"
integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E=
dependencies:
wrappy "1"
wrappy@1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=

View File

@@ -0,0 +1,44 @@
{
"name": "26-multiple-lock-files-pnpm",
"version": "1.0.0",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "26-multiple-lock-files-pnpm",
"version": "1.0.0",
"license": "ISC",
"dependencies": {
"once": "^1.4.0"
}
},
"node_modules/once": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
"integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
"dependencies": {
"wrappy": "1"
}
},
"node_modules/wrappy": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
"integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8="
}
},
"dependencies": {
"once": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
"integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
"requires": {
"wrappy": "1"
}
},
"wrappy": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
"integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8="
}
}
}

View File

@@ -0,0 +1,14 @@
{
"private": "true",
"name": "26-multiple-lock-files-pnpm",
"workspaces": [
"a",
"b"
],
"scripts": {
"build": "mkdir -p public && (printf \"pnpm version: \" && pnpm -v) > public/index.txt"
},
"dependencies": {
"once": "^1.4.0"
}
}

View File

@@ -0,0 +1,19 @@
lockfileVersion: 5.3
specifiers:
once: ^1.4.0
dependencies:
once: 1.4.0
packages:
/once/1.4.0:
resolution: {integrity: sha1-WDsap3WWHUsROsF9nFC6753Xa9E=}
dependencies:
wrappy: 1.0.2
dev: false
/wrappy/1.0.2:
resolution: {integrity: sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=}
dev: false

View File

@@ -0,0 +1,3 @@
packages:
- 'a'
- 'b'

View File

@@ -0,0 +1,11 @@
{
"version": 2,
"builds": [{ "src": "package.json", "use": "@vercel/static-build" }],
"probes": [
{
"path": "/",
"mustContain": "pnpm version: 6",
"logMustContain": "pnpm run build"
}
]
}

View File

@@ -0,0 +1,15 @@
{
"name": "a",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"debug": "^4.3.2"
}
}

View File

@@ -0,0 +1,15 @@
{
"name": "b",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"cowsay": "^1.5.0"
}
}

View File

@@ -0,0 +1,9 @@
{
"name": "21-npm-workspaces",
"version": "1.0.0",
"private": true,
"workspaces": [
"a",
"b"
]
}

View File

@@ -0,0 +1,232 @@
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
# yarn lockfile v1
ansi-regex@^3.0.0:
version "3.0.1"
resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.1.tgz#123d6479e92ad45ad897d4054e3c7ca7db4944e1"
integrity sha512-+O9Jct8wf++lXxxFc4hc8LsjaSq0HFzzL7cVsw8pRDIPdjKD2mT4ytDZlLuSBZ4cLKZFXIrMGO7DbQCtMJJMKw==
ansi-regex@^5.0.1:
version "5.0.1"
resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304"
integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==
ansi-styles@^4.0.0:
version "4.3.0"
resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937"
integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==
dependencies:
color-convert "^2.0.1"
camelcase@^5.0.0:
version "5.3.1"
resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320"
integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==
cliui@^6.0.0:
version "6.0.0"
resolved "https://registry.yarnpkg.com/cliui/-/cliui-6.0.0.tgz#511d702c0c4e41ca156d7d0e96021f23e13225b1"
integrity sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==
dependencies:
string-width "^4.2.0"
strip-ansi "^6.0.0"
wrap-ansi "^6.2.0"
color-convert@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3"
integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==
dependencies:
color-name "~1.1.4"
color-name@~1.1.4:
version "1.1.4"
resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2"
integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==
cowsay@^1.5.0:
version "1.5.0"
resolved "https://registry.yarnpkg.com/cowsay/-/cowsay-1.5.0.tgz#4a2a453b8b59383c7d7a50e44d765c5de0bf615f"
integrity sha512-8Ipzr54Z8zROr/62C8f0PdhQcDusS05gKTS87xxdji8VbWefWly0k8BwGK7+VqamOrkv3eGsCkPtvlHzrhWsCA==
dependencies:
get-stdin "8.0.0"
string-width "~2.1.1"
strip-final-newline "2.0.0"
yargs "15.4.1"
debug@^4.3.2:
version "4.3.4"
resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865"
integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==
dependencies:
ms "2.1.2"
decamelize@^1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290"
integrity sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=
emoji-regex@^8.0.0:
version "8.0.0"
resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37"
integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==
find-up@^4.1.0:
version "4.1.0"
resolved "https://registry.yarnpkg.com/find-up/-/find-up-4.1.0.tgz#97afe7d6cdc0bc5928584b7c8d7b16e8a9aa5d19"
integrity sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==
dependencies:
locate-path "^5.0.0"
path-exists "^4.0.0"
get-caller-file@^2.0.1:
version "2.0.5"
resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e"
integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==
get-stdin@8.0.0:
version "8.0.0"
resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-8.0.0.tgz#cbad6a73feb75f6eeb22ba9e01f89aa28aa97a53"
integrity sha512-sY22aA6xchAzprjyqmSEQv4UbAAzRN0L2dQB0NlN5acTTK9Don6nhoc3eAbUnpZiCANAMfd/+40kVdKfFygohg==
is-fullwidth-code-point@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f"
integrity sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=
is-fullwidth-code-point@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d"
integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==
locate-path@^5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-5.0.0.tgz#1afba396afd676a6d42504d0a67a3a7eb9f62aa0"
integrity sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==
dependencies:
p-locate "^4.1.0"
ms@2.1.2:
version "2.1.2"
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009"
integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==
p-limit@^2.2.0:
version "2.3.0"
resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1"
integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==
dependencies:
p-try "^2.0.0"
p-locate@^4.1.0:
version "4.1.0"
resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-4.1.0.tgz#a3428bb7088b3a60292f66919278b7c297ad4f07"
integrity sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==
dependencies:
p-limit "^2.2.0"
p-try@^2.0.0:
version "2.2.0"
resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6"
integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==
path-exists@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3"
integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==
require-directory@^2.1.1:
version "2.1.1"
resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42"
integrity sha1-jGStX9MNqxyXbiNE/+f3kqam30I=
require-main-filename@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b"
integrity sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==
set-blocking@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7"
integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc=
string-width@^4.1.0, string-width@^4.2.0:
version "4.2.3"
resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
dependencies:
emoji-regex "^8.0.0"
is-fullwidth-code-point "^3.0.0"
strip-ansi "^6.0.1"
string-width@~2.1.1:
version "2.1.1"
resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e"
integrity sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==
dependencies:
is-fullwidth-code-point "^2.0.0"
strip-ansi "^4.0.0"
strip-ansi@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-4.0.0.tgz#a8479022eb1ac368a871389b635262c505ee368f"
integrity sha1-qEeQIusaw2iocTibY1JixQXuNo8=
dependencies:
ansi-regex "^3.0.0"
strip-ansi@^6.0.0, strip-ansi@^6.0.1:
version "6.0.1"
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
dependencies:
ansi-regex "^5.0.1"
strip-final-newline@2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-2.0.0.tgz#89b852fb2fcbe936f6f4b3187afb0a12c1ab58ad"
integrity sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==
which-module@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a"
integrity sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=
wrap-ansi@^6.2.0:
version "6.2.0"
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-6.2.0.tgz#e9393ba07102e6c91a3b221478f0257cd2856e53"
integrity sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==
dependencies:
ansi-styles "^4.0.0"
string-width "^4.1.0"
strip-ansi "^6.0.0"
y18n@^4.0.0:
version "4.0.3"
resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.3.tgz#b5f259c82cd6e336921efd7bfd8bf560de9eeedf"
integrity sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==
yargs-parser@^18.1.2:
version "18.1.3"
resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-18.1.3.tgz#be68c4975c6b2abf469236b0c870362fab09a7b0"
integrity sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==
dependencies:
camelcase "^5.0.0"
decamelize "^1.2.0"
yargs@15.4.1:
version "15.4.1"
resolved "https://registry.yarnpkg.com/yargs/-/yargs-15.4.1.tgz#0d87a16de01aee9d8bec2bfbf74f67851730f4f8"
integrity sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==
dependencies:
cliui "^6.0.0"
decamelize "^1.2.0"
find-up "^4.1.0"
get-caller-file "^2.0.1"
require-directory "^2.1.1"
require-main-filename "^2.0.0"
set-blocking "^2.0.0"
string-width "^4.2.0"
which-module "^2.0.0"
y18n "^4.0.0"
yargs-parser "^18.1.2"

View File

@@ -0,0 +1,9 @@
{
"private": "true",
"scripts": {
"build": "mkdir -p public && (printf \"pnpm version: \" && pnpm -v) > public/index.txt"
},
"dependencies": {
"once": "^1.4.0"
}
}

View File

@@ -0,0 +1,19 @@
lockfileVersion: 5.4
specifiers:
once: ^1.4.0
dependencies:
once: 1.4.0
packages:
/once/1.4.0:
resolution: {integrity: sha1-WDsap3WWHUsROsF9nFC6753Xa9E=}
dependencies:
wrappy: 1.0.2
dev: false
/wrappy/1.0.2:
resolution: {integrity: sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=}
dev: false

View File

@@ -0,0 +1,11 @@
{
"version": 2,
"builds": [{ "src": "package.json", "use": "@vercel/static-build" }],
"probes": [
{
"path": "/",
"mustContain": "pnpm version: 7",
"logMustContain": "pnpm run build"
}
]
}

View File

@@ -32,6 +32,7 @@ const skipFixtures: string[] = [
'08-zero-config-middleman',
'21-npm-workspaces',
'23-pnpm-workspaces',
'27-yarn-workspaces',
];
// eslint-disable-next-line no-restricted-syntax

View File

@@ -0,0 +1,31 @@
import path from 'path';
import { detectFramework } from '../src/detect-framework';
import workspaceManagers from '../src/workspaces/workspace-managers';
import { FixtureFilesystem } from './utils/fixture-filesystem';
describe('workspace-managers', () => {
describe.each([
['npm', '21-npm-workspaces'],
['pnpm', '23-pnpm-workspaces'],
['yarn', '27-yarn-workspaces'],
['yarn', '25-multiple-lock-files-yarn'],
['pnpm', '26-multiple-lock-files-pnpm'],
[null, '22-pnpm'],
])('with detectFramework', (frameworkSlug, fixturePath) => {
const testName = frameworkSlug
? `should detect a ${frameworkSlug} workspace for ${fixturePath}`
: `should not detect framework for ${fixturePath}`;
it(testName, async () => {
const fixture = path.join(__dirname, 'fixtures', fixturePath);
const fs = new FixtureFilesystem(fixture);
const result = await detectFramework({
fs,
frameworkList: workspaceManagers,
});
expect(result).toBe(frameworkSlug);
});
});
});

View File

@@ -1,16 +1,17 @@
import assert from 'assert';
import { getEnvForPackageManager, NodeVersion } from '../src';
import { CliType } from '../src/fs/run-user-scripts';
import { getEnvForPackageManager } from '../src';
describe('Test `getEnvForPackageManager()`', () => {
const cases = [
const cases: Array<{
name: string;
args: Parameters<typeof getEnvForPackageManager>[0];
want: unknown;
}> = [
{
name: 'should do nothing to env for npm < 6 and node < 16',
args: {
cliType: 'npm' as CliType,
nodeVersion: {
major: 14,
} as NodeVersion,
cliType: 'npm',
nodeVersion: { major: 14, range: '14.x', runtime: 'nodejs14.x' },
lockfileVersion: 1,
env: {
FOO: 'bar',
@@ -23,10 +24,8 @@ describe('Test `getEnvForPackageManager()`', () => {
{
name: 'should set path if npm 7+ is detected and node < 16',
args: {
cliType: 'npm' as CliType,
nodeVersion: {
major: 14,
} as NodeVersion,
cliType: 'npm',
nodeVersion: { major: 14, range: '14.x', runtime: 'nodejs14.x' },
lockfileVersion: 2,
env: {
FOO: 'bar',
@@ -41,10 +40,8 @@ describe('Test `getEnvForPackageManager()`', () => {
{
name: 'should not set path if node is 16 and npm 7+ is detected',
args: {
cliType: 'npm' as CliType,
nodeVersion: {
major: 16,
} as NodeVersion,
cliType: 'npm',
nodeVersion: { major: 16, range: '16.x', runtime: 'nodejs16.x' },
lockfileVersion: 2,
env: {
FOO: 'bar',
@@ -59,10 +56,8 @@ describe('Test `getEnvForPackageManager()`', () => {
{
name: 'should set YARN_NODE_LINKER w/yarn if it is not already defined',
args: {
cliType: 'yarn' as CliType,
nodeVersion: {
major: 16,
} as NodeVersion,
cliType: 'yarn',
nodeVersion: { major: 16, range: '16.x', runtime: 'nodejs16.x' },
lockfileVersion: 2,
env: {
FOO: 'bar',
@@ -76,10 +71,8 @@ describe('Test `getEnvForPackageManager()`', () => {
{
name: 'should not set YARN_NODE_LINKER if it already exists',
args: {
cliType: 'yarn' as CliType,
nodeVersion: {
major: 16,
} as NodeVersion,
cliType: 'yarn',
nodeVersion: { major: 16, range: '16.x', runtime: 'nodejs16.x' },
lockfileVersion: 2,
env: {
FOO: 'bar',
@@ -91,6 +84,50 @@ describe('Test `getEnvForPackageManager()`', () => {
YARN_NODE_LINKER: 'exists',
},
},
{
name: 'should set path if pnpm 7+ is detected and Node version is greater than 12',
args: {
cliType: 'pnpm',
nodeVersion: { major: 16, range: '16.x', runtime: 'nodejs16.x' },
lockfileVersion: 5.4,
env: {
FOO: 'bar',
PATH: 'foo',
},
},
want: {
FOO: 'bar',
PATH: '/pnpm7/pnpm:foo',
},
},
{
name: 'should not set path if pnpm 7+ is detected and Node version is less than or equal to 12',
args: {
cliType: 'pnpm',
nodeVersion: { major: 12, range: '12.x', runtime: 'nodejs12.x' },
lockfileVersion: 5.4,
env: {
FOO: 'bar',
},
},
want: {
FOO: 'bar',
},
},
{
name: 'should not set path if pnpm 6 is detected',
args: {
cliType: 'pnpm',
nodeVersion: { major: 14, range: '14.x', runtime: 'nodejs14.x' },
lockfileVersion: 5.3,
env: {
FOO: 'bar',
},
},
want: {
FOO: 'bar',
},
},
];
for (const { name, want, args } of cases) {

View File

@@ -15,6 +15,7 @@ import {
runPackageJsonScript,
scanParentDirs,
FileBlob,
Meta,
} from '../src';
async function expectBuilderError(promise: Promise<any>, pattern: string) {
@@ -175,9 +176,13 @@ it('should only match supported node versions, otherwise throw an error', async
'major',
14
);
expect(await getSupportedNodeVersion('16.x', false)).toHaveProperty(
'major',
16
);
const autoMessage =
'Please set Node.js Version to 14.x in your Project Settings to use Node.js 14.';
'Please set Node.js Version to 16.x in your Project Settings to use Node.js 16.';
await expectBuilderError(
getSupportedNodeVersion('8.11.x', true),
autoMessage
@@ -195,9 +200,13 @@ it('should only match supported node versions, otherwise throw an error', async
'major',
14
);
expect(await getSupportedNodeVersion('16.x', true)).toHaveProperty(
'major',
16
);
const foundMessage =
'Please set "engines": { "node": "14.x" } in your `package.json` file to use Node.js 14.';
'Please set "engines": { "node": "16.x" } in your `package.json` file to use Node.js 16.';
await expectBuilderError(
getSupportedNodeVersion('8.11.x', false),
foundMessage
@@ -218,8 +227,8 @@ it('should match all semver ranges', async () => {
// See https://docs.npmjs.com/files/package.json#engines
expect(await getSupportedNodeVersion('12.0.0')).toHaveProperty('major', 12);
expect(await getSupportedNodeVersion('12.x')).toHaveProperty('major', 12);
expect(await getSupportedNodeVersion('>=10')).toHaveProperty('major', 14);
expect(await getSupportedNodeVersion('>=10.3.0')).toHaveProperty('major', 14);
expect(await getSupportedNodeVersion('>=10')).toHaveProperty('major', 16);
expect(await getSupportedNodeVersion('>=10.3.0')).toHaveProperty('major', 16);
expect(await getSupportedNodeVersion('11.5.0 - 12.5.0')).toHaveProperty(
'major',
12
@@ -230,6 +239,10 @@ it('should match all semver ranges', async () => {
);
expect(await getSupportedNodeVersion('~12.5.0')).toHaveProperty('major', 12);
expect(await getSupportedNodeVersion('^12.5.0')).toHaveProperty('major', 12);
expect(await getSupportedNodeVersion('12.5.0 - 14.5.0')).toHaveProperty(
'major',
14
);
});
it('should ignore node version in vercel dev getNodeVersion()', async () => {
@@ -245,8 +258,8 @@ it('should ignore node version in vercel dev getNodeVersion()', async () => {
it('should select project setting from config when no package.json is found', async () => {
expect(
await getNodeVersion('/tmp', undefined, { nodeVersion: '14.x' }, {})
).toHaveProperty('range', '14.x');
await getNodeVersion('/tmp', undefined, { nodeVersion: '16.x' }, {})
).toHaveProperty('range', '16.x');
expect(warningMessages).toStrictEqual([]);
});
@@ -277,7 +290,7 @@ it('should not warn when package.json engines matches project setting from confi
});
it('should get latest node version', async () => {
expect(getLatestNodeVersion()).toHaveProperty('major', 14);
expect(getLatestNodeVersion()).toHaveProperty('major', 16);
});
it('should throw for discontinued versions', async () => {
@@ -312,8 +325,8 @@ it('should warn for deprecated versions, soon to be discontinued', async () => {
10
);
expect(warningMessages).toStrictEqual([
'Error: Node.js version 10.x is deprecated. Deployments created on or after 2021-04-20 will fail to build. Please set "engines": { "node": "14.x" } in your `package.json` file to use Node.js 14. This change is the result of a decision made by an upstream infrastructure provider (AWS).',
'Error: Node.js version 10.x is deprecated. Deployments created on or after 2021-04-20 will fail to build. Please set Node.js Version to 14.x in your Project Settings to use Node.js 14. This change is the result of a decision made by an upstream infrastructure provider (AWS).',
'Error: Node.js version 10.x is deprecated. Deployments created on or after 2021-04-20 will fail to build. Please set "engines": { "node": "16.x" } in your `package.json` file to use Node.js 16. This change is the result of a decision made by an upstream infrastructure provider (AWS).',
'Error: Node.js version 10.x is deprecated. Deployments created on or after 2021-04-20 will fail to build. Please set Node.js Version to 16.x in your Project Settings to use Node.js 16. This change is the result of a decision made by an upstream infrastructure provider (AWS).',
]);
global.Date.now = realDateNow;
@@ -413,3 +426,39 @@ it('should detect pnpm Workspaces', async () => {
expect(result.cliType).toEqual('pnpm');
expect(result.lockfileVersion).toEqual(5.3);
});
it('should only invoke `runNpmInstall()` once per `package.json` file (serial)', async () => {
const meta: Meta = {};
const fixture = path.join(__dirname, 'fixtures', '02-zero-config-api');
const apiDir = path.join(fixture, 'api');
const run1 = await runNpmInstall(apiDir, [], undefined, meta);
expect(run1).toEqual(true);
expect(
(meta.runNpmInstallSet as Set<string>).has(
path.join(fixture, 'package.json')
)
).toEqual(true);
const run2 = await runNpmInstall(apiDir, [], undefined, meta);
expect(run2).toEqual(false);
const run3 = await runNpmInstall(fixture, [], undefined, meta);
expect(run3).toEqual(false);
});
it('should only invoke `runNpmInstall()` once per `package.json` file (parallel)', async () => {
const meta: Meta = {};
const fixture = path.join(__dirname, 'fixtures', '02-zero-config-api');
const apiDir = path.join(fixture, 'api');
const [run1, run2, run3] = await Promise.all([
runNpmInstall(apiDir, [], undefined, meta),
runNpmInstall(apiDir, [], undefined, meta),
runNpmInstall(fixture, [], undefined, meta),
]);
expect(run1).toEqual(true);
expect(run2).toEqual(false);
expect(run3).toEqual(false);
expect(
(meta.runNpmInstallSet as Set<string>).has(
path.join(fixture, 'package.json')
)
).toEqual(true);
});

View File

@@ -0,0 +1,35 @@
import { promises } from 'fs';
import path from 'path';
import { DetectorFilesystem } from '../../src';
const { stat, readFile } = promises;
export class FixtureFilesystem extends DetectorFilesystem {
private rootPath: string;
constructor(fixturePath: string) {
super();
this.rootPath = fixturePath;
}
async _hasPath(name: string): Promise<boolean> {
try {
const filePath = path.join(this.rootPath, name);
await stat(filePath);
return true;
} catch {
return false;
}
}
async _readFile(name: string): Promise<Buffer> {
const filePath = path.join(this.rootPath, name);
return readFile(filePath);
}
async _isFile(name: string): Promise<boolean> {
const filePath = path.join(this.rootPath, name);
return (await stat(filePath)).isFile();
}
}

View File

@@ -1,6 +1,6 @@
{
"name": "vercel",
"version": "24.0.2-canary.0",
"version": "24.2.1",
"preferGlobal": true,
"license": "Apache-2.0",
"description": "The command-line interface for Vercel",
@@ -43,14 +43,15 @@
"node": ">= 12"
},
"dependencies": {
"@vercel/build-utils": "2.15.1-canary.0",
"@vercel/go": "1.3.2-canary.0",
"@vercel/node": "1.14.1-canary.0",
"@vercel/python": "2.2.2-canary.0",
"@vercel/ruby": "1.3.2-canary.0",
"@vercel/build-utils": "2.17.0",
"@vercel/go": "1.4.1",
"@vercel/node": "1.15.1",
"@vercel/python": "2.3.1",
"@vercel/ruby": "1.3.4",
"update-notifier": "4.1.0"
},
"devDependencies": {
"@alex_neo/jest-expect-message": "1.0.5",
"@next/env": "11.1.2",
"@sentry/node": "5.5.0",
"@sindresorhus/slugify": "0.11.0",
@@ -68,7 +69,8 @@
"@types/glob": "7.1.1",
"@types/http-proxy": "1.16.2",
"@types/inquirer": "7.3.1",
"@types/jest": "27.0.1",
"@types/jest": "27.4.1",
"@types/jest-expect-message": "1.0.3",
"@types/load-json-file": "2.0.7",
"@types/mime-types": "2.1.0",
"@types/minimatch": "3.0.3",
@@ -88,11 +90,9 @@
"@types/update-notifier": "5.1.0",
"@types/which": "1.3.2",
"@types/write-json-file": "2.2.1",
"@vercel/client": "10.4.1-canary.0",
"@vercel/fetch-retry": "5.0.3",
"@vercel/frameworks": "0.7.1-canary.0",
"@vercel/client": "11.0.1",
"@vercel/frameworks": "0.9.0",
"@vercel/ncc": "0.24.0",
"@vercel/nft": "0.17.5",
"@zeit/fun": "0.11.2",
"@zeit/source-map-support": "0.6.2",
"ajv": "6.12.2",
@@ -177,6 +177,9 @@
"isolatedModules": true
}
},
"setupFilesAfterEnv": [
"@alex_neo/jest-expect-message"
],
"verbose": false,
"testEnvironment": "node",
"testMatch": [

View File

@@ -73,7 +73,7 @@ export default async function set(
if (args.length === 0) {
output.error(
`To ship to production, optionally configure your domains (${link(
'https://vercel.com/docs/v2/custom-domains'
'https://vercel.link/domain-configuration'
)}) and run ${getCommandName(`--prod`)}.`
);
return 1;

View File

@@ -42,6 +42,7 @@ export const help = () => `
-h, --help Output usage information
-v, --version Output the version number
--cwd Current working directory
-V, --platform-version Set the platform version to deploy to
-A ${chalk.bold.underline('FILE')}, --local-config=${chalk.bold.underline(
'FILE'

View File

@@ -61,6 +61,8 @@ import { getPreferredPreviewURL } from '../../util/deploy/get-preferred-preview-
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';
export default async (client: Client) => {
const { output } = client;
@@ -155,7 +157,7 @@ export default async (client: Client) => {
}
}
const { log, debug, error, warn, isTTY } = output;
const { log, debug, error, prettyError, isTTY } = output;
const quiet = !isTTY;
@@ -181,6 +183,46 @@ export default async (client: Client) => {
);
}
// build `target`
const target = parseTarget(output, argv['--target'], argv['--prod']);
if (typeof target === 'number') {
return target;
}
// build `--prebuilt`
if (argv['--prebuilt']) {
const prebuiltExists = await fs.pathExists(join(path, '.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(path);
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;
}
}
// retrieve `project` and `org` from .vercel
const link = await getLinkedProject(client, path);
@@ -403,33 +445,6 @@ export default async (client: Client) => {
.filter(Boolean);
const regions = regionFlag.length > 0 ? regionFlag : localConfig.regions;
// build `target`
let target;
if (argv['--target']) {
const deprecatedTarget = argv['--target'];
if (!['staging', 'production'].includes(deprecatedTarget)) {
error(
`The specified ${param('--target')} ${code(
deprecatedTarget
)} is not valid`
);
return 1;
}
if (deprecatedTarget === 'production') {
warn(
'We recommend using the much shorter `--prod` option instead of `--target production` (deprecated)'
);
}
output.debug(`Setting target to ${deprecatedTarget}`);
target = deprecatedTarget;
} else if (argv['--prod']) {
output.debug('Setting target to production');
target = 'production';
}
const currentTeam = org?.type === 'team' ? org.id : undefined;
const now = new Now({
client,

View File

@@ -10,6 +10,8 @@ import { ProjectSettings } from '../../types';
import getDecryptedEnvRecords from '../../util/get-decrypted-env-records';
import setupAndLink from '../../util/link/setup-and-link';
import getSystemEnvValues from '../../util/env/get-system-env-values';
import { getCommandName } from '../../util/pkg-name';
import param from '../../util/output/param';
type Options = {
'--listen': string;
@@ -46,6 +48,13 @@ export default async function dev(
}
if (link.status === 'error') {
if (link.reason === 'HEADLESS') {
client.output.error(
`Command ${getCommandName(
'dev'
)} requires confirmation. Use option ${param('--confirm')} to confirm.`
);
}
return link.exitCode;
}

View File

@@ -1,4 +1,5 @@
import chalk from 'chalk';
import { ProjectEnvTarget } from '../../types';
import Client from '../../util/client';
import { getEnvTargetPlaceholder } from '../../util/env/env-target';
import getArgs from '../../util/get-args';
@@ -114,9 +115,11 @@ export default async function main(client: Client) {
return 2;
}
const { subcommand, args } = getSubcommand(argv._.slice(1), COMMAND_CONFIG);
const cwd = argv['--cwd'] || process.cwd();
const subArgs = argv._.slice(1);
const { subcommand, args } = getSubcommand(subArgs, COMMAND_CONFIG);
const { output, config } = client;
const link = await getLinkedProject(client);
const link = await getLinkedProject(client, cwd);
if (link.status === 'error') {
return link.exitCode;
} else if (link.status === 'not_linked') {
@@ -137,14 +140,15 @@ export default async function main(client: Client) {
case 'rm':
return rm(client, project, argv, args, output);
case 'pull':
output.warn(
`${getCommandName(
'env pull'
)} is deprecated and will be removed in future releases. Run ${getCommandName(
'pull'
)} instead.`
return pull(
client,
project,
ProjectEnvTarget.Development,
argv,
args,
output,
cwd
);
return pull(client, project, argv, args, output);
default:
output.error(getInvalidSubcommand(COMMAND_CONFIG));
help();

View File

@@ -1,7 +1,8 @@
import chalk from 'chalk';
import { closeSync, openSync, promises, readSync } from 'fs';
import { outputFile } from 'fs-extra';
import { closeSync, openSync, readSync } from 'fs';
import { resolve } from 'path';
import { Project } from '../../types';
import { Project, ProjectEnvTarget } from '../../types';
import Client from '../../util/client';
import exposeSystemEnvs from '../../util/dev/expose-system-envs';
import { emoji, prependEmoji } from '../../util/emoji';
@@ -12,7 +13,6 @@ import { Output } from '../../util/output';
import param from '../../util/output/param';
import stamp from '../../util/output/stamp';
import { getCommandName } from '../../util/pkg-name';
const { writeFile } = promises;
const CONTENTS_PREFIX = '# Created by Vercel CLI\n';
@@ -45,9 +45,11 @@ function tryReadHeadSync(path: string, length: number) {
export default async function pull(
client: Client,
project: Project,
environment: ProjectEnvTarget,
opts: Partial<Options>,
args: string[],
output: Output
output: Output,
cwd: string
) {
if (args.length > 1) {
output.error(
@@ -58,7 +60,7 @@ export default async function pull(
// handle relative or absolute filename
const [filename = '.env'] = args;
const fullPath = resolve(filename);
const fullPath = resolve(cwd, filename);
const skipConfirmation = opts['--yes'];
const head = tryReadHeadSync(fullPath, Buffer.byteLength(CONTENTS_PREFIX));
@@ -79,7 +81,7 @@ export default async function pull(
}
output.print(
`Downloading Development Environment Variables for Project ${chalk.bold(
`Downloading "${environment}" Environment Variables for Project ${chalk.bold(
project.name
)}\n`
);
@@ -88,7 +90,7 @@ export default async function pull(
output.spinner('Downloading');
const [{ envs: projectEnvs }, { systemEnvValues }] = await Promise.all([
getDecryptedEnvRecords(output, client, project.id),
getDecryptedEnvRecords(output, client, project.id, environment),
project.autoExposeSystemEnvs
? getSystemEnvValues(output, client, project.id)
: { systemEnvValues: [] },
@@ -97,7 +99,9 @@ export default async function pull(
const records = exposeSystemEnvs(
projectEnvs,
systemEnvValues,
project.autoExposeSystemEnvs
project.autoExposeSystemEnvs,
undefined,
environment
);
const contents =
@@ -107,7 +111,7 @@ export default async function pull(
.join('\n') +
'\n';
await writeFile(fullPath, contents, 'utf8');
await outputFile(fullPath, contents, 'utf8');
output.print(
`${prependEmoji(

View File

@@ -4,6 +4,8 @@ import getArgs from '../../util/get-args';
import logo from '../../util/output/logo';
import { getPkgName } from '../../util/pkg-name';
import setupAndLink from '../../util/link/setup-and-link';
import { getCommandName } from '../../util/pkg-name';
import param from '../../util/output/param';
const help = () => {
console.log(`
@@ -67,6 +69,13 @@ export default async function main(client: Client) {
});
if (link.status === 'error') {
if (link.reason === 'HEADLESS') {
client.output.error(
`Command ${getCommandName(
'link'
)} requires confirmation. Use option ${param('--confirm')} to confirm.`
);
}
return link.exitCode;
} else if (link.status === 'not_linked') {
// User aborted project linking questions

View File

@@ -1,9 +1,9 @@
import chalk from 'chalk';
import { join } from 'path';
import Client from '../util/client';
import { ProjectEnvTarget } from '../types';
import { emoji, prependEmoji } from '../util/emoji';
import getArgs from '../util/get-args';
import handleError from '../util/handle-error';
import setupAndLink from '../util/link/setup-and-link';
import logo from '../util/output/logo';
import stamp from '../util/output/stamp';
@@ -14,7 +14,14 @@ import {
VERCEL_DIR_PROJECT,
} from '../util/projects/link';
import { writeProjectSettings } from '../util/projects/project-settings';
import pull from './env/pull';
import envPull from './env/pull';
import { getCommandName } from '../util/pkg-name';
import param from '../util/output/param';
import type { Project, Org } from '../types';
import {
isValidEnvTarget,
getEnvTargetPlaceholder,
} from '../util/env/env-target';
const help = () => {
return console.log(`
@@ -30,7 +37,7 @@ const help = () => {
'DIR'
)} Path to the global ${'`.vercel`'} directory
-d, --debug Debug mode [off]
--env [filename] The file to write Development Environment Variables to [.env]
--environment [environment] Deployment environment [development]
-y, --yes Skip the confirmation prompt
${chalk.dim('Examples:')}
@@ -41,32 +48,46 @@ const help = () => {
${chalk.cyan(`$ ${getPkgName()} pull ./path-to-project`)}
${chalk.cyan(`$ ${getPkgName()} pull --env .env.local`)}
${chalk.cyan(`$ ${getPkgName()} pull ./path-to-project --env .env.local`)}
${chalk.gray('')} Pull specific environment's Project Settings from the cloud
${chalk.cyan(
`$ ${getPkgName()} pull --environment=${getEnvTargetPlaceholder()}`
)}
`);
};
export default async function main(client: Client) {
let argv;
try {
argv = getArgs(client.argv.slice(2), {
'--yes': Boolean,
'--env': String,
'--debug': Boolean,
'-d': '--debug',
'-y': '--yes',
});
} catch (err) {
handleError(err);
return 1;
}
function processArgs(client: Client) {
return getArgs(client.argv.slice(2), {
'--yes': Boolean,
'--env': String, // deprecated
'--environment': String,
'--debug': Boolean,
'-d': '--debug',
'-y': '--yes',
});
}
function parseArgs(client: Client) {
const argv = processArgs(client);
if (argv['--help']) {
help();
return 2;
}
const cwd = argv._[1] || process.cwd();
const yes = argv['--yes'];
const env = argv['--env'] ?? '.env';
const settingsStamp = stamp();
return argv;
}
type LinkResult = {
org: Org;
project: Project;
};
async function ensureLink(
client: Client,
cwd: string,
yes: boolean
): Promise<LinkResult | number> {
let link = await getLinkedProject(client, cwd);
if (link.status === 'not_linked') {
link = await setupAndLink(client, cwd, {
@@ -82,27 +103,80 @@ export default async function main(client: Client) {
}
if (link.status === 'error') {
if (link.reason === 'HEADLESS') {
client.output.error(
`Command ${getCommandName(
'pull'
)} requires confirmation. Use option ${param('--yes')} to confirm.`
);
}
return link.exitCode;
}
return { org: link.org, project: link.project };
}
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
);
}
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 yes = Boolean(argv['--yes']);
const environment = parseEnvironment(argv['--environment'] || undefined);
const link = await ensureLink(client, cwd, yes);
if (typeof link === 'number') {
return link;
}
const { project, org } = link;
client.config.currentTeam = org.type === 'team' ? org.id : undefined;
const result = await pull(
const pullResultCode = await pullAllEnvFiles(
environment,
client,
project,
argv,
[join(cwd, env)],
client.output
cwd
);
if (result !== 0) {
// an error happened
return result;
if (pullResultCode !== 0) {
return pullResultCode;
}
await writeProjectSettings(cwd, project, org);
const settingsStamp = stamp();
client.output.print(
`${prependEmoji(
`Downloaded project settings to ${chalk.bold(

View File

@@ -12,7 +12,7 @@ try {
}
import { join } from 'path';
import { existsSync, lstatSync } from 'fs';
import { existsSync } from 'fs';
import sourceMap from '@zeit/source-map-support';
import { mkdirp } from 'fs-extra';
import chalk from 'chalk';
@@ -82,7 +82,12 @@ let debug: (s: string) => void = () => {};
let apiUrl = 'https://api.vercel.com';
const main = async () => {
const { isTTY } = process.stdout;
let { isTTY } = process.stdout;
if (process.env.FORCE_TTY === '1') {
isTTY = true;
process.stdout.isTTY = true;
process.stdin.isTTY = true;
}
let argv;
@@ -136,6 +141,11 @@ const main = async () => {
return 1;
}
const cwd = argv['--cwd'];
if (cwd) {
process.chdir(cwd);
}
// Print update information, if available
if (notifier.update && notifier.update.latest !== pkg.version && isTTY) {
const { latest } = notifier.update;
@@ -386,34 +396,11 @@ const main = async () => {
GLOBAL_COMMANDS.has(targetOrSubcommand) ||
commands.has(targetOrSubcommand);
if (targetPathExists && subcommandExists) {
const fileType = lstatSync(targetPath).isDirectory()
? 'subdirectory'
: 'file';
const plural = targetOrSubcommand + 's';
const singular = targetOrSubcommand.endsWith('s')
? targetOrSubcommand.slice(0, -1)
: '';
let alternative = '';
if (commands.has(plural)) {
alternative = plural;
} else if (commands.has(singular)) {
alternative = singular;
}
console.error(
error(
`The supplied argument ${param(targetOrSubcommand)} is ambiguous.` +
`\nIf you wish to deploy the ${fileType} ${param(
targetOrSubcommand
)}, first run "cd ${targetOrSubcommand}". ` +
(alternative
? `\nIf you wish to use the subcommand ${param(
targetOrSubcommand
)}, use ${param(alternative)} instead.`
: '')
)
if (targetPathExists && subcommandExists && !argv['--cwd']) {
output.warn(
`Did you mean to deploy the subdirectory "${targetOrSubcommand}"? ` +
`Use \`vc --cwd ${targetOrSubcommand}\` instead.`
);
return 1;
}
if (subcommandExists) {
@@ -603,73 +590,73 @@ const main = async () => {
let func: any;
switch (targetCommand) {
case 'alias':
func = await import('./commands/alias');
func = require('./commands/alias').default;
break;
case 'billing':
func = await import('./commands/billing');
func = require('./commands/billing').default;
break;
case 'bisect':
func = await import('./commands/bisect');
func = require('./commands/bisect').default;
break;
case 'certs':
func = await import('./commands/certs');
func = require('./commands/certs').default;
break;
case 'deploy':
func = await import('./commands/deploy');
func = require('./commands/deploy').default;
break;
case 'dev':
func = await import('./commands/dev');
func = require('./commands/dev').default;
break;
case 'dns':
func = await import('./commands/dns');
func = require('./commands/dns').default;
break;
case 'domains':
func = await import('./commands/domains');
func = require('./commands/domains').default;
break;
case 'env':
func = await import('./commands/env');
func = require('./commands/env').default;
break;
case 'init':
func = await import('./commands/init');
func = require('./commands/init').default;
break;
case 'inspect':
func = await import('./commands/inspect');
func = require('./commands/inspect').default;
break;
case 'link':
func = await import('./commands/link');
func = require('./commands/link').default;
break;
case 'list':
func = await import('./commands/list');
func = require('./commands/list').default;
break;
case 'logs':
func = await import('./commands/logs');
func = require('./commands/logs').default;
break;
case 'login':
func = await import('./commands/login');
func = require('./commands/login').default;
break;
case 'logout':
func = await import('./commands/logout');
func = require('./commands/logout').default;
break;
case 'projects':
func = await import('./commands/projects');
func = require('./commands/projects').default;
break;
case 'pull':
func = await import('./commands/pull');
func = require('./commands/pull').default;
break;
case 'remove':
func = await import('./commands/remove');
func = require('./commands/remove').default;
break;
case 'secrets':
func = await import('./commands/secrets');
func = require('./commands/secrets').default;
break;
case 'teams':
func = await import('./commands/teams');
func = require('./commands/teams').default;
break;
case 'update':
func = await import('./commands/update');
func = require('./commands/update').default;
break;
case 'whoami':
func = await import('./commands/whoami');
func = require('./commands/whoami').default;
break;
default:
func = null;

View File

@@ -277,7 +277,17 @@ export interface PaginationOptions {
export type ProjectLinkResult =
| { status: 'linked'; org: Org; project: Project }
| { status: 'not_linked'; org: null; project: null }
| { status: 'error'; exitCode: number };
| {
status: 'error';
exitCode: number;
reason?:
| 'HEADLESS'
| 'NOT_AUTHORIZED'
| 'TEAM_DELETED'
| 'PATH_IS_FILE'
| 'INVALID_ROOT_DIRECTORY'
| 'MISSING_PROJECT_SETTINGS';
};
export interface Token {
id: string;

View File

@@ -25,7 +25,9 @@ const ARG_COMMON = {
'--api': String,
'--target': String
'--target': String,
'--cwd': String,
};
export default () => ARG_COMMON;

View File

@@ -0,0 +1,12 @@
import fs from 'fs-extra';
import { join } from 'path';
export default async function getPrebuiltJson(directory: string) {
try {
return await fs.readJSON(join(directory, '.vercel/output/builds.json'));
} catch (error) {
// ignoring error
}
return null;
}

View File

@@ -0,0 +1,41 @@
import { Output } from '../../util/output';
import param from '../../util/output/param';
import code from '../../util/output/code';
/**
* Parses the environment target from the `--target` and `--prod` flags.
*/
export default function parseTarget(
output: Output,
targetArg?: string,
prodArg?: boolean
): string | number | undefined {
if (targetArg) {
const deprecatedTarget = targetArg;
if (!['staging', 'production'].includes(deprecatedTarget)) {
output.error(
`The specified ${param('--target')} ${code(
deprecatedTarget
)} is not valid`
);
return 1;
}
if (deprecatedTarget === 'production') {
output.warn(
'We recommend using the much shorter `--prod` option instead of `--target production` (deprecated)'
);
}
output.debug(`Setting target to ${deprecatedTarget}`);
return deprecatedTarget;
}
if (prodArg) {
output.debug('Setting target to production');
return 'production';
}
return undefined;
}

View File

@@ -9,7 +9,6 @@ import {
import { Output } from '../output';
// @ts-ignore
import Now from '../../util';
import { VercelConfig } from '../dev/types';
import { Org } from '../../types';
import ua from '../ua';
import { linkFolderToProject } from '../projects/link';
@@ -43,7 +42,6 @@ export default async function processDeployment({
uploadStamp: () => string;
deployStamp: () => string;
quiet: boolean;
nowConfig?: VercelConfig;
force?: boolean;
withCache?: boolean;
org: Org;
@@ -62,7 +60,6 @@ export default async function processDeployment({
deployStamp,
force,
withCache,
nowConfig,
quiet,
prebuilt,
rootDirectory,
@@ -104,11 +101,7 @@ export default async function processDeployment({
const indications = [];
try {
for await (const event of createDeployment(
clientOptions,
requestBody,
nowConfig
)) {
for await (const event of createDeployment(clientOptions, requestBody)) {
if (['tip', 'notice', 'warning'].includes(event.type)) {
indications.push(event);
}

View File

@@ -1,4 +1,8 @@
import { ProjectEnvType, ProjectEnvVariable } from '../../types';
import {
ProjectEnvType,
ProjectEnvVariable,
ProjectEnvTarget,
} from '../../types';
import { Env } from '@vercel/build-utils';
function getSystemEnvValue(
@@ -16,13 +20,14 @@ export default function exposeSystemEnvs(
projectEnvs: ProjectEnvVariable[],
systemEnvValues: string[],
autoExposeSystemEnvs: boolean | undefined,
vercelUrl?: string
vercelUrl?: string,
target?: ProjectEnvTarget
) {
const envs: Env = {};
if (autoExposeSystemEnvs) {
envs['VERCEL'] = '1';
envs['VERCEL_ENV'] = 'development';
envs['VERCEL_ENV'] = target || 'development';
for (const key of systemEnvValues) {
envs[key] = getSystemEnvValue(key, { vercelUrl });

View File

@@ -26,7 +26,7 @@ export default async function addEnvRecord(
target: targets,
gitBranch: gitBranch || undefined,
};
const url = `/v7/projects/${projectId}/env`;
const url = `/v8/projects/${projectId}/env`;
await client.fetch(url, {
method: 'POST',
body: JSON.stringify(body),

View File

@@ -32,7 +32,7 @@ export default async function getEnvRecords(
query.set('decrypt', decrypt.toString());
}
const url = `/v7/projects/${projectId}/env?${query}`;
const url = `/v8/projects/${projectId}/env?${query}`;
return client.fetch<{ envs: ProjectEnvVariable[] }>(url);
}

View File

@@ -10,7 +10,7 @@ export default async function removeEnvRecord(
): Promise<void> {
output.debug(`Removing Environment Variable ${env.key}`);
const urlProject = `/v7/projects/${projectId}/env/${env.id}`;
const urlProject = `/v8/projects/${projectId}/env/${env.id}`;
await client.fetch<ProjectEnvVariable>(urlProject, {
method: 'DELETE',

View File

@@ -11,10 +11,11 @@ import getEnvRecords from './env/get-env-records';
export default async function getDecryptedEnvRecords(
output: Output,
client: Client,
projectId: string
projectId: string,
target?: ProjectEnvTarget
): Promise<{ envs: ProjectEnvVariable[] }> {
const { envs } = await getEnvRecords(output, client, projectId, {
target: ProjectEnvTarget.Development,
target: target || ProjectEnvTarget.Development,
decrypt: true,
});

View File

@@ -160,7 +160,6 @@ export default class Now extends EventEmitter {
uploadStamp,
deployStamp,
quiet,
nowConfig,
force: forceNew,
withCache,
org,

View File

@@ -19,19 +19,24 @@ export default async function inputProject(
// attempt to auto-detect a project to link
let detectedProject = null;
output.spinner('Searching for existing projects…', 1000);
try {
const [project, slugifiedProject] = await Promise.all([
getProjectByIdOrName(client, detectedProjectName, org.id),
slugifiedName !== detectedProjectName
? getProjectByIdOrName(client, slugifiedName, org.id)
: null,
]);
detectedProject = !(project instanceof ProjectNotFound)
? project
: !(slugifiedProject instanceof ProjectNotFound)
? slugifiedProject
: null;
} catch (error) {}
const [project, slugifiedProject] = await Promise.all([
getProjectByIdOrName(client, detectedProjectName, org.id),
slugifiedName !== detectedProjectName
? getProjectByIdOrName(client, slugifiedName, org.id)
: null,
]);
detectedProject = !(project instanceof ProjectNotFound)
? project
: !(slugifiedProject instanceof ProjectNotFound)
? slugifiedProject
: null;
if (detectedProject && !detectedProject.id) {
throw new Error(`Detected linked project does not have "id".`);
}
output.stopSpinner();
if (autoConfirm) {

View File

@@ -6,6 +6,8 @@ import {
getLinkedProject,
linkFolderToProject,
getVercelDirectory,
VERCEL_DIR_README,
VERCEL_DIR_PROJECT,
} from '../projects/link';
import createProject from '../projects/create-project';
import updateProject from '../projects/update-project';
@@ -49,7 +51,7 @@ export default async function setupAndLink(
const isFile = !isDirectory(path);
if (isFile) {
output.error(`Expected directory but found file: ${path}`);
return { status: 'error', exitCode: 1 };
return { status: 'error', exitCode: 1, reason: 'PATH_IS_FILE' };
}
const link = await getLinkedProject(client, path);
const isTTY = process.stdout.isTTY;
@@ -65,7 +67,12 @@ export default async function setupAndLink(
if (forceDelete) {
const vercelDir = getVercelDirectory(path);
remove(vercelDir);
remove(join(vercelDir, VERCEL_DIR_README));
remove(join(vercelDir, VERCEL_DIR_PROJECT));
}
if (!isTTY && !autoConfirm) {
return { status: 'error', exitCode: 1, reason: 'HEADLESS' };
}
const shouldStartSetup =
@@ -87,9 +94,14 @@ export default async function setupAndLink(
autoConfirm
);
} catch (err) {
if (err.code === 'NOT_AUTHORIZED' || err.code === 'TEAM_DELETED') {
if (err.code === 'NOT_AUTHORIZED') {
output.prettyError(err);
return { status: 'error', exitCode: 1 };
return { status: 'error', exitCode: 1, reason: 'NOT_AUTHORIZED' };
}
if (err.code === 'TEAM_DELETED') {
output.prettyError(err);
return { status: 'error', exitCode: 1, reason: 'TEAM_DELETED' };
}
throw err;
@@ -135,7 +147,7 @@ export default async function setupAndLink(
rootDirectory &&
!(await validateRootDirectory(output, path, sourcePath, ''))
) {
return { status: 'error', exitCode: 1 };
return { status: 'error', exitCode: 1, reason: 'INVALID_ROOT_DIRECTORY' };
}
config.currentTeam = org.type === 'team' ? org.id : undefined;
@@ -191,7 +203,11 @@ export default async function setupAndLink(
if (debug) {
console.log(deployment);
}
return { status: 'error', exitCode: 1 };
return {
status: 'error',
exitCode: 1,
reason: 'MISSING_PROJECT_SETTINGS',
};
}
const { projectSettings, framework } = deployment;
@@ -227,6 +243,7 @@ export default async function setupAndLink(
return { status: 'linked', org, project };
} catch (err) {
handleError(err);
return { status: 'error', exitCode: 1 };
}
}

View File

@@ -9,7 +9,7 @@ export default async function getProjectByNameOrId(
) {
try {
const project = await client.fetch<Project>(
`/projects/${encodeURIComponent(projectNameOrId)}`,
`/v8/projects/${encodeURIComponent(projectNameOrId)}`,
{ accountId }
);
return project;

View File

@@ -1,4 +1,4 @@
import { writeFile } from 'fs-extra';
import { outputJSON } from 'fs-extra';
import { Org, Project, ProjectLink } from '../../types';
import { getLinkFromDir, VERCEL_DIR, VERCEL_DIR_PROJECT } from './link';
import { join } from 'path';
@@ -22,21 +22,22 @@ export async function writeProjectSettings(
project: Project,
org: Org
) {
return await writeFile(
join(cwd, VERCEL_DIR, VERCEL_DIR_PROJECT),
JSON.stringify({
projectId: project.id,
orgId: org.id,
settings: {
buildCommand: project.buildCommand,
devCommand: project.devCommand,
outputDirectory: project.outputDirectory,
directoryListing: project.directoryListing,
rootDirectory: project.rootDirectory,
framework: project.framework,
},
})
);
const data = {
projectId: project.id,
orgId: org.id,
settings: {
buildCommand: project.buildCommand,
devCommand: project.devCommand,
outputDirectory: project.outputDirectory,
directoryListing: project.directoryListing,
rootDirectory: project.rootDirectory,
framework: project.framework,
},
};
const path = join(cwd, VERCEL_DIR, VERCEL_DIR_PROJECT);
return await outputJSON(path, data, {
spaces: 2,
});
}
export async function readProjectSettings(cwd: string) {

View File

@@ -2,6 +2,10 @@ import { join } from 'path';
import { fileNameSymbol } from '@vercel/client';
import { client } from '../mocks/client';
import deploy from '../../src/commands/deploy';
import { setupFixture } from '../helpers/setup-fixture';
import { defaultProject, useProject } from '../mocks/project';
import { useTeams } from '../mocks/team';
import { useUser } from '../mocks/user';
describe('deploy', () => {
it('should reject deploying a single file', async () => {
@@ -31,6 +35,59 @@ describe('deploy', () => {
);
});
it('should reject deploying a directory that does not contain ".vercel/output" when `--prebuilt` is used', async () => {
client.setArgv('deploy', __dirname, '--prebuilt');
const exitCode = await deploy(client);
expect(exitCode).toEqual(1);
expect(client.outputBuffer).toEqual(
'Error! The "--prebuilt" option was used, but no prebuilt output found in ".vercel/output". Run `vercel build` to generate a local build.\n'
);
});
it('should reject deploying a directory that was built with a different target environment when `--prebuilt --prod` is used on "preview" output', async () => {
const cwd = setupFixture('build-output-api-preview');
useUser();
useTeams('team_dummy');
useProject({
...defaultProject,
id: 'build-output-api-preview',
name: 'build-output-api-preview',
});
client.setArgv('deploy', cwd, '--prebuilt', '--prod');
const exitCode = await deploy(client);
expect(exitCode).toEqual(1);
expect(client.outputBuffer).toEqual(
'Error! The "--prebuilt" option was used with the target environment "production",' +
' but the prebuilt output found in ".vercel/output" was built with target environment "preview".' +
' Please run `vercel --prebuilt`.\n' +
'Learn More: https://vercel.link/prebuilt-environment-mismatch\n'
);
});
it('should reject deploying a directory that was built with a different target environment when `--prebuilt` is used on "production" output', async () => {
const cwd = setupFixture('build-output-api-production');
useUser();
useTeams('team_dummy');
useProject({
...defaultProject,
id: 'build-output-api-preview',
name: 'build-output-api-preview',
});
client.setArgv('deploy', cwd, '--prebuilt');
const exitCode = await deploy(client);
expect(exitCode).toEqual(1);
expect(client.outputBuffer).toEqual(
'Error! The "--prebuilt" option was used with the target environment "preview",' +
' but the prebuilt output found in ".vercel/output" was built with target environment "production".' +
' Please run `vercel --prebuilt --prod`.\n' +
'Learn More: https://vercel.link/prebuilt-environment-mismatch\n'
);
});
it('should reject deploying "version: 1"', async () => {
client.setArgv('deploy');
client.localConfig = {

View File

@@ -0,0 +1,75 @@
import fs from 'fs-extra';
import path from 'path';
import env from '../../src/commands/env';
import { setupFixture } from '../helpers/setup-fixture';
import { client } from '../mocks/client';
import { defaultProject, useProject } from '../mocks/project';
import { useTeams } from '../mocks/team';
import { useUser } from '../mocks/user';
describe('env', () => {
describe('pull', () => {
it('should handle pulling', async () => {
const cwd = setupFixture('vercel-env-pull');
useUser();
useTeams('team_dummy');
useProject({
...defaultProject,
id: 'vercel-env-pull',
name: 'vercel-env-pull',
});
client.setArgv('env', 'pull', '--yes', '--cwd', cwd);
const exitCode = await env(client);
expect(exitCode, client.outputBuffer).toEqual(0);
const rawDevEnv = await fs.readFile(path.join(cwd, '.env'));
// check for development env value
const devFileHasDevEnv = rawDevEnv.toString().includes('SPECIAL_FLAG');
expect(devFileHasDevEnv).toBeTruthy();
});
it('should handle alternate filename', async () => {
const cwd = setupFixture('vercel-env-pull');
useUser();
useTeams('team_dummy');
useProject({
...defaultProject,
id: 'vercel-env-pull',
name: 'vercel-env-pull',
});
client.setArgv('env', 'pull', 'other.env', '--yes', '--cwd', cwd);
const exitCode = await env(client);
expect(exitCode, client.outputBuffer).toEqual(0);
const rawDevEnv = await fs.readFile(path.join(cwd, 'other.env'));
// check for development env value
const devFileHasDevEnv = rawDevEnv.toString().includes('SPECIAL_FLAG');
expect(devFileHasDevEnv).toBeTruthy();
});
it('should expose production system env variables', async () => {
const cwd = setupFixture('vercel-env-pull');
useUser();
useTeams('team_dummy');
useProject({
...defaultProject,
id: 'vercel-env-pull',
name: 'vercel-env-pull',
autoExposeSystemEnvs: true,
});
client.setArgv('env', 'pull', 'other.env', '--yes', '--cwd', cwd);
const exitCode = await env(client);
expect(exitCode, client.outputBuffer).toEqual(0);
const rawDevEnv = await fs.readFile(path.join(cwd, 'other.env'));
const productionFileHasVercelEnv = rawDevEnv
.toString()
.includes('VERCEL_ENV="development"');
expect(productionFileHasVercelEnv).toBeTruthy();
});
});
});

View File

@@ -11,35 +11,138 @@ describe('pull', () => {
it('should handle pulling', async () => {
const cwd = setupFixture('vercel-pull-next');
useUser();
useTeams();
useTeams('team_dummy');
useProject({
...defaultProject,
id: 'vercel-pull-next',
name: 'vercel-pull-next',
});
client.setArgv('pull', '--yes', cwd);
client.setArgv('pull', cwd);
const exitCode = await pull(client);
expect(exitCode).toEqual(0);
expect(exitCode, client.outputBuffer).toEqual(0);
const rawDevEnv = await fs.readFile(
path.join(cwd, '.vercel', '.env.development.local')
);
const devFileHasDevEnv = rawDevEnv.toString().includes('SPECIAL_FLAG');
expect(devFileHasDevEnv).toBeTruthy();
});
it('should handle custom --env flag', async () => {
it('should fail with message to pull without a link and without --env', async () => {
try {
process.stdout.isTTY = undefined;
const cwd = setupFixture('vercel-pull-unlinked');
useUser();
useTeams('team_dummy');
client.setArgv('pull', cwd);
const exitCode = await pull(client);
expect(exitCode, client.outputBuffer).toEqual(1);
expect(client.outputBuffer).toMatch(
/Command `vercel pull` requires confirmation. Use option "--yes" to confirm./gm
);
} finally {
process.stdout.isTTY = true;
}
});
it('should fail without message to pull without a link and with --env', async () => {
const cwd = setupFixture('vercel-pull-next');
useUser();
useTeams();
useTeams('team_dummy');
client.setArgv('pull', cwd, '--yes');
const exitCode = await pull(client);
expect(exitCode, client.outputBuffer).toEqual(1);
expect(client.outputBuffer).not.toMatch(
/Command `vercel pull` requires confirmation. Use option "--yes" to confirm./gm
);
});
it('should handle pulling with env vars (headless mode)', async () => {
try {
process.env.VERCEL_PROJECT_ID = 'vercel-pull-next';
process.env.VERCEL_ORG_ID = 'team_dummy';
const cwd = setupFixture('vercel-pull-next');
// Remove the `.vercel` dir to ensure that the `pull`
// command creates a new one based on env vars
await fs.remove(path.join(cwd, '.vercel'));
useUser();
useTeams('team_dummy');
useProject({
...defaultProject,
id: 'vercel-pull-next',
name: 'vercel-pull-next',
});
client.setArgv('pull', cwd);
const exitCode = await pull(client);
expect(exitCode, client.outputBuffer).toEqual(0);
const config = await fs.readJSON(path.join(cwd, '.vercel/project.json'));
expect(config).toMatchInlineSnapshot(`
Object {
"orgId": "team_dummy",
"projectId": "vercel-pull-next",
"settings": Object {},
}
`);
} finally {
delete process.env.VERCEL_PROJECT_ID;
delete process.env.VERCEL_ORG_ID;
}
});
it('should handle --environment=preview flag', async () => {
const cwd = setupFixture('vercel-pull-next');
useUser();
useTeams('team_dummy');
useProject({
...defaultProject,
id: 'vercel-pull-next',
name: 'vercel-pull-next',
});
const expectedEnvFilename = '.env.vercel';
client.setArgv('pull', '--yes', `--env=${expectedEnvFilename}`, cwd);
client.setArgv('pull', '--environment=preview', cwd);
const exitCode = await pull(client);
const actualEnv = await fs.pathExists(path.join(cwd, expectedEnvFilename));
const raw = await fs.readFile(path.join(cwd, expectedEnvFilename));
expect(exitCode).toEqual(0);
expect(actualEnv).toBeTruthy();
expect(raw.includes('# Created by Vercel CLI')).toBeTruthy();
const rawPreviewEnv = await fs.readFile(
path.join(cwd, '.vercel', '.env.preview.local')
);
const previewFileHasPreviewEnv = rawPreviewEnv
.toString()
.includes('REDIS_CONNECTION_STRING');
expect(previewFileHasPreviewEnv).toBeTruthy();
});
it('should handle --environment=production flag', async () => {
const cwd = setupFixture('vercel-pull-next');
useUser();
useTeams('team_dummy');
useProject({
...defaultProject,
id: 'vercel-pull-next',
name: 'vercel-pull-next',
});
client.setArgv('pull', '--environment=production', cwd);
const exitCode = await pull(client);
expect(exitCode).toEqual(0);
const rawProdEnv = await fs.readFile(
path.join(cwd, '.vercel', '.env.production.local')
);
const previewFileHasPreviewEnv1 = rawProdEnv
.toString()
.includes('REDIS_CONNECTION_STRING');
expect(previewFileHasPreviewEnv1).toBeTruthy();
const previewFileHasPreviewEnv2 = rawProdEnv
.toString()
.includes('SQL_CONNECTION_STRING');
expect(previewFileHasPreviewEnv2).toBeTruthy();
});
});

View File

@@ -15,7 +15,7 @@ cache:
env:
global:
# See https://git.io/vdao3 for details.
# See https://github.com/ember-cli/ember-cli/blob/master/docs/build-concurrency.md
- JOBS=1
script:

View File

@@ -0,0 +1,3 @@
{
"target": "preview"
}

View File

@@ -0,0 +1,3 @@
{
"target": "production"
}

View File

@@ -0,0 +1,2 @@
.next
yarn.lock

View File

@@ -0,0 +1,4 @@
{
"orgId": "team_dummy",
"projectId": "vercel-env-pull"
}

View File

@@ -0,0 +1,12 @@
{
"scripts": {
"build": "next build",
"dev": "next",
"now-build": "next build"
},
"dependencies": {
"next": "^8.0.0",
"react": "^16.7.0",
"react-dom": "^16.7.0"
}
}

View File

@@ -0,0 +1,11 @@
import { withRouter } from 'next/router';
function Index({ router }) {
const data = {
pathname: router.pathname,
query: router.query,
};
return <div>{JSON.stringify(data)}</div>;
}
export default withRouter(Index);

View File

@@ -0,0 +1,10 @@
{
"version": 2,
"name": "vercel-env-pull",
"routes": [
{
"src": "/(.*)",
"dest": "/index?route-param=b"
}
]
}

View File

@@ -1,3 +1,2 @@
.next
yarn.lock
.vercel

View File

@@ -0,0 +1,4 @@
{
"orgId": "team_dummy",
"projectId": "vercel-pull-next"
}

View File

@@ -1,7 +1,6 @@
{
"version": 2,
"name": "vercel-pull-next",
"builds": [{ "src": "package.json", "use": "@vercel/next@canary" }],
"routes": [
{
"src": "/(.*)",

View File

@@ -0,0 +1,2 @@
.next
yarn.lock

View File

@@ -0,0 +1,12 @@
{
"scripts": {
"build": "next build",
"dev": "next",
"now-build": "next build"
},
"dependencies": {
"next": "^8.0.0",
"react": "^16.7.0",
"react-dom": "^16.7.0"
}
}

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