Compare commits

...

59 Commits

Author SHA1 Message Date
Vercel Release Bot
acc10e47c7 Version Packages (#10123)
This PR was opened by the [Changesets
release](https://github.com/changesets/action) GitHub action. When
you're ready to do a release, you can merge this and the packages will
be published to npm automatically. If you're not ready to do a release
yet, that's fine, whenever you add more changesets to main, this PR will
be updated.


# Releases
## vercel@31.0.0

### Major Changes

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

### Patch Changes

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

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

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

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

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

## @vercel/build-utils@6.8.0

### Minor Changes

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

## @vercel/client@12.6.3

### Patch Changes

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

## @vercel/frameworks@1.4.3

### Patch Changes

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

## @vercel/fs-detectors@4.0.1

### Patch Changes

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

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

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

### Patch Changes

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

## @vercel/next@3.8.7

### Patch Changes

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

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

## @vercel/node@2.15.3

### Patch Changes

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

## @vercel/remix-builder@1.8.15

### Patch Changes

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

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

## @vercel/static-build@1.3.38

### Patch Changes

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

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

## @vercel-internals/constants@1.0.3

### Patch Changes

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

## @vercel-internals/types@1.0.3

### Patch Changes

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


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

Consider the following:

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

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

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

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

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

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

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

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

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

to a better error

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

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

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

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

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

---------

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

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

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

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

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

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

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

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

Fixing:

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

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

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

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

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

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

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

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

---------

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

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

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

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

---------

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

4
.github/CODEOWNERS vendored
View File

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

View File

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

View File

@@ -8,17 +8,17 @@
"name": "nextjs",
"version": "0.1.0",
"dependencies": {
"eslint": "8.41.0",
"eslint-config-next": "13.4.4",
"next": "13.4.4",
"eslint": "8.43.0",
"eslint-config-next": "13.4.7",
"next": "13.4.7",
"react": "18.2.0",
"react-dom": "18.2.0"
}
},
"node_modules/@babel/runtime": {
"version": "7.21.5",
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.21.5.tgz",
"integrity": "sha512-8jI69toZqqcsnqGGqwGS4Qb1VwLOEp4hz+CXPywcvjs60u3B4Pom/U/7rm4W8tMOYEB+E9wgD0mW1l3r8qlI9Q==",
"version": "7.22.5",
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.22.5.tgz",
"integrity": "sha512-ecjvYlnAaZ/KVneE/OdKYBYfgXV3Ptu6zQWmgEF7vwKhQnvVS6bjMD2XYgj+SNvQ1GfK/pjgokfPkC/2CO8CuA==",
"dependencies": {
"regenerator-runtime": "^0.13.11"
},
@@ -71,17 +71,17 @@
}
},
"node_modules/@eslint/js": {
"version": "8.41.0",
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.41.0.tgz",
"integrity": "sha512-LxcyMGxwmTh2lY9FwHPGWOHmYFCZvbrFCBZL4FzSSsxsRPuhrYUg/49/0KDfW8tnIEaEHtfmn6+NPN+1DqaNmA==",
"version": "8.43.0",
"resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.43.0.tgz",
"integrity": "sha512-s2UHCoiXfxMvmfzqoN+vrQ84ahUSYde9qNO1MdxmoEhyHWsfmwOpFlwYV+ePJEVc7gFnATGUi376WowX1N7tFg==",
"engines": {
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
}
},
"node_modules/@humanwhocodes/config-array": {
"version": "0.11.8",
"resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.8.tgz",
"integrity": "sha512-UybHIJzJnR5Qc/MsD9Kr+RpO2h+/P1GhOwdiLPXK5TWk5sgTdu88bTD9UP+CKbPPh5Rni1u0GjAdYQLemG8g+g==",
"version": "0.11.10",
"resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.10.tgz",
"integrity": "sha512-KVVjQmNUepDVGXNuoRRdmmEjruj0KfiGSbS8LVc12LMsWDQzRXJ0qdhN8L8uUigKpfEHRhlaQFY0ib1tnUbNeQ==",
"dependencies": {
"@humanwhocodes/object-schema": "^1.2.1",
"debug": "^4.1.1",
@@ -109,22 +109,22 @@
"integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA=="
},
"node_modules/@next/env": {
"version": "13.4.4",
"resolved": "https://registry.npmjs.org/@next/env/-/env-13.4.4.tgz",
"integrity": "sha512-q/y7VZj/9YpgzDe64Zi6rY1xPizx80JjlU2BTevlajtaE3w1LqweH1gGgxou2N7hdFosXHjGrI4OUvtFXXhGLg=="
"version": "13.4.7",
"resolved": "https://registry.npmjs.org/@next/env/-/env-13.4.7.tgz",
"integrity": "sha512-ZlbiFulnwiFsW9UV1ku1OvX/oyIPLtMk9p/nnvDSwI0s7vSoZdRtxXNsaO+ZXrLv/pMbXVGq4lL8TbY9iuGmVw=="
},
"node_modules/@next/eslint-plugin-next": {
"version": "13.4.4",
"resolved": "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-13.4.4.tgz",
"integrity": "sha512-5jnh7q6I15efnjR/rR+/TGTc9hn53g3JTbEjAMjmeQiExKqEUgIXqrHI5zlTNlNyzCPkBB860/ctxXheZaF2Vw==",
"version": "13.4.7",
"resolved": "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-13.4.7.tgz",
"integrity": "sha512-ANEPltxzXbyyG7CvqxdY4PmeM5+RyWdAJGufTHnU+LA/i3J6IDV2r8Z4onKwskwKEhwqzz5lMaSYGGXLyHX+mg==",
"dependencies": {
"glob": "7.1.7"
}
},
"node_modules/@next/swc-darwin-arm64": {
"version": "13.4.4",
"resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-13.4.4.tgz",
"integrity": "sha512-xfjgXvp4KalNUKZMHmsFxr1Ug+aGmmO6NWP0uoh4G3WFqP/mJ1xxfww0gMOeMeSq/Jyr5k7DvoZ2Pv+XOITTtw==",
"version": "13.4.7",
"resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-13.4.7.tgz",
"integrity": "sha512-VZTxPv1b59KGiv/pZHTO5Gbsdeoxcj2rU2cqJu03btMhHpn3vwzEK0gUSVC/XW96aeGO67X+cMahhwHzef24/w==",
"cpu": [
"arm64"
],
@@ -137,9 +137,9 @@
}
},
"node_modules/@next/swc-darwin-x64": {
"version": "13.4.4",
"resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-13.4.4.tgz",
"integrity": "sha512-ZY9Ti1hkIwJsxGus3nlubIkvYyB0gNOYxKrfsOrLEqD0I2iCX8D7w8v6QQZ2H+dDl6UT29oeEUdDUNGk4UEpfg==",
"version": "13.4.7",
"resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-13.4.7.tgz",
"integrity": "sha512-gO2bw+2Ymmga+QYujjvDz9955xvYGrWofmxTq7m70b9pDPvl7aDFABJOZ2a8SRCuSNB5mXU8eTOmVVwyp/nAew==",
"cpu": [
"x64"
],
@@ -152,9 +152,9 @@
}
},
"node_modules/@next/swc-linux-arm64-gnu": {
"version": "13.4.4",
"resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-13.4.4.tgz",
"integrity": "sha512-+KZnDeMShYkpkqAvGCEDeqYTRADJXc6SY1jWXz+Uo6qWQO/Jd9CoyhTJwRSxvQA16MoYzvILkGaDqirkRNctyA==",
"version": "13.4.7",
"resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-13.4.7.tgz",
"integrity": "sha512-6cqp3vf1eHxjIDhEOc7Mh/s8z1cwc/l5B6ZNkOofmZVyu1zsbEM5Hmx64s12Rd9AYgGoiCz4OJ4M/oRnkE16/Q==",
"cpu": [
"arm64"
],
@@ -167,9 +167,9 @@
}
},
"node_modules/@next/swc-linux-arm64-musl": {
"version": "13.4.4",
"resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-13.4.4.tgz",
"integrity": "sha512-evC1twrny2XDT4uOftoubZvW3EG0zs0ZxMwEtu/dDGVRO5n5pT48S8qqEIBGBUZYu/Xx4zzpOkIxx1vpWdE+9A==",
"version": "13.4.7",
"resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-13.4.7.tgz",
"integrity": "sha512-T1kD2FWOEy5WPidOn1si0rYmWORNch4a/NR52Ghyp4q7KyxOCuiOfZzyhVC5tsLIBDH3+cNdB5DkD9afpNDaOw==",
"cpu": [
"arm64"
],
@@ -182,9 +182,9 @@
}
},
"node_modules/@next/swc-linux-x64-gnu": {
"version": "13.4.4",
"resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-13.4.4.tgz",
"integrity": "sha512-PX706XcCHr2FfkyhP2lpf+pX/tUvq6/ke7JYnnr0ykNdEMo+sb7cC/o91gnURh4sPYSiZJhsF2gbIqg9rciOHQ==",
"version": "13.4.7",
"resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-13.4.7.tgz",
"integrity": "sha512-zaEC+iEiAHNdhl6fuwl0H0shnTzQoAoJiDYBUze8QTntE/GNPfTYpYboxF5LRYIjBwETUatvE0T64W6SKDipvg==",
"cpu": [
"x64"
],
@@ -197,9 +197,9 @@
}
},
"node_modules/@next/swc-linux-x64-musl": {
"version": "13.4.4",
"resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-13.4.4.tgz",
"integrity": "sha512-TKUUx3Ftd95JlHV6XagEnqpT204Y+IsEa3awaYIjayn0MOGjgKZMZibqarK3B1FsMSPaieJf2FEAcu9z0yT5aA==",
"version": "13.4.7",
"resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-13.4.7.tgz",
"integrity": "sha512-X6r12F8d8SKAtYJqLZBBMIwEqcTRvUdVm+xIq+l6pJqlgT2tNsLLf2i5Cl88xSsIytBICGsCNNHd+siD2fbWBA==",
"cpu": [
"x64"
],
@@ -212,9 +212,9 @@
}
},
"node_modules/@next/swc-win32-arm64-msvc": {
"version": "13.4.4",
"resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-13.4.4.tgz",
"integrity": "sha512-FP8AadgSq4+HPtim7WBkCMGbhr5vh9FePXiWx9+YOdjwdQocwoCK5ZVC3OW8oh3TWth6iJ0AXJ/yQ1q1cwSZ3A==",
"version": "13.4.7",
"resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-13.4.7.tgz",
"integrity": "sha512-NPnmnV+vEIxnu6SUvjnuaWRglZzw4ox5n/MQTxeUhb5iwVWFedolPFebMNwgrWu4AELwvTdGtWjqof53AiWHcw==",
"cpu": [
"arm64"
],
@@ -227,9 +227,9 @@
}
},
"node_modules/@next/swc-win32-ia32-msvc": {
"version": "13.4.4",
"resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-13.4.4.tgz",
"integrity": "sha512-3WekVmtuA2MCdcAOrgrI+PuFiFURtSyyrN1I3UPtS0ckR2HtLqyqmS334Eulf15g1/bdwMteePdK363X/Y9JMg==",
"version": "13.4.7",
"resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-13.4.7.tgz",
"integrity": "sha512-6Hxijm6/a8XqLQpOOf/XuwWRhcuc/g4rBB2oxjgCMuV9Xlr2bLs5+lXyh8w9YbAUMYR3iC9mgOlXbHa79elmXw==",
"cpu": [
"ia32"
],
@@ -242,9 +242,9 @@
}
},
"node_modules/@next/swc-win32-x64-msvc": {
"version": "13.4.4",
"resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-13.4.4.tgz",
"integrity": "sha512-AHRITu/CrlQ+qzoqQtEMfaTu7GHaQ6bziQln/pVWpOYC1wU+Mq6VQQFlsDtMCnDztPZtppAXdvvbNS7pcfRzlw==",
"version": "13.4.7",
"resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-13.4.7.tgz",
"integrity": "sha512-sW9Yt36Db1nXJL+mTr2Wo0y+VkPWeYhygvcHj1FF0srVtV+VoDjxleKtny21QHaG05zdeZnw2fCtf2+dEqgwqA==",
"cpu": [
"x64"
],
@@ -308,9 +308,9 @@
}
},
"node_modules/@rushstack/eslint-patch": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.3.0.tgz",
"integrity": "sha512-IthPJsJR85GhOkp3Hvp8zFOPK5ynKn6STyHa/WZpioK7E1aYDiBzpqQPrngc14DszIUkIrdd3k9Iu0XSzlP/1w=="
"version": "1.3.2",
"resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.3.2.tgz",
"integrity": "sha512-V+MvGwaHH03hYhY+k6Ef/xKd6RYlc4q8WBx+2ANmipHJcKuktNcI/NgEsJgdSUF6Lw32njT6OnrRsKYCdgHjYw=="
},
"node_modules/@swc/helpers": {
"version": "0.5.1",
@@ -326,13 +326,13 @@
"integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ=="
},
"node_modules/@typescript-eslint/parser": {
"version": "5.59.7",
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.59.7.tgz",
"integrity": "sha512-VhpsIEuq/8i5SF+mPg9jSdIwgMBBp0z9XqjiEay+81PYLJuroN+ET1hM5IhkiYMJd9MkTz8iJLt7aaGAgzWUbQ==",
"version": "5.60.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.60.0.tgz",
"integrity": "sha512-jBONcBsDJ9UoTWrARkRRCgDz6wUggmH5RpQVlt7BimSwaTkTjwypGzKORXbR4/2Hqjk9hgwlon2rVQAjWNpkyQ==",
"dependencies": {
"@typescript-eslint/scope-manager": "5.59.7",
"@typescript-eslint/types": "5.59.7",
"@typescript-eslint/typescript-estree": "5.59.7",
"@typescript-eslint/scope-manager": "5.60.0",
"@typescript-eslint/types": "5.60.0",
"@typescript-eslint/typescript-estree": "5.60.0",
"debug": "^4.3.4"
},
"engines": {
@@ -352,12 +352,12 @@
}
},
"node_modules/@typescript-eslint/scope-manager": {
"version": "5.59.7",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.59.7.tgz",
"integrity": "sha512-FL6hkYWK9zBGdxT2wWEd2W8ocXMu3K94i3gvMrjXpx+koFYdYV7KprKfirpgY34vTGzEPPuKoERpP8kD5h7vZQ==",
"version": "5.60.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.60.0.tgz",
"integrity": "sha512-hakuzcxPwXi2ihf9WQu1BbRj1e/Pd8ZZwVTG9kfbxAMZstKz8/9OoexIwnmLzShtsdap5U/CoQGRCWlSuPbYxQ==",
"dependencies": {
"@typescript-eslint/types": "5.59.7",
"@typescript-eslint/visitor-keys": "5.59.7"
"@typescript-eslint/types": "5.60.0",
"@typescript-eslint/visitor-keys": "5.60.0"
},
"engines": {
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
@@ -368,9 +368,9 @@
}
},
"node_modules/@typescript-eslint/types": {
"version": "5.59.7",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.59.7.tgz",
"integrity": "sha512-UnVS2MRRg6p7xOSATscWkKjlf/NDKuqo5TdbWck6rIRZbmKpVNTLALzNvcjIfHBE7736kZOFc/4Z3VcZwuOM/A==",
"version": "5.60.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.60.0.tgz",
"integrity": "sha512-ascOuoCpNZBccFVNJRSC6rPq4EmJ2NkuoKnd6LDNyAQmdDnziAtxbCGWCbefG1CNzmDvd05zO36AmB7H8RzKPA==",
"engines": {
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
},
@@ -380,12 +380,12 @@
}
},
"node_modules/@typescript-eslint/typescript-estree": {
"version": "5.59.7",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.59.7.tgz",
"integrity": "sha512-4A1NtZ1I3wMN2UGDkU9HMBL+TIQfbrh4uS0WDMMpf3xMRursDbqEf1ahh6vAAe3mObt8k3ZATnezwG4pdtWuUQ==",
"version": "5.60.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.60.0.tgz",
"integrity": "sha512-R43thAuwarC99SnvrBmh26tc7F6sPa2B3evkXp/8q954kYL6Ro56AwASYWtEEi+4j09GbiNAHqYwNNZuNlARGQ==",
"dependencies": {
"@typescript-eslint/types": "5.59.7",
"@typescript-eslint/visitor-keys": "5.59.7",
"@typescript-eslint/types": "5.60.0",
"@typescript-eslint/visitor-keys": "5.60.0",
"debug": "^4.3.4",
"globby": "^11.1.0",
"is-glob": "^4.0.3",
@@ -406,11 +406,11 @@
}
},
"node_modules/@typescript-eslint/visitor-keys": {
"version": "5.59.7",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.59.7.tgz",
"integrity": "sha512-tyN+X2jvMslUszIiYbF0ZleP+RqQsFVpGrKI6e0Eet1w8WmhsAtmzaqm8oM8WJQ1ysLwhnsK/4hYHJjOgJVfQQ==",
"version": "5.60.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.60.0.tgz",
"integrity": "sha512-wm9Uz71SbCyhUKgcaPRauBdTegUyY/ZWl8gLwD/i/ybJqscrrdVSFImpvUz16BLPChIeKBK5Fa9s6KDQjsjyWw==",
"dependencies": {
"@typescript-eslint/types": "5.59.7",
"@typescript-eslint/types": "5.60.0",
"eslint-visitor-keys": "^3.3.0"
},
"engines": {
@@ -422,9 +422,9 @@
}
},
"node_modules/acorn": {
"version": "8.8.2",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz",
"integrity": "sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==",
"version": "8.9.0",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.9.0.tgz",
"integrity": "sha512-jaVNAFBHNLXspO543WnNNPZFRtavh3skAkITqD0/2aeMkKZTN+254PyhwxFYrk3vQ1xfY+2wbesJMs/JC8/PwQ==",
"bin": {
"acorn": "bin/acorn"
},
@@ -483,11 +483,11 @@
"integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="
},
"node_modules/aria-query": {
"version": "5.1.3",
"resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.1.3.tgz",
"integrity": "sha512-R5iJ5lkuHybztUfuOAznmboyjWq8O6sqNqtK7CLOqdydi54VNbORp49mb14KbWgG1QD3JFO9hJdZ+y4KutfdOQ==",
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.2.1.tgz",
"integrity": "sha512-7uFg4b+lETFgdaJyETnILsXgnnzVnkHcgRbwbPwevm5x/LmUlt3MjczMRe1zg824iBgXZNRPTBftNYyRSKLp2g==",
"dependencies": {
"deep-equal": "^2.0.5"
"dequal": "^2.0.3"
}
},
"node_modules/array-buffer-byte-length": {
@@ -599,11 +599,11 @@
}
},
"node_modules/axobject-query": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-3.1.1.tgz",
"integrity": "sha512-goKlv8DZrK9hUh975fnHzhNIO4jUnFCfv/dszV5VwUGDFjI6vQ2VwoyjYjYNEbBE8AH87TduWP5uyDR1D+Iteg==",
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-3.2.1.tgz",
"integrity": "sha512-jsyHu61e6N4Vbz/v18DHwWYKK0bSWLqn47eeDSKPB7m8tqMHF9YJ+mhIk2lVteyZrY8tnSj/jHOv4YiTCuCJgg==",
"dependencies": {
"deep-equal": "^2.0.5"
"dequal": "^2.0.3"
}
},
"node_modules/balanced-match": {
@@ -696,9 +696,9 @@
}
},
"node_modules/caniuse-lite": {
"version": "1.0.30001489",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001489.tgz",
"integrity": "sha512-x1mgZEXK8jHIfAxm+xgdpHpk50IN3z3q3zP261/WS+uvePxW8izXuCu6AHz0lkuYTlATDehiZ/tNyYBdSQsOUQ==",
"version": "1.0.30001506",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001506.tgz",
"integrity": "sha512-6XNEcpygZMCKaufIcgpQNZNf00GEqc7VQON+9Rd0K1bMYo8xhMZRAo5zpbnbMNizi4YNgIDAFrdykWsvY3H4Hw==",
"funding": [
{
"type": "opencollective",
@@ -789,34 +789,6 @@
}
}
},
"node_modules/deep-equal": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-2.2.1.tgz",
"integrity": "sha512-lKdkdV6EOGoVn65XaOsPdH4rMxTZOnmFyuIkMjM1i5HHCbfjC97dawgTAy0deYNfuqUqW+Q5VrVaQYtUpSd6yQ==",
"dependencies": {
"array-buffer-byte-length": "^1.0.0",
"call-bind": "^1.0.2",
"es-get-iterator": "^1.1.3",
"get-intrinsic": "^1.2.0",
"is-arguments": "^1.1.1",
"is-array-buffer": "^3.0.2",
"is-date-object": "^1.0.5",
"is-regex": "^1.1.4",
"is-shared-array-buffer": "^1.0.2",
"isarray": "^2.0.5",
"object-is": "^1.1.5",
"object-keys": "^1.1.1",
"object.assign": "^4.1.4",
"regexp.prototype.flags": "^1.5.0",
"side-channel": "^1.0.4",
"which-boxed-primitive": "^1.0.2",
"which-collection": "^1.0.1",
"which-typed-array": "^1.1.9"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/deep-is": {
"version": "0.1.4",
"resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz",
@@ -880,6 +852,14 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/dequal": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz",
"integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==",
"engines": {
"node": ">=6"
}
},
"node_modules/dir-glob": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz",
@@ -908,9 +888,9 @@
"integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg=="
},
"node_modules/enhanced-resolve": {
"version": "5.14.1",
"resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.14.1.tgz",
"integrity": "sha512-Vklwq2vDKtl0y/vtwjSesgJ5MYS7Etuk5txS8VdKL4AOS1aUlD96zqIfsOSLQsdv3xgMRbtkWM8eG9XDfKUPow==",
"version": "5.15.0",
"resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.15.0.tgz",
"integrity": "sha512-LXYT42KJ7lpIKECr2mAXIaMldcNCh/7E0KBKOu4KSfkHmP+mZmSs+8V5gBAqisWBy0OO4W5Oyys0GO1Y8KtdKg==",
"dependencies": {
"graceful-fs": "^4.2.4",
"tapable": "^2.2.0"
@@ -966,25 +946,6 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/es-get-iterator": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/es-get-iterator/-/es-get-iterator-1.1.3.tgz",
"integrity": "sha512-sPZmqHBe6JIiTfN5q2pEi//TwxmAFHwj/XEuYjTuse78i8KxaqMTTzxPoFKuzRpDpTJ+0NAbpfenkmH2rePtuw==",
"dependencies": {
"call-bind": "^1.0.2",
"get-intrinsic": "^1.1.3",
"has-symbols": "^1.0.3",
"is-arguments": "^1.1.1",
"is-map": "^2.0.2",
"is-set": "^2.0.2",
"is-string": "^1.0.7",
"isarray": "^2.0.5",
"stop-iteration-iterator": "^1.0.0"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/es-set-tostringtag": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.1.tgz",
@@ -1034,15 +995,15 @@
}
},
"node_modules/eslint": {
"version": "8.41.0",
"resolved": "https://registry.npmjs.org/eslint/-/eslint-8.41.0.tgz",
"integrity": "sha512-WQDQpzGBOP5IrXPo4Hc0814r4/v2rrIsB0rhT7jtunIalgg6gYXWhRMOejVO8yH21T/FGaxjmFjBMNqcIlmH1Q==",
"version": "8.43.0",
"resolved": "https://registry.npmjs.org/eslint/-/eslint-8.43.0.tgz",
"integrity": "sha512-aaCpf2JqqKesMFGgmRPessmVKjcGXqdlAYLLC3THM8t5nBRZRQ+st5WM/hoJXkdioEXLLbXgclUpM0TXo5HX5Q==",
"dependencies": {
"@eslint-community/eslint-utils": "^4.2.0",
"@eslint-community/regexpp": "^4.4.0",
"@eslint/eslintrc": "^2.0.3",
"@eslint/js": "8.41.0",
"@humanwhocodes/config-array": "^0.11.8",
"@eslint/js": "8.43.0",
"@humanwhocodes/config-array": "^0.11.10",
"@humanwhocodes/module-importer": "^1.0.1",
"@nodelib/fs.walk": "^1.2.8",
"ajv": "^6.10.0",
@@ -1089,11 +1050,11 @@
}
},
"node_modules/eslint-config-next": {
"version": "13.4.4",
"resolved": "https://registry.npmjs.org/eslint-config-next/-/eslint-config-next-13.4.4.tgz",
"integrity": "sha512-z/PMbm6L0iC/fwISULxe8IVy4DtNqZk2wQY711o35klenq70O6ns82A8yuMVCFjHC0DIyB2lyugesRtuk9u8dQ==",
"version": "13.4.7",
"resolved": "https://registry.npmjs.org/eslint-config-next/-/eslint-config-next-13.4.7.tgz",
"integrity": "sha512-+IRAyD0+J1MZaTi9RQMPUfr6Q+GCZ1wOkK6XM52Vokh7VI4R6YFGOFzdkEFHl4ZyIX4FKa5vcwUP2WscSFNjNQ==",
"dependencies": {
"@next/eslint-plugin-next": "13.4.4",
"@next/eslint-plugin-next": "13.4.7",
"@rushstack/eslint-patch": "^1.1.3",
"@typescript-eslint/parser": "^5.42.0",
"eslint-import-resolver-node": "^0.3.6",
@@ -1157,9 +1118,9 @@
}
},
"node_modules/eslint-import-resolver-typescript/node_modules/globby": {
"version": "13.1.4",
"resolved": "https://registry.npmjs.org/globby/-/globby-13.1.4.tgz",
"integrity": "sha512-iui/IiiW+QrJ1X1hKH5qwlMQyv34wJAYwH1vrf8b9kBA4sNiif3gKsMHa+BrdnOpEudWjpotfa7LrTzB1ERS/g==",
"version": "13.2.0",
"resolved": "https://registry.npmjs.org/globby/-/globby-13.2.0.tgz",
"integrity": "sha512-jWsQfayf13NvqKUIL3Ta+CIqMnvlaIDFveWE/dpOZ9+3AMEJozsxDvKA02zync9UuvOM8rOXzsD5GqKP4OnWPQ==",
"dependencies": {
"dir-glob": "^3.0.1",
"fast-glob": "^3.2.11",
@@ -1664,9 +1625,12 @@
}
},
"node_modules/get-tsconfig": {
"version": "4.5.0",
"resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.5.0.tgz",
"integrity": "sha512-MjhiaIWCJ1sAU4pIQ5i5OfOuHHxVo1oYeNsWTON7jxYkod8pHocXeh+SSbmu5OZZZK73B6cbJ2XADzXehLyovQ==",
"version": "4.6.0",
"resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.6.0.tgz",
"integrity": "sha512-lgbo68hHTQnFddybKbbs/RDRJnJT5YyGy2kQzVwbq+g67X73i+5MVTval34QxGkOe9X5Ujf1UYpCaphLyltjEg==",
"dependencies": {
"resolve-pkg-maps": "^1.0.0"
},
"funding": {
"url": "https://github.com/privatenumber/get-tsconfig?sponsor=1"
}
@@ -1701,6 +1665,11 @@
"node": ">=10.13.0"
}
},
"node_modules/glob-to-regexp": {
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz",
"integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw=="
},
"node_modules/globals": {
"version": "13.20.0",
"resolved": "https://registry.npmjs.org/globals/-/globals-13.20.0.tgz",
@@ -1909,21 +1878,6 @@
"node": ">= 0.4"
}
},
"node_modules/is-arguments": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz",
"integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==",
"dependencies": {
"call-bind": "^1.0.2",
"has-tostringtag": "^1.0.0"
},
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/is-array-buffer": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.2.tgz",
@@ -2049,14 +2003,6 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/is-map": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.2.tgz",
"integrity": "sha512-cOZFQQozTha1f4MxLFzlgKYPTyj26picdZTx82hbc/Xf4K/tZOOXSCkMvU4pKioRXGDLJRn0GM7Upe7kR721yg==",
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/is-negative-zero": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz",
@@ -2113,14 +2059,6 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/is-set": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.2.tgz",
"integrity": "sha512-+2cnTEZeY5z/iXGbLhPrOAaK/Mau5k5eXq9j14CpRTftq0pAJu2MwVRSZhyZWBzx3o6X795Lz6Bpb6R0GKf37g==",
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/is-shared-array-buffer": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz",
@@ -2189,14 +2127,6 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/is-weakmap": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.1.tgz",
"integrity": "sha512-NSBR4kH5oVj1Uwvv970ruUkCV7O1mzgVFO4/rev2cLRda9Tm9HrL70ZPut4rOHgY0FNrUu9BCbXA2sdQ+x0chA==",
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/is-weakref": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz",
@@ -2208,18 +2138,6 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/is-weakset": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.2.tgz",
"integrity": "sha512-t2yVvttHkQktwnNNmBQ98AhENLdPUTDTE21uPqAQ0ARwQfGeQKRVS0NNurH7bTf7RrvcVn1OOge45CnBeHCSmg==",
"dependencies": {
"call-bind": "^1.0.2",
"get-intrinsic": "^1.1.1"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/is-wsl": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz",
@@ -2245,11 +2163,6 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/isarray": {
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz",
"integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw=="
},
"node_modules/isexe": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
@@ -2453,16 +2366,17 @@
"integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw=="
},
"node_modules/next": {
"version": "13.4.4",
"resolved": "https://registry.npmjs.org/next/-/next-13.4.4.tgz",
"integrity": "sha512-C5S0ysM0Ily9McL4Jb48nOQHT1BukOWI59uC3X/xCMlYIh9rJZCv7nzG92J6e1cOBqQbKovlpgvHWFmz4eKKEA==",
"version": "13.4.7",
"resolved": "https://registry.npmjs.org/next/-/next-13.4.7.tgz",
"integrity": "sha512-M8z3k9VmG51SRT6v5uDKdJXcAqLzP3C+vaKfLIAM0Mhx1um1G7MDnO63+m52qPdZfrTFzMZNzfsgvm3ghuVHIQ==",
"dependencies": {
"@next/env": "13.4.4",
"@next/env": "13.4.7",
"@swc/helpers": "0.5.1",
"busboy": "1.6.0",
"caniuse-lite": "^1.0.30001406",
"postcss": "8.4.14",
"styled-jsx": "5.1.1",
"watchpack": "2.4.0",
"zod": "3.21.4"
},
"bin": {
@@ -2472,15 +2386,15 @@
"node": ">=16.8.0"
},
"optionalDependencies": {
"@next/swc-darwin-arm64": "13.4.4",
"@next/swc-darwin-x64": "13.4.4",
"@next/swc-linux-arm64-gnu": "13.4.4",
"@next/swc-linux-arm64-musl": "13.4.4",
"@next/swc-linux-x64-gnu": "13.4.4",
"@next/swc-linux-x64-musl": "13.4.4",
"@next/swc-win32-arm64-msvc": "13.4.4",
"@next/swc-win32-ia32-msvc": "13.4.4",
"@next/swc-win32-x64-msvc": "13.4.4"
"@next/swc-darwin-arm64": "13.4.7",
"@next/swc-darwin-x64": "13.4.7",
"@next/swc-linux-arm64-gnu": "13.4.7",
"@next/swc-linux-arm64-musl": "13.4.7",
"@next/swc-linux-x64-gnu": "13.4.7",
"@next/swc-linux-x64-musl": "13.4.7",
"@next/swc-win32-arm64-msvc": "13.4.7",
"@next/swc-win32-ia32-msvc": "13.4.7",
"@next/swc-win32-x64-msvc": "13.4.7"
},
"peerDependencies": {
"@opentelemetry/api": "^1.1.0",
@@ -2542,21 +2456,6 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/object-is": {
"version": "1.1.5",
"resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.5.tgz",
"integrity": "sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw==",
"dependencies": {
"call-bind": "^1.0.2",
"define-properties": "^1.1.3"
},
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/object-keys": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz",
@@ -2927,6 +2826,14 @@
"node": ">=4"
}
},
"node_modules/resolve-pkg-maps": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz",
"integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==",
"funding": {
"url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1"
}
},
"node_modules/reusify": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz",
@@ -3090,9 +2997,9 @@
}
},
"node_modules/semver": {
"version": "7.5.1",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.5.1.tgz",
"integrity": "sha512-Wvss5ivl8TMRZXXESstBA4uR5iXgEN/VC5/sOcuXdVLzcdkz4HWetIoRfG5gb5X+ij/G9rw9YoGn3QoQ8OCSpw==",
"version": "7.5.2",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.5.2.tgz",
"integrity": "sha512-SoftuTROv/cRjCze/scjGyiDtcUyxw1rgYQSZY7XTmtR5hX+dm76iDbTH8TkLPHCQmlbQVSSbNZCPM2hb0knnQ==",
"dependencies": {
"lru-cache": "^6.0.0"
},
@@ -3156,17 +3063,6 @@
"node": ">=0.10.0"
}
},
"node_modules/stop-iteration-iterator": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.0.0.tgz",
"integrity": "sha512-iCGQj+0l0HOdZ2AEeBADlsRC+vsnDsZsbdSiH1yNSjcfKM7fdpCMfqAL/dwF5BLiw/XhRft/Wax6zQbhq2BcjQ==",
"dependencies": {
"internal-slot": "^1.0.4"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/streamsearch": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz",
@@ -3382,9 +3278,9 @@
}
},
"node_modules/tslib": {
"version": "2.5.2",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.2.tgz",
"integrity": "sha512-5svOrSA2w3iGFDs1HibEVBGbDrAY82bFQ3HZ3ixB+88nsbsWQoKqDRb5UBYAUPEzbBn6dAp5gRNXglySbx1MlA=="
"version": "2.5.3",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.3.tgz",
"integrity": "sha512-mSxlJJwl3BMEQCUNnxXBU9jP4JBktcEGhURcPR6VQVlnP0FdDEsIaz0C35dXNGLyRfrATNofF0F5p2KPxQgB+w=="
},
"node_modules/tsutils": {
"version": "3.21.0",
@@ -3441,16 +3337,16 @@
}
},
"node_modules/typescript": {
"version": "5.0.4",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.0.4.tgz",
"integrity": "sha512-cW9T5W9xY37cc+jfEnaUvX91foxtHkza3Nw3wkoF4sSlKn0MONdkdEndig/qPBWXNkmplh3NzayQzCiHM4/hqw==",
"version": "5.1.3",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.1.3.tgz",
"integrity": "sha512-XH627E9vkeqhlZFQuL+UsyAXEnibT0kWR2FWONlr4sTjvxyJYnyefgrkyECLzM5NenmKzRAy2rR/OlYLA1HkZw==",
"peer": true,
"bin": {
"tsc": "bin/tsc",
"tsserver": "bin/tsserver"
},
"engines": {
"node": ">=12.20"
"node": ">=14.17"
}
},
"node_modules/unbox-primitive": {
@@ -3483,6 +3379,18 @@
"punycode": "^2.1.0"
}
},
"node_modules/watchpack": {
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.0.tgz",
"integrity": "sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg==",
"dependencies": {
"glob-to-regexp": "^0.4.1",
"graceful-fs": "^4.1.2"
},
"engines": {
"node": ">=10.13.0"
}
},
"node_modules/which": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
@@ -3512,20 +3420,6 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/which-collection": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.1.tgz",
"integrity": "sha512-W8xeTUwaln8i3K/cY1nGXzdnVZlidBcagyNFtBdD5kxnb4TvGKR7FfSIS3mYpwWS1QUCutfKz8IY8RjftB0+1A==",
"dependencies": {
"is-map": "^2.0.1",
"is-set": "^2.0.1",
"is-weakmap": "^2.0.1",
"is-weakset": "^2.0.1"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/which-typed-array": {
"version": "1.1.9",
"resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.9.tgz",

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,5 +1,17 @@
# @vercel/build-utils
## 6.8.0
### Minor Changes
- Add `getNodeBinPaths()` and `traverseUpDirectories()` functions ([#10150](https://github.com/vercel/vercel/pull/10150))
## 6.7.5
### Patch Changes
- Publish missing build-utils ([`cd35071f6`](https://github.com/vercel/vercel/commit/cd35071f609d615d47bc04634c123b33768436cb))
## 6.7.4
### Patch Changes

View File

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

View File

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

View File

@@ -30,7 +30,9 @@ import {
getNodeVersion,
getSpawnOptions,
getNodeBinPath,
getNodeBinPaths,
scanParentDirs,
traverseUpDirectories,
} from './fs/run-user-scripts';
import {
getLatestNodeVersion,
@@ -43,6 +45,7 @@ import { getPlatformEnv } from './get-platform-env';
import { getPrefixedEnvVars } from './get-prefixed-env-vars';
import { cloneEnv } from './clone-env';
import { hardLinkDir } from './hard-link-dir';
import { validateNpmrc } from './validate-npmrc';
export {
FileBlob,
@@ -67,6 +70,7 @@ export {
spawnCommand,
walkParentDirs,
getNodeBinPath,
getNodeBinPaths,
runNpmInstall,
runBundleInstall,
runPipInstall,
@@ -88,6 +92,8 @@ export {
getIgnoreFilter,
cloneEnv,
hardLinkDir,
traverseUpDirectories,
validateNpmrc,
};
export { EdgeFunction } from './edge-function';

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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,100 @@
# vercel
## 31.0.0
### Major Changes
- Update `vc dev` redirect response to match production behavior ([#10143](https://github.com/vercel/vercel/pull/10143))
### Patch Changes
- require `--yes` to promote preview deployment ([#10135](https://github.com/vercel/vercel/pull/10135))
- [cli] Optimize write build result for vc build ([#10154](https://github.com/vercel/vercel/pull/10154))
- Only show relevant Project matches in Project selector ([#10114](https://github.com/vercel/vercel/pull/10114))
- [cli] Fix error message when token is invalid ([#10131](https://github.com/vercel/vercel/pull/10131))
- Updated dependencies [[`e4895d979`](https://github.com/vercel/vercel/commit/e4895d979b57e369e0618481c5974243887d72cc), [`346892210`](https://github.com/vercel/vercel/commit/3468922108f411482a72acd0331f0f2ee52a6d4c), [`346892210`](https://github.com/vercel/vercel/commit/3468922108f411482a72acd0331f0f2ee52a6d4c), [`a6de052ed`](https://github.com/vercel/vercel/commit/a6de052ed2f09cc80bf4c2d0f06bedd267a63cdc)]:
- @vercel/next@3.8.7
- @vercel/static-build@1.3.38
- @vercel/build-utils@6.8.0
- @vercel/remix-builder@1.8.15
- @vercel/node@2.15.3
## 30.2.3
### Patch Changes
- [cli] do not force auto-assign value on deployments ([#10110](https://github.com/vercel/vercel/pull/10110))
- Updated dependencies [[`91406abdb`](https://github.com/vercel/vercel/commit/91406abdb0c332152fc6c7c1e4bd3a872b084434), [`2230ea6cc`](https://github.com/vercel/vercel/commit/2230ea6cc1b84c1f03227a4e197b7684635b5955), [`8b3a4146a`](https://github.com/vercel/vercel/commit/8b3a4146af68d2b7288c80a5b919d832dba929b5)]:
- @vercel/node@2.15.2
- @vercel/remix-builder@1.8.14
- @vercel/static-build@1.3.37
## 30.2.2
### Patch Changes
- [cli] vc env pull should add `.env*.local` to `.gitignore` ([#10085](https://github.com/vercel/vercel/pull/10085))
- [cli] Fix team validation bug where you are apart of a team ([#10092](https://github.com/vercel/vercel/pull/10092))
- Add support for `vc dev` command with repo link ([#10082](https://github.com/vercel/vercel/pull/10082))
- Add support for `vc deploy --prebuilt` command with repo link ([#10083](https://github.com/vercel/vercel/pull/10083))
- Move readme copy logic to a helper function for `vc link` ([#10084](https://github.com/vercel/vercel/pull/10084))
- Add support for `vc pull` command with repo link ([#10078](https://github.com/vercel/vercel/pull/10078))
- Add support for `vc build` command with repo link ([#10075](https://github.com/vercel/vercel/pull/10075))
## 30.2.1
### Patch Changes
- Updated dependencies [[`a04bf557f`](https://github.com/vercel/vercel/commit/a04bf557fc6e1080a117428977d0993dec78b004)]:
- @vercel/node@2.15.1
- @vercel/static-build@1.3.36
## 30.2.0
### Minor Changes
- [node] Add isomorphic functions ([#9947](https://github.com/vercel/vercel/pull/9947))
### Patch Changes
- Add `client.fetchPaginated()` helper function ([#10054](https://github.com/vercel/vercel/pull/10054))
- Updated dependencies [[`bc5afe24c`](https://github.com/vercel/vercel/commit/bc5afe24c4547dbf798b939199e8212c4b34038e), [`49c717856`](https://github.com/vercel/vercel/commit/49c7178567ec5bcebe633b598c8c9c0e1aa40fbb), [`0039c8b5c`](https://github.com/vercel/vercel/commit/0039c8b5cea975316a62c4f6aaca5d66d731cc0d)]:
- @vercel/node@2.15.0
- @vercel/remix-builder@1.8.13
- @vercel/static-build@1.3.35
## 30.1.2
### Patch Changes
- Publish missing build-utils ([`cd35071f6`](https://github.com/vercel/vercel/commit/cd35071f609d615d47bc04634c123b33768436cb))
- Updated dependencies [[`cd35071f6`](https://github.com/vercel/vercel/commit/cd35071f609d615d47bc04634c123b33768436cb)]:
- @vercel/build-utils@6.7.5
- @vercel/node@2.14.5
- @vercel/remix-builder@1.8.12
- @vercel/static-build@1.3.34
## 30.1.1
### Patch Changes
- [cli] vc build ignore '.env\*' & ignore files for '@vercel/static' ([#10056](https://github.com/vercel/vercel/pull/10056))
- [cli] Ensure .npmrc does not contain use-node-version ([#10049](https://github.com/vercel/vercel/pull/10049))
## 30.1.0
### Minor Changes

View File

@@ -1,6 +1,6 @@
{
"name": "vercel",
"version": "30.1.0",
"version": "31.0.0",
"preferGlobal": true,
"license": "Apache-2.0",
"description": "The command-line interface for Vercel",
@@ -32,20 +32,20 @@
"node": ">= 14"
},
"dependencies": {
"@vercel/build-utils": "6.7.4",
"@vercel/build-utils": "6.8.0",
"@vercel/go": "2.5.1",
"@vercel/hydrogen": "0.0.64",
"@vercel/next": "3.8.6",
"@vercel/node": "2.14.4",
"@vercel/next": "3.8.7",
"@vercel/node": "2.15.3",
"@vercel/python": "3.1.60",
"@vercel/redwood": "1.1.15",
"@vercel/remix-builder": "1.8.11",
"@vercel/remix-builder": "1.8.15",
"@vercel/ruby": "1.3.76",
"@vercel/static-build": "1.3.33"
"@vercel/static-build": "1.3.38"
},
"devDependencies": {
"@alex_neo/jest-expect-message": "1.0.5",
"@edge-runtime/node-utils": "2.0.3",
"@edge-runtime/node-utils": "2.1.0",
"@next/env": "11.1.2",
"@sentry/node": "5.5.0",
"@sindresorhus/slugify": "0.11.0",
@@ -85,13 +85,13 @@
"@types/which": "3.0.0",
"@types/write-json-file": "2.2.1",
"@types/yauzl-promise": "2.1.0",
"@vercel-internals/constants": "1.0.1",
"@vercel-internals/constants": "1.0.3",
"@vercel-internals/get-package-json": "1.0.0",
"@vercel-internals/types": "1.0.1",
"@vercel/client": "12.6.1",
"@vercel-internals/types": "1.0.3",
"@vercel/client": "12.6.3",
"@vercel/error-utils": "1.0.10",
"@vercel/frameworks": "1.4.2",
"@vercel/fs-detectors": "3.9.3",
"@vercel/frameworks": "1.4.3",
"@vercel/fs-detectors": "4.0.1",
"@vercel/fun": "1.0.4",
"@vercel/ncc": "0.24.0",
"@vercel/routing-utils": "2.2.1",

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1131,7 +1131,7 @@ export default class DevServer {
body = redirectTemplate({ location, statusCode });
} else {
res.setHeader('content-type', 'text/plain; charset=utf-8');
body = `Redirecting to ${location} (${statusCode})\n`;
body = `Redirecting...\n`;
}
res.end(body);
}

View File

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

View File

@@ -50,7 +50,7 @@ export interface CreateOptions {
projectSettings?: any;
skipAutoDetectionConfirmation?: boolean;
noWait?: boolean;
autoAssignCustomDomains: boolean;
autoAssignCustomDomains?: boolean;
}
export interface RemoveOptions {

View File

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

View File

@@ -2,15 +2,11 @@ import chalk from 'chalk';
import pluralize from 'pluralize';
import { homedir } from 'os';
import { join, normalize } from 'path';
import { normalizePath } from '@vercel/build-utils';
import { lstat, readJSON, outputJSON, writeFile, readFile } from 'fs-extra';
import { normalizePath, traverseUpDirectories } from '@vercel/build-utils';
import { lstat, readJSON, outputJSON } from 'fs-extra';
import confirm from '../input/confirm';
import toHumanPath from '../humanize-path';
import {
VERCEL_DIR,
VERCEL_DIR_README,
VERCEL_DIR_REPO,
} from '../projects/link';
import { VERCEL_DIR, VERCEL_DIR_REPO, writeReadme } from '../projects/link';
import { getRemoteUrls } from '../create-git-meta';
import link from '../output/link';
import { emoji, prependEmoji } from '../emoji';
@@ -44,9 +40,12 @@ export interface RepoLink {
* and returns the parsed `.vercel/repo.json` file if the repository
* has already been linked.
*/
export async function getRepoLink(cwd: string): Promise<RepoLink | undefined> {
export async function getRepoLink(
client: Client,
cwd: string
): Promise<RepoLink | undefined> {
// Determine where the root of the repo is
const rootPath = await findRepoRoot(cwd);
const rootPath = await findRepoRoot(client, cwd);
if (!rootPath) return undefined;
// Read the `repo.json`, if this repo has already been linked
@@ -67,7 +66,7 @@ export async function ensureRepoLink(
): Promise<RepoLink | undefined> {
const { output } = client;
const repoLink = await getRepoLink(cwd);
const repoLink = await getRepoLink(client, cwd);
if (repoLink) {
output.debug(`Found Git repository root directory: ${repoLink.rootPath}`);
} else {
@@ -128,9 +127,30 @@ export async function ensureRepoLink(
output.spinner(
`Fetching Projects for ${link(repoUrl)} under ${chalk.bold(org.slug)}`
);
// TODO: Add pagination to fetch all Projects
const query = new URLSearchParams({ repoUrl, limit: '100' });
const projects: Project[] = await client.fetch(`/v2/projects?${query}`);
let projects: Project[] = [];
const query = new URLSearchParams({ repoUrl });
const projectsIterator = client.fetchPaginated<{
projects: Project[];
}>(`/v9/projects?${query}`);
let printedFound = false;
for await (const chunk of projectsIterator) {
projects = projects.concat(chunk.projects);
if (!printedFound && projects.length > 0) {
output.log(
`${pluralize('Project', chunk.projects.length)} linked to ${link(
repoUrl
)} under ${chalk.bold(org.slug)}:`
);
printedFound = true;
}
for (const project of chunk.projects) {
output.print(` * ${chalk.cyan(`${org.slug}/${project.name}\n`)}`);
}
if (chunk.pagination.next) {
output.spinner(`Found ${chalk.bold(projects.length)} Projects…`, 0);
}
}
if (projects.length === 0) {
output.log(
`No Projects are linked to ${link(repoUrl)} under ${chalk.bold(
@@ -140,24 +160,17 @@ export async function ensureRepoLink(
// TODO: run detection logic to find potential projects.
// then prompt user to select valid projects.
// then create new Projects
} else {
output.log(
`Found ${chalk.bold(projects.length)} ${pluralize(
'Project',
projects.length
)} linked to ${link(repoUrl)} under ${chalk.bold(org.slug)}:`
);
}
for (const project of projects) {
output.print(` * ${chalk.cyan(`${org.slug}/${project.name}\n`)}`);
}
shouldLink =
yes ||
(await confirm(
client,
`Link to ${projects.length === 1 ? 'it' : 'them'}?`,
`Link to ${
projects.length === 1
? 'this Project'
: `these ${chalk.bold(projects.length)} Projects`
}?`,
true
));
@@ -179,10 +192,7 @@ export async function ensureRepoLink(
};
await outputJSON(repoConfigPath, repoConfig, { spaces: 2 });
await writeFile(
join(rootPath, VERCEL_DIR, VERCEL_DIR_README),
await readFile(join(__dirname, 'VERCEL_DIR_README.txt'), 'utf8')
);
await writeReadme(rootPath);
// update .gitignore
const isGitIgnoreUpdated = await addToGitIgnore(rootPath);
@@ -211,45 +221,47 @@ export async function ensureRepoLink(
* the nearest `.git/config` file is found. Returns the directory where
* the Git config was found, or `undefined` when no Git repo was found.
*/
export async function findRepoRoot(start: string): Promise<string | undefined> {
for (const current of traverseUpDirectories(start)) {
export async function findRepoRoot(
client: Client,
start: string
): Promise<string | undefined> {
const { debug } = client.output;
const REPO_JSON_PATH = join(VERCEL_DIR, VERCEL_DIR_REPO);
const GIT_CONFIG_PATH = normalize('.git/config');
for (const current of traverseUpDirectories({ start })) {
if (current === home) {
// Sometimes the $HOME directory is set up as a Git repo
// (for dotfiles, etc.). In this case it's safe to say that
// this isn't the repo we're looking for. Bail.
debug('Arrived at home directory');
break;
}
// if `.vercel/repo.json` exists (already linked),
// then consider this the repo root
const repoConfigPath = join(current, VERCEL_DIR, VERCEL_DIR_REPO);
const repoConfigPath = join(current, REPO_JSON_PATH);
let stat = await lstat(repoConfigPath).catch(err => {
if (err.code !== 'ENOENT') throw err;
});
if (stat) {
debug(`Found "${REPO_JSON_PATH}" - detected "${current}" as repo root`);
return current;
}
// if `.git/config` exists (unlinked),
// then consider this the repo root
const gitConfigPath = join(current, '.git/config');
const gitConfigPath = join(current, GIT_CONFIG_PATH);
stat = await lstat(gitConfigPath).catch(err => {
if (err.code !== 'ENOENT') throw err;
});
if (stat) {
debug(`Found "${GIT_CONFIG_PATH}" - detected "${current}" as repo root`);
return current;
}
}
}
export function* traverseUpDirectories(start: string) {
let current: string | undefined = normalize(start);
while (current) {
yield current;
// Go up one directory
const next = join(current, '..');
current = next === current ? undefined : next;
}
debug('Aborting search for repo root');
}
function sortByDirectory(a: RepoProjectConfig, b: RepoProjectConfig): number {
@@ -268,7 +280,7 @@ export function findProjectsFromPath(
path: string
): RepoProjectConfig[] {
const normalizedPath = normalizePath(path);
return projects
const matches = projects
.slice()
.sort(sortByDirectory)
.filter(project => {
@@ -281,14 +293,9 @@ export function findProjectsFromPath(
normalizedPath.startsWith(`${project.directory}/`)
);
});
}
/**
* TODO: remove
*/
export function findProjectFromPath(
projects: RepoProjectConfig[],
path: string
): RepoProjectConfig | undefined {
return findProjectsFromPath(projects, path)[0];
// If there are multiple matches, we only want the most relevant
// selections (with the deepest directory depth), so pick the first
// one and filter on those matches.
const firstMatch = matches[0];
return matches.filter(match => match.directory === firstMatch.directory);
}

View File

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

View File

@@ -69,7 +69,7 @@ export function getVercelDirectory(cwd: string): string {
return existingDirs[0] || possibleDirs[0];
}
async function getProjectLink(
export async function getProjectLink(
client: Client,
path: string
): Promise<ProjectLink | null> {
@@ -83,7 +83,7 @@ async function getProjectLinkFromRepoLink(
client: Client,
path: string
): Promise<ProjectLink | null> {
const repoLink = await getRepoLink(path);
const repoLink = await getRepoLink(client, path);
if (!repoLink?.repoConfig) {
return null;
}
@@ -95,11 +95,13 @@ async function getProjectLinkFromRepoLink(
if (projects.length === 1) {
project = projects[0];
} else {
const selectableProjects =
projects.length > 0 ? projects : repoLink.repoConfig.projects;
const { p } = await client.prompt({
name: 'p',
type: 'list',
message: `Please select a Project:`,
choices: repoLink.repoConfig.projects.map(p => ({
choices: selectableProjects.map(p => ({
value: p,
name: p.name,
})),
@@ -108,9 +110,10 @@ async function getProjectLinkFromRepoLink(
}
if (project) {
return {
repoRoot: repoLink.rootPath,
orgId: repoLink.repoConfig.orgId,
projectId: project.id,
repoRoot: repoLink.rootPath,
projectRootDirectory: project.directory,
};
}
return null;
@@ -166,6 +169,7 @@ async function getOrgById(client: Client, orgId: string): Promise<Org | null> {
}
async function hasProjectLink(
client: Client,
projectLink: ProjectLink,
path: string
): Promise<boolean> {
@@ -180,7 +184,7 @@ async function hasProjectLink(
}
// linked via `repo.json`?
const repoLink = await getRepoLink(path);
const repoLink = await getRepoLink(client, path);
if (
repoLink?.repoConfig?.orgId === projectLink.orgId &&
repoLink.repoConfig.projects.find(p => p.id === projectLink.projectId)
@@ -242,7 +246,7 @@ export async function getLinkedProject(
if (isAPIError(err) && err.status === 403) {
output.stopSpinner();
if (err.missingToken) {
if (err.missingToken || err.invalidToken) {
throw new InvalidToken();
} else {
throw new NowBuildError({
@@ -284,6 +288,13 @@ export async function getLinkedProject(
return { status: 'linked', org, project, repoRoot: link.repoRoot };
}
export async function writeReadme(path: string) {
await writeFile(
join(path, VERCEL_DIR, VERCEL_DIR_README),
await readFile(join(__dirname, 'VERCEL_DIR_README.txt'), 'utf8')
);
}
export async function linkFolderToProject(
client: Client,
path: string,
@@ -293,7 +304,7 @@ export async function linkFolderToProject(
successEmoji: EmojiLabel = 'link'
) {
// if the project is already linked, we skip linking
if (await hasProjectLink(projectLink, path)) {
if (await hasProjectLink(client, projectLink, path)) {
return;
}
@@ -313,10 +324,7 @@ export async function linkFolderToProject(
JSON.stringify(projectLink)
);
await writeFile(
join(path, VERCEL_DIR, VERCEL_DIR_README),
await readFile(join(__dirname, 'VERCEL_DIR_README.txt'), 'utf8')
);
await writeReadme(path);
// update .gitignore
const isGitIgnoreUpdated = await addToGitIgnore(path);

View File

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

View File

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

View File

@@ -88,10 +88,10 @@ test(
async (testPath: any) => {
const vcRobots = `https://vercel.com/robots.txt`;
await testPath(200, '/rewrite', /User-Agent: \*/m);
await testPath(308, '/redirect', `Redirecting to ${vcRobots} (308)`, {
await testPath(308, '/redirect', `Redirecting...`, {
Location: vcRobots,
});
await testPath(307, '/tempRedirect', `Redirecting to ${vcRobots} (307)`, {
await testPath(307, '/tempRedirect', `Redirecting...`, {
Location: vcRobots,
});
}
@@ -103,10 +103,10 @@ test(
testFixtureStdio('test-routing-case-sensitive', async (testPath: any) => {
await testPath(200, '/Path', 'UPPERCASE');
await testPath(200, '/path', 'lowercase');
await testPath(308, '/GoTo', 'Redirecting to /upper.html (308)', {
await testPath(308, '/GoTo', 'Redirecting...', {
Location: '/upper.html',
});
await testPath(308, '/goto', 'Redirecting to /lower.html (308)', {
await testPath(308, '/goto', 'Redirecting...', {
Location: '/lower.html',
});
})

View File

@@ -233,7 +233,7 @@ test(
expect(res.headers.get('location')).toBe(
`http://localhost:${port}/?foo=bar`
);
expect(body).toBe('Redirecting to /?foo=bar (301)\n');
expect(body).toBe('Redirecting...\n');
}
{

View File

@@ -197,21 +197,18 @@ test(
await testPath(200, '/sub', 'Sub Index Page');
await testPath(200, '/sub/another', 'Sub Another Page');
await testPath(200, '/style.css', 'body { color: green }');
await testPath(308, '/index.html', 'Redirecting to / (308)', {
await testPath(308, '/index.html', 'Redirecting...', {
Location: '/',
});
await testPath(308, '/about.html', 'Redirecting to /about (308)', {
await testPath(308, '/about.html', 'Redirecting...', {
Location: '/about',
});
await testPath(308, '/sub/index.html', 'Redirecting to /sub (308)', {
await testPath(308, '/sub/index.html', 'Redirecting...', {
Location: '/sub',
});
await testPath(
308,
'/sub/another.html',
'Redirecting to /sub/another (308)',
{ Location: '/sub/another' }
);
await testPath(308, '/sub/another.html', 'Redirecting...', {
Location: '/sub/another',
});
})
);
@@ -225,21 +222,18 @@ test(
await testPath(200, '/sub', 'Sub Index Page');
await testPath(200, '/sub/another', 'Sub Another Page');
await testPath(200, '/style.css', 'body { color: green }');
await testPath(308, '/index.html', 'Redirecting to / (308)', {
await testPath(308, '/index.html', 'Redirecting...', {
Location: '/',
});
await testPath(308, '/about.html', 'Redirecting to /about (308)', {
await testPath(308, '/about.html', 'Redirecting...', {
Location: '/about',
});
await testPath(308, '/sub/index.html', 'Redirecting to /sub (308)', {
await testPath(308, '/sub/index.html', 'Redirecting...', {
Location: '/sub',
});
await testPath(
308,
'/sub/another.html',
'Redirecting to /sub/another (308)',
{ Location: '/sub/another' }
);
await testPath(308, '/sub/another.html', 'Redirecting...', {
Location: '/sub/another',
});
}
)
);
@@ -264,21 +258,16 @@ test(
await testPath(200, '/sub/another/', 'Sub Another Page');
await testPath(200, '/style.css', 'body { color: green }');
//TODO: fix this test so that location is `/` instead of `//`
//await testPath(308, '/index.html', 'Redirecting to / (308)', { Location: '/' });
await testPath(308, '/about.html', 'Redirecting to /about/ (308)', {
//await testPath(308, '/index.html', 'Redirecting...', { Location: '/' });
await testPath(308, '/about.html', 'Redirecting...', {
Location: '/about/',
});
await testPath(308, '/sub/index.html', 'Redirecting to /sub/ (308)', {
await testPath(308, '/sub/index.html', 'Redirecting...', {
Location: '/sub/',
});
await testPath(
308,
'/sub/another.html',
'Redirecting to /sub/another/ (308)',
{
Location: '/sub/another/',
}
);
await testPath(308, '/sub/another.html', 'Redirecting...', {
Location: '/sub/another/',
});
})
);
@@ -315,13 +304,13 @@ test(
await testPath(200, '/sub/index.html', 'Sub Index Page');
await testPath(200, '/sub/another.html', 'Sub Another Page');
await testPath(200, '/style.css', 'body { color: green }');
await testPath(308, '/about.html/', 'Redirecting to /about.html (308)', {
await testPath(308, '/about.html/', 'Redirecting...', {
Location: '/about.html',
});
await testPath(308, '/style.css/', 'Redirecting to /style.css (308)', {
await testPath(308, '/style.css/', 'Redirecting...', {
Location: '/style.css',
});
await testPath(308, '/sub', 'Redirecting to /sub/ (308)', {
await testPath(308, '/sub', 'Redirecting...', {
Location: '/sub/',
});
})
@@ -347,20 +336,15 @@ test(
await testPath(200, '/sub/index.html', 'Sub Index Page');
await testPath(200, '/sub/another.html', 'Sub Another Page');
await testPath(200, '/style.css', 'body { color: green }');
await testPath(308, '/about.html/', 'Redirecting to /about.html (308)', {
await testPath(308, '/about.html/', 'Redirecting...', {
Location: '/about.html',
});
await testPath(308, '/sub/', 'Redirecting to /sub (308)', {
await testPath(308, '/sub/', 'Redirecting...', {
Location: '/sub',
});
await testPath(
308,
'/sub/another.html/',
'Redirecting to /sub/another.html (308)',
{
Location: '/sub/another.html',
}
);
await testPath(308, '/sub/another.html/', 'Redirecting...', {
Location: '/sub/another.html',
});
})
);

View File

@@ -44,7 +44,9 @@ function fetchWithRetry(url, opts = {}) {
if (res.status !== opts.status) {
const text = await res.text();
throw new Error(
`Failed to fetch ${url} with status ${res.status} (expected ${opts.status}):\n\n${text}\n\n`
`Failed to fetch "${url}", received ${res.status}, expected ${
opts.status
}, id: ${res.headers.get('x-vercel-id')}:\n\n${text}\n\n`
);
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -32,7 +32,9 @@ function fetchTokenInformation(token: string, retries = 3) {
if (!res.ok) {
throw new Error(
`Failed to fetch ${url}, received status ${res.status}`
`Failed to fetch "${url}", status: ${
res.status
}, id: ${res.headers.get('x-vercel-id')}`
);
}

View File

@@ -43,7 +43,9 @@ function fetchTokenInformation(token: string, retries = 3) {
if (!res.ok) {
throw new Error(
`Failed to fetch ${url}, received status ${res.status}`
`Failed to fetch "${url}", status: ${
res.status
}, id: ${res.headers.get('x-vercel-id')}`
);
}

View File

@@ -47,7 +47,9 @@ function fetchTokenInformation(token: string, retries = 3) {
if (!res.ok) {
throw new Error(
`Failed to fetch ${url}, received status ${res.status}`
`Failed to fetch "${url}", status: ${
res.status
}, id: ${res.headers.get('x-vercel-id')}`
);
}

View File

@@ -18,6 +18,7 @@ export function useDeployment({
state = 'READY',
createdAt,
project = defaultProject,
target = 'production',
}: {
creator: Pick<User, 'id' | 'email' | 'name' | 'username'>;
state?:
@@ -29,6 +30,7 @@ export function useDeployment({
| 'CANCELED';
createdAt?: number;
project: any; // FIX ME: Use `Project` once PR #9956 is merged
target?: Deployment['target'];
}) {
setupDeploymentEndpoints();
@@ -63,7 +65,7 @@ export function useDeployment({
regions: [],
routes: [],
status: state,
target: 'production',
target,
type: 'LAMBDAS',
url: url.hostname,
version: 2,

View File

@@ -5,9 +5,11 @@ export function useTeams(
teamId?: string,
options: {
failMissingToken?: boolean;
failInvalidToken?: boolean;
failNoAccess?: boolean;
} = {
failMissingToken: false,
failInvalidToken: false,
failNoAccess: false,
}
) {
@@ -34,6 +36,15 @@ export function useTeams(
});
return;
}
if (options.failInvalidToken) {
res.statusCode = 403;
res.json({
message: 'Not authorized',
code: 'forbidden',
invalidToken: true,
});
return;
}
if (options.failNoAccess) {
res.statusCode = 403;

View File

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

View File

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

View File

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

View File

@@ -101,6 +101,71 @@ describe('promote', () => {
await expect(exitCodePromise).resolves.toEqual(0);
});
it('should fail to promote a preview deployment when user says no', async () => {
const { cwd, previousDeployment } = initPromoteTest({
deploymentTarget: 'preview',
});
client.cwd = cwd;
client.setArgv('promote', previousDeployment.url);
const exitCodePromise = promote(client);
await expect(client.stderr).toOutput(
`Fetching deployment "${previousDeployment.url}" in ${previousDeployment.creator?.username}`
);
await expect(client.stderr).toOutput(
'? This deployment does not target production, therefore promotion will not apply\n production environment variables. Are you sure you want to continue?'
);
// say "no" to the prompt
client.stdin.write('n\n');
await expect(client.stderr).toOutput('Error: Canceled');
await expect(exitCodePromise).resolves.toEqual(0);
});
it('should promote a preview deployment when user says yes', async () => {
const { cwd, previousDeployment } = initPromoteTest({
deploymentTarget: 'preview',
});
client.cwd = cwd;
client.setArgv('promote', previousDeployment.url);
const exitCodePromise = promote(client);
await expect(client.stderr).toOutput(
`Fetching deployment "${previousDeployment.url}" in ${previousDeployment.creator?.username}`
);
await expect(client.stderr).toOutput(
'? This deployment does not target production, therefore promotion will not apply\n production environment variables. Are you sure you want to continue?'
);
// say "yes" to the prompt
client.stdin.write('y\n');
await expect(exitCodePromise).resolves.toEqual(0);
});
it('should promote a preview deployment with --yes', async () => {
const { cwd, previousDeployment } = initPromoteTest({
deploymentTarget: 'preview',
});
client.cwd = cwd;
client.setArgv('promote', previousDeployment.url, '--yes');
const exitCodePromise = promote(client);
await expect(client.stderr).toOutput(
`Fetching deployment "${previousDeployment.url}" in ${previousDeployment.creator?.username}`
);
await expect(client.stderr).toOutput('Promote in progress');
await expect(client.stderr).toOutput(
`Success! ${chalk.bold('vercel-promote')} was promoted to ${
previousDeployment.url
} (${previousDeployment.id})`
);
await expect(exitCodePromise).resolves.toEqual(0);
});
it('should get status while promoting', async () => {
const { cwd, previousDeployment, project } = initPromoteTest({
promotePollCount: 10,
@@ -278,11 +343,13 @@ function initPromoteTest({
promoteJobStatus = 'succeeded',
promotePollCount = 2,
promoteStatusCode,
deploymentTarget,
}: {
promoteAliases?: DeploymentAlias[];
promoteJobStatus?: LastAliasRequest['jobStatus'];
promotePollCount?: number;
promoteStatusCode?: number;
deploymentTarget?: Deployment['target'];
} = {}) {
const cwd = setupUnitFixture('commands/promote/simple-next-site');
const user = useUser();
@@ -294,7 +361,11 @@ function initPromoteTest({
});
const currentDeployment = useDeployment({ creator: user, project });
const previousDeployment = useDeployment({ creator: user, project });
const previousDeployment = useDeployment({
creator: user,
project,
target: deploymentTarget,
});
let pollCounter = 0;
let lastAliasRequest: LastAliasRequest | null = null;

View File

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

View File

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

View File

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

View File

@@ -41,6 +41,35 @@ describe('getLinkedProject', () => {
);
});
it('should fail to return a link when token is invalid', async () => {
const cwd = fixture('vercel-pull-next');
useUser();
useTeams('team_dummy', { failInvalidToken: true });
useProject({
...defaultProject,
id: 'vercel-pull-next',
name: 'vercel-pull-next',
});
let link: UnPromisify<ReturnType<typeof getLinkedProject>> | undefined;
let error: Error | undefined;
try {
link = await getLinkedProject(client, cwd);
} catch (err) {
error = err as Error;
}
expect(link).toBeUndefined();
if (!error) {
throw new Error(`Expected an error to be thrown.`);
}
expect(error.message).toBe(
'The specified token is not valid. Use `vercel login` to generate a new token.'
);
});
it('should fail to return a link when no access to team', async () => {
const cwd = fixture('vercel-pull-next');

View File

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

View File

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

View File

@@ -21,7 +21,7 @@
"build:docs": "typedoc && node scripts/fix-links.js && prettier --write docs/**/*.md docs/*.md"
},
"devDependencies": {
"@edge-runtime/jest-environment": "2.0.0",
"@edge-runtime/jest-environment": "2.2.3",
"@types/jest": "27.4.1",
"ts-node": "8.9.1",
"tsup": "6.1.2",

View File

@@ -0,0 +1,7 @@
# @vercel/frameworks
## 1.4.3
### Patch Changes
- [frameworks] Update `saber.land` to `saber.egoist.dev` ([#10148](https://github.com/vercel/vercel/pull/10148))

View File

@@ -1,6 +1,6 @@
{
"name": "@vercel/frameworks",
"version": "1.4.2",
"version": "1.4.3",
"main": "./dist/frameworks.js",
"types": "./dist/frameworks.d.ts",
"files": [

View File

@@ -1291,7 +1291,7 @@ export const frameworks = [
tagline:
'Saber is a framework for building static sites in Vue.js that supports data from any source.',
description: 'A Saber site, created with npm init.',
website: 'https://saber.land/',
website: 'https://saber.egoist.dev',
detectors: {
every: [
{

View File

@@ -1,5 +1,20 @@
# @vercel/fs-detectors
## 4.0.1
### Patch Changes
- Resolve symlinks in `LocalFileSystemDetector#readdir()` ([#10126](https://github.com/vercel/vercel/pull/10126))
- Updated dependencies [[`0867f11a6`](https://github.com/vercel/vercel/commit/0867f11a6a1086ef4f4701db2b98da8fcc299586)]:
- @vercel/frameworks@1.4.3
## 4.0.0
### Major Changes
- `LocalFileSystemDetector#readdir()` now returns paths relative to the root dir, instead of absolute paths. This is to align with the usage of the detectors that are using the `DetectorFilesystem` interface. ([#10100](https://github.com/vercel/vercel/pull/10100))
## 3.9.3
### Patch Changes

View File

@@ -1,6 +1,6 @@
{
"name": "@vercel/fs-detectors",
"version": "3.9.3",
"version": "4.0.1",
"description": "Vercel filesystem detectors",
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
@@ -15,12 +15,13 @@
"license": "Apache-2.0",
"scripts": {
"build": "tsc",
"test": "jest --env node --verbose --runInBand --bail test/unit.*test.*",
"test-unit": "pnpm test"
"test": "jest --env node --verbose --runInBand --bail",
"test-unit": "pnpm test test/unit.*test.*",
"test-e2e": "pnpm test test/integration.test.ts"
},
"dependencies": {
"@vercel/error-utils": "1.0.10",
"@vercel/frameworks": "1.4.2",
"@vercel/frameworks": "1.4.3",
"@vercel/routing-utils": "2.2.1",
"glob": "8.0.3",
"js-yaml": "4.1.0",
@@ -35,7 +36,7 @@
"@types/minimatch": "3.0.5",
"@types/node": "14.18.33",
"@types/semver": "7.3.10",
"@vercel/build-utils": "6.7.4",
"@vercel/build-utils": "6.8.0",
"typescript": "4.9.5"
}
}

View File

@@ -1,15 +1,16 @@
import fs from 'fs/promises';
import type { Dirent } from 'fs';
import path from 'path';
import { join, relative } from 'path';
import { DetectorFilesystem, DetectorFilesystemStat } from './filesystem';
import { isErrnoException } from '@vercel/error-utils';
export class LocalFileSystemDetector extends DetectorFilesystem {
private rootPath: string;
constructor(rootPath: string) {
super();
this.rootPath = rootPath;
}
async _hasPath(name: string): Promise<boolean> {
try {
await fs.stat(this.getFilePath(name));
@@ -21,37 +22,50 @@ export class LocalFileSystemDetector extends DetectorFilesystem {
throw err;
}
}
_readFile(name: string): Promise<Buffer> {
return fs.readFile(this.getFilePath(name));
}
async _isFile(name: string): Promise<boolean> {
const stat = await fs.stat(this.getFilePath(name));
return stat.isFile();
}
async _readdir(name: string): Promise<DetectorFilesystemStat[]> {
const dirPath = this.getFilePath(name);
const dir = await fs.readdir(dirPath, {
withFileTypes: true,
});
const getType = (dirent: Dirent) => {
if (dirent.isFile()) {
return 'file';
} else if (dirent.isDirectory()) {
return 'dir';
} else {
throw new Error(`Dirent was neither file nor directory`);
}
};
return dir.map(dirent => ({
name: dirent.name,
path: path.join(dirPath, dirent.name),
type: getType(dirent),
}));
async _readdir(dir: string): Promise<DetectorFilesystemStat[]> {
const dirPath = this.getFilePath(dir);
const files = await fs.readdir(dirPath);
return Promise.all(
files.map(async name => {
const absPath = join(this.rootPath, dir, name);
const path = join(this.getRelativeFilePath(dir), name);
const stat = await fs.stat(absPath);
let type: DetectorFilesystemStat['type'];
if (stat.isFile()) {
type = 'file';
} else if (stat.isDirectory()) {
type = 'dir';
} else {
throw new Error(`Dirent was neither file nor directory: ${path}`);
}
return { name, path, type };
})
);
}
_chdir(name: string): DetectorFilesystem {
return new LocalFileSystemDetector(this.getFilePath(name));
}
private getRelativeFilePath(name: string) {
return name.startsWith(this.rootPath)
? relative(this.rootPath, name)
: name;
}
private getFilePath(name: string) {
return path.join(this.rootPath, name);
return join(this.rootPath, this.getRelativeFilePath(name));
}
}

View File

@@ -0,0 +1,40 @@
{
"probes": [
{
"path": "/api/my-endpoint",
"mustContain": "my-endpoint",
"status": 200
},
{
"path": "/api/other-endpoint",
"mustContain": "other-endpoint",
"status": 200
},
{
"path": "/api/team/zeit",
"mustContain": "team/zeit",
"status": 200
},
{
"path": "/api/user/myself",
"mustContain": "user/myself",
"status": 200
},
{
"path": "/api/not-okay/",
"status": 404
},
{
"path": "/api",
"status": 404
},
{
"path": "/api/",
"status": 404
},
{
"path": "/",
"mustContain": "hello from index.txt"
}
]
}

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