mirror of
https://github.com/LukeHagar/vercel.git
synced 2025-12-11 04:22:13 +00:00
Compare commits
104 Commits
@vercel/ru
...
@vercel/py
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
40f38948a0 | ||
|
|
87eba56063 | ||
|
|
d0a5676c26 | ||
|
|
da9fa997ed | ||
|
|
3d79a9d4d4 | ||
|
|
ae13c5ee92 | ||
|
|
ad654139df | ||
|
|
74f8414e12 | ||
|
|
65c2860e14 | ||
|
|
3b5b397b35 | ||
|
|
051e061176 | ||
|
|
30d46321cc | ||
|
|
c2563535ea | ||
|
|
5f2bed4f24 | ||
|
|
cfb7946f4b | ||
|
|
05c5b3a80d | ||
|
|
45bd855250 | ||
|
|
49de8ad9a0 | ||
|
|
a1ea56fd67 | ||
|
|
e88addc9ed | ||
|
|
5d50013f93 | ||
|
|
44e1eb3983 | ||
|
|
f8af013349 | ||
|
|
972cc495ec | ||
|
|
1c580da3d8 | ||
|
|
244554ab1b | ||
|
|
053c185481 | ||
|
|
8805b586ea | ||
|
|
681070ffa0 | ||
|
|
362b17d60a | ||
|
|
c7c9b1a791 | ||
|
|
c42f309463 | ||
|
|
a0ead28369 | ||
|
|
8814fc1515 | ||
|
|
0d044b4eac | ||
|
|
f6bd1aa8c0 | ||
|
|
8cd84ec066 | ||
|
|
a8df231e4c | ||
|
|
f674842bed | ||
|
|
bf5cfa9a41 | ||
|
|
12121b7a71 | ||
|
|
baa56aed2c | ||
|
|
6f767367e4 | ||
|
|
0e4124f94c | ||
|
|
30503d0a3f | ||
|
|
6c9164f67d | ||
|
|
906b7a8f2c | ||
|
|
43499b13d8 | ||
|
|
7d6e56670f | ||
|
|
dba337f148 | ||
|
|
a825bc9540 | ||
|
|
f5486a8297 | ||
|
|
225e0a4de3 | ||
|
|
0ecfdc1325 | ||
|
|
628409d233 | ||
|
|
51d968314f | ||
|
|
b4e2cbc6e5 | ||
|
|
7323beea8e | ||
|
|
e4bb311144 | ||
|
|
288546659d | ||
|
|
822224e212 | ||
|
|
6b23950b65 | ||
|
|
1558f21e49 | ||
|
|
a6aee8b904 | ||
|
|
27f4034bdc | ||
|
|
e43b968fbe | ||
|
|
48eb720556 | ||
|
|
30e4d74db6 | ||
|
|
75840896ee | ||
|
|
9657bda86d | ||
|
|
4536029d42 | ||
|
|
a35654d047 | ||
|
|
f6a01a1af3 | ||
|
|
c2bca954e0 | ||
|
|
21701c3c3d | ||
|
|
7f4723b3f8 | ||
|
|
619ca93421 | ||
|
|
cea2981512 | ||
|
|
1f30e3a4b7 | ||
|
|
20e8fdd049 | ||
|
|
3df8313d69 | ||
|
|
95c9ea92c4 | ||
|
|
7ddebb099d | ||
|
|
6498fd1aab | ||
|
|
9d7d822da3 | ||
|
|
b8110d97d1 | ||
|
|
2d9373b0f1 | ||
|
|
cc7577a648 | ||
|
|
f3f3d7df5b | ||
|
|
0bafea6d51 | ||
|
|
5abb74eddd | ||
|
|
23d7eaccab | ||
|
|
2cb008e8ed | ||
|
|
b5c3610113 | ||
|
|
f23c7fc4fc | ||
|
|
3dc1ad6437 | ||
|
|
4dc0bef62b | ||
|
|
8b3c52b9e8 | ||
|
|
fc74300ad0 | ||
|
|
28f8a38e00 | ||
|
|
c318ce9695 | ||
|
|
5b36eaacff | ||
|
|
c9f7ca23a8 | ||
|
|
57e0db0f65 |
1
.github/CONTRIBUTING.md
vendored
1
.github/CONTRIBUTING.md
vendored
@@ -13,6 +13,7 @@ To get started, execute the following:
|
||||
```
|
||||
git clone https://github.com/vercel/vercel
|
||||
cd vercel
|
||||
corepack enable
|
||||
yarn install
|
||||
yarn bootstrap
|
||||
yarn build
|
||||
|
||||
17
.github/workflows/cancel.yml
vendored
17
.github/workflows/cancel.yml
vendored
@@ -1,17 +0,0 @@
|
||||
name: Cancel
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- '**'
|
||||
- '!main'
|
||||
|
||||
jobs:
|
||||
cancel:
|
||||
name: 'Cancel Previous Runs'
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 2
|
||||
steps:
|
||||
- uses: styfle/cancel-workflow-action@0.9.1
|
||||
with:
|
||||
workflow_id: test.yml, test-integration-cli.yml, test-unit.yml
|
||||
access_token: ${{ github.token }}
|
||||
11
.github/workflows/publish.yml
vendored
11
.github/workflows/publish.yml
vendored
@@ -7,13 +7,20 @@ on:
|
||||
tags:
|
||||
- '!*'
|
||||
|
||||
env:
|
||||
TURBO_REMOTE_ONLY: 'true'
|
||||
TURBO_TEAM: 'vercel'
|
||||
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
|
||||
|
||||
jobs:
|
||||
publish:
|
||||
name: Publish
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v1
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Check Release
|
||||
id: check-release
|
||||
run: |
|
||||
@@ -32,8 +39,10 @@ jobs:
|
||||
- name: Setup Node
|
||||
if: ${{ steps.check-release.outputs.IS_RELEASE == 'true' }}
|
||||
uses: actions/setup-node@v3
|
||||
timeout-minutes: 5 # See https://github.com/actions/cache/issues/810
|
||||
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
|
||||
|
||||
31
.github/workflows/test-integration-cli.yml
vendored
31
.github/workflows/test-integration-cli.yml
vendored
@@ -8,6 +8,15 @@ on:
|
||||
- '!*'
|
||||
pull_request:
|
||||
|
||||
env:
|
||||
TURBO_REMOTE_ONLY: 'true'
|
||||
TURBO_TEAM: 'vercel'
|
||||
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: ${{ github.ref != 'refs/heads/main' }}
|
||||
|
||||
jobs:
|
||||
test:
|
||||
name: CLI
|
||||
@@ -19,28 +28,20 @@ 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
|
||||
timeout-minutes: 5 # See https://github.com/actions/cache/issues/810
|
||||
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
|
||||
env:
|
||||
VERCEL_TEAM_TOKEN: ${{ secrets.VERCEL_TEAM_TOKEN }}
|
||||
VERCEL_REGISTRATION_URL: ${{ secrets.VERCEL_REGISTRATION_URL }}
|
||||
VERCEL_TEST_TOKEN: ${{ secrets.VERCEL_TEST_TOKEN }}
|
||||
VERCEL_TEST_REGISTRATION_URL: ${{ secrets.VERCEL_TEST_REGISTRATION_URL }}
|
||||
|
||||
29
.github/workflows/test-unit.yml
vendored
29
.github/workflows/test-unit.yml
vendored
@@ -8,6 +8,15 @@ on:
|
||||
- '!*'
|
||||
pull_request:
|
||||
|
||||
env:
|
||||
TURBO_REMOTE_ONLY: 'true'
|
||||
TURBO_TEAM: 'vercel'
|
||||
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: ${{ github.ref != 'refs/heads/main' }}
|
||||
|
||||
jobs:
|
||||
test:
|
||||
name: Unit
|
||||
@@ -19,25 +28,17 @@ 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
|
||||
timeout-minutes: 5 # See https://github.com/actions/cache/issues/810
|
||||
with:
|
||||
node-version: ${{ matrix.node }}
|
||||
cache: 'yarn'
|
||||
- run: yarn install --network-timeout 1000000 --frozen-lockfile
|
||||
- run: yarn run build
|
||||
- run: yarn run lint
|
||||
|
||||
26
.github/workflows/test.yml
vendored
26
.github/workflows/test.yml
vendored
@@ -10,6 +10,13 @@ on:
|
||||
|
||||
env:
|
||||
NODE_VERSION: '14'
|
||||
TURBO_REMOTE_ONLY: 'true'
|
||||
TURBO_TEAM: 'vercel'
|
||||
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: ${{ github.ref != 'refs/heads/main' }}
|
||||
|
||||
jobs:
|
||||
setup:
|
||||
@@ -20,9 +27,13 @@ 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
|
||||
timeout-minutes: 5 # See https://github.com/actions/cache/issues/810
|
||||
with:
|
||||
node-version: ${{ env.NODE_VERSION }}
|
||||
cache: 'yarn'
|
||||
@@ -52,12 +63,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
|
||||
@@ -65,6 +70,7 @@ jobs:
|
||||
with:
|
||||
go-version: '1.13.15'
|
||||
- uses: actions/setup-node@v3
|
||||
timeout-minutes: 5 # See https://github.com/actions/cache/issues/810
|
||||
with:
|
||||
node-version: ${{ env.NODE_VERSION }}
|
||||
cache: 'yarn'
|
||||
@@ -84,8 +90,8 @@ jobs:
|
||||
shell: bash
|
||||
env:
|
||||
VERCEL_CLI_VERSION: ${{ needs.setup.outputs.dplUrl }}/tarballs/vercel.tgz
|
||||
VERCEL_TEAM_TOKEN: ${{ secrets.VERCEL_TEAM_TOKEN }}
|
||||
VERCEL_REGISTRATION_URL: ${{ secrets.VERCEL_REGISTRATION_URL }}
|
||||
VERCEL_TEST_TOKEN: ${{ secrets.VERCEL_TEST_TOKEN }}
|
||||
VERCEL_TEST_REGISTRATION_URL: ${{ secrets.VERCEL_TEST_REGISTRATION_URL }}
|
||||
FORCE_COLOR: '1'
|
||||
|
||||
conclusion:
|
||||
|
||||
@@ -380,8 +380,8 @@ This is a [class](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Refere
|
||||
|
||||
This is an abstract enumeration type that is implemented by one of the following possible `String` values:
|
||||
|
||||
- `nodejs16.x`
|
||||
- `nodejs14.x`
|
||||
- `nodejs12.x`
|
||||
- `go1.x`
|
||||
- `java11`
|
||||
- `python3.9`
|
||||
|
||||
@@ -19,11 +19,9 @@
|
||||
|
||||
## Vercel
|
||||
|
||||
Vercel is a platform for **static sites and frontend frameworks**, built to integrate with your headless content, commerce, or database.
|
||||
Vercel is the platform for frontend developers, providing the speed and reliability innovators need to create at the moment of inspiration.
|
||||
|
||||
We provide a **frictionless developer experience** to take care of the hard things: deploy instantly, scale automatically, and serve personalized content around the globe.
|
||||
|
||||
We make it easy for frontend teams to **develop, preview, and ship** delightful user experiences, where performance is the default.
|
||||
We enable teams to iterate quickly and develop, preview, and ship delightful user experiences. Vercel has zero-configuration support for 35+ frontend frameworks and integrates with your headless content, commerce, or database of choice.
|
||||
|
||||
## Deploy
|
||||
|
||||
|
||||
@@ -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,18 @@ 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 }
|
||||
);
|
||||
|
||||
await fs.cp(
|
||||
join(repoRoot, 'packages', 'fs-detectors', 'logos'),
|
||||
join(pubDir, 'monorepo-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 +52,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 +62,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.');
|
||||
|
||||
@@ -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;
|
||||
});
|
||||
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
"version": "0.0.0",
|
||||
"private": true,
|
||||
"license": "Apache-2.0",
|
||||
"packageManager": "yarn@1.22.19",
|
||||
"workspaces": {
|
||||
"packages": [
|
||||
"packages/*"
|
||||
@@ -34,7 +35,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",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vercel/build-utils",
|
||||
"version": "5.4.2",
|
||||
"version": "5.5.5",
|
||||
"license": "MIT",
|
||||
"main": "./dist/index.js",
|
||||
"types": "./dist/index.d.js",
|
||||
|
||||
34
packages/build-utils/src/clone-env.ts
Normal file
34
packages/build-utils/src/clone-env.ts
Normal 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;
|
||||
}, {});
|
||||
}
|
||||
@@ -38,6 +38,9 @@ export class EdgeFunction {
|
||||
*/
|
||||
assets?: { name: string; path: string }[];
|
||||
|
||||
/** The regions where the edge function will be executed on */
|
||||
regions?: 'auto' | string[] | 'all' | 'default';
|
||||
|
||||
constructor(params: Omit<EdgeFunction, 'type'>) {
|
||||
this.type = 'EdgeFunction';
|
||||
this.name = params.name;
|
||||
@@ -46,5 +49,6 @@ export class EdgeFunction {
|
||||
this.files = params.files;
|
||||
this.envVarsInUse = params.envVarsInUse;
|
||||
this.assets = params.assets;
|
||||
this.regions = params.regions;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -61,14 +61,14 @@ export function getPrettyError(obj: {
|
||||
}
|
||||
|
||||
return new NowBuildError({
|
||||
code: 'DEV_VALIDATE_CONFIG',
|
||||
code: 'INVALID_VERCEL_CONFIG',
|
||||
message: message,
|
||||
link: prop ? `${docsUrl}#project/${prop.toLowerCase()}` : docsUrl,
|
||||
action: 'View Documentation',
|
||||
});
|
||||
} catch (e) {
|
||||
return new NowBuildError({
|
||||
code: 'DEV_VALIDATE_CONFIG',
|
||||
code: 'INVALID_VERCEL_CONFIG',
|
||||
message: `Failed to validate configuration.`,
|
||||
link: docsUrl,
|
||||
action: 'View Documentation',
|
||||
|
||||
@@ -5,7 +5,13 @@ import path from 'path';
|
||||
import Sema from 'async-sema';
|
||||
import { FileBase } from './types';
|
||||
|
||||
const semaToPreventEMFILE = new Sema(20);
|
||||
const DEFAULT_SEMA = 20;
|
||||
const semaToPreventEMFILE = new Sema(
|
||||
parseInt(
|
||||
process.env.VERCEL_INTERNAL_FILE_FS_REF_SEMA || String(DEFAULT_SEMA),
|
||||
10
|
||||
) || DEFAULT_SEMA
|
||||
);
|
||||
|
||||
interface FileFsRefOptions {
|
||||
mode?: number;
|
||||
|
||||
@@ -12,7 +12,13 @@ interface FileRefOptions {
|
||||
mutable?: boolean;
|
||||
}
|
||||
|
||||
const semaToDownloadFromS3 = new Sema(5);
|
||||
const DEFAULT_SEMA = 5;
|
||||
const semaToDownloadFromS3 = new Sema(
|
||||
parseInt(
|
||||
process.env.VERCEL_INTERNAL_FILE_REF_SEMA || String(DEFAULT_SEMA),
|
||||
10
|
||||
) || DEFAULT_SEMA
|
||||
);
|
||||
|
||||
class BailableError extends Error {
|
||||
public bail: boolean;
|
||||
|
||||
@@ -10,7 +10,7 @@ const allOptions = [
|
||||
major: 12,
|
||||
range: '12.x',
|
||||
runtime: 'nodejs12.x',
|
||||
discontinueDate: new Date('2022-10-01'),
|
||||
discontinueDate: new Date('2022-10-03'),
|
||||
},
|
||||
{
|
||||
major: 10,
|
||||
|
||||
@@ -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) {
|
||||
@@ -250,33 +251,24 @@ export async function getNodeVersion(
|
||||
meta: Meta = {}
|
||||
): Promise<NodeVersion> {
|
||||
const latest = getLatestNodeVersion();
|
||||
if (meta && meta.isDev) {
|
||||
if (meta.isDev) {
|
||||
// Use the system-installed version of `node` in PATH for `vercel dev`
|
||||
return { ...latest, runtime: 'nodejs' };
|
||||
}
|
||||
const { packageJson } = await scanParentDirs(destPath, true);
|
||||
let { nodeVersion } = config;
|
||||
let isAuto = true;
|
||||
if (packageJson && packageJson.engines && packageJson.engines.node) {
|
||||
if (packageJson?.engines?.node) {
|
||||
const { node } = packageJson.engines;
|
||||
if (
|
||||
nodeVersion &&
|
||||
validRange(node) &&
|
||||
!intersects(nodeVersion, node) &&
|
||||
!meta.isDev
|
||||
) {
|
||||
if (nodeVersion && validRange(node) && !intersects(nodeVersion, node)) {
|
||||
console.warn(
|
||||
`Warning: Due to "engines": { "node": "${node}" } in your \`package.json\` file, the Node.js Version defined in your Project Settings ("${nodeVersion}") will not apply. Learn More: http://vercel.link/node-version`
|
||||
);
|
||||
} else if (coerce(node)?.raw === node && !meta.isDev) {
|
||||
} else if (coerce(node)?.raw === node) {
|
||||
console.warn(
|
||||
`Warning: Detected "engines": { "node": "${node}" } in your \`package.json\` with major.minor.patch, but only major Node.js Version can be selected. Learn More: http://vercel.link/node-version`
|
||||
);
|
||||
} else if (
|
||||
validRange(node) &&
|
||||
intersects(`${latest.major + 1}.x`, node) &&
|
||||
!meta.isDev
|
||||
) {
|
||||
} else if (validRange(node) && intersects(`${latest.major + 1}.x`, node)) {
|
||||
console.warn(
|
||||
`Warning: Detected "engines": { "node": "${node}" } in your \`package.json\` that will automatically upgrade when a new major Node.js Version is released. Learn More: http://vercel.link/node-version`
|
||||
);
|
||||
@@ -449,7 +441,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 +450,30 @@ export async function runNpmInstall(
|
||||
env,
|
||||
});
|
||||
let commandArgs: string[];
|
||||
const isPotentiallyBrokenNpm =
|
||||
cliType === 'npm' &&
|
||||
(nodeVersion?.major === 16 ||
|
||||
opts.env.PATH?.includes('/node16/bin-npm7')) &&
|
||||
!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 +490,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 +620,7 @@ export async function runPackageJsonScript(
|
||||
cliType,
|
||||
lockfileVersion,
|
||||
nodeVersion: undefined,
|
||||
env: {
|
||||
...process.env,
|
||||
...spawnOpts?.env,
|
||||
},
|
||||
env: cloneEnv(process.env, spawnOpts?.env),
|
||||
}),
|
||||
};
|
||||
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -342,6 +342,7 @@ export interface BuilderV2 {
|
||||
version: 2;
|
||||
build: BuildV2;
|
||||
prepareCache?: PrepareCache;
|
||||
shouldServe?: ShouldServe;
|
||||
}
|
||||
|
||||
export interface BuilderV3 {
|
||||
|
||||
2
packages/build-utils/test/fixtures/14-npm-6-legacy-peer-deps/.gitignore
vendored
Normal file
2
packages/build-utils/test/fixtures/14-npm-6-legacy-peer-deps/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
node_modules
|
||||
.vercel
|
||||
64
packages/build-utils/test/fixtures/14-npm-6-legacy-peer-deps/package-lock.json
generated
vendored
Normal file
64
packages/build-utils/test/fixtures/14-npm-6-legacy-peer-deps/package-lock.json
generated
vendored
Normal file
@@ -0,0 +1,64 @@
|
||||
{
|
||||
"requires": true,
|
||||
"lockfileVersion": 1,
|
||||
"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=="
|
||||
}
|
||||
}
|
||||
}
|
||||
11
packages/build-utils/test/fixtures/14-npm-6-legacy-peer-deps/package.json
vendored
Normal file
11
packages/build-utils/test/fixtures/14-npm-6-legacy-peer-deps/package.json
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"build": "mkdir -p public && echo 'legacy peer deps' > public/index.txt"
|
||||
},
|
||||
"packageManager": "npm@6.14.17",
|
||||
"dependencies": {
|
||||
"swr": "1.3.0",
|
||||
"react": "16.8.0"
|
||||
}
|
||||
}
|
||||
3
packages/build-utils/test/fixtures/14-npm-6-legacy-peer-deps/probes.json
vendored
Normal file
3
packages/build-utils/test/fixtures/14-npm-6-legacy-peer-deps/probes.json
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"probes": [{ "path": "/", "mustContain": "legacy peer deps" }]
|
||||
}
|
||||
2
packages/build-utils/test/fixtures/15-npm-8-legacy-peer-deps/.gitignore
vendored
Normal file
2
packages/build-utils/test/fixtures/15-npm-8-legacy-peer-deps/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
node_modules
|
||||
.vercel
|
||||
143
packages/build-utils/test/fixtures/15-npm-8-legacy-peer-deps/package-lock.json
generated
vendored
Normal file
143
packages/build-utils/test/fixtures/15-npm-8-legacy-peer-deps/package-lock.json
generated
vendored
Normal 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=="
|
||||
}
|
||||
}
|
||||
}
|
||||
11
packages/build-utils/test/fixtures/15-npm-8-legacy-peer-deps/package.json
vendored
Normal file
11
packages/build-utils/test/fixtures/15-npm-8-legacy-peer-deps/package.json
vendored
Normal 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"
|
||||
}
|
||||
}
|
||||
3
packages/build-utils/test/fixtures/15-npm-8-legacy-peer-deps/probes.json
vendored
Normal file
3
packages/build-utils/test/fixtures/15-npm-8-legacy-peer-deps/probes.json
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"probes": [{ "path": "/", "mustContain": "legacy peer deps" }]
|
||||
}
|
||||
1
packages/build-utils/test/fixtures/20-npm-7/package-lock.json
generated
vendored
1
packages/build-utils/test/fixtures/20-npm-7/package-lock.json
generated
vendored
@@ -5,6 +5,7 @@
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "20-npm-7",
|
||||
"version": "1.0.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
117
packages/build-utils/test/unit.clone-env.test.ts
vendored
Normal file
117
packages/build-utils/test/unit.clone-env.test.ts
vendored
Normal 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',
|
||||
});
|
||||
});
|
||||
197
packages/build-utils/test/unit.run-npm-install.test.ts
vendored
Normal file
197
packages/build-utils/test/unit.run-npm-install.test.ts
vendored
Normal file
@@ -0,0 +1,197 @@
|
||||
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 = 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 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 include peer dependencies when VERCEL_NPM_LEGACY_PEER_DEPS=1 on node14 and npm7+', 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',
|
||||
'--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 and npm6', async () => {
|
||||
const fixture = path.join(__dirname, 'fixtures', '14-npm-6-legacy-peer-deps');
|
||||
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),
|
||||
});
|
||||
});
|
||||
93
packages/build-utils/test/unit.test.ts
vendored
93
packages/build-utils/test/unit.test.ts
vendored
@@ -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);
|
||||
@@ -218,10 +216,6 @@ it('should download symlinks even with incorrect file', async () => {
|
||||
});
|
||||
|
||||
it('should only match supported node versions, otherwise throw an error', async () => {
|
||||
expect(await getSupportedNodeVersion('12.x', false)).toHaveProperty(
|
||||
'major',
|
||||
12
|
||||
);
|
||||
expect(await getSupportedNodeVersion('14.x', false)).toHaveProperty(
|
||||
'major',
|
||||
14
|
||||
@@ -242,10 +236,6 @@ it('should only match supported node versions, otherwise throw an error', async
|
||||
await expectBuilderError(getSupportedNodeVersion('foo', true), autoMessage);
|
||||
await expectBuilderError(getSupportedNodeVersion('=> 10', true), autoMessage);
|
||||
|
||||
expect(await getSupportedNodeVersion('12.x', true)).toHaveProperty(
|
||||
'major',
|
||||
12
|
||||
);
|
||||
expect(await getSupportedNodeVersion('14.x', true)).toHaveProperty(
|
||||
'major',
|
||||
14
|
||||
@@ -275,21 +265,21 @@ it('should only match supported node versions, otherwise throw an error', async
|
||||
|
||||
it('should match all semver ranges', async () => {
|
||||
// See https://docs.npmjs.com/files/package.json#engines
|
||||
expect(await getSupportedNodeVersion('12.0.0')).toHaveProperty('major', 12);
|
||||
expect(await getSupportedNodeVersion('12.x')).toHaveProperty('major', 12);
|
||||
expect(await getSupportedNodeVersion('14.0.0')).toHaveProperty('major', 14);
|
||||
expect(await getSupportedNodeVersion('14.x')).toHaveProperty('major', 14);
|
||||
expect(await getSupportedNodeVersion('>=10')).toHaveProperty('major', 16);
|
||||
expect(await getSupportedNodeVersion('>=10.3.0')).toHaveProperty('major', 16);
|
||||
expect(await getSupportedNodeVersion('11.5.0 - 12.5.0')).toHaveProperty(
|
||||
expect(await getSupportedNodeVersion('16.5.0 - 16.9.0')).toHaveProperty(
|
||||
'major',
|
||||
12
|
||||
16
|
||||
);
|
||||
expect(await getSupportedNodeVersion('>=9.5.0 <=12.5.0')).toHaveProperty(
|
||||
expect(await getSupportedNodeVersion('>=9.5.0 <=14.5.0')).toHaveProperty(
|
||||
'major',
|
||||
12
|
||||
14
|
||||
);
|
||||
expect(await getSupportedNodeVersion('~12.5.0')).toHaveProperty('major', 12);
|
||||
expect(await getSupportedNodeVersion('^12.5.0')).toHaveProperty('major', 12);
|
||||
expect(await getSupportedNodeVersion('12.5.0 - 14.5.0')).toHaveProperty(
|
||||
expect(await getSupportedNodeVersion('~14.5.0')).toHaveProperty('major', 14);
|
||||
expect(await getSupportedNodeVersion('^14.5.0')).toHaveProperty('major', 14);
|
||||
expect(await getSupportedNodeVersion('14.5.0 - 14.20.0')).toHaveProperty(
|
||||
'major',
|
||||
14
|
||||
);
|
||||
@@ -436,8 +426,8 @@ it('should warn for deprecated versions, soon to be discontinued', async () => {
|
||||
expect(warningMessages).toStrictEqual([
|
||||
'Error: Node.js version 10.x has reached End-of-Life. Deployments created on or after 2021-04-20 will fail to build. Please set "engines": { "node": "16.x" } in your `package.json` file to use Node.js 16.',
|
||||
'Error: Node.js version 10.x has reached End-of-Life. Deployments created on or after 2021-04-20 will fail to build. Please set Node.js Version to 16.x in your Project Settings to use Node.js 16.',
|
||||
'Error: Node.js version 12.x has reached End-of-Life. Deployments created on or after 2022-10-01 will fail to build. Please set "engines": { "node": "16.x" } in your `package.json` file to use Node.js 16.',
|
||||
'Error: Node.js version 12.x has reached End-of-Life. Deployments created on or after 2022-10-01 will fail to build. Please set Node.js Version to 16.x in your Project Settings to use Node.js 16.',
|
||||
'Error: Node.js version 12.x has reached End-of-Life. Deployments created on or after 2022-10-03 will fail to build. Please set "engines": { "node": "16.x" } in your `package.json` file to use Node.js 16.',
|
||||
'Error: Node.js version 12.x has reached End-of-Life. Deployments created on or after 2022-10-03 will fail to build. Please set Node.js Version to 16.x in your Project Settings to use Node.js 16.',
|
||||
]);
|
||||
|
||||
global.Date.now = realDateNow;
|
||||
@@ -578,53 +568,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.',
|
||||
]);
|
||||
});
|
||||
|
||||
@@ -10,11 +10,9 @@
|
||||
|
||||
## Usage
|
||||
|
||||
Vercel is a platform for **static sites and frontend frameworks**, built to integrate with your headless content, commerce, or database.
|
||||
Vercel is the platform for frontend developers, providing the speed and reliability innovators need to create at the moment of inspiration.
|
||||
|
||||
We provide a **frictionless developer experience** to take care of the hard things: deploy instantly, scale automatically, and serve personalized content around the globe.
|
||||
|
||||
We make it easy for frontend teams to **develop, preview, and ship** delightful user experiences, where performance is the default.
|
||||
We enable teams to iterate quickly and develop, preview, and ship delightful user experiences. Vercel has zero-configuration support for 35+ frontend frameworks and integrates with your headless content, commerce, or database of choice.
|
||||
|
||||
To install the latest version of Vercel CLI, run this command:
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "vercel",
|
||||
"version": "28.2.3",
|
||||
"version": "28.4.11",
|
||||
"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.5.5",
|
||||
"@vercel/go": "2.2.13",
|
||||
"@vercel/hydrogen": "0.0.26",
|
||||
"@vercel/next": "3.2.5",
|
||||
"@vercel/node": "2.5.25",
|
||||
"@vercel/python": "3.1.22",
|
||||
"@vercel/redwood": "1.0.31",
|
||||
"@vercel/remix": "1.0.32",
|
||||
"@vercel/ruby": "1.3.39",
|
||||
"@vercel/static-build": "1.0.32",
|
||||
"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.13",
|
||||
"@vercel/frameworks": "1.1.8",
|
||||
"@vercel/fs-detectors": "3.4.5",
|
||||
"@vercel/fun": "1.0.4",
|
||||
"@vercel/ncc": "0.24.0",
|
||||
"@zeit/source-map-support": "0.6.2",
|
||||
|
||||
@@ -2,7 +2,6 @@ import open from 'open';
|
||||
import boxen from 'boxen';
|
||||
import execa from 'execa';
|
||||
import plural from 'pluralize';
|
||||
import inquirer from 'inquirer';
|
||||
import { resolve } from 'path';
|
||||
import chalk, { Chalk } from 'chalk';
|
||||
import { URLSearchParams, parse } from 'url';
|
||||
@@ -150,7 +149,9 @@ export default async function main(client: Client): Promise<number> {
|
||||
|
||||
if (badDeployment) {
|
||||
if (badDeployment instanceof Error) {
|
||||
badDeployment.message += ` "${bad}"`;
|
||||
badDeployment.message += ` when requesting bad deployment "${normalizeURL(
|
||||
bad
|
||||
)}"`;
|
||||
output.prettyError(badDeployment);
|
||||
return 1;
|
||||
}
|
||||
@@ -165,7 +166,9 @@ export default async function main(client: Client): Promise<number> {
|
||||
|
||||
if (goodDeployment) {
|
||||
if (goodDeployment instanceof Error) {
|
||||
goodDeployment.message += ` "${good}"`;
|
||||
goodDeployment.message += ` when requesting good deployment "${normalizeURL(
|
||||
good
|
||||
)}"`;
|
||||
output.prettyError(goodDeployment);
|
||||
return 1;
|
||||
}
|
||||
@@ -226,7 +229,8 @@ export default async function main(client: Client): Promise<number> {
|
||||
// If we have the "good" deployment in this chunk, then we're done
|
||||
for (let i = 0; i < newDeployments.length; i++) {
|
||||
if (newDeployments[i].url === good) {
|
||||
newDeployments = newDeployments.slice(0, i + 1);
|
||||
// grab all deployments up until the good one
|
||||
newDeployments = newDeployments.slice(0, i);
|
||||
next = undefined;
|
||||
break;
|
||||
}
|
||||
@@ -316,7 +320,7 @@ export default async function main(client: Client): Promise<number> {
|
||||
if (openEnabled) {
|
||||
await open(testUrl);
|
||||
}
|
||||
const answer = await inquirer.prompt({
|
||||
const answer = await client.prompt({
|
||||
type: 'expand',
|
||||
name: 'action',
|
||||
message: 'Select an action:',
|
||||
|
||||
@@ -3,6 +3,7 @@ import chalk from 'chalk';
|
||||
import dotenv from 'dotenv';
|
||||
import { join, normalize, relative, resolve } from 'path';
|
||||
import {
|
||||
getDiscontinuedNodeVersions,
|
||||
normalizePath,
|
||||
Files,
|
||||
FileFsRef,
|
||||
@@ -25,6 +26,7 @@ import {
|
||||
MergeRoutesProps,
|
||||
Route,
|
||||
} from '@vercel/routing-utils';
|
||||
import { fileNameSymbol } from '@vercel/client';
|
||||
import type { VercelConfig } from '@vercel/client';
|
||||
|
||||
import pull from './pull';
|
||||
@@ -54,6 +56,7 @@ import { importBuilders } from '../util/build/import-builders';
|
||||
import { initCorepack, cleanupCorepack } from '../util/build/corepack';
|
||||
import { sortBuilders } from '../util/build/sort-builders';
|
||||
import { toEnumerableError } from '../util/error';
|
||||
import { validateConfig } from '../util/validate-config';
|
||||
|
||||
type BuildResult = BuildResultV2 | BuildResultV3;
|
||||
|
||||
@@ -232,7 +235,8 @@ export default async function main(client: Client): Promise<number> {
|
||||
process.env.VERCEL = '1';
|
||||
process.env.NOW_BUILDER = '1';
|
||||
|
||||
return await doBuild(client, project, buildsJson, cwd, outputDir);
|
||||
await doBuild(client, project, buildsJson, cwd, outputDir);
|
||||
return 0;
|
||||
} catch (err: any) {
|
||||
output.prettyError(err);
|
||||
|
||||
@@ -265,23 +269,36 @@ async function doBuild(
|
||||
buildsJson: BuildsManifest,
|
||||
cwd: string,
|
||||
outputDir: string
|
||||
): Promise<number> {
|
||||
): Promise<void> {
|
||||
const { output } = client;
|
||||
const workPath = join(cwd, project.settings.rootDirectory || '.');
|
||||
|
||||
// Load `package.json` and `vercel.json` files
|
||||
const [pkg, vercelConfig] = await Promise.all([
|
||||
const [pkg, vercelConfig, nowConfig] = await Promise.all([
|
||||
readJSONFile<PackageJson>(join(workPath, 'package.json')),
|
||||
readJSONFile<VercelConfig>(join(workPath, 'vercel.json')).then(
|
||||
config => config || readJSONFile<VercelConfig>(join(workPath, 'now.json'))
|
||||
),
|
||||
readJSONFile<VercelConfig>(join(workPath, 'vercel.json')),
|
||||
readJSONFile<VercelConfig>(join(workPath, 'now.json')),
|
||||
]);
|
||||
|
||||
if (pkg instanceof CantParseJSONFile) throw pkg;
|
||||
if (vercelConfig instanceof CantParseJSONFile) throw vercelConfig;
|
||||
if (nowConfig instanceof CantParseJSONFile) throw nowConfig;
|
||||
|
||||
if (vercelConfig) {
|
||||
vercelConfig[fileNameSymbol] = 'vercel.json';
|
||||
} else if (nowConfig) {
|
||||
nowConfig[fileNameSymbol] = 'now.json';
|
||||
}
|
||||
|
||||
const localConfig = vercelConfig || nowConfig || {};
|
||||
const validateError = validateConfig(localConfig);
|
||||
|
||||
if (validateError) {
|
||||
throw validateError;
|
||||
}
|
||||
|
||||
const projectSettings = {
|
||||
...project.settings,
|
||||
...pickOverrides(vercelConfig || {}),
|
||||
...pickOverrides(localConfig),
|
||||
};
|
||||
|
||||
// Get a list of source files
|
||||
@@ -289,12 +306,12 @@ async function doBuild(
|
||||
normalizePath(relative(workPath, f))
|
||||
);
|
||||
|
||||
const routesResult = getTransformedRoutes(vercelConfig || {});
|
||||
const routesResult = getTransformedRoutes(localConfig);
|
||||
if (routesResult.error) {
|
||||
throw routesResult.error;
|
||||
}
|
||||
|
||||
if (vercelConfig?.builds && vercelConfig.functions) {
|
||||
if (localConfig.builds && localConfig.functions) {
|
||||
throw new NowBuildError({
|
||||
code: 'bad_request',
|
||||
message:
|
||||
@@ -303,7 +320,7 @@ async function doBuild(
|
||||
});
|
||||
}
|
||||
|
||||
let builds = vercelConfig?.builds || [];
|
||||
let builds = localConfig.builds || [];
|
||||
let zeroConfigRoutes: Route[] = [];
|
||||
let isZeroConfig = false;
|
||||
|
||||
@@ -318,7 +335,7 @@ async function doBuild(
|
||||
|
||||
// Detect the Vercel Builders that will need to be invoked
|
||||
const detectedBuilders = await detectBuilders(files, pkg, {
|
||||
...vercelConfig,
|
||||
...localConfig,
|
||||
projectSettings,
|
||||
ignoreBuildScript: true,
|
||||
featHandleMiss: true,
|
||||
@@ -395,13 +412,10 @@ async function doBuild(
|
||||
})
|
||||
);
|
||||
buildsJson.builds = Array.from(buildsJsonBuilds.values());
|
||||
const buildsJsonPath = join(outputDir, 'builds.json');
|
||||
const writeBuildsJsonPromise = fs.writeJSON(buildsJsonPath, buildsJson, {
|
||||
await fs.writeJSON(join(outputDir, 'builds.json'), buildsJson, {
|
||||
spaces: 2,
|
||||
});
|
||||
|
||||
ops.push(writeBuildsJsonPromise);
|
||||
|
||||
// The `meta` config property is re-used for each Builder
|
||||
// invocation so that Builders can share state between
|
||||
// subsequent entrypoint builds.
|
||||
@@ -454,6 +468,25 @@ async function doBuild(
|
||||
);
|
||||
const buildResult = await builder.build(buildOptions);
|
||||
|
||||
if (
|
||||
buildResult &&
|
||||
'output' in buildResult &&
|
||||
'runtime' in buildResult.output &&
|
||||
'type' in buildResult.output &&
|
||||
buildResult.output.type === 'Lambda'
|
||||
) {
|
||||
const lambdaRuntime = buildResult.output.runtime;
|
||||
if (
|
||||
getDiscontinuedNodeVersions().some(o => o.runtime === lambdaRuntime)
|
||||
) {
|
||||
throw new NowBuildError({
|
||||
code: 'NODEJS_DISCONTINUED_VERSION',
|
||||
message: `The Runtime "${build.use}" is using "${lambdaRuntime}", which is discontinued. Please upgrade your Runtime to a more recent version or consult the author for more details.`,
|
||||
link: 'https://github.com/vercel/vercel/blob/main/DEVELOPING_A_RUNTIME.md#lambdaruntime',
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Store the build result to generate the final `config.json` after
|
||||
// all builds have completed
|
||||
buildResults.set(build, buildResult);
|
||||
@@ -466,7 +499,7 @@ async function doBuild(
|
||||
build,
|
||||
builder,
|
||||
builderPkg,
|
||||
vercelConfig
|
||||
localConfig
|
||||
).then(
|
||||
override => {
|
||||
if (override) overrides.push(override);
|
||||
@@ -475,26 +508,11 @@ async function doBuild(
|
||||
)
|
||||
);
|
||||
} catch (err: any) {
|
||||
output.prettyError(err);
|
||||
|
||||
const writeConfigJsonPromise = fs.writeJSON(
|
||||
join(outputDir, 'config.json'),
|
||||
{ version: 3 },
|
||||
{ spaces: 2 }
|
||||
);
|
||||
|
||||
await Promise.all([writeBuildsJsonPromise, writeConfigJsonPromise]);
|
||||
|
||||
const buildJsonBuild = buildsJsonBuilds.get(build);
|
||||
if (buildJsonBuild) {
|
||||
buildJsonBuild.error = toEnumerableError(err);
|
||||
|
||||
await fs.writeJSON(buildsJsonPath, buildsJson, {
|
||||
spaces: 2,
|
||||
});
|
||||
}
|
||||
|
||||
return 1;
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -555,7 +573,7 @@ async function doBuild(
|
||||
builds: builderRoutes,
|
||||
});
|
||||
|
||||
const mergedImages = mergeImages(buildResults.values());
|
||||
const mergedImages = mergeImages(localConfig.images, buildResults.values());
|
||||
const mergedWildcard = mergeWildcard(buildResults.values());
|
||||
const mergedOverrides: Record<string, PathOverride> =
|
||||
overrides.length > 0 ? Object.assign({}, ...overrides) : undefined;
|
||||
@@ -581,8 +599,6 @@ async function doBuild(
|
||||
emoji('success')
|
||||
)}\n`
|
||||
);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
function expandBuild(files: string[], build: Builder): Builder[] {
|
||||
@@ -624,9 +640,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);
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
};
|
||||
|
||||
@@ -23,6 +23,7 @@ import type {
|
||||
} from '../types';
|
||||
import { sharedPromise } from './promise';
|
||||
import { APIError } from './errors-ts';
|
||||
import { normalizeError } from './is-error';
|
||||
|
||||
const isSAMLError = (v: any): v is SAMLError => {
|
||||
return v && v.saml;
|
||||
@@ -146,10 +147,15 @@ export default class Client extends EventEmitter implements Stdio {
|
||||
const error = await responseError(res);
|
||||
|
||||
if (isSAMLError(error)) {
|
||||
// A SAML error means the token is expired, or is not
|
||||
// designated for the requested team, so the user needs
|
||||
// to re-authenticate
|
||||
await this.reauthenticate(error);
|
||||
try {
|
||||
// A SAML error means the token is expired, or is not
|
||||
// designated for the requested team, so the user needs
|
||||
// to re-authenticate
|
||||
await this.reauthenticate(error);
|
||||
} catch (reauthError) {
|
||||
// there's no sense in retrying
|
||||
return bail(normalizeError(reauthError));
|
||||
}
|
||||
} else if (res.status >= 400 && res.status < 500) {
|
||||
// Any other 4xx should bail without retrying
|
||||
return bail(error);
|
||||
@@ -186,7 +192,7 @@ export default class Client extends EventEmitter implements Stdio {
|
||||
`Failed to re-authenticate for ${bold(error.scope)} scope`
|
||||
);
|
||||
}
|
||||
process.exit(1);
|
||||
throw error;
|
||||
}
|
||||
|
||||
this.authConfig.token = result.token;
|
||||
|
||||
@@ -78,16 +78,23 @@ function getLastCommit(directory: string): Promise<git.Commit> {
|
||||
|
||||
export function isDirty(directory: string, output: Output): Promise<boolean> {
|
||||
return new Promise(resolve => {
|
||||
exec('git status -s', { cwd: directory }, function (err, stdout, stderr) {
|
||||
let debugMessage = `Failed to determine if Git repo has been modified:`;
|
||||
if (err || stderr) {
|
||||
if (err) debugMessage += `\n${err}`;
|
||||
if (stderr) debugMessage += `\n${stderr.trim()}`;
|
||||
output.debug(debugMessage);
|
||||
return resolve(false);
|
||||
// note: we specify the `--no-optional-locks` git flag so that `git status`
|
||||
// does not perform any "optional" operations such as optimizing the index
|
||||
// in the background: https://git-scm.com/docs/git-status#_background_refresh
|
||||
exec(
|
||||
'git --no-optional-locks status -s',
|
||||
{ cwd: directory },
|
||||
function (err, stdout, stderr) {
|
||||
let debugMessage = `Failed to determine if Git repo has been modified:`;
|
||||
if (err || stderr) {
|
||||
if (err) debugMessage += `\n${err}`;
|
||||
if (stderr) debugMessage += `\n${stderr.trim()}`;
|
||||
output.debug(debugMessage);
|
||||
return resolve(false);
|
||||
}
|
||||
resolve(stdout.trim().length > 0);
|
||||
}
|
||||
resolve(stdout.trim().length > 0);
|
||||
});
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -115,29 +115,39 @@ export default async function processDeployment({
|
||||
.reduce((a: number, b: number) => a + b, 0);
|
||||
const totalSizeHuman = bytes.format(missingSize, { decimalPlaces: 1 });
|
||||
|
||||
uploads.forEach((e: any) =>
|
||||
e.on('progress', () => {
|
||||
const uploadedBytes = uploads.reduce((acc: number, e: any) => {
|
||||
return acc + e.bytesUploaded;
|
||||
}, 0);
|
||||
// When stderr is not a TTY then we only want to
|
||||
// print upload progress in 25% increments
|
||||
let nextStep = 0;
|
||||
const stepSize = now._client.stderr.isTTY ? 0 : 0.25;
|
||||
|
||||
const bar = progress(uploadedBytes, missingSize);
|
||||
if (!bar || uploadedBytes === missingSize) {
|
||||
output.spinner(deployingSpinnerVal, 0);
|
||||
} else {
|
||||
const uploadedHuman = bytes.format(uploadedBytes, {
|
||||
decimalPlaces: 1,
|
||||
fixedDecimals: true,
|
||||
});
|
||||
const updateProgress = () => {
|
||||
const uploadedBytes = uploads.reduce((acc: number, e: any) => {
|
||||
return acc + e.bytesUploaded;
|
||||
}, 0);
|
||||
|
||||
const bar = progress(uploadedBytes, missingSize);
|
||||
if (!bar) {
|
||||
output.spinner(deployingSpinnerVal, 0);
|
||||
} else {
|
||||
const uploadedHuman = bytes.format(uploadedBytes, {
|
||||
decimalPlaces: 1,
|
||||
fixedDecimals: true,
|
||||
});
|
||||
const percent = uploadedBytes / missingSize;
|
||||
if (percent >= nextStep) {
|
||||
output.spinner(
|
||||
`Uploading ${chalk.reset(
|
||||
`[${bar}] (${uploadedHuman}/${totalSizeHuman})`
|
||||
)}`,
|
||||
0
|
||||
);
|
||||
nextStep += stepSize;
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
uploads.forEach((e: any) => e.on('progress', updateProgress));
|
||||
updateProgress();
|
||||
}
|
||||
|
||||
if (event.type === 'file-uploaded') {
|
||||
|
||||
@@ -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];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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(),
|
||||
|
||||
@@ -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');
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
@@ -57,16 +57,11 @@ import { MissingDotenvVarsError } from '../errors-ts';
|
||||
import cliPkg from '../pkg';
|
||||
import { getVercelDirectory } from '../projects/link';
|
||||
import { staticFiles as getFiles } from '../get-files';
|
||||
import { validateConfig } from './validate';
|
||||
import { validateConfig } from '../validate-config';
|
||||
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) {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -4,6 +4,8 @@ import wait, { StopSpinner } from './wait';
|
||||
import type { WritableTTY } from '../../types';
|
||||
import { errorToString } from '../is-error';
|
||||
|
||||
const IS_TEST = process.env.NODE_ENV === 'test';
|
||||
|
||||
export interface OutputOptions {
|
||||
debug?: boolean;
|
||||
}
|
||||
@@ -108,12 +110,15 @@ export class Output {
|
||||
};
|
||||
|
||||
spinner = (message: string, delay: number = 300): void => {
|
||||
this.spinnerMessage = message;
|
||||
if (this.debugEnabled) {
|
||||
this.debug(`Spinner invoked (${message}) with a ${delay}ms delay`);
|
||||
return;
|
||||
}
|
||||
if (this.stream.isTTY) {
|
||||
if (IS_TEST || !this.stream.isTTY) {
|
||||
this.print(`${message}\n`);
|
||||
} else {
|
||||
this.spinnerMessage = message;
|
||||
|
||||
if (this._spinner) {
|
||||
this._spinner.text = message;
|
||||
} else {
|
||||
@@ -125,8 +130,6 @@ export class Output {
|
||||
delay
|
||||
);
|
||||
}
|
||||
} else {
|
||||
this.print(`${message}\n`);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -28,6 +28,16 @@ export async function writeProjectSettings(
|
||||
project: Project,
|
||||
org: Org
|
||||
) {
|
||||
let analyticsId: string | undefined;
|
||||
if (
|
||||
project.analytics?.id &&
|
||||
(!project.analytics.disabledAt ||
|
||||
(project.analytics.enabledAt &&
|
||||
project.analytics.enabledAt > project.analytics.disabledAt))
|
||||
) {
|
||||
analyticsId = project.analytics.id;
|
||||
}
|
||||
|
||||
const projectLinkAndSettings: ProjectLinkAndSettings = {
|
||||
projectId: project.id,
|
||||
orgId: org.id,
|
||||
@@ -41,7 +51,7 @@ export async function writeProjectSettings(
|
||||
rootDirectory: project.rootDirectory,
|
||||
directoryListing: project.directoryListing,
|
||||
nodeVersion: project.nodeVersion,
|
||||
analyticsId: project.analytics?.id,
|
||||
analyticsId,
|
||||
},
|
||||
};
|
||||
const path = join(cwd, VERCEL_DIR, VERCEL_DIR_PROJECT);
|
||||
|
||||
@@ -7,7 +7,7 @@ import {
|
||||
rewritesSchema,
|
||||
trailingSlashSchema,
|
||||
} from '@vercel/routing-utils';
|
||||
import { VercelConfig } from './types';
|
||||
import { VercelConfig } from './dev/types';
|
||||
import {
|
||||
functionsSchema,
|
||||
buildsSchema,
|
||||
@@ -16,6 +16,83 @@ import {
|
||||
} from '@vercel/build-utils';
|
||||
import { fileNameSymbol } from '@vercel/client';
|
||||
|
||||
const imagesSchema = {
|
||||
type: 'object',
|
||||
additionalProperties: false,
|
||||
required: ['sizes'],
|
||||
properties: {
|
||||
contentSecurityPolicy: {
|
||||
type: 'string',
|
||||
minLength: 1,
|
||||
maxLength: 256,
|
||||
},
|
||||
dangerouslyAllowSVG: {
|
||||
type: 'boolean',
|
||||
},
|
||||
domains: {
|
||||
type: 'array',
|
||||
minItems: 0,
|
||||
maxItems: 50,
|
||||
items: {
|
||||
type: 'string',
|
||||
minLength: 1,
|
||||
maxLength: 256,
|
||||
},
|
||||
},
|
||||
formats: {
|
||||
type: 'array',
|
||||
minItems: 1,
|
||||
maxItems: 4,
|
||||
items: {
|
||||
enum: ['image/avif', 'image/webp', 'image/jpeg', 'image/png'],
|
||||
},
|
||||
},
|
||||
minimumCacheTTL: {
|
||||
type: 'integer',
|
||||
minimum: 1,
|
||||
maximum: 315360000,
|
||||
},
|
||||
remotePatterns: {
|
||||
type: 'array',
|
||||
minItems: 0,
|
||||
maxItems: 50,
|
||||
items: {
|
||||
type: 'object',
|
||||
additionalProperties: false,
|
||||
required: ['hostname'],
|
||||
properties: {
|
||||
protocol: {
|
||||
enum: ['http', 'https'],
|
||||
},
|
||||
hostname: {
|
||||
type: 'string',
|
||||
minLength: 1,
|
||||
maxLength: 256,
|
||||
},
|
||||
port: {
|
||||
type: 'string',
|
||||
minLength: 1,
|
||||
maxLength: 5,
|
||||
},
|
||||
pathname: {
|
||||
type: 'string',
|
||||
minLength: 1,
|
||||
maxLength: 256,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
sizes: {
|
||||
type: 'array',
|
||||
minItems: 1,
|
||||
maxItems: 50,
|
||||
items: {
|
||||
type: 'number',
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const vercelConfigSchema = {
|
||||
type: 'object',
|
||||
// These are not all possibilities because `vc dev`
|
||||
@@ -30,6 +107,7 @@ const vercelConfigSchema = {
|
||||
rewrites: rewritesSchema,
|
||||
trailingSlash: trailingSlashSchema,
|
||||
functions: functionsSchema,
|
||||
images: imagesSchema,
|
||||
},
|
||||
};
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
import generateUUID from '../vendor/generate-uuid';
|
||||
|
||||
export const config = {
|
||||
runtime: 'experimental-edge',
|
||||
};
|
||||
|
||||
export default async function edge(request, event) {
|
||||
return new Response(
|
||||
JSON.stringify({
|
||||
randomString: generateUUID(),
|
||||
})
|
||||
);
|
||||
}
|
||||
6
packages/cli/test/dev/fixtures/edge-function/vendor/generate-uuid/index-browser.js
vendored
Normal file
6
packages/cli/test/dev/fixtures/edge-function/vendor/generate-uuid/index-browser.js
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
// This uses the `crypto` globaly,
|
||||
// which will work fine in a browser or Edge Runtime environment
|
||||
|
||||
export default function generateUUID(message) {
|
||||
return crypto.randomUUID();
|
||||
}
|
||||
8
packages/cli/test/dev/fixtures/edge-function/vendor/generate-uuid/index-node.js
vendored
Normal file
8
packages/cli/test/dev/fixtures/edge-function/vendor/generate-uuid/index-node.js
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
import crypto from 'crypto';
|
||||
|
||||
// this uses a core Node library,
|
||||
// which will fail to execute in a browser or Edge Runtime context
|
||||
|
||||
export default function say(message) {
|
||||
return crypto.generateUUID();
|
||||
}
|
||||
6
packages/cli/test/dev/fixtures/edge-function/vendor/generate-uuid/package.json
vendored
Normal file
6
packages/cli/test/dev/fixtures/edge-function/vendor/generate-uuid/package.json
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"name": "generate-uuid",
|
||||
"private": true,
|
||||
"main": "index-node.js",
|
||||
"browser": "index-browser.js"
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
export const config = {
|
||||
runtime: 'experimental-edge',
|
||||
};
|
||||
|
||||
export default async function edge(request: Request, event: Event) {
|
||||
return new Response('heyo');
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
<h1>Hello from Index</h1>
|
||||
@@ -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',
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -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, {
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
{
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "vite --port $PORT",
|
||||
"build": "vite build",
|
||||
"serve": "vite preview"
|
||||
},
|
||||
|
||||
3
packages/cli/test/dev/fixtures/vite-dev/vercel.json
Normal file
3
packages/cli/test/dev/fixtures/vite-dev/vercel.json
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"devCommand": "vite --port $PORT --host ::1"
|
||||
}
|
||||
@@ -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 {
|
||||
@@ -85,6 +86,7 @@ test(
|
||||
testFixtureStdio('edge-function', async (testPath: any) => {
|
||||
await testPath(500, '/api/edge-500-response');
|
||||
await testPath(200, '/api/edge-success');
|
||||
await testPath(200, '/api/edge-import-browser');
|
||||
})
|
||||
);
|
||||
|
||||
@@ -723,12 +725,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();
|
||||
});
|
||||
|
||||
|
||||
@@ -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) => {
|
||||
|
||||
2
packages/cli/test/fixtures/unit/commands/build/discontinued-nodejs12.x/.gitignore
vendored
Normal file
2
packages/cli/test/fixtures/unit/commands/build/discontinued-nodejs12.x/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
.vercel/builders
|
||||
.vercel/output
|
||||
7
packages/cli/test/fixtures/unit/commands/build/discontinued-nodejs12.x/.vercel/project.json
vendored
Normal file
7
packages/cli/test/fixtures/unit/commands/build/discontinued-nodejs12.x/.vercel/project.json
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"orgId": ".",
|
||||
"projectId": ".",
|
||||
"settings": {
|
||||
"framework": null
|
||||
}
|
||||
}
|
||||
1
packages/cli/test/fixtures/unit/commands/build/discontinued-nodejs12.x/api/index.php
vendored
Normal file
1
packages/cli/test/fixtures/unit/commands/build/discontinued-nodejs12.x/api/index.php
vendored
Normal file
@@ -0,0 +1 @@
|
||||
<?php echo 'This version of vercel-php uses the nodejs12.x Lambda Runtime'; ?>
|
||||
8
packages/cli/test/fixtures/unit/commands/build/discontinued-nodejs12.x/vercel.json
vendored
Normal file
8
packages/cli/test/fixtures/unit/commands/build/discontinued-nodejs12.x/vercel.json
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"$schema": "https://openapi.vercel.sh/vercel.json",
|
||||
"functions": {
|
||||
"api/index.php": {
|
||||
"runtime": "vercel-php@0.1.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
7
packages/cli/test/fixtures/unit/commands/build/images/.vercel/project.json
vendored
Normal file
7
packages/cli/test/fixtures/unit/commands/build/images/.vercel/project.json
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"orgId": ".",
|
||||
"projectId": ".",
|
||||
"settings": {
|
||||
"framework": null
|
||||
}
|
||||
}
|
||||
1
packages/cli/test/fixtures/unit/commands/build/images/index.html
vendored
Normal file
1
packages/cli/test/fixtures/unit/commands/build/images/index.html
vendored
Normal file
@@ -0,0 +1 @@
|
||||
<h1>Vercel</h1>
|
||||
8
packages/cli/test/fixtures/unit/commands/build/images/vercel.json
vendored
Normal file
8
packages/cli/test/fixtures/unit/commands/build/images/vercel.json
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"images": {
|
||||
"sizes": [256, 384, 600, 1000],
|
||||
"domains": [],
|
||||
"minimumCacheTTL": 60,
|
||||
"formats": ["image/avif", "image/webp"]
|
||||
}
|
||||
}
|
||||
7
packages/cli/test/fixtures/unit/commands/build/invalid-rewrites/.vercel/project.json
vendored
Normal file
7
packages/cli/test/fixtures/unit/commands/build/invalid-rewrites/.vercel/project.json
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"orgId": ".",
|
||||
"projectId": ".",
|
||||
"settings": {
|
||||
"framework": null
|
||||
}
|
||||
}
|
||||
1
packages/cli/test/fixtures/unit/commands/build/invalid-rewrites/index.html
vendored
Normal file
1
packages/cli/test/fixtures/unit/commands/build/invalid-rewrites/index.html
vendored
Normal file
@@ -0,0 +1 @@
|
||||
<h1>Vercel</h1>
|
||||
16
packages/cli/test/fixtures/unit/commands/build/invalid-rewrites/vercel.json
vendored
Normal file
16
packages/cli/test/fixtures/unit/commands/build/invalid-rewrites/vercel.json
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"rewrites": [
|
||||
{
|
||||
"source": "/one",
|
||||
"destination": "/api/one"
|
||||
},
|
||||
{
|
||||
"source": "/two",
|
||||
"destination": "/api/two"
|
||||
},
|
||||
{
|
||||
"src": "/three",
|
||||
"dest": "/api/three"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -326,7 +326,7 @@ module.exports = async function prepare(session, binaryPath, tmpFixturesDir) {
|
||||
'vercel.json': JSON.stringify({
|
||||
functions: {
|
||||
'api/**/*.php': {
|
||||
runtime: 'vercel-php@0.1.0',
|
||||
runtime: 'vercel-php@0.5.2',
|
||||
},
|
||||
},
|
||||
}),
|
||||
|
||||
@@ -3,20 +3,26 @@ import chance from 'chance';
|
||||
import { Deployment } from '@vercel/client';
|
||||
import { client } from './client';
|
||||
import { Build, User } from '../../src/types';
|
||||
import type { Request, Response } from 'express';
|
||||
|
||||
let deployments = new Map<string, Deployment>();
|
||||
let deploymentBuilds = new Map<Deployment, Build[]>();
|
||||
let alreadySetupDeplomentEndpoints = false;
|
||||
|
||||
type State = Deployment['readyState'];
|
||||
|
||||
export function useDeployment({
|
||||
creator,
|
||||
state = 'READY',
|
||||
createdAt,
|
||||
}: {
|
||||
creator: Pick<User, 'id' | 'email' | 'name' | 'username'>;
|
||||
state?: State;
|
||||
createdAt?: number;
|
||||
}) {
|
||||
const createdAt = Date.now();
|
||||
setupDeploymentEndpoints();
|
||||
|
||||
createdAt = createdAt || Date.now();
|
||||
const url = new URL(chance().url());
|
||||
const name = chance().name();
|
||||
const id = `dpl_${chance().guid()}`;
|
||||
@@ -69,7 +75,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: {
|
||||
@@ -99,6 +105,15 @@ export function useDeploymentMissingProjectSettings() {
|
||||
beforeEach(() => {
|
||||
deployments = new Map();
|
||||
deploymentBuilds = new Map();
|
||||
alreadySetupDeplomentEndpoints = false;
|
||||
});
|
||||
|
||||
function setupDeploymentEndpoints() {
|
||||
if (alreadySetupDeplomentEndpoints) {
|
||||
return;
|
||||
}
|
||||
|
||||
alreadySetupDeplomentEndpoints = true;
|
||||
|
||||
client.scenario.get('/:version/deployments/:id', (req, res) => {
|
||||
const { id } = req.params;
|
||||
@@ -136,8 +151,21 @@ beforeEach(() => {
|
||||
res.json({ builds });
|
||||
});
|
||||
|
||||
client.scenario.get('/:version/now/deployments', (req, res) => {
|
||||
const deploymentsList = Array.from(deployments.values());
|
||||
res.json({ deployments: deploymentsList });
|
||||
});
|
||||
});
|
||||
function handleGetDeployments(req: Request, res: Response) {
|
||||
const currentDeployments = Array.from(deployments.values()).sort(
|
||||
(a: Deployment, b: Deployment) => {
|
||||
// sort in reverse chronological order
|
||||
return b.createdAt - a.createdAt;
|
||||
}
|
||||
);
|
||||
|
||||
res.json({
|
||||
pagination: {
|
||||
count: currentDeployments.length,
|
||||
},
|
||||
deployments: currentDeployments,
|
||||
});
|
||||
}
|
||||
client.scenario.get('/:version/now/deployments', handleGetDeployments);
|
||||
client.scenario.get('/:version/deployments', handleGetDeployments);
|
||||
}
|
||||
|
||||
55
packages/cli/test/unit/commands/bisect.test.ts
Normal file
55
packages/cli/test/unit/commands/bisect.test.ts
Normal file
@@ -0,0 +1,55 @@
|
||||
import { client } from '../../mocks/client';
|
||||
import { useUser } from '../../mocks/user';
|
||||
import bisect from '../../../src/commands/bisect';
|
||||
import { useDeployment } from '../../mocks/deployment';
|
||||
|
||||
describe('bisect', () => {
|
||||
it('should find the bad deployment', async () => {
|
||||
const user = useUser();
|
||||
|
||||
const now = Date.now();
|
||||
const deployment1 = useDeployment({ creator: user, createdAt: now });
|
||||
const deployment2 = useDeployment({
|
||||
creator: user,
|
||||
createdAt: now + 10000,
|
||||
});
|
||||
const deployment3 = useDeployment({
|
||||
creator: user,
|
||||
createdAt: now + 20000,
|
||||
});
|
||||
|
||||
// also create an extra deployment before the known good deployment
|
||||
// to make sure the bisect pool doesn't include it
|
||||
useDeployment({
|
||||
creator: user,
|
||||
createdAt: now - 30000,
|
||||
});
|
||||
|
||||
const bisectPromise = bisect(client);
|
||||
|
||||
await expect(client.stderr).toOutput('Specify a URL where the bug occurs:');
|
||||
client.stdin.write(`https://${deployment3.url}\n`);
|
||||
|
||||
await expect(client.stderr).toOutput(
|
||||
'Specify a URL where the bug does not occur:'
|
||||
);
|
||||
client.stdin.write(`https://${deployment1.url}\n`);
|
||||
|
||||
await expect(client.stderr).toOutput(
|
||||
'Specify the URL subpath where the bug occurs:'
|
||||
);
|
||||
client.stdin.write('/docs\n');
|
||||
|
||||
await expect(client.stderr).toOutput('Bisecting');
|
||||
await expect(client.stderr).toOutput(
|
||||
`Deployment URL: https://${deployment2.url}`
|
||||
);
|
||||
client.stdin.write('b\n');
|
||||
|
||||
await expect(client.stderr).toOutput(
|
||||
`The first bad deployment is: https://${deployment2.url}`
|
||||
);
|
||||
|
||||
await expect(bisectPromise).resolves.toEqual(0);
|
||||
});
|
||||
});
|
||||
@@ -750,11 +750,71 @@ describe('build', () => {
|
||||
const errorBuilds = builds.builds.filter((b: any) => 'error' in b);
|
||||
expect(errorBuilds).toHaveLength(1);
|
||||
|
||||
expect(errorBuilds[0].error.name).toEqual('Error');
|
||||
expect(errorBuilds[0].error.message).toMatch(`TS1005`);
|
||||
expect(errorBuilds[0].error.message).toMatch(`',' expected.`);
|
||||
expect(errorBuilds[0].error.hideStackTrace).toEqual(true);
|
||||
expect(errorBuilds[0].error.code).toEqual('NODE_TYPESCRIPT_ERROR');
|
||||
expect(errorBuilds[0].error).toEqual({
|
||||
name: 'Error',
|
||||
message: expect.stringContaining('TS1005'),
|
||||
stack: expect.stringContaining('api/typescript.ts'),
|
||||
hideStackTrace: true,
|
||||
code: 'NODE_TYPESCRIPT_ERROR',
|
||||
});
|
||||
|
||||
// top level "error" also contains the same error
|
||||
expect(builds.error).toEqual({
|
||||
name: 'Error',
|
||||
message: expect.stringContaining('TS1005'),
|
||||
stack: expect.stringContaining('api/typescript.ts'),
|
||||
hideStackTrace: true,
|
||||
code: 'NODE_TYPESCRIPT_ERROR',
|
||||
});
|
||||
|
||||
// `config.json` contains `version`
|
||||
const configJson = await fs.readJSON(join(output, 'config.json'));
|
||||
expect(configJson.version).toBe(3);
|
||||
} finally {
|
||||
process.chdir(originalCwd);
|
||||
delete process.env.__VERCEL_BUILD_RUNNING;
|
||||
}
|
||||
});
|
||||
|
||||
it('should error when "functions" has runtime that emits discontinued "nodejs12.x"', async () => {
|
||||
if (process.platform === 'win32') {
|
||||
console.log('Skipping test on Windows');
|
||||
return;
|
||||
}
|
||||
const cwd = fixture('discontinued-nodejs12.x');
|
||||
const output = join(cwd, '.vercel/output');
|
||||
try {
|
||||
process.chdir(cwd);
|
||||
const exitCode = await build(client);
|
||||
expect(exitCode).toEqual(1);
|
||||
|
||||
// Error gets printed to the terminal
|
||||
await expect(client.stderr).toOutput(
|
||||
'The Runtime "vercel-php@0.1.0" is using "nodejs12.x", which is discontinued. Please upgrade your Runtime to a more recent version or consult the author for more details.'
|
||||
);
|
||||
|
||||
// `builds.json` contains "error" build
|
||||
const builds = await fs.readJSON(join(output, 'builds.json'));
|
||||
const errorBuilds = builds.builds.filter((b: any) => 'error' in b);
|
||||
expect(errorBuilds).toHaveLength(1);
|
||||
expect(errorBuilds[0].error).toEqual({
|
||||
name: 'Error',
|
||||
message: expect.stringContaining('Please upgrade your Runtime'),
|
||||
stack: expect.stringContaining('Please upgrade your Runtime'),
|
||||
hideStackTrace: true,
|
||||
code: 'NODEJS_DISCONTINUED_VERSION',
|
||||
link: 'https://github.com/vercel/vercel/blob/main/DEVELOPING_A_RUNTIME.md#lambdaruntime',
|
||||
});
|
||||
|
||||
// top level "error" also contains the same error
|
||||
expect(builds.error).toEqual({
|
||||
name: 'Error',
|
||||
message: expect.stringContaining('Please upgrade your Runtime'),
|
||||
stack: expect.stringContaining('Please upgrade your Runtime'),
|
||||
hideStackTrace: true,
|
||||
code: 'NODEJS_DISCONTINUED_VERSION',
|
||||
link: 'https://github.com/vercel/vercel/blob/main/DEVELOPING_A_RUNTIME.md#lambdaruntime',
|
||||
});
|
||||
|
||||
// `config.json` contains `version`
|
||||
const configJson = await fs.readJSON(join(output, 'config.json'));
|
||||
@@ -895,6 +955,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 {
|
||||
@@ -915,6 +981,62 @@ describe('build', () => {
|
||||
}
|
||||
});
|
||||
|
||||
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/avif', 'image/webp'],
|
||||
},
|
||||
});
|
||||
} finally {
|
||||
process.chdir(originalCwd);
|
||||
delete process.env.__VERCEL_BUILD_RUNNING;
|
||||
}
|
||||
});
|
||||
|
||||
it('should fail with invalid "rewrites" configuration from `vercel.json`', async () => {
|
||||
const cwd = fixture('invalid-rewrites');
|
||||
const output = join(cwd, '.vercel/output');
|
||||
try {
|
||||
process.chdir(cwd);
|
||||
const exitCode = await build(client);
|
||||
expect(exitCode).toEqual(1);
|
||||
await expect(client.stderr).toOutput(
|
||||
'Error: Invalid vercel.json - `rewrites[2]` should NOT have additional property `src`. Did you mean `source`?' +
|
||||
'\n' +
|
||||
'View Documentation: https://vercel.com/docs/configuration#project/rewrites'
|
||||
);
|
||||
const builds = await fs.readJSON(join(output, 'builds.json'));
|
||||
expect(builds.builds).toBeUndefined();
|
||||
expect(builds.error).toEqual({
|
||||
name: 'Error',
|
||||
message:
|
||||
'Invalid vercel.json - `rewrites[2]` should NOT have additional property `src`. Did you mean `source`?',
|
||||
stack: expect.stringContaining('at validateConfig'),
|
||||
hideStackTrace: true,
|
||||
code: 'INVALID_VERCEL_CONFIG',
|
||||
link: 'https://vercel.com/docs/configuration#project/rewrites',
|
||||
action: 'View Documentation',
|
||||
});
|
||||
const configJson = await fs.readJSON(join(output, 'config.json'));
|
||||
expect(configJson.version).toBe(3);
|
||||
} finally {
|
||||
process.chdir(originalCwd);
|
||||
delete process.env.__VERCEL_BUILD_RUNNING;
|
||||
}
|
||||
});
|
||||
|
||||
describe('should find packages with different main/module/browser keys', function () {
|
||||
let output: string;
|
||||
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
import bytes from 'bytes';
|
||||
import fs from 'fs-extra';
|
||||
import { join } from 'path';
|
||||
import { randomBytes } from 'crypto';
|
||||
import { fileNameSymbol } from '@vercel/client';
|
||||
import { client } from '../../mocks/client';
|
||||
import deploy from '../../../src/commands/deploy';
|
||||
@@ -199,4 +202,119 @@ describe('deploy', () => {
|
||||
process.chdir(originalCwd);
|
||||
}
|
||||
});
|
||||
|
||||
it('should upload missing files', async () => {
|
||||
const cwd = setupFixture('commands/deploy/archive');
|
||||
const originalCwd = process.cwd();
|
||||
|
||||
// Add random 1mb file
|
||||
await fs.writeFile(join(cwd, 'data'), randomBytes(bytes('1mb')));
|
||||
|
||||
try {
|
||||
process.chdir(cwd);
|
||||
|
||||
const user = useUser();
|
||||
useTeams('team_dummy');
|
||||
useProject({
|
||||
...defaultProject,
|
||||
name: 'archive',
|
||||
id: 'archive',
|
||||
});
|
||||
|
||||
let body: any;
|
||||
let fileUploaded = false;
|
||||
client.scenario.post(`/v13/deployments`, (req, res) => {
|
||||
if (fileUploaded) {
|
||||
body = req.body;
|
||||
res.json({
|
||||
creator: {
|
||||
uid: user.id,
|
||||
username: user.username,
|
||||
},
|
||||
id: 'dpl_archive_test',
|
||||
});
|
||||
} else {
|
||||
const sha = req.body.files[0].sha;
|
||||
res.status(400).json({
|
||||
error: {
|
||||
code: 'missing_files',
|
||||
message: 'Missing files',
|
||||
missing: [sha],
|
||||
},
|
||||
});
|
||||
}
|
||||
});
|
||||
client.scenario.post('/v2/files', (req, res) => {
|
||||
// Wait for file to be finished uploading
|
||||
req.on('data', () => {
|
||||
// Noop
|
||||
});
|
||||
req.on('end', () => {
|
||||
fileUploaded = true;
|
||||
res.end();
|
||||
});
|
||||
});
|
||||
client.scenario.get(`/v13/deployments/dpl_archive_test`, (req, res) => {
|
||||
res.json({
|
||||
creator: {
|
||||
uid: user.id,
|
||||
username: user.username,
|
||||
},
|
||||
id: 'dpl_archive_test',
|
||||
readyState: 'READY',
|
||||
aliasAssigned: true,
|
||||
alias: [],
|
||||
});
|
||||
});
|
||||
client.scenario.get(
|
||||
`/v10/now/deployments/dpl_archive_test`,
|
||||
(req, res) => {
|
||||
res.json({
|
||||
creator: {
|
||||
uid: user.id,
|
||||
username: user.username,
|
||||
},
|
||||
id: 'dpl_archive_test',
|
||||
readyState: 'READY',
|
||||
aliasAssigned: true,
|
||||
alias: [],
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
// When stderr is not a TTY we expect 5 progress lines to be printed
|
||||
client.stderr.isTTY = false;
|
||||
|
||||
client.setArgv('deploy', '--archive=tgz');
|
||||
const uploadingLines: string[] = [];
|
||||
client.stderr.on('data', data => {
|
||||
if (data.startsWith('Uploading [')) {
|
||||
uploadingLines.push(data);
|
||||
}
|
||||
});
|
||||
client.stderr.resume();
|
||||
const exitCode = await deploy(client);
|
||||
expect(exitCode).toEqual(0);
|
||||
expect(body?.files?.length).toEqual(1);
|
||||
expect(body?.files?.[0].file).toEqual('.vercel/source.tgz');
|
||||
expect(uploadingLines.length).toEqual(5);
|
||||
expect(
|
||||
uploadingLines[0].startsWith('Uploading [--------------------]')
|
||||
).toEqual(true);
|
||||
expect(
|
||||
uploadingLines[1].startsWith('Uploading [=====---------------]')
|
||||
).toEqual(true);
|
||||
expect(
|
||||
uploadingLines[2].startsWith('Uploading [==========----------]')
|
||||
).toEqual(true);
|
||||
expect(
|
||||
uploadingLines[3].startsWith('Uploading [===============-----]')
|
||||
).toEqual(true);
|
||||
expect(
|
||||
uploadingLines[4].startsWith('Uploading [====================]')
|
||||
).toEqual(true);
|
||||
} finally {
|
||||
process.chdir(originalCwd);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -38,11 +38,11 @@ describe('list', () => {
|
||||
|
||||
await list(client);
|
||||
|
||||
const output = await readOutputStream(client, 4);
|
||||
const output = await readOutputStream(client, 6);
|
||||
|
||||
const { org } = pluckIdentifiersFromDeploymentList(output.split('\n')[0]);
|
||||
const header: string[] = parseSpacedTableRow(output.split('\n')[3]);
|
||||
const data: string[] = parseSpacedTableRow(output.split('\n')[4]);
|
||||
const { org } = pluckIdentifiersFromDeploymentList(output.split('\n')[2]);
|
||||
const header: string[] = parseSpacedTableRow(output.split('\n')[5]);
|
||||
const data: string[] = parseSpacedTableRow(output.split('\n')[6]);
|
||||
data.shift();
|
||||
|
||||
expect(org).toEqual(team[0].slug);
|
||||
@@ -81,11 +81,11 @@ describe('list', () => {
|
||||
client.setArgv('-S', user.username);
|
||||
await list(client);
|
||||
|
||||
const output = await readOutputStream(client, 4);
|
||||
const output = await readOutputStream(client, 6);
|
||||
|
||||
const { org } = pluckIdentifiersFromDeploymentList(output.split('\n')[0]);
|
||||
const header: string[] = parseSpacedTableRow(output.split('\n')[3]);
|
||||
const data: string[] = parseSpacedTableRow(output.split('\n')[4]);
|
||||
const { org } = pluckIdentifiersFromDeploymentList(output.split('\n')[2]);
|
||||
const header: string[] = parseSpacedTableRow(output.split('\n')[5]);
|
||||
const data: string[] = parseSpacedTableRow(output.split('\n')[6]);
|
||||
data.shift();
|
||||
|
||||
expect(org).toEqual(user.username);
|
||||
@@ -116,11 +116,11 @@ describe('list', () => {
|
||||
client.setArgv(deployment.name);
|
||||
await list(client);
|
||||
|
||||
const output = await readOutputStream(client, 4);
|
||||
const output = await readOutputStream(client, 6);
|
||||
|
||||
const { org } = pluckIdentifiersFromDeploymentList(output.split('\n')[0]);
|
||||
const header: string[] = parseSpacedTableRow(output.split('\n')[3]);
|
||||
const data: string[] = parseSpacedTableRow(output.split('\n')[4]);
|
||||
const { org } = pluckIdentifiersFromDeploymentList(output.split('\n')[2]);
|
||||
const header: string[] = parseSpacedTableRow(output.split('\n')[5]);
|
||||
const data: string[] = parseSpacedTableRow(output.split('\n')[6]);
|
||||
data.shift();
|
||||
|
||||
expect(org).toEqual(teamSlug || team[0].slug);
|
||||
|
||||
@@ -22,10 +22,10 @@ describe('project', () => {
|
||||
client.setArgv('project', 'ls');
|
||||
await projects(client);
|
||||
|
||||
const output = await readOutputStream(client, 2);
|
||||
const { org } = pluckIdentifiersFromDeploymentList(output.split('\n')[0]);
|
||||
const header: string[] = parseSpacedTableRow(output.split('\n')[2]);
|
||||
const data: string[] = parseSpacedTableRow(output.split('\n')[3]);
|
||||
const output = await readOutputStream(client, 3);
|
||||
const { org } = pluckIdentifiersFromDeploymentList(output.split('\n')[1]);
|
||||
const header: string[] = parseSpacedTableRow(output.split('\n')[3]);
|
||||
const data: string[] = parseSpacedTableRow(output.split('\n')[4]);
|
||||
data.pop();
|
||||
|
||||
expect(org).toEqual(user.username);
|
||||
@@ -47,10 +47,10 @@ describe('project', () => {
|
||||
client.setArgv('project', 'ls');
|
||||
await projects(client);
|
||||
|
||||
const output = await readOutputStream(client, 2);
|
||||
const { org } = pluckIdentifiersFromDeploymentList(output.split('\n')[0]);
|
||||
const header: string[] = parseSpacedTableRow(output.split('\n')[2]);
|
||||
const data: string[] = parseSpacedTableRow(output.split('\n')[3]);
|
||||
const output = await readOutputStream(client, 3);
|
||||
const { org } = pluckIdentifiersFromDeploymentList(output.split('\n')[1]);
|
||||
const header: string[] = parseSpacedTableRow(output.split('\n')[3]);
|
||||
const data: string[] = parseSpacedTableRow(output.split('\n')[4]);
|
||||
data.pop();
|
||||
|
||||
expect(org).toEqual(user.username);
|
||||
|
||||
@@ -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()', () => {
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
});
|
||||
@@ -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);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { validateConfig } from '../../../../src/util/dev/validate';
|
||||
import { validateConfig } from '../../../../src/util/validate-config';
|
||||
|
||||
describe('validateConfig', () => {
|
||||
it('should not error with empty config', async () => {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vercel/client",
|
||||
"version": "12.2.4",
|
||||
"version": "12.2.13",
|
||||
"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.5.5",
|
||||
"@vercel/routing-utils": "2.0.2",
|
||||
"@zeit/fetch": "5.2.0",
|
||||
"async-retry": "1.2.3",
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { Agent } from 'https';
|
||||
import http from 'http';
|
||||
import https from 'https';
|
||||
import { Readable } from 'stream';
|
||||
import { EventEmitter } from 'events';
|
||||
import retry from 'async-retry';
|
||||
@@ -78,7 +79,9 @@ export async function* upload(
|
||||
debug('Building an upload list...');
|
||||
|
||||
const semaphore = new Sema(50, { capacity: 50 });
|
||||
const agent = new Agent({ keepAlive: true });
|
||||
const agent = apiUrl?.startsWith('https://')
|
||||
? new https.Agent({ keepAlive: true })
|
||||
: new http.Agent({ keepAlive: true });
|
||||
|
||||
shas.forEach((sha, index) => {
|
||||
const uploadProgress = uploads[index];
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@vercel/frameworks",
|
||||
"version": "1.1.4",
|
||||
"version": "1.1.8",
|
||||
"main": "./dist/frameworks.js",
|
||||
"types": "./dist/frameworks.d.ts",
|
||||
"files": [
|
||||
|
||||
@@ -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 world’s 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,28 @@ 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.jsx',
|
||||
},
|
||||
{
|
||||
path: 'sanity.config.ts',
|
||||
},
|
||||
{
|
||||
path: 'sanity.config.tsx',
|
||||
},
|
||||
],
|
||||
},
|
||||
settings: {
|
||||
@@ -1822,16 +1834,19 @@ 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',
|
||||
useRuntime: { src: 'package.json', use: '@vercel/hydrogen' },
|
||||
detectors: {
|
||||
every: [
|
||||
some: [
|
||||
{
|
||||
path: 'hydrogen.config.js',
|
||||
},
|
||||
{
|
||||
path: 'hydrogen.config.ts',
|
||||
},
|
||||
],
|
||||
},
|
||||
settings: {
|
||||
@@ -1856,7 +1871,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: {
|
||||
|
||||
@@ -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;
|
||||
/**
|
||||
|
||||
33
packages/frameworks/test/frameworks.unit.test.ts
vendored
33
packages/frameworks/test/frameworks.unit.test.ts
vendored
@@ -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([]);
|
||||
|
||||
8
packages/fs-detectors/logos/nx.svg
Normal file
8
packages/fs-detectors/logos/nx.svg
Normal file
@@ -0,0 +1,8 @@
|
||||
<svg width="100" height="100" viewBox="0 0 100 100" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M49.8779 58.813L37.2099 39.0114L37.1908 55.6489L15.3588 18.8931H0V80.9427H15.187L15.2366 44.2328L36.8435 79.2977L49.8779 58.813Z" fill="white"/>
|
||||
<path d="M37.2137 34.8435H52.3817L52.416 34.6679V18.8931H37.229L37.2099 34.6679L37.2137 34.8435Z" fill="white"/>
|
||||
<path d="M75.8244 52.0458C72.1908 51.9769 68.9079 54.2048 67.6298 57.6069C69.7576 54.4249 73.809 53.1463 77.3779 54.5305C79.0267 55.2023 81.1947 56.2863 82.9122 55.7061C81.2787 53.4137 78.6393 52.0506 75.8244 52.0458Z" fill="#96D8E9"/>
|
||||
<path d="M93.0343 59.5114C93.0343 57.6527 92.0229 57.2214 89.9237 56.458C88.3664 55.916 86.5992 55.3053 85.313 53.6259C85.0611 53.2939 84.8282 52.9198 84.5802 52.5229C84.0244 51.4948 83.2707 50.5868 82.3626 49.8511C81.1374 48.9504 79.5305 48.5114 77.4466 48.5114C73.265 48.5144 69.4978 51.0384 67.9046 54.9046C69.9187 52.1099 73.2659 50.5956 76.6947 50.928C80.1235 51.2605 83.1175 53.3895 84.5572 56.5191C85.2766 57.706 86.6952 58.2728 88.0343 57.9084C90.084 57.4695 89.9427 59.4084 93.0382 60.0649L93.0343 59.5114Z" fill="#48C4E5"/>
|
||||
<path d="M99.7939 59.1145V59.0725C99.7481 46.7672 89.7595 36.2405 77.4466 36.2405C69.9264 36.2352 62.9091 40.0172 58.7786 46.3015L58.6985 46.1641L52.3779 36.2405L37.2137 36.2404L51.6221 58.8053L37.5611 80.9427H52.2901L58.6985 71.0992L65.2214 80.9427H79.9542L66.5954 59.9198C66.3737 59.5102 66.2533 59.0535 66.2443 58.5878C66.2433 55.6185 67.4224 52.7705 69.522 50.6708C71.6216 48.5712 74.4696 47.3921 77.4389 47.3931C83.6298 47.3931 84.6641 51.0954 86 52.8359C88.6374 56.271 93.9046 54.7672 93.9046 59.2481C93.9203 60.2981 94.4949 61.2599 95.4121 61.7713C96.3292 62.2827 97.4495 62.266 98.351 61.7274C99.2525 61.1889 99.7981 60.2103 99.7824 59.1603V59.1145H99.7939Z" fill="white"/>
|
||||
<path d="M99.8092 62.2824C99.9589 63.3289 99.8107 64.3964 99.3817 65.3626C98.542 67.3053 97.1412 66.6565 97.1412 66.6565C97.1412 66.6565 95.9351 66.0687 96.6679 64.9427C97.4809 63.687 99.0878 63.8321 99.8092 62.2824Z" fill="#022F56"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.0 KiB |
3
packages/fs-detectors/logos/rush.svg
Normal file
3
packages/fs-detectors/logos/rush.svg
Normal file
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 7.2 KiB |
35
packages/fs-detectors/logos/turborepo-dark.svg
Normal file
35
packages/fs-detectors/logos/turborepo-dark.svg
Normal file
@@ -0,0 +1,35 @@
|
||||
<svg width="100" height="125" viewBox="0 0 100 125" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M40 0.0374222C25.8531 -0.46154 11.7078 4.02912 0 13.5094L7.80471 18C17.0735 11.0146 28.7797 8.02081 40 8.51977V0.0374222Z" fill="url(#paint0_linear_8_11)"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M82 19.1224C72.2699 8.83673 59.6211 2.46939 46 1V9.32653C57.6764 11.2857 67.8927 16.6735 76.1626 25L82 19.1224Z" fill="url(#paint1_linear_8_11)"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M100 60C99.5253 47 94.7753 34 86.701 23L81 29C87.6503 38.5001 91.451 49 91.9256 60H100Z" fill="url(#paint2_linear_8_11)"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M86.701 102C94.7753 91.2973 99.5253 78.6487 100 66H91.9256C91.451 76.7027 87.6503 86.9189 81 96.1621L86.701 102Z" fill="url(#paint3_linear_8_11)"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M46 124C59.135 122.531 72.2699 116.163 82 105.878L76.1626 100C67.8927 108.816 57.1886 114.204 46 115.673V124Z" fill="url(#paint4_linear_8_11)"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M0 111.491C11.7078 120.971 25.8531 125.461 40 124.963V116.48C28.7797 116.979 17.561 113.985 7.80471 107L0 111.491Z" fill="url(#paint5_linear_8_11)"/>
|
||||
<path d="M19.9222 63C19.9222 65.6382 20.4182 68.0713 21.4084 70.299C22.3987 72.5267 23.7403 74.4614 25.4299 76.103C27.1196 77.7446 29.0732 79.0343 31.2875 79.9724C33.5605 80.8518 35.9495 81.2915 38.4563 81.2915C40.963 81.2915 43.3235 80.8518 45.5377 79.9724C47.8108 79.0343 49.793 77.7446 51.4826 76.103C53.2311 74.4614 54.6014 72.5267 55.5916 70.299C56.5819 68.0713 57.0778 65.6382 57.0778 63C57.0778 60.3618 56.5819 57.9288 55.5916 55.701C54.6014 53.4733 53.2311 51.5386 51.4826 49.897C49.793 48.2554 47.8108 46.995 45.5377 46.1156C43.3235 45.1775 40.963 44.7085 38.4563 44.7085C35.9495 44.7085 33.5605 45.1775 31.2875 46.1156C29.0732 46.995 27.1196 48.2554 25.4299 49.897C23.7403 51.5386 22.3987 53.4733 21.4084 55.701C20.4182 57.9288 19.9222 60.3618 19.9222 63ZM2 63C2 58.0754 2.90283 53.5025 4.71018 49.2814C6.51753 45.0016 9.02263 41.2789 12.2288 38.1131C15.4349 34.9472 19.2513 32.4849 23.6814 30.7261C28.1687 28.9087 33.0947 28 38.4563 28C43.7606 28 48.6565 28.9087 53.1437 30.7261C57.631 32.4849 61.4777 34.9472 64.6838 38.1131C67.9471 41.2789 70.4825 45.0016 72.2899 49.2814C74.0972 53.5025 75 58.0754 75 63C75 67.9246 74.0972 72.5267 72.2899 76.8065C70.4825 81.0276 67.9471 84.7211 64.6838 87.8869C61.4777 91.0528 57.631 93.5443 53.1437 95.3618C48.6565 97.1206 43.7606 98 38.4563 98C33.0947 98 28.1687 97.1206 23.6814 95.3618C19.2513 93.5443 15.4349 91.0528 12.2288 87.8869C9.02263 84.7211 6.51753 81.0276 4.71018 76.8065C2.90283 72.5267 2 67.9246 2 63Z" fill="white"/>
|
||||
<defs>
|
||||
<linearGradient id="paint0_linear_8_11" x1="49.5117" y1="-6.94291e-08" x2="49.5117" y2="125.813" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#1E90FF"/>
|
||||
<stop offset="1" stop-color="#FF1E56"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint1_linear_8_11" x1="50.135" y1="0.473494" x2="50.135" y2="123.975" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#1E90FF"/>
|
||||
<stop offset="1" stop-color="#FF1E56"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint2_linear_8_11" x1="51.7888" y1="-0.0373877" x2="51.7888" y2="126.037" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#1E90FF"/>
|
||||
<stop offset="1" stop-color="#FF1E56"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint3_linear_8_11" x1="51.7888" y1="1.74712" x2="51.7888" y2="124.415" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#1E90FF"/>
|
||||
<stop offset="1" stop-color="#FF1E56"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint4_linear_8_11" x1="50.135" y1="1.02464" x2="50.135" y2="124.526" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#1E90FF"/>
|
||||
<stop offset="1" stop-color="#FF1E56"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint5_linear_8_11" x1="49.5117" y1="-0.812955" x2="49.5117" y2="125" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#1E90FF"/>
|
||||
<stop offset="1" stop-color="#FF1E56"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.9 KiB |
35
packages/fs-detectors/logos/turborepo.svg
Normal file
35
packages/fs-detectors/logos/turborepo.svg
Normal file
@@ -0,0 +1,35 @@
|
||||
<svg width="100" height="125" viewBox="0 0 100 125" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M40 0.0374222C25.8531 -0.46154 11.7078 4.02912 0 13.5094L7.80471 18C17.0735 11.0146 28.7797 8.02081 40 8.51977V0.0374222Z" fill="url(#paint0_linear_8_11)"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M82 19.1224C72.2699 8.83673 59.6211 2.46939 46 1V9.32653C57.6764 11.2857 67.8927 16.6735 76.1626 25L82 19.1224Z" fill="url(#paint1_linear_8_11)"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M100 60C99.5253 47 94.7753 34 86.701 23L81 29C87.6503 38.5001 91.451 49 91.9256 60H100Z" fill="url(#paint2_linear_8_11)"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M86.701 102C94.7753 91.2973 99.5253 78.6487 100 66H91.9256C91.451 76.7027 87.6503 86.9189 81 96.1621L86.701 102Z" fill="url(#paint3_linear_8_11)"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M46 124C59.135 122.531 72.2699 116.163 82 105.878L76.1626 100C67.8927 108.816 57.1886 114.204 46 115.673V124Z" fill="url(#paint4_linear_8_11)"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M0 111.491C11.7078 120.971 25.8531 125.461 40 124.963V116.48C28.7797 116.979 17.561 113.985 7.80471 107L0 111.491Z" fill="url(#paint5_linear_8_11)"/>
|
||||
<path d="M19.9222 63C19.9222 65.6382 20.4182 68.0713 21.4084 70.299C22.3987 72.5267 23.7403 74.4614 25.4299 76.103C27.1196 77.7446 29.0732 79.0343 31.2875 79.9724C33.5605 80.8518 35.9495 81.2915 38.4563 81.2915C40.963 81.2915 43.3235 80.8518 45.5377 79.9724C47.8108 79.0343 49.793 77.7446 51.4826 76.103C53.2311 74.4614 54.6014 72.5267 55.5916 70.299C56.5819 68.0713 57.0778 65.6382 57.0778 63C57.0778 60.3618 56.5819 57.9288 55.5916 55.701C54.6014 53.4733 53.2311 51.5386 51.4826 49.897C49.793 48.2554 47.8108 46.995 45.5377 46.1156C43.3235 45.1775 40.963 44.7085 38.4563 44.7085C35.9495 44.7085 33.5605 45.1775 31.2875 46.1156C29.0732 46.995 27.1196 48.2554 25.4299 49.897C23.7403 51.5386 22.3987 53.4733 21.4084 55.701C20.4182 57.9288 19.9222 60.3618 19.9222 63ZM2 63C2 58.0754 2.90283 53.5025 4.71018 49.2814C6.51753 45.0016 9.02263 41.2789 12.2288 38.1131C15.4349 34.9472 19.2513 32.4849 23.6814 30.7261C28.1687 28.9087 33.0947 28 38.4563 28C43.7606 28 48.6565 28.9087 53.1437 30.7261C57.631 32.4849 61.4777 34.9472 64.6838 38.1131C67.9471 41.2789 70.4825 45.0016 72.2899 49.2814C74.0972 53.5025 75 58.0754 75 63C75 67.9246 74.0972 72.5267 72.2899 76.8065C70.4825 81.0276 67.9471 84.7211 64.6838 87.8869C61.4777 91.0528 57.631 93.5443 53.1437 95.3618C48.6565 97.1206 43.7606 98 38.4563 98C33.0947 98 28.1687 97.1206 23.6814 95.3618C19.2513 93.5443 15.4349 91.0528 12.2288 87.8869C9.02263 84.7211 6.51753 81.0276 4.71018 76.8065C2.90283 72.5267 2 67.9246 2 63Z" fill="black"/>
|
||||
<defs>
|
||||
<linearGradient id="paint0_linear_8_11" x1="49.5117" y1="-6.94291e-08" x2="49.5117" y2="125.813" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#1E90FF"/>
|
||||
<stop offset="1" stop-color="#FF1E56"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint1_linear_8_11" x1="50.135" y1="0.473494" x2="50.135" y2="123.975" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#1E90FF"/>
|
||||
<stop offset="1" stop-color="#FF1E56"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint2_linear_8_11" x1="51.7888" y1="-0.0373877" x2="51.7888" y2="126.037" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#1E90FF"/>
|
||||
<stop offset="1" stop-color="#FF1E56"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint3_linear_8_11" x1="51.7888" y1="1.74712" x2="51.7888" y2="124.415" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#1E90FF"/>
|
||||
<stop offset="1" stop-color="#FF1E56"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint4_linear_8_11" x1="50.135" y1="1.02464" x2="50.135" y2="124.526" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#1E90FF"/>
|
||||
<stop offset="1" stop-color="#FF1E56"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint5_linear_8_11" x1="49.5117" y1="-0.812955" x2="49.5117" y2="125" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#1E90FF"/>
|
||||
<stop offset="1" stop-color="#FF1E56"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.9 KiB |
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user