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 # Documentation
# https://help.github.com/en/articles/about-code-owners # https://help.github.com/en/articles/about-code-owners
# Restricted Paths
* @TooTallNate @EndangeredMassa @styfle @cb1kenobi @Ethan-Arrowood * @TooTallNate @EndangeredMassa @styfle @cb1kenobi @Ethan-Arrowood
/.github/workflows @TooTallNate @EndangeredMassa @styfle @cb1kenobi @Ethan-Arrowood @ijjk /.github/workflows @TooTallNate @EndangeredMassa @styfle @cb1kenobi @Ethan-Arrowood @ijjk
/packages/fs-detectors @TooTallNate @EndangeredMassa @styfle @cb1kenobi @Ethan-Arrowood @agadzik @chloetedder /packages/fs-detectors @TooTallNate @EndangeredMassa @styfle @cb1kenobi @Ethan-Arrowood @agadzik @chloetedder
@@ -14,3 +15,6 @@
/examples/jekyll @styfle /examples/jekyll @styfle
/examples/zola @styfle /examples/zola @styfle
/packages/node @TooTallNate @EndangeredMassa @styfle @cb1kenobi @Ethan-Arrowood @Kikobeats /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' }} cancel-in-progress: ${{ github.ref != 'refs/heads/main' }}
jobs: 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: test:
name: Lint name: Lint
timeout-minutes: 10 timeout-minutes: 10

View File

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

View File

