Compare commits

...

33 Commits

Author SHA1 Message Date
Vercel Release Bot
f057f0421b Version Packages (#10081) 2023-06-14 10:47:10 -07:00
Nathan Rajlich
42c0b32a8d [fs-detectors] Use LocalFileSystemDetector instead of FixtureFilesystem (#10100)
The code for these two are almost identical, so consolidate into one codebase.

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

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

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

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

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

Fixing:

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

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

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

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

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

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

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

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

---------

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

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

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

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

---------

Co-authored-by: Nathan Rajlich <n@n8.io>
2023-06-05 10:05:53 +02:00
Vercel Release Bot
5070e3bbbd [tests] Upgrade Turbo to version 1.10.1 (#10059)
This auto-generated PR updates Turbo to version 1.10.1
2023-06-05 03:57:56 +00:00
Vercel Release Bot
4ad1cbbd7d Version Packages (#10058)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2023-06-02 19:29:17 -05:00
Chris Barber
4f4e09477d Attempting to publish build-utils and vercel (#10057) 2023-06-02 19:24:32 -05:00
Chris Barber
cd35071f60 Attempting to publish build-utils and vercel (#10057) 2023-06-02 19:22:22 -05:00
Vercel Release Bot
f373c94508 Version Packages (#10055)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2023-06-02 16:28:57 -05:00
Chris Barber
553c001eb0 [cli] vc build ignore .env* & ignore files for @vercel/static (#10056)
For projects with static files only (e.g. `@vercel/static` builder), do not copy`.env*`, `.vercelignore`, or `.nowignore` into the BOA output dir.
2023-06-02 21:04:03 +00:00
Chris Barber
f6c3a95783 [cli] Ensure .npmrc does not contain use-node-version (#10049)
When a project has a `.npmrc` containing `use-node-version`, package managers (notably `pnpm`) will download the specified Node.js version. This is not the correct way as it can lead to `pnpm` downloading Node.js 18 or newer which depends on a version of GLIBC that is not present in the current AWS image. The proper way is to set the `"engines"` in the `package.json`.

<img width="468" alt="image" src="https://github.com/vercel/vercel/assets/97262/0974cf05-6a11-4d95-88e8-13affc4aad2a">

Discussion: https://github.com/orgs/vercel/discussions/2436
2023-06-02 15:27:28 +00:00
Sean Massa
c0bcef0ca4 Update CODEOWNERS for .changeset/ (#10053) 2023-06-01 12:07:18 -05:00
101 changed files with 1492 additions and 564 deletions

4
.github/CODEOWNERS vendored
View File

@@ -1,6 +1,7 @@
# Documentation
# https://help.github.com/en/articles/about-code-owners
# Restricted Paths
* @TooTallNate @EndangeredMassa @styfle @cb1kenobi @Ethan-Arrowood
/.github/workflows @TooTallNate @EndangeredMassa @styfle @cb1kenobi @Ethan-Arrowood @ijjk
/packages/fs-detectors @TooTallNate @EndangeredMassa @styfle @cb1kenobi @Ethan-Arrowood @agadzik @chloetedder
@@ -14,3 +15,6 @@
/examples/jekyll @styfle
/examples/zola @styfle
/packages/node @TooTallNate @EndangeredMassa @styfle @cb1kenobi @Ethan-Arrowood @Kikobeats
# Unrestricted Paths
.changeset/

View File

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

View File

@@ -8,17 +8,17 @@
"name": "nextjs",
"version": "0.1.0",
"dependencies": {
"eslint": "8.41.0",
"eslint-config-next": "13.4.4",
"next": "13.4.4",
"eslint": "8.42.0",
"eslint-config-next": "13.4.5",
"next": "13.4.5",
"react": "18.2.0",
"react-dom": "18.2.0"
}
},
"node_modules/@babel/runtime": {
"version": "7.21.5",
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.21.5.tgz",
"integrity": "sha512-8jI69toZqqcsnqGGqwGS4Qb1VwLOEp4hz+CXPywcvjs60u3B4Pom/U/7rm4W8tMOYEB+E9wgD0mW1l3r8qlI9Q==",
"version": "7.22.5",
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.22.5.tgz",
"integrity": "sha512-ecjvYlnAaZ/KVneE/OdKYBYfgXV3Ptu6zQWmgEF7vwKhQnvVS6bjMD2XYgj+SNvQ1GfK/pjgokfPkC/2CO8CuA==",
"dependencies": {
"regenerator-runtime": "^0.13.11"
},
@@ -71,17 +71,17 @@
}
},
"node_modules/@eslint/js": {
"version": "8.41.0",
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.41.0.tgz",
"integrity": "sha512-LxcyMGxwmTh2lY9FwHPGWOHmYFCZvbrFCBZL4FzSSsxsRPuhrYUg/49/0KDfW8tnIEaEHtfmn6+NPN+1DqaNmA==",
"version": "8.42.0",
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.42.0.tgz",
"integrity": "sha512-6SWlXpWU5AvId8Ac7zjzmIOqMOba/JWY8XZ4A7q7Gn1Vlfg/SFFIlrtHXt9nPn4op9ZPAkl91Jao+QQv3r/ukw==",
"engines": {
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
}
},
"node_modules/@humanwhocodes/config-array": {
"version": "0.11.8",
"resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.8.tgz",
"integrity": "sha512-UybHIJzJnR5Qc/MsD9Kr+RpO2h+/P1GhOwdiLPXK5TWk5sgTdu88bTD9UP+CKbPPh5Rni1u0GjAdYQLemG8g+g==",
"version": "0.11.10",
"resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.10.tgz",
"integrity": "sha512-KVVjQmNUepDVGXNuoRRdmmEjruj0KfiGSbS8LVc12LMsWDQzRXJ0qdhN8L8uUigKpfEHRhlaQFY0ib1tnUbNeQ==",
"dependencies": {
"@humanwhocodes/object-schema": "^1.2.1",
"debug": "^4.1.1",
@@ -109,22 +109,22 @@
"integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA=="
},
"node_modules/@next/env": {
"version": "13.4.4",
"resolved": "https://registry.npmjs.org/@next/env/-/env-13.4.4.tgz",
"integrity": "sha512-q/y7VZj/9YpgzDe64Zi6rY1xPizx80JjlU2BTevlajtaE3w1LqweH1gGgxou2N7hdFosXHjGrI4OUvtFXXhGLg=="
"version": "13.4.5",
"resolved": "https://registry.npmjs.org/@next/env/-/env-13.4.5.tgz",
"integrity": "sha512-SG/gKH6eij4vwQy87b/3mbpQ1X3x2vUdnpwq6/qL2IQWjtq58EY/UuNAp9CoEZoC9sI4L9AD1r+73Z9r4d3uug=="
},
"node_modules/@next/eslint-plugin-next": {
"version": "13.4.4",
"resolved": "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-13.4.4.tgz",
"integrity": "sha512-5jnh7q6I15efnjR/rR+/TGTc9hn53g3JTbEjAMjmeQiExKqEUgIXqrHI5zlTNlNyzCPkBB860/ctxXheZaF2Vw==",
"version": "13.4.5",
"resolved": "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-13.4.5.tgz",
"integrity": "sha512-/xD/kyJhXmBZq+0xGKOdjL22c9/4i3mBAXaU9aOGEHTXqqFeOz8scJbScWF13aMqigeoFCsDqngIB2MIatcn4g==",
"dependencies": {
"glob": "7.1.7"
}
},
"node_modules/@next/swc-darwin-arm64": {
"version": "13.4.4",
"resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-13.4.4.tgz",
"integrity": "sha512-xfjgXvp4KalNUKZMHmsFxr1Ug+aGmmO6NWP0uoh4G3WFqP/mJ1xxfww0gMOeMeSq/Jyr5k7DvoZ2Pv+XOITTtw==",
"version": "13.4.5",
"resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-13.4.5.tgz",
"integrity": "sha512-XvTzi2ASUN5bECFIAAcBiSoDb0xsq+KLj4F0bof4d4rdc+FgOqLvseGQaOXwVi1TIh5bHa7o4b6droSJMO5+2g==",
"cpu": [
"arm64"
],
@@ -137,9 +137,9 @@
}
},
"node_modules/@next/swc-darwin-x64": {
"version": "13.4.4",
"resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-13.4.4.tgz",
"integrity": "sha512-ZY9Ti1hkIwJsxGus3nlubIkvYyB0gNOYxKrfsOrLEqD0I2iCX8D7w8v6QQZ2H+dDl6UT29oeEUdDUNGk4UEpfg==",
"version": "13.4.5",
"resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-13.4.5.tgz",
"integrity": "sha512-NQdqal/VKAqlJTuzhjZmNtdo8QSqwmfO7b2xJSAengTEVxQvsH76oGEzQeIv8Ci4NP6DysAFtFrJq++TmIxcUA==",
"cpu": [
"x64"
],
@@ -152,9 +152,9 @@
}
},
"node_modules/@next/swc-linux-arm64-gnu": {
"version": "13.4.4",
"resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-13.4.4.tgz",
"integrity": "sha512-+KZnDeMShYkpkqAvGCEDeqYTRADJXc6SY1jWXz+Uo6qWQO/Jd9CoyhTJwRSxvQA16MoYzvILkGaDqirkRNctyA==",
"version": "13.4.5",
"resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-13.4.5.tgz",
"integrity": "sha512-nB8TjtpJCXtzIFjYOMbnQu68ajkA8QK58TreHjTGojSQjsF0StDqo5zFHglVVVHrd8d3N/+EjC18yFNSWnd/ZA==",
"cpu": [
"arm64"
],
@@ -167,9 +167,9 @@
}
},
"node_modules/@next/swc-linux-arm64-musl": {
"version": "13.4.4",
"resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-13.4.4.tgz",
"integrity": "sha512-evC1twrny2XDT4uOftoubZvW3EG0zs0ZxMwEtu/dDGVRO5n5pT48S8qqEIBGBUZYu/Xx4zzpOkIxx1vpWdE+9A==",
"version": "13.4.5",
"resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-13.4.5.tgz",
"integrity": "sha512-W126XUW599OV3giSH9Co40VpT8VAOT47xONVHXZaYEpeca0qEevjj6WUr5IJu/8u+XGWm5xI1S0DYWjR6W+olw==",
"cpu": [
"arm64"
],
@@ -182,9 +182,9 @@
}
},
"node_modules/@next/swc-linux-x64-gnu": {
"version": "13.4.4",
"resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-13.4.4.tgz",
"integrity": "sha512-PX706XcCHr2FfkyhP2lpf+pX/tUvq6/ke7JYnnr0ykNdEMo+sb7cC/o91gnURh4sPYSiZJhsF2gbIqg9rciOHQ==",
"version": "13.4.5",
"resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-13.4.5.tgz",
"integrity": "sha512-ZbPLO/oztQdtjGmWvGhRmtkZ6j9kQqg65kiO7F7Ijj7ojTtu3hh/vY+XRsHa/4Cse6HgyJ8XGZJMGoLb8ecQfQ==",
"cpu": [
"x64"
],
@@ -197,9 +197,9 @@
}
},
"node_modules/@next/swc-linux-x64-musl": {
"version": "13.4.4",
"resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-13.4.4.tgz",
"integrity": "sha512-TKUUx3Ftd95JlHV6XagEnqpT204Y+IsEa3awaYIjayn0MOGjgKZMZibqarK3B1FsMSPaieJf2FEAcu9z0yT5aA==",
"version": "13.4.5",
"resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-13.4.5.tgz",
"integrity": "sha512-f+/h8KMNixVUoRB+2vza8I+jsthJ4KcvopGUsDIUHe7Q4t+m8nKwGFBeyNu9qNIenYK5g5QYEsSwYFEqZylrTQ==",
"cpu": [
"x64"
],
@@ -212,9 +212,9 @@
}
},
"node_modules/@next/swc-win32-arm64-msvc": {
"version": "13.4.4",
"resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-13.4.4.tgz",
"integrity": "sha512-FP8AadgSq4+HPtim7WBkCMGbhr5vh9FePXiWx9+YOdjwdQocwoCK5ZVC3OW8oh3TWth6iJ0AXJ/yQ1q1cwSZ3A==",
"version": "13.4.5",
"resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-13.4.5.tgz",
"integrity": "sha512-dvtPQZ5+J+zUE1uq7gP853Oj63e+n0T1ydZ/yRdVh7d8zW9ZFuC9fFrg3MqP1cv1NPPur8rrTqDKN2mRBkSSBw==",
"cpu": [
"arm64"
],
@@ -227,9 +227,9 @@
}
},
"node_modules/@next/swc-win32-ia32-msvc": {
"version": "13.4.4",
"resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-13.4.4.tgz",
"integrity": "sha512-3WekVmtuA2MCdcAOrgrI+PuFiFURtSyyrN1I3UPtS0ckR2HtLqyqmS334Eulf15g1/bdwMteePdK363X/Y9JMg==",
"version": "13.4.5",
"resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-13.4.5.tgz",
"integrity": "sha512-gK9zwGe25x31S4AjPy3Bf2niQvHIAbmwgkzmqWG3OmD4K2Z/Dh2ju4vuyzPzIt0pwQe4B520meP9NizTBmVWSg==",
"cpu": [
"ia32"
],
@@ -242,9 +242,9 @@
}
},
"node_modules/@next/swc-win32-x64-msvc": {
"version": "13.4.4",
"resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-13.4.4.tgz",
"integrity": "sha512-AHRITu/CrlQ+qzoqQtEMfaTu7GHaQ6bziQln/pVWpOYC1wU+Mq6VQQFlsDtMCnDztPZtppAXdvvbNS7pcfRzlw==",
"version": "13.4.5",
"resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-13.4.5.tgz",
"integrity": "sha512-iyNQVc7eGehrik9RJt9xGcnO6b/pi8C7GCfg8RGenx1IlalEKbYRgBJloF7DQzwlrV47E9bQl8swT+JawaNcKA==",
"cpu": [
"x64"
],
@@ -308,9 +308,9 @@
}
},
"node_modules/@rushstack/eslint-patch": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.3.0.tgz",
"integrity": "sha512-IthPJsJR85GhOkp3Hvp8zFOPK5ynKn6STyHa/WZpioK7E1aYDiBzpqQPrngc14DszIUkIrdd3k9Iu0XSzlP/1w=="
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.3.1.tgz",
"integrity": "sha512-RkmuBcqiNioeeBKbgzMlOdreUkJfYaSjwgx9XDgGGpjvWgyaxWvDmZVSN9CS6LjEASadhgPv2BcFp+SeouWXXA=="
},
"node_modules/@swc/helpers": {
"version": "0.5.1",
@@ -326,13 +326,13 @@
"integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ=="
},
"node_modules/@typescript-eslint/parser": {
"version": "5.59.7",
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.59.7.tgz",
"integrity": "sha512-VhpsIEuq/8i5SF+mPg9jSdIwgMBBp0z9XqjiEay+81PYLJuroN+ET1hM5IhkiYMJd9MkTz8iJLt7aaGAgzWUbQ==",
"version": "5.59.9",
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.59.9.tgz",
"integrity": "sha512-FsPkRvBtcLQ/eVK1ivDiNYBjn3TGJdXy2fhXX+rc7czWl4ARwnpArwbihSOHI2Peg9WbtGHrbThfBUkZZGTtvQ==",
"dependencies": {
"@typescript-eslint/scope-manager": "5.59.7",
"@typescript-eslint/types": "5.59.7",
"@typescript-eslint/typescript-estree": "5.59.7",
"@typescript-eslint/scope-manager": "5.59.9",
"@typescript-eslint/types": "5.59.9",
"@typescript-eslint/typescript-estree": "5.59.9",
"debug": "^4.3.4"
},
"engines": {
@@ -352,12 +352,12 @@
}
},
"node_modules/@typescript-eslint/scope-manager": {
"version": "5.59.7",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.59.7.tgz",
"integrity": "sha512-FL6hkYWK9zBGdxT2wWEd2W8ocXMu3K94i3gvMrjXpx+koFYdYV7KprKfirpgY34vTGzEPPuKoERpP8kD5h7vZQ==",
"version": "5.59.9",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.59.9.tgz",
"integrity": "sha512-8RA+E+w78z1+2dzvK/tGZ2cpGigBZ58VMEHDZtpE1v+LLjzrYGc8mMaTONSxKyEkz3IuXFM0IqYiGHlCsmlZxQ==",
"dependencies": {
"@typescript-eslint/types": "5.59.7",
"@typescript-eslint/visitor-keys": "5.59.7"
"@typescript-eslint/types": "5.59.9",
"@typescript-eslint/visitor-keys": "5.59.9"
},
"engines": {
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
@@ -368,9 +368,9 @@
}
},
"node_modules/@typescript-eslint/types": {
"version": "5.59.7",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.59.7.tgz",
"integrity": "sha512-UnVS2MRRg6p7xOSATscWkKjlf/NDKuqo5TdbWck6rIRZbmKpVNTLALzNvcjIfHBE7736kZOFc/4Z3VcZwuOM/A==",
"version": "5.59.9",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.59.9.tgz",
"integrity": "sha512-uW8H5NRgTVneSVTfiCVffBb8AbwWSKg7qcA4Ot3JI3MPCJGsB4Db4BhvAODIIYE5mNj7Q+VJkK7JxmRhk2Lyjw==",
"engines": {
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
},
@@ -380,12 +380,12 @@
}
},
"node_modules/@typescript-eslint/typescript-estree": {
"version": "5.59.7",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.59.7.tgz",
"integrity": "sha512-4A1NtZ1I3wMN2UGDkU9HMBL+TIQfbrh4uS0WDMMpf3xMRursDbqEf1ahh6vAAe3mObt8k3ZATnezwG4pdtWuUQ==",
"version": "5.59.9",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.59.9.tgz",
"integrity": "sha512-pmM0/VQ7kUhd1QyIxgS+aRvMgw+ZljB3eDb+jYyp6d2bC0mQWLzUDF+DLwCTkQ3tlNyVsvZRXjFyV0LkU/aXjA==",
"dependencies": {
"@typescript-eslint/types": "5.59.7",
"@typescript-eslint/visitor-keys": "5.59.7",
"@typescript-eslint/types": "5.59.9",
"@typescript-eslint/visitor-keys": "5.59.9",
"debug": "^4.3.4",
"globby": "^11.1.0",
"is-glob": "^4.0.3",
@@ -406,11 +406,11 @@
}
},
"node_modules/@typescript-eslint/visitor-keys": {
"version": "5.59.7",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.59.7.tgz",
"integrity": "sha512-tyN+X2jvMslUszIiYbF0ZleP+RqQsFVpGrKI6e0Eet1w8WmhsAtmzaqm8oM8WJQ1ysLwhnsK/4hYHJjOgJVfQQ==",
"version": "5.59.9",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.59.9.tgz",
"integrity": "sha512-bT7s0td97KMaLwpEBckbzj/YohnvXtqbe2XgqNvTl6RJVakY5mvENOTPvw5u66nljfZxthESpDozs86U+oLY8Q==",
"dependencies": {
"@typescript-eslint/types": "5.59.7",
"@typescript-eslint/types": "5.59.9",
"eslint-visitor-keys": "^3.3.0"
},
"engines": {
@@ -696,9 +696,9 @@
}
},
"node_modules/caniuse-lite": {
"version": "1.0.30001489",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001489.tgz",
"integrity": "sha512-x1mgZEXK8jHIfAxm+xgdpHpk50IN3z3q3zP261/WS+uvePxW8izXuCu6AHz0lkuYTlATDehiZ/tNyYBdSQsOUQ==",
"version": "1.0.30001498",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001498.tgz",
"integrity": "sha512-LFInN2zAwx3ANrGCDZ5AKKJroHqNKyjXitdV5zRIVIaQlXKj3GmxUKagoKsjqUfckpAObPCEWnk5EeMlyMWcgw==",
"funding": [
{
"type": "opencollective",
@@ -1034,15 +1034,15 @@
}
},
"node_modules/eslint": {
"version": "8.41.0",
"resolved": "https://registry.npmjs.org/eslint/-/eslint-8.41.0.tgz",
"integrity": "sha512-WQDQpzGBOP5IrXPo4Hc0814r4/v2rrIsB0rhT7jtunIalgg6gYXWhRMOejVO8yH21T/FGaxjmFjBMNqcIlmH1Q==",
"version": "8.42.0",
"resolved": "https://registry.npmjs.org/eslint/-/eslint-8.42.0.tgz",
"integrity": "sha512-ulg9Ms6E1WPf67PHaEY4/6E2tEn5/f7FXGzr3t9cBMugOmf1INYvuUwwh1aXQN4MfJ6a5K2iNwP3w4AColvI9A==",
"dependencies": {
"@eslint-community/eslint-utils": "^4.2.0",
"@eslint-community/regexpp": "^4.4.0",
"@eslint/eslintrc": "^2.0.3",
"@eslint/js": "8.41.0",
"@humanwhocodes/config-array": "^0.11.8",
"@eslint/js": "8.42.0",
"@humanwhocodes/config-array": "^0.11.10",
"@humanwhocodes/module-importer": "^1.0.1",
"@nodelib/fs.walk": "^1.2.8",
"ajv": "^6.10.0",
@@ -1089,11 +1089,11 @@
}
},
"node_modules/eslint-config-next": {
"version": "13.4.4",
"resolved": "https://registry.npmjs.org/eslint-config-next/-/eslint-config-next-13.4.4.tgz",
"integrity": "sha512-z/PMbm6L0iC/fwISULxe8IVy4DtNqZk2wQY711o35klenq70O6ns82A8yuMVCFjHC0DIyB2lyugesRtuk9u8dQ==",
"version": "13.4.5",
"resolved": "https://registry.npmjs.org/eslint-config-next/-/eslint-config-next-13.4.5.tgz",
"integrity": "sha512-7qgJmRp9ClRzPgkzEz7ahK+Rasiv4k2aU3eqkkORzseNUGdtImZVYomcXUhUheHwkxzdN2p//nbIA7zJrCxsCg==",
"dependencies": {
"@next/eslint-plugin-next": "13.4.4",
"@next/eslint-plugin-next": "13.4.5",
"@rushstack/eslint-patch": "^1.1.3",
"@typescript-eslint/parser": "^5.42.0",
"eslint-import-resolver-node": "^0.3.6",
@@ -1664,9 +1664,12 @@
}
},
"node_modules/get-tsconfig": {
"version": "4.5.0",
"resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.5.0.tgz",
"integrity": "sha512-MjhiaIWCJ1sAU4pIQ5i5OfOuHHxVo1oYeNsWTON7jxYkod8pHocXeh+SSbmu5OZZZK73B6cbJ2XADzXehLyovQ==",
"version": "4.6.0",
"resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.6.0.tgz",
"integrity": "sha512-lgbo68hHTQnFddybKbbs/RDRJnJT5YyGy2kQzVwbq+g67X73i+5MVTval34QxGkOe9X5Ujf1UYpCaphLyltjEg==",
"dependencies": {
"resolve-pkg-maps": "^1.0.0"
},
"funding": {
"url": "https://github.com/privatenumber/get-tsconfig?sponsor=1"
}
@@ -1701,6 +1704,11 @@
"node": ">=10.13.0"
}
},
"node_modules/glob-to-regexp": {
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz",
"integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw=="
},
"node_modules/globals": {
"version": "13.20.0",
"resolved": "https://registry.npmjs.org/globals/-/globals-13.20.0.tgz",
@@ -2453,16 +2461,17 @@
"integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw=="
},
"node_modules/next": {
"version": "13.4.4",
"resolved": "https://registry.npmjs.org/next/-/next-13.4.4.tgz",
"integrity": "sha512-C5S0ysM0Ily9McL4Jb48nOQHT1BukOWI59uC3X/xCMlYIh9rJZCv7nzG92J6e1cOBqQbKovlpgvHWFmz4eKKEA==",
"version": "13.4.5",
"resolved": "https://registry.npmjs.org/next/-/next-13.4.5.tgz",
"integrity": "sha512-pfNsRLVM9e5Y1/z02VakJRfD6hMQkr24FaN2xc9GbcZDBxoOgiNAViSg5cXwlWCoMhtm4U315D7XYhgOr96Q3Q==",
"dependencies": {
"@next/env": "13.4.4",
"@next/env": "13.4.5",
"@swc/helpers": "0.5.1",
"busboy": "1.6.0",
"caniuse-lite": "^1.0.30001406",
"postcss": "8.4.14",
"styled-jsx": "5.1.1",
"watchpack": "2.4.0",
"zod": "3.21.4"
},
"bin": {
@@ -2472,15 +2481,15 @@
"node": ">=16.8.0"
},
"optionalDependencies": {
"@next/swc-darwin-arm64": "13.4.4",
"@next/swc-darwin-x64": "13.4.4",
"@next/swc-linux-arm64-gnu": "13.4.4",
"@next/swc-linux-arm64-musl": "13.4.4",
"@next/swc-linux-x64-gnu": "13.4.4",
"@next/swc-linux-x64-musl": "13.4.4",
"@next/swc-win32-arm64-msvc": "13.4.4",
"@next/swc-win32-ia32-msvc": "13.4.4",
"@next/swc-win32-x64-msvc": "13.4.4"
"@next/swc-darwin-arm64": "13.4.5",
"@next/swc-darwin-x64": "13.4.5",
"@next/swc-linux-arm64-gnu": "13.4.5",
"@next/swc-linux-arm64-musl": "13.4.5",
"@next/swc-linux-x64-gnu": "13.4.5",
"@next/swc-linux-x64-musl": "13.4.5",
"@next/swc-win32-arm64-msvc": "13.4.5",
"@next/swc-win32-ia32-msvc": "13.4.5",
"@next/swc-win32-x64-msvc": "13.4.5"
},
"peerDependencies": {
"@opentelemetry/api": "^1.1.0",
@@ -2927,6 +2936,14 @@
"node": ">=4"
}
},
"node_modules/resolve-pkg-maps": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz",
"integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==",
"funding": {
"url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1"
}
},
"node_modules/reusify": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz",
@@ -3382,9 +3399,9 @@
}
},
"node_modules/tslib": {
"version": "2.5.2",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.2.tgz",
"integrity": "sha512-5svOrSA2w3iGFDs1HibEVBGbDrAY82bFQ3HZ3ixB+88nsbsWQoKqDRb5UBYAUPEzbBn6dAp5gRNXglySbx1MlA=="
"version": "2.5.3",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.3.tgz",
"integrity": "sha512-mSxlJJwl3BMEQCUNnxXBU9jP4JBktcEGhURcPR6VQVlnP0FdDEsIaz0C35dXNGLyRfrATNofF0F5p2KPxQgB+w=="
},
"node_modules/tsutils": {
"version": "3.21.0",
@@ -3441,16 +3458,16 @@
}
},
"node_modules/typescript": {
"version": "5.0.4",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.0.4.tgz",
"integrity": "sha512-cW9T5W9xY37cc+jfEnaUvX91foxtHkza3Nw3wkoF4sSlKn0MONdkdEndig/qPBWXNkmplh3NzayQzCiHM4/hqw==",
"version": "5.1.3",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.1.3.tgz",
"integrity": "sha512-XH627E9vkeqhlZFQuL+UsyAXEnibT0kWR2FWONlr4sTjvxyJYnyefgrkyECLzM5NenmKzRAy2rR/OlYLA1HkZw==",
"peer": true,
"bin": {
"tsc": "bin/tsc",
"tsserver": "bin/tsserver"
},
"engines": {
"node": ">=12.20"
"node": ">=14.17"
}
},
"node_modules/unbox-primitive": {
@@ -3483,6 +3500,18 @@
"punycode": "^2.1.0"
}
},
"node_modules/watchpack": {
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.0.tgz",
"integrity": "sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg==",
"dependencies": {
"glob-to-regexp": "^0.4.1",
"graceful-fs": "^4.1.2"
},
"engines": {
"node": ">=10.13.0"
}
},
"node_modules/which": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",

