Compare commits

...

40 Commits

Author SHA1 Message Date
Steven
27f4034bdc Publish Stable
- @vercel/build-utils@5.4.4
 - vercel@28.4.0
 - @vercel/client@12.2.6
 - @vercel/go@2.2.7
 - @vercel/hydrogen@0.0.20
 - @vercel/next@3.1.27
 - @vercel/node@2.5.16
 - @vercel/python@3.1.16
 - @vercel/redwood@1.0.25
 - @vercel/remix@1.0.26
 - @vercel/ruby@1.3.33
 - @vercel/static-build@1.0.25
2022-09-21 09:24:15 -04:00
Steven
e43b968fbe [build-utils] Retry with legacy peer deps on install failure (#8598)
In this PR, we try to detect when npm install fails due to invalid peer deps and retry with `--legacy-peer-deps`.

This is a follow up to PR #8550 that explains more.

Steps to reproduce the npm bug:

```sh
corepack enable npm
echo '{"packageManager":"npm@8.5.5","dependencies":{"swr":"1.3.0","react":"16.8.0"}}' > package.json
npm install --legacy-peer-deps
npm install # works fine now that a lockfile exists
echo '{"packageManager":"npm@8.6.0","dependencies":{"swr":"1.3.0","react":"16.8.0"}}' > package.json
npm install # fails with code ERESOLVE (Conflicting peer dependency)
```
2022-09-21 13:14:21 +00:00
Chris Barber
48eb720556 [build-utils][next] Remove Path once copied to PATH. (#8596)
Windows doesn't like it when the env has both a `PATH` and a `Path`, so `cloneEnv()` must remove `Path` after copying it to `PATH`.

Renamed the next builder `01-normalize-routes` test to `01-normalize-paths` as it's test normalization of output paths, then re-enabled the test for Windows.

### 📋 Checklist

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

#### Tests

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

#### Code Review

- [ ] This PR has a concise title and thorough description useful to a reviewer
- [ ] Issue from task tracker has a link to this PR
2022-09-20 22:10:26 +00:00
Steven
30e4d74db6 [tests] Add turbo caching to publish (#8597) 2022-09-20 20:50:56 +00:00
Steven
75840896ee [tests] Update workflows to use global env (#8594)
Follow up to #8586 because [that syntax](https://docs.github.com/en/actions/using-workflows/workflow-commands-for-github-actions#setting-an-environment-variable) never worked on windows
2022-09-20 20:16:54 +00:00
Nathan Rajlich
9657bda86d [cli] Add "images" optimization configuration support in vercel.json (#8566)
This is a (further 😄) continuation of #8379 by @theMosaad, so that the tests can run.

I also added a change to make `images` _not_ be sent to the create deployment API endpoint, since it fails validation there.
2022-09-20 19:58:20 +00:00
Steven
4536029d42 Revert "[fs-detectors] Rush json workspace" (#8591)
Reverts vercel/vercel#8565 since tests are failing on windows

https://github.com/vercel/vercel/actions/runs/3091763554/jobs/5002704682
2022-09-20 15:13:23 -04:00
Steven
a35654d047 [tests] Skip test 01-normalize-routes temporarily (#8590)
The  `01-normalize-routes` test is failing on windows so we need to temporarily disable until the root cause can be corrected.
2022-09-20 14:51:01 -04:00
github-actions[bot]
f6a01a1af3 [examples] Upgrade Next.js to version 12.3.1 (#8587)
This auto-generated PR updates Next.js to version 12.3.1
Co-authored-by: Steven <steven@ceriously.com>
2022-09-20 14:13:12 -04:00
chloetedder
c2bca954e0 [fs-detectors] Rush json workspace (#8565)
### Related Issues

Add in Rush workspace

### 📋 Checklist

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

#### Tests

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

#### Code Review

- [ ] This PR has a concise title and thorough description useful to a
reviewer
- [ ] Issue from task tracker has a link to this PR
2022-09-20 12:14:45 -05:00
Steven
21701c3c3d [tests] Fix turbo remote cache (#8586)
Follow up to #7874 since the remote cache was not used on `main`.

We can unconditionally set these env vars with the latest turbo so it will work for contributors too.
2022-09-20 12:19:38 -04:00
JJ Kasper
7f4723b3f8 [next] Update local app edge test (#8588)
### Related Issues

Fixes:
https://github.com/vercel/vercel/actions/runs/3086450305/jobs/4990850756

### 📋 Checklist

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

#### Tests

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

#### Code Review

- [ ] This PR has a concise title and thorough description useful to a
reviewer
- [ ] Issue from task tracker has a link to this PR
2022-09-19 18:15:13 -07:00
Sean Massa
619ca93421 Publish Stable
- @vercel/build-utils@5.4.3
 - vercel@28.3.0
 - @vercel/client@12.2.5
 - @vercel/frameworks@1.1.6
 - @vercel/fs-detectors@3.3.0
 - @vercel/go@2.2.6
 - @vercel/hydrogen@0.0.19
 - @vercel/next@3.1.26
 - @vercel/node@2.5.15
 - @vercel/python@3.1.15
 - @vercel/redwood@1.0.24
 - @vercel/remix@1.0.25
 - @vercel/ruby@1.3.32
 - @vercel/static-build@1.0.24
2022-09-19 16:40:10 -05:00
Steven
cea2981512 [tests] Change some runNpmInstall() unit tests to mocks (#8581)
- Closes #8580
2022-09-19 21:30:32 +00:00
JJ Kasper
1f30e3a4b7 [next] Update app test fixture (#8584)
### Related Issues

Updates our test fixture for related changes in latest canary of
Next.js.

Fixes: https://vercel.slack.com/archives/CGU8HUTUH/p1663607276817069

### 📋 Checklist

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

#### Tests

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

#### Code Review

- [ ] This PR has a concise title and thorough description useful to a
reviewer
- [ ] Issue from task tracker has a link to this PR
2022-09-19 13:08:10 -07:00
Steven
20e8fdd049 [tests] Add yarn cache to github actions (#8567)
We had this enable for some workflows but not all so this PR enables the yarn cache for the remaining workflows.
2022-09-15 02:05:26 +00:00
Chris Barber
3df8313d69 [next] Use posix path separator to join routes and overrides paths (#8530)
Use posix path separators for routes (static pages, lambdas, etc) instead of the native platform path separator (e.g. backslash on Windows).

#### Tests

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

#### Code Review

- [ ] This PR has a concise title and thorough description useful to a reviewer
- [ ] Issue from task tracker has a link to this PR
2022-09-14 23:47:57 +00:00
Steven
95c9ea92c4 [build-utils] Add flag for npm install --legacy-peer-deps (#8550)
Prior to `npm@8.6.0`, running `npm install --legacy-peer-deps` to generate a lock file and then later running `npm install` would work fine.

After `npm@8.6.0`, running `npm install` with that same lock file will fail with `code ERESOLVE` and `Conflicting peer dependency`.

Steps to reproduce:

```sh
corepack enable npm
echo '{"packageManager":"npm@8.5.5","dependencies":{"next":"12.3.0","react":"16.8.0"}}' > package.json
npm install --legacy-peer-deps
npm install # works
echo '{"packageManager":"npm@8.6.0","dependencies":{"next":"12.3.0","react":"16.8.0"}}' > package.json
npm install # fails with code ERESOLVE
```


This PR introduces a flag so we can safely upgrade npm to a newer version without breaking thousands of deployments.

The [npm docs](https://docs.npmjs.com/cli/v8/using-npm/config#legacy-peer-deps) say the flag is not recommended so we also make sure that once we upgrade to Node.js 18, we stop flagging `--legacy-peer-deps` because its safe to introduce breaking changes.
2022-09-14 22:18:40 +00:00
Chris Barber
7ddebb099d [build-utils][cli][go][node][ruby][static-build] Explicitly set PATH when copying env vars (#8532)
On Windows 10 and 11 machines, environment variables are not case sensitive. The system PATH is actually defined as `process.env.Path`, however Node.js kindly handles the case sensitivity and will automatically return the system path when specifying `process.env.PATH`.

When we clone the environment variables via `{ ...process.env }`, we lose the automatic resolving of `Path` to `PATH`. To fix this, we need to explicitly copy the `PATH`.

#### Tests

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

#### Code Review

- [ ] This PR has a concise title and thorough description useful to a reviewer
- [ ] Issue from task tracker has a link to this PR
2022-09-14 20:29:52 +00:00
chloetedder
6498fd1aab [fs-detectors] Add Nx Workspace (#8549)
### Related Issues


https://nx.dev/more-concepts/folder-structure#integrated-repo-folder-structure

Nx monorepo has an option to use Nx workspaces.
Nx workspace is defined within the root `workspace.json` file
Within this `workspace.json` file the workspace packages are under
projects
```{
  "$schema": "./node_modules/nx/schemas/workspace-schema.json",
  "version": 2,
  "projects": {
    "myblog": "apps/myblog",
    "svelte-app": "apps/svelte-app",
  }
}
```
Within `getNxWorkspacePackagePaths` get the projects object values for
the paths
Nx is listed as the last workspace manager within
`workspace-managers.ts` because other workspace managers could exist to
check for before Nx workspaces because the `workspace.json` could exist
but not be the correct workspace manager

Nx workspace file can exist when yarn/npm workspaces exist. When this
happens, the workspace.json file is empty with no projects so it will
not add any package paths to the list to look through for projects.


### 📋 Checklist

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

#### Tests

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

#### Code Review

- [ ] This PR has a concise title and thorough description useful to a
reviewer
- [ ] Issue from task tracker has a link to this PR
2022-09-14 15:10:14 -05:00
Nathan Rajlich
9d7d822da3 [tests] Add test for path segments when go.mod is present (#7361)
Path segment previously were not working when a `go.mod` file is present. This was fixed likely by https://github.com/vercel/vercel/pull/8291, so now this PR is just a test to ensure this case stays working.
2022-09-14 19:10:49 +00:00
Nathan Rajlich
b8110d97d1 [tests] Add more tests for importBuilders() scenarios (#8554)
Covers:
  - Importing a built-in Builder with `@latest` should use the CLI's version
  - Importing a built-in Builder with `@canary` should use the CLI's version
  - Importing a built-in Builder with an explicit version should install to `.vercel/builders`
2022-09-13 23:56:10 +00:00
Nathan Rajlich
2d9373b0f1 [tests] Fix "50-ionic-react" test fixture (#8556)
Update "typescript" and add a `yarn.lock` file.

It started failing without the lockfile with compilation issues:

```
$ react-scripts build
Creating an optimized production build...
Failed to compile.
/vercel/path1/node_modules/@types/react-router/index.d.ts
TypeScript error in /vercel/path1/node_modules/@types/react-router/index.d.ts(149,100):
Type expected.  TS1110
    147 | ): match<Params> | null;
    148 |
  > 149 | export type ExtractRouteOptionalParam<T extends string, U = string | number | boolean> = T extends `${infer Param}?`
        |                                                                                                    ^
    150 |     ? { [k in Param]?: U }
    151 |     : T extends `${infer Param}*`
    152 |     ? { [k in Param]?: U }
error Command failed with exit code 1.
```
2022-09-13 23:28:36 +00:00
Nathan Rajlich
cc7577a648 [cli] Use Builder importing logic from vc build in vc dev (#8485)
This makes `vc dev` utilize the same Builder installing and importing logic that `vc build` uses. So now the Builders will be installed locally on a per-project basis, rather than being installed to a shared global installation directory.
2022-09-13 21:35:01 +00:00
Nathan Rajlich
f3f3d7df5b [cli] Replace "0.0.0.0" with "localhost" in vc dev (#8547)
Instead of replacing "127.0.0.1" in `vc dev`, replace "0.0.0.0" when rendering the URL to access the `vc dev` server.

The thought here is that the IPv6 form is replacing the catch-all host name (`::`), but the IPv4 version is only replacing the explicit "127.0.0.1" version, so there's an inconsistency.

Considering the catch-all host names are not usually intended to be routable anyways, we will fix the inconsistency by only replacing the catch-all versions instead of localhost.

Also, since Node.js will default to binding to the catch-all address when not explicitly specifying a host, this replacement will only happen in that case. If the user is explicitly specifing a host to bind to (i.e. `vc dev -l 127.0.0.1:8080` or `vc dev -l tcp://[::1]:8080`) then it makes more sense to print explicitly what the user specified.
2022-09-13 18:38:42 +00:00
Steven
0bafea6d51 [tests] Fix tarball lookup with sha contains 0 (#8552)
This PR fixes an issue when the sha begins with `0` and causes [an error](https://vercel.com/vercel/vercel/EP7fVcXKsoodzWRy3fwG8AoeQcs6) when looking up the tarball:

> Error: ENOENT: no such file or directory, 
> copyfile '/vercel/path0/packages/build-utils/vercel-build-utils-v5.4.2-0251253.tgz' 
>  -> '/vercel/path0/public/tarballs/@vercel/build-utils.tgz'

The fix is to no longer rely on the exact tarball name because we can't guarantee the name from `yarn pack` and instead rely on a pattern for the tarball name.
2022-09-13 18:08:45 +00:00
Nathan Rajlich
5abb74eddd [cli] Use isStaticRuntime() helper function when importing Builders (#8551) 2022-09-13 12:40:06 +00:00
Steven
23d7eaccab [frameworks] Update framework logos to be file paths (#8546)
This removes the reliance on raw github hosting and instead relies on the Vercel deployment hosting the logo images.

Since we already have static files in each deployment (tarballs), it makes sense to start adding static images too.
2022-09-12 23:07:46 +00:00
Nathan Rajlich
2cb008e8ed [cli] Switch to npm and add more context to install errors during Builder import (#8545)
Before:

<img width="1012" alt="Screen Shot 2022-09-09 at 7 03 01 PM" src="https://user-images.githubusercontent.com/71256/189464732-8cf6398a-9432-423b-8509-a52bd714333e.png">

After:

<img width="579" alt="Screen Shot 2022-09-12 at 12 27 31 PM" src="https://user-images.githubusercontent.com/71256/189739091-86399428-d9b8-4d03-b0e6-7d27a1037bce.png">

This is a precursor to https://github.com/vercel/vercel/pull/8485, since the last remaining test failure there is related to specialized messaging we had for these same cases.
2022-09-12 22:05:38 +00:00
chloetedder
b5c3610113 Publish Stable
- vercel@28.2.5
 - @vercel/fs-detectors@3.2.0
2022-09-12 11:32:13 -05:00
chloetedder
f23c7fc4fc [fs-detectors] Add monorepo managers (#8534)
### Related Issues

Adding in Nx and Rush as monorepo managers.
This will allow to help with starting zero config for both the above managers.
I have added in unit tests for both Nx and Rush.

### 📋 Checklist

#### Tests

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

#### Code Review

- [ ] This PR has a concise title and thorough description useful to a reviewer
- [ ] Issue from task tracker has a link to this PR
2022-09-09 22:51:56 +00:00
Sean Massa
3dc1ad6437 [dev] consolidate dev server address parsing (#8538)
Consolidates parsing and access to `this.address` so that we don't have to `new URL(this.address)` all over the place.

---

Follow-up from: https://github.com/vercel/vercel/pull/8457#issuecomment-1238653904
2022-09-09 22:07:58 +00:00
github-actions[bot]
4dc0bef62b [examples] Upgrade Next.js to version 12.3.0 (#8537)
This auto-generated PR updates Next.js to version 12.3.0

Co-authored-by: Steven <steven@ceriously.com>
2022-09-09 17:41:41 -04:00
Sean Massa
8b3c52b9e8 Publish Stable
- vercel@28.2.4
 - @vercel/frameworks@1.1.5
 - @vercel/fs-detectors@3.1.0
 - @vercel/static-build@1.0.23
2022-09-09 16:28:36 -05:00
Sean Massa
fc74300ad0 [dev] fix middleware rewrites to relative paths that do not exist (#8535)
When using a `rewrite` in middleware to a relative path that does not exist, the logic gets confused and falls back to the original path.

This was caused by two changes that, in combination, caused this behavior.

1. `prevUrl` was created to keep track of route results that point to other routes, but falls back to `req.url` (`prevUrl`'s initial value): [#4033](40e4b69267 (diff-142c93a61d03a1718eb765cd25e5f18d07a95358bb27ce5d5d4da314ee2fa286R1283-R1284))
2. `prevUrl` was reassigned when handling middleware: [#7973](ee1211416f (diff-00ef6e7b63ed4cae298afc2a8c84f49247855a2506270f748e4d3e41da97ad99R1538))

Because `prevUrl` was reset back to `req.url` after the first `phase` (null) was run, the updated `prevUrl` value from middleware was lost. This only matters if the second `phase` ("filesystem") was going to be hit.

Further confusing matters, this was partially fixed by https://github.com/vercel/vercel/pull/8457 because it either returned a proxy pass (which doesn't have this problem) or assigned `req.url` to include the `rewritePath`, which meant that later when `prevUrl` would default to `req.url`, it still had come from `rewritePath`. So, this is fixed for the absolute URL case, but not the relative path case.

Given all that, I think the fix we need is to keep `prevUrl` at its current value when it's not being updated based on the `routeResult`.

---

So, I made that change here. I added a test that exercises this specific behavior.
2022-09-09 20:34:14 +00:00
Andrew Gadzik
28f8a38e00 [fs-detectors] Add the writeFile function to DetectorFilesystem (#8536)
Adds a `writeFile` function to `DetectorFilesystem` that will be used to update the various file cache maps.

**Why is this needed?**

When detecting npm7+ monorepos, we identified a performance improvement where the service can inspect the `package-lock.json` file for workspaces, and reuse the package information for each workspace in framework-detection.

The pseudo code in `vercel/api` will look something like this

For a given lockfile
```json
{
  ...,
  "packages": {
    "": {
      "name": "npm-workspaces",
      "version": "1.0.0",
      "license": "ISC",
      "workspaces": {
        "packages": [
          "apps/*"
        ]
      }
    },
    "apps/admin": {
      "version": "0.1.0",
      "dependencies": {
        "next": "12.2.5",
        "react": "18.2.0",
        "react-dom": "18.2.0"
      },
      "devDependencies": {
        "eslint": "8.23.0",
        "eslint-config-next": "12.2.5"
      }
    },
    ...,
}
```

```ts
// for each projectPath we detect in package-lock.json
// switch the cwd of the fs to the project directory
const projectFs = fs.chdir(projectPath);
// gets the package info from the lockfile
const projectPackageInfo = lockFile.packages[projectPath];
// insert this content into fs cache
await projectFs.writeFile('package.json', projectPackageInfo)
// call detectFramework, which should now have a cached "package.json" file 
const projectFramework = await detectFramework(projectFs);
```

### Related Issues

Related to https://linear.app/vercel/issue/HIT-57/monorepo-detection-api-prevent-rate-limits

### 📋 Checklist

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

#### Tests

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

#### Code Review

- [x] This PR has a concise title and thorough description useful to a reviewer
- [x] Issue from task tracker has a link to this PR
2022-09-09 19:39:05 +00:00
Jacob Ford
c318ce9695 [frameworks] Detect Sanity v3 config (#8540)
Sanity v3 is currently in [developer preview](https://www.sanity.io/blog/sanity-studio-v3-developer-preview), and has deprecated `sanity.json` in the project root, replacing it with `sanity.config.js`. This should detect either.

All commands remain the same in both v2 and v3.

Co-authored-by: Steven <steven@ceriously.com>
2022-09-09 14:59:12 -04:00
Nathan Rajlich
5b36eaacff [cli] Poll the Dev Command server on both IPv4 and IPv6 in vc dev (#8542)
This change will allow the downstream Dev Command server be able to listen on either of `127.0.0.1` for IPv4 or `[::1]` for IPv6.

- Fixes #6639
- Fixes #8511
- Fixes #8121
- Closes #8512
- Closes #8384
2022-09-09 14:57:50 -04:00
Chris Barber
c9f7ca23a8 [tests] Skip tests that won't run on Windows due to 'mkdir -p' and symlinks (#8531)
Found a few tests that won't run on Windows. The project settings override test runs `mkdir -p` which errors. The other two build tests fail because they try to create symlinks.

#### Tests

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

#### Code Review

- [ ] This PR has a concise title and thorough description useful to a reviewer
- [ ] Issue from task tracker has a link to this PR
2022-09-08 14:26:30 +00:00
Sean Massa
57e0db0f65 [dev] allow middleware rewrites to change origin in vc dev (#8457)
Middleware rewrites in `vc dev` were not respecting changes to the origin. This PR implements that to match prodution.

---

Paired with: @TooTallNate
2022-09-07 20:30:10 +00:00
161 changed files with 14786 additions and 1619 deletions

View File

@@ -7,6 +7,11 @@ on:
tags:
- '!*'
env:
TURBO_REMOTE_ONLY: 'true'
TURBO_TEAM: 'vercel'
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
jobs:
publish:
name: Publish
@@ -34,6 +39,7 @@ jobs:
uses: actions/setup-node@v3
with:
node-version: 14
cache: 'yarn'
- name: Install
if: ${{ steps.check-release.outputs.IS_RELEASE == 'true' }}
run: yarn install --check-files --frozen-lockfile --network-timeout 1000000

View File

@@ -8,6 +8,11 @@ on:
- '!*'
pull_request:
env:
TURBO_REMOTE_ONLY: 'true'
TURBO_TEAM: 'vercel'
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
jobs:
test:
name: CLI
@@ -19,25 +24,16 @@ jobs:
node: [14]
runs-on: ${{ matrix.os }}
steps:
- name: Conditionally set remote env
if: github.event.pull_request.head.repo.full_name == github.repository
run: |
echo "TURBO_REMOTE_ONLY=true" >> $GITHUB_ENV
echo "TURBO_TEAM=vercel" >> $GITHUB_ENV
echo "TURBO_TOKEN=${{ secrets.TURBO_TOKEN }}" >> $GITHUB_ENV
- uses: actions/checkout@v3
with:
fetch-depth: 2
- uses: actions/setup-go@v3
with:
go-version: '1.13.15'
- uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node }}
- uses: actions/checkout@v3
with:
fetch-depth: 100
- run: git --version
- run: git fetch origin main --depth=100
- run: git fetch origin ${{ github.ref }} --depth=100
- run: git diff origin/main...HEAD --name-only
cache: 'yarn'
- run: yarn install --network-timeout 1000000 --frozen-lockfile
- run: yarn run build
- run: yarn test-integration-cli

View File

@@ -8,6 +8,11 @@ on:
- '!*'
pull_request:
env:
TURBO_REMOTE_ONLY: 'true'
TURBO_TEAM: 'vercel'
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
jobs:
test:
name: Unit
@@ -19,25 +24,16 @@ jobs:
node: [14]
runs-on: ${{ matrix.os }}
steps:
- name: Conditionally set remote env
if: github.event.pull_request.head.repo.full_name == github.repository
run: |
echo "TURBO_REMOTE_ONLY=true" >> $GITHUB_ENV
echo "TURBO_TEAM=vercel" >> $GITHUB_ENV
echo "TURBO_TOKEN=${{ secrets.TURBO_TOKEN }}" >> $GITHUB_ENV
- uses: actions/setup-go@v3
with:
go-version: '1.13.15'
- uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node }}
- uses: actions/checkout@v3
with:
fetch-depth: 100
- run: git --version
- run: git fetch origin main --depth=100
- run: git fetch origin ${{ github.ref }} --depth=100
- run: git diff origin/main...HEAD --name-only
fetch-depth: 2
- uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node }}
cache: 'yarn'
- run: yarn install --network-timeout 1000000 --frozen-lockfile
- run: yarn run build
- run: yarn run lint

View File

@@ -10,6 +10,9 @@ on:
env:
NODE_VERSION: '14'
TURBO_REMOTE_ONLY: 'true'
TURBO_TEAM: 'vercel'
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
jobs:
setup:
@@ -20,8 +23,11 @@ jobs:
dplUrl: ${{ steps.waitForTarball.outputs.url }}
steps:
- uses: actions/checkout@v3
- run: git --version
- run: git fetch origin main
with:
fetch-depth: 2
- uses: actions/setup-go@v3
with:
go-version: '1.13.15'
- uses: actions/setup-node@v3
with:
node-version: ${{ env.NODE_VERSION }}
@@ -52,12 +58,6 @@ jobs:
matrix:
include: ${{ fromJson(needs.setup.outputs['tests']) }}
steps:
- name: Conditionally set remote env
if: github.event.pull_request.head.repo.full_name == github.repository
run: |
echo "TURBO_REMOTE_ONLY=true" >> $GITHUB_ENV
echo "TURBO_TEAM=vercel" >> $GITHUB_ENV
echo "TURBO_TOKEN=${{ secrets.TURBO_TOKEN }}" >> $GITHUB_ENV
- uses: actions/checkout@v3
with:
fetch-depth: 2

View File

@@ -1,6 +1,5 @@
import fs from 'fs/promises';
import { join, dirname } from 'path';
import execa from 'execa';
import { getExampleList } from '../examples/example-list';
import { mapOldToNew } from '../examples/map-old-to-new';
@@ -13,6 +12,12 @@ async function main() {
await fs.rm(pubDir, { recursive: true, force: true });
await fs.mkdir(pubDir);
await fs.cp(
join(repoRoot, 'packages', 'frameworks', 'logos'),
join(pubDir, 'framework-logos'),
{ recursive: true, force: true }
);
const examples = await getExampleList();
const pathListAll = join(pubDir, 'list-all.json');
await fs.writeFile(pathListAll, JSON.stringify(examples));
@@ -41,10 +46,6 @@ async function main() {
JSON.stringify([...existingExamples, ...oldExamples])
);
const { stdout: sha } = await execa('git', ['rev-parse', '--short', 'HEAD'], {
cwd: repoRoot,
});
const tarballsDir = join(pubDir, 'tarballs');
const packagesDir = join(repoRoot, 'packages');
const packages = await fs.readdir(packagesDir);
@@ -55,12 +56,21 @@ async function main() {
'utf-8'
);
const packageJson = JSON.parse(packageJsonRaw);
const tarballName = `${packageJson.name
.replace('@', '')
.replace('/', '-')}-v${packageJson.version}-${sha.trim()}.tgz`;
const files = await fs.readdir(fullDir);
const tarballName = files.find(f => /^vercel-.+\.tgz$/.test(f));
if (!tarballName) {
throw new Error(
`Expected vercel-*.tgz in ${fullDir} but found ${JSON.stringify(
files,
null,
2
)}`
);
}
const srcTarballPath = join(fullDir, tarballName);
const destTarballPath = join(tarballsDir, `${packageJson.name}.tgz`);
await fs.mkdir(dirname(destTarballPath), { recursive: true });
await fs.copyFile(join(fullDir, tarballName), destTarballPath);
await fs.copyFile(srcTarballPath, destTarballPath);
}
console.log('Completed building static frontend.');

View File

@@ -16,10 +16,6 @@ const frameworks = (_frameworks as Framework[])
defaultRoutes: undefined,
};
if (framework.logo) {
framework.logo = `https://assets.vercel.com/zeit-inc/image/fetch/${framework.logo}`;
}
return framework;
});

View File

@@ -9,12 +9,12 @@
"lint": "next lint"
},
"dependencies": {
"next": "12.2.5",
"next": "12.3.1",
"react": "18.2.0",
"react-dom": "18.2.0"
},
"devDependencies": {
"eslint": "8.22.0",
"eslint-config-next": "12.2.5"
"eslint": "8.23.1",
"eslint-config-next": "12.3.1"
}
}

View File

@@ -3,28 +3,28 @@
"@babel/runtime-corejs3@^7.10.2":
version "7.18.9"
resolved "https://registry.yarnpkg.com/@babel/runtime-corejs3/-/runtime-corejs3-7.18.9.tgz#7bacecd1cb2dd694eacd32a91fcf7021c20770ae"
integrity sha512-qZEWeccZCrHA2Au4/X05QW5CMdm4VjUDCrGq5gf1ZDcM4hRqreKrtwAn7yci9zfgAS9apvnsFXiGBHBAxZdK9A==
version "7.19.1"
resolved "https://registry.yarnpkg.com/@babel/runtime-corejs3/-/runtime-corejs3-7.19.1.tgz#f0cbbe7edda7c4109cd253bb1dee99aba4594ad9"
integrity sha512-j2vJGnkopRzH+ykJ8h68wrHnEUmtK//E723jjixiAl/PPf6FhqY/vYRcMVlNydRKQjQsTsYEjpx+DZMIvnGk/g==
dependencies:
core-js-pure "^3.20.2"
core-js-pure "^3.25.1"
regenerator-runtime "^0.13.4"
"@babel/runtime@^7.10.2", "@babel/runtime@^7.18.9":
version "7.18.9"
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.18.9.tgz#b4fcfce55db3d2e5e080d2490f608a3b9f407f4a"
integrity sha512-lkqXDcvlFT5rvEjiu6+QYO+1GXrEHRo2LOtS7E4GtX5ESIZOgepqsZBVIj6Pv+a6zqsya9VCgiK1KAK4BvJDAw==
version "7.19.0"
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.19.0.tgz#22b11c037b094d27a8a2504ea4dcff00f50e2259"
integrity sha512-eR8Lo9hnDS7tqkO7NsV+mKvCmv5boaXFSZ70DnfhcgiEne8hv9oCEd36Klw74EtizEqLsy4YnW8UWwpBVolHZA==
dependencies:
regenerator-runtime "^0.13.4"
"@eslint/eslintrc@^1.3.0":
version "1.3.0"
resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-1.3.0.tgz#29f92c30bb3e771e4a2048c95fa6855392dfac4f"
integrity sha512-UWW0TMTmk2d7hLcWD1/e2g5HDM/HQ3csaLSqXCfqwh4uNDuNqlaKWXmEsL4Cs41Z0KnILNvwbHAah3C2yt06kw==
"@eslint/eslintrc@^1.3.2":
version "1.3.2"
resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-1.3.2.tgz#58b69582f3b7271d8fa67fe5251767a5b38ea356"
integrity sha512-AXYd23w1S/bv3fTs3Lz0vjiYemS08jWkI3hYyS9I1ry+0f+Yjs1wm+sU0BS8qDOPrBIkp4qHYC16I8uVtpLajQ==
dependencies:
ajv "^6.12.4"
debug "^4.3.2"
espree "^9.3.2"
espree "^9.4.0"
globals "^13.15.0"
ignore "^5.2.0"
import-fresh "^3.2.1"
@@ -46,87 +46,92 @@
resolved "https://registry.yarnpkg.com/@humanwhocodes/gitignore-to-minimatch/-/gitignore-to-minimatch-1.0.2.tgz#316b0a63b91c10e53f242efb4ace5c3b34e8728d"
integrity sha512-rSqmMJDdLFUsyxR6FMtD00nfQKKLFb1kv+qBbOVKqErvloEIJLo5bDTJTQNTYgeyp78JsA7u/NPi5jT1GR/MuA==
"@humanwhocodes/module-importer@^1.0.1":
version "1.0.1"
resolved "https://registry.yarnpkg.com/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz#af5b2691a22b44be847b0ca81641c5fb6ad0172c"
integrity sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==
"@humanwhocodes/object-schema@^1.2.1":
version "1.2.1"
resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz#b520529ec21d8e5945a1851dfd1c32e94e39ff45"
integrity sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==
"@next/env@12.2.5":
version "12.2.5"
resolved "https://registry.yarnpkg.com/@next/env/-/env-12.2.5.tgz#d908c57b35262b94db3e431e869b72ac3e1ad3e3"
integrity sha512-vLPLV3cpPGjUPT3PjgRj7e3nio9t6USkuew3JE/jMeon/9Mvp1WyR18v3iwnCuX7eUAm1HmAbJHHLAbcu/EJcw==
"@next/env@12.3.1":
version "12.3.1"
resolved "https://registry.yarnpkg.com/@next/env/-/env-12.3.1.tgz#18266bd92de3b4aa4037b1927aa59e6f11879260"
integrity sha512-9P9THmRFVKGKt9DYqeC2aKIxm8rlvkK38V1P1sRE7qyoPBIs8l9oo79QoSdPtOWfzkbDAVUqvbQGgTMsb8BtJg==
"@next/eslint-plugin-next@12.2.5":
version "12.2.5"
resolved "https://registry.yarnpkg.com/@next/eslint-plugin-next/-/eslint-plugin-next-12.2.5.tgz#4f3acccd2ed4f9300fbf9fd480cc8a0b261889a8"
integrity sha512-VBjVbmqEzGiOTBq4+wpeVXt/KgknnGB6ahvC/AxiIGnN93/RCSyXhFRI4uSfftM2Ba3w7ZO7076bfKasZsA0fw==
"@next/eslint-plugin-next@12.3.1":
version "12.3.1"
resolved "https://registry.yarnpkg.com/@next/eslint-plugin-next/-/eslint-plugin-next-12.3.1.tgz#b821f27b0f175954d8d18e5d323fce040ecc79a6"
integrity sha512-sw+lTf6r6P0j+g/n9y4qdWWI2syPqZx+uc0+B/fRENqfR3KpSid6MIKqc9gNwGhJASazEQ5b3w8h4cAET213jw==
dependencies:
glob "7.1.7"
"@next/swc-android-arm-eabi@12.2.5":
version "12.2.5"
resolved "https://registry.yarnpkg.com/@next/swc-android-arm-eabi/-/swc-android-arm-eabi-12.2.5.tgz#903a5479ab4c2705d9c08d080907475f7bacf94d"
integrity sha512-cPWClKxGhgn2dLWnspW+7psl3MoLQUcNqJqOHk2BhNcou9ARDtC0IjQkKe5qcn9qg7I7U83Gp1yh2aesZfZJMA==
"@next/swc-android-arm-eabi@12.3.1":
version "12.3.1"
resolved "https://registry.yarnpkg.com/@next/swc-android-arm-eabi/-/swc-android-arm-eabi-12.3.1.tgz#b15ce8ad376102a3b8c0f3c017dde050a22bb1a3"
integrity sha512-i+BvKA8tB//srVPPQxIQN5lvfROcfv4OB23/L1nXznP+N/TyKL8lql3l7oo2LNhnH66zWhfoemg3Q4VJZSruzQ==
"@next/swc-android-arm64@12.2.5":
version "12.2.5"
resolved "https://registry.yarnpkg.com/@next/swc-android-arm64/-/swc-android-arm64-12.2.5.tgz#2f9a98ec4166c7860510963b31bda1f57a77c792"
integrity sha512-vMj0efliXmC5b7p+wfcQCX0AfU8IypjkzT64GiKJD9PgiA3IILNiGJr1fw2lyUDHkjeWx/5HMlMEpLnTsQslwg==
"@next/swc-android-arm64@12.3.1":
version "12.3.1"
resolved "https://registry.yarnpkg.com/@next/swc-android-arm64/-/swc-android-arm64-12.3.1.tgz#85d205f568a790a137cb3c3f720d961a2436ac9c"
integrity sha512-CmgU2ZNyBP0rkugOOqLnjl3+eRpXBzB/I2sjwcGZ7/Z6RcUJXK5Evz+N0ucOxqE4cZ3gkTeXtSzRrMK2mGYV8Q==
"@next/swc-darwin-arm64@12.2.5":
version "12.2.5"
resolved "https://registry.yarnpkg.com/@next/swc-darwin-arm64/-/swc-darwin-arm64-12.2.5.tgz#31b1c3c659d54be546120c488a1e1bad21c24a1d"
integrity sha512-VOPWbO5EFr6snla/WcxUKtvzGVShfs302TEMOtzYyWni6f9zuOetijJvVh9CCTzInnXAZMtHyNhefijA4HMYLg==
"@next/swc-darwin-arm64@12.3.1":
version "12.3.1"
resolved "https://registry.yarnpkg.com/@next/swc-darwin-arm64/-/swc-darwin-arm64-12.3.1.tgz#b105457d6760a7916b27e46c97cb1a40547114ae"
integrity sha512-hT/EBGNcu0ITiuWDYU9ur57Oa4LybD5DOQp4f22T6zLfpoBMfBibPtR8XktXmOyFHrL/6FC2p9ojdLZhWhvBHg==
"@next/swc-darwin-x64@12.2.5":
version "12.2.5"
resolved "https://registry.yarnpkg.com/@next/swc-darwin-x64/-/swc-darwin-x64-12.2.5.tgz#2e44dd82b2b7fef88238d1bc4d3bead5884cedfd"
integrity sha512-5o8bTCgAmtYOgauO/Xd27vW52G2/m3i5PX7MUYePquxXAnX73AAtqA3WgPXBRitEB60plSKZgOTkcpqrsh546A==
"@next/swc-darwin-x64@12.3.1":
version "12.3.1"
resolved "https://registry.yarnpkg.com/@next/swc-darwin-x64/-/swc-darwin-x64-12.3.1.tgz#6947b39082271378896b095b6696a7791c6e32b1"
integrity sha512-9S6EVueCVCyGf2vuiLiGEHZCJcPAxglyckTZcEwLdJwozLqN0gtS0Eq0bQlGS3dH49Py/rQYpZ3KVWZ9BUf/WA==
"@next/swc-freebsd-x64@12.2.5":
version "12.2.5"
resolved "https://registry.yarnpkg.com/@next/swc-freebsd-x64/-/swc-freebsd-x64-12.2.5.tgz#e24e75d8c2581bfebc75e4f08f6ddbd116ce9dbd"
integrity sha512-yYUbyup1JnznMtEBRkK4LT56N0lfK5qNTzr6/DEyDw5TbFVwnuy2hhLBzwCBkScFVjpFdfiC6SQAX3FrAZzuuw==
"@next/swc-freebsd-x64@12.3.1":
version "12.3.1"
resolved "https://registry.yarnpkg.com/@next/swc-freebsd-x64/-/swc-freebsd-x64-12.3.1.tgz#2b6c36a4d84aae8b0ea0e0da9bafc696ae27085a"
integrity sha512-qcuUQkaBZWqzM0F1N4AkAh88lLzzpfE6ImOcI1P6YeyJSsBmpBIV8o70zV+Wxpc26yV9vpzb+e5gCyxNjKJg5Q==
"@next/swc-linux-arm-gnueabihf@12.2.5":
version "12.2.5"
resolved "https://registry.yarnpkg.com/@next/swc-linux-arm-gnueabihf/-/swc-linux-arm-gnueabihf-12.2.5.tgz#46d8c514d834d2b5f67086013f0bd5e3081e10b9"
integrity sha512-2ZE2/G921Acks7UopJZVMgKLdm4vN4U0yuzvAMJ6KBavPzqESA2yHJlm85TV/K9gIjKhSk5BVtauIUntFRP8cg==
"@next/swc-linux-arm-gnueabihf@12.3.1":
version "12.3.1"
resolved "https://registry.yarnpkg.com/@next/swc-linux-arm-gnueabihf/-/swc-linux-arm-gnueabihf-12.3.1.tgz#6e421c44285cfedac1f4631d5de330dd60b86298"
integrity sha512-diL9MSYrEI5nY2wc/h/DBewEDUzr/DqBjIgHJ3RUNtETAOB3spMNHvJk2XKUDjnQuluLmFMloet9tpEqU2TT9w==
"@next/swc-linux-arm64-gnu@12.2.5":
version "12.2.5"
resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-12.2.5.tgz#91f725ac217d3a1f4f9f53b553615ba582fd3d9f"
integrity sha512-/I6+PWVlz2wkTdWqhlSYYJ1pWWgUVva6SgX353oqTh8njNQp1SdFQuWDqk8LnM6ulheVfSsgkDzxrDaAQZnzjQ==
"@next/swc-linux-arm64-gnu@12.3.1":
version "12.3.1"
resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-12.3.1.tgz#8863f08a81f422f910af126159d2cbb9552ef717"
integrity sha512-o/xB2nztoaC7jnXU3Q36vGgOolJpsGG8ETNjxM1VAPxRwM7FyGCPHOMk1XavG88QZSQf+1r+POBW0tLxQOJ9DQ==
"@next/swc-linux-arm64-musl@12.2.5":
version "12.2.5"
resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-12.2.5.tgz#e627e8c867920995810250303cd9b8e963598383"
integrity sha512-LPQRelfX6asXyVr59p5sTpx5l+0yh2Vjp/R8Wi4X9pnqcayqT4CUJLiHqCvZuLin3IsFdisJL0rKHMoaZLRfmg==
"@next/swc-linux-arm64-musl@12.3.1":
version "12.3.1"
resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-12.3.1.tgz#0038f07cf0b259d70ae0c80890d826dfc775d9f3"
integrity sha512-2WEasRxJzgAmP43glFNhADpe8zB7kJofhEAVNbDJZANp+H4+wq+/cW1CdDi8DqjkShPEA6/ejJw+xnEyDID2jg==
"@next/swc-linux-x64-gnu@12.2.5":
version "12.2.5"
resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-12.2.5.tgz#83a5e224fbc4d119ef2e0f29d0d79c40cc43887e"
integrity sha512-0szyAo8jMCClkjNK0hknjhmAngUppoRekW6OAezbEYwHXN/VNtsXbfzgYOqjKWxEx3OoAzrT3jLwAF0HdX2MEw==
"@next/swc-linux-x64-gnu@12.3.1":
version "12.3.1"
resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-12.3.1.tgz#c66468f5e8181ffb096c537f0dbfb589baa6a9c1"
integrity sha512-JWEaMyvNrXuM3dyy9Pp5cFPuSSvG82+yABqsWugjWlvfmnlnx9HOQZY23bFq3cNghy5V/t0iPb6cffzRWylgsA==
"@next/swc-linux-x64-musl@12.2.5":
version "12.2.5"
resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-12.2.5.tgz#be700d48471baac1ec2e9539396625584a317e95"
integrity sha512-zg/Y6oBar1yVnW6Il1I/08/2ukWtOG6s3acdJdEyIdsCzyQi4RLxbbhkD/EGQyhqBvd3QrC6ZXQEXighQUAZ0g==
"@next/swc-linux-x64-musl@12.3.1":
version "12.3.1"
resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-12.3.1.tgz#c6269f3e96ac0395bc722ad97ce410ea5101d305"
integrity sha512-xoEWQQ71waWc4BZcOjmatuvPUXKTv6MbIFzpm4LFeCHsg2iwai0ILmNXf81rJR+L1Wb9ifEke2sQpZSPNz1Iyg==
"@next/swc-win32-arm64-msvc@12.2.5":
version "12.2.5"
resolved "https://registry.yarnpkg.com/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-12.2.5.tgz#a93e958133ad3310373fda33a79aa10af2a0aa97"
integrity sha512-3/90DRNSqeeSRMMEhj4gHHQlLhhKg5SCCoYfE3kBjGpE63EfnblYUqsszGGZ9ekpKL/R4/SGB40iCQr8tR5Jiw==
"@next/swc-win32-arm64-msvc@12.3.1":
version "12.3.1"
resolved "https://registry.yarnpkg.com/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-12.3.1.tgz#83c639ee969cee36ce247c3abd1d9df97b5ecade"
integrity sha512-hswVFYQYIeGHE2JYaBVtvqmBQ1CppplQbZJS/JgrVI3x2CurNhEkmds/yqvDONfwfbttTtH4+q9Dzf/WVl3Opw==
"@next/swc-win32-ia32-msvc@12.2.5":
version "12.2.5"
resolved "https://registry.yarnpkg.com/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-12.2.5.tgz#4f5f7ba0a98ff89a883625d4af0125baed8b2e19"
integrity sha512-hGLc0ZRAwnaPL4ulwpp4D2RxmkHQLuI8CFOEEHdzZpS63/hMVzv81g8jzYA0UXbb9pus/iTc3VRbVbAM03SRrw==
"@next/swc-win32-ia32-msvc@12.3.1":
version "12.3.1"
resolved "https://registry.yarnpkg.com/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-12.3.1.tgz#52995748b92aa8ad053440301bc2c0d9fbcf27c2"
integrity sha512-Kny5JBehkTbKPmqulr5i+iKntO5YMP+bVM8Hf8UAmjSMVo3wehyLVc9IZkNmcbxi+vwETnQvJaT5ynYBkJ9dWA==
"@next/swc-win32-x64-msvc@12.2.5":
version "12.2.5"
resolved "https://registry.yarnpkg.com/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-12.2.5.tgz#20fed129b04a0d3f632c6d0de135345bb623b1e4"
integrity sha512-7h5/ahY7NeaO2xygqVrSG/Y8Vs4cdjxIjowTZ5W6CKoTKn7tmnuxlUc2h74x06FKmbhAd9agOjr/AOKyxYYm9Q==
"@next/swc-win32-x64-msvc@12.3.1":
version "12.3.1"
resolved "https://registry.yarnpkg.com/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-12.3.1.tgz#27d71a95247a9eaee03d47adee7e3bd594514136"
integrity sha512-W1ijvzzg+kPEX6LAc+50EYYSEo0FVu7dmTE+t+DM4iOLqgGHoW9uYSz9wCVdkXOEEMP9xhXfGpcSxsfDucyPkA==
"@nodelib/fs.scandir@2.1.5":
version "2.1.5"
@@ -150,14 +155,14 @@
fastq "^1.6.0"
"@rushstack/eslint-patch@^1.1.3":
version "1.1.4"
resolved "https://registry.yarnpkg.com/@rushstack/eslint-patch/-/eslint-patch-1.1.4.tgz#0c8b74c50f29ee44f423f7416829c0bf8bb5eb27"
integrity sha512-LwzQKA4vzIct1zNZzBmRKI9QuNpLgTQMEjsQLf3BXuGYb3QPTP4Yjf6mkdX+X1mYttZ808QpOwAzZjv28kq7DA==
version "1.2.0"
resolved "https://registry.yarnpkg.com/@rushstack/eslint-patch/-/eslint-patch-1.2.0.tgz#8be36a1f66f3265389e90b5f9c9962146758f728"
integrity sha512-sXo/qW2/pAcmT43VoRKOJbDOfV3cYpq3szSVfIThQXNt+E4DfKj361vaAt3c88U5tPUxzEswam7GW48PJqtKAg==
"@swc/helpers@0.4.3":
version "0.4.3"
resolved "https://registry.yarnpkg.com/@swc/helpers/-/helpers-0.4.3.tgz#16593dfc248c53b699d4b5026040f88ddb497012"
integrity sha512-6JrF+fdUK2zbGpJIlN7G3v966PQjyx/dPt1T9km2wj+EUBqgrxCk3uX4Kct16MIm9gGxfKRcfax2hVf5jvlTzA==
"@swc/helpers@0.4.11":
version "0.4.11"
resolved "https://registry.yarnpkg.com/@swc/helpers/-/helpers-0.4.11.tgz#db23a376761b3d31c26502122f349a21b592c8de"
integrity sha512-rEUrBSGIoSFuYxwBYtlUFMlE2CwGhmW+w9355/5oduSw8e5h2+Tj4UrAGNNgP9915++wj5vkQo0UuOBqOAq4nw==
dependencies:
tslib "^2.4.0"
@@ -167,47 +172,47 @@
integrity sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==
"@typescript-eslint/parser@^5.21.0":
version "5.34.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.34.0.tgz#ca710858ea85dbfd30c9b416a335dc49e82dbc07"
integrity sha512-SZ3NEnK4usd2CXkoV3jPa/vo1mWX1fqRyIVUQZR4As1vyp4fneknBNJj+OFtV8WAVgGf+rOHMSqQbs2Qn3nFZQ==
version "5.38.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.38.0.tgz#5a59a1ff41a7b43aacd1bb2db54f6bf1c02b2ff8"
integrity sha512-/F63giJGLDr0ms1Cr8utDAxP2SPiglaD6V+pCOcG35P2jCqdfR7uuEhz1GIC3oy4hkUF8xA1XSXmd9hOh/a5EA==
dependencies:
"@typescript-eslint/scope-manager" "5.34.0"
"@typescript-eslint/types" "5.34.0"
"@typescript-eslint/typescript-estree" "5.34.0"
"@typescript-eslint/scope-manager" "5.38.0"
"@typescript-eslint/types" "5.38.0"
"@typescript-eslint/typescript-estree" "5.38.0"
debug "^4.3.4"
"@typescript-eslint/scope-manager@5.34.0":
version "5.34.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.34.0.tgz#14efd13dc57602937e25f188fd911f118781e527"
integrity sha512-HNvASMQlah5RsBW6L6c7IJ0vsm+8Sope/wu5sEAf7joJYWNb1LDbJipzmdhdUOnfrDFE6LR1j57x1EYVxrY4ow==
"@typescript-eslint/scope-manager@5.38.0":
version "5.38.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.38.0.tgz#8f0927024b6b24e28671352c93b393a810ab4553"
integrity sha512-ByhHIuNyKD9giwkkLqzezZ9y5bALW8VNY6xXcP+VxoH4JBDKjU5WNnsiD4HJdglHECdV+lyaxhvQjTUbRboiTA==
dependencies:
"@typescript-eslint/types" "5.34.0"
"@typescript-eslint/visitor-keys" "5.34.0"
"@typescript-eslint/types" "5.38.0"
"@typescript-eslint/visitor-keys" "5.38.0"
"@typescript-eslint/types@5.34.0":
version "5.34.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.34.0.tgz#217bf08049e9e7b86694d982e88a2c1566330c78"
integrity sha512-49fm3xbbUPuzBIOcy2CDpYWqy/X7VBkxVN+DC21e0zIm3+61Z0NZi6J9mqPmSW1BDVk9FIOvuCFyUPjXz93sjA==
"@typescript-eslint/types@5.38.0":
version "5.38.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.38.0.tgz#8cd15825e4874354e31800dcac321d07548b8a5f"
integrity sha512-HHu4yMjJ7i3Cb+8NUuRCdOGu2VMkfmKyIJsOr9PfkBVYLYrtMCK/Ap50Rpov+iKpxDTfnqvDbuPLgBE5FwUNfA==
"@typescript-eslint/typescript-estree@5.34.0":
version "5.34.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.34.0.tgz#ba7b83f4bf8ccbabf074bbf1baca7a58de3ccb9a"
integrity sha512-mXHAqapJJDVzxauEkfJI96j3D10sd567LlqroyCeJaHnu42sDbjxotGb3XFtGPYKPD9IyLjhsoULML1oI3M86A==
"@typescript-eslint/typescript-estree@5.38.0":
version "5.38.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.38.0.tgz#89f86b2279815c6fb7f57d68cf9b813f0dc25d98"
integrity sha512-6P0RuphkR+UuV7Avv7MU3hFoWaGcrgOdi8eTe1NwhMp2/GjUJoODBTRWzlHpZh6lFOaPmSvgxGlROa0Sg5Zbyg==
dependencies:
"@typescript-eslint/types" "5.34.0"
"@typescript-eslint/visitor-keys" "5.34.0"
"@typescript-eslint/types" "5.38.0"
"@typescript-eslint/visitor-keys" "5.38.0"
debug "^4.3.4"
globby "^11.1.0"
is-glob "^4.0.3"
semver "^7.3.7"
tsutils "^3.21.0"
"@typescript-eslint/visitor-keys@5.34.0":
version "5.34.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.34.0.tgz#d0fb3e31033e82ddd5de048371ad39eb342b2d40"
integrity sha512-O1moYjOSrab0a2fUvFpsJe0QHtvTC+cR+ovYpgKrAVXzqQyc74mv76TgY6z+aEtjQE2vgZux3CQVtGryqdcOAw==
"@typescript-eslint/visitor-keys@5.38.0":
version "5.38.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.38.0.tgz#60591ca3bf78aa12b25002c0993d067c00887e34"
integrity sha512-MxnrdIyArnTi+XyFLR+kt/uNAcdOnmT+879os7qDRI+EYySR4crXJq9BXPfRzzLGq0wgxkwidrCJ9WCAoacm1w==
dependencies:
"@typescript-eslint/types" "5.34.0"
"@typescript-eslint/types" "5.38.0"
eslint-visitor-keys "^3.3.0"
acorn-jsx@^5.3.2:
@@ -339,10 +344,10 @@ callsites@^3.0.0:
resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73"
integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==
caniuse-lite@^1.0.30001332:
version "1.0.30001382"
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001382.tgz#4d37f0d0b6fffb826c8e5e1c0f4bf8ce592db949"
integrity sha512-2rtJwDmSZ716Pxm1wCtbPvHtbDWAreTPxXbkc5RkKglow3Ig/4GNGazDI9/BVnXbG/wnv6r3B5FEbkfg9OcTGg==
caniuse-lite@^1.0.30001406:
version "1.0.30001406"
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001406.tgz#d0146e7919635479f873b4507517b627f66ab269"
integrity sha512-bWTlaXUy/rq0BBtYShc/jArYfBPjEV95euvZ8JVtO43oQExEN/WquoqpufFjNu4kSpi5cy5kMbNvzztWDfv1Jg==
chalk@^4.0.0:
version "4.1.2"
@@ -369,10 +374,10 @@ concat-map@0.0.1:
resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==
core-js-pure@^3.20.2:
version "3.24.1"
resolved "https://registry.yarnpkg.com/core-js-pure/-/core-js-pure-3.24.1.tgz#8839dde5da545521bf282feb7dc6d0b425f39fd3"
integrity sha512-r1nJk41QLLPyozHUUPmILCEMtMw24NG4oWK6RbsDdjzQgg9ZvrUsPBj1MnG0wXXp1DCDU6j+wUvEmBSrtRbLXg==
core-js-pure@^3.25.1:
version "3.25.2"
resolved "https://registry.yarnpkg.com/core-js-pure/-/core-js-pure-3.25.2.tgz#44a4fd873bdd4fecf6ca11512bcefedbe87e744a"
integrity sha512-ItD7YpW1cUB4jaqFLZXe1AXkyqIxz6GqPnsDV4uF4hVcWh/WAGIqSqw5p0/WdsILM0Xht9s3Koyw05R3K6RtiA==
cross-spawn@^7.0.2:
version "7.0.3"
@@ -449,15 +454,15 @@ emoji-regex@^9.2.2:
integrity sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==
es-abstract@^1.19.0, es-abstract@^1.19.1, es-abstract@^1.19.2, es-abstract@^1.19.5:
version "1.20.1"
resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.20.1.tgz#027292cd6ef44bd12b1913b828116f54787d1814"
integrity sha512-WEm2oBhfoI2sImeM4OF2zE2V3BYdSF+KnSi9Sidz51fQHd7+JuF8Xgcj9/0o+OWeIeIS/MiuNnlruQrJf16GQA==
version "1.20.2"
resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.20.2.tgz#8495a07bc56d342a3b8ea3ab01bd986700c2ccb3"
integrity sha512-XxXQuVNrySBNlEkTYJoDNFe5+s2yIOpzq80sUHEdPdQr0S5nTLz4ZPPPswNIpKseDDUS5yghX1gfLIHQZ1iNuQ==
dependencies:
call-bind "^1.0.2"
es-to-primitive "^1.2.1"
function-bind "^1.1.1"
function.prototype.name "^1.1.5"
get-intrinsic "^1.1.1"
get-intrinsic "^1.1.2"
get-symbol-description "^1.0.0"
has "^1.0.3"
has-property-descriptors "^1.0.0"
@@ -469,9 +474,9 @@ es-abstract@^1.19.0, es-abstract@^1.19.1, es-abstract@^1.19.2, es-abstract@^1.19
is-shared-array-buffer "^1.0.2"
is-string "^1.0.7"
is-weakref "^1.0.2"
object-inspect "^1.12.0"
object-inspect "^1.12.2"
object-keys "^1.1.1"
object.assign "^4.1.2"
object.assign "^4.1.4"
regexp.prototype.flags "^1.4.3"
string.prototype.trimend "^1.0.5"
string.prototype.trimstart "^1.0.5"
@@ -498,19 +503,19 @@ escape-string-regexp@^4.0.0:
resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34"
integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==
eslint-config-next@12.2.5:
version "12.2.5"
resolved "https://registry.yarnpkg.com/eslint-config-next/-/eslint-config-next-12.2.5.tgz#76ce83f18cc02f6f42ed407a127f83db54fabd3c"
integrity sha512-SOowilkqPzW6DxKp3a3SYlrfPi5Ajs9MIzp9gVfUDxxH9QFM5ElkR1hX5m/iICJuvCbWgQqFBiA3mCMozluniw==
eslint-config-next@12.3.1:
version "12.3.1"
resolved "https://registry.yarnpkg.com/eslint-config-next/-/eslint-config-next-12.3.1.tgz#5d4eb0b7903cea81fd0d5106601d3afb0a453ff4"
integrity sha512-EN/xwKPU6jz1G0Qi6Bd/BqMnHLyRAL0VsaQaWA7F3KkjAgZHi4f1uL1JKGWNxdQpHTW/sdGONBd0bzxUka/DJg==
dependencies:
"@next/eslint-plugin-next" "12.2.5"
"@next/eslint-plugin-next" "12.3.1"
"@rushstack/eslint-patch" "^1.1.3"
"@typescript-eslint/parser" "^5.21.0"
eslint-import-resolver-node "^0.3.6"
eslint-import-resolver-typescript "^2.7.1"
eslint-plugin-import "^2.26.0"
eslint-plugin-jsx-a11y "^6.5.1"
eslint-plugin-react "^7.29.4"
eslint-plugin-react "^7.31.7"
eslint-plugin-react-hooks "^4.5.0"
eslint-import-resolver-node@^0.3.6:
@@ -582,10 +587,10 @@ eslint-plugin-react-hooks@^4.5.0:
resolved "https://registry.yarnpkg.com/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.6.0.tgz#4c3e697ad95b77e93f8646aaa1630c1ba607edd3"
integrity sha512-oFc7Itz9Qxh2x4gNHStv3BqJq54ExXmfC+a1NjAta66IAN87Wu0R/QArgIS9qKzX3dXKPI9H5crl9QchNMY9+g==
eslint-plugin-react@^7.29.4:
version "7.30.1"
resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.30.1.tgz#2be4ab23ce09b5949c6631413ba64b2810fd3e22"
integrity sha512-NbEvI9jtqO46yJA3wcRF9Mo0lF9T/jhdHqhCHXiXtD+Zcb98812wvokjWpU7Q4QH5edo6dmqrukxVvWWXHlsUg==
eslint-plugin-react@^7.31.7:
version "7.31.8"
resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.31.8.tgz#3a4f80c10be1bcbc8197be9e8b641b2a3ef219bf"
integrity sha512-5lBTZmgQmARLLSYiwI71tiGVTLUuqXantZM6vlSY39OaDSV0M7+32K5DnLkmFrwTe+Ksz0ffuLUC91RUviVZfw==
dependencies:
array-includes "^3.1.5"
array.prototype.flatmap "^1.3.0"
@@ -627,14 +632,15 @@ eslint-visitor-keys@^3.3.0:
resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz#f6480fa6b1f30efe2d1968aa8ac745b862469826"
integrity sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==
eslint@8.22.0:
version "8.22.0"
resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.22.0.tgz#78fcb044196dfa7eef30a9d65944f6f980402c48"
integrity sha512-ci4t0sz6vSRKdmkOGmprBo6fmI4PrphDFMy5JEq/fNS0gQkJM3rLmrqcp8ipMcdobH3KtUP40KniAE9W19S4wA==
eslint@8.23.1:
version "8.23.1"
resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.23.1.tgz#cfd7b3f7fdd07db8d16b4ac0516a29c8d8dca5dc"
integrity sha512-w7C1IXCc6fNqjpuYd0yPlcTKKmHlHHktRkzmBPZ+7cvNBQuiNjx0xaMTjAJGCafJhQkrFJooREv0CtrVzmHwqg==
dependencies:
"@eslint/eslintrc" "^1.3.0"
"@eslint/eslintrc" "^1.3.2"
"@humanwhocodes/config-array" "^0.10.4"
"@humanwhocodes/gitignore-to-minimatch" "^1.0.2"
"@humanwhocodes/module-importer" "^1.0.1"
ajv "^6.10.0"
chalk "^4.0.0"
cross-spawn "^7.0.2"
@@ -644,13 +650,12 @@ eslint@8.22.0:
eslint-scope "^7.1.1"
eslint-utils "^3.0.0"
eslint-visitor-keys "^3.3.0"
espree "^9.3.3"
espree "^9.4.0"
esquery "^1.4.0"
esutils "^2.0.2"
fast-deep-equal "^3.1.3"
file-entry-cache "^6.0.1"
find-up "^5.0.0"
functional-red-black-tree "^1.0.1"
glob-parent "^6.0.1"
globals "^13.15.0"
globby "^11.1.0"
@@ -659,6 +664,7 @@ eslint@8.22.0:
import-fresh "^3.0.0"
imurmurhash "^0.1.4"
is-glob "^4.0.0"
js-sdsl "^4.1.4"
js-yaml "^4.1.0"
json-stable-stringify-without-jsonify "^1.0.1"
levn "^0.4.1"
@@ -670,12 +676,11 @@ eslint@8.22.0:
strip-ansi "^6.0.1"
strip-json-comments "^3.1.0"
text-table "^0.2.0"
v8-compile-cache "^2.0.3"
espree@^9.3.2, espree@^9.3.3:
version "9.3.3"
resolved "https://registry.yarnpkg.com/espree/-/espree-9.3.3.tgz#2dd37c4162bb05f433ad3c1a52ddf8a49dc08e9d"
integrity sha512-ORs1Rt/uQTqUKjDdGCyrtYxbazf5umATSf/K4qxjmZHORR6HJk+2s/2Pqe+Kk49HHINC/xNIrGfgh8sZcll0ng==
espree@^9.4.0:
version "9.4.0"
resolved "https://registry.yarnpkg.com/espree/-/espree-9.4.0.tgz#cd4bc3d6e9336c433265fc0aa016fc1aaf182f8a"
integrity sha512-DQmnRpLj7f6TgN/NYb0MTzJXL+vJF9h3pHy4JhCIs3zwcgez8xmGg3sXHcEO97BrmO2OSvCwMdfdlyl+E9KjOw==
dependencies:
acorn "^8.8.0"
acorn-jsx "^5.3.2"
@@ -711,9 +716,9 @@ fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3:
integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==
fast-glob@^3.2.9:
version "3.2.11"
resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.11.tgz#a1172ad95ceb8a16e20caa5c5e56480e5129c1d9"
integrity sha512-xrO3+1bxSo3ZVHAnqzyuewYT6aMFHRAd4Kcs92MAonjwQZLsK9d0SF1IyQ3k5PoirxTW0Oe/RqFgMQ6TcNE5Ew==
version "3.2.12"
resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.12.tgz#7f39ec99c2e6ab030337142da9e0c18f37afae80"
integrity sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==
dependencies:
"@nodelib/fs.stat" "^2.0.2"
"@nodelib/fs.walk" "^1.2.3"
@@ -793,20 +798,15 @@ function.prototype.name@^1.1.5:
es-abstract "^1.19.0"
functions-have-names "^1.2.2"
functional-red-black-tree@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327"
integrity sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g==
functions-have-names@^1.2.2:
version "1.2.3"
resolved "https://registry.yarnpkg.com/functions-have-names/-/functions-have-names-1.2.3.tgz#0404fe4ee2ba2f607f0e0ec3c80bae994133b834"
integrity sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==
get-intrinsic@^1.0.2, get-intrinsic@^1.1.0, get-intrinsic@^1.1.1:
version "1.1.2"
resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.1.2.tgz#336975123e05ad0b7ba41f152ee4aadbea6cf598"
integrity sha512-Jfm3OyCxHh9DJyc28qGk+JmfkpO41A4XkneDSujN9MDXrm4oDKdHvndhZ2dN94+ERNfkYJWDclW6k2L/ZGHjXA==
get-intrinsic@^1.0.2, get-intrinsic@^1.1.0, get-intrinsic@^1.1.1, get-intrinsic@^1.1.2:
version "1.1.3"
resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.1.3.tgz#063c84329ad93e83893c7f4f243ef63ffa351385"
integrity sha512-QJVz1Tj7MS099PevUG5jvnt9tSkXN8K14dxQlikJuPt4uD9hHAHjLyLBiLR5zELelBdD9QNRAXZzsJx0WaDL9A==
dependencies:
function-bind "^1.1.1"
has "^1.0.3"
@@ -974,9 +974,9 @@ is-boolean-object@^1.1.0:
has-tostringtag "^1.0.0"
is-callable@^1.1.4, is-callable@^1.2.4:
version "1.2.4"
resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.4.tgz#47301d58dd0259407865547853df6d61fe471945"
integrity sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w==
version "1.2.6"
resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.6.tgz#fd6170b0b8c7e2cc73de342ef8284a2202023c44"
integrity sha512-krO72EO2NptOGAX2KYyqbP9vYMlNAXdB53rq6f8LXY6RY7JdSR/3BD6wLUlPHSAesmY9vstNrjvqGaCiRK/91Q==
is-core-module@^2.8.1, is-core-module@^2.9.0:
version "2.10.0"
@@ -1062,6 +1062,11 @@ isexe@^2.0.0:
resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10"
integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==
js-sdsl@^4.1.4:
version "4.1.4"
resolved "https://registry.yarnpkg.com/js-sdsl/-/js-sdsl-4.1.4.tgz#78793c90f80e8430b7d8dc94515b6c77d98a26a6"
integrity sha512-Y2/yD55y5jteOAmY50JbUZYwk3CP3wnLPEZnlR1w9oKhITrBEtAxwuWKebFf8hMrPMgbYwFoWK/lH2sBkErELw==
"js-tokens@^3.0.0 || ^4.0.0":
version "4.0.0"
resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499"
@@ -1195,38 +1200,38 @@ natural-compare@^1.4.0:
resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7"
integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==
next@12.2.5:
version "12.2.5"
resolved "https://registry.yarnpkg.com/next/-/next-12.2.5.tgz#14fb5975e8841fad09553b8ef41fe1393602b717"
integrity sha512-tBdjqX5XC/oFs/6gxrZhjmiq90YWizUYU6qOWAfat7zJwrwapJ+BYgX2PmiacunXMaRpeVT4vz5MSPSLgNkrpA==
next@12.3.1:
version "12.3.1"
resolved "https://registry.yarnpkg.com/next/-/next-12.3.1.tgz#127b825ad2207faf869b33393ec8c75fe61e50f1"
integrity sha512-l7bvmSeIwX5lp07WtIiP9u2ytZMv7jIeB8iacR28PuUEFG5j0HGAPnMqyG5kbZNBG2H7tRsrQ4HCjuMOPnANZw==
dependencies:
"@next/env" "12.2.5"
"@swc/helpers" "0.4.3"
caniuse-lite "^1.0.30001332"
"@next/env" "12.3.1"
"@swc/helpers" "0.4.11"
caniuse-lite "^1.0.30001406"
postcss "8.4.14"
styled-jsx "5.0.4"
styled-jsx "5.0.7"
use-sync-external-store "1.2.0"
optionalDependencies:
"@next/swc-android-arm-eabi" "12.2.5"
"@next/swc-android-arm64" "12.2.5"
"@next/swc-darwin-arm64" "12.2.5"
"@next/swc-darwin-x64" "12.2.5"
"@next/swc-freebsd-x64" "12.2.5"
"@next/swc-linux-arm-gnueabihf" "12.2.5"
"@next/swc-linux-arm64-gnu" "12.2.5"
"@next/swc-linux-arm64-musl" "12.2.5"
"@next/swc-linux-x64-gnu" "12.2.5"
"@next/swc-linux-x64-musl" "12.2.5"
"@next/swc-win32-arm64-msvc" "12.2.5"
"@next/swc-win32-ia32-msvc" "12.2.5"
"@next/swc-win32-x64-msvc" "12.2.5"
"@next/swc-android-arm-eabi" "12.3.1"
"@next/swc-android-arm64" "12.3.1"
"@next/swc-darwin-arm64" "12.3.1"
"@next/swc-darwin-x64" "12.3.1"
"@next/swc-freebsd-x64" "12.3.1"
"@next/swc-linux-arm-gnueabihf" "12.3.1"
"@next/swc-linux-arm64-gnu" "12.3.1"
"@next/swc-linux-arm64-musl" "12.3.1"
"@next/swc-linux-x64-gnu" "12.3.1"
"@next/swc-linux-x64-musl" "12.3.1"
"@next/swc-win32-arm64-msvc" "12.3.1"
"@next/swc-win32-ia32-msvc" "12.3.1"
"@next/swc-win32-x64-msvc" "12.3.1"
object-assign@^4.1.1:
version "4.1.1"
resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863"
integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==
object-inspect@^1.12.0, object-inspect@^1.9.0:
object-inspect@^1.12.2, object-inspect@^1.9.0:
version "1.12.2"
resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.12.2.tgz#c0641f26394532f28ab8d796ab954e43c009a8ea"
integrity sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ==
@@ -1236,7 +1241,7 @@ object-keys@^1.1.1:
resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e"
integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==
object.assign@^4.1.2, object.assign@^4.1.3:
object.assign@^4.1.3, object.assign@^4.1.4:
version "4.1.4"
resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.4.tgz#9673c7c7c351ab8c4d0b516f4343ebf4dfb7799f"
integrity sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==
@@ -1569,10 +1574,10 @@ strip-json-comments@^3.1.0, strip-json-comments@^3.1.1:
resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006"
integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==
styled-jsx@5.0.4:
version "5.0.4"
resolved "https://registry.yarnpkg.com/styled-jsx/-/styled-jsx-5.0.4.tgz#5b1bd0b9ab44caae3dd1361295559706e044aa53"
integrity sha512-sDFWLbg4zR+UkNzfk5lPilyIgtpddfxXEULxhujorr5jtePTUqiPDc5BC0v1NRqTr/WaFBGQQUoYToGlF4B2KQ==
styled-jsx@5.0.7:
version "5.0.7"
resolved "https://registry.yarnpkg.com/styled-jsx/-/styled-jsx-5.0.7.tgz#be44afc53771b983769ac654d355ca8d019dff48"
integrity sha512-b3sUzamS086YLRuvnaDigdAewz1/EFYlHpYBP5mZovKEdQQOIIYq8lApylub3HHZ6xFjV051kkGU7cudJmrXEA==
supports-color@^7.1.0:
version "7.2.0"
@@ -1659,11 +1664,6 @@ use-sync-external-store@1.2.0:
resolved "https://registry.yarnpkg.com/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz#7dbefd6ef3fe4e767a0cf5d7287aacfb5846928a"
integrity sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==
v8-compile-cache@^2.0.3:
version "2.3.0"
resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz#2de19618c66dc247dcfb6f99338035d8245a2cee"
integrity sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==
which-boxed-primitive@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz#13757bc89b209b049fe5d86430e21cf40a89a8e6"

View File

@@ -34,7 +34,7 @@
"prettier": "2.6.2",
"ts-eager": "2.0.2",
"ts-jest": "28.0.5",
"turbo": "1.3.2-canary.1"
"turbo": "1.4.7"
},
"scripts": {
"lerna": "lerna",

View File

@@ -1,6 +1,6 @@
{
"name": "@vercel/build-utils",
"version": "5.4.2",
"version": "5.4.4",
"license": "MIT",
"main": "./dist/index.js",
"types": "./dist/index.d.js",

View File

@@ -0,0 +1,34 @@
import type { Env } from './types';
const { hasOwnProperty } = Object.prototype;
/**
* Clones zero or more objects into a single new object while ensuring that the
* `PATH` environment variable is defined when the `PATH` or `Path` environment
* variables are defined.
*
* @param {Object} [...envs] Objects and/or `process.env` to clone and merge
* @returns {Object} The new object
*/
export function cloneEnv(...envs: (Env | undefined)[]): Env {
return envs.reduce((obj: Env, env) => {
if (env === undefined || env === null) {
return obj;
}
// mixin the env first
obj = Object.assign(obj, env);
if (hasOwnProperty.call(env, 'Path')) {
// the system path is called `Path` on Windows and Node.js will
// automatically return the system path when accessing `PATH`,
// however we lose this proxied value when we destructure and
// thus we must explicitly copy it, but we must also remove the
// `Path` property since we can't have both a `PATH` and `Path`
obj.PATH = obj.Path;
delete obj.Path;
}
return obj;
}, {});
}

View File

@@ -11,6 +11,7 @@ import { NowBuildError } from '../errors';
import { Meta, PackageJson, NodeVersion, Config } from '../types';
import { getSupportedNodeVersion, getLatestNodeVersion } from './node-version';
import { readConfigFile } from './read-config-file';
import { cloneEnv } from '../clone-env';
// Only allow one `runNpmInstall()` invocation to run concurrently
const runNpmInstallSema = new Sema(1);
@@ -217,7 +218,7 @@ export function getSpawnOptions(
nodeVersion: NodeVersion
): SpawnOptions {
const opts = {
env: { ...process.env },
env: cloneEnv(process.env),
};
if (!meta.isDev) {
@@ -449,7 +450,7 @@ export async function runNpmInstall(
debug(`Installing to ${destPath}`);
const opts: SpawnOptionsExtended = { cwd: destPath, ...spawnOpts };
const env = opts.env ? { ...opts.env } : { ...process.env };
const env = cloneEnv(opts.env || process.env);
delete env.NODE_ENV;
opts.env = getEnvForPackageManager({
cliType,
@@ -458,12 +459,29 @@ export async function runNpmInstall(
env,
});
let commandArgs: string[];
const isPotentiallyBrokenNpm =
cliType === 'npm' &&
nodeVersion?.major === 16 &&
!args.includes('--legacy-peer-deps') &&
spawnOpts?.env?.ENABLE_EXPERIMENTAL_COREPACK !== '1';
if (cliType === 'npm') {
opts.prettyCommand = 'npm install';
commandArgs = args
.filter(a => a !== '--prefer-offline')
.concat(['install', '--no-audit', '--unsafe-perm']);
if (
isPotentiallyBrokenNpm &&
spawnOpts?.env?.VERCEL_NPM_LEGACY_PEER_DEPS === '1'
) {
// Starting in npm@8.6.0, if you ran `npm install --legacy-peer-deps`,
// and then later ran `npm install`, it would fail. So the only way
// to safely upgrade npm from npm@8.5.0 is to set this flag. The docs
// say this flag is not recommended so its is behind a feature flag
// so we can remove it in node@18, which can introduce breaking changes.
// See https://docs.npmjs.com/cli/v8/using-npm/config#legacy-peer-deps
commandArgs.push('--legacy-peer-deps');
}
} else if (cliType === 'pnpm') {
// PNPM's install command is similar to NPM's but without the audit nonsense
// @see options https://pnpm.io/cli/install
@@ -480,7 +498,26 @@ export async function runNpmInstall(
commandArgs.push('--production');
}
await spawnAsync(cliType, commandArgs, opts);
try {
await spawnAsync(cliType, commandArgs, opts);
} catch (_) {
const potentialErrorPath = path.join(
process.env.HOME || '/',
'.npm',
'eresolve-report.txt'
);
if (
isPotentiallyBrokenNpm &&
!commandArgs.includes('--legacy-peer-deps') &&
fs.existsSync(potentialErrorPath)
) {
console.warn(
'Warning: Retrying "Install Command" with `--legacy-peer-deps` which may accept a potentially broken dependency and slow install time.'
);
commandArgs.push('--legacy-peer-deps');
await spawnAsync(cliType, commandArgs, opts);
}
}
debug(`Install complete [${Date.now() - installTime}ms]`);
return true;
} finally {
@@ -591,10 +628,7 @@ export async function runPackageJsonScript(
cliType,
lockfileVersion,
nodeVersion: undefined,
env: {
...process.env,
...spawnOpts?.env,
},
env: cloneEnv(process.env, spawnOpts?.env),
}),
};

View File

@@ -41,6 +41,7 @@ import debug from './debug';
import getIgnoreFilter from './get-ignore-filter';
import { getPlatformEnv } from './get-platform-env';
import { getPrefixedEnvVars } from './get-prefixed-env-vars';
import { cloneEnv } from './clone-env';
export {
FileBlob,
@@ -84,6 +85,7 @@ export {
getLambdaOptionsFromFunction,
scanParentDirs,
getIgnoreFilter,
cloneEnv,
};
export { EdgeFunction } from './edge-function';

View File

@@ -342,6 +342,7 @@ export interface BuilderV2 {
version: 2;
build: BuildV2;
prepareCache?: PrepareCache;
shouldServe?: ShouldServe;
}
export interface BuilderV3 {

View File

@@ -0,0 +1,2 @@
node_modules
.vercel

View File

@@ -0,0 +1,143 @@
{
"name": "15-npm-8-legacy-peer-deps",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"dependencies": {
"react": "16.8.0",
"swr": "1.3.0"
}
},
"node_modules/js-tokens": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
"integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="
},
"node_modules/loose-envify": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
"integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
"dependencies": {
"js-tokens": "^3.0.0 || ^4.0.0"
},
"bin": {
"loose-envify": "cli.js"
}
},
"node_modules/object-assign": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
"integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/prop-types": {
"version": "15.8.1",
"resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
"integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==",
"dependencies": {
"loose-envify": "^1.4.0",
"object-assign": "^4.1.1",
"react-is": "^16.13.1"
}
},
"node_modules/react": {
"version": "16.8.0",
"resolved": "https://registry.npmjs.org/react/-/react-16.8.0.tgz",
"integrity": "sha512-g+nikW2D48kqgWSPwNo0NH9tIGG3DsQFlrtrQ1kj6W77z5ahyIHG0w8kPpz4Sdj6gyLnz0lEd/xsjOoGge2MYQ==",
"dependencies": {
"loose-envify": "^1.1.0",
"object-assign": "^4.1.1",
"prop-types": "^15.6.2",
"scheduler": "^0.13.0"
},
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/react-is": {
"version": "16.13.1",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="
},
"node_modules/scheduler": {
"version": "0.13.6",
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.13.6.tgz",
"integrity": "sha512-IWnObHt413ucAYKsD9J1QShUKkbKLQQHdxRyw73sw4FN26iWr3DY/H34xGPe4nmL1DwXyWmSWmMrA9TfQbE/XQ==",
"dependencies": {
"loose-envify": "^1.1.0",
"object-assign": "^4.1.1"
}
},
"node_modules/swr": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/swr/-/swr-1.3.0.tgz",
"integrity": "sha512-dkghQrOl2ORX9HYrMDtPa7LTVHJjCTeZoB1dqTbnnEDlSvN8JEKpYIYurDfvbQFUUS8Cg8PceFVZNkW0KNNYPw==",
"peerDependencies": {
"react": "^16.11.0 || ^17.0.0 || ^18.0.0"
}
}
},
"dependencies": {
"js-tokens": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
"integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="
},
"loose-envify": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
"integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
"requires": {
"js-tokens": "^3.0.0 || ^4.0.0"
}
},
"object-assign": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
"integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg=="
},
"prop-types": {
"version": "15.8.1",
"resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
"integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==",
"requires": {
"loose-envify": "^1.4.0",
"object-assign": "^4.1.1",
"react-is": "^16.13.1"
}
},
"react": {
"version": "16.8.0",
"resolved": "https://registry.npmjs.org/react/-/react-16.8.0.tgz",
"integrity": "sha512-g+nikW2D48kqgWSPwNo0NH9tIGG3DsQFlrtrQ1kj6W77z5ahyIHG0w8kPpz4Sdj6gyLnz0lEd/xsjOoGge2MYQ==",
"requires": {
"loose-envify": "^1.1.0",
"object-assign": "^4.1.1",
"prop-types": "^15.6.2",
"scheduler": "^0.13.0"
}
},
"react-is": {
"version": "16.13.1",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="
},
"scheduler": {
"version": "0.13.6",
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.13.6.tgz",
"integrity": "sha512-IWnObHt413ucAYKsD9J1QShUKkbKLQQHdxRyw73sw4FN26iWr3DY/H34xGPe4nmL1DwXyWmSWmMrA9TfQbE/XQ==",
"requires": {
"loose-envify": "^1.1.0",
"object-assign": "^4.1.1"
}
},
"swr": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/swr/-/swr-1.3.0.tgz",
"integrity": "sha512-dkghQrOl2ORX9HYrMDtPa7LTVHJjCTeZoB1dqTbnnEDlSvN8JEKpYIYurDfvbQFUUS8Cg8PceFVZNkW0KNNYPw=="
}
}
}

View File

@@ -0,0 +1,11 @@
{
"private": true,
"scripts": {
"build": "mkdir -p public && echo 'legacy peer deps' > public/index.txt"
},
"packageManager": "npm@8.6.0",
"dependencies": {
"swr": "1.3.0",
"react": "16.8.0"
}
}

View File

@@ -0,0 +1,3 @@
{
"probes": [{ "path": "/", "mustContain": "legacy peer deps" }]
}

View File

@@ -5,6 +5,7 @@
"requires": true,
"packages": {
"": {
"name": "20-npm-7",
"version": "1.0.0"
}
}

View File

@@ -21,6 +21,8 @@ const skipFixtures: string[] = [
'08-zero-config-middleman',
'21-npm-workspaces',
'23-pnpm-workspaces',
'41-nx-monorepo',
'42-npm-workspace-with-nx',
];
// eslint-disable-next-line no-restricted-syntax

View File

@@ -0,0 +1,117 @@
import { cloneEnv } from '../src';
it('should clone env with Path', () => {
expect(
cloneEnv(
new Proxy(
{
foo: 'bar',
Path: 'baz',
},
{
get(target: typeof process.env, prop: string) {
if (prop === 'PATH') {
return target.PATH ?? target.Path;
}
return target[prop];
},
}
)
)
).toEqual({
foo: 'bar',
PATH: 'baz',
});
});
it('should clone env with PATH', () => {
expect(
cloneEnv({
foo: 'bar',
PATH: 'baz',
})
).toEqual({
foo: 'bar',
PATH: 'baz',
});
});
it('should clone and merge multiple env objects', () => {
// note: this also tests the last object doesn't overwrite `PATH` with
// `undefined`
expect(
cloneEnv(
{
foo: 'bar',
},
{
PATH: 'baz',
},
{
baz: 'wiz',
}
)
).toEqual({
foo: 'bar',
PATH: 'baz',
baz: 'wiz',
});
});
it('should clone the actual process.env object', () => {
expect(cloneEnv(process.env).PATH).toEqual(process.env.PATH);
});
it('should overwrite PATH with last value', () => {
expect(
cloneEnv(
new Proxy(
{
Path: 'foo',
},
{
get(target: typeof process.env, prop: string) {
if (prop === 'PATH') {
return target.PATH ?? target.Path;
}
return target[prop];
},
}
),
{
PATH: 'bar',
},
{
PATH: undefined,
}
)
).toEqual({
PATH: undefined,
});
});
it('should handle process.env at any argument position', () => {
expect(
cloneEnv(
{
foo: 'bar',
},
new Proxy(
{
Path: 'baz',
},
{
get(target: typeof process.env, prop: string) {
if (prop === 'PATH') {
return target.PATH ?? target.Path;
}
return target[prop];
},
}
)
)
).toEqual({
foo: 'bar',
PATH: 'baz',
});
});

View File

@@ -0,0 +1,172 @@
const spawnMock = jest.fn();
jest.mock('cross-spawn', () => {
const spawn = (...args: any) => {
spawnMock(...args);
const child = {
on: (type: string, fn: (code: number) => void) => {
if (type === 'close') {
return fn(0);
}
},
};
return child;
};
return spawn;
});
afterEach(() => {
spawnMock.mockClear();
});
import path from 'path';
import { runNpmInstall, cloneEnv } from '../src';
import type { Meta } from '../src/types';
function getTestSpawnOpts(env: Record<string, string>) {
return { env: cloneEnv(process.env, env) };
}
function getNodeVersion(major: number) {
return { major, range: `${major}.x`, runtime: `nodejs${major}.x` };
}
it('should not include peer dependencies when missing VERCEL_NPM_LEGACY_PEER_DEPS on node16', async () => {
const fixture = path.join(__dirname, 'fixtures', '20-npm-7');
const meta: Meta = {};
const spawnOpts = getTestSpawnOpts({});
const nodeVersion = { major: 16 } as any;
await runNpmInstall(fixture, [], spawnOpts, meta, nodeVersion);
expect(spawnMock.mock.calls.length).toBe(1);
const args = spawnMock.mock.calls[0];
expect(args[0]).toEqual('npm');
expect(args[1]).toEqual(['install', '--no-audit', '--unsafe-perm']);
expect(args[2]).toEqual({
cwd: fixture,
prettyCommand: 'npm install',
stdio: 'inherit',
env: expect.any(Object),
});
});
it('should include peer dependencies when VERCEL_NPM_LEGACY_PEER_DEPS=1 on node16', async () => {
const fixture = path.join(__dirname, 'fixtures', '20-npm-7');
const meta: Meta = {};
const spawnOpts = getTestSpawnOpts({ VERCEL_NPM_LEGACY_PEER_DEPS: '1' });
const nodeVersion = getNodeVersion(16);
await runNpmInstall(fixture, [], spawnOpts, meta, nodeVersion);
expect(spawnMock.mock.calls.length).toBe(1);
const args = spawnMock.mock.calls[0];
expect(args[0]).toEqual('npm');
expect(args[1]).toEqual([
'install',
'--no-audit',
'--unsafe-perm',
'--legacy-peer-deps',
]);
expect(args[2]).toEqual({
cwd: fixture,
prettyCommand: 'npm install',
stdio: 'inherit',
env: expect.any(Object),
});
});
it('should not include peer dependencies when VERCEL_NPM_LEGACY_PEER_DEPS=1 on node14', async () => {
const fixture = path.join(__dirname, 'fixtures', '20-npm-7');
const meta: Meta = {};
const spawnOpts = getTestSpawnOpts({ VERCEL_NPM_LEGACY_PEER_DEPS: '1' });
const nodeVersion = getNodeVersion(14);
await runNpmInstall(fixture, [], spawnOpts, meta, nodeVersion);
expect(spawnMock.mock.calls.length).toBe(1);
const args = spawnMock.mock.calls[0];
expect(args[0]).toEqual('npm');
expect(args[1]).toEqual(['install', '--no-audit', '--unsafe-perm']);
expect(args[2]).toEqual({
cwd: fixture,
prettyCommand: 'npm install',
stdio: 'inherit',
env: expect.any(Object),
});
});
it('should not include peer dependencies when VERCEL_NPM_LEGACY_PEER_DEPS=1 on node16 with corepack enabled', async () => {
const fixture = path.join(__dirname, 'fixtures', '20-npm-7');
const meta: Meta = {};
const spawnOpts = getTestSpawnOpts({
VERCEL_NPM_LEGACY_PEER_DEPS: '1',
ENABLE_EXPERIMENTAL_COREPACK: '1',
});
const nodeVersion = getNodeVersion(16);
await runNpmInstall(fixture, [], spawnOpts, meta, nodeVersion);
expect(spawnMock.mock.calls.length).toBe(1);
const args = spawnMock.mock.calls[0];
expect(args[0]).toEqual('npm');
expect(args[1]).toEqual(['install', '--no-audit', '--unsafe-perm']);
expect(args[2]).toEqual({
cwd: fixture,
prettyCommand: 'npm install',
stdio: 'inherit',
env: expect.any(Object),
});
});
it('should only invoke `runNpmInstall()` once per `package.json` file (serial)', async () => {
const meta: Meta = {};
const fixture = path.join(__dirname, 'fixtures', '02-zero-config-api');
const apiDir = path.join(fixture, 'api');
const run1 = await runNpmInstall(apiDir, [], undefined, meta);
expect(run1).toEqual(true);
expect(
(meta.runNpmInstallSet as Set<string>).has(
path.join(fixture, 'package.json')
)
).toEqual(true);
const run2 = await runNpmInstall(apiDir, [], undefined, meta);
expect(run2).toEqual(false);
const run3 = await runNpmInstall(fixture, [], undefined, meta);
expect(run3).toEqual(false);
expect(spawnMock.mock.calls.length).toBe(1);
const args = spawnMock.mock.calls[0];
expect(args[0]).toEqual('yarn');
expect(args[1]).toEqual(['install']);
expect(args[2]).toEqual({
cwd: apiDir,
prettyCommand: 'yarn install',
stdio: 'inherit',
env: expect.any(Object),
});
});
it('should only invoke `runNpmInstall()` once per `package.json` file (parallel)', async () => {
const meta: Meta = {};
const fixture = path.join(__dirname, 'fixtures', '02-zero-config-api');
const apiDir = path.join(fixture, 'api');
const [run1, run2, run3] = await Promise.all([
runNpmInstall(apiDir, [], undefined, meta),
runNpmInstall(apiDir, [], undefined, meta),
runNpmInstall(fixture, [], undefined, meta),
]);
expect(run1).toEqual(true);
expect(run2).toEqual(false);
expect(run3).toEqual(false);
expect(
(meta.runNpmInstallSet as Set<string>).has(
path.join(fixture, 'package.json')
)
).toEqual(true);
expect(spawnMock.mock.calls.length).toBe(1);
const args = spawnMock.mock.calls[0];
expect(args[0]).toEqual('yarn');
expect(args[1]).toEqual(['install']);
expect(args[2]).toEqual({
cwd: apiDir,
prettyCommand: 'yarn install',
stdio: 'inherit',
env: expect.any(Object),
});
});

View File

@@ -1,7 +1,6 @@
import ms from 'ms';
import path from 'path';
import fs, { readlink } from 'fs-extra';
import retry from 'async-retry';
import { strict as assert, strictEqual } from 'assert';
import { createZip } from '../src/lambda';
import { getSupportedNodeVersion } from '../src/fs/node-version';
@@ -16,7 +15,6 @@ import {
runPackageJsonScript,
scanParentDirs,
FileBlob,
Meta,
} from '../src';
jest.setTimeout(10 * 1000);
@@ -578,53 +576,16 @@ it('should detect package.json in nested frontend', async () => {
expect(result.packageJsonPath).toEqual(path.join(fixture, 'package.json'));
});
it('should only invoke `runNpmInstall()` once per `package.json` file (serial)', async () => {
const meta: Meta = {};
const fixture = path.join(__dirname, 'fixtures', '02-zero-config-api');
const apiDir = path.join(fixture, 'api');
const retryOpts = { maxRetryTime: 1000 };
let run1, run2, run3;
await retry(async () => {
run1 = await runNpmInstall(apiDir, [], undefined, meta);
expect(run1).toEqual(true);
expect(
(meta.runNpmInstallSet as Set<string>).has(
path.join(fixture, 'package.json')
)
).toEqual(true);
}, retryOpts);
await retry(async () => {
run2 = await runNpmInstall(apiDir, [], undefined, meta);
expect(run2).toEqual(false);
}, retryOpts);
await retry(async () => {
run3 = await runNpmInstall(fixture, [], undefined, meta);
expect(run3).toEqual(false);
}, retryOpts);
});
it('should only invoke `runNpmInstall()` once per `package.json` file (parallel)', async () => {
const meta: Meta = {};
const fixture = path.join(__dirname, 'fixtures', '02-zero-config-api');
const apiDir = path.join(fixture, 'api');
let results: [boolean, boolean, boolean] | undefined;
await retry(
async () => {
results = await Promise.all([
runNpmInstall(apiDir, [], undefined, meta),
runNpmInstall(apiDir, [], undefined, meta),
runNpmInstall(fixture, [], undefined, meta),
]);
},
{ maxRetryTime: 3000 }
);
const [run1, run2, run3] = results || [];
expect(run1).toEqual(true);
expect(run2).toEqual(false);
expect(run3).toEqual(false);
expect(
(meta.runNpmInstallSet as Set<string>).has(
path.join(fixture, 'package.json')
)
).toEqual(true);
it('should retry npm install when peer deps invalid and npm@8 on node@16', async () => {
const nodeMajor = Number(process.versions.node.split('.')[0]);
if (nodeMajor !== 16) {
console.log(`Skipping test on node@${nodeMajor}`);
return;
}
const fixture = path.join(__dirname, 'fixtures', '15-npm-8-legacy-peer-deps');
const nodeVersion = { major: nodeMajor } as any;
await runNpmInstall(fixture, [], {}, {}, nodeVersion);
expect(warningMessages).toStrictEqual([
'Warning: Retrying "Install Command" with `--legacy-peer-deps` which may accept a potentially broken dependency and slow install time.',
]);
});

View File

@@ -1,6 +1,6 @@
{
"name": "vercel",
"version": "28.2.3",
"version": "28.4.0",
"preferGlobal": true,
"license": "Apache-2.0",
"description": "The command-line interface for Vercel",
@@ -41,16 +41,16 @@
"node": ">= 14"
},
"dependencies": {
"@vercel/build-utils": "5.4.2",
"@vercel/go": "2.2.5",
"@vercel/hydrogen": "0.0.18",
"@vercel/next": "3.1.25",
"@vercel/node": "2.5.14",
"@vercel/python": "3.1.14",
"@vercel/redwood": "1.0.23",
"@vercel/remix": "1.0.24",
"@vercel/ruby": "1.3.31",
"@vercel/static-build": "1.0.22",
"@vercel/build-utils": "5.4.4",
"@vercel/go": "2.2.7",
"@vercel/hydrogen": "0.0.20",
"@vercel/next": "3.1.27",
"@vercel/node": "2.5.16",
"@vercel/python": "3.1.16",
"@vercel/redwood": "1.0.25",
"@vercel/remix": "1.0.26",
"@vercel/ruby": "1.3.33",
"@vercel/static-build": "1.0.25",
"update-notifier": "5.1.0"
},
"devDependencies": {
@@ -95,9 +95,9 @@
"@types/which": "1.3.2",
"@types/write-json-file": "2.2.1",
"@types/yauzl-promise": "2.1.0",
"@vercel/client": "12.2.4",
"@vercel/frameworks": "1.1.4",
"@vercel/fs-detectors": "3.0.0",
"@vercel/client": "12.2.6",
"@vercel/frameworks": "1.1.6",
"@vercel/fs-detectors": "3.3.0",
"@vercel/fun": "1.0.4",
"@vercel/ncc": "0.24.0",
"@zeit/source-map-support": "0.6.2",

View File

@@ -555,7 +555,150 @@ async function doBuild(
builds: builderRoutes,
});
const mergedImages = mergeImages(buildResults.values());
const images = vercelConfig?.images
if (images) {
if (typeof images !== 'object') {
throw new Error(
`vercel.json "images" should be an object received ${typeof images}.`
);
}
if (!Array.isArray(images.domains)) {
throw new Error(
`vercel.json "images.domains" should be an Array received ${typeof images.domains}.`
);
}
if (images.domains.length > 50) {
throw new Error(
`vercel.json "images.domains" exceeds length of 50 received length (${images.domains.length}).`
);
}
const invalidImageDomains = images.domains.filter(
(d: unknown) => typeof d !== 'string'
);
if (invalidImageDomains.length > 0) {
throw new Error(
`vercel.json "images.domains" should be an Array of strings received invalid values (${invalidImageDomains.join(
', '
)}).`
);
}
if (images.remotePatterns) {
if (!Array.isArray(images.remotePatterns)) {
throw new Error(
`vercel.json "images.remotePatterns" should be an Array received ${typeof images.remotePatterns}.`
);
}
if (images.remotePatterns.length > 50) {
throw new Error(
`vercel.json "images.remotePatterns" exceeds length of 50, received length (${images.remotePatterns.length}).`
);
}
const validProps = new Set(['protocol', 'hostname', 'pathname', 'port']);
const requiredProps = ['hostname'];
const invalidPatterns = images.remotePatterns.filter(
(d: unknown) =>
!d ||
typeof d !== 'object' ||
Object.entries(d).some(
([k, v]) => !validProps.has(k) || typeof v !== 'string'
) ||
requiredProps.some(k => !(k in d))
);
if (invalidPatterns.length > 0) {
throw new Error(
`vercel.json "images.remotePatterns" received invalid values:\n${invalidPatterns
.map(item => JSON.stringify(item))
.join(
'\n'
)}\n\nremotePatterns value must follow format { protocol: 'https', hostname: 'example.com', port: '', pathname: '/imgs/**' }.`
);
}
}
if (!Array.isArray(images.sizes)) {
throw new Error(
`vercel.json "images.sizes" should be an Array received ${typeof images.sizes}.`
);
}
if (images.sizes.length < 1 || images.sizes.length > 50) {
throw new Error(
`vercel.json "images.sizes" should be an Array of length between 1 to 50 received length (${images.sizes.length}).`
);
}
const invalidImageSizes = images.sizes.filter((d: unknown) => {
return typeof d !== 'number' || d < 1 || d > 10000;
});
if (invalidImageSizes.length > 0) {
throw new Error(
`vercel.json "images.sizes" should be an Array of numbers that are between 1 and 10000, received invalid values (${invalidImageSizes.join(
', '
)}).`
);
}
if (images.minimumCacheTTL) {
if (
!Number.isInteger(images.minimumCacheTTL) ||
images.minimumCacheTTL < 0
) {
throw new Error(
`vercel.json "images.minimumCacheTTL" should be an integer 0 or more received (${images.minimumCacheTTL}).`
);
}
}
if (images.formats) {
if (!Array.isArray(images.formats)) {
throw new Error(
`vercel.json "images.formats" should be an Array received ${typeof images.formats}.`
);
}
if (images.formats.length < 1 || images.formats.length > 2) {
throw new Error(
`vercel.json "images.formats" must be length 1 or 2, received length (${images.formats.length}).`
);
}
const invalid = images.formats.filter(f => {
return f !== 'image/avif' && f !== 'image/webp';
});
if (invalid.length > 0) {
throw new Error(
`vercel.json "images.formats" should be an Array of mime type strings, received invalid values (${invalid.join(
', '
)}).`
);
}
}
if (
typeof images.dangerouslyAllowSVG !== 'undefined' &&
typeof images.dangerouslyAllowSVG !== 'boolean'
) {
throw new Error(
`vercel.json "images.dangerouslyAllowSVG" should be a boolean received (${images.dangerouslyAllowSVG}).`
);
}
if (
typeof images.contentSecurityPolicy !== 'undefined' &&
typeof images.contentSecurityPolicy !== 'string'
) {
throw new Error(
`vercel.json "images.contentSecurityPolicy" should be a string received ${images.contentSecurityPolicy}`
);
}
}
const mergedImages = mergeImages(images, buildResults.values());
const mergedWildcard = mergeWildcard(buildResults.values());
const mergedOverrides: Record<string, PathOverride> =
overrides.length > 0 ? Object.assign({}, ...overrides) : undefined;
@@ -624,9 +767,9 @@ function expandBuild(files: string[], build: Builder): Builder[] {
}
function mergeImages(
images: BuildResultV2Typical['images'],
buildResults: Iterable<BuildResult>
): BuildResultV2Typical['images'] {
let images: BuildResultV2Typical['images'] = undefined;
for (const result of buildResults) {
if ('images' in result && result.images) {
images = Object.assign({}, images, result.images);

View File

@@ -522,7 +522,12 @@ export default async (client: Client): Promise<number> => {
quiet,
wantsPublic: argv['--public'] || localConfig.public,
type: null,
nowConfig: localConfig,
nowConfig: {
...localConfig,
// `images` is allowed in "vercel.json" and processed
// by `vc build`, but don't send it to the API endpoint
images: undefined,
},
regions,
meta,
gitMetadata,

View File

@@ -2,7 +2,7 @@ import { resolve, join } from 'path';
import fs from 'fs-extra';
import DevServer from '../../util/dev/server';
import parseListen from '../../util/dev/parse-listen';
import { parseListen } from '../../util/dev/parse-listen';
import { ProjectEnvVariable } from '../../types';
import Client from '../../util/client';
import { getLinkedProject } from '../../util/projects/link';

View File

@@ -1,7 +1,10 @@
import { URL } from 'url';
import plural from 'pluralize';
import npa from 'npm-package-arg';
import { satisfies } from 'semver';
import { dirname, join } from 'path';
import { mkdirp, outputJSON, readJSON, symlink } from 'fs-extra';
import { isStaticRuntime } from '@vercel/fs-detectors';
import {
BuilderV2,
BuilderV3,
@@ -13,6 +16,9 @@ import { VERCEL_DIR } from '../projects/link';
import { Output } from '../output';
import readJSONFile from '../read-json-file';
import { CantParseJSONFile } from '../errors-ts';
import { errorToString, isErrnoException, isError } from '../is-error';
import cmd from '../output/cmd';
import code from '../output/code';
export interface BuilderWithPkg {
path: string;
@@ -81,7 +87,7 @@ export async function resolveBuilders(
continue;
}
if (name === '@vercel/static' || name === '@now/static') {
if (isStaticRuntime(name)) {
// `@vercel/static` is a special-case built-in builder
builders.set(name, {
builder: staticBuilder,
@@ -201,15 +207,54 @@ async function installBuilders(
if (err.code !== 'EEXIST') throw err;
}
output.debug(`Installing Builders: ${Array.from(buildersToAdd).join(', ')}`);
await spawnAsync('yarn', ['add', '@vercel/build-utils', ...buildersToAdd], {
cwd: buildersDir,
});
output.log(
`Installing ${plural('Builder', buildersToAdd.size)}: ${Array.from(
buildersToAdd
).join(', ')}`
);
try {
await spawnAsync(
'npm',
['install', '@vercel/build-utils', ...buildersToAdd],
{
cwd: buildersDir,
stdio: 'pipe',
}
);
} catch (err: unknown) {
if (isError(err)) {
(err as any).link =
'https://vercel.link/builder-dependencies-install-failed';
if (isErrnoException(err) && err.code === 'ENOENT') {
// `npm` is not installed
err.message = `Please install ${cmd('npm')} before continuing`;
} else {
const message = errorToString(err);
const notFound = /GET (.*) - Not found/.exec(message);
if (notFound) {
const url = new URL(notFound[1]);
const packageName = decodeURIComponent(url.pathname.slice(1));
err.message = `The package ${code(
packageName
)} is not published on the npm registry`;
}
}
}
throw err;
}
// Symlink `@now/build-utils` -> `@vercel/build-utils` to support legacy Builders
const nowScopePath = join(buildersDir, 'node_modules/@now');
await mkdirp(nowScopePath);
await symlink('../@vercel/build-utils', join(nowScopePath, 'build-utils'));
try {
await symlink('../@vercel/build-utils', join(nowScopePath, 'build-utils'));
} catch (err: unknown) {
if (!isErrnoException(err) || err.code !== 'EEXIST') {
// Throw unless the error is due to the symlink already existing
throw err;
}
}
// Cross-reference any builderSpecs from the saved `package.json` file,
// in case they were installed from a URL

View File

@@ -1,5 +1,6 @@
import minimatch from 'minimatch';
import { BuildV2, Files } from '@vercel/build-utils';
import { shouldServe as defaultShouldServe } from '@vercel/build-utils';
import type { BuildV2, Files, ShouldServe } from '@vercel/build-utils';
export const version = 2;
@@ -39,3 +40,18 @@ export const build: BuildV2 = async ({ entrypoint, files, config }) => {
return { output };
};
export const shouldServe: ShouldServe = _opts => {
const opts = { ..._opts };
const {
config: { zeroConfig, outputDirectory },
} = opts;
// Add the output directory prefix
if (zeroConfig && outputDirectory) {
opts.entrypoint = `${outputDirectory}/${opts.entrypoint}`;
opts.requestPath = `${outputDirectory}/${opts.requestPath}`;
}
return defaultShouldServe(opts);
};

View File

@@ -1,412 +0,0 @@
import chalk from 'chalk';
import execa from 'execa';
import semver from 'semver';
import npa from 'npm-package-arg';
import pluralize from 'pluralize';
import { basename, join } from 'path';
import XDGAppPaths from 'xdg-app-paths';
import { mkdirp, readJSON, writeJSON } from 'fs-extra';
import { NowBuildError, PackageJson } from '@vercel/build-utils';
import cliPkg from '../pkg';
import cmd from '../output/cmd';
import { Output } from '../output';
import { NoBuilderCacheError } from '../errors-ts';
import * as staticBuilder from './static-builder';
import { BuilderWithPackage } from './types';
import { isErrnoException } from '../is-error';
const require_: typeof require = eval('require');
const registryTypes = new Set(['version', 'tag', 'range']);
const createStaticBuilder = (scope: string): BuilderWithPackage => {
return {
runInProcess: true,
requirePath: `${scope}/static`,
builder: Object.freeze(staticBuilder),
package: Object.freeze({ name: `@${scope}/static`, version: '' }),
};
};
const localBuilders: { [key: string]: BuilderWithPackage } = {
'@now/static': createStaticBuilder('now'),
'@vercel/static': createStaticBuilder('vercel'),
};
export const cacheDirPromise = prepareCacheDir();
export const builderDirPromise = prepareBuilderDir();
/**
* Prepare cache directory for installing Vercel runtimes.
*/
export async function prepareCacheDir() {
const designated = XDGAppPaths('com.vercel.cli').cache();
if (!designated) {
throw new NoBuilderCacheError();
}
const cacheDir = join(designated, 'dev');
await mkdirp(cacheDir);
return cacheDir;
}
export async function prepareBuilderDir() {
const builderDir = join(await cacheDirPromise, 'builders');
await mkdirp(builderDir);
// Create an empty `package.json` file, only if one does not already exist
try {
const buildersPkg = join(builderDir, 'package.json');
await writeJSON(buildersPkg, { private: true }, { flag: 'wx' });
} catch (err: unknown) {
if (!isErrnoException(err) || err.code !== 'EEXIST') {
throw err;
}
}
return builderDir;
}
function getNpmVersion(use = ''): string {
const parsed = npa(use);
if (registryTypes.has(parsed.type)) {
return parsed.fetchSpec || '';
}
return '';
}
export function getBuildUtils(packages: string[]): string {
const version = packages
.map(getNpmVersion)
.some(ver => ver.includes('canary'))
? 'canary'
: 'latest';
return `@vercel/build-utils@${version}`;
}
function parseVersionSafe(rawSpec: string) {
try {
return semver.parse(rawSpec);
} catch (e) {
return null;
}
}
export function filterPackage(
builderSpec: string,
buildersPkg: PackageJson,
cliPkg: Partial<PackageJson>
) {
if (builderSpec in localBuilders) return false;
const parsed = npa(builderSpec);
const parsedVersion = parseVersionSafe(parsed.rawSpec);
// Skip install of Runtimes that are part of Vercel CLI's `dependencies`
if (isBundledBuilder(parsed, cliPkg)) {
return false;
}
// Skip install of already installed Runtime with exact version match
if (
parsed.name &&
parsed.type === 'version' &&
parsedVersion &&
buildersPkg.dependencies &&
parsedVersion.version == buildersPkg.dependencies[parsed.name]
) {
return false;
}
return true;
}
/**
* Install a list of builders to the cache directory.
*/
export async function installBuilders(
packagesSet: Set<string>,
output: Output,
builderDir?: string
): Promise<void> {
const packages = Array.from(packagesSet);
if (
packages.length === 0 ||
(packages.length === 1 &&
Object.hasOwnProperty.call(localBuilders, packages[0]))
) {
// Static deployment, no builders to install
return;
}
if (!builderDir) {
builderDir = await builderDirPromise;
}
const buildersPkgPath = join(builderDir, 'package.json');
const buildersPkgBefore = await readJSON(buildersPkgPath);
const depsBefore = {
...buildersPkgBefore.devDependencies,
...buildersPkgBefore.dependencies,
};
// Filter out any packages that come packaged with Vercel CLI
const packagesToInstall = packages.filter(p =>
filterPackage(p, buildersPkgBefore, cliPkg)
);
if (packagesToInstall.length === 0) {
output.debug('No Runtimes need to be installed');
return;
}
packagesToInstall.push(getBuildUtils(packages));
await npmInstall(builderDir, output, packagesToInstall, false);
const updatedPackages: string[] = [];
const buildersPkgAfter = await readJSON(buildersPkgPath);
const depsAfter = {
...buildersPkgAfter.devDependencies,
...buildersPkgAfter.dependencies,
};
for (const [name, version] of Object.entries(depsAfter)) {
if (version !== depsBefore[name]) {
output.debug(`Runtime "${name}" updated to version \`${version}\``);
updatedPackages.push(name);
}
}
purgeRequireCache(updatedPackages, builderDir, output);
}
async function npmInstall(
cwd: string,
output: Output,
packagesToInstall: string[],
silent: boolean
) {
const sortedPackages = packagesToInstall.sort();
if (!silent) {
output.spinner(
`Installing ${pluralize(
'Runtime',
sortedPackages.length
)}: ${sortedPackages.join(', ')}`
);
}
output.debug(`Running npm install in ${cwd}`);
try {
const args = [
'install',
'--save-exact',
'--no-package-lock',
'--no-audit',
'--no-progress',
];
if (process.stderr.isTTY) {
// Force colors in the npm child process
// https://docs.npmjs.com/misc/config#color
args.push('--color=always');
}
args.push(...sortedPackages);
const result = await execa('npm', args, {
cwd,
reject: false,
stdio: output.isDebugEnabled() ? 'inherit' : 'pipe',
});
if (result.failed) {
output.stopSpinner();
if (result.stdout) {
console.log(result.stdout);
}
if (result.stderr) {
console.error(result.stderr);
}
throw new NowBuildError({
message:
(result as any).code === 'ENOENT'
? `Command not found: ${chalk.cyan(
'npm'
)}\nPlease ensure that ${cmd('npm')} is properly installed`
: 'Failed to install `vercel dev` dependencies',
code: 'NPM_INSTALL_ERROR',
link: 'https://vercel.link/npm-install-failed-dev',
});
}
} finally {
output.stopSpinner();
}
}
export async function updateBuilders(
packagesSet: Set<string>,
output: Output,
builderDir?: string
): Promise<string[]> {
if (!builderDir) {
builderDir = await builderDirPromise;
}
const updatedPackages: string[] = [];
const packages = Array.from(packagesSet);
const buildersPkgPath = join(builderDir, 'package.json');
const buildersPkgBefore = await readJSON(buildersPkgPath);
const depsBefore = {
...buildersPkgBefore.devDependencies,
...buildersPkgBefore.dependencies,
};
const packagesToUpdate = packages.filter(p => {
if (p in localBuilders) return false;
// If it's a builder that is part of Vercel CLI's
// `dependencies` then don't update it
if (isBundledBuilder(npa(p), cliPkg)) {
return false;
}
return true;
});
if (packagesToUpdate.length > 0) {
packagesToUpdate.push(getBuildUtils(packages));
await npmInstall(builderDir, output, packagesToUpdate, true);
const buildersPkgAfter = await readJSON(buildersPkgPath);
const depsAfter = {
...buildersPkgAfter.devDependencies,
...buildersPkgAfter.dependencies,
};
for (const [name, version] of Object.entries(depsAfter)) {
if (version !== depsBefore[name]) {
output.debug(`Runtime "${name}" updated to version \`${version}\``);
updatedPackages.push(name);
}
}
purgeRequireCache(updatedPackages, builderDir, output);
}
return updatedPackages;
}
/**
* Get a builder from the cache directory.
*/
export async function getBuilder(
builderPkg: string,
output: Output,
builderDir?: string,
isRetry = false
): Promise<BuilderWithPackage> {
let builderWithPkg: BuilderWithPackage = localBuilders[builderPkg];
if (!builderWithPkg) {
if (!builderDir) {
builderDir = await builderDirPromise;
}
let requirePath: string;
const parsed = npa(builderPkg);
// First check if it's a bundled Runtime in Vercel CLI's `node_modules`
const bundledBuilder = isBundledBuilder(parsed, cliPkg);
if (bundledBuilder && parsed.name) {
requirePath = parsed.name;
} else {
const buildersPkg = await readJSON(join(builderDir, 'package.json'));
const pkgName = getPackageName(parsed, buildersPkg) || builderPkg;
requirePath = join(builderDir, 'node_modules', pkgName);
}
try {
output.debug(`Requiring runtime: "${requirePath}"`);
const mod = require_(requirePath);
const pkg = require_(join(requirePath, 'package.json'));
builderWithPkg = {
requirePath,
builder: Object.freeze(mod),
package: Object.freeze(pkg),
};
} catch (err: unknown) {
if (
isErrnoException(err) &&
err.code === 'MODULE_NOT_FOUND' &&
!isRetry
) {
output.debug(
`Attempted to require ${requirePath}, but it is not installed`
);
const pkgSet = new Set([builderPkg]);
await installBuilders(pkgSet, output, builderDir);
// Run `getBuilder()` again now that the builder has been installed
return getBuilder(builderPkg, output, builderDir, true);
}
throw err;
}
// If it's a bundled builder, then cache the require call
if (bundledBuilder) {
localBuilders[builderPkg] = builderWithPkg;
}
}
return builderWithPkg;
}
export function isBundledBuilder(
parsed: npa.Result,
{ dependencies = {} }: PackageJson
): boolean {
if (!parsed.name) {
return false;
}
const inCliDependencyList = !!dependencies[parsed.name];
const inScope = parsed.scope === '@vercel';
const isVersionedReference = ['tag', 'version', 'range'].includes(
parsed.type
);
return inCliDependencyList && inScope && isVersionedReference;
}
function getPackageName(
parsed: npa.Result,
buildersPkg: PackageJson
): string | null {
if (registryTypes.has(parsed.type)) {
return parsed.name;
}
const deps: PackageJson.DependencyMap = {
...buildersPkg.devDependencies,
...buildersPkg.dependencies,
};
for (const [name, dep] of Object.entries(deps)) {
if (dep === parsed.raw || basename(dep) === basename(parsed.raw)) {
return name;
}
}
return null;
}
function purgeRequireCache(
packages: string[],
builderDir: string,
output: Output
) {
// The `require()` cache for the builder's assets must be purged
const packagesPaths = packages.map(b => join(builderDir, 'node_modules', b));
for (const id of Object.keys(require_.cache)) {
for (const path of packagesPaths) {
if (id.startsWith(path)) {
output.debug(`Purging require cache for "${id}"`);
delete require_.cache[id];
}
}
}
}

View File

@@ -15,7 +15,7 @@ import {
FileFsRef,
normalizePath,
} from '@vercel/build-utils';
import { isOfficialRuntime } from '@vercel/fs-detectors';
import { isStaticRuntime } from '@vercel/fs-detectors';
import plural from 'pluralize';
import minimatch from 'minimatch';
@@ -26,7 +26,6 @@ import { relative } from '../path-helpers';
import { LambdaSizeExceededError } from '../errors-ts';
import DevServer from './server';
import { getBuilder } from './builder-cache';
import {
VercelConfig,
BuildMatch,
@@ -41,6 +40,7 @@ import {
import { normalizeRoutes } from '@vercel/routing-utils';
import getUpdateCommand from '../get-update-command';
import { getTitleName } from '../pkg-name';
import { importBuilders } from '../build/import-builders';
interface BuildMessage {
type: string;
@@ -108,18 +108,18 @@ export async function executeBuild(
filesRemoved?: string[]
): Promise<void> {
const {
builderWithPkg: { runInProcess, requirePath, builder, package: pkg },
builderWithPkg: { path: requirePath, builder, pkg },
} = match;
const { entrypoint } = match;
const { entrypoint, use } = match;
const isStatic = isStaticRuntime(use);
const { envConfigs, cwd: workPath, devCacheDir } = devServer;
const debug = devServer.output.isDebugEnabled();
const startTime = Date.now();
const showBuildTimestamp =
!isOfficialRuntime('static', match.use) && (!isInitialBuild || debug);
const showBuildTimestamp = !isStatic && (!isInitialBuild || debug);
if (showBuildTimestamp) {
devServer.output.log(`Building ${match.use}:${entrypoint}`);
devServer.output.log(`Building ${use}:${entrypoint}`);
devServer.output.debug(
`Using \`${pkg.name}${pkg.version ? `@${pkg.version}` : ''}\``
);
@@ -130,7 +130,7 @@ export async function executeBuild(
let result: BuildResult;
let { buildProcess } = match;
if (!runInProcess && !buildProcess) {
if (!isStatic && !buildProcess) {
buildProcess = await createBuildProcess(
match,
envConfigs,
@@ -158,7 +158,7 @@ export async function executeBuild(
},
};
let buildResultOrOutputs: BuilderOutputs | BuildResult | BuildResultV3;
let buildResultOrOutputs;
if (buildProcess) {
buildProcess.send({
type: 'build',
@@ -198,16 +198,12 @@ export async function executeBuild(
}
// Sort out build result to builder v2 shape
if (!builder.version || builder.version === 1) {
if (!builder.version || (builder as any).version === 1) {
// `BuilderOutputs` map was returned (Now Builder v1 behavior)
result = {
output: buildResultOrOutputs as BuilderOutputs,
routes: [],
watch: [],
distPath:
typeof buildResultOrOutputs.distPath === 'string'
? buildResultOrOutputs.distPath
: undefined,
};
} else if (builder.version === 2) {
result = buildResultOrOutputs as BuildResult;
@@ -253,7 +249,7 @@ export async function executeBuild(
} else {
throw new Error(
`${getTitleName()} CLI does not support builder version ${
builder.version
(builder as any).version
}.\nPlease run \`${await getUpdateCommand()}\` to update to the latest CLI.`
);
}
@@ -383,7 +379,7 @@ export async function executeBuild(
if (showBuildTimestamp) {
const endTime = Date.now();
devServer.output.log(
`Built ${match.use}:${entrypoint} [${ms(endTime - startTime)}]`
`Built ${use}:${entrypoint} [${ms(endTime - startTime)}]`
);
}
}
@@ -405,6 +401,8 @@ export async function getBuildMatches(
const noMatches: Builder[] = [];
const builds = vercelConfig.builds || [{ src: '**', use: '@vercel/static' }];
const builderSpecs = new Set(builds.map(b => b.use).filter(Boolean));
const buildersWithPkgs = await importBuilders(builderSpecs, cwd, output);
for (const buildConfig of builds) {
let { src = '**', use, config = {} } = buildConfig;
@@ -439,6 +437,8 @@ export async function getBuildMatches(
for (const file of files) {
src = relative(cwd, file);
const entrypoint = mapToEntrypoint.get(src) || src;
// Remove the output directory prefix
if (config.zeroConfig && config.outputDirectory) {
const outputMatch = config.outputDirectory + '/';
@@ -447,11 +447,15 @@ export async function getBuildMatches(
}
}
const builderWithPkg = await getBuilder(use, output);
const builderWithPkg = buildersWithPkgs.get(use);
if (!builderWithPkg) {
throw new Error(`Failed to load Builder "${use}"`);
}
matches.push({
...buildConfig,
src,
entrypoint: mapToEntrypoint.get(src) || src,
entrypoint,
builderWithPkg,
buildOutput: {},
buildResults: new Map(),

View File

@@ -1,10 +1,7 @@
import { parse } from 'url';
import { ListenSpec } from './types';
export default function parseListen(
str: string,
defaultPort = 3000
): ListenSpec {
export function parseListen(str: string, defaultPort = 3000): ListenSpec {
let port = Number(str);
if (!isNaN(port)) {
@@ -50,3 +47,7 @@ export default function parseListen(
);
}
}
export function replaceLocalhost(address: string): string {
return address.replace('[::]', 'localhost').replace('0.0.0.0', 'localhost');
}

View File

@@ -1,4 +1,3 @@
import ms from 'ms';
import url, { URL } from 'url';
import http from 'http';
import fs from 'fs-extra';
@@ -32,6 +31,7 @@ import {
} from '@vercel/routing-utils';
import {
Builder,
cloneEnv,
Env,
StartDevServerResult,
FileFsRef,
@@ -62,11 +62,6 @@ import { devRouter, getRoutesTypes } from './router';
import getMimeType from './mime-type';
import { executeBuild, getBuildMatches, shutdownBuilder } from './builder';
import { generateErrorMessage, generateHttpStatusDescription } from './errors';
import {
installBuilders,
updateBuilders,
builderDirPromise,
} from './builder-cache';
// HTML templates
import errorTemplate from './templates/error';
@@ -101,7 +96,9 @@ import {
isError,
isSpawnError,
} from '../is-error';
import isURL from './is-url';
import { pickOverrides } from '../projects/project-settings';
import { replaceLocalhost } from './parse-listen';
const frontendRuntimeSet = new Set(
frameworkList.map(f => f.useRuntime?.use || '@vercel/static-build')
@@ -134,9 +131,18 @@ export default class DevServer {
public proxy: httpProxy;
public envConfigs: EnvConfigs;
public files: BuilderInputs;
public address: string;
public devCacheDir: string;
private _address: URL | undefined;
public get address(): URL {
if (!this._address) {
throw new Error(
'Invalid access to `address` because `start` has not yet populated `this.address`.'
);
}
return this._address;
}
public devCacheDir: string;
private currentDevCommand?: string;
private caseSensitive: boolean;
private apiDir: string | null;
@@ -152,7 +158,7 @@ export default class DevServer {
private filter: (path: string) => boolean;
private podId: string;
private devProcess?: ChildProcess;
private devProcessPort?: number;
private devProcessOrigin?: string;
private devServerPids: Set<number>;
private originalProjectSettings?: ProjectSettings;
private projectSettings?: ProjectSettings;
@@ -160,8 +166,6 @@ export default class DevServer {
private vercelConfigWarning: boolean;
private getVercelConfigPromise: Promise<VercelConfig> | null;
private blockingBuildsPromise: Promise<void> | null;
private updateBuildersPromise: Promise<void> | null;
private updateBuildersTimeout: NodeJS.Timeout | undefined;
private startPromise: Promise<void> | null;
private systemEnvValues: string[];
@@ -174,17 +178,22 @@ export default class DevServer {
this.systemEnvValues = options.systemEnvValues || [];
this.projectEnvs = options.projectEnvs || [];
this.files = {};
this.address = '';
this.originalProjectSettings = options.projectSettings;
this.projectSettings = options.projectSettings;
this.caseSensitive = false;
this.apiDir = null;
this.apiExtensions = new Set();
this.proxy = httpProxy.createProxyServer({
changeOrigin: true,
ws: true,
xfwd: true,
});
this.proxy.on('proxyRes', proxyRes => {
// override "server" header, like production
proxyRes.headers['server'] = 'Vercel';
});
this.server = http.createServer(this.devServerHandler);
this.server.timeout = 0; // Disable timeout
this.stopping = false;
@@ -195,7 +204,6 @@ export default class DevServer {
this.vercelConfigWarning = false;
this.getVercelConfigPromise = null;
this.blockingBuildsPromise = null;
this.updateBuildersPromise = null;
this.startPromise = null;
this.watchAggregationId = null;
@@ -477,33 +485,6 @@ export default class DevServer {
);
}
async invalidateBuildMatches(
vercelConfig: VercelConfig,
updatedBuilders: string[]
): Promise<void> {
if (updatedBuilders.length === 0) {
this.output.debug('No builders were updated');
return;
}
// Delete any build matches that have the old builder required already
for (const buildMatch of this.buildMatches.values()) {
const {
src,
builderWithPkg: { package: pkg },
} = buildMatch;
if (isOfficialRuntime('static', pkg.name)) continue;
if (pkg.name && updatedBuilders.includes(pkg.name)) {
shutdownBuilder(buildMatch, this.output);
this.buildMatches.delete(src);
this.output.debug(`Invalidated build match for "${src}"`);
}
}
// Re-add the build matches that were just removed, but with the new builder
await this.updateBuildMatches(vercelConfig);
}
async getLocalEnv(fileName: string, base?: Env): Promise<Env> {
// TODO: use the file watcher to only invalidate the env `dotfile`
// once a change to the `fileName` occurs
@@ -707,7 +688,7 @@ export default class DevServer {
this.projectEnvs || [],
this.systemEnvValues || [],
this.projectSettings?.autoExposeSystemEnvs,
new URL(this.address).host
this.address.host
);
allEnv = { ...cloudEnv };
@@ -842,7 +823,7 @@ export default class DevServer {
injectSystemValuesInDotenv(env: Env): Env {
for (const name of Object.keys(env)) {
if (name === 'VERCEL_URL') {
env['VERCEL_URL'] = new URL(this.address).host;
env['VERCEL_URL'] = this.address.host;
} else if (name === 'VERCEL_REGION') {
env['VERCEL_REGION'] = 'dev1';
}
@@ -915,9 +896,7 @@ export default class DevServer {
}
}
this.address = address
.replace('[::]', 'localhost')
.replace('127.0.0.1', 'localhost');
this._address = new URL(replaceLocalhost(address));
const vercelConfig = await this.getVercelConfig();
const devCommandPromise = this.runDevCommand();
@@ -934,30 +913,8 @@ export default class DevServer {
}
}
const builders = new Set<string>(
(vercelConfig.builds || [])
.filter((b: Builder) => b.use)
.map((b: Builder) => b.use)
);
await installBuilders(builders, this.output);
await this.updateBuildMatches(vercelConfig, true);
// Updating builders happens lazily, and any builders that were updated
// get their "build matches" invalidated so that the new version is used.
this.updateBuildersTimeout = setTimeout(() => {
this.updateBuildersPromise = updateBuilders(builders, this.output)
.then(updatedBuilders => {
this.updateBuildersPromise = null;
this.invalidateBuildMatches(vercelConfig, updatedBuilders);
})
.catch(err => {
this.updateBuildersPromise = null;
this.output.prettyError(err);
this.output.debug(err.stack);
});
}, ms('30s'));
// Builders that do not define a `shouldServe()` function need to be
// executed at boot-up time in order to get the initial assets and/or routes
// that can be served by the builder.
@@ -1007,37 +964,37 @@ export default class DevServer {
// Configure the server to forward WebSocket "upgrade" events to the proxy.
this.server.on('upgrade', async (req, socket, head) => {
await this.startPromise;
if (!this.devProcessPort) {
if (!this.devProcessOrigin) {
this.output.debug(
`Detected "upgrade" event, but closing socket because no frontend dev server is running`
);
socket.destroy();
return;
}
const target = `http://127.0.0.1:${this.devProcessPort}`;
const target = this.devProcessOrigin;
this.output.debug(`Detected "upgrade" event, proxying to ${target}`);
this.proxy.ws(req, socket, head, { target });
});
await devCommandPromise;
this.output.ready(`Available at ${link(this.address)}`);
let addressFormatted = this.address.toString();
if (this.address.pathname === '/' && this.address.protocol === 'http:') {
// log address without trailing slash to maintain backwards compatibility
addressFormatted = addressFormatted.replace(/\/$/, '');
}
this.output.ready(`Available at ${link(addressFormatted)}`);
}
/**
* Shuts down the `vercel dev` server, and cleans up any temporary resources.
*/
async stop(exitCode?: number): Promise<void> {
const { devProcess } = this;
const { debug } = this.output;
if (this.stopping) return;
this.stopping = true;
if (typeof this.updateBuildersTimeout !== 'undefined') {
clearTimeout(this.updateBuildersTimeout);
}
const { devProcess } = this;
const { debug } = this.output;
const ops: Promise<any>[] = [];
for (const match of this.buildMatches.values()) {
@@ -1055,18 +1012,10 @@ export default class DevServer {
ops.push(this.watcher.close());
}
if (this.updateBuildersPromise) {
debug(`Waiting for builders update to complete`);
ops.push(this.updateBuildersPromise);
}
for (const pid of this.devServerPids) {
ops.push(this.killBuilderDevServer(pid));
}
// Ensure that the builders module cache is created
ops.push(builderDirPromise);
try {
await Promise.all(ops);
} catch (err: unknown) {
@@ -1465,8 +1414,9 @@ export default class DevServer {
// the middleware server for every HTTP request?
const { envConfigs, files, devCacheDir, cwd: workPath } = this;
try {
startMiddlewareResult =
await middleware.builderWithPkg.builder.startDevServer?.({
const { builder } = middleware.builderWithPkg;
if (builder.version === 3) {
startMiddlewareResult = await builder.startDevServer?.({
files,
entrypoint: middleware.entrypoint,
workPath,
@@ -1480,6 +1430,7 @@ export default class DevServer {
buildEnv: { ...envConfigs.buildEnv },
},
});
}
if (startMiddlewareResult) {
const { port, pid } = startMiddlewareResult;
@@ -1560,15 +1511,34 @@ export default class DevServer {
}
if (rewritePath) {
// TODO: add validation?
debug(`Detected rewrite path from middleware: "${rewritePath}"`);
prevUrl = rewritePath;
// Retain orginal pathname, but override query parameters from the rewrite
const beforeRewriteUrl = req.url || '/';
const rewriteUrlParsed = url.parse(beforeRewriteUrl);
rewriteUrlParsed.search = url.parse(rewritePath).search;
req.url = url.format(rewriteUrlParsed);
if (isURL(rewritePath)) {
const rewriteUrlParsed = new URL(rewritePath);
// `this.address` already has localhost normalized from ip4 and ip6 values
if (this.address.origin === rewriteUrlParsed.origin) {
// remove origin, leaving the path
req.url =
rewritePath.slice(rewriteUrlParsed.origin.length) || '/';
prevUrl = req.url;
} else {
// Proxy to absolute URL with different origin
debug(`ProxyPass: ${rewritePath}`);
this.setResponseHeaders(res, requestId);
proxyPass(req, res, rewritePath, this, requestId);
return;
}
} else {
// Retain orginal pathname, but override query parameters from the rewrite
const rewriteUrlParsed = url.parse(beforeRewriteUrl);
rewriteUrlParsed.search = url.parse(rewritePath).search;
req.url = url.format(rewriteUrlParsed);
}
debug(
`Rewrote incoming HTTP URL from "${beforeRewriteUrl}" to "${req.url}"`
);
@@ -1617,12 +1587,16 @@ export default class DevServer {
missRoutes,
phase
);
prevUrl =
routeResult.continue && routeResult.dest
? getReqUrl(routeResult)
: req.url;
prevHeaders =
routeResult.continue && routeResult.headers ? routeResult.headers : {};
if (routeResult.continue) {
if (routeResult.dest) {
prevUrl = getReqUrl(routeResult);
}
if (routeResult.headers) {
prevHeaders = routeResult.headers;
}
}
if (routeResult.isDestUrl) {
// Mix the `routes` result dest query params into the req path
@@ -1795,8 +1769,8 @@ export default class DevServer {
if (!match) {
// If the dev command is started, then proxy to it
if (this.devProcessPort) {
const upstream = `http://127.0.0.1:${this.devProcessPort}`;
if (this.devProcessOrigin) {
const upstream = this.devProcessOrigin;
debug(`Proxying to frontend dev server: ${upstream}`);
// Add the Vercel platform proxy request headers
@@ -1869,8 +1843,8 @@ export default class DevServer {
// up a single-serve dev HTTP server that vercel dev will proxy this HTTP request
// to. Once the proxied request is finished, vercel dev shuts down the dev
// server child process.
const { builder, package: builderPkg } = match.builderWithPkg;
if (typeof builder.startDevServer === 'function') {
const { builder, pkg: builderPkg } = match.builderWithPkg;
if (builder.version === 3 && typeof builder.startDevServer === 'function') {
let devServerResult: StartDevServerResult = null;
try {
const { envConfigs, files, devCacheDir, cwd: workPath } = this;
@@ -1975,7 +1949,7 @@ export default class DevServer {
// - when there is no asset
// - when the asset is not a Lambda (the dev server must take care of all static files)
if (
this.devProcessPort &&
this.devProcessOrigin &&
(!foundAsset || (foundAsset && foundAsset.asset.type !== 'Lambda'))
) {
debug('Proxying to frontend dev server');
@@ -1987,14 +1961,7 @@ export default class DevServer {
}
this.setResponseHeaders(res, requestId);
return proxyPass(
req,
res,
`http://127.0.0.1:${this.devProcessPort}`,
this,
requestId,
false
);
return proxyPass(req, res, this.devProcessOrigin, this, requestId, false);
}
if (!foundAsset) {
@@ -2256,18 +2223,22 @@ export default class DevServer {
const port = await getPort();
const env: Env = {
// Because of child process 'pipe' below, isTTY will be false.
// Most frameworks use `chalk`/`supports-color` so we enable it anyway.
FORCE_COLOR: process.stdout.isTTY ? '1' : '0',
// Prevent framework dev servers from automatically opening a web
// browser window, since it will not be the port that `vc dev`
// is listening on and thus will be missing Vercel features.
BROWSER: 'none',
...process.env,
...this.envConfigs.allEnv,
PORT: `${port}`,
};
const env: Env = cloneEnv(
{
// Because of child process 'pipe' below, isTTY will be false.
// Most frameworks use `chalk`/`supports-color` so we enable it anyway.
FORCE_COLOR: process.stdout.isTTY ? '1' : '0',
// Prevent framework dev servers from automatically opening a web
// browser window, since it will not be the port that `vc dev`
// is listening on and thus will be missing Vercel features.
BROWSER: 'none',
},
process.env,
this.envConfigs.allEnv,
{
PORT: `${port}`,
}
);
// This is necesary so that the dev command in the Project
// will work cross-platform (especially Windows).
@@ -2301,7 +2272,6 @@ export default class DevServer {
this.output.debug(`Spawning dev command: ${command}`);
const devPort = new URL(this.address).port;
const proxyPort = new RegExp(port.toString(), 'g');
const p = spawnCommand(command, {
stdio: ['inherit', 'pipe', 'pipe'],
@@ -2318,17 +2288,16 @@ export default class DevServer {
p.stdout.setEncoding('utf8');
p.stdout.on('data', (data: string) => {
process.stdout.write(data.replace(proxyPort, devPort));
process.stdout.write(data.replace(proxyPort, this.address.port));
});
p.on('exit', (code, signal) => {
this.output.debug(`Dev command exited with "${signal || code}"`);
this.devProcessPort = undefined;
this.devProcessOrigin = undefined;
});
await checkForPort(port, 1000 * 60 * 5);
this.devProcessPort = port;
const devProcessHost = await checkForPort(port, 1000 * 60 * 5);
this.devProcessOrigin = `http://${devProcessHost}:${port}`;
}
}
@@ -2616,15 +2585,29 @@ function needsBlockingBuild(buildMatch: BuildMatch): boolean {
return typeof builder.shouldServe !== 'function';
}
async function checkForPort(port: number, timeout: number): Promise<void> {
const opts = { host: '127.0.0.1' };
async function checkForPort(port: number, timeout: number): Promise<string> {
let host;
const start = Date.now();
while (!(await isPortReachable(port, opts))) {
while (!(host = await getReachableHostOnPort(port))) {
if (Date.now() - start > timeout) {
throw new Error(`Detecting port ${port} timed out after ${timeout}ms`);
break;
}
await sleep(100);
}
if (!host) {
throw new Error(`Detecting port ${port} timed out after ${timeout}ms`);
}
return host;
}
async function getReachableHostOnPort(port: number): Promise<string | false> {
const optsIpv4 = { host: '127.0.0.1' };
const optsIpv6 = { host: '::1' };
const results = await Promise.all([
isPortReachable(port, optsIpv6).then(r => r && `[${optsIpv6.host}]`),
isPortReachable(port, optsIpv4).then(r => r && optsIpv4.host),
]);
return results.find(Boolean) || false;
}
function filterFrontendBuilds(build: Builder) {

View File

@@ -1,42 +0,0 @@
import {
FileFsRef,
BuildOptions,
shouldServe as defaultShouldServe,
ShouldServeOptions,
} from '@vercel/build-utils';
import { BuildResult } from './types';
export const version = 2;
export function build({
files,
entrypoint,
config: { zeroConfig, outputDirectory },
}: BuildOptions): BuildResult {
const path =
zeroConfig && outputDirectory
? `${outputDirectory}/${entrypoint}`
: entrypoint;
return {
output: {
[entrypoint]: files[path] as FileFsRef,
},
routes: [],
watch: [path],
};
}
export function shouldServe(_opts: ShouldServeOptions) {
const opts = { ..._opts };
let {
config: { zeroConfig, outputDirectory },
} = opts;
// Add the output directory prefix
if (zeroConfig && outputDirectory) {
opts.entrypoint = `${outputDirectory}/${opts.entrypoint}`;
opts.requestPath = `${outputDirectory}/${opts.requestPath}`;
}
return defaultShouldServe(opts);
}

View File

@@ -12,12 +12,12 @@ import {
FileBlob,
FileFsRef,
Lambda,
PackageJson,
} from '@vercel/build-utils';
import { VercelConfig } from '@vercel/client';
import { HandleValue, Route } from '@vercel/routing-utils';
import { Output } from '../output';
import { ProjectEnvVariable, ProjectSettings } from '../../types';
import { BuilderWithPkg } from '../build/import-builders';
export { VercelConfig };
@@ -48,7 +48,7 @@ export interface EnvConfigs {
export interface BuildMatch extends BuildConfig {
entrypoint: string;
src: string;
builderWithPkg: BuilderWithPackage;
builderWithPkg: BuilderWithPkg;
buildOutput: BuilderOutputs;
buildResults: Map<string | null, BuildResult>;
buildTimestamp: number;
@@ -122,13 +122,6 @@ export interface BuildResultV4 {
distPath?: string;
}
export interface BuilderWithPackage {
runInProcess?: boolean;
requirePath: string;
builder: Readonly<Builder>;
package: Readonly<PackageJson>;
}
export interface HttpHeadersConfig {
[name: string]: string;
}

View File

@@ -0,0 +1,7 @@
export const config = {
runtime: 'experimental-edge',
};
export default async function edge(request: Request, event: Event) {
return new Response('heyo');
}

View File

@@ -0,0 +1 @@
<h1>Hello from Index</h1>

View File

@@ -0,0 +1,21 @@
export const config = {
runtime: 'experimental-edge',
};
export default async function edge(request: Request, event: Event) {
if (request.url.indexOf('/index.html') > -1) {
return new Response(null, {
headers: {
'x-middleware-rewrite': '/does-not-exist.html',
},
});
}
if (request.url.indexOf('/api/edge') > -1) {
return new Response(null, {
headers: {
'x-middleware-rewrite': '/api/does-not-exist',
},
});
}
}

View File

@@ -1,6 +1,15 @@
export default req => {
const url = new URL(req.url);
const rewriteTo = url.searchParams.get('to');
if (rewriteTo) {
return new Response(null, {
headers: {
'x-middleware-rewrite': rewriteTo,
},
});
}
if (url.pathname === '/') {
// Pass-through "index.html" page
return new Response(null, {

View File

@@ -1,7 +1,6 @@
{
"private": true,
"scripts": {
"dev": "vite --port $PORT",
"build": "vite build",
"serve": "vite preview"
},

View File

@@ -0,0 +1,3 @@
{
"devCommand": "vite --port $PORT --host ::1"
}

View File

@@ -3,6 +3,7 @@ import url from 'url';
import fs from 'fs-extra';
import { join } from 'path';
import listen from 'async-listen';
import stripAnsi from 'strip-ansi';
import { createServer } from 'http';
const {
@@ -723,12 +724,15 @@ test('[vercel dev] should support custom 404 routes', async () => {
test('[vercel dev] prints `npm install` errors', async () => {
const dir = fixture('runtime-not-installed');
const result = await exec(dir);
expect(result.stderr.includes('npm ERR! 404')).toBeTruthy();
expect(
result.stderr.includes('Failed to install `vercel dev` dependencies')
stripAnsi(result.stderr).includes(
'Error: The package `@vercel/does-not-exist` is not published on the npm registry'
)
).toBeTruthy();
expect(
result.stderr.includes('https://vercel.link/npm-install-failed-dev')
result.stderr.includes(
'https://vercel.link/builder-dependencies-install-failed'
)
).toBeTruthy();
});

View File

@@ -457,9 +457,42 @@ test(
await testPath(200, '/another', '<h1>Another</h1>');
await testPath(200, '/another.html', '<h1>Another</h1>');
await testPath(200, '/foo', '<h1>Another</h1>');
// different origin
await testPath(200, '?to=http://example.com', /Example Domain/);
})
);
test('[vercel dev] Middleware rewrites with same origin', async () => {
const directory = fixture('middleware-rewrite');
const { dev, port, readyResolver } = await testFixture(directory);
try {
dev.unref();
await readyResolver;
let response = await fetch(
`http://localhost:${port}?to=http://localhost:${port}`
);
validateResponseHeaders(response);
expect(response.status).toBe(200);
expect(await response.text()).toMatch(/<h1>Index<\/h1>/);
response = await fetch(
`http://localhost:${port}?to=http://127.0.0.1:${port}`
);
validateResponseHeaders(response);
expect(response.status).toBe(200);
expect(await response.text()).toMatch(/<h1>Index<\/h1>/);
response = await fetch(`http://localhost:${port}?to=http://[::1]:${port}`);
validateResponseHeaders(response);
expect(response.status).toBe(200);
expect(await response.text()).toMatch(/<h1>Index<\/h1>/);
} finally {
await dev.kill('SIGTERM');
}
});
test(
'[vercel dev] Middleware that rewrites with custom query params',
testFixtureStdio('middleware-rewrite-query', async (testPath: any) => {
@@ -477,6 +510,14 @@ test(
})
);
test(
'[vercel dev] Middleware that rewrites to 404s',
testFixtureStdio('middleware-rewrite-404', async (testPath: any) => {
await testPath(404, '/api/edge', /NOT_FOUND/);
await testPath(404, '/index.html', /NOT_FOUND/);
})
);
test(
'[vercel dev] Middleware that redirects',
testFixtureStdio('middleware-redirect', async (testPath: any) => {

View File

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

View File

@@ -0,0 +1 @@
<h1>Vercel</h1>

View File

@@ -0,0 +1,8 @@
{
"images": {
"sizes": [256, 384, 600, 1000],
"domains": [],
"minimumCacheTTL": 60,
"formats": ["image/webp", "image/avif"]
}
}

View File

@@ -69,7 +69,7 @@ export function useDeploymentMissingProjectSettings() {
framework: {
name: 'Other',
slug: null,
logo: 'https://raw.githubusercontent.com/vercel/vercel/main/packages/frameworks/logos/other.svg',
logo: 'https://api-frameworks.vercel.sh/framework-logos/other.svg',
description: 'No framework or an unoptimized framework.',
settings: {
installCommand: {

View File

@@ -895,6 +895,12 @@ describe('build', () => {
});
it('should apply project settings overrides from "vercel.json"', async () => {
if (process.platform === 'win32') {
// this test runs a build command with `mkdir -p` which is unsupported on Windows
console.log('Skipping test on Windows');
return;
}
const cwd = fixture('project-settings-override');
const output = join(cwd, '.vercel/output');
try {
@@ -914,6 +920,30 @@ describe('build', () => {
delete process.env.__VERCEL_BUILD_RUNNING;
}
});
it('should apply "images" configuration from `vercel.json`', async () => {
const cwd = fixture('images');
const output = join(cwd, '.vercel/output');
try {
process.chdir(cwd);
const exitCode = await build(client);
expect(exitCode).toEqual(0);
// `config.json` includes "images" from `vercel.json`
const configJson = await fs.readJSON(join(output, 'config.json'));
expect(configJson).toMatchObject({
images: {
sizes: [256, 384, 600, 1000],
domains: [],
minimumCacheTTL: 60,
formats: ['image/webp', 'image/avif'],
},
});
} finally {
process.chdir(originalCwd);
delete process.env.__VERCEL_BUILD_RUNNING;
}
});
describe('should find packages with different main/module/browser keys', function () {
let output: string;

View File

@@ -12,6 +12,8 @@ import vercelNodePkg from '@vercel/node/package.json';
jest.setTimeout(ms('30 seconds'));
const repoRoot = join(__dirname, '../../../../../..');
describe('importBuilders()', () => {
it('should import built-in Builders', async () => {
const specs = new Set(['@vercel/node', '@vercel/next']);
@@ -19,6 +21,12 @@ describe('importBuilders()', () => {
expect(builders.size).toEqual(2);
expect(builders.get('@vercel/node')?.pkg).toMatchObject(vercelNodePkg);
expect(builders.get('@vercel/next')?.pkg).toMatchObject(vercelNextPkg);
expect(builders.get('@vercel/node')?.pkgPath).toEqual(
join(repoRoot, 'packages/node/package.json')
);
expect(builders.get('@vercel/next')?.pkgPath).toEqual(
join(repoRoot, 'packages/next/package.json')
);
expect(typeof builders.get('@vercel/node')?.builder.build).toEqual(
'function'
);
@@ -27,25 +35,124 @@ describe('importBuilders()', () => {
);
});
it('should import 3rd party Builders', async () => {
it('should import built-in Builders using `@latest`', async () => {
const specs = new Set(['@vercel/node@latest', '@vercel/next@latest']);
const builders = await importBuilders(specs, process.cwd(), client.output);
expect(builders.size).toEqual(2);
expect(builders.get('@vercel/node@latest')?.pkg).toMatchObject(
vercelNodePkg
);
expect(builders.get('@vercel/next@latest')?.pkg).toMatchObject(
vercelNextPkg
);
expect(builders.get('@vercel/node@latest')?.pkgPath).toEqual(
join(repoRoot, 'packages/node/package.json')
);
expect(builders.get('@vercel/next@latest')?.pkgPath).toEqual(
join(repoRoot, 'packages/next/package.json')
);
expect(typeof builders.get('@vercel/node@latest')?.builder.build).toEqual(
'function'
);
expect(typeof builders.get('@vercel/next@latest')?.builder.build).toEqual(
'function'
);
});
it('should import built-in Builders using `@canary`', async () => {
const specs = new Set(['@vercel/node@canary', '@vercel/next@canary']);
const builders = await importBuilders(specs, process.cwd(), client.output);
expect(builders.size).toEqual(2);
expect(builders.get('@vercel/node@canary')?.pkg).toMatchObject(
vercelNodePkg
);
expect(builders.get('@vercel/next@canary')?.pkg).toMatchObject(
vercelNextPkg
);
expect(builders.get('@vercel/node@canary')?.pkgPath).toEqual(
join(repoRoot, 'packages/node/package.json')
);
expect(builders.get('@vercel/next@canary')?.pkgPath).toEqual(
join(repoRoot, 'packages/next/package.json')
);
expect(typeof builders.get('@vercel/node@canary')?.builder.build).toEqual(
'function'
);
expect(typeof builders.get('@vercel/next@canary')?.builder.build).toEqual(
'function'
);
});
it('should install and import 1st party Builders with explicit version', async () => {
if (process.platform === 'win32') {
// this test creates symlinks which require admin by default on Windows
console.log('Skipping test on Windows');
return;
}
const cwd = await getWriteableDirectory();
try {
const spec = 'vercel-deno@2.0.1';
const spec = '@vercel/node@2.0.0';
const specs = new Set([spec]);
const builders = await importBuilders(specs, cwd, client.output);
expect(builders.size).toEqual(1);
expect(builders.get(spec)?.pkg.name).toEqual('@vercel/node');
expect(builders.get(spec)?.pkg.version).toEqual('2.0.0');
expect(builders.get(spec)?.pkgPath).toEqual(
join(cwd, '.vercel/builders/node_modules/@vercel/node/package.json')
);
expect(typeof builders.get(spec)?.builder.build).toEqual('function');
await expect(client.stderr).toOutput(
'> Installing Builder: @vercel/node'
);
} finally {
await remove(cwd);
}
});
it('should install and import 3rd party Builders', async () => {
if (process.platform === 'win32') {
// this test creates symlinks which require admin by default on Windows
console.log('Skipping test on Windows');
return;
}
const cwd = await getWriteableDirectory();
try {
const spec = 'vercel-deno@2.0.1';
const tarballSpec = 'https://test2020-h5hdll5dz-tootallnate.vercel.app';
const specs = new Set([spec, tarballSpec]);
const builders = await importBuilders(specs, cwd, client.output);
expect(builders.size).toEqual(2);
expect(builders.get(spec)?.pkg.name).toEqual('vercel-deno');
expect(builders.get(spec)?.pkg.version).toEqual('2.0.1');
expect(builders.get(spec)?.pkgPath).toEqual(
join(cwd, '.vercel/builders/node_modules/vercel-deno/package.json')
);
expect(typeof builders.get(spec)?.builder.build).toEqual('function');
expect(builders.get(tarballSpec)?.pkg.name).toEqual('vercel-bash');
expect(builders.get(tarballSpec)?.pkg.version).toEqual('4.1.0');
expect(builders.get(tarballSpec)?.pkgPath).toEqual(
join(cwd, '.vercel/builders/node_modules/vercel-bash/package.json')
);
expect(typeof builders.get(tarballSpec)?.builder.build).toEqual(
'function'
);
await expect(client.stderr).toOutput(
'> Installing Builders: vercel-deno@2.0.1, https://test2020-h5hdll5dz-tootallnate.vercel.app'
);
} finally {
await remove(cwd);
}
});
it('should import legacy `@now/build-utils` Builders', async () => {
it('should install and import legacy `@now/build-utils` Builders', async () => {
if (process.platform === 'win32') {
// this test creates symlinks which require admin by default on Windows
console.log('Skipping test on Windows');
return;
}
const cwd = await getWriteableDirectory();
try {
const spec = '@frontity/now@1.2.0';
@@ -62,6 +169,31 @@ describe('importBuilders()', () => {
await remove(cwd);
}
});
it('should throw when importing a Builder that is not on npm registry', async () => {
let err: Error | undefined;
const cwd = await getWriteableDirectory();
try {
const spec = '@vercel/does-not-exist@0.0.1';
const specs = new Set([spec]);
await importBuilders(specs, cwd, client.output);
} catch (_err) {
err = _err;
} finally {
await remove(cwd);
}
if (!err) {
throw new Error('Expected `err` to be defined');
}
expect(err.message).toEqual(
'The package `@vercel/does-not-exist` is not published on the npm registry'
);
expect((err as any).link).toEqual(
'https://vercel.link/builder-dependencies-install-failed'
);
});
});
describe('resolveBuilders()', () => {

View File

@@ -1,235 +0,0 @@
import npa from 'npm-package-arg';
import {
filterPackage,
getBuildUtils,
isBundledBuilder,
} from '../../../../src/util/dev/builder-cache';
describe('filterPackage', () => {
const cliPkg = {
dependencies: {
'@vercel/build-utils': '0.0.1',
},
};
it('should filter package that does not appear in CLI package.json', () => {
const result = filterPackage('@vercel/other', {}, cliPkg);
expect(result).toEqual(true);
});
it('should not filter "latest", cached canary', () => {
const buildersPkg = {
dependencies: {
'@vercel/build-utils': '0.0.1-canary.0',
},
};
const result = filterPackage('@vercel/build-utils', buildersPkg, cliPkg);
expect(result).toEqual(false);
});
it('should filter install "canary", cached stable', () => {
const buildersPkg = {
dependencies: {
'@vercel/build-utils': '0.0.1',
},
};
const result = filterPackage(
'@vercel/build-utils@canary',
buildersPkg,
cliPkg
);
expect(result).toEqual(false);
});
it('should filter install "latest", cached stable', () => {
const buildersPkg = {
dependencies: {
'@vercel/build-utils': '0.0.1',
},
};
const result = filterPackage('@vercel/build-utils', buildersPkg, cliPkg);
expect(result).toEqual(false);
});
it('should filter install "canary", cached canary', () => {
const buildersPkg = {
dependencies: {
'@vercel/build-utils': '0.0.1-canary.0',
},
};
const result = filterPackage(
'@vercel/build-utils@canary',
buildersPkg,
cliPkg
);
expect(result).toEqual(false);
});
it('should filter install URL, cached stable', () => {
const buildersPkg = {
dependencies: {
'@vercel/build-utils': '0.0.1',
},
};
const result = filterPackage('https://tarball.now.sh', buildersPkg, cliPkg);
expect(result).toEqual(true);
});
it('should filter install URL, cached canary', () => {
const buildersPkg = {
dependencies: {
'@vercel/build-utils': '0.0.1-canary.0',
},
};
const result = filterPackage('https://tarball.now.sh', buildersPkg, cliPkg);
expect(result).toEqual(true);
});
it('should filter install "latest", cached URL - canary', () => {
const buildersPkg = {
dependencies: {
'@vercel/build-utils': 'https://tarball.now.sh',
},
};
const result = filterPackage('@vercel/build-utils', buildersPkg, cliPkg);
expect(result).toEqual(false);
});
it('should filter install not bundled version, cached same version', () => {
const buildersPkg = {
dependencies: {
'not-bundled-package': '0.0.1',
},
};
const result = filterPackage(
'not-bundled-package@0.0.1',
buildersPkg,
cliPkg
);
expect(result).toEqual(false);
});
it('should filter install not bundled version, cached different version', () => {
const buildersPkg = {
dependencies: {
'not-bundled-package': '0.0.9',
},
};
const result = filterPackage(
'not-bundled-package@0.0.1',
buildersPkg,
cliPkg
);
expect(result).toEqual(true);
});
it('should filter install not bundled stable, cached version', () => {
const buildersPkg = {
dependencies: {
'not-bundled-package': '0.0.1',
},
};
const result = filterPackage('not-bundled-package', buildersPkg, cliPkg);
expect(result).toEqual(true);
});
it('should filter install not bundled tagged, cached tagged', () => {
const buildersPkg = {
dependencies: {
'not-bundled-package': '16.9.0-alpha.0',
},
};
const result = filterPackage(
'not-bundled-package@alpha',
buildersPkg,
cliPkg
);
expect(result).toEqual(true);
});
});
describe('getBuildUtils', () => {
const tests: [string[], string][] = [
[['@vercel/static', '@vercel/node@canary'], 'canary'],
[['@vercel/static', '@vercel/node@0.7.4-canary.0'], 'canary'],
[['@vercel/static', '@vercel/node@0.8.0'], 'latest'],
[['@vercel/static', '@vercel/node'], 'latest'],
[['@vercel/static'], 'latest'],
[['@vercel/md@canary'], 'canary'],
[['custom-builder'], 'latest'],
[['custom-builder@canary'], 'canary'],
[['canary-bird'], 'latest'],
[['canary-bird@4.0.0'], 'latest'],
[['canary-bird@canary'], 'canary'],
[['@canary/bird'], 'latest'],
[['@canary/bird@0.1.0'], 'latest'],
[['@canary/bird@canary'], 'canary'],
[['https://example.com'], 'latest'],
[[''], 'latest'],
];
for (const [input, expected] of tests) {
it(`should install "${expected}" with input ${JSON.stringify(
input
)}`, () => {
const result = getBuildUtils(input);
expect(result).toEqual(`@vercel/build-utils@${expected}`);
});
}
});
describe('isBundledBuilder', () => {
const cliPkg = {
dependencies: {
'@vercel/node': '0.0.1',
},
};
it('should not detect when dependency does not appear in CLI package.json', () => {
const parsed = npa('@vercel/node');
const result = isBundledBuilder(parsed, {});
expect(result).toEqual(false);
});
it('should detect "canary" tagged releases', () => {
const parsed = npa('@vercel/node@canary');
const result = isBundledBuilder(parsed, cliPkg);
expect(result).toEqual(true);
});
it('should detect "canary" versioned releases', () => {
const parsed = npa('@vercel/node@1.6.1-canary.0');
const result = isBundledBuilder(parsed, cliPkg);
expect(result).toEqual(true);
});
it('should detect latest releases', () => {
const parsed = npa('@vercel/node');
const result = isBundledBuilder(parsed, cliPkg);
expect(result).toEqual(true);
});
it('should detect "latest" tagged releases', () => {
const parsed = npa('@vercel/node@latest');
const result = isBundledBuilder(parsed, cliPkg);
expect(result).toEqual(true);
});
it('should detect versioned releases', () => {
const parsed = npa('@vercel/node@1.6.1');
const result = isBundledBuilder(parsed, cliPkg);
expect(result).toEqual(true);
});
it('should NOT detect URL releases', () => {
const parsed = npa('https://example.com');
const result = isBundledBuilder(parsed, cliPkg);
expect(result).toEqual(false);
});
it('should NOT detect git url releases', () => {
const parsed = npa('git://example.com/repo.git');
const result = isBundledBuilder(parsed, cliPkg);
expect(result).toEqual(false);
});
});

View File

@@ -1,4 +1,7 @@
import parseListen from '../../../../src/util/dev/parse-listen';
import {
parseListen,
replaceLocalhost,
} from '../../../../src/util/dev/parse-listen';
const IS_WINDOWS = process.platform === 'win32';
@@ -69,3 +72,15 @@ describe('parseListen', () => {
expect(err.message).toEqual('Unknown `--listen` scheme (protocol): bad:');
});
});
describe('replaceLocalhost', () => {
test.each([
{ input: 'http://192.168.0.1:1234', output: 'http://192.168.0.1:1234' },
{ input: 'http://127.0.0.1:4000', output: 'http://127.0.0.1:4000' },
{ input: 'http://[::1]:3001', output: 'http://[::1]:3001' },
{ input: 'http://0.0.0.0:3000', output: 'http://localhost:3000' },
{ input: 'http://[::]:3002', output: 'http://localhost:3002' },
])('"$input" → "$output"', ({ input, output }) => {
expect(replaceLocalhost(input)).toEqual(output);
});
});

View File

@@ -1,6 +1,6 @@
{
"name": "@vercel/client",
"version": "12.2.4",
"version": "12.2.6",
"main": "dist/index.js",
"typings": "dist/index.d.ts",
"homepage": "https://vercel.com",
@@ -43,7 +43,7 @@
]
},
"dependencies": {
"@vercel/build-utils": "5.4.2",
"@vercel/build-utils": "5.4.4",
"@vercel/routing-utils": "2.0.2",
"@zeit/fetch": "5.2.0",
"async-retry": "1.2.3",

View File

@@ -1,6 +1,7 @@
import type {
Builder,
BuilderFunctions,
Images,
ProjectSettings,
} from '@vercel/build-utils';
import type { Header, Route, Redirect, Rewrite } from '@vercel/routing-utils';
@@ -152,6 +153,7 @@ export interface VercelConfig {
installCommand?: string | null;
framework?: string | null;
outputDirectory?: string | null;
images?: Images;
}
export interface GitMetadata {

View File

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

View File

@@ -21,7 +21,7 @@ export const frameworks = [
name: 'Blitz.js',
slug: 'blitzjs',
demo: 'https://blitz-template.vercel.app',
logo: 'https://raw.githubusercontent.com/vercel/vercel/main/packages/frameworks/logos/blitz.svg',
logo: 'https://api-frameworks.vercel.sh/framework-logos/blitz.svg',
tagline: 'Blitz.js: The Fullstack React Framework',
description:
'A brand new Blitz.js app - the result of running `npx blitz new`.',
@@ -58,9 +58,9 @@ export const frameworks = [
name: 'Next.js',
slug: 'nextjs',
demo: 'https://nextjs-template.vercel.app',
logo: 'https://raw.githubusercontent.com/vercel/vercel/main/packages/frameworks/logos/next.svg',
logo: 'https://api-frameworks.vercel.sh/framework-logos/next.svg',
darkModeLogo:
'https://raw.githubusercontent.com/vercel/vercel/main/packages/frameworks/logos/next-dark.svg',
'https://api-frameworks.vercel.sh/framework-logos/next-dark.svg',
screenshot:
'https://assets.vercel.com/image/upload/v1647366075/front/import/nextjs.png',
tagline:
@@ -108,7 +108,7 @@ export const frameworks = [
name: 'Gatsby.js',
slug: 'gatsby',
demo: 'https://gatsby.vercel.app',
logo: 'https://raw.githubusercontent.com/vercel/vercel/main/packages/frameworks/logos/gatsby.svg',
logo: 'https://api-frameworks.vercel.sh/framework-logos/gatsby.svg',
tagline:
'Gatsby helps developers build blazing fast websites and apps with React.',
description: 'A Gatsby starter app with an API Route.',
@@ -195,7 +195,7 @@ export const frameworks = [
name: 'Remix',
slug: 'remix',
demo: 'https://remix-run-template.vercel.app',
logo: 'https://raw.githubusercontent.com/vercel/vercel/main/packages/frameworks/logos/remix-no-shadow.svg',
logo: 'https://api-frameworks.vercel.sh/framework-logos/remix-no-shadow.svg',
tagline: 'Build Better Websites',
description: 'A new Remix app — the result of running `npx create-remix`.',
website: 'https://remix.run',
@@ -232,9 +232,9 @@ export const frameworks = [
name: 'Astro',
slug: 'astro',
demo: 'https://astro-template.vercel.app',
logo: 'https://raw.githubusercontent.com/vercel/vercel/main/packages/frameworks/logos/astro.svg',
logo: 'https://api-frameworks.vercel.sh/framework-logos/astro.svg',
darkModeLogo:
'https://raw.githubusercontent.com/vercel/vercel/main/packages/frameworks/logos/astro-dark.svg',
'https://api-frameworks.vercel.sh/framework-logos/astro-dark.svg',
tagline:
'Astro is a new kind of static site builder for the modern web. Powerful developer experience meets lightweight output.',
description: 'An Astro site, using the basics starter kit.',
@@ -286,7 +286,7 @@ export const frameworks = [
name: 'Hexo',
slug: 'hexo',
demo: 'https://hexo-template.vercel.app',
logo: 'https://raw.githubusercontent.com/vercel/vercel/main/packages/frameworks/logos/hexo.svg',
logo: 'https://api-frameworks.vercel.sh/framework-logos/hexo.svg',
tagline:
'Hexo is a fast, simple & powerful blog framework powered by Node.js.',
description: 'A Hexo site, created with the Hexo CLI.',
@@ -323,7 +323,7 @@ export const frameworks = [
name: 'Eleventy',
slug: 'eleventy',
demo: 'https://eleventy-template.vercel.app',
logo: 'https://raw.githubusercontent.com/vercel/vercel/main/packages/frameworks/logos/eleventy.svg',
logo: 'https://api-frameworks.vercel.sh/framework-logos/eleventy.svg',
tagline:
'11ty is a simpler static site generator written in JavaScript, created to be an alternative to Jekyll.',
description: 'An Eleventy site, created with npm init.',
@@ -361,7 +361,7 @@ export const frameworks = [
name: 'Docusaurus 2',
slug: 'docusaurus-2',
demo: 'https://docusaurus-2-template.vercel.app',
logo: 'https://raw.githubusercontent.com/vercel/vercel/main/packages/frameworks/logos/docusaurus.svg',
logo: 'https://api-frameworks.vercel.sh/framework-logos/docusaurus.svg',
tagline:
'Docusaurus makes it easy to maintain Open Source documentation websites.',
description:
@@ -449,7 +449,7 @@ export const frameworks = [
name: 'Docusaurus 1',
slug: 'docusaurus',
demo: 'https://docusaurus-template.vercel.app',
logo: 'https://raw.githubusercontent.com/vercel/vercel/main/packages/frameworks/logos/docusaurus.svg',
logo: 'https://api-frameworks.vercel.sh/framework-logos/docusaurus.svg',
tagline:
'Docusaurus makes it easy to maintain Open Source documentation websites.',
description:
@@ -501,7 +501,7 @@ export const frameworks = [
name: 'Preact',
slug: 'preact',
demo: 'https://preact-template.vercel.app',
logo: 'https://raw.githubusercontent.com/vercel/vercel/main/packages/frameworks/logos/preact.svg',
logo: 'https://api-frameworks.vercel.sh/framework-logos/preact.svg',
tagline:
'Preact is a fast 3kB alternative to React with the same modern API.',
description: 'A Preact app, created with the Preact CLI.',
@@ -547,7 +547,7 @@ export const frameworks = [
name: 'SolidStart',
slug: 'solidstart',
demo: 'https://solid-start-template.vercel.app',
logo: 'https://raw.githubusercontent.com/vercel/vercel/main/packages/frameworks/logos/solid.svg',
logo: 'https://api-frameworks.vercel.sh/framework-logos/solid.svg',
tagline: 'Simple and performant reactivity for building user interfaces.',
description: 'A Solid app, created with SolidStart.',
website: 'https://solidjs.com',
@@ -587,7 +587,7 @@ export const frameworks = [
name: 'Dojo',
slug: 'dojo',
demo: 'https://dojo-template.vercel.app',
logo: 'https://raw.githubusercontent.com/vercel/vercel/main/packages/frameworks/logos/dojo.png',
logo: 'https://api-frameworks.vercel.sh/framework-logos/dojo.png',
tagline: 'Dojo is a modern progressive, TypeScript first framework.',
description:
"A Dojo app, created with the Dojo CLI's cli-create-app command.",
@@ -649,7 +649,7 @@ export const frameworks = [
name: 'Ember.js',
slug: 'ember',
demo: 'https://ember-template.vercel.app',
logo: 'https://raw.githubusercontent.com/vercel/vercel/main/packages/frameworks/logos/ember.svg',
logo: 'https://api-frameworks.vercel.sh/framework-logos/ember.svg',
tagline:
'Ember.js helps webapp developers be more productive out of the box.',
description: 'An Ember app, created with the Ember CLI.',
@@ -695,7 +695,7 @@ export const frameworks = [
name: 'Vue.js',
slug: 'vue',
demo: 'https://vue-template.vercel.app',
logo: 'https://raw.githubusercontent.com/vercel/vercel/main/packages/frameworks/logos/vue.svg',
logo: 'https://api-frameworks.vercel.sh/framework-logos/vue.svg',
tagline:
'Vue.js is a versatile JavaScript framework that is as approachable as it is performant.',
description: 'A Vue.js app, created with the Vue CLI.',
@@ -752,7 +752,7 @@ export const frameworks = [
name: 'Scully',
slug: 'scully',
demo: 'https://scully-template.vercel.app',
logo: 'https://raw.githubusercontent.com/vercel/vercel/main/packages/frameworks/logos/scullyio-logo.png',
logo: 'https://api-frameworks.vercel.sh/framework-logos/scullyio-logo.png',
tagline: 'Scully is a static site generator for Angular.',
description: 'The Static Site Generator for Angular apps.',
website: 'https://github.com/scullyio/scully',
@@ -788,7 +788,7 @@ export const frameworks = [
name: 'Ionic Angular',
slug: 'ionic-angular',
demo: 'https://ionic-angular-template.vercel.app',
logo: 'https://raw.githubusercontent.com/vercel/vercel/main/packages/frameworks/logos/ionic.svg',
logo: 'https://api-frameworks.vercel.sh/framework-logos/ionic.svg',
tagline:
'Ionic Angular allows you to build mobile PWAs with Angular and the Ionic Framework.',
description: 'An Ionic Angular site, created with the Ionic CLI.',
@@ -833,7 +833,7 @@ export const frameworks = [
name: 'Angular',
slug: 'angular',
demo: 'https://angular-template.vercel.app',
logo: 'https://raw.githubusercontent.com/vercel/vercel/main/packages/frameworks/logos/angular.svg',
logo: 'https://api-frameworks.vercel.sh/framework-logos/angular.svg',
tagline:
'Angular is a TypeScript-based cross-platform framework from Google.',
description: 'An Angular app, created with the Angular CLI.',
@@ -893,7 +893,7 @@ export const frameworks = [
name: 'Polymer',
slug: 'polymer',
demo: 'https://polymer-template.vercel.app',
logo: 'https://raw.githubusercontent.com/vercel/vercel/main/packages/frameworks/logos/polymer.svg',
logo: 'https://api-frameworks.vercel.sh/framework-logos/polymer.svg',
tagline:
'Polymer is an open-source webapps library from Google, for building using Web Components.',
description: 'A Polymer app, created with the Polymer CLI.',
@@ -950,7 +950,7 @@ export const frameworks = [
name: 'Svelte',
slug: 'svelte',
demo: 'https://svelte.vercel.app',
logo: 'https://raw.githubusercontent.com/vercel/vercel/main/packages/frameworks/logos/svelte.svg',
logo: 'https://api-frameworks.vercel.sh/framework-logos/svelte.svg',
tagline:
'Svelte lets you write high performance reactive apps with significantly less boilerplate.',
description: 'A basic Svelte app using the default template.',
@@ -1001,7 +1001,7 @@ export const frameworks = [
name: 'SvelteKit',
slug: 'sveltekit',
demo: 'https://sveltekit-template.vercel.app',
logo: 'https://raw.githubusercontent.com/vercel/vercel/main/packages/frameworks/logos/svelte.svg',
logo: 'https://api-frameworks.vercel.sh/framework-logos/svelte.svg',
screenshot:
'https://assets.vercel.com/image/upload/v1647366075/front/import/sveltekit.png',
tagline:
@@ -1040,7 +1040,7 @@ export const frameworks = [
name: 'Ionic React',
slug: 'ionic-react',
demo: 'https://ionic-react-template.vercel.app',
logo: 'https://raw.githubusercontent.com/vercel/vercel/main/packages/frameworks/logos/ionic.svg',
logo: 'https://api-frameworks.vercel.sh/framework-logos/ionic.svg',
tagline:
'Ionic React allows you to build mobile PWAs with React and the Ionic Framework.',
description: 'An Ionic React site, created with the Ionic CLI.',
@@ -1101,7 +1101,7 @@ export const frameworks = [
name: 'Create React App',
slug: 'create-react-app',
demo: 'https://create-react-template.vercel.app',
logo: 'https://raw.githubusercontent.com/vercel/vercel/main/packages/frameworks/logos/react.svg',
logo: 'https://api-frameworks.vercel.sh/framework-logos/react.svg',
tagline: 'Create React App allows you to get going with React in no time.',
description: 'A client-side React app created with create-react-app.',
website: 'https://create-react-app.dev',
@@ -1168,7 +1168,7 @@ export const frameworks = [
name: 'Gridsome',
slug: 'gridsome',
demo: 'https://gridsome-template.vercel.app',
logo: 'https://raw.githubusercontent.com/vercel/vercel/main/packages/frameworks/logos/gridsome.svg',
logo: 'https://api-frameworks.vercel.sh/framework-logos/gridsome.svg',
tagline:
'Gridsome is a Vue.js-powered framework for building websites & apps that are fast by default.',
description: 'A Gridsome app, created with the Gridsome CLI.',
@@ -1205,7 +1205,7 @@ export const frameworks = [
name: 'UmiJS',
slug: 'umijs',
demo: 'https://umijs-template.vercel.app',
logo: 'https://raw.githubusercontent.com/vercel/vercel/main/packages/frameworks/logos/umi.svg',
logo: 'https://api-frameworks.vercel.sh/framework-logos/umi.svg',
tagline:
'UmiJS is an extensible enterprise-level React application framework.',
description: 'An UmiJS app, created using the Umi CLI.',
@@ -1251,7 +1251,7 @@ export const frameworks = [
name: 'Sapper',
slug: 'sapper',
demo: 'https://sapper-template.vercel.app',
logo: 'https://raw.githubusercontent.com/vercel/vercel/main/packages/frameworks/logos/svelte.svg',
logo: 'https://api-frameworks.vercel.sh/framework-logos/svelte.svg',
tagline:
'Sapper is a framework for building high-performance universal web apps with Svelte.',
description: 'A Sapper app, using the Sapper template.',
@@ -1288,7 +1288,7 @@ export const frameworks = [
name: 'Saber',
slug: 'saber',
demo: 'https://saber-template.vercel.app',
logo: 'https://raw.githubusercontent.com/vercel/vercel/main/packages/frameworks/logos/saber.svg',
logo: 'https://api-frameworks.vercel.sh/framework-logos/saber.svg',
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.',
@@ -1339,7 +1339,7 @@ export const frameworks = [
name: 'Stencil',
slug: 'stencil',
demo: 'https://stencil.vercel.app',
logo: 'https://raw.githubusercontent.com/vercel/vercel/main/packages/frameworks/logos/stencil.svg',
logo: 'https://api-frameworks.vercel.sh/framework-logos/stencil.svg',
tagline:
'Stencil is a powerful toolchain for building Progressive Web Apps and Design Systems.',
description: 'A Stencil site, created with the Stencil CLI.',
@@ -1400,7 +1400,7 @@ export const frameworks = [
name: 'Nuxt.js',
slug: 'nuxtjs',
demo: 'https://nuxtjs-template.vercel.app',
logo: 'https://raw.githubusercontent.com/vercel/vercel/main/packages/frameworks/logos/nuxt.svg',
logo: 'https://api-frameworks.vercel.sh/framework-logos/nuxt.svg',
screenshot:
'https://assets.vercel.com/image/upload/v1647366075/front/import/nuxtjs.png',
tagline:
@@ -1460,7 +1460,7 @@ export const frameworks = [
name: 'RedwoodJS',
slug: 'redwoodjs',
demo: 'https://redwood-template.vercel.app',
logo: 'https://raw.githubusercontent.com/vercel/vercel/main/packages/frameworks/logos/redwoodjs.svg',
logo: 'https://api-frameworks.vercel.sh/framework-logos/redwoodjs.svg',
tagline: 'RedwoodJS is a full-stack framework for the Jamstack.',
description: 'A RedwoodJS app, bootstraped with create-redwood-app.',
website: 'https://redwoodjs.com',
@@ -1497,7 +1497,7 @@ export const frameworks = [
name: 'Hugo',
slug: 'hugo',
demo: 'https://hugo-template.vercel.app',
logo: 'https://raw.githubusercontent.com/vercel/vercel/main/packages/frameworks/logos/hugo.svg',
logo: 'https://api-frameworks.vercel.sh/framework-logos/hugo.svg',
tagline:
'Hugo is the worlds fastest framework for building websites, written in Go.',
description: 'A Hugo site, created with the Hugo CLI.',
@@ -1550,7 +1550,7 @@ export const frameworks = [
name: 'Jekyll',
slug: 'jekyll',
demo: 'https://jekyll-template.vercel.app',
logo: 'https://raw.githubusercontent.com/vercel/vercel/main/packages/frameworks/logos/jekyll.svg',
logo: 'https://api-frameworks.vercel.sh/framework-logos/jekyll.svg',
tagline:
'Jekyll makes it super easy to transform your plain text into static websites and blogs.',
description: 'A Jekyll site, created with the Jekyll CLI.',
@@ -1591,7 +1591,7 @@ export const frameworks = [
name: 'Brunch',
slug: 'brunch',
demo: 'https://brunch-template.vercel.app',
logo: 'https://raw.githubusercontent.com/vercel/vercel/main/packages/frameworks/logos/brunch.svg',
logo: 'https://api-frameworks.vercel.sh/framework-logos/brunch.svg',
tagline:
'Brunch is a fast and simple webapp build tool with seamless incremental compilation for rapid development.',
description: 'A Brunch app, created with the Brunch CLI.',
@@ -1625,7 +1625,7 @@ export const frameworks = [
name: 'Middleman',
slug: 'middleman',
demo: 'https://middleman-template.vercel.app',
logo: 'https://raw.githubusercontent.com/vercel/vercel/main/packages/frameworks/logos/middleman.svg',
logo: 'https://api-frameworks.vercel.sh/framework-logos/middleman.svg',
tagline:
'Middleman is a static site generator that uses all the shortcuts and tools in modern web development.',
description: 'A Middleman app, created with the Middleman CLI.',
@@ -1660,7 +1660,7 @@ export const frameworks = [
name: 'Zola',
slug: 'zola',
demo: 'https://zola-template.vercel.app',
logo: 'https://raw.githubusercontent.com/vercel/vercel/main/packages/frameworks/logos/zola.png',
logo: 'https://api-frameworks.vercel.sh/framework-logos/zola.png',
tagline: 'Everything you need to make a static site engine in one binary.',
description: 'A Zola app, created with the "Getting Started" tutorial.',
website: 'https://www.getzola.org',
@@ -1694,7 +1694,7 @@ export const frameworks = [
name: 'Vite',
slug: 'vite',
demo: 'https://vite-vue-template.vercel.app',
logo: 'https://raw.githubusercontent.com/vercel/vercel/main/packages/frameworks/logos/vite.svg',
logo: 'https://api-frameworks.vercel.sh/framework-logos/vite.svg',
tagline:
'Vite is a new breed of frontend build tool that significantly improves the frontend development experience.',
description: 'A Vue.js app, created with Vite.',
@@ -1732,7 +1732,7 @@ export const frameworks = [
name: 'Parcel',
slug: 'parcel',
demo: 'https://parcel-template.vercel.app',
logo: 'https://raw.githubusercontent.com/vercel/vercel/main/packages/frameworks/logos/parcel.png',
logo: 'https://api-frameworks.vercel.sh/framework-logos/parcel.png',
tagline:
'Parcel is a zero configuration build tool for the web that scales to projects of any size and complexity.',
description: 'A vanilla web app built with Parcel.',
@@ -1779,16 +1779,22 @@ export const frameworks = [
name: 'Sanity',
slug: 'sanity',
demo: 'https://sanity-studio-template.vercel.app',
logo: 'https://raw.githubusercontent.com/vercel/vercel/main/packages/frameworks/logos/sanity.svg',
logo: 'https://api-frameworks.vercel.sh/framework-logos/sanity.svg',
tagline: 'The structured content platform.',
description: 'A Sanity Studio',
website: 'https://www.sanity.io',
envPrefix: 'SANITY_STUDIO_',
detectors: {
every: [
some: [
{
path: 'sanity.json',
},
{
path: 'sanity.config.js',
},
{
path: 'sanity.config.ts',
},
],
},
settings: {
@@ -1822,7 +1828,7 @@ export const frameworks = [
name: 'Hydrogen',
slug: 'hydrogen',
demo: 'https://hydrogen-template.vercel.app',
logo: 'https://raw.githubusercontent.com/vercel/vercel/main/packages/frameworks/logos/hydrogen.svg',
logo: 'https://api-frameworks.vercel.sh/framework-logos/hydrogen.svg',
tagline: 'React framework for headless commerce',
description: 'React framework for headless commerce',
website: 'https://hydrogen.shopify.dev',
@@ -1856,7 +1862,7 @@ export const frameworks = [
{
name: 'Other',
slug: null,
logo: 'https://raw.githubusercontent.com/vercel/vercel/main/packages/frameworks/logos/other.svg',
logo: 'https://api-frameworks.vercel.sh/framework-logos/other.svg',
description: 'No framework or an unoptimized framework.',
settings: {
installCommand: {

View File

@@ -53,12 +53,12 @@ export interface Framework {
slug: string | null;
/**
* A URL to the logo of the framework
* @example "https://raw.githubusercontent.com/vercel/vercel/main/packages/frameworks/logos/next.svg"
* @example "https://api-frameworks.vercel.sh/framework-logos/next.svg"
*/
logo: string;
/**
* An additional URL to the logo of the framework optimized for dark mode
* @example "https://raw.githubusercontent.com/vercel/vercel/main/packages/frameworks/logos/next-dark.svg"
* @example "https://api-frameworks.vercel.sh/framework-logos/next-dark.svg"
*/
darkModeLogo?: string;
/**

View File

@@ -10,6 +10,8 @@ import frameworkList from '../src/frameworks';
// bump timeout for Windows as network can be slower
jest.setTimeout(15 * 1000);
const logoPrefix = 'https://api-frameworks.vercel.sh/framework-logos/';
const SchemaFrameworkDetectionItem = {
type: 'array',
items: [
@@ -172,14 +174,33 @@ describe('frameworks', () => {
expect(result).toBe(true);
});
it('ensure logo', async () => {
it('ensure logo starts with url prefix', async () => {
const invalid = frameworkList
.map(f => f.logo)
.filter(logo => {
return logo && !logo.startsWith(logoPrefix);
});
expect(invalid).toEqual([]);
});
it('ensure darkModeLogo starts with url prefix', async () => {
const invalid = frameworkList
.map(f => f.darkModeLogo)
.filter(darkModeLogo => {
return darkModeLogo && !darkModeLogo.startsWith(logoPrefix);
});
expect(invalid).toEqual([]);
});
it('ensure logo file exists in ./packages/frameworks/logos/', async () => {
const missing = frameworkList
.map(f => f.logo)
.filter(url => {
const prefix =
'https://raw.githubusercontent.com/vercel/vercel/main/packages/frameworks/logos/';
const name = url.replace(prefix, '');
return existsSync(join(__dirname, '..', 'logos', name)) === false;
.filter(logo => {
const filename = logo.slice(logoPrefix.length);
const filepath = join(__dirname, '..', 'logos', filename);
return existsSync(filepath) === false;
});
expect(missing).toEqual([]);

View File

@@ -1,6 +1,6 @@
{
"name": "@vercel/fs-detectors",
"version": "3.0.0",
"version": "3.3.0",
"description": "Vercel filesystem detectors",
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
@@ -19,7 +19,7 @@
"test-unit": "yarn test"
},
"dependencies": {
"@vercel/frameworks": "1.1.4",
"@vercel/frameworks": "1.1.6",
"@vercel/routing-utils": "2.0.2",
"glob": "8.0.3",
"js-yaml": "4.1.0",

View File

@@ -125,4 +125,15 @@ export abstract class DetectorFilesystem {
public chdir = (name: string): DetectorFilesystem => {
return this._chdir(name);
};
/**
* Writes a file to the filesystem cache.
* @param name the name of the file to write
* @param content The content of the file
*/
public writeFile = async (name: string, content: string): Promise<void> => {
this.readFileCache.set(name, Promise.resolve(Buffer.from(content)));
this.fileCache.set(name, Promise.resolve(true));
this.pathCache.set(name, Promise.resolve(true));
};
}

View File

@@ -31,6 +31,28 @@ export const monorepoManagers: Array<
],
},
},
{
name: 'Nx',
slug: 'nx',
detectors: {
every: [
{
path: 'nx.json',
},
],
},
},
{
name: 'Rush',
slug: 'rush',
detectors: {
every: [
{
path: 'rush.json',
},
],
},
},
];
export default monorepoManagers;

View File

@@ -33,6 +33,9 @@ export async function getWorkspacePackagePaths({
case 'pnpm':
results = await getPnpmWorkspacePackagePaths({ fs: workspaceFs });
break;
case 'nx':
results = await getNxWorkspacePackagePaths({ fs: workspaceFs });
break;
default:
throw new Error(`Unknown workspace implementation: ${type}`);
}
@@ -103,6 +106,17 @@ async function getPackageJsonWorkspacePackagePaths({
return getPackagePaths(packages, fs);
}
async function getNxWorkspacePackagePaths({
fs,
}: GetPackagePathOptions): Promise<string[]> {
const nxWorkspaceJsonAsBuffer = await fs.readFile('workspace.json');
const { projects } = JSON.parse(nxWorkspaceJsonAsBuffer.toString());
const packages: string[] = Object.values(projects);
return getPackagePaths(packages, fs);
}
async function getPnpmWorkspacePackagePaths({
fs,
}: GetPackagePathOptions): Promise<string[]> {

View File

@@ -12,7 +12,7 @@ export interface GetWorkspaceOptions {
cwd?: string;
}
export type WorkspaceType = 'yarn' | 'pnpm' | 'npm';
export type WorkspaceType = 'yarn' | 'pnpm' | 'npm' | 'nx';
export type Workspace = {
type: WorkspaceType;

View File

@@ -59,6 +59,18 @@ export const workspaceManagers: Array<
],
},
},
{
name: 'nx',
slug: 'nx',
detectors: {
every: [
{
path: 'workspace.json',
matchContent: '"projects":\\s*{[^}]',
},
],
},
},
{
name: 'default',
slug: 'yarn',

View File

@@ -0,0 +1,119 @@
{
"projectType": "application",
"generators": {
"@nrwl/workspace:component": {
"style": "scss"
}
},
"sourceRoot": "apps/products/src",
"prefix": "nx-example",
"targets": {
"build": {
"executor": "@angular-devkit/build-angular:browser",
"options": {
"aot": true,
"outputPath": "dist/apps/products",
"index": "apps/products/src/index.html",
"main": "apps/products/src/main.ts",
"polyfills": "apps/products/src/polyfills.ts",
"tsConfig": "apps/products/tsconfig.app.json",
"assets": [
"apps/products/src/_redirects",
{
"input": "libs/shared/assets/src/assets",
"glob": "**/*",
"output": "assets"
},
{
"input": "libs/shared/assets/src",
"glob": "favicon.ico",
"output": "."
}
],
"styles": [
"libs/shared/styles/src/index.scss",
"libs/shared/header/index.scss",
"node_modules/normalize.css/normalize.css"
],
"scripts": []
},
"configurations": {
"production": {
"fileReplacements": [
{
"replace": "apps/products/src/environments/environment.ts",
"with": "apps/products/src/environments/environment.prod.ts"
}
],
"optimization": true,
"outputHashing": "all",
"sourceMap": false,
"namedChunks": false,
"aot": true,
"extractLicenses": true,
"vendorChunk": false,
"buildOptimizer": true,
"budgets": [
{
"type": "initial",
"maximumWarning": "2mb",
"maximumError": "5mb"
},
{
"type": "anyComponentStyle",
"maximumWarning": "6kb"
}
]
}
},
"outputs": ["{options.outputPath}"]
},
"serve": {
"executor": "@angular-devkit/build-angular:dev-server",
"options": {
"browserTarget": "products:build"
},
"configurations": {
"production": {
"browserTarget": "products:build:production"
}
}
},
"extract-i18n": {
"executor": "@angular-devkit/build-angular:extract-i18n",
"options": {
"browserTarget": "products:build"
}
},
"lint": {
"executor": "@nrwl/linter:eslint",
"options": {
"lintFilePatterns": [
"apps/products/src/**/*.ts",
"apps/products/src/**/*.html"
]
},
"outputs": ["{options.outputFile}"]
},
"test": {
"executor": "@nrwl/jest:jest",
"options": {
"jestConfig": "apps/products/jest.config.ts",
"passWithNoTests": true
},
"outputs": ["coverage/apps/products"]
},
"deploy": {
"executor": "@nrwl/workspace:run-commands",
"options": {
"commands": [
{
"command": "npx ts-node --project tools/tsconfig.tools.json tools/scripts/deploy --siteName nrwl-nx-examples-products --outputPath dist/apps/products"
}
]
}
}
},
"tags": ["type:app", "scope:products"],
"implicitDependencies": ["shared-assets", "shared-styles"]
}

View File

@@ -0,0 +1,51 @@
{
"implicitDependencies": {
"package.json": {
"dependencies": "*",
"devDependencies": "*"
},
".eslintrc.json": "*"
},
"affected": {
"defaultBase": "master"
},
"npmScope": "nx-example",
"tasksRunnerOptions": {
"default": {
"runner": "@nrwl/nx-cloud",
"options": {
"accessToken": "YmM1MGMwMTUtNzAyYi00ZjM4LWExNGUtZDM2ZjYyNzNiODAxfHJlYWQ=",
"cacheableOperations": ["build", "lint", "test"],
"parallel": 1
}
}
},
"cli": {
"warnings": {
"versionMismatch": false
},
"packageManager": "yarn",
"analytics": false
},
"generators": {
"@nrwl/angular:application": {
"unitTestRunner": "jest",
"e2eTestRunner": "cypress"
},
"@nrwl/angular:library": {
"unitTestRunner": "jest"
},
"@nrwl/angular": {
"convert-tslint-to-eslint": {
"removeTSLintIfNoMoreTSLintTargets": true
}
}
},
"defaultProject": "app",
"$schema": "./node_modules/nx/schemas/nx-schema.json",
"targetDefaults": {
"build": {
"dependsOn": ["^build"]
}
}
}

View File

@@ -0,0 +1,27 @@
{
"$schema": "https://developer.microsoft.com/json-schemas/rush/v5/rush.schema.json",
"rushVersion": "5.76.1",
"pnpmVersion": "6.7.1",
"pnpmOptions": {
"useWorkspaces": true
},
"nodeSupportedVersionRange": ">=12.13.0 <13.0.0 || >=14.15.0 <15.0.0 || >=16.13.0 <17.0.0",
"gitPolicy": {},
"repository": {},
"eventHooks": {
"preRushInstall": [],
"postRushInstall": [],
"preRushBuild": [],
"postRushBuild": []
},
"variants": [],
"projects": []
}

View File

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

View File

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

View File

@@ -0,0 +1,7 @@
{
"version": 2,
"projects": {
"myblog": "apps/app-one",
"svelte-app": "apps/app-two"
}
}

View File

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

View File

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

View File

@@ -0,0 +1,782 @@
{
"name": "42-npm-workspace-with-nx",
"version": "1.0.0",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"version": "1.0.0",
"workspaces": [
"apps/app-one",
"apps/app-two"
]
},
"apps/app-one": {
"version": "1.0.0",
"license": "ISC",
"dependencies": {
"debug": "^4.3.2"
}
},
"apps/app-two": {
"version": "1.0.0",
"license": "ISC",
"dependencies": {
"cowsay": "^1.5.0"
}
},
"node_modules/a": {
"resolved": "a",
"link": true
},
"node_modules/ansi-regex": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz",
"integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=",
"engines": {
"node": ">=4"
}
},
"node_modules/ansi-styles": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
"dependencies": {
"color-convert": "^2.0.1"
},
"engines": {
"node": ">=8"
},
"funding": {
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
}
},
"node_modules/b": {
"resolved": "b",
"link": true
},
"node_modules/camelcase": {
"version": "5.3.1",
"resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz",
"integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==",
"engines": {
"node": ">=6"
}
},
"node_modules/cliui": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz",
"integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==",
"dependencies": {
"string-width": "^4.2.0",
"strip-ansi": "^6.0.0",
"wrap-ansi": "^6.2.0"
}
},
"node_modules/cliui/node_modules/ansi-regex": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz",
"integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==",
"engines": {
"node": ">=8"
}
},
"node_modules/cliui/node_modules/is-fullwidth-code-point": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
"integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
"engines": {
"node": ">=8"
}
},
"node_modules/cliui/node_modules/string-width": {
"version": "4.2.2",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz",
"integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==",
"dependencies": {
"emoji-regex": "^8.0.0",
"is-fullwidth-code-point": "^3.0.0",
"strip-ansi": "^6.0.0"
},
"engines": {
"node": ">=8"
}
},
"node_modules/cliui/node_modules/strip-ansi": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz",
"integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==",
"dependencies": {
"ansi-regex": "^5.0.0"
},
"engines": {
"node": ">=8"
}
},
"node_modules/color-convert": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
"dependencies": {
"color-name": "~1.1.4"
},
"engines": {
"node": ">=7.0.0"
}
},
"node_modules/color-name": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
},
"node_modules/cowsay": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/cowsay/-/cowsay-1.5.0.tgz",
"integrity": "sha512-8Ipzr54Z8zROr/62C8f0PdhQcDusS05gKTS87xxdji8VbWefWly0k8BwGK7+VqamOrkv3eGsCkPtvlHzrhWsCA==",
"dependencies": {
"get-stdin": "8.0.0",
"string-width": "~2.1.1",
"strip-final-newline": "2.0.0",
"yargs": "15.4.1"
},
"bin": {
"cowsay": "cli.js",
"cowthink": "cli.js"
},
"engines": {
"node": ">= 4"
}
},
"node_modules/debug": {
"version": "4.3.2",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz",
"integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==",
"dependencies": {
"ms": "2.1.2"
},
"engines": {
"node": ">=6.0"
},
"peerDependenciesMeta": {
"supports-color": {
"optional": true
}
}
},
"node_modules/decamelize": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz",
"integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/emoji-regex": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="
},
"node_modules/find-up": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz",
"integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==",
"dependencies": {
"locate-path": "^5.0.0",
"path-exists": "^4.0.0"
},
"engines": {
"node": ">=8"
}
},
"node_modules/get-caller-file": {
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
"integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
"engines": {
"node": "6.* || 8.* || >= 10.*"
}
},
"node_modules/get-stdin": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-8.0.0.tgz",
"integrity": "sha512-sY22aA6xchAzprjyqmSEQv4UbAAzRN0L2dQB0NlN5acTTK9Don6nhoc3eAbUnpZiCANAMfd/+40kVdKfFygohg==",
"engines": {
"node": ">=10"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/is-fullwidth-code-point": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
"integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=",
"engines": {
"node": ">=4"
}
},
"node_modules/locate-path": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz",
"integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==",
"dependencies": {
"p-locate": "^4.1.0"
},
"engines": {
"node": ">=8"
}
},
"node_modules/ms": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
},
"node_modules/p-limit": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
"integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
"dependencies": {
"p-try": "^2.0.0"
},
"engines": {
"node": ">=6"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/p-locate": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz",
"integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==",
"dependencies": {
"p-limit": "^2.2.0"
},
"engines": {
"node": ">=8"
}
},
"node_modules/p-try": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz",
"integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==",
"engines": {
"node": ">=6"
}
},
"node_modules/path-exists": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
"integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
"engines": {
"node": ">=8"
}
},
"node_modules/require-directory": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
"integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/require-main-filename": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz",
"integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg=="
},
"node_modules/set-blocking": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
"integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc="
},
"node_modules/string-width": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz",
"integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==",
"dependencies": {
"is-fullwidth-code-point": "^2.0.0",
"strip-ansi": "^4.0.0"
},
"engines": {
"node": ">=4"
}
},
"node_modules/strip-ansi": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz",
"integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=",
"dependencies": {
"ansi-regex": "^3.0.0"
},
"engines": {
"node": ">=4"
}
},
"node_modules/strip-final-newline": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz",
"integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==",
"engines": {
"node": ">=6"
}
},
"node_modules/which-module": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz",
"integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho="
},
"node_modules/wrap-ansi": {
"version": "6.2.0",
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz",
"integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==",
"dependencies": {
"ansi-styles": "^4.0.0",
"string-width": "^4.1.0",
"strip-ansi": "^6.0.0"
},
"engines": {
"node": ">=8"
}
},
"node_modules/wrap-ansi/node_modules/ansi-regex": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz",
"integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==",
"engines": {
"node": ">=8"
}
},
"node_modules/wrap-ansi/node_modules/is-fullwidth-code-point": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
"integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
"engines": {
"node": ">=8"
}
},
"node_modules/wrap-ansi/node_modules/string-width": {
"version": "4.2.2",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz",
"integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==",
"dependencies": {
"emoji-regex": "^8.0.0",
"is-fullwidth-code-point": "^3.0.0",
"strip-ansi": "^6.0.0"
},
"engines": {
"node": ">=8"
}
},
"node_modules/wrap-ansi/node_modules/strip-ansi": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz",
"integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==",
"dependencies": {
"ansi-regex": "^5.0.0"
},
"engines": {
"node": ">=8"
}
},
"node_modules/y18n": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz",
"integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ=="
},
"node_modules/yargs": {
"version": "15.4.1",
"resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz",
"integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==",
"dependencies": {
"cliui": "^6.0.0",
"decamelize": "^1.2.0",
"find-up": "^4.1.0",
"get-caller-file": "^2.0.1",
"require-directory": "^2.1.1",
"require-main-filename": "^2.0.0",
"set-blocking": "^2.0.0",
"string-width": "^4.2.0",
"which-module": "^2.0.0",
"y18n": "^4.0.0",
"yargs-parser": "^18.1.2"
},
"engines": {
"node": ">=8"
}
},
"node_modules/yargs-parser": {
"version": "18.1.3",
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz",
"integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==",
"dependencies": {
"camelcase": "^5.0.0",
"decamelize": "^1.2.0"
},
"engines": {
"node": ">=6"
}
},
"node_modules/yargs/node_modules/ansi-regex": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz",
"integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==",
"engines": {
"node": ">=8"
}
},
"node_modules/yargs/node_modules/is-fullwidth-code-point": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
"integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
"engines": {
"node": ">=8"
}
},
"node_modules/yargs/node_modules/string-width": {
"version": "4.2.2",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz",
"integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==",
"dependencies": {
"emoji-regex": "^8.0.0",
"is-fullwidth-code-point": "^3.0.0",
"strip-ansi": "^6.0.0"
},
"engines": {
"node": ">=8"
}
},
"node_modules/yargs/node_modules/strip-ansi": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz",
"integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==",
"dependencies": {
"ansi-regex": "^5.0.0"
},
"engines": {
"node": ">=8"
}
}
},
"dependencies": {
"a": {
"version": "file:a",
"requires": {
"debug": "^4.3.2"
}
},
"ansi-regex": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz",
"integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg="
},
"ansi-styles": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
"requires": {
"color-convert": "^2.0.1"
}
},
"b": {
"version": "file:b",
"requires": {
"cowsay": "^1.5.0"
}
},
"camelcase": {
"version": "5.3.1",
"resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz",
"integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg=="
},
"cliui": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz",
"integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==",
"requires": {
"string-width": "^4.2.0",
"strip-ansi": "^6.0.0",
"wrap-ansi": "^6.2.0"
},
"dependencies": {
"ansi-regex": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz",
"integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg=="
},
"is-fullwidth-code-point": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
"integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg=="
},
"string-width": {
"version": "4.2.2",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz",
"integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==",
"requires": {
"emoji-regex": "^8.0.0",
"is-fullwidth-code-point": "^3.0.0",
"strip-ansi": "^6.0.0"
}
},
"strip-ansi": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz",
"integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==",
"requires": {
"ansi-regex": "^5.0.0"
}
}
}
},
"color-convert": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
"requires": {
"color-name": "~1.1.4"
}
},
"color-name": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
},
"cowsay": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/cowsay/-/cowsay-1.5.0.tgz",
"integrity": "sha512-8Ipzr54Z8zROr/62C8f0PdhQcDusS05gKTS87xxdji8VbWefWly0k8BwGK7+VqamOrkv3eGsCkPtvlHzrhWsCA==",
"requires": {
"get-stdin": "8.0.0",
"string-width": "~2.1.1",
"strip-final-newline": "2.0.0",
"yargs": "15.4.1"
}
},
"debug": {
"version": "4.3.2",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz",
"integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==",
"requires": {
"ms": "2.1.2"
}
},
"decamelize": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz",
"integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA="
},
"emoji-regex": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="
},
"find-up": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz",
"integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==",
"requires": {
"locate-path": "^5.0.0",
"path-exists": "^4.0.0"
}
},
"get-caller-file": {
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
"integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg=="
},
"get-stdin": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-8.0.0.tgz",
"integrity": "sha512-sY22aA6xchAzprjyqmSEQv4UbAAzRN0L2dQB0NlN5acTTK9Don6nhoc3eAbUnpZiCANAMfd/+40kVdKfFygohg=="
},
"is-fullwidth-code-point": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
"integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8="
},
"locate-path": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz",
"integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==",
"requires": {
"p-locate": "^4.1.0"
}
},
"ms": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
},
"p-limit": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
"integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
"requires": {
"p-try": "^2.0.0"
}
},
"p-locate": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz",
"integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==",
"requires": {
"p-limit": "^2.2.0"
}
},
"p-try": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz",
"integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ=="
},
"path-exists": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
"integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w=="
},
"require-directory": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
"integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I="
},
"require-main-filename": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz",
"integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg=="
},
"set-blocking": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
"integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc="
},
"string-width": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz",
"integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==",
"requires": {
"is-fullwidth-code-point": "^2.0.0",
"strip-ansi": "^4.0.0"
}
},
"strip-ansi": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz",
"integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=",
"requires": {
"ansi-regex": "^3.0.0"
}
},
"strip-final-newline": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz",
"integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA=="
},
"which-module": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz",
"integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho="
},
"wrap-ansi": {
"version": "6.2.0",
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz",
"integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==",
"requires": {
"ansi-styles": "^4.0.0",
"string-width": "^4.1.0",
"strip-ansi": "^6.0.0"
},
"dependencies": {
"ansi-regex": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz",
"integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg=="
},
"is-fullwidth-code-point": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
"integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg=="
},
"string-width": {
"version": "4.2.2",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz",
"integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==",
"requires": {
"emoji-regex": "^8.0.0",
"is-fullwidth-code-point": "^3.0.0",
"strip-ansi": "^6.0.0"
}
},
"strip-ansi": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz",
"integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==",
"requires": {
"ansi-regex": "^5.0.0"
}
}
}
},
"y18n": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz",
"integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ=="
},
"yargs": {
"version": "15.4.1",
"resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz",
"integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==",
"requires": {
"cliui": "^6.0.0",
"decamelize": "^1.2.0",
"find-up": "^4.1.0",
"get-caller-file": "^2.0.1",
"require-directory": "^2.1.1",
"require-main-filename": "^2.0.0",
"set-blocking": "^2.0.0",
"string-width": "^4.2.0",
"which-module": "^2.0.0",
"y18n": "^4.0.0",
"yargs-parser": "^18.1.2"
},
"dependencies": {
"ansi-regex": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz",
"integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg=="
},
"is-fullwidth-code-point": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
"integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg=="
},
"string-width": {
"version": "4.2.2",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz",
"integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==",
"requires": {
"emoji-regex": "^8.0.0",
"is-fullwidth-code-point": "^3.0.0",
"strip-ansi": "^6.0.0"
}
},
"strip-ansi": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz",
"integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==",
"requires": {
"ansi-regex": "^5.0.0"
}
}
}
},
"yargs-parser": {
"version": "18.1.3",
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz",
"integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==",
"requires": {
"camelcase": "^5.0.0",
"decamelize": "^1.2.0"
}
}
}
}

View File

@@ -0,0 +1,8 @@
{
"name": "42-npm-workspace-with-nx",
"version": "1.0.0",
"workspaces": [
"apps/app-one",
"apps/app-two"
]
}

View File

@@ -0,0 +1,4 @@
{
"version": 2,
"projects": {}
}

View File

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

View File

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

View File

@@ -0,0 +1,3 @@
{
"version": 2
}

View File

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

View File

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

View File

@@ -0,0 +1,4 @@
{
"version": 2,
"projects": "hello"
}

View File

@@ -8,6 +8,8 @@ describe('monorepo-managers', () => {
['28-turborepo-with-yarn-workspaces', 'turbo'],
['31-turborepo-in-package-json', 'turbo'],
['22-pnpm', null],
['39-nx-monorepo', 'nx'],
['40-rush-monorepo', 'rush'],
])('with detectFramework', (fixturePath, frameworkSlug) => {
const testName = frameworkSlug
? `should detect a ${frameworkSlug} workspace for ${fixturePath}`

View File

@@ -1,5 +1,6 @@
import path from 'path';
import frameworkList from '@vercel/frameworks';
import workspaceManagers from '../src/workspaces/workspace-managers';
import { detectFramework, DetectorFilesystem } from '../src';
import { Stat } from '../src/detectors/filesystem';
@@ -173,6 +174,24 @@ describe('DetectorFilesystem', () => {
expect(hasPathSpy).not.toHaveBeenCalled();
});
it('should be able to write files', async () => {
const files = {};
const fs = new VirtualFilesystem(files);
const hasPathSpy = jest.spyOn(fs, '_hasPath');
const isFileSpy = jest.spyOn(fs, '_isFile');
const readFileSpy = jest.spyOn(fs, '_readFile');
await fs.writeFile('file.txt', 'Hello World');
expect(await fs.readFile('file.txt')).toEqual(Buffer.from('Hello World'));
expect(await fs.hasPath('file.txt')).toBe(true);
expect(await fs.isFile('file.txt')).toBe(true);
// We expect that the fs returned values from it's caches instead of calling the underlying functions
expect(hasPathSpy).not.toHaveBeenCalled();
expect(isFileSpy).not.toHaveBeenCalled();
expect(readFileSpy).not.toHaveBeenCalled();
});
it('should be able to change directories', async () => {
const nextPackageJson = JSON.stringify({
dependencies: {
@@ -253,6 +272,28 @@ describe('DetectorFilesystem', () => {
expect(await detectFramework({ fs, frameworkList })).toBe(null);
});
it('Detect nx', async () => {
const fs = new VirtualFilesystem({
'workspace.json': JSON.stringify({
projects: { 'app-one': 'apps/app-one' },
}),
});
expect(
await detectFramework({ fs, frameworkList: workspaceManagers })
).toBe('nx');
});
it('Do not detect anything', async () => {
const fs = new VirtualFilesystem({
'workspace.json': JSON.stringify({ projects: {} }),
});
expect(
await detectFramework({ fs, frameworkList: workspaceManagers })
).toBe(null);
});
it('Detect Next.js', async () => {
const fs = new VirtualFilesystem({
'package.json': JSON.stringify({

View File

@@ -14,6 +14,10 @@ describe.each<[string, string[]]>([
['/backend/c', '/backend/d', '/frontend/a', '/frontend/b'],
],
['22-pnpm', []],
['41-nx-workspace', ['/apps/app-one', '/apps/app-two']],
['42-npm-workspace-with-nx', ['/apps/app-one', '/apps/app-two']],
['43-nx-json-misshaped', []],
['44-nx-json-string', []],
])('`getWorkspacesPackagePaths()`', (fixturePath, packagePaths) => {
const testName =
packagePaths.length > 0

View File

@@ -27,6 +27,7 @@ import {
getWriteableDirectory,
shouldServe,
debug,
cloneEnv,
} from '@vercel/build-utils';
const TMP = tmpdir();
@@ -694,11 +695,9 @@ Learn more: https://vercel.com/docs/runtimes#official-runtimes/go`
`vercel-dev-port-${Math.random().toString(32).substring(2)}`
);
const env: typeof process.env = {
...process.env,
...meta.env,
const env = cloneEnv(process.env, meta.env, {
VERCEL_DEV_PORT_FILE: portFile,
};
});
const tmpRelative = `.${sep}${entrypointDir}`;
const child = spawn('go', ['run', tmpRelative], {

View File

@@ -1,6 +1,6 @@
{
"name": "@vercel/go",
"version": "2.2.5",
"version": "2.2.7",
"license": "MIT",
"main": "./dist/index",
"homepage": "https://vercel.com/docs/runtimes#official-runtimes/go",
@@ -35,7 +35,7 @@
"@types/jest": "28.1.6",
"@types/node-fetch": "^2.3.0",
"@types/tar": "^4.0.0",
"@vercel/build-utils": "5.4.2",
"@vercel/build-utils": "5.4.4",
"@vercel/ncc": "0.24.0",
"async-retry": "1.3.1",
"execa": "^1.0.0",

View File

@@ -0,0 +1,10 @@
package handler
import (
"fmt"
"net/http"
)
func Handler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "hello:RANDOMNESS_PLACEHOLDER")
}

View File

@@ -0,0 +1,10 @@
package handler
import (
"fmt"
"net/http"
)
func Handler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "hi:RANDOMNESS_PLACEHOLDER")
}

View File

@@ -0,0 +1,3 @@
module github.com/vercel/vercel/packages/go/test/fixtures/21-square-brackets-with-go-mod
go 1.16

View File

@@ -0,0 +1,9 @@
{
"probes": [
{
"path": "/api/a",
"mustContain": "hello:RANDOMNESS_PLACEHOLDER"
},
{ "path": "/api/sub/b", "mustContain": "hi:RANDOMNESS_PLACEHOLDER" }
]
}

View File

@@ -1,6 +1,6 @@
{
"name": "@vercel/hydrogen",
"version": "0.0.18",
"version": "0.0.20",
"license": "MIT",
"main": "./dist/index.js",
"homepage": "https://vercel.com/docs",
@@ -21,7 +21,7 @@
"devDependencies": {
"@types/jest": "27.5.1",
"@types/node": "*",
"@vercel/build-utils": "5.4.2",
"@vercel/build-utils": "5.4.4",
"typescript": "4.6.4"
}
}

View File

@@ -1,6 +1,6 @@
{
"name": "@vercel/next",
"version": "3.1.25",
"version": "3.1.27",
"license": "MIT",
"main": "./dist/index",
"homepage": "https://vercel.com/docs/runtimes#official-runtimes/next-js",
@@ -8,7 +8,7 @@
"build": "node build.js",
"build-dev": "node build.js --dev",
"test": "jest --env node --verbose --bail --runInBand",
"test-unit": "yarn test test/build.test.ts test/unit/",
"test-unit": "yarn test test/unit/",
"test-next-local": "jest --env node --verbose --bail --forceExit --testTimeout=360000 test/integration/*.test.js test/integration/*.test.ts",
"test-next-local:middleware": "jest --env node --verbose --bail --useStderr --testTimeout=360000 test/integration/middleware.test.ts",
"test-integration-once": "rm test/builder-info.json; jest --env node --verbose --runInBand --bail test/fixtures/**/*.test.js"
@@ -44,7 +44,7 @@
"@types/semver": "6.0.0",
"@types/text-table": "0.2.1",
"@types/webpack-sources": "3.2.0",
"@vercel/build-utils": "5.4.2",
"@vercel/build-utils": "5.4.4",
"@vercel/nft": "0.22.1",
"@vercel/routing-utils": "2.0.2",
"async-sema": "3.0.1",

View File

@@ -675,7 +675,7 @@ export const build: BuildV2 = async ({
}
}
let dynamicPrefix = path.join('/', entryDirectory);
let dynamicPrefix = path.posix.join('/', entryDirectory);
dynamicPrefix = dynamicPrefix === '/' ? '' : dynamicPrefix;
if (imagesManifest) {
@@ -824,7 +824,7 @@ export const build: BuildV2 = async ({
// Make sure to 404 for the /404 path itself
{
src: path.join('/', entryDirectory, '404/?'),
src: path.posix.join('/', entryDirectory, '404/?'),
status: 404,
continue: true,
},
@@ -838,7 +838,7 @@ export const build: BuildV2 = async ({
...(routesManifest?.basePath
? [
{
src: path.join('/', entryDirectory, '_next/image/?'),
src: path.posix.join('/', entryDirectory, '_next/image/?'),
dest: '/_next/image',
check: true,
},
@@ -848,12 +848,12 @@ export const build: BuildV2 = async ({
// No-op _next/data rewrite to trigger handle: 'rewrites' and then 404
// if no match to prevent rewriting _next/data unexpectedly
{
src: path.join('/', entryDirectory, '_next/data/(.*)'),
dest: path.join('/', entryDirectory, '_next/data/$1'),
src: path.posix.join('/', entryDirectory, '_next/data/(.*)'),
dest: path.posix.join('/', entryDirectory, '_next/data/$1'),
check: true,
},
{
src: path.join('/', entryDirectory, '_next/data/(.*)'),
src: path.posix.join('/', entryDirectory, '_next/data/(.*)'),
status: 404,
},
@@ -867,14 +867,14 @@ export const build: BuildV2 = async ({
...fallbackRewrites,
{ src: path.join('/', entryDirectory, '.*'), status: 404 },
{ src: path.posix.join('/', entryDirectory, '.*'), status: 404 },
// We need to make sure to 404 for /_next after handle: miss since
// handle: miss is called before rewrites and to prevent rewriting
// /_next
{ handle: 'miss' },
{
src: path.join(
src: path.posix.join(
'/',
entryDirectory,
'_next/static/(?:[^/]+/pages|pages|chunks|runtime|css|image|media)/.+'
@@ -894,7 +894,7 @@ export const build: BuildV2 = async ({
{
// This ensures we only match known emitted-by-Next.js files and not
// user-emitted files which may be missing a hash in their filename.
src: path.join(
src: path.posix.join(
'/',
entryDirectory,
`_next/static/(?:[^/]+/pages|pages|chunks|runtime|css|image|media|${escapedBuildId})/.+`
@@ -909,15 +909,15 @@ export const build: BuildV2 = async ({
},
// error handling
...(output[path.join('./', entryDirectory, '404')] ||
output[path.join('./', entryDirectory, '404/index')]
...(output[path.posix.join('./', entryDirectory, '404')] ||
output[path.posix.join('./', entryDirectory, '404/index')]
? [
{ handle: 'error' } as RouteWithHandle,
{
status: 404,
src: path.join(entryDirectory, '.*'),
dest: path.join('/', entryDirectory, '404'),
src: path.posix.join(entryDirectory, '.*'),
dest: path.posix.join('/', entryDirectory, '404'),
},
]
: []),
@@ -1063,7 +1063,7 @@ export const build: BuildV2 = async ({
}
debug(`Creating serverless function for page: "${page}"...`);
lambdas[path.join(entryDirectory, pathname)] = new NodejsLambda({
lambdas[path.posix.join(entryDirectory, pathname)] = new NodejsLambda({
files: {
...nextFiles,
...pageFiles,
@@ -1114,30 +1114,32 @@ export const build: BuildV2 = async ({
prerenderManifest,
routesManifest
);
hasStatic500 = !!staticPages[path.join(entryDirectory, '500')];
hasStatic500 = !!staticPages[path.posix.join(entryDirectory, '500')];
// this can be either 404.html in latest versions
// or _errors/404.html versions while this was experimental
static404Page =
staticPages[path.join(entryDirectory, '404')] && hasPages404
? path.join(entryDirectory, '404')
: staticPages[path.join(entryDirectory, '_errors/404')]
? path.join(entryDirectory, '_errors/404')
staticPages[path.posix.join(entryDirectory, '404')] && hasPages404
? path.posix.join(entryDirectory, '404')
: staticPages[path.posix.join(entryDirectory, '_errors/404')]
? path.posix.join(entryDirectory, '_errors/404')
: undefined;
const { i18n } = routesManifest || {};
if (!static404Page && i18n) {
static404Page = staticPages[
path.join(entryDirectory, i18n.defaultLocale, '404')
path.posix.join(entryDirectory, i18n.defaultLocale, '404')
]
? path.join(entryDirectory, i18n.defaultLocale, '404')
? path.posix.join(entryDirectory, i18n.defaultLocale, '404')
: undefined;
}
if (!hasStatic500 && i18n) {
hasStatic500 =
!!staticPages[path.join(entryDirectory, i18n.defaultLocale, '500')];
!!staticPages[
path.posix.join(entryDirectory, i18n.defaultLocale, '500')
];
}
if (routesManifest) {
@@ -1171,7 +1173,7 @@ export const build: BuildV2 = async ({
src: (
dataRoute.namedDataRouteRegex || dataRoute.dataRouteRegex
).replace(/^\^/, `^${appMountPrefixNoTrailingSlash}`),
dest: path.join(
dest: path.posix.join(
'/',
entryDirectory,
// make sure to route SSG data route to the data prerender

View File

@@ -169,7 +169,7 @@ export async function serverBuild({
CORRECTED_MANIFESTS_VERSION
);
let hasStatic500 = !!staticPages[path.join(entryDirectory, '500')];
let hasStatic500 = !!staticPages[path.posix.join(entryDirectory, '500')];
if (lambdaPageKeys.length === 0) {
throw new NowBuildError({
@@ -187,23 +187,23 @@ export async function serverBuild({
const hasPages404 = routesManifest.pages404;
let static404Page =
staticPages[path.join(entryDirectory, '404')] && hasPages404
? path.join(entryDirectory, '404')
: staticPages[path.join(entryDirectory, '_errors/404')]
? path.join(entryDirectory, '_errors/404')
staticPages[path.posix.join(entryDirectory, '404')] && hasPages404
? path.posix.join(entryDirectory, '404')
: staticPages[path.posix.join(entryDirectory, '_errors/404')]
? path.posix.join(entryDirectory, '_errors/404')
: undefined;
if (!static404Page && i18n) {
static404Page = staticPages[
path.join(entryDirectory, i18n.defaultLocale, '404')
path.posix.join(entryDirectory, i18n.defaultLocale, '404')
]
? path.join(entryDirectory, i18n.defaultLocale, '404')
? path.posix.join(entryDirectory, i18n.defaultLocale, '404')
: undefined;
}
if (!hasStatic500 && i18n) {
hasStatic500 =
!!staticPages[path.join(entryDirectory, i18n.defaultLocale, '500')];
!!staticPages[path.posix.join(entryDirectory, i18n.defaultLocale, '500')];
}
const lstatSema = new Sema(25);
@@ -377,7 +377,7 @@ export async function serverBuild({
if (i18n) {
for (const locale of i18n.locales) {
const static404File =
staticPages[path.join(entryDirectory, locale, '/404')] ||
staticPages[path.posix.join(entryDirectory, locale, '/404')] ||
new FileFsRef({
fsPath: path.join(pagesDir, locale, '/404.html'),
});
@@ -520,14 +520,14 @@ export async function serverBuild({
if (
entryDirectory !== '.' &&
path.join('/', entryDirectory) !== routesManifest.basePath
path.posix.join('/', entryDirectory) !== routesManifest.basePath
) {
// we normalize the entryDirectory in the request URL since
// Next.js isn't aware of it and it isn't included in the
// x-matched-path header
launcher = launcher.replace(
'// entryDirectory handler',
`req.url = req.url.replace(/^${path
`req.url = req.url.replace(/^${path.posix
.join('/', entryDirectory)
.replace(/\//g, '\\/')}/, '')`
);
@@ -848,7 +848,7 @@ export async function serverBuild({
}
const outputName = normalizeIndexOutput(
path.join(entryDirectory, pageNoExt),
path.posix.join(entryDirectory, pageNoExt),
true
);
@@ -863,7 +863,7 @@ export async function serverBuild({
for (const locale of i18n.locales) {
lambdas[
normalizeIndexOutput(
path.join(
path.posix.join(
entryDirectory,
locale,
pageNoExt === 'index' ? '' : pageNoExt
@@ -918,7 +918,7 @@ export async function serverBuild({
route = normalizeLocalePath(route, routesManifest.i18n.locales).pathname;
}
delete lambdas[
path.join('.', entryDirectory, route === '/' ? 'index' : route)
path.posix.join('.', entryDirectory, route === '/' ? 'index' : route)
];
});
@@ -968,7 +968,7 @@ export async function serverBuild({
// routes since only the status is being modified and we don't want
// to exceed the routes limit
const starterRouteSrc = `^${
entryDirectory !== '.' ? path.join('/', entryDirectory, '()') : '()'
entryDirectory !== '.' ? path.posix.join('/', entryDirectory, '()') : '()'
}`;
let currentRouteSrc = starterRouteSrc;
@@ -1019,14 +1019,14 @@ export async function serverBuild({
? [
// strip _next/data prefix for resolving
{
src: `^${path.join(
src: `^${path.posix.join(
'/',
entryDirectory,
'/_next/data/',
escapedBuildId,
'/(.*).json'
)}`,
dest: `${path.join(
dest: `${path.posix.join(
'/',
entryDirectory,
'/$1',
@@ -1044,14 +1044,18 @@ export async function serverBuild({
// normalize "/index" from "/_next/data/index.json" to -> just "/"
// as matches a rewrite sources will expect just "/"
{
src: path.join('^/', entryDirectory, '/index(?:/)?'),
src: path.posix.join('^/', entryDirectory, '/index(?:/)?'),
has: [
{
type: 'header',
key: 'x-nextjs-data',
},
],
dest: path.join('/', entryDirectory, trailingSlash ? '/' : ''),
dest: path.posix.join(
'/',
entryDirectory,
trailingSlash ? '/' : ''
),
...(isOverride ? { override: true } : {}),
continue: true,
},
@@ -1063,14 +1067,19 @@ export async function serverBuild({
return isNextDataServerResolving
? [
{
src: path.join('^/', entryDirectory, trailingSlash ? '/' : '', '$'),
src: path.posix.join(
'^/',
entryDirectory,
trailingSlash ? '/' : '',
'$'
),
has: [
{
type: 'header',
key: 'x-nextjs-data',
},
],
dest: `${path.join(
dest: `${path.posix.join(
'/',
entryDirectory,
'/_next/data/',
@@ -1081,7 +1090,7 @@ export async function serverBuild({
...(isOverride ? { override: true } : {}),
},
{
src: path.join(
src: path.posix.join(
'^/',
entryDirectory,
'((?!_next/)(?:.*[^/]|.*))/?$'
@@ -1092,7 +1101,7 @@ export async function serverBuild({
key: 'x-nextjs-data',
},
],
dest: `${path.join(
dest: `${path.posix.join(
'/',
entryDirectory,
'/_next/data/',
@@ -1176,7 +1185,7 @@ export async function serverBuild({
// Handle auto-adding current default locale to path based on
// $wildcard
{
src: `^${path.join(
src: `^${path.posix.join(
'/',
entryDirectory,
'/'
@@ -1186,7 +1195,9 @@ export async function serverBuild({
// we aren't able to ensure trailing slash mode here
// so ensure this comes after the trailing slash redirect
dest: `${
entryDirectory !== '.' ? path.join('/', entryDirectory) : ''
entryDirectory !== '.'
? path.posix.join('/', entryDirectory)
: ''
}$wildcard/$1`,
continue: true,
},
@@ -1197,7 +1208,10 @@ export async function serverBuild({
i18n.localeDetection !== false
? [
{
src: `^${path.join('/', entryDirectory)}/?(?:${i18n.locales
src: `^${path.posix.join(
'/',
entryDirectory
)}/?(?:${i18n.locales
.map(locale => escapeStringRegexp(locale))
.join('|')})?/?$`,
locale: {
@@ -1251,8 +1265,12 @@ export async function serverBuild({
: []),
{
src: `^${path.join('/', entryDirectory)}$`,
dest: `${path.join('/', entryDirectory, i18n.defaultLocale)}`,
src: `^${path.posix.join('/', entryDirectory)}$`,
dest: `${path.posix.join(
'/',
entryDirectory,
i18n.defaultLocale
)}`,
continue: true,
},
@@ -1262,14 +1280,18 @@ export async function serverBuild({
// e.g. for /de/posts/[slug] x-now-route-matches would have
// 1=posts%2Fpost-1
{
src: `^${path.join(
src: `^${path.posix.join(
'/',
entryDirectory,
'/'
)}(?!(?:_next/.*|${i18n.locales
.map(locale => escapeStringRegexp(locale))
.join('|')})(?:/.*|$))(.*)$`,
dest: `${path.join('/', entryDirectory, i18n.defaultLocale)}/$1`,
dest: `${path.posix.join(
'/',
entryDirectory,
i18n.defaultLocale
)}/$1`,
continue: true,
},
]
@@ -1293,7 +1315,11 @@ export async function serverBuild({
...(i18n
? [
{
src: `${path.join('/', entryDirectory, '/')}(?:${i18n.locales
src: `${path.posix.join(
'/',
entryDirectory,
'/'
)}(?:${i18n.locales
.map(locale => escapeStringRegexp(locale))
.join('|')})?[/]?404/?`,
status: 404,
@@ -1302,7 +1328,7 @@ export async function serverBuild({
]
: [
{
src: path.join('/', entryDirectory, '404/?'),
src: path.posix.join('/', entryDirectory, '404/?'),
status: 404,
continue: true,
},
@@ -1314,7 +1340,11 @@ export async function serverBuild({
: i18n
? [
{
src: `${path.join('/', entryDirectory, '/')}(?:${i18n.locales
src: `${path.posix.join(
'/',
entryDirectory,
'/'
)}(?:${i18n.locales
.map(locale => escapeStringRegexp(locale))
.join('|')})?[/]?500`,
status: 500,
@@ -1323,7 +1353,7 @@ export async function serverBuild({
]
: [
{
src: path.join('/', entryDirectory, '500'),
src: path.posix.join('/', entryDirectory, '500'),
status: 500,
continue: true,
},
@@ -1346,7 +1376,7 @@ export async function serverBuild({
...(routesManifest?.basePath
? [
{
src: path.join('/', entryDirectory, '_next/image/?'),
src: path.posix.join('/', entryDirectory, '_next/image/?'),
dest: '/_next/image',
check: true,
},
@@ -1361,8 +1391,8 @@ export async function serverBuild({
// No-op _next/data rewrite to trigger handle: 'rewrites' and then 404
// if no match to prevent rewriting _next/data unexpectedly
{
src: path.join('/', entryDirectory, '_next/data/(.*)'),
dest: path.join('/', entryDirectory, '_next/data/$1'),
src: path.posix.join('/', entryDirectory, '_next/data/(.*)'),
dest: path.posix.join('/', entryDirectory, '_next/data/$1'),
check: true,
},
]
@@ -1378,13 +1408,13 @@ export async function serverBuild({
...fallbackRewrites,
{ src: path.join('/', entryDirectory, '.*'), status: 404 },
{ src: path.posix.join('/', entryDirectory, '.*'), status: 404 },
// We need to make sure to 404 for /_next after handle: miss since
// handle: miss is called before rewrites and to prevent rewriting /_next
{ handle: 'miss' },
{
src: path.join(
src: path.posix.join(
'/',
entryDirectory,
'_next/static/(?:[^/]+/pages|pages|chunks|runtime|css|image|media)/.+'
@@ -1399,10 +1429,10 @@ export async function serverBuild({
...(i18n
? [
{
src: `^${path.join('/', entryDirectory)}/?(?:${i18n.locales
src: `^${path.posix.join('/', entryDirectory)}/?(?:${i18n.locales
.map(locale => escapeStringRegexp(locale))
.join('|')})/(.*)`,
dest: `${path.join('/', entryDirectory, '/')}$1`,
dest: `${path.posix.join('/', entryDirectory, '/')}$1`,
check: true,
},
]
@@ -1474,8 +1504,8 @@ export async function serverBuild({
// ensure we 404 for non-existent _next/data routes before
// trying page dynamic routes
{
src: path.join('/', entryDirectory, '_next/data/(.*)'),
dest: path.join('/', entryDirectory, '404'),
src: path.posix.join('/', entryDirectory, '_next/data/(.*)'),
dest: path.posix.join('/', entryDirectory, '404'),
status: 404,
},
]
@@ -1488,7 +1518,7 @@ export async function serverBuild({
...(isNextDataServerResolving
? [
{
src: `^${path.join(
src: `^${path.posix.join(
'/',
entryDirectory,
'/_next/data/',
@@ -1504,7 +1534,7 @@ export async function serverBuild({
// add a catch-all data route so we don't 404 when getting
// middleware effects
{
src: `^${path.join(
src: `^${path.posix.join(
'/',
entryDirectory,
'/_next/data/',
@@ -1522,7 +1552,7 @@ export async function serverBuild({
{
// This ensures we only match known emitted-by-Next.js files and not
// user-emitted files which may be missing a hash in their filename.
src: path.join(
src: path.posix.join(
'/',
entryDirectory,
`_next/static/(?:[^/]+/pages|pages|chunks|runtime|css|image|media|${escapedBuildId})/.+`
@@ -1539,7 +1569,7 @@ export async function serverBuild({
// TODO: remove below workaround when `/` is allowed to be output
// different than `/index`
{
src: path.join('/', entryDirectory, '/index'),
src: path.posix.join('/', entryDirectory, '/index'),
headers: {
'x-matched-path': '/',
},
@@ -1547,7 +1577,7 @@ export async function serverBuild({
important: true,
},
{
src: path.join('/', entryDirectory, `/((?!index$).*)`),
src: path.posix.join('/', entryDirectory, `/((?!index$).*)`),
headers: {
'x-matched-path': '/$1',
},
@@ -1562,20 +1592,20 @@ export async function serverBuild({
...(i18n && (static404Page || hasIsr404Page || lambdaPages['404.js'])
? [
{
src: `${path.join(
src: `${path.posix.join(
'/',
entryDirectory,
'/'
)}(?<nextLocale>${i18n.locales
.map(locale => escapeStringRegexp(locale))
.join('|')})(/.*|$)`,
dest: path.join('/', entryDirectory, '/$nextLocale/404'),
dest: path.posix.join('/', entryDirectory, '/$nextLocale/404'),
status: 404,
caseSensitive: true,
},
{
src: path.join('/', entryDirectory, '.*'),
dest: path.join(
src: path.posix.join('/', entryDirectory, '.*'),
dest: path.posix.join(
'/',
entryDirectory,
`/${i18n.defaultLocale}/404`
@@ -1585,13 +1615,13 @@ export async function serverBuild({
]
: [
{
src: path.join('/', entryDirectory, '.*'),
dest: path.join(
src: path.posix.join('/', entryDirectory, '.*'),
dest: path.posix.join(
'/',
entryDirectory,
static404Page ||
hasIsr404Page ||
lambdas[path.join(entryDirectory, '404')]
lambdas[path.posix.join(entryDirectory, '404')]
? '/404'
: '/_error'
),
@@ -1603,20 +1633,20 @@ export async function serverBuild({
...(i18n && (hasStatic500 || hasIsr500Page || lambdaPages['500.js'])
? [
{
src: `${path.join(
src: `${path.posix.join(
'/',
entryDirectory,
'/'
)}(?<nextLocale>${i18n.locales
.map(locale => escapeStringRegexp(locale))
.join('|')})(/.*|$)`,
dest: path.join('/', entryDirectory, '/$nextLocale/500'),
dest: path.posix.join('/', entryDirectory, '/$nextLocale/500'),
status: 500,
caseSensitive: true,
},
{
src: path.join('/', entryDirectory, '.*'),
dest: path.join(
src: path.posix.join('/', entryDirectory, '.*'),
dest: path.posix.join(
'/',
entryDirectory,
`/${i18n.defaultLocale}/500`
@@ -1626,13 +1656,13 @@ export async function serverBuild({
]
: [
{
src: path.join('/', entryDirectory, '.*'),
dest: path.join(
src: path.posix.join('/', entryDirectory, '.*'),
dest: path.posix.join(
'/',
entryDirectory,
hasStatic500 ||
hasIsr500Page ||
lambdas[path.join(entryDirectory, '500')]
lambdas[path.posix.join(entryDirectory, '500')]
? '/500'
: '/_error'
),

View File

@@ -290,7 +290,7 @@ export async function getDynamicRoutes(
.map(({ page, regex }: { page: string; regex: string }) => {
return {
src: regex,
dest: !isDev ? path.join('/', entryDirectory, page) : page,
dest: !isDev ? path.posix.join('/', entryDirectory, page) : page,
check: true,
status:
canUsePreviewMode && omittedRoutes?.has(page) ? 404 : undefined,
@@ -315,7 +315,9 @@ export async function getDynamicRoutes(
const { page, namedRegex, regex, routeKeys } = params;
const route: RouteWithSrc = {
src: namedRegex || regex,
dest: `${!isDev ? path.join('/', entryDirectory, page) : page}${
dest: `${
!isDev ? path.posix.join('/', entryDirectory, page) : page
}${
routeKeys
? `?${Object.keys(routeKeys)
.map(key => `${routeKeys[key]}=$${key}`)
@@ -410,7 +412,7 @@ export async function getDynamicRoutes(
pageMatchers.forEach(pageMatcher => {
// in `vercel dev` we don't need to prefix the destination
const dest = !isDev
? path.join('/', entryDirectory, pageMatcher.pageName)
? path.posix.join('/', entryDirectory, pageMatcher.pageName)
: pageMatcher.pageName;
if (pageMatcher && pageMatcher.matcher) {
@@ -465,8 +467,8 @@ export function localizeDynamicRoutes(
// ensure destination has locale prefix to match prerender output
// path so that the prerender object is used
route.dest = route.dest!.replace(
`${path.join('/', entryDirectory, '/')}`,
`${path.join('/', entryDirectory, '$nextLocale', '/')}`
`${path.posix.join('/', entryDirectory, '/')}`,
`${path.posix.join('/', entryDirectory, '$nextLocale', '/')}`
);
}
} else {
@@ -575,7 +577,7 @@ export function filterStaticPages(
return;
}
const staticRoute = path.join(entryDirectory, pathname);
const staticRoute = path.posix.join(entryDirectory, pathname);
staticPages[staticRoute] = staticPageFiles[page];
staticPages[staticRoute].contentType = htmlContentType;
@@ -1616,11 +1618,11 @@ export const onPrerenderRouteInitial = (
// if the 404 page used getStaticProps we need to update static404Page
// since it wasn't populated from the staticPages group
if (routeNoLocale === '/404') {
static404Page = path.join(entryDirectory, routeKey);
static404Page = path.posix.join(entryDirectory, routeKey);
}
if (routeNoLocale === '/500') {
static500Page = path.join(entryDirectory, routeKey);
static500Page = path.posix.join(entryDirectory, routeKey);
}
if (
@@ -1851,7 +1853,10 @@ export const onPrerenderRoute =
'/',
srcRoute == null
? outputPathPageOrig
: path.join(entryDirectory, srcRoute === '/' ? '/index' : srcRoute)
: path.posix.join(
entryDirectory,
srcRoute === '/' ? '/index' : srcRoute
)
),
isServerMode
);
@@ -2066,17 +2071,17 @@ export async function getStaticFiles(
const publicDirectoryFiles: Record<string, FileFsRef> = {};
for (const file of Object.keys(nextStaticFiles)) {
staticFiles[path.join(entryDirectory, `_next/static/${file}`)] =
staticFiles[path.posix.join(entryDirectory, `_next/static/${file}`)] =
nextStaticFiles[file];
}
for (const file of Object.keys(staticFolderFiles)) {
staticDirectoryFiles[path.join(entryDirectory, 'static', file)] =
staticDirectoryFiles[path.posix.join(entryDirectory, 'static', file)] =
staticFolderFiles[file];
}
for (const file of Object.keys(publicFolderFiles)) {
publicDirectoryFiles[path.join(entryDirectory, file)] =
publicDirectoryFiles[path.posix.join(entryDirectory, file)] =
publicFolderFiles[file];
}

View File

@@ -0,0 +1,10 @@
export default function Root({ children }) {
return (
<html className="this-is-another-document-html">
<head>
<title>{`hello world`}</title>
</head>
<body className="this-is-another-document-body">{children}</body>
</html>
);
}

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