@@ -9,9 +9,9 @@
"lint": "next lint" "lint": "next lint"
}, },
"dependencies": { "dependencies": {
"eslint": "8.41.0", "eslint": "8.42.0",
"eslint-config-next": "13.4.4", "eslint-config-next": "13.4.5",
"next": "13.4.4", "next": "13.4.5",
"react": "18.2.0", "react": "18.2.0",
"react-dom": "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 # @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 ## 1.0.1
### Patch Changes ### Patch Changes

View File

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

View File

@@ -1,5 +1,13 @@
# @vercel-internals/types # @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 ## 1.0.1
### Patch Changes ### Patch Changes

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,6 +1,6 @@
{ {
"name": "@vercel/build-utils", "name": "@vercel/build-utils",
"version": "6.7.4", "version": "6.7.5",
"license": "Apache-2.0", "license": "Apache-2.0",
"main": "./dist/index.js", "main": "./dist/index.js",
"types": "./dist/index.d.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 { getPrefixedEnvVars } from './get-prefixed-env-vars';
import { cloneEnv } from './clone-env'; import { cloneEnv } from './clone-env';
import { hardLinkDir } from './hard-link-dir'; import { hardLinkDir } from './hard-link-dir';
import { validateNpmrc } from './validate-npmrc';
export { export {
FileBlob, FileBlob,
@@ -88,6 +89,7 @@ export {
getIgnoreFilter, getIgnoreFilter,
cloneEnv, cloneEnv,
hardLinkDir, hardLinkDir,
validateNpmrc,
}; };
export { EdgeFunction } from './edge-function'; 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 # 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 ## 30.1.0
### Minor Changes ### Minor Changes

View File

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

View File

@@ -6,21 +6,19 @@ import chalk, { Chalk } from 'chalk';
import { URLSearchParams, parse } from 'url'; import { URLSearchParams, parse } from 'url';
import box from '../../util/output/box'; import box from '../../util/output/box';
import sleep from '../../util/sleep';
import formatDate from '../../util/format-date'; import formatDate from '../../util/format-date';
import link from '../../util/output/link'; import link from '../../util/output/link';
import logo from '../../util/output/logo'; import logo from '../../util/output/logo';
import getArgs from '../../util/get-args'; import getArgs from '../../util/get-args';
import Client from '../../util/client'; import Client from '../../util/client';
import { getPkgName } from '../../util/pkg-name'; 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 { normalizeURL } from '../../util/bisect/normalize-url';
import getScope from '../../util/get-scope'; import getScope from '../../util/get-scope';
import getDeployment from '../../util/get-deployment'; import getDeployment from '../../util/get-deployment';
interface Deployments { interface Deployments {
deployments: Deployment[]; deployments: Deployment[];
pagination: PaginationOptions;
} }
const pkgName = getPkgName(); 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 // Fetch all the project's "READY" deployments with the pagination API
let deployments: Deployment[] = []; 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, accountId: badDeployment.ownerId,
}); }
)) {
next = res.pagination.next; let newDeployments = chunk.deployments;
let newDeployments = res.deployments;
// If we have the "good" deployment in this chunk, then we're done // If we have the "good" deployment in this chunk, then we're done
for (let i = 0; i < newDeployments.length; i++) { for (let i = 0; i < newDeployments.length; i++) {
if (newDeployments[i].url === good) { if (newDeployments[i].url === good) {
// grab all deployments up until the good one // grab all deployments up until the good one
newDeployments = newDeployments.slice(0, i); newDeployments = newDeployments.slice(0, i);
next = undefined;
break; break;
} }
} }
deployments = deployments.concat(newDeployments); deployments = deployments.concat(newDeployments);
}
if (next) {
// Small sleep to avoid rate limiting
await sleep(100);
}
} while (next);
if (!deployments.length) { if (!deployments.length) {
output.error( output.error(

View File

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

View File

@@ -203,56 +203,6 @@ export default async (client: Client): Promise<number> => {
return target; 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']; const archive = argv['--archive'];
if (typeof archive === 'string' && !isValidArchive(archive)) { if (typeof archive === 'string' && !isValidArchive(archive)) {
output.error(`Format must be one of: ${VALID_ARCHIVE_FORMATS.join(', ')}`); 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`); 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 // Set the `contextName` and `currentTeam` as specified by the
// Project Settings, so that API calls happen with the proper scope // Project Settings, so that API calls happen with the proper scope
const contextName = org.slug; const contextName = org.slug;

View File

@@ -58,7 +58,13 @@ export default async function dev(
let projectSettings: ProjectSettings | undefined; let projectSettings: ProjectSettings | undefined;
let envValues: Record<string, string> = {}; let envValues: Record<string, string> = {};
if (link.status === 'linked') { 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; client.config.currentTeam = org.type === 'team' ? org.id : undefined;
projectSettings = project; projectSettings = project;

View File

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

View File

@@ -2,7 +2,11 @@ import chalk from 'chalk';
import { outputFile } from 'fs-extra'; import { outputFile } from 'fs-extra';
import { closeSync, openSync, readSync } from 'fs'; import { closeSync, openSync, readSync } from 'fs';
import { resolve } from 'path'; 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 Client from '../../util/client';
import { emoji, prependEmoji } from '../../util/emoji'; import { emoji, prependEmoji } from '../../util/emoji';
import confirm from '../../util/input/confirm'; import confirm from '../../util/input/confirm';
@@ -19,6 +23,7 @@ import {
createEnvObject, createEnvObject,
} from '../../util/env/diff-env-files'; } from '../../util/env/diff-env-files';
import { isErrnoException } from '@vercel/error-utils'; import { isErrnoException } from '@vercel/error-utils';
import { addToGitIgnore } from '../../util/link/add-to-gitignore';
const CONTENTS_PREFIX = '# Created by Vercel CLI\n'; const CONTENTS_PREFIX = '# Created by Vercel CLI\n';
@@ -51,6 +56,7 @@ function tryReadHeadSync(path: string, length: number) {
export default async function pull( export default async function pull(
client: Client, client: Client,
link: ProjectLinked,
project: Project, project: Project,
environment: ProjectEnvTarget, environment: ProjectEnvTarget,
opts: Partial<Options>, opts: Partial<Options>,
@@ -136,11 +142,22 @@ export default async function pull(
output.log('No changes found.'); 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( output.print(
`${prependEmoji( `${prependEmoji(
`${exists ? 'Updated' : 'Created'} ${chalk.bold( `${exists ? 'Updated' : 'Created'} ${chalk.bold(filename)} file ${
filename isGitIgnoreUpdated ? 'and added it to .gitignore' : ''
)} file ${chalk.gray(pullStamp())}`, } ${chalk.gray(pullStamp())}`,
emoji('success') emoji('success')
)}\n` )}\n`
); );

View File

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

View File

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

View File

@@ -19,11 +19,13 @@ import type {
Stdio, Stdio,
ReadableTTY, ReadableTTY,
WritableTTY, WritableTTY,
PaginationOptions,
} from '@vercel-internals/types'; } from '@vercel-internals/types';
import { sharedPromise } from './promise'; import { sharedPromise } from './promise';
import { APIError } from './errors-ts'; import { APIError } from './errors-ts';
import { normalizeError } from '@vercel/error-utils'; import { normalizeError } from '@vercel/error-utils';
import type { Agent } from 'http'; import type { Agent } from 'http';
import sleep from './sleep';
const isSAMLError = (v: any): v is SAMLError => { const isSAMLError = (v: any): v is SAMLError => {
return v && v.saml; return v && v.saml;
@@ -176,6 +178,31 @@ export default class Client extends EventEmitter implements Stdio {
}, opts.retry); }, 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 ( reauthenticate = sharedPromise(async function (
this: Client, this: Client,
error: SAMLError error: SAMLError

View File

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

View File

@@ -1,15 +1,10 @@
import { Org, Project } from '@vercel-internals/types';
import Client from '../client'; import Client from '../client';
import setupAndLink from '../link/setup-and-link'; import setupAndLink from '../link/setup-and-link';
import param from '../output/param'; import param from '../output/param';
import { getCommandName } from '../pkg-name'; import { getCommandName } from '../pkg-name';
import { getLinkedProject } from '../projects/link'; import { getLinkedProject } from '../projects/link';
import type { SetupAndLinkOptions } from '../link/setup-and-link'; import type { SetupAndLinkOptions } from '../link/setup-and-link';
import type { ProjectLinked } from '@vercel-internals/types';
type LinkResult = {
org: Org;
project: Project;
};
/** /**
* Checks if a project is already linked and if not, links the project and * Checks if a project is already linked and if not, links the project and
@@ -23,15 +18,15 @@ type LinkResult = {
* directory * directory
* @param opts.projectName - The project name to use when linking, otherwise * @param opts.projectName - The project name to use when linking, otherwise
* the current directory * 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 * error, otherwise an object containing the org an project
*/ */
export async function ensureLink( export async function ensureLink(
commandName: string, commandName: string,
client: Client, client: Client,
cwd: string, cwd: string,
opts: SetupAndLinkOptions opts: SetupAndLinkOptions = {}
): Promise<LinkResult | number> { ): Promise<ProjectLinked | number> {
let { link } = opts; let { link } = opts;
if (!link) { if (!link) {
link = await getLinkedProject(client, cwd); link = await getLinkedProject(client, cwd);
@@ -61,5 +56,5 @@ export async function ensureLink(
return link.exitCode; 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 { homedir } from 'os';
import { join, normalize } from 'path'; import { join, normalize } from 'path';
import { normalizePath } from '@vercel/build-utils'; 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 confirm from '../input/confirm';
import toHumanPath from '../humanize-path'; import toHumanPath from '../humanize-path';
import { import { VERCEL_DIR, VERCEL_DIR_REPO, writeReadme } from '../projects/link';
VERCEL_DIR,
VERCEL_DIR_README,
VERCEL_DIR_REPO,
} from '../projects/link';
import { getRemoteUrls } from '../create-git-meta'; import { getRemoteUrls } from '../create-git-meta';
import link from '../output/link'; import link from '../output/link';
import { emoji, prependEmoji } from '../emoji'; import { emoji, prependEmoji } from '../emoji';
@@ -128,9 +124,30 @@ export async function ensureRepoLink(
output.spinner( output.spinner(
`Fetching Projects for ${link(repoUrl)} under ${chalk.bold(org.slug)}` `Fetching Projects for ${link(repoUrl)} under ${chalk.bold(org.slug)}`
); );
// TODO: Add pagination to fetch all Projects let projects: Project[] = [];
const query = new URLSearchParams({ repoUrl, limit: '100' }); const query = new URLSearchParams({ repoUrl });
const projects: Project[] = await client.fetch(`/v2/projects?${query}`); 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) { if (projects.length === 0) {
output.log( output.log(
`No Projects are linked to ${link(repoUrl)} under ${chalk.bold( `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. // TODO: run detection logic to find potential projects.
// then prompt user to select valid projects. // then prompt user to select valid projects.
// then create new 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 = shouldLink =
yes || yes ||
(await confirm( (await confirm(
client, client,
`Link to ${projects.length === 1 ? 'it' : 'them'}?`, `Link to ${
projects.length === 1
? 'this Project'
: `these ${chalk.bold(projects.length)} Projects`
}?`,
true true
)); ));
@@ -179,10 +189,7 @@ export async function ensureRepoLink(
}; };
await outputJSON(repoConfigPath, repoConfig, { spaces: 2 }); await outputJSON(repoConfigPath, repoConfig, { spaces: 2 });
await writeFile( await writeReadme(rootPath);
join(rootPath, VERCEL_DIR, VERCEL_DIR_README),
await readFile(join(__dirname, 'VERCEL_DIR_README.txt'), 'utf8')
);
// update .gitignore // update .gitignore
const isGitIgnoreUpdated = await addToGitIgnore(rootPath); const isGitIgnoreUpdated = await addToGitIgnore(rootPath);
@@ -268,7 +275,7 @@ export function findProjectsFromPath(
path: string path: string
): RepoProjectConfig[] { ): RepoProjectConfig[] {
const normalizedPath = normalizePath(path); const normalizedPath = normalizePath(path);
return projects const matches = projects
.slice() .slice()
.sort(sortByDirectory) .sort(sortByDirectory)
.filter(project => { .filter(project => {
@@ -281,14 +288,9 @@ export function findProjectsFromPath(
normalizedPath.startsWith(`${project.directory}/`) normalizedPath.startsWith(`${project.directory}/`)
); );
}); });
} // 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.
* TODO: remove const firstMatch = matches[0];
*/ return matches.filter(match => match.directory === firstMatch.directory);
export function findProjectFromPath(
projects: RepoProjectConfig[],
path: string
): RepoProjectConfig | undefined {
return findProjectsFromPath(projects, path)[0];
} }

View File

@@ -74,9 +74,7 @@ export default async function getProjectByDeployment({
err.code = 'ERR_INVALID_TEAM'; err.code = 'ERR_INVALID_TEAM';
throw err; throw err;
} }
} } else if (team) {
if (team) {
const err: NodeJS.ErrnoException = new Error( const err: NodeJS.ErrnoException = new Error(
`Deployment doesn't belong to current team ${chalk.bold(contextName)}` `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]; return existingDirs[0] || possibleDirs[0];
} }
async function getProjectLink( export async function getProjectLink(
client: Client, client: Client,
path: string path: string
): Promise<ProjectLink | null> { ): Promise<ProjectLink | null> {
@@ -108,9 +108,10 @@ async function getProjectLinkFromRepoLink(
} }
if (project) { if (project) {
return { return {
repoRoot: repoLink.rootPath,
orgId: repoLink.repoConfig.orgId, orgId: repoLink.repoConfig.orgId,
projectId: project.id, projectId: project.id,
repoRoot: repoLink.rootPath, projectRootDirectory: project.directory,
}; };
} }
return null; return null;
@@ -284,6 +285,13 @@ export async function getLinkedProject(
return { status: 'linked', org, project, repoRoot: link.repoRoot }; 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( export async function linkFolderToProject(
client: Client, client: Client,
path: string, path: string,
@@ -313,10 +321,7 @@ export async function linkFolderToProject(
JSON.stringify(projectLink) JSON.stringify(projectLink)
); );
await writeFile( await writeReadme(path);
join(path, VERCEL_DIR, VERCEL_DIR_README),
await readFile(join(__dirname, 'VERCEL_DIR_README.txt'), 'utf8')
);
// update .gitignore // update .gitignore
const isGitIgnoreUpdated = await addToGitIgnore(path); 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 { join } from 'path';
import { outputJSON, readFile } from 'fs-extra';
import { VercelConfig } from '@vercel/client'; import { VercelConfig } from '@vercel/client';
import { VERCEL_DIR, VERCEL_DIR_PROJECT } from './link';
import { PartialProjectSettings } from '../input/edit-project-settings'; 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: { settings: {
createdAt: Project['createdAt']; createdAt: Project['createdAt'];
installCommand: Project['installCommand']; installCommand: Project['installCommand'];
@@ -26,7 +27,8 @@ export type ProjectLinkAndSettings = ProjectLink & {
export async function writeProjectSettings( export async function writeProjectSettings(
cwd: string, cwd: string,
project: Project, project: Project,
org: Org org: Org,
isRepoLinked: boolean
) { ) {
let analyticsId: string | undefined; let analyticsId: string | undefined;
if ( if (
@@ -39,8 +41,8 @@ export async function writeProjectSettings(
} }
const projectLinkAndSettings: ProjectLinkAndSettings = { const projectLinkAndSettings: ProjectLinkAndSettings = {
projectId: project.id, projectId: isRepoLinked ? undefined : project.id,
orgId: org.id, orgId: isRepoLinked ? undefined : org.id,
settings: { settings: {
createdAt: project.createdAt, createdAt: project.createdAt,
framework: project.framework, framework: project.framework,
@@ -60,8 +62,28 @@ export async function writeProjectSettings(
}); });
} }
export async function readProjectSettings(cwd: string) { export async function readProjectSettings(vercelDir: string) {
return await getLinkFromDir<ProjectLinkAndSettings>(cwd); 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( export function pickOverrides(

View File

@@ -322,14 +322,17 @@ test('[vercel dev] should handle missing handler errors thrown in edge functions
); );
validateResponseHeaders(res); validateResponseHeaders(res);
const { stderr } = await dev.kill(); const { stdout } = await dev.kill();
expect(await res.text()).toMatch( expect(await res.text()).toMatch(
/<strong>500<\/strong>: INTERNAL_SERVER_ERROR/g /<strong>500<\/strong>: INTERNAL_SERVER_ERROR/g
); );
expect(stderr).toMatch( const url = `http://localhost:${port}/api/edge-error-no-handler`;
/No default export was found. Add a default export to handle requests./g 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 { } finally {
await dev.kill(); 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 ?? ''); const cwd = path.join(tempRoot.name, String(tempNumber++), fixtureName ?? '');
fs.mkdirpSync(cwd); fs.mkdirpSync(cwd);
return cwd; return fs.realpathSync(cwd);
} }
export function cleanupFixtures() { export function cleanupFixtures() {

View File

@@ -1154,4 +1154,97 @@ describe('build', () => {
delete process.env.STORYBOOK_DISABLE_TELEMETRY; 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 () => { it('should reject deploying when `--prebuilt` is used and `vc build` failed before Builders', async () => {
const cwd = setupUnitFixture('build-output-api-failed-before-builds'); 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'); client.setArgv('deploy', cwd, '--prebuilt');
const exitCodePromise = deploy(client); const exitCodePromise = deploy(client);
await expect(client.stderr).toOutput( 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 () => { 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'); 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'); client.setArgv('deploy', cwd, '--prebuilt');
const exitCodePromise = deploy(client); const exitCodePromise = deploy(client);
await expect(client.stderr).toOutput( 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 () => { 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); const exitCodePromise = deploy(client);
await expect(client.stderr).toOutput( 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' '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'); useTeams('team_dummy');
useProject({ useProject({
...defaultProject, ...defaultProject,
id: 'build-output-api-preview', id: 'build-output-api-production',
name: 'build-output-api-preview', name: 'build-output-api-production',
}); });
client.setArgv('deploy', cwd, '--prebuilt'); client.setArgv('deploy', cwd, '--prebuilt');

View File

@@ -25,7 +25,9 @@ describe('env', () => {
await expect(client.stderr).toOutput( await expect(client.stderr).toOutput(
'Downloading `development` Environment Variables for Project vercel-env-pull' '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); await expect(exitCodePromise).resolves.toEqual(0);
const rawDevEnv = await fs.readFile(path.join(cwd, '.env.local')); const rawDevEnv = await fs.readFile(path.join(cwd, '.env.local'));
@@ -50,7 +52,9 @@ describe('env', () => {
await expect(client.stderr).toOutput( await expect(client.stderr).toOutput(
'Downloading `preview` Environment Variables for Project vercel-env-pull' '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); await expect(exitCodePromise).resolves.toEqual(0);
// check for Preview env vars // check for Preview env vars
@@ -86,7 +90,9 @@ describe('env', () => {
await expect(client.stderr).toOutput( await expect(client.stderr).toOutput(
'Downloading `preview` Environment Variables for Project vercel-env-pull' '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); await expect(exitCodePromise).resolves.toEqual(0);
// check for Preview env vars // check for Preview env vars
@@ -122,6 +128,7 @@ describe('env', () => {
'Downloading `development` Environment Variables for Project vercel-env-pull' 'Downloading `development` Environment Variables for Project vercel-env-pull'
); );
await expect(client.stderr).toOutput('Created other.env file'); 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); await expect(exitCodePromise).resolves.toEqual(0);
const rawDevEnv = await fs.readFile(path.join(cwd, 'other.env')); const rawDevEnv = await fs.readFile(path.join(cwd, 'other.env'));
@@ -146,7 +153,9 @@ describe('env', () => {
await expect(client.stderr).toOutput( await expect(client.stderr).toOutput(
`Downloading \`production\` Environment Variables for Project vercel-env-pull` `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); await expect(exitCodePromise).resolves.toEqual(0);
const rawProdEnv = await fs.readFile(path.join(cwd, '.env.local')); 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' 'Downloading `development` Environment Variables for Project vercel-env-pull'
); );
await expect(client.stderr).toOutput('Created other.env file'); 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); await expect(exitCodePromise).resolves.toEqual(0);
const rawDevEnv = await fs.readFile(path.join(cwd, 'other.env')); const rawDevEnv = await fs.readFile(path.join(cwd, 'other.env'));
@@ -247,7 +257,9 @@ describe('env', () => {
await expect(client.stderr).toOutput( await expect(client.stderr).toOutput(
'+ SPECIAL_FLAG (Updated)\n+ NEW_VAR\n- TEST\n' '+ 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); await expect(pullPromise).resolves.toEqual(0);
} finally { } finally {
@@ -268,7 +280,9 @@ describe('env', () => {
client.cwd = cwd; client.cwd = cwd;
client.setArgv('env', 'pull', '--yes'); client.setArgv('env', 'pull', '--yes');
const pullPromise = env(client); 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); await expect(pullPromise).resolves.toEqual(0);
}); });
@@ -284,7 +298,9 @@ describe('env', () => {
client.setArgv('env', 'pull', '--yes'); client.setArgv('env', 'pull', '--yes');
const pullPromise = env(client); const pullPromise = env(client);
await expect(client.stderr).toOutput('> No changes found.'); 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); await expect(pullPromise).resolves.toEqual(0);
}); });
@@ -321,7 +337,9 @@ describe('env', () => {
'Downloading `development` Environment Variables for Project env-pull-delta' 'Downloading `development` Environment Variables for Project env-pull-delta'
); );
await expect(client.stderr).toOutput('No changes found.\n'); 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); await expect(pullPromise).resolves.toEqual(0);
} finally { } finally {
@@ -371,5 +389,42 @@ describe('env', () => {
await env(client); 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` `Created .vercel${path.sep}.env.development.local file`
); );
await expect(client.stderr).toOutput( 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); await expect(exitCodePromise).resolves.toEqual(0);
@@ -92,7 +92,7 @@ describe('pull', () => {
`Created .vercel${path.sep}.env.development.local file` `Created .vercel${path.sep}.env.development.local file`
); );
await expect(client.stderr).toOutput( 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); await expect(exitCodePromise).resolves.toEqual(0);
@@ -130,7 +130,7 @@ describe('pull', () => {
`Created .vercel${path.sep}.env.preview.local file` `Created .vercel${path.sep}.env.preview.local file`
); );
await expect(client.stderr).toOutput( 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); await expect(exitCodePromise).resolves.toEqual(0);
@@ -161,7 +161,7 @@ describe('pull', () => {
`Created .vercel${path.sep}.env.production.local file` `Created .vercel${path.sep}.env.production.local file`
); );
await expect(client.stderr).toOutput( 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); await expect(exitCodePromise).resolves.toEqual(0);
@@ -177,4 +177,29 @@ describe('pull', () => {
.includes('SQL_CONNECTION_STRING'); .includes('SQL_CONNECTION_STRING');
expect(previewFileHasPreviewEnv2).toBeTruthy(); 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 { tmpdir } from 'node:os';
import { join, sep } from 'node:path'; import { join, sep } from 'node:path';
import { import {
findProjectFromPath, findProjectsFromPath,
findRepoRoot, findRepoRoot,
RepoProjectConfig, RepoProjectConfig,
traverseUpDirectories, traverseUpDirectories,
@@ -57,35 +57,36 @@ describe('traverseUpDirectories()', () => {
}); });
}); });
describe('findProjectFromPath()', () => { describe('findProjectsFromPath()', () => {
const projects: RepoProjectConfig[] = [ const projects: RepoProjectConfig[] = [
{ id: 'root', name: 'r', directory: '.' }, { id: 'root', name: 'r', directory: '.' },
{ id: 'site', name: 'a', directory: 'apps/site' }, { id: 'site', name: 'a', directory: 'apps/site' },
{ id: 'site2', name: 'a', directory: 'apps/site2' }, { id: 'site2', name: 'a', directory: 'apps/site2' },
{ id: 'other', name: 'b', directory: 'apps/other' }, { id: 'other', name: 'b', directory: 'apps/other' },
{ id: 'duplicate', name: 'd', directory: 'apps/other' },
{ id: 'nested', name: 'n', directory: 'apps/other/nested' }, { id: 'nested', name: 'n', directory: 'apps/other/nested' },
]; ];
it.each([ it.each([
{ id: 'root', path: '.' }, { ids: ['root'], path: '.' },
{ id: 'root', path: 'lib' }, { ids: ['root'], path: 'lib' },
{ id: 'root', path: 'lib' }, { ids: ['root'], path: 'lib' },
{ id: 'site', path: `apps${sep}site` }, { ids: ['site'], path: `apps${sep}site` },
{ id: 'site', path: `apps${sep}site` }, { ids: ['site'], path: `apps${sep}site` },
{ id: 'site', path: `apps${sep}site${sep}components` }, { ids: ['site'], path: `apps${sep}site${sep}components` },
{ id: 'site2', path: `apps${sep}site2` }, { ids: ['site2'], path: `apps${sep}site2` },
{ id: 'site2', path: `apps${sep}site2${sep}inner` }, { ids: ['site2'], path: `apps${sep}site2${sep}inner` },
{ id: 'other', path: `apps${sep}other` }, { ids: ['other', 'duplicate'], path: `apps${sep}other` },
{ id: 'other', path: `apps${sep}other${sep}lib` }, { ids: ['other', 'duplicate'], path: `apps${sep}other${sep}lib` },
{ id: 'nested', path: `apps${sep}other${sep}nested` }, { ids: ['nested'], path: `apps${sep}other${sep}nested` },
{ id: 'nested', path: `apps${sep}other${sep}nested${sep}foo` }, { ids: ['nested'], path: `apps${sep}other${sep}nested${sep}foo` },
])('should find Project "$id" for path "$path"', ({ path, id }) => { ])('should find Project "$id" for path "$path"', ({ path, ids }) => {
const actual = findProjectFromPath(projects, path); const actual = findProjectsFromPath(projects, path);
expect(actual?.id).toEqual(id); expect(actual.map(a => a.id)).toEqual(ids);
}); });
it('should return `undefined` when there are no matching Projects', () => { it('should return empty array when there are no matching Projects', () => {
const actual = findProjectFromPath([projects[1]], '.'); const actual = findProjectsFromPath([projects[1]], '.');
expect(actual).toBeUndefined(); 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 # @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 ## 12.6.1
### Patch Changes ### Patch Changes

View File

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

View File

@@ -1,5 +1,11 @@
# @vercel/fs-detectors # @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 ## 3.9.3
### Patch Changes ### Patch Changes

View File

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

View File

@@ -6,10 +6,12 @@ import { isErrnoException } from '@vercel/error-utils';
export class LocalFileSystemDetector extends DetectorFilesystem { export class LocalFileSystemDetector extends DetectorFilesystem {
private rootPath: string; private rootPath: string;
constructor(rootPath: string) { constructor(rootPath: string) {
super(); super();
this.rootPath = rootPath; this.rootPath = rootPath;
} }
async _hasPath(name: string): Promise<boolean> { async _hasPath(name: string): Promise<boolean> {
try { try {
await fs.stat(this.getFilePath(name)); await fs.stat(this.getFilePath(name));
@@ -21,13 +23,16 @@ export class LocalFileSystemDetector extends DetectorFilesystem {
throw err; throw err;
} }
} }
_readFile(name: string): Promise<Buffer> { _readFile(name: string): Promise<Buffer> {
return fs.readFile(this.getFilePath(name)); return fs.readFile(this.getFilePath(name));
} }
async _isFile(name: string): Promise<boolean> { async _isFile(name: string): Promise<boolean> {
const stat = await fs.stat(this.getFilePath(name)); const stat = await fs.stat(this.getFilePath(name));
return stat.isFile(); return stat.isFile();
} }
async _readdir(name: string): Promise<DetectorFilesystemStat[]> { async _readdir(name: string): Promise<DetectorFilesystemStat[]> {
const dirPath = this.getFilePath(name); const dirPath = this.getFilePath(name);
const dir = await fs.readdir(dirPath, { const dir = await fs.readdir(dirPath, {
@@ -44,14 +49,22 @@ export class LocalFileSystemDetector extends DetectorFilesystem {
}; };
return dir.map(dirent => ({ return dir.map(dirent => ({
name: dirent.name, name: dirent.name,
path: path.join(dirPath, dirent.name), path: path.join(this.getRelativeFilePath(name), dirent.name),
type: getType(dirent), type: getType(dirent),
})); }));
} }
_chdir(name: string): DetectorFilesystem { _chdir(name: string): DetectorFilesystem {
return new LocalFileSystemDetector(this.getFilePath(name)); 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) { 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 path from 'path';
import { LocalFileSystemDetector } from '../src';
import { detectFramework } from '../src/detect-framework'; import { detectFramework } from '../src/detect-framework';
import monorepoManagers from '../src/monorepos/monorepo-managers'; import monorepoManagers from '../src/monorepos/monorepo-managers';
import { FixtureFilesystem } from './utils/fixture-filesystem';
describe('monorepo-managers', () => { describe('monorepo-managers', () => {
describe.each([ describe.each([
@@ -17,7 +17,7 @@ describe('monorepo-managers', () => {
it(testName, async () => { it(testName, async () => {
const fixture = path.join(__dirname, 'fixtures', fixturePath); const fixture = path.join(__dirname, 'fixtures', fixturePath);
const fs = new FixtureFilesystem(fixture); const fs = new LocalFileSystemDetector(fixture);
const result = await detectFramework({ const result = await detectFramework({
fs, fs,

View File

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

View File

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

View File

@@ -1,13 +1,12 @@
import frameworkList from '@vercel/frameworks'; import frameworkList from '@vercel/frameworks';
import { detectFramework } from '../src'; import { detectFramework, LocalFileSystemDetector } from '../src';
import { FixtureFilesystem } from './utils/fixture-filesystem';
import { getExamples } from '../../../examples/__tests__/test-utils'; import { getExamples } from '../../../examples/__tests__/test-utils';
describe('examples should be detected', () => { describe('examples should be detected', () => {
it.each(getExamples())( it.each(getExamples())(
'should detect $exampleName', 'should detect $exampleName',
async ({ exampleName, examplePath }) => { async ({ exampleName, examplePath }) => {
const fs = new FixtureFilesystem(examplePath); const fs = new LocalFileSystemDetector(examplePath);
const framework = await detectFramework({ fs, frameworkList }); const framework = await detectFramework({ fs, frameworkList });
if (!framework) { if (!framework) {
throw new Error(`Framework not detected for example "${exampleName}".`); 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 { import {
getMonorepoDefaultSettings, getMonorepoDefaultSettings,
LocalFileSystemDetector, LocalFileSystemDetector,
MissingBuildPipeline, MissingBuildPipeline,
MissingBuildTarget, MissingBuildTarget,
} from '../src'; } from '../src';
import path from 'path';
import fs from 'fs';
import os from 'os';
import { FixtureFilesystem } from './utils/fixture-filesystem';
describe('getMonorepoDefaultSettings', () => { describe('getMonorepoDefaultSettings', () => {
test('MissingBuildTarget is an error', () => { test('MissingBuildTarget is an error', () => {
@@ -69,7 +68,7 @@ describe('getMonorepoDefaultSettings', () => {
}, },
}; };
const ffs = new FixtureFilesystem( const fs = new LocalFileSystemDetector(
path.join( path.join(
__dirname, __dirname,
'fixtures', 'fixtures',
@@ -81,16 +80,16 @@ describe('getMonorepoDefaultSettings', () => {
packageName, packageName,
isRoot ? '/' : 'packages/app-1', isRoot ? '/' : 'packages/app-1',
isRoot ? '/' : '../..', isRoot ? '/' : '../..',
ffs fs
); );
expect(result).toStrictEqual(expectedResultMap[expectedResultKey]); expect(result).toStrictEqual(expectedResultMap[expectedResultKey]);
} }
); );
test('returns null when neither nx nor turbo is detected', async () => { test('returns null when neither nx nor turbo is detected', async () => {
const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'monorepo-test-')); const dir = mkdtempSync(path.join(os.tmpdir(), 'monorepo-test-'));
const lfs = new LocalFileSystemDetector(dir); const fs = new LocalFileSystemDetector(dir);
const result = await getMonorepoDefaultSettings('', '', '', lfs); const result = await getMonorepoDefaultSettings('', '', '', fs);
expect(result).toBe(null); expect(result).toBe(null);
}); });
}); });

View File

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

View File

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

View File

@@ -1,6 +1,6 @@
import path from 'path'; import path from 'path';
import { LocalFileSystemDetector } from '../src';
import { getWorkspaces, Workspace } from '../src/workspaces/get-workspaces'; import { getWorkspaces, Workspace } from '../src/workspaces/get-workspaces';
import { FixtureFilesystem } from './utils/fixture-filesystem';
describe.each<[string, Workspace[]]>([ describe.each<[string, Workspace[]]>([
['21-npm-workspaces', [{ type: 'npm', rootPath: '/' }]], ['21-npm-workspaces', [{ type: 'npm', rootPath: '/' }]],
@@ -34,7 +34,7 @@ describe.each<[string, Workspace[]]>([
it(testName, async () => { it(testName, async () => {
const fixture = path.join(__dirname, 'fixtures', fixturePath); const fixture = path.join(__dirname, 'fixtures', fixturePath);
const fs = new FixtureFilesystem(fixture); const fs = new LocalFileSystemDetector(fixture);
const actualWorkspaces = await getWorkspaces({ fs }); 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 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 files = ['foo', 'bar'];
const filePaths = dirs.flatMap(dir => files.map(file => path.join(dir, file))); const filePaths = dirs.flatMap(dir => files.map(file => path.join(dir, file)));
@@ -63,12 +63,7 @@ describe('LocalFileSystemDetector', () => {
const readdirResults = await Promise.all( const readdirResults = await Promise.all(
dirs.map(dir => localFileSystem.readdir(dir)) dirs.map(dir => localFileSystem.readdir(dir))
); );
const expectedPaths = [ const expectedPaths = [...dirs, ...filePaths].sort().slice(1); // drop the first path since its the root
...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 actualPaths = readdirResults const actualPaths = readdirResults
.flatMap(result => result.map(stat => stat.path)) .flatMap(result => result.map(stat => stat.path))
.sort(); .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 # @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 ## 1.3.6
### Patch Changes ### Patch Changes

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,5 +1,26 @@
# @vercel/node # @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 ## 2.14.4
### Patch Changes ### Patch Changes

View File

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

View File

@@ -10,14 +10,14 @@ function getUrl(url, headers) {
return urlObj.toString(); return urlObj.toString();
} }
async function respond(userEdgeHandler, event, options, dependencies) { async function respond(handler, event, options, dependencies) {
const { Request, Response } = dependencies; const { Request, Response } = dependencies;
const { isMiddleware } = options; const { isMiddleware } = options;
event.request.headers.set( event.request.headers.set(
'host', 'host',
event.request.headers.get('x-forwarded-host') event.request.headers.get('x-forwarded-host')
); );
let response = await userEdgeHandler( let response = await handler(
new Request( new Request(
getUrl(event.request.url, event.request.headers), getUrl(event.request.url, event.request.headers),
event.request event.request
@@ -62,16 +62,34 @@ async function parseRequestEvent(event) {
// This will be invoked by logic using this template // This will be invoked by logic using this template
// eslint-disable-next-line @typescript-eslint/no-unused-vars // 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 => { addEventListener('fetch', async event => {
try { try {
const response = await respond( if (typeof module.default === 'function') {
userEdgeHandler, handler = module.default;
event, } else {
options, if (
dependencies ['GET', 'HEAD', 'OPTIONS', 'POST', 'PUT', 'DELETE', 'PATCH'].some(
); method => typeof module[method] === 'function'
return event.respondWith(response); )
) {
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) { } catch (error) {
event.respondWith(toResponseError(error, dependencies.Response)); event.respondWith(toResponseError(error, dependencies.Response));
} }

View File

@@ -89,12 +89,7 @@ async function compileUserCode(
// user code // user code
${compiledFile.text}; ${compiledFile.text};
const userEdgeHandler = module.exports.default; const userModule = module.exports;
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'
);
}
// request metadata // request metadata
const isMiddleware = ${isMiddleware}; const isMiddleware = ${isMiddleware};
@@ -104,7 +99,7 @@ async function compileUserCode(
${edgeHandlerTemplate}; ${edgeHandlerTemplate};
const dependencies = { Request, Response }; const dependencies = { Request, Response };
const options = { isMiddleware, entrypointLabel }; const options = { isMiddleware, entrypointLabel };
registerFetchListener(userEdgeHandler, options, dependencies); registerFetchListener(userModule, options, dependencies);
`; `;
return { return {

View File

@@ -506,6 +506,7 @@ export const build: BuildV3 = async ({
shouldAddSourcemapSupport, shouldAddSourcemapSupport,
awsLambdaHandler, awsLambdaHandler,
supportsResponseStreaming, 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) { function getBodyParser(body: Buffer, contentType: string | undefined) {
return function parseBody(): VercelRequestBody { return function parseBody(): VercelRequestBody {
const { parse: parseContentType } = require('content-type'); const type = normalizeContentType(contentType);
const { type } = parseContentType(contentType);
if (type === 'application/json') { if (type === 'application/json') {
try { try {

View File

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

View File

@@ -59,7 +59,12 @@ export function entrypointToOutputPath(
} }
export function logError(error: Error) { 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) { if (error.stack) {
// only show the stack trace if debug is enabled // only show the stack trace if debug is enabled
// because it points to internals, not user code // 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); jest.setTimeout(20 * 1000);
const [NODE_MAJOR] = process.versions.node.split('.').map(v => Number(v));
function testForkDevServer(entrypoint: string) { function testForkDevServer(entrypoint: string) {
const ext = extname(entrypoint); const ext = extname(entrypoint);
const isTypeScript = ext === '.ts'; 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 () => { test('runs an edge function that uses `WebSocket`', async () => {
const child = testForkDevServer('./edge-websocket.js'); const child = testForkDevServer('./edge-websocket.js');
try { try {
@@ -57,9 +94,8 @@ test('runs an edge function that uses `buffer`', async () => {
throw new Error('Exited. error: ' + JSON.stringify(result.value)); throw new Error('Exited. error: ' + JSON.stringify(result.value));
} }
const response = await fetch( const { address, port } = result.value;
`http://localhost:${result.value.port}/api/edge-buffer` const response = await fetch(`http://${address}:${port}/api/edge-buffer`);
);
expect({ expect({
status: response.status, status: response.status,
json: await response.json(), json: await response.json(),
@@ -84,9 +120,8 @@ test('runs a mjs endpoint', async () => {
throw new Error('Exited. error: ' + JSON.stringify(result.value)); throw new Error('Exited. error: ' + JSON.stringify(result.value));
} }
const response = await fetch( const { address, port } = result.value;
`http://localhost:${result.value.port}/api/hello` const response = await fetch(`http://${address}:${port}/api/hello`);
);
expect({ expect({
status: response.status, status: response.status,
headers: Object.fromEntries(response.headers), 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)); throw new Error('Exited. error: ' + JSON.stringify(result.value));
} }
const response = await fetch( const { address, port } = result.value;
`http://localhost:${result.value.port}/api/hello` const response = await fetch(`http://${address}:${port}/api/hello`);
);
expect({ expect({
status: response.status, status: response.status,
headers: Object.fromEntries(response.headers), 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)}`); throw new Error(`Exited. error: ${JSON.stringify(result.value)}`);
} }
const response = await fetch( const { address, port } = result.value;
`http://localhost:${result.value.port}/api/hello` const response = await fetch(`http://${address}:${port}/api/hello`);
);
expect({ expect({
status: response.status, status: response.status,
text: await response.text(), text: await response.text(),

View File

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

View File

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

View File

@@ -1,5 +1,18 @@
# @vercel/remix-builder # @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 ## 1.8.11
### Patch Changes ### Patch Changes

View File

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

View File

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

View File

@@ -1,5 +1,26 @@
# @vercel/static-build # @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 ## 1.3.33
### Patch Changes ### Patch Changes

View File

@@ -1,6 +1,6 @@
{ {
"name": "@vercel/static-build", "name": "@vercel/static-build",
"version": "1.3.33", "version": "1.3.36",
"license": "Apache-2.0", "license": "Apache-2.0",
"main": "./dist/index", "main": "./dist/index",
"homepage": "https://vercel.com/docs/build-step", "homepage": "https://vercel.com/docs/build-step",
@@ -20,7 +20,7 @@
}, },
"dependencies": { "dependencies": {
"@vercel/gatsby-plugin-vercel-analytics": "1.0.10", "@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": { "devDependencies": {
"@types/aws-lambda": "8.10.64", "@types/aws-lambda": "8.10.64",
@@ -32,10 +32,10 @@
"@types/node-fetch": "2.5.4", "@types/node-fetch": "2.5.4",
"@types/promise-timeout": "1.3.0", "@types/promise-timeout": "1.3.0",
"@types/semver": "7.3.13", "@types/semver": "7.3.13",
"@vercel/build-utils": "6.7.4", "@vercel/build-utils": "6.7.5",
"@vercel/error-utils": "1.0.10", "@vercel/error-utils": "1.0.10",
"@vercel/frameworks": "1.4.2", "@vercel/frameworks": "1.4.2",
"@vercel/fs-detectors": "3.9.3", "@vercel/fs-detectors": "4.0.0",
"@vercel/ncc": "0.24.0", "@vercel/ncc": "0.24.0",
"@vercel/routing-utils": "2.2.1", "@vercel/routing-utils": "2.2.1",
"@vercel/static-config": "2.0.17", "@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 = { export const config = {
runtime: 'deno', runtime: 'deno',
location: 'https://example.com/page', location: 'https://example.com/page',
maxDuration: 60
}; };
export default async ({ request }: Deno.RequestEvent) => { export default async ({ request }: Deno.RequestEvent) => {

View File

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

View File

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

View File

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

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