View File

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

View File

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

View File

@@ -1,5 +1,12 @@
# @vercel-internals/constants
## 1.0.2
### Patch Changes
- Updated dependencies [[`cd35071f6`](https://github.com/vercel/vercel/commit/cd35071f609d615d47bc04634c123b33768436cb)]:
- @vercel/build-utils@6.7.5
## 1.0.1
### Patch Changes

View File

@@ -1,14 +1,14 @@
{
"private": true,
"name": "@vercel-internals/constants",
"version": "1.0.1",
"version": "1.0.2",
"types": "dist/index.d.ts",
"main": "dist/index.js",
"scripts": {
"build": "tsc -p tsconfig.json"
},
"dependencies": {
"@vercel/build-utils": "6.7.4",
"@vercel/build-utils": "6.7.5",
"@vercel/routing-utils": "2.2.1"
},
"devDependencies": {

View File

@@ -1,5 +1,13 @@
# @vercel-internals/types
## 1.0.2
### Patch Changes
- Updated dependencies [[`cd35071f6`](https://github.com/vercel/vercel/commit/cd35071f609d615d47bc04634c123b33768436cb)]:
- @vercel/build-utils@6.7.5
- @vercel-internals/constants@1.0.2
## 1.0.1
### Patch Changes

View File

@@ -378,28 +378,60 @@ export interface ProjectLink {
* to the root directory of the repository.
*/
repoRoot?: string;
/**
* When linked as a repository, contains the relative path
* to the selected project root directory.
*/
projectRootDirectory?: string;
}
export interface PaginationOptions {
prev: number;
/**
* Amount of items in the current page.
* @example 20
*/
count: number;
next?: number;
/**
* Timestamp that must be used to request the next page.
* @example 1540095775951
*/
next: number | null;
/**
* Timestamp that must be used to request the previous page.
* @example 1540095775951
*/
prev: number | null;
}
export type ProjectLinked = {
status: 'linked';
org: Org;
project: Project;
repoRoot?: string;
};
export type ProjectNotLinked = {
status: 'not_linked';
org: null;
project: null;
};
export type ProjectLinkedError = {
status: 'error';
exitCode: number;
reason?:
| 'HEADLESS'
| 'NOT_AUTHORIZED'
| 'TEAM_DELETED'
| 'PATH_IS_FILE'
| 'INVALID_ROOT_DIRECTORY'
| 'MISSING_PROJECT_SETTINGS';
};
export type ProjectLinkResult =
| { status: 'linked'; org: Org; project: Project; repoRoot?: string }
| { status: 'not_linked'; org: null; project: null }
| {
status: 'error';
exitCode: number;
reason?:
| 'HEADLESS'
| 'NOT_AUTHORIZED'
| 'TEAM_DELETED'
| 'PATH_IS_FILE'
| 'INVALID_ROOT_DIRECTORY'
| 'MISSING_PROJECT_SETTINGS';
};
| ProjectLinked
| ProjectNotLinked
| ProjectLinkedError;
/**
* @deprecated - `RollbackJobStatus` has been replace by `LastAliasRequest['jobStatus']`.

View File

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

View File

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

View File

@@ -1,5 +1,11 @@
# @vercel/build-utils
## 6.7.5
### Patch Changes
- Publish missing build-utils ([`cd35071f6`](https://github.com/vercel/vercel/commit/cd35071f609d615d47bc04634c123b33768436cb))
## 6.7.4
### Patch Changes

View File

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

View File

@@ -43,6 +43,7 @@ import { getPlatformEnv } from './get-platform-env';
import { getPrefixedEnvVars } from './get-prefixed-env-vars';
import { cloneEnv } from './clone-env';
import { hardLinkDir } from './hard-link-dir';
import { validateNpmrc } from './validate-npmrc';
export {
FileBlob,
@@ -88,6 +89,7 @@ export {
getIgnoreFilter,
cloneEnv,
hardLinkDir,
validateNpmrc,
};
export { EdgeFunction } from './edge-function';

View File

@@ -0,0 +1,26 @@
import { join } from 'path';
import { readFile } from 'fs/promises';
/**
* Checks if there is a `.npmrc` in the cwd (project root) and makes sure it
* doesn't contain a `use-node-version`. This config setting is not supported
* since it causes the package manager to install the Node.js version which in
* the case of newer Node.js versions is not compatible with AWS due to
* outdated GLIBC binaries.
*
* @see https://pnpm.io/npmrc#use-node-version
*
* @param cwd The current working directory (e.g. project root);
*/
export async function validateNpmrc(cwd: string): Promise<void> {
const npmrc = await readFile(join(cwd, '.npmrc'), 'utf-8').catch(err => {
if (err.code !== 'ENOENT') throw err;
});
const nodeRegExp = /(?<!#.*)use-node-version/;
if (npmrc?.match(nodeRegExp)) {
throw new Error(
'Detected unsupported "use-node-version" in your ".npmrc". Please use "engines" in your "package.json" instead.'
);
}
}

View File

@@ -0,0 +1,2 @@
foo=bar
# use-node-version=16.16.0

View File

@@ -0,0 +1 @@
foo=bar

View File

@@ -0,0 +1,3 @@
foo=bar
# the next line is not supported
use-node-version=16.16.0

View File

@@ -0,0 +1,22 @@
import { join } from 'path';
import { validateNpmrc } from '../src/validate-npmrc';
const fixture = (name: string) => join(__dirname, 'fixtures', '29-npmrc', name);
describe('validateNpmrc', () => {
it('should not error with no use-node-version', async () => {
await expect(validateNpmrc(fixture('good'))).resolves.toBe(undefined);
});
it('should throw when use-node-version is found', async () => {
await expect(
validateNpmrc(fixture('has-use-node-version'))
).rejects.toThrow('Detected unsupported');
});
it('should not error when use-node-version is commented out', async () => {
await expect(
validateNpmrc(fixture('comment-use-node-version'))
).resolves.toBe(undefined);
});
});

View File

@@ -1,5 +1,66 @@
# vercel
## 30.2.2
### Patch Changes
- [cli] vc env pull should add `.env*.local` to `.gitignore` ([#10085](https://github.com/vercel/vercel/pull/10085))
- [cli] Fix team validation bug where you are apart of a team ([#10092](https://github.com/vercel/vercel/pull/10092))
- Add support for `vc dev` command with repo link ([#10082](https://github.com/vercel/vercel/pull/10082))
- Add support for `vc deploy --prebuilt` command with repo link ([#10083](https://github.com/vercel/vercel/pull/10083))
- Move readme copy logic to a helper function for `vc link` ([#10084](https://github.com/vercel/vercel/pull/10084))
- Add support for `vc pull` command with repo link ([#10078](https://github.com/vercel/vercel/pull/10078))
- Add support for `vc build` command with repo link ([#10075](https://github.com/vercel/vercel/pull/10075))
## 30.2.1
### Patch Changes
- Updated dependencies [[`a04bf557f`](https://github.com/vercel/vercel/commit/a04bf557fc6e1080a117428977d0993dec78b004)]:
- @vercel/node@2.15.1
- @vercel/static-build@1.3.36
## 30.2.0
### Minor Changes
- [node] Add isomorphic functions ([#9947](https://github.com/vercel/vercel/pull/9947))
### Patch Changes
- Add `client.fetchPaginated()` helper function ([#10054](https://github.com/vercel/vercel/pull/10054))
- Updated dependencies [[`bc5afe24c`](https://github.com/vercel/vercel/commit/bc5afe24c4547dbf798b939199e8212c4b34038e), [`49c717856`](https://github.com/vercel/vercel/commit/49c7178567ec5bcebe633b598c8c9c0e1aa40fbb), [`0039c8b5c`](https://github.com/vercel/vercel/commit/0039c8b5cea975316a62c4f6aaca5d66d731cc0d)]:
- @vercel/node@2.15.0
- @vercel/remix-builder@1.8.13
- @vercel/static-build@1.3.35
## 30.1.2
### Patch Changes
- Publish missing build-utils ([`cd35071f6`](https://github.com/vercel/vercel/commit/cd35071f609d615d47bc04634c123b33768436cb))
- Updated dependencies [[`cd35071f6`](https://github.com/vercel/vercel/commit/cd35071f609d615d47bc04634c123b33768436cb)]:
- @vercel/build-utils@6.7.5
- @vercel/node@2.14.5
- @vercel/remix-builder@1.8.12
- @vercel/static-build@1.3.34
## 30.1.1
### Patch Changes
- [cli] vc build ignore '.env\*' & ignore files for '@vercel/static' ([#10056](https://github.com/vercel/vercel/pull/10056))
- [cli] Ensure .npmrc does not contain use-node-version ([#10049](https://github.com/vercel/vercel/pull/10049))
## 30.1.0
### Minor Changes

View File

@@ -1,6 +1,6 @@
{
"name": "vercel",
"version": "30.1.0",
"version": "30.2.2",
"preferGlobal": true,
"license": "Apache-2.0",
"description": "The command-line interface for Vercel",
@@ -32,16 +32,16 @@
"node": ">= 14"
},
"dependencies": {
"@vercel/build-utils": "6.7.4",
"@vercel/build-utils": "6.7.5",
"@vercel/go": "2.5.1",
"@vercel/hydrogen": "0.0.64",
"@vercel/next": "3.8.6",
"@vercel/node": "2.14.4",
"@vercel/node": "2.15.1",
"@vercel/python": "3.1.60",
"@vercel/redwood": "1.1.15",
"@vercel/remix-builder": "1.8.11",
"@vercel/remix-builder": "1.8.13",
"@vercel/ruby": "1.3.76",
"@vercel/static-build": "1.3.33"
"@vercel/static-build": "1.3.36"
},
"devDependencies": {
"@alex_neo/jest-expect-message": "1.0.5",
@@ -85,13 +85,13 @@
"@types/which": "3.0.0",
"@types/write-json-file": "2.2.1",
"@types/yauzl-promise": "2.1.0",
"@vercel-internals/constants": "1.0.1",
"@vercel-internals/constants": "1.0.2",
"@vercel-internals/get-package-json": "1.0.0",
"@vercel-internals/types": "1.0.1",
"@vercel/client": "12.6.1",
"@vercel-internals/types": "1.0.2",
"@vercel/client": "12.6.2",
"@vercel/error-utils": "1.0.10",
"@vercel/frameworks": "1.4.2",
"@vercel/fs-detectors": "3.9.3",
"@vercel/fs-detectors": "4.0.0",
"@vercel/fun": "1.0.4",
"@vercel/ncc": "0.24.0",
"@vercel/routing-utils": "2.2.1",

View File

@@ -6,21 +6,19 @@ import chalk, { Chalk } from 'chalk';
import { URLSearchParams, parse } from 'url';
import box from '../../util/output/box';
import sleep from '../../util/sleep';
import formatDate from '../../util/format-date';
import link from '../../util/output/link';
import logo from '../../util/output/logo';
import getArgs from '../../util/get-args';
import Client from '../../util/client';
import { getPkgName } from '../../util/pkg-name';
import { Deployment, PaginationOptions } from '@vercel-internals/types';
import { Deployment } from '@vercel-internals/types';
import { normalizeURL } from '../../util/bisect/normalize-url';
import getScope from '../../util/get-scope';
import getDeployment from '../../util/get-deployment';
interface Deployments {
deployments: Deployment[];
pagination: PaginationOptions;
}
const pkgName = getPkgName();
@@ -206,44 +204,34 @@ export default async function main(client: Client): Promise<number> {
// Fetch all the project's "READY" deployments with the pagination API
let deployments: Deployment[] = [];
let next: number | undefined = badDeployment.createdAt + 1;
do {
const query = new URLSearchParams();
query.set('projectId', projectId);
if (badDeployment.target) {
query.set('target', badDeployment.target);
}
query.set('limit', '100');
query.set('state', 'READY');
if (next) {
query.set('until', String(next));
}
const res = await client.fetch<Deployments>(`/v6/deployments?${query}`, {
const query = new URLSearchParams();
query.set('projectId', projectId);
if (badDeployment.target) {
query.set('target', badDeployment.target);
}
query.set('state', 'READY');
query.set('until', String(badDeployment.createdAt + 1));
for await (const chunk of client.fetchPaginated<Deployments>(
`/v6/deployments?${query}`,
{
accountId: badDeployment.ownerId,
});
next = res.pagination.next;
let newDeployments = res.deployments;
}
)) {
let newDeployments = chunk.deployments;
// If we have the "good" deployment in this chunk, then we're done
for (let i = 0; i < newDeployments.length; i++) {
if (newDeployments[i].url === good) {
// grab all deployments up until the good one
newDeployments = newDeployments.slice(0, i);
next = undefined;
break;
}
}
deployments = deployments.concat(newDeployments);
if (next) {
// Small sleep to avoid rate limiting
await sleep(100);
}
} while (next);
}
if (!deployments.length) {
output.error(

View File

@@ -1,7 +1,10 @@
import fs from 'fs-extra';
import chalk from 'chalk';
import dotenv from 'dotenv';
import semver from 'semver';
import minimatch from 'minimatch';
import { join, normalize, relative, resolve, sep } from 'path';
import frameworks from '@vercel/frameworks';
import {
getDiscontinuedNodeVersions,
normalizePath,
@@ -17,13 +20,14 @@ import {
BuildResultV3,
NowBuildError,
Cron,
validateNpmrc,
} from '@vercel/build-utils';
import {
detectBuilders,
detectFrameworkRecord,
detectFrameworkVersion,
LocalFileSystemDetector,
} from '@vercel/fs-detectors';
import minimatch from 'minimatch';
import {
appendRoutesToPhase,
getTransformedRoutes,
@@ -48,7 +52,7 @@ import {
ProjectLinkAndSettings,
readProjectSettings,
} from '../util/projects/project-settings';
import { VERCEL_DIR } from '../util/projects/link';
import { getProjectLink, VERCEL_DIR } from '../util/projects/link';
import confirm from '../util/input/confirm';
import { emoji, prependEmoji } from '../util/emoji';
import stamp from '../util/output/stamp';
@@ -62,11 +66,7 @@ import { initCorepack, cleanupCorepack } from '../util/build/corepack';
import { sortBuilders } from '../util/build/sort-builders';
import { toEnumerableError } from '../util/error';
import { validateConfig } from '../util/validate-config';
import { setMonorepoDefaultSettings } from '../util/build/monorepo';
import frameworks from '@vercel/frameworks';
import { detectFrameworkVersion } from '@vercel/fs-detectors';
import semver from 'semver';
type BuildResult = BuildResultV2 | BuildResultV3;
@@ -133,7 +133,8 @@ const help = () => {
};
export default async function main(client: Client): Promise<number> {
const { cwd, output } = client;
let { cwd } = client;
const { output } = client;
// Ensure that `vc build` is not being invoked recursively
if (process.env.__VERCEL_BUILD_RUNNING) {
@@ -169,10 +170,25 @@ export default async function main(client: Client): Promise<number> {
const target = argv['--prod'] ? 'production' : 'preview';
const yes = Boolean(argv['--yes']);
try {
await validateNpmrc(cwd);
} catch (err) {
output.prettyError(err);
return 1;
}
// If repo linked, update `cwd` to the repo root
const link = await getProjectLink(client, cwd);
const projectRootDirectory = link?.projectRootDirectory ?? '';
if (link?.repoRoot) {
cwd = client.cwd = link.repoRoot;
}
// TODO: read project settings from the API, fall back to local `project.json` if that fails
// Read project settings, and pull them from Vercel if necessary
let project = await readProjectSettings(join(cwd, VERCEL_DIR));
const vercelDir = join(cwd, projectRootDirectory, VERCEL_DIR);
let project = await readProjectSettings(vercelDir);
const isTTY = process.stdin.isTTY;
while (!project?.settings) {
let confirmed = yes;
@@ -199,6 +215,7 @@ export default async function main(client: Client): Promise<number> {
return 0;
}
const { argv: originalArgv } = client;
client.cwd = join(cwd, projectRootDirectory);
client.argv = [
...originalArgv.slice(0, 2),
'pull',
@@ -209,12 +226,13 @@ export default async function main(client: Client): Promise<number> {
if (result !== 0) {
return result;
}
client.cwd = cwd;
client.argv = originalArgv;
project = await readProjectSettings(join(cwd, VERCEL_DIR));
project = await readProjectSettings(vercelDir);
}
// Delete output directory from potential previous build
const defaultOutputDir = join(cwd, OUTPUT_DIR);
const defaultOutputDir = join(cwd, projectRootDirectory, OUTPUT_DIR);
const outputDir = argv['--output']
? resolve(argv['--output'])
: defaultOutputDir;
@@ -233,7 +251,12 @@ export default async function main(client: Client): Promise<number> {
const envToUnset = new Set<string>(['VERCEL', 'NOW_BUILDER']);
try {
const envPath = join(cwd, VERCEL_DIR, `.env.${target}.local`);
const envPath = join(
cwd,
projectRootDirectory,
VERCEL_DIR,
`.env.${target}.local`
);
// TODO (maybe?): load env vars from the API, fall back to the local file if that fails
const dotenvResult = dotenv.config({
path: envPath,

View File

@@ -203,56 +203,6 @@ export default async (client: Client): Promise<number> => {
return target;
}
// build `--prebuilt`
if (argv['--prebuilt']) {
const prebuiltExists = await fs.pathExists(join(cwd, '.vercel/output'));
if (!prebuiltExists) {
error(
`The ${param(
'--prebuilt'
)} option was used, but no prebuilt output found in ".vercel/output". Run ${getCommandName(
'build'
)} to generate a local build.`
);
return 1;
}
const prebuiltBuild = await getPrebuiltJson(cwd);
// Ensure that there was not a build error
const prebuiltError =
prebuiltBuild?.error ||
prebuiltBuild?.builds?.find(build => 'error' in build)?.error;
if (prebuiltError) {
output.log(
`Prebuilt deployment cannot be created because ${getCommandName(
'build'
)} failed with error:\n`
);
prettyError(prebuiltError);
return 1;
}
// Ensure that the deploy target matches the build target
const assumedTarget = target || 'preview';
if (prebuiltBuild?.target && prebuiltBuild.target !== assumedTarget) {
let specifyTarget = '';
if (prebuiltBuild.target === 'production') {
specifyTarget = ` --prod`;
}
prettyError({
message: `The ${param(
'--prebuilt'
)} option was used with the target environment "${assumedTarget}", but the prebuilt output found in ".vercel/output" was built with target environment "${
prebuiltBuild.target
}". Please run ${getCommandName(`--prebuilt${specifyTarget}`)}.`,
link: 'https://vercel.link/prebuilt-environment-mismatch',
});
return 1;
}
}
const archive = argv['--archive'];
if (typeof archive === 'string' && !isValidArchive(archive)) {
output.error(`Format must be one of: ${VALID_ARCHIVE_FORMATS.join(', ')}`);
@@ -355,6 +305,66 @@ export default async (client: Client): Promise<number> => {
throw new Error(`"org" is not defined`);
}
// build `--prebuilt`
if (argv['--prebuilt']) {
// For repo-style linking, update `cwd` to be the Project
// subdirectory when `rootDirectory` setting is defined
if (
link.status === 'linked' &&
link.repoRoot &&
link.project.rootDirectory
) {
cwd = join(cwd, link.project.rootDirectory);
}
const prebuiltExists = await fs.pathExists(join(cwd, '.vercel/output'));
if (!prebuiltExists) {
error(
`The ${param(
'--prebuilt'
)} option was used, but no prebuilt output found in ".vercel/output". Run ${getCommandName(
'build'
)} to generate a local build.`
);
return 1;
}
const prebuiltBuild = await getPrebuiltJson(cwd);
// Ensure that there was not a build error
const prebuiltError =
prebuiltBuild?.error ||
prebuiltBuild?.builds?.find(build => 'error' in build)?.error;
if (prebuiltError) {
output.log(
`Prebuilt deployment cannot be created because ${getCommandName(
'build'
)} failed with error:\n`
);
prettyError(prebuiltError);
return 1;
}
// Ensure that the deploy target matches the build target
const assumedTarget = target || 'preview';
if (prebuiltBuild?.target && prebuiltBuild.target !== assumedTarget) {
let specifyTarget = '';
if (prebuiltBuild.target === 'production') {
specifyTarget = ` --prod`;
}
prettyError({
message: `The ${param(
'--prebuilt'
)} option was used with the target environment "${assumedTarget}", but the prebuilt output found in ".vercel/output" was built with target environment "${
prebuiltBuild.target
}". Please run ${getCommandName(`--prebuilt${specifyTarget}`)}.`,
link: 'https://vercel.link/prebuilt-environment-mismatch',
});
return 1;
}
}
// Set the `contextName` and `currentTeam` as specified by the
// Project Settings, so that API calls happen with the proper scope
const contextName = org.slug;

View File

@@ -58,7 +58,13 @@ export default async function dev(
let projectSettings: ProjectSettings | undefined;
let envValues: Record<string, string> = {};
if (link.status === 'linked') {
const { project, org } = link;
const { project, org, repoRoot } = link;
// If repo linked, update `cwd` to the repo root
if (repoRoot) {
cwd = repoRoot;
}
client.config.currentTeam = org.type === 'team' ? org.id : undefined;
projectSettings = project;

View File

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

View File

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

View File

@@ -1,7 +1,11 @@
import chalk from 'chalk';
import { join } from 'path';
import Client from '../util/client';
import type { Project, ProjectEnvTarget } from '@vercel-internals/types';
import type {
Project,
ProjectEnvTarget,
ProjectLinked,
} from '@vercel-internals/types';
import { emoji, prependEmoji } from '../util/emoji';
import getArgs from '../util/get-args';
import logo from '../util/output/logo';
@@ -15,6 +19,7 @@ import {
getEnvTargetPlaceholder,
} from '../util/env/env-target';
import { ensureLink } from '../util/link/ensure-link';
import humanizePath from '../util/humanize-path';
const help = () => {
return console.log(`
@@ -81,6 +86,7 @@ function parseArgs(client: Client) {
async function pullAllEnvFiles(
environment: ProjectEnvTarget,
client: Client,
link: ProjectLinked,
project: Project,
argv: ReturnType<typeof processArgs>,
cwd: string
@@ -88,6 +94,7 @@ async function pullAllEnvFiles(
const environmentFile = `.env.${environment}.local`;
return envPull(
client,
link,
project,
environment,
argv,
@@ -115,7 +122,7 @@ export default async function main(client: Client) {
return argv;
}
const cwd = argv._[1] || process.cwd();
let cwd = argv._[1] || client.cwd;
const autoConfirm = Boolean(argv['--yes']);
const environment = parseEnvironment(argv['--environment'] || undefined);
@@ -124,13 +131,18 @@ export default async function main(client: Client) {
return link;
}
const { project, org } = link;
const { project, org, repoRoot } = link;
if (repoRoot) {
cwd = join(repoRoot, project.rootDirectory || '');
}
client.config.currentTeam = org.type === 'team' ? org.id : undefined;
const pullResultCode = await pullAllEnvFiles(
environment,
client,
link,
project,
argv,
cwd
@@ -141,13 +153,14 @@ export default async function main(client: Client) {
client.output.print('\n');
client.output.log('Downloading project settings');
await writeProjectSettings(cwd, project, org);
const isRepoLinked = typeof repoRoot === 'string';
await writeProjectSettings(cwd, project, org, isRepoLinked);
const settingsStamp = stamp();
client.output.print(
`${prependEmoji(
`Downloaded project settings to ${chalk.bold(
join(VERCEL_DIR, VERCEL_DIR_PROJECT)
humanizePath(join(cwd, VERCEL_DIR, VERCEL_DIR_PROJECT))
)} ${chalk.gray(settingsStamp())}`,
emoji('success')
)}\n`

View File

@@ -12,7 +12,10 @@ export const build: BuildV2 = async ({ entrypoint, files, config }) => {
if (
filename.startsWith('.git/') ||
filename === 'vercel.json' ||
filename === 'now.json'
filename === '.vercelignore' ||
filename === 'now.json' ||
filename === '.nowignore' ||
filename.startsWith('.env')
) {
continue;
}

View File

@@ -19,11 +19,13 @@ import type {
Stdio,
ReadableTTY,
WritableTTY,
PaginationOptions,
} from '@vercel-internals/types';
import { sharedPromise } from './promise';
import { APIError } from './errors-ts';
import { normalizeError } from '@vercel/error-utils';
import type { Agent } from 'http';
import sleep from './sleep';
const isSAMLError = (v: any): v is SAMLError => {
return v && v.saml;
@@ -176,6 +178,31 @@ export default class Client extends EventEmitter implements Stdio {
}, opts.retry);
}
async *fetchPaginated<T>(
url: string | URL,
opts?: FetchOptions
): AsyncGenerator<T & { pagination: PaginationOptions }> {
const endpoint =
typeof url === 'string' ? new URL(url, this.apiUrl) : new URL(url.href);
if (!endpoint.searchParams.has('limit')) {
endpoint.searchParams.set('limit', '100');
}
let next: number | null | undefined;
do {
if (next) {
// Small sleep to avoid rate limiting
await sleep(100);
endpoint.searchParams.set('until', String(next));
}
const res = await this.fetch<T & { pagination: PaginationOptions }>(
endpoint.href,
opts
);
yield res;
next = res.pagination?.next;
} while (next);
}
reauthenticate = sharedPromise(async function (
this: Client,
error: SAMLError

View File

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

View File

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

View File

@@ -3,14 +3,10 @@ import pluralize from 'pluralize';
import { homedir } from 'os';
import { join, normalize } from 'path';
import { normalizePath } from '@vercel/build-utils';
import { lstat, readJSON, outputJSON, writeFile, readFile } from 'fs-extra';
import { lstat, readJSON, outputJSON } from 'fs-extra';
import confirm from '../input/confirm';
import toHumanPath from '../humanize-path';
import {
VERCEL_DIR,
VERCEL_DIR_README,
VERCEL_DIR_REPO,
} from '../projects/link';
import { VERCEL_DIR, VERCEL_DIR_REPO, writeReadme } from '../projects/link';
import { getRemoteUrls } from '../create-git-meta';
import link from '../output/link';
import { emoji, prependEmoji } from '../emoji';
@@ -128,9 +124,30 @@ export async function ensureRepoLink(
output.spinner(
`Fetching Projects for ${link(repoUrl)} under ${chalk.bold(org.slug)}`
);
// TODO: Add pagination to fetch all Projects
const query = new URLSearchParams({ repoUrl, limit: '100' });
const projects: Project[] = await client.fetch(`/v2/projects?${query}`);
let projects: Project[] = [];
const query = new URLSearchParams({ repoUrl });
const projectsIterator = client.fetchPaginated<{
projects: Project[];
}>(`/v9/projects?${query}`);
let printedFound = false;
for await (const chunk of projectsIterator) {
projects = projects.concat(chunk.projects);
if (!printedFound && projects.length > 0) {
output.log(
`${pluralize('Project', chunk.projects.length)} linked to ${link(
repoUrl
)} under ${chalk.bold(org.slug)}:`
);
printedFound = true;
}
for (const project of chunk.projects) {
output.print(` * ${chalk.cyan(`${org.slug}/${project.name}\n`)}`);
}
if (chunk.pagination.next) {
output.spinner(`Found ${chalk.bold(projects.length)} Projects…`, 0);
}
}
if (projects.length === 0) {
output.log(
`No Projects are linked to ${link(repoUrl)} under ${chalk.bold(
@@ -140,24 +157,17 @@ export async function ensureRepoLink(
// TODO: run detection logic to find potential projects.
// then prompt user to select valid projects.
// then create new Projects
} else {
output.log(
`Found ${chalk.bold(projects.length)} ${pluralize(
'Project',
projects.length
)} linked to ${link(repoUrl)} under ${chalk.bold(org.slug)}:`
);
}
for (const project of projects) {
output.print(` * ${chalk.cyan(`${org.slug}/${project.name}\n`)}`);
}
shouldLink =
yes ||
(await confirm(
client,
`Link to ${projects.length === 1 ? 'it' : 'them'}?`,
`Link to ${
projects.length === 1
? 'this Project'
: `these ${chalk.bold(projects.length)} Projects`
}?`,
true
));
@@ -179,10 +189,7 @@ export async function ensureRepoLink(
};
await outputJSON(repoConfigPath, repoConfig, { spaces: 2 });
await writeFile(
join(rootPath, VERCEL_DIR, VERCEL_DIR_README),
await readFile(join(__dirname, 'VERCEL_DIR_README.txt'), 'utf8')
);
await writeReadme(rootPath);
// update .gitignore
const isGitIgnoreUpdated = await addToGitIgnore(rootPath);
@@ -268,7 +275,7 @@ export function findProjectsFromPath(
path: string
): RepoProjectConfig[] {
const normalizedPath = normalizePath(path);
return projects
const matches = projects
.slice()
.sort(sortByDirectory)
.filter(project => {
@@ -281,14 +288,9 @@ export function findProjectsFromPath(
normalizedPath.startsWith(`${project.directory}/`)
);
});
}
/**
* TODO: remove
*/
export function findProjectFromPath(
projects: RepoProjectConfig[],
path: string
): RepoProjectConfig | undefined {
return findProjectsFromPath(projects, path)[0];
// If there are multiple matches, we only want the most relevant
// selections (with the deepest directory depth), so pick the first
// one and filter on those matches.
const firstMatch = matches[0];
return matches.filter(match => match.directory === firstMatch.directory);
}

View File

@@ -74,9 +74,7 @@ export default async function getProjectByDeployment({
err.code = 'ERR_INVALID_TEAM';
throw err;
}
}
if (team) {
} else if (team) {
const err: NodeJS.ErrnoException = new Error(
`Deployment doesn't belong to current team ${chalk.bold(contextName)}`
);

View File

@@ -69,7 +69,7 @@ export function getVercelDirectory(cwd: string): string {
return existingDirs[0] || possibleDirs[0];
}
async function getProjectLink(
export async function getProjectLink(
client: Client,
path: string
): Promise<ProjectLink | null> {
@@ -108,9 +108,10 @@ async function getProjectLinkFromRepoLink(
}
if (project) {
return {
repoRoot: repoLink.rootPath,
orgId: repoLink.repoConfig.orgId,
projectId: project.id,
repoRoot: repoLink.rootPath,
projectRootDirectory: project.directory,
};
}
return null;
@@ -284,6 +285,13 @@ export async function getLinkedProject(
return { status: 'linked', org, project, repoRoot: link.repoRoot };
}
export async function writeReadme(path: string) {
await writeFile(
join(path, VERCEL_DIR, VERCEL_DIR_README),
await readFile(join(__dirname, 'VERCEL_DIR_README.txt'), 'utf8')
);
}
export async function linkFolderToProject(
client: Client,
path: string,
@@ -313,10 +321,7 @@ export async function linkFolderToProject(
JSON.stringify(projectLink)
);
await writeFile(
join(path, VERCEL_DIR, VERCEL_DIR_README),
await readFile(join(__dirname, 'VERCEL_DIR_README.txt'), 'utf8')
);
await writeReadme(path);
// update .gitignore
const isGitIgnoreUpdated = await addToGitIgnore(path);

View File

@@ -1,11 +1,12 @@
import { outputJSON } from 'fs-extra';
import { Org, Project, ProjectLink } from '@vercel-internals/types';
import { getLinkFromDir, VERCEL_DIR, VERCEL_DIR_PROJECT } from './link';
import { join } from 'path';
import { outputJSON, readFile } from 'fs-extra';
import { VercelConfig } from '@vercel/client';
import { VERCEL_DIR, VERCEL_DIR_PROJECT } from './link';
import { PartialProjectSettings } from '../input/edit-project-settings';
import type { Org, Project, ProjectLink } from '@vercel-internals/types';
import { isErrnoException, isError } from '@vercel/error-utils';
export type ProjectLinkAndSettings = ProjectLink & {
export type ProjectLinkAndSettings = Partial<ProjectLink> & {
settings: {
createdAt: Project['createdAt'];
installCommand: Project['installCommand'];
@@ -26,7 +27,8 @@ export type ProjectLinkAndSettings = ProjectLink & {
export async function writeProjectSettings(
cwd: string,
project: Project,
org: Org
org: Org,
isRepoLinked: boolean
) {
let analyticsId: string | undefined;
if (
@@ -39,8 +41,8 @@ export async function writeProjectSettings(
}
const projectLinkAndSettings: ProjectLinkAndSettings = {
projectId: project.id,
orgId: org.id,
projectId: isRepoLinked ? undefined : project.id,
orgId: isRepoLinked ? undefined : org.id,
settings: {
createdAt: project.createdAt,
framework: project.framework,
@@ -60,8 +62,28 @@ export async function writeProjectSettings(
});
}
export async function readProjectSettings(cwd: string) {
return await getLinkFromDir<ProjectLinkAndSettings>(cwd);
export async function readProjectSettings(vercelDir: string) {
try {
return JSON.parse(
await readFile(join(vercelDir, VERCEL_DIR_PROJECT), 'utf8')
);
} catch (err: unknown) {
// `project.json` file does not exists, so project settings have not been pulled
if (
isErrnoException(err) &&
err.code &&
['ENOENT', 'ENOTDIR'].includes(err.code)
) {
return null;
}
// failed to parse JSON, treat the same as if project settings have not been pulled
if (isError(err) && err.name === 'SyntaxError') {
return null;
}
throw err;
}
}
export function pickOverrides(

View File

@@ -322,14 +322,17 @@ test('[vercel dev] should handle missing handler errors thrown in edge functions
);
validateResponseHeaders(res);
const { stderr } = await dev.kill();
const { stdout } = await dev.kill();
expect(await res.text()).toMatch(
/<strong>500<\/strong>: INTERNAL_SERVER_ERROR/g
);
expect(stderr).toMatch(
/No default export was found. Add a default export to handle requests./g
);
const url = `http://localhost:${port}/api/edge-error-no-handler`;
expect(stdout).toMatchInlineSnapshot(`
"Error from API Route /api/edge-error-no-handler: No default or HTTP-named export was found at ${url}. Add one to handle requests. Learn more: https://vercel.link/creating-edge-middleware
at (api/edge-error-no-handler.js)
"
`);
} finally {
await dev.kill();
}

View File

@@ -0,0 +1,4 @@
{
"orgId": "team_dummy",
"projectId": "build-output-api-failed-before-builds"
}

View File

@@ -0,0 +1,4 @@
{
"orgId": "team_dummy",
"projectId": "build-output-api-failed-within-build"
}

View File

@@ -0,0 +1,4 @@
{
"orgId": "team_dummy",
"projectId": "build-output-api-preview"
}

View File

@@ -0,0 +1,4 @@
{
"orgId": "team_dummy",
"projectId": "build-output-api-production"
}

View File

@@ -0,0 +1,3 @@
foo=bar
# the next line is not supported
use-node-version=16.16.0

View File

@@ -0,0 +1 @@
FOO=bar

View File

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

View File

@@ -0,0 +1,5 @@
<html>
<body>
Hello world!
</body>
</html>

View File

@@ -1 +1,3 @@
!.vercel
dist
!/.vercel
.vercel/output

View File

@@ -0,0 +1,6 @@
{
"name": "blog",
"scripts": {
"build": "mkdir -p dist && echo blog > dist/index.txt"
}
}

View File

@@ -0,0 +1,6 @@
{
"name": "dashboard",
"scripts": {
"build": "mkdir -p dist && echo dashboard > dist/index.txt"
}
}

View File

@@ -0,0 +1,6 @@
{
"name": "marketing",
"scripts": {
"build": "mkdir -p dist && echo marketing > dist/index.txt"
}
}

View File

@@ -0,0 +1,4 @@
.next
yarn.lock
!.vercel
.env*.local

View File

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

View File

@@ -47,7 +47,7 @@ export function setupTmpDir(fixtureName?: string) {
const cwd = path.join(tempRoot.name, String(tempNumber++), fixtureName ?? '');
fs.mkdirpSync(cwd);
return cwd;
return fs.realpathSync(cwd);
}
export function cleanupFixtures() {

View File

@@ -1154,4 +1154,97 @@ describe('build', () => {
delete process.env.STORYBOOK_DISABLE_TELEMETRY;
}
});
it('should error if .npmrc exists containing use-node-version', async () => {
const cwd = fixture('npmrc-use-node-version');
client.cwd = cwd;
client.setArgv('build');
const exitCodePromise = build(client);
await expect(client.stderr).toOutput('Error: Detected unsupported');
await expect(exitCodePromise).resolves.toEqual(1);
});
it('should ignore `.env` for static site', async () => {
const cwd = fixture('static-env');
const output = join(cwd, '.vercel/output');
client.cwd = cwd;
const exitCode = await build(client);
expect(exitCode).toEqual(0);
expect(fs.existsSync(join(output, 'static', 'index.html'))).toBe(true);
expect(fs.existsSync(join(output, 'static', '.env'))).toBe(false);
});
it('should build with `repo.json` link', async () => {
const cwd = fixture('../../monorepo-link');
useUser();
useTeams('team_dummy');
// "blog" app
useProject({
...defaultProject,
id: 'QmScb7GPQt6gsS',
name: 'monorepo-blog',
rootDirectory: 'blog',
outputDirectory: 'dist',
framework: null,
});
let output = join(cwd, 'blog/.vercel/output');
client.cwd = join(cwd, 'blog');
client.setArgv('build', '--yes');
let exitCode = await build(client);
expect(exitCode).toEqual(0);
delete process.env.__VERCEL_BUILD_RUNNING;
let files = await fs.readdir(join(output, 'static'));
expect(files.sort()).toEqual(['index.txt']);
expect(
(await fs.readFile(join(output, 'static/index.txt'), 'utf8')).trim()
).toEqual('blog');
// "dashboard" app
useProject({
...defaultProject,
id: 'QmbKpqpiUqbcke',
name: 'monorepo-dashboard',
rootDirectory: 'dashboard',
outputDirectory: 'dist',
framework: null,
});
output = join(cwd, 'dashboard/.vercel/output');
client.cwd = join(cwd, 'dashboard');
client.setArgv('build', '--yes');
exitCode = await build(client);
expect(exitCode).toEqual(0);
delete process.env.__VERCEL_BUILD_RUNNING;
files = await fs.readdir(join(output, 'static'));
expect(files.sort()).toEqual(['index.txt']);
expect(
(await fs.readFile(join(output, 'static/index.txt'), 'utf8')).trim()
).toEqual('dashboard');
// "marketing" app
useProject({
...defaultProject,
id: 'QmX6P93ChNDoZP',
name: 'monorepo-marketing',
rootDirectory: 'marketing',
outputDirectory: 'dist',
framework: null,
});
output = join(cwd, 'marketing/.vercel/output');
client.cwd = join(cwd, 'marketing');
client.setArgv('build', '--yes');
exitCode = await build(client);
expect(exitCode).toEqual(0);
delete process.env.__VERCEL_BUILD_RUNNING;
files = await fs.readdir(join(output, 'static'));
expect(files.sort()).toEqual(['index.txt']);
expect(
(await fs.readFile(join(output, 'static/index.txt'), 'utf8')).trim()
).toEqual('marketing');
});
});

View File

@@ -45,6 +45,14 @@ describe('deploy', () => {
it('should reject deploying when `--prebuilt` is used and `vc build` failed before Builders', async () => {
const cwd = setupUnitFixture('build-output-api-failed-before-builds');
useUser();
useTeams('team_dummy');
useProject({
...defaultProject,
id: 'build-output-api-failed-before-builds',
name: 'build-output-api-failed-before-builds',
});
client.setArgv('deploy', cwd, '--prebuilt');
const exitCodePromise = deploy(client);
await expect(client.stderr).toOutput(
@@ -56,6 +64,14 @@ describe('deploy', () => {
it('should reject deploying when `--prebuilt` is used and `vc build` failed within a Builder', async () => {
const cwd = setupUnitFixture('build-output-api-failed-within-build');
useUser();
useTeams('team_dummy');
useProject({
...defaultProject,
id: 'build-output-api-failed-within-build',
name: 'build-output-api-failed-within-build',
});
client.setArgv('deploy', cwd, '--prebuilt');
const exitCodePromise = deploy(client);
await expect(client.stderr).toOutput(
@@ -65,7 +81,16 @@ describe('deploy', () => {
});
it('should reject deploying a directory that does not contain ".vercel/output" when `--prebuilt` is used', async () => {
client.setArgv('deploy', __dirname, '--prebuilt');
useUser();
useTeams('team_dummy');
useProject({
...defaultProject,
name: 'static',
id: 'static',
});
client.cwd = setupUnitFixture('commands/deploy/static');
client.setArgv('deploy', '--prebuilt');
const exitCodePromise = deploy(client);
await expect(client.stderr).toOutput(
'Error: The "--prebuilt" option was used, but no prebuilt output found in ".vercel/output". Run `vercel build` to generate a local build.\n'
@@ -102,8 +127,8 @@ describe('deploy', () => {
useTeams('team_dummy');
useProject({
...defaultProject,
id: 'build-output-api-preview',
name: 'build-output-api-preview',
id: 'build-output-api-production',
name: 'build-output-api-production',
});
client.setArgv('deploy', cwd, '--prebuilt');

View File

@@ -25,7 +25,9 @@ describe('env', () => {
await expect(client.stderr).toOutput(
'Downloading `development` Environment Variables for Project vercel-env-pull'
);
await expect(client.stderr).toOutput('Created .env.local file');
await expect(client.stderr).toOutput(
'Created .env.local file and added it to .gitignore'
);
await expect(exitCodePromise).resolves.toEqual(0);
const rawDevEnv = await fs.readFile(path.join(cwd, '.env.local'));
@@ -50,7 +52,9 @@ describe('env', () => {
await expect(client.stderr).toOutput(
'Downloading `preview` Environment Variables for Project vercel-env-pull'
);
await expect(client.stderr).toOutput('Created .env.local file');
await expect(client.stderr).toOutput(
'Created .env.local file and added it to .gitignore'
);
await expect(exitCodePromise).resolves.toEqual(0);
// check for Preview env vars
@@ -86,7 +90,9 @@ describe('env', () => {
await expect(client.stderr).toOutput(
'Downloading `preview` Environment Variables for Project vercel-env-pull'
);
await expect(client.stderr).toOutput('Created .env.local file');
await expect(client.stderr).toOutput(
'Created .env.local file and added it to .gitignore'
);
await expect(exitCodePromise).resolves.toEqual(0);
// check for Preview env vars
@@ -122,6 +128,7 @@ describe('env', () => {
'Downloading `development` Environment Variables for Project vercel-env-pull'
);
await expect(client.stderr).toOutput('Created other.env file');
await expect(client.stderr).not.toOutput('and added it to .gitignore');
await expect(exitCodePromise).resolves.toEqual(0);
const rawDevEnv = await fs.readFile(path.join(cwd, 'other.env'));
@@ -146,7 +153,9 @@ describe('env', () => {
await expect(client.stderr).toOutput(
`Downloading \`production\` Environment Variables for Project vercel-env-pull`
);
await expect(client.stderr).toOutput('Created .env.local file');
await expect(client.stderr).toOutput(
'Created .env.local file and added it to .gitignore'
);
await expect(exitCodePromise).resolves.toEqual(0);
const rawProdEnv = await fs.readFile(path.join(cwd, '.env.local'));
@@ -201,6 +210,7 @@ describe('env', () => {
'Downloading `development` Environment Variables for Project vercel-env-pull'
);
await expect(client.stderr).toOutput('Created other.env file');
await expect(client.stderr).not.toOutput('and added it to .gitignore');
await expect(exitCodePromise).resolves.toEqual(0);
const rawDevEnv = await fs.readFile(path.join(cwd, 'other.env'));
@@ -247,7 +257,9 @@ describe('env', () => {
await expect(client.stderr).toOutput(
'+ SPECIAL_FLAG (Updated)\n+ NEW_VAR\n- TEST\n'
);
await expect(client.stderr).toOutput('Updated .env.local file');
await expect(client.stderr).toOutput(
'Updated .env.local file and added it to .gitignore'
);
await expect(pullPromise).resolves.toEqual(0);
} finally {
@@ -268,7 +280,9 @@ describe('env', () => {
client.cwd = cwd;
client.setArgv('env', 'pull', '--yes');
const pullPromise = env(client);
await expect(client.stderr).toOutput('Updated .env.local file');
await expect(client.stderr).toOutput(
'Updated .env.local file and added it to .gitignore'
);
await expect(pullPromise).resolves.toEqual(0);
});
@@ -284,7 +298,9 @@ describe('env', () => {
client.setArgv('env', 'pull', '--yes');
const pullPromise = env(client);
await expect(client.stderr).toOutput('> No changes found.');
await expect(client.stderr).toOutput('Updated .env.local file');
await expect(client.stderr).toOutput(
'Updated .env.local file and added it to .gitignore'
);
await expect(pullPromise).resolves.toEqual(0);
});
@@ -321,7 +337,9 @@ describe('env', () => {
'Downloading `development` Environment Variables for Project env-pull-delta'
);
await expect(client.stderr).toOutput('No changes found.\n');
await expect(client.stderr).toOutput('Updated .env.local file');
await expect(client.stderr).toOutput(
'Updated .env.local file and added it to .gitignore'
);
await expect(pullPromise).resolves.toEqual(0);
} finally {
@@ -371,5 +389,42 @@ describe('env', () => {
await env(client);
}
});
it('should not update .gitignore if it contains a match', async () => {
const prj = 'vercel-env-pull-with-gitignore';
useUser();
useTeams('team_dummy');
useProject({
...defaultProject,
id: prj,
name: prj,
});
const cwd = setupUnitFixture(prj);
const gitignoreBefore = await fs.readFile(
path.join(cwd, '.gitignore'),
'utf8'
);
client.cwd = cwd;
client.setArgv('env', 'pull', '--yes');
const exitCodePromise = env(client);
await expect(client.stderr).toOutput(
'Downloading `development` Environment Variables for Project ' + prj
);
await expect(client.stderr).toOutput('Created .env.local file');
await expect(client.stderr).not.toOutput('and added it to .gitignore');
await expect(exitCodePromise).resolves.toEqual(0);
const rawDevEnv = await fs.readFile(path.join(cwd, '.env.local'));
// check for development env value
const devFileHasDevEnv = rawDevEnv.toString().includes('SPECIAL_FLAG');
expect(devFileHasDevEnv).toBeTruthy();
const gitignoreAfter = await fs.readFile(
path.join(cwd, '.gitignore'),
'utf8'
);
expect(gitignoreAfter).toBe(gitignoreBefore);
});
});
});

View File

@@ -26,7 +26,7 @@ describe('pull', () => {
`Created .vercel${path.sep}.env.development.local file`
);
await expect(client.stderr).toOutput(
`Downloaded project settings to .vercel${path.sep}project.json`
`Downloaded project settings to ${cwd}${path.sep}.vercel${path.sep}project.json`
);
await expect(exitCodePromise).resolves.toEqual(0);
@@ -92,7 +92,7 @@ describe('pull', () => {
`Created .vercel${path.sep}.env.development.local file`
);
await expect(client.stderr).toOutput(
`Downloaded project settings to .vercel${path.sep}project.json`
`Downloaded project settings to ${cwd}${path.sep}.vercel${path.sep}project.json`
);
await expect(exitCodePromise).resolves.toEqual(0);
@@ -130,7 +130,7 @@ describe('pull', () => {
`Created .vercel${path.sep}.env.preview.local file`
);
await expect(client.stderr).toOutput(
`Downloaded project settings to .vercel${path.sep}project.json`
`Downloaded project settings to ${cwd}${path.sep}.vercel${path.sep}project.json`
);
await expect(exitCodePromise).resolves.toEqual(0);
@@ -161,7 +161,7 @@ describe('pull', () => {
`Created .vercel${path.sep}.env.production.local file`
);
await expect(client.stderr).toOutput(
`Downloaded project settings to .vercel${path.sep}project.json`
`Downloaded project settings to ${cwd}${path.sep}.vercel${path.sep}project.json`
);
await expect(exitCodePromise).resolves.toEqual(0);
@@ -177,4 +177,29 @@ describe('pull', () => {
.includes('SQL_CONNECTION_STRING');
expect(previewFileHasPreviewEnv2).toBeTruthy();
});
it('should work with repo link', async () => {
const cwd = setupUnitFixture('monorepo-link');
useUser();
useTeams('team_dummy');
useProject({
...defaultProject,
id: 'QmbKpqpiUqbcke',
name: 'dashboard',
rootDirectory: 'dashboard',
});
client.cwd = path.join(cwd, 'dashboard');
client.setArgv('pull');
const exitCodePromise = pull(client);
await expect(client.stderr).toOutput(
'Downloading `development` Environment Variables for Project dashboard'
);
await expect(client.stderr).toOutput(
`Created .vercel${path.sep}.env.development.local file`
);
await expect(client.stderr).toOutput(
`Downloaded project settings to ${cwd}${path.sep}dashboard${path.sep}.vercel${path.sep}project.json`
);
await expect(exitCodePromise).resolves.toEqual(0);
});
});

View File

@@ -1,7 +1,7 @@
import { tmpdir } from 'node:os';
import { join, sep } from 'node:path';
import {
findProjectFromPath,
findProjectsFromPath,
findRepoRoot,
RepoProjectConfig,
traverseUpDirectories,
@@ -57,35 +57,36 @@ describe('traverseUpDirectories()', () => {
});
});
describe('findProjectFromPath()', () => {
describe('findProjectsFromPath()', () => {
const projects: RepoProjectConfig[] = [
{ id: 'root', name: 'r', directory: '.' },
{ id: 'site', name: 'a', directory: 'apps/site' },
{ id: 'site2', name: 'a', directory: 'apps/site2' },
{ id: 'other', name: 'b', directory: 'apps/other' },
{ id: 'duplicate', name: 'd', directory: 'apps/other' },
{ id: 'nested', name: 'n', directory: 'apps/other/nested' },
];
it.each([
{ id: 'root', path: '.' },
{ id: 'root', path: 'lib' },
{ id: 'root', path: 'lib' },
{ id: 'site', path: `apps${sep}site` },
{ id: 'site', path: `apps${sep}site` },
{ id: 'site', path: `apps${sep}site${sep}components` },
{ id: 'site2', path: `apps${sep}site2` },
{ id: 'site2', path: `apps${sep}site2${sep}inner` },
{ id: 'other', path: `apps${sep}other` },
{ id: 'other', path: `apps${sep}other${sep}lib` },
{ id: 'nested', path: `apps${sep}other${sep}nested` },
{ id: 'nested', path: `apps${sep}other${sep}nested${sep}foo` },
])('should find Project "$id" for path "$path"', ({ path, id }) => {
const actual = findProjectFromPath(projects, path);
expect(actual?.id).toEqual(id);
{ ids: ['root'], path: '.' },
{ ids: ['root'], path: 'lib' },
{ ids: ['root'], path: 'lib' },
{ ids: ['site'], path: `apps${sep}site` },
{ ids: ['site'], path: `apps${sep}site` },
{ ids: ['site'], path: `apps${sep}site${sep}components` },
{ ids: ['site2'], path: `apps${sep}site2` },
{ ids: ['site2'], path: `apps${sep}site2${sep}inner` },
{ ids: ['other', 'duplicate'], path: `apps${sep}other` },
{ ids: ['other', 'duplicate'], path: `apps${sep}other${sep}lib` },
{ ids: ['nested'], path: `apps${sep}other${sep}nested` },
{ ids: ['nested'], path: `apps${sep}other${sep}nested${sep}foo` },
])('should find Project "$id" for path "$path"', ({ path, ids }) => {
const actual = findProjectsFromPath(projects, path);
expect(actual.map(a => a.id)).toEqual(ids);
});
it('should return `undefined` when there are no matching Projects', () => {
const actual = findProjectFromPath([projects[1]], '.');
expect(actual).toBeUndefined();
it('should return empty array when there are no matching Projects', () => {
const actual = findProjectsFromPath([projects[1]], '.');
expect(actual).toHaveLength(0);
});
});

View File

@@ -0,0 +1,104 @@
import { client } from '../../../mocks/client';
import getProjectByDeployment from '../../../../src/util/projects/get-project-by-deployment';
import { useTeams } from '../../../mocks/team';
import { useUser } from '../../../mocks/user';
import { useDeployment } from '../../../mocks/deployment';
import { defaultProject, useProject } from '../../../mocks/project';
describe('getProjectByDeployment', () => {
it('should get project and deployment', async () => {
const user = useUser();
const { project: p } = useProject({
...defaultProject,
id: 'foo',
name: 'foo',
});
const d = useDeployment({
creator: user,
createdAt: Date.now(),
project: p,
});
const { deployment, project } = await getProjectByDeployment({
client,
deployId: d.id,
output: client.output,
});
expect(project.id).toBe(p.id);
expect(deployment.id).toBe(d.id);
});
it('should get project and deployment associated to a team', async () => {
const [team] = useTeams('team_dummy');
const user = useUser();
const { project: p } = useProject({
...defaultProject,
id: 'foo',
name: 'foo',
});
const d = useDeployment({
creator: {
id: team.id,
name: team.name,
email: user.email,
username: team.slug,
},
createdAt: Date.now(),
project: p,
});
client.config.currentTeam = team.id;
d.team = team;
const { deployment, project } = await getProjectByDeployment({
client,
deployId: d.id,
output: client.output,
});
expect(project.id).toBe(p.id);
expect(deployment.id).toBe(d.id);
});
it("should error if deployment team doesn't match current user's team", async () => {
const [team] = useTeams('team_dummy');
const user = useUser();
const { project: p } = useProject({
...defaultProject,
id: 'foo',
name: 'foo',
});
const d = useDeployment({
creator: {
id: team.id,
name: team.name,
email: user.email,
username: team.slug,
},
createdAt: Date.now(),
project: p,
});
client.config.currentTeam = team.id;
await expect(
getProjectByDeployment({
client,
deployId: d.id,
output: client.output,
})
).rejects.toThrowError("Deployment doesn't belong to current team");
client.config.currentTeam = undefined;
d.team = team;
await expect(
getProjectByDeployment({
client,
deployId: d.id,
output: client.output,
})
).rejects.toThrowError('Deployment belongs to a different team');
});
});

View File

@@ -1,5 +1,12 @@
# @vercel/client
## 12.6.2
### Patch Changes
- Updated dependencies [[`cd35071f6`](https://github.com/vercel/vercel/commit/cd35071f609d615d47bc04634c123b33768436cb)]:
- @vercel/build-utils@6.7.5
## 12.6.1
### Patch Changes

View File

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

View File

@@ -1,5 +1,11 @@
# @vercel/fs-detectors
## 4.0.0
### Major Changes
- `LocalFileSystemDetector#readdir()` now returns paths relative to the root dir, instead of absolute paths. This is to align with the usage of the detectors that are using the `DetectorFilesystem` interface. ([#10100](https://github.com/vercel/vercel/pull/10100))
## 3.9.3
### Patch Changes

View File

@@ -1,6 +1,6 @@
{
"name": "@vercel/fs-detectors",
"version": "3.9.3",
"version": "4.0.0",
"description": "Vercel filesystem detectors",
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
@@ -15,8 +15,8 @@
"license": "Apache-2.0",
"scripts": {
"build": "tsc",
"test": "jest --env node --verbose --runInBand --bail test/unit.*test.*",
"test-unit": "pnpm test"
"test": "jest --env node --verbose --runInBand --bail",
"test-unit": "pnpm test test/unit.*test.*"
},
"dependencies": {
"@vercel/error-utils": "1.0.10",
@@ -35,7 +35,7 @@
"@types/minimatch": "3.0.5",
"@types/node": "14.18.33",
"@types/semver": "7.3.10",
"@vercel/build-utils": "6.7.4",
"@vercel/build-utils": "6.7.5",
"typescript": "4.9.5"
}
}

View File

@@ -6,10 +6,12 @@ import { isErrnoException } from '@vercel/error-utils';
export class LocalFileSystemDetector extends DetectorFilesystem {
private rootPath: string;
constructor(rootPath: string) {
super();
this.rootPath = rootPath;
}
async _hasPath(name: string): Promise<boolean> {
try {
await fs.stat(this.getFilePath(name));
@@ -21,13 +23,16 @@ export class LocalFileSystemDetector extends DetectorFilesystem {
throw err;
}
}
_readFile(name: string): Promise<Buffer> {
return fs.readFile(this.getFilePath(name));
}
async _isFile(name: string): Promise<boolean> {
const stat = await fs.stat(this.getFilePath(name));
return stat.isFile();
}
async _readdir(name: string): Promise<DetectorFilesystemStat[]> {
const dirPath = this.getFilePath(name);
const dir = await fs.readdir(dirPath, {
@@ -44,14 +49,22 @@ export class LocalFileSystemDetector extends DetectorFilesystem {
};
return dir.map(dirent => ({
name: dirent.name,
path: path.join(dirPath, dirent.name),
path: path.join(this.getRelativeFilePath(name), dirent.name),
type: getType(dirent),
}));
}
_chdir(name: string): DetectorFilesystem {
return new LocalFileSystemDetector(this.getFilePath(name));
}
private getRelativeFilePath(name: string) {
return name.startsWith(this.rootPath)
? path.relative(this.rootPath, name)
: name;
}
private getFilePath(name: string) {
return path.join(this.rootPath, name);
return path.join(this.rootPath, this.getRelativeFilePath(name));
}
}

View File

@@ -1,7 +1,7 @@
import path from 'path';
import { LocalFileSystemDetector } from '../src';
import { detectFramework } from '../src/detect-framework';
import monorepoManagers from '../src/monorepos/monorepo-managers';
import { FixtureFilesystem } from './utils/fixture-filesystem';
describe('monorepo-managers', () => {
describe.each([
@@ -17,7 +17,7 @@ describe('monorepo-managers', () => {
it(testName, async () => {
const fixture = path.join(__dirname, 'fixtures', fixturePath);
const fs = new FixtureFilesystem(fixture);
const fs = new LocalFileSystemDetector(fixture);
const result = await detectFramework({
fs,

View File

@@ -1,6 +1,9 @@
import path from 'path';
import { packageManagers, detectFramework } from '../src';
import { FixtureFilesystem } from './utils/fixture-filesystem';
import {
packageManagers,
detectFramework,
LocalFileSystemDetector,
} from '../src';
describe('package-managers', () => {
describe.each([
@@ -16,7 +19,7 @@ describe('package-managers', () => {
it(testName, async () => {
const fixture = path.join(__dirname, 'fixtures', fixturePath);
const fs = new FixtureFilesystem(fixture);
const fs = new LocalFileSystemDetector(fixture);
const result = await detectFramework({
fs,

View File

@@ -1,7 +1,7 @@
import path from 'path';
import { LocalFileSystemDetector } from '../src';
import { detectFramework } from '../src/detect-framework';
import workspaceManagers from '../src/workspaces/workspace-managers';
import { FixtureFilesystem } from './utils/fixture-filesystem';
describe('workspace-managers', () => {
describe.each([
@@ -19,7 +19,7 @@ describe('workspace-managers', () => {
it(testName, async () => {
const fixture = path.join(__dirname, 'fixtures', fixturePath);
const fs = new FixtureFilesystem(fixture);
const fs = new LocalFileSystemDetector(fixture);
const result = await detectFramework({
fs,

View File

@@ -1,13 +1,12 @@
import frameworkList from '@vercel/frameworks';
import { detectFramework } from '../src';
import { FixtureFilesystem } from './utils/fixture-filesystem';
import { detectFramework, LocalFileSystemDetector } from '../src';
import { getExamples } from '../../../examples/__tests__/test-utils';
describe('examples should be detected', () => {
it.each(getExamples())(
'should detect $exampleName',
async ({ exampleName, examplePath }) => {
const fs = new FixtureFilesystem(examplePath);
const fs = new LocalFileSystemDetector(examplePath);
const framework = await detectFramework({ fs, frameworkList });
if (!framework) {
throw new Error(`Framework not detected for example "${exampleName}".`);

View File

@@ -1,13 +1,12 @@
import os from 'os';
import path from 'path';
import { mkdtempSync } from 'fs';
import {
getMonorepoDefaultSettings,
LocalFileSystemDetector,
MissingBuildPipeline,
MissingBuildTarget,
} from '../src';
import path from 'path';
import fs from 'fs';
import os from 'os';
import { FixtureFilesystem } from './utils/fixture-filesystem';
describe('getMonorepoDefaultSettings', () => {
test('MissingBuildTarget is an error', () => {
@@ -69,7 +68,7 @@ describe('getMonorepoDefaultSettings', () => {
},
};
const ffs = new FixtureFilesystem(
const fs = new LocalFileSystemDetector(
path.join(
__dirname,
'fixtures',
@@ -81,16 +80,16 @@ describe('getMonorepoDefaultSettings', () => {
packageName,
isRoot ? '/' : 'packages/app-1',
isRoot ? '/' : '../..',
ffs
fs
);
expect(result).toStrictEqual(expectedResultMap[expectedResultKey]);
}
);
test('returns null when neither nx nor turbo is detected', async () => {
const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'monorepo-test-'));
const lfs = new LocalFileSystemDetector(dir);
const result = await getMonorepoDefaultSettings('', '', '', lfs);
const dir = mkdtempSync(path.join(os.tmpdir(), 'monorepo-test-'));
const fs = new LocalFileSystemDetector(dir);
const result = await getMonorepoDefaultSettings('', '', '', fs);
expect(result).toBe(null);
});
});

View File

@@ -1,7 +1,7 @@
import path from 'path';
import { normalizePath } from '@vercel/build-utils';
import { getProjectPaths, ProjectPath } from '../src/get-project-paths';
import { FixtureFilesystem } from './utils/fixture-filesystem';
import { LocalFileSystemDetector } from '../src';
describe.each<{
fixturePath: string;
@@ -52,7 +52,7 @@ describe.each<{
it(testName, async () => {
const fixture = path.join(__dirname, 'fixtures', fixturePath);
const fs = new FixtureFilesystem(fixture);
const fs = new LocalFileSystemDetector(fixture);
const mockReaddir = jest.fn().mockImplementation(fs.readdir);
const mockHasPath = jest.fn().mockImplementation(fs.hasPath);
fs.readdir = mockReaddir;

View File

@@ -1,7 +1,7 @@
import path from 'path';
import { getWorkspaces } from '../src/workspaces/get-workspaces';
import { getWorkspacePackagePaths } from '../src/workspaces/get-workspace-package-paths';
import { FixtureFilesystem } from './utils/fixture-filesystem';
import { LocalFileSystemDetector } from '../src';
describe.each<[string, string[]]>([
['21-npm-workspaces', ['/a', '/b']],
@@ -32,7 +32,7 @@ describe.each<[string, string[]]>([
it(testName, async () => {
const fixture = path.join(__dirname, 'fixtures', fixturePath);
const fs = new FixtureFilesystem(fixture);
const fs = new LocalFileSystemDetector(fixture);
const workspaces = await getWorkspaces({ fs });
const actualPackagePaths = (

View File

@@ -1,6 +1,6 @@
import path from 'path';
import { LocalFileSystemDetector } from '../src';
import { getWorkspaces, Workspace } from '../src/workspaces/get-workspaces';
import { FixtureFilesystem } from './utils/fixture-filesystem';
describe.each<[string, Workspace[]]>([
['21-npm-workspaces', [{ type: 'npm', rootPath: '/' }]],
@@ -34,7 +34,7 @@ describe.each<[string, Workspace[]]>([
it(testName, async () => {
const fixture = path.join(__dirname, 'fixtures', fixturePath);
const fs = new FixtureFilesystem(fixture);
const fs = new LocalFileSystemDetector(fixture);
const actualWorkspaces = await getWorkspaces({ fs });

View File

@@ -5,7 +5,7 @@ import { LocalFileSystemDetector, DetectorFilesystem } from '../src';
const tmpdir = path.join(os.tmpdir(), 'local-file-system-test');
const dirs = ['', 'a', 'a/b']; // root, single-nested, double-nested
const dirs = ['', 'a', `a${path.sep}b`]; // root, single-nested, double-nested
const files = ['foo', 'bar'];
const filePaths = dirs.flatMap(dir => files.map(file => path.join(dir, file)));
@@ -63,12 +63,7 @@ describe('LocalFileSystemDetector', () => {
const readdirResults = await Promise.all(
dirs.map(dir => localFileSystem.readdir(dir))
);
const expectedPaths = [
...dirs.map(dir => path.join(tmpdir, dir)),
...filePaths.map(filePath => path.join(tmpdir, filePath)),
]
.sort()
.slice(1); // drop the first path since its the root
const expectedPaths = [...dirs, ...filePaths].sort().slice(1); // drop the first path since its the root
const actualPaths = readdirResults
.flatMap(result => result.map(stat => stat.path))
.sort();

View File

@@ -1,51 +0,0 @@
import { promises } from 'fs';
import path from 'path';
import { DetectorFilesystem } from '../../src';
import { DetectorFilesystemStat } from '../../src/detectors/filesystem';
const { stat, readFile, readdir } = 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();
}
async _readdir(name: string): Promise<DetectorFilesystemStat[]> {
const dirPath = path.join(this.rootPath, name);
const files = await readdir(dirPath, { withFileTypes: true });
return files.map(file => ({
name: file.name,
type: file.isFile() ? 'file' : 'dir',
path: path.join(name, file.name),
}));
}
_chdir(name: string): DetectorFilesystem {
return new FixtureFilesystem(path.join(this.rootPath, name));
}
}

View File

@@ -1,5 +1,27 @@
# @vercel/gatsby-plugin-vercel-builder
## 1.3.9
### Patch Changes
- Updated dependencies [[`a04bf557f`](https://github.com/vercel/vercel/commit/a04bf557fc6e1080a117428977d0993dec78b004)]:
- @vercel/node@2.15.1
## 1.3.8
### Patch Changes
- Updated dependencies [[`bc5afe24c`](https://github.com/vercel/vercel/commit/bc5afe24c4547dbf798b939199e8212c4b34038e), [`0039c8b5c`](https://github.com/vercel/vercel/commit/0039c8b5cea975316a62c4f6aaca5d66d731cc0d)]:
- @vercel/node@2.15.0
## 1.3.7
### Patch Changes
- Updated dependencies [[`cd35071f6`](https://github.com/vercel/vercel/commit/cd35071f609d615d47bc04634c123b33768436cb)]:
- @vercel/build-utils@6.7.5
- @vercel/node@2.14.5
## 1.3.6
### Patch Changes

View File

@@ -1,6 +1,6 @@
{
"name": "@vercel/gatsby-plugin-vercel-builder",
"version": "1.3.6",
"version": "1.3.9",
"main": "dist/index.js",
"files": [
"dist",
@@ -20,8 +20,8 @@
},
"dependencies": {
"@sinclair/typebox": "0.25.24",
"@vercel/build-utils": "6.7.4",
"@vercel/node": "2.14.4",
"@vercel/build-utils": "6.7.5",
"@vercel/node": "2.15.1",
"@vercel/routing-utils": "2.2.1",
"esbuild": "0.14.47",
"etag": "1.8.1",

View File

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

View File

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

View File

@@ -35,7 +35,7 @@
"@types/semver": "6.0.0",
"@types/text-table": "0.2.1",
"@types/webpack-sources": "3.2.0",
"@vercel/build-utils": "6.7.4",
"@vercel/build-utils": "6.7.5",
"@vercel/nft": "0.22.5",
"@vercel/routing-utils": "2.2.1",
"async-sema": "3.0.1",

View File

@@ -1,5 +1,26 @@
# @vercel/node
## 2.15.1
### Patch Changes
- handle undefined content type in `vc dev` ([#10077](https://github.com/vercel/vercel/pull/10077))
## 2.15.0
### Minor Changes
- Add maxDuration config support for vc node deployments ([#10028](https://github.com/vercel/vercel/pull/10028))
- [node] Add isomorphic functions ([#9947](https://github.com/vercel/vercel/pull/9947))
## 2.14.5
### Patch Changes
- Updated dependencies [[`cd35071f6`](https://github.com/vercel/vercel/commit/cd35071f609d615d47bc04634c123b33768436cb)]:
- @vercel/build-utils@6.7.5
## 2.14.4
### Patch Changes

View File

@@ -1,6 +1,6 @@
{
"name": "@vercel/node",
"version": "2.14.4",
"version": "2.15.1",
"license": "Apache-2.0",
"main": "./dist/index",
"homepage": "https://vercel.com/docs/runtimes#official-runtimes/node-js",
@@ -19,10 +19,12 @@
"dist"
],
"dependencies": {
"@edge-runtime/node-utils": "2.0.3",
"@edge-runtime/primitives": "2.1.2",
"@edge-runtime/vm": "2.0.0",
"@types/node": "14.18.33",
"@types/node-fetch": "2.6.3",
"@vercel/build-utils": "6.7.4",
"@vercel/build-utils": "6.7.5",
"@vercel/error-utils": "1.0.10",
"@vercel/static-config": "2.0.17",
"async-listen": "3.0.0",

View File

@@ -10,14 +10,14 @@ function getUrl(url, headers) {
return urlObj.toString();
}
async function respond(userEdgeHandler, event, options, dependencies) {
async function respond(handler, event, options, dependencies) {
const { Request, Response } = dependencies;
const { isMiddleware } = options;
event.request.headers.set(
'host',
event.request.headers.get('x-forwarded-host')
);
let response = await userEdgeHandler(
let response = await handler(
new Request(
getUrl(event.request.url, event.request.headers),
event.request
@@ -62,16 +62,34 @@ async function parseRequestEvent(event) {
// This will be invoked by logic using this template
// eslint-disable-next-line @typescript-eslint/no-unused-vars
function registerFetchListener(userEdgeHandler, options, dependencies) {
function registerFetchListener(module, options, dependencies) {
let handler;
addEventListener('fetch', async event => {
try {
const response = await respond(
userEdgeHandler,
event,
options,
dependencies
);
return event.respondWith(response);
if (typeof module.default === 'function') {
handler = module.default;
} else {
if (
['GET', 'HEAD', 'OPTIONS', 'POST', 'PUT', 'DELETE', 'PATCH'].some(
method => typeof module[method] === 'function'
)
) {
const method = event.request.method ?? 'GET';
handler =
typeof module[method] === 'function'
? module[method]
: () => new dependencies.Response(null, { status: 405 });
}
}
if (!handler) {
const url = getUrl(event.request.url, event.request.headers);
throw new Error(
`No default or HTTP-named export was found at ${url}. Add one to handle requests. Learn more: https://vercel.link/creating-edge-middleware`
);
}
const response = await respond(handler, event, options, dependencies);
event.respondWith(response);
} catch (error) {
event.respondWith(toResponseError(error, dependencies.Response));
}

View File

@@ -89,12 +89,7 @@ async function compileUserCode(
// user code
${compiledFile.text};
const userEdgeHandler = module.exports.default;
if (!userEdgeHandler) {
throw new Error(
'No default export was found. Add a default export to handle requests. Learn more: https://vercel.link/creating-edge-middleware'
);
}
const userModule = module.exports;
// request metadata
const isMiddleware = ${isMiddleware};
@@ -104,7 +99,7 @@ async function compileUserCode(
${edgeHandlerTemplate};
const dependencies = { Request, Response };
const options = { isMiddleware, entrypointLabel };
registerFetchListener(userEdgeHandler, options, dependencies);
registerFetchListener(userModule, options, dependencies);
`;
return {

View File

@@ -506,6 +506,7 @@ export const build: BuildV3 = async ({
shouldAddSourcemapSupport,
awsLambdaHandler,
supportsResponseStreaming,
maxDuration: staticConfig?.maxDuration,
});
}

View File

@@ -0,0 +1,76 @@
import type { ServerResponse, IncomingMessage } from 'http';
import type { NodeHandler } from '@edge-runtime/node-utils';
import { buildToNodeHandler } from '@edge-runtime/node-utils';
class FetchEvent {
public request: Request;
public awaiting: Set<Promise<void>>;
public response: Response | null;
constructor(request: Request) {
this.request = request;
this.response = null;
this.awaiting = new Set();
}
respondWith(response: Response) {
this.response = response;
}
waitUntil() {
throw new Error('waitUntil is not implemented yet for Node.js');
}
}
const webHandlerToNodeHandler = buildToNodeHandler(
{
Headers,
ReadableStream,
Request: class extends Request {
constructor(input: RequestInfo | URL, init?: RequestInit | undefined) {
super(input, addDuplexToInit(init));
}
},
Uint8Array: Uint8Array,
FetchEvent: FetchEvent,
},
{ defaultOrigin: 'https://vercel.com' }
);
/**
* When users export at least one HTTP handler, we will generate
* a generic handler routing to the right method. If there is no
* handler function exported returns null.
*/
export function getWebExportsHandler(listener: any, methods: string[]) {
const handlerByMethod: { [key: string]: NodeHandler } = {};
for (const key of methods) {
handlerByMethod[key] =
typeof listener[key] !== 'undefined'
? webHandlerToNodeHandler(listener[key])
: defaultHttpHandler;
}
return (req: IncomingMessage, res: ServerResponse) => {
const method = req.method ?? 'GET';
handlerByMethod[method](req, res);
};
}
/**
* Add `duplex: 'half'` by default to all requests
* https://github.com/vercel/edge-runtime/blob/bf167c418247a79d3941bfce4a5d43c37f512502/packages/primitives/src/primitives/fetch.js#L22-L26
* https://developer.chrome.com/articles/fetch-streaming-requests/#streaming-request-bodies
*/
function addDuplexToInit(init: RequestInit | undefined) {
if (typeof init === 'undefined' || typeof init === 'object') {
return { duplex: 'half', ...init };
}
return init;
}
function defaultHttpHandler(_: IncomingMessage, res: ServerResponse) {
res.statusCode = 405;
res.end();
}

View File

@@ -27,10 +27,19 @@ class ApiError extends Error {
}
}
function normalizeContentType(contentType: string | undefined) {
if (!contentType) {
return 'text/plain';
}
const { parse: parseContentType } = require('content-type');
const { type } = parseContentType(contentType);
return type;
}
function getBodyParser(body: Buffer, contentType: string | undefined) {
return function parseBody(): VercelRequestBody {
const { parse: parseContentType } = require('content-type');
const { type } = parseContentType(contentType);
const type = normalizeContentType(contentType);
if (type === 'application/json') {
try {

View File

@@ -21,40 +21,63 @@ type ServerlessFunctionSignature = (
res: ServerResponse | VercelResponse
) => void;
async function createServerlessServer(
userCode: ServerlessFunctionSignature,
options: ServerlessServerOptions
) {
const server = createServer(async (req, res) => {
if (options.shouldAddHelpers) await addHelpers(req, res);
return userCode(req, res);
});
const [NODE_MAJOR] = process.versions.node.split('.').map(v => Number(v));
/* https://nextjs.org/docs/app/building-your-application/routing/router-handlers#supported-http-methods */
const HTTP_METHODS = [
'GET',
'HEAD',
'OPTIONS',
'POST',
'PUT',
'DELETE',
'PATCH',
];
async function createServerlessServer(userCode: ServerlessFunctionSignature) {
const server = createServer(userCode);
exitHook(() => server.close());
return { url: await listen(server) };
}
async function compileUserCode(entrypointPath: string) {
async function compileUserCode(
entrypointPath: string,
options: ServerlessServerOptions
) {
const id = isAbsolute(entrypointPath)
? pathToFileURL(entrypointPath).href
: entrypointPath;
let fn = await import(id);
let listener = await import(id);
/**
* In some cases we might have nested default props due to TS => JS
*/
for (let i = 0; i < 5; i++) {
if (fn.default) fn = fn.default;
if (listener.default) listener = listener.default;
}
return fn;
if (HTTP_METHODS.some(method => typeof listener[method] === 'function')) {
if (NODE_MAJOR < 18) {
throw new Error(
'Node.js v18 or above is required to use HTTP method exports in your functions.'
);
}
const { getWebExportsHandler } = await import('./helpers-web.js');
return getWebExportsHandler(listener, HTTP_METHODS);
}
return async (req: IncomingMessage, res: ServerResponse) => {
if (options.shouldAddHelpers) await addHelpers(req, res);
return listener(req, res);
};
}
export async function createServerlessEventHandler(
entrypointPath: string,
options: ServerlessServerOptions
): Promise<(request: IncomingMessage) => Promise<VercelProxyResponse>> {
const userCode = await compileUserCode(entrypointPath);
const server = await createServerlessServer(userCode, options);
const userCode = await compileUserCode(entrypointPath, options);
const server = await createServerlessServer(userCode);
return async function (request: IncomingMessage) {
const url = new URL(request.url ?? '/', server.url);

View File

@@ -59,7 +59,12 @@ export function entrypointToOutputPath(
}
export function logError(error: Error) {
console.error(error.message);
let message = error.message;
if (!message.startsWith('Error:')) {
message = `Error: ${message}`;
}
console.error(message);
if (error.stack) {
// only show the stack trace if debug is enabled
// because it points to internals, not user code

View File

@@ -0,0 +1,10 @@
/* global Response */
const baseUrl = ({ headers }) =>
`${headers.get('x-forwarded-proto')}://${headers.get('x-forwarded-host')}`;
export function GET(request) {
const { searchParams } = new URL(request.url, baseUrl(request));
const name = searchParams.get('name');
return new Response(`Greetings, ${name}`);
}

View File

@@ -4,6 +4,8 @@ import fetch from 'node-fetch';
jest.setTimeout(20 * 1000);
const [NODE_MAJOR] = process.versions.node.split('.').map(v => Number(v));
function testForkDevServer(entrypoint: string) {
const ext = extname(entrypoint);
const isTypeScript = ext === '.ts';
@@ -24,6 +26,41 @@ function testForkDevServer(entrypoint: string) {
});
}
(NODE_MAJOR < 18 ? test.skip : test)(
'runs an serverless function that exports GET',
async () => {
const child = testForkDevServer('./serverless-web.js');
try {
const result = await readMessage(child);
if (result.state !== 'message') {
throw new Error('Exited. error: ' + JSON.stringify(result.value));
}
const { address, port } = result.value;
{
const response = await fetch(
`http://${address}:${port}/api/serverless-web?name=Vercel`
);
expect({
status: response.status,
body: await response.text(),
}).toEqual({ status: 200, body: 'Greetings, Vercel' });
}
{
const response = await fetch(
`http://${address}:${port}/api/serverless-web?name=Vercel`,
{ method: 'HEAD' }
);
expect({ status: response.status }).toEqual({ status: 405 });
}
} finally {
child.kill(9);
}
}
);
test('runs an edge function that uses `WebSocket`', async () => {
const child = testForkDevServer('./edge-websocket.js');
try {
@@ -57,9 +94,8 @@ test('runs an edge function that uses `buffer`', async () => {
throw new Error('Exited. error: ' + JSON.stringify(result.value));
}
const response = await fetch(
`http://localhost:${result.value.port}/api/edge-buffer`
);
const { address, port } = result.value;
const response = await fetch(`http://${address}:${port}/api/edge-buffer`);
expect({
status: response.status,
json: await response.json(),
@@ -84,9 +120,8 @@ test('runs a mjs endpoint', async () => {
throw new Error('Exited. error: ' + JSON.stringify(result.value));
}
const response = await fetch(
`http://localhost:${result.value.port}/api/hello`
);
const { address, port } = result.value;
const response = await fetch(`http://${address}:${port}/api/hello`);
expect({
status: response.status,
headers: Object.fromEntries(response.headers),
@@ -117,9 +152,8 @@ test('runs a esm typescript endpoint', async () => {
throw new Error('Exited. error: ' + JSON.stringify(result.value));
}
const response = await fetch(
`http://localhost:${result.value.port}/api/hello`
);
const { address, port } = result.value;
const response = await fetch(`http://${address}:${port}/api/hello`);
expect({
status: response.status,
headers: Object.fromEntries(response.headers),
@@ -150,9 +184,8 @@ test('allow setting multiple cookies with same name', async () => {
throw new Error(`Exited. error: ${JSON.stringify(result.value)}`);
}
const response = await fetch(
`http://localhost:${result.value.port}/api/hello`
);
const { address, port } = result.value;
const response = await fetch(`http://${address}:${port}/api/hello`);
expect({
status: response.status,
text: await response.text(),

View File

@@ -23,7 +23,7 @@
"@types/execa": "^0.9.0",
"@types/jest": "27.4.1",
"@types/node": "14.18.33",
"@vercel/build-utils": "6.7.4",
"@vercel/build-utils": "6.7.5",
"@vercel/ncc": "0.24.0",
"execa": "^1.0.0"
}

View File

@@ -27,7 +27,7 @@
"@types/aws-lambda": "8.10.19",
"@types/node": "14.18.33",
"@types/semver": "6.0.0",
"@vercel/build-utils": "6.7.4",
"@vercel/build-utils": "6.7.5",
"execa": "3.2.0",
"fs-extra": "11.1.0"
}

View File

@@ -1,5 +1,18 @@
# @vercel/remix-builder
## 1.8.13
### Patch Changes
- Update `@remix-run/dev` fork to v1.17.0 ([#10072](https://github.com/vercel/vercel/pull/10072))
## 1.8.12
### Patch Changes
- Updated dependencies [[`cd35071f6`](https://github.com/vercel/vercel/commit/cd35071f609d615d47bc04634c123b33768436cb)]:
- @vercel/build-utils@6.7.5
## 1.8.11
### Patch Changes

View File

@@ -1,6 +1,6 @@
{
"name": "@vercel/remix-builder",
"version": "1.8.11",
"version": "1.8.13",
"license": "Apache-2.0",
"main": "./dist/index.js",
"homepage": "https://vercel.com/docs",
@@ -20,8 +20,8 @@
"defaults"
],
"dependencies": {
"@remix-run/dev": "npm:@vercel/remix-run-dev@1.16.1",
"@vercel/build-utils": "6.7.4",
"@remix-run/dev": "npm:@vercel/remix-run-dev@1.17.0",
"@vercel/build-utils": "6.7.5",
"@vercel/nft": "0.22.5",
"@vercel/static-config": "2.0.17",
"path-to-regexp": "6.2.1",

View File

@@ -22,7 +22,7 @@
"devDependencies": {
"@types/fs-extra": "8.0.0",
"@types/semver": "6.0.0",
"@vercel/build-utils": "6.7.4",
"@vercel/build-utils": "6.7.5",
"@vercel/ncc": "0.24.0",
"execa": "2.0.4",
"fs-extra": "^7.0.1",

View File

@@ -1,5 +1,26 @@
# @vercel/static-build
## 1.3.36
### Patch Changes
- Updated dependencies []:
- @vercel/gatsby-plugin-vercel-builder@1.3.9
## 1.3.35
### Patch Changes
- Updated dependencies []:
- @vercel/gatsby-plugin-vercel-builder@1.3.8
## 1.3.34
### Patch Changes
- Updated dependencies []:
- @vercel/gatsby-plugin-vercel-builder@1.3.7
## 1.3.33
### Patch Changes

View File

@@ -1,6 +1,6 @@
{
"name": "@vercel/static-build",
"version": "1.3.33",
"version": "1.3.36",
"license": "Apache-2.0",
"main": "./dist/index",
"homepage": "https://vercel.com/docs/build-step",
@@ -20,7 +20,7 @@
},
"dependencies": {
"@vercel/gatsby-plugin-vercel-analytics": "1.0.10",
"@vercel/gatsby-plugin-vercel-builder": "1.3.6"
"@vercel/gatsby-plugin-vercel-builder": "1.3.9"
},
"devDependencies": {
"@types/aws-lambda": "8.10.64",
@@ -32,10 +32,10 @@
"@types/node-fetch": "2.5.4",
"@types/promise-timeout": "1.3.0",
"@types/semver": "7.3.13",
"@vercel/build-utils": "6.7.4",
"@vercel/build-utils": "6.7.5",
"@vercel/error-utils": "1.0.10",
"@vercel/frameworks": "1.4.2",
"@vercel/fs-detectors": "3.9.3",
"@vercel/fs-detectors": "4.0.0",
"@vercel/ncc": "0.24.0",
"@vercel/routing-utils": "2.2.1",
"@vercel/static-config": "2.0.17",

View File

@@ -4,6 +4,7 @@ import { readerFromStreamReader } from 'https://deno.land/std@0.107.0/io/streams
export const config = {
runtime: 'deno',
location: 'https://example.com/page',
maxDuration: 60
};
export default async ({ request }: Deno.RequestEvent) => {

View File

@@ -3,6 +3,7 @@ import fs from 'fs';
export const config = {
runtime: 'nodejs',
memory: 1024,
maxDuration: 60,
};
export default function (req, res) {

View File

@@ -9,6 +9,7 @@ describe('getConfig()', () => {
const config = getConfig(project, sourcePath);
expect(config).toMatchInlineSnapshot(`
{
"maxDuration": 60,
"memory": 1024,
"runtime": "nodejs",
}
@@ -27,6 +28,7 @@ describe('getConfig()', () => {
expect(config).toMatchInlineSnapshot(`
{
"location": "https://example.com/page",
"maxDuration": 60,
"runtime": "deno",
}
`);

View File

@@ -56,6 +56,7 @@ describe('getConfig for swc', () => {
const config = getConfig(ast, BaseFunctionConfigSchema);
expect(config).toMatchInlineSnapshot(`
{
"maxDuration": 60,
"memory": 1024,
"runtime": "nodejs",
}
@@ -73,6 +74,7 @@ describe('getConfig for swc', () => {
expect(config).toMatchInlineSnapshot(`
{
"location": "https://example.com/page",
"maxDuration": 60,
"runtime": "deno",
}
`);